com.sun.pdfview.decode.PNGPredictor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pdf-renderer Show documentation
Show all versions of pdf-renderer Show documentation
PDF renderer implementation supporting the subset of PDF 1.4 specification.
The newest version!
/*
* $Id: PNGPredictor.java,v 1.3 2009-02-12 13:53:58 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.decode;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Undo prediction based on the PNG algorithm.
*/
public class PNGPredictor extends Predictor {
/** Creates a new instance of PNGPredictor */
public PNGPredictor() {
super (PNG);
}
/**
* Undo data based on the png algorithm
*/
public ByteBuffer unpredict(ByteBuffer imageData)
throws IOException
{
List rows = new ArrayList();
byte[] curLine = null;
byte[] prevLine = null;
// get the number of bytes per row
int rowSize = getColumns() * getColors() * getBitsPerComponent();
rowSize = (int) Math.ceil(rowSize / 8.0);
while(imageData.remaining() >= rowSize + 1) {
// the first byte determines the algorithm
int algorithm = (int) (imageData.get() & 0xff);
// read the rest of the line
curLine = new byte[rowSize];
imageData.get(curLine);
// use the algorithm, Luke
switch (algorithm) {
case 0:
// none
break;
case 1:
doSubLine(curLine);
break;
case 2:
doUpLine(curLine, prevLine);
break;
case 3:
doAverageLine(curLine, prevLine);
break;
case 4:
doPaethLine(curLine, prevLine);
break;
}
rows.add(curLine);
prevLine = curLine;
}
// turn into byte array
ByteBuffer outBuf = ByteBuffer.allocate(rows.size() * rowSize);
for (Iterator i = rows.iterator(); i.hasNext();) {
outBuf.put((byte[]) i.next());
}
// reset start pointer
outBuf.flip();
// return
return outBuf;
}
/**
* Return the value of the Sub algorithm on the line (compare bytes to
* the previous byte of the same color on this line).
*/
protected void doSubLine(byte[] curLine) {
// get the number of bytes per sample
int sub = (int) Math.ceil((getBitsPerComponent() * getColors()) / 8.0);
for (int i = 0; i < curLine.length; i++) {
int prevIdx = i - sub;
if (prevIdx >= 0) {
curLine[i] += curLine[prevIdx];
}
}
}
/**
* Return the value of the up algorithm on the line (compare bytes to
* the same byte in the previous line)
*/
protected void doUpLine(byte[] curLine, byte[] prevLine) {
if (prevLine == null) {
// do nothing if this is the first line
return;
}
for (int i = 0; i < curLine.length; i++) {
curLine[i] += prevLine[i];
}
}
/**
* Return the value of the average algorithm on the line (compare
* bytes to the average of the previous byte of the same color and
* the same byte on the previous line)
*/
protected void doAverageLine(byte[] curLine, byte[] prevLine) {
// get the number of bytes per sample
int sub = (int) Math.ceil((getBitsPerComponent() * getColors()) / 8.0);
for (int i = 0; i < curLine.length; i++) {
int raw = 0;
int prior = 0;
// get the last value of this color
int prevIdx = i - sub;
if (prevIdx >= 0) {
raw = curLine[prevIdx] & 0xff;
}
// get the value on the previous line
if (prevLine != null) {
prior = prevLine[i] & 0xff;
}
// add the average
curLine[i] += (byte) Math.floor((raw + prior) / 2);
}
}
/**
* Return the value of the average algorithm on the line (compare
* bytes to the average of the previous byte of the same color and
* the same byte on the previous line)
*/
protected void doPaethLine(byte[] curLine, byte[] prevLine) {
// get the number of bytes per sample
int sub = (int) Math.ceil((getBitsPerComponent() * getColors()) / 8.0);
for (int i = 0; i < curLine.length; i++) {
int left = 0;
int up = 0;
int upLeft = 0;
// get the last value of this color
int prevIdx = i - sub;
if (prevIdx >= 0) {
left = curLine[prevIdx] & 0xff;
}
// get the value on the previous line
if (prevLine != null) {
up = prevLine[i] & 0xff;
}
if (prevIdx > 0 && prevLine != null) {
upLeft = prevLine[prevIdx] & 0xff;
}
// add the average
curLine[i] += (byte) paeth(left, up, upLeft);
}
}
/**
* The paeth algorithm
*/
protected int paeth(int left, int up, int upLeft) {
int p = left + up - upLeft;
int pa = Math.abs(p - left);
int pb = Math.abs(p - up);
int pc = Math.abs(p - upLeft);
if ((pa <= pb) && (pa <= pc)) {
return left;
} else if (pb <= pc) {
return up;
} else {
return upLeft;
}
}
}