001: /*
002: * $Id: PNGPredictor.java,v 1.2 2007/12/20 18:33:33 rbair Exp $
003: *
004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005: * Santa Clara, California 95054, U.S.A. All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020: */
021:
022: package com.sun.pdfview.decode;
023:
024: import java.nio.ByteBuffer;
025:
026: import java.io.IOException;
027:
028: import java.util.ArrayList;
029: import java.util.Iterator;
030: import java.util.List;
031:
032: /**
033: * Undo prediction based on the PNG algorithm.
034: */
035: public class PNGPredictor extends Predictor {
036: /** Creates a new instance of PNGPredictor */
037: public PNGPredictor() {
038: super (PNG);
039: }
040:
041: /**
042: * Undo data based on the png algorithm
043: */
044: public ByteBuffer unpredict(ByteBuffer imageData)
045: throws IOException {
046: List rows = new ArrayList();
047:
048: byte[] curLine = null;
049: byte[] prevLine = null;
050:
051: // get the number of bytes per row
052: int rowSize = getColumns() * getColors()
053: * getBitsPerComponent();
054: rowSize = (int) Math.ceil(rowSize / 8.0);
055:
056: while (imageData.remaining() >= rowSize + 1) {
057: // the first byte determines the algorithm
058: int algorithm = (int) (imageData.get() & 0xff);
059:
060: // read the rest of the line
061: curLine = new byte[rowSize];
062: imageData.get(curLine);
063:
064: // use the algorithm, Luke
065: switch (algorithm) {
066: case 0:
067: // none
068: break;
069: case 1:
070: doSubLine(curLine);
071: break;
072: case 2:
073: doUpLine(curLine, prevLine);
074: break;
075: case 3:
076: doAverageLine(curLine, prevLine);
077: break;
078: case 4:
079: doPaethLine(curLine, prevLine);
080: break;
081: }
082:
083: rows.add(curLine);
084: prevLine = curLine;
085: }
086:
087: // turn into byte array
088: ByteBuffer outBuf = ByteBuffer.allocate(rows.size() * rowSize);
089: for (Iterator i = rows.iterator(); i.hasNext();) {
090: outBuf.put((byte[]) i.next());
091: }
092:
093: // reset start pointer
094: outBuf.flip();
095:
096: // return
097: return outBuf;
098:
099: }
100:
101: /**
102: * Return the value of the Sub algorithm on the line (compare bytes to
103: * the previous byte of the same color on this line).
104: */
105: protected void doSubLine(byte[] curLine) {
106: // get the number of bytes per sample
107: int sub = (int) Math
108: .ceil((getBitsPerComponent() * getColors()) / 8.0);
109:
110: for (int i = 0; i < curLine.length; i++) {
111: int prevIdx = i - sub;
112: if (prevIdx >= 0) {
113: curLine[i] += curLine[prevIdx];
114: }
115: }
116: }
117:
118: /**
119: * Return the value of the up algorithm on the line (compare bytes to
120: * the same byte in the previous line)
121: */
122: protected void doUpLine(byte[] curLine, byte[] prevLine) {
123: if (prevLine == null) {
124: // do nothing if this is the first line
125: return;
126: }
127:
128: for (int i = 0; i < curLine.length; i++) {
129: curLine[i] += prevLine[i];
130: }
131: }
132:
133: /**
134: * Return the value of the average algorithm on the line (compare
135: * bytes to the average of the previous byte of the same color and
136: * the same byte on the previous line)
137: */
138: protected void doAverageLine(byte[] curLine, byte[] prevLine) {
139: // get the number of bytes per sample
140: int sub = (int) Math
141: .ceil((getBitsPerComponent() * getColors()) / 8.0);
142:
143: for (int i = 0; i < curLine.length; i++) {
144: int raw = 0;
145: int prior = 0;
146:
147: // get the last value of this color
148: int prevIdx = i - sub;
149: if (prevIdx >= 0) {
150: raw = curLine[prevIdx] & 0xff;
151: }
152:
153: // get the value on the previous line
154: if (prevLine != null) {
155: prior = prevLine[i] & 0xff;
156: }
157:
158: // add the average
159: curLine[i] += (byte) Math.floor((raw + prior) / 2);
160: }
161: }
162:
163: /**
164: * Return the value of the average algorithm on the line (compare
165: * bytes to the average of the previous byte of the same color and
166: * the same byte on the previous line)
167: */
168: protected void doPaethLine(byte[] curLine, byte[] prevLine) {
169: // get the number of bytes per sample
170: int sub = (int) Math
171: .ceil((getBitsPerComponent() * getColors()) / 8.0);
172:
173: for (int i = 0; i < curLine.length; i++) {
174: int left = 0;
175: int up = 0;
176: int upLeft = 0;
177:
178: // get the last value of this color
179: int prevIdx = i - sub;
180: if (prevIdx >= 0) {
181: left = curLine[prevIdx] & 0xff;
182: }
183:
184: // get the value on the previous line
185: if (prevLine != null) {
186: up = prevLine[i] & 0xff;
187: }
188:
189: if (prevIdx > 0 && prevLine != null) {
190: upLeft = prevLine[prevIdx] & 0xff;
191: }
192:
193: // add the average
194: curLine[i] += (byte) paeth(left, up, upLeft);
195: }
196: }
197:
198: /**
199: * The paeth algorithm
200: */
201: protected int paeth(int left, int up, int upLeft) {
202: int p = left + up - upLeft;
203: int pa = Math.abs(p - left);
204: int pb = Math.abs(p - up);
205: int pc = Math.abs(p - upLeft);
206:
207: if ((pa <= pb) && (pa <= pc)) {
208: return left;
209: } else if (pb <= pc) {
210: return up;
211: } else {
212: return upLeft;
213: }
214: }
215:
216: }
|