/*
* Copyright 2014 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pdfbox.filter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.pdfbox.io.IOUtils;
/**
* Helper class to contain predictor decoding used by Flate and LZW filter.
* To see the history, look at the FlateFilter class.
*/
public final class Predictor
{
private Predictor()
{
}
static void decodePredictor(int predictor, int colors, int bitsPerComponent, int columns, InputStream in, OutputStream out)
throws IOException
{
if (predictor == 1)
{
// no prediction
IOUtils.copy(in, out);
}
else
{
// calculate sizes
final int bitsPerPixel = colors * bitsPerComponent;
final int bytesPerPixel = (bitsPerPixel + 7) / 8;
final int rowlength = (columns * bitsPerPixel + 7) / 8;
byte[] actline = new byte[rowlength];
byte[] lastline = new byte[rowlength];
int linepredictor = predictor;
while (in.available() > 0)
{
// test for PNG predictor; each value >= 10 (not only 15) indicates usage of PNG predictor
if (predictor >= 10)
{
// PNG predictor; each row starts with predictor type (0, 1, 2, 3, 4)
// read per line predictor
linepredictor = in.read();
if (linepredictor == -1)
{
return;
}
// add 10 to tread value 0 as 10, 1 as 11, ...
linepredictor += 10;
}
// read line
int i, offset = 0;
while (offset < rowlength && ((i = in.read(actline, offset, rowlength - offset)) != -1))
{
offset += i;
}
// do prediction as specified in PNG-Specification 1.2
switch (linepredictor)
{
case 2:
// PRED TIFF SUB
if (bitsPerComponent == 8)
{
// for 8 bits per component it is the same algorithm as PRED SUB of PNG format
for (int p = bytesPerPixel; p < rowlength; p++)
{
int sub = actline[p] & 0xff;
int left = actline[p - bytesPerPixel] & 0xff;
actline[p] = (byte) (sub + left);
}
break;
}
if (bitsPerComponent == 16)
{
for (int p = bytesPerPixel; p < rowlength; p += 2)
{
int sub = ((actline[p] & 0xff) << 8) + (actline[p + 1] & 0xff);
int left = (((actline[p - bytesPerPixel] & 0xff) << 8)
+ (actline[p - bytesPerPixel + 1] & 0xff));
actline[p] = (byte) (((sub + left) >> 8) & 0xff);
actline[p + 1] = (byte) ((sub + left) & 0xff);
}
break;
}
if (bitsPerComponent == 1 && colors == 1)
{
// bytesPerPixel cannot be used:
// "A row shall occupy a whole number of bytes, rounded up if necessary.
// Samples and their components shall be packed into bytes
// from high-order to low-order bits."
for (int p = 0; p < rowlength; p++)
{
for (int bit = 7; bit >= 0; --bit)
{
int sub = (actline[p] >> bit) & 1;
if (p == 0 && bit == 7)
{
continue;
}
int left;
if (bit == 7)
{
// use bit #0 from previous byte
left = actline[p - 1] & 1;
}
else
{
// use "previous" bit
left = (actline[p] >> (bit + 1)) & 1;
}
if (((sub + left) & 1) == 0)
{
// reset bit
actline[p] = (byte) (actline[p] & ~(1 << bit));
}
else
{
// set bit
actline[p] = (byte) (actline[p] | (1 << bit));
}
}
}
break;
}
// everything else, i.e. bpc 2 and 4, but has been tested for bpc 1 and 8 too
int elements = columns * colors;
for (int p = colors; p < elements; ++p)
{
int bytePosSub = p * bitsPerComponent / 8;
int bitPosSub = 8 - p * bitsPerComponent % 8 - bitsPerComponent;
int bytePosLeft = (p - colors) * bitsPerComponent / 8;
int bitPosLeft = 8 - (p - colors) * bitsPerComponent % 8 - bitsPerComponent;
int sub = getBitSeq(actline[bytePosSub], bitPosSub, bitsPerComponent);
int left = getBitSeq(actline[bytePosLeft], bitPosLeft, bitsPerComponent);
actline[bytePosSub] = (byte) calcSetBitSeq(actline[bytePosSub], bitPosSub, bitsPerComponent, sub + left);
}
break;
case 10:
// PRED NONE
// do nothing
break;
case 11:
// PRED SUB
for (int p = bytesPerPixel; p < rowlength; p++)
{
int sub = actline[p];
int left = actline[p - bytesPerPixel];
actline[p] = (byte) (sub + left);
}
break;
case 12:
// PRED UP
for (int p = 0; p < rowlength; p++)
{
int up = actline[p] & 0xff;
int prior = lastline[p] & 0xff;
actline[p] = (byte) ((up + prior) & 0xff);
}
break;
case 13:
// PRED AVG
for (int p = 0; p < rowlength; p++)
{
int avg = actline[p] & 0xff;
int left = p - bytesPerPixel >= 0 ? actline[p - bytesPerPixel] & 0xff : 0;
int up = lastline[p] & 0xff;
actline[p] = (byte) ((avg + (left + up) / 2) & 0xff);
}
break;
case 14:
// PRED PAETH
for (int p = 0; p < rowlength; p++)
{
int paeth = actline[p] & 0xff;
int a = p - bytesPerPixel >= 0 ? actline[p - bytesPerPixel] & 0xff : 0;// left
int b = lastline[p] & 0xff;// upper
int c = p - bytesPerPixel >= 0 ? lastline[p - bytesPerPixel] & 0xff : 0;// upperleft
int value = a + b - c;
int absa = Math.abs(value - a);
int absb = Math.abs(value - b);
int absc = Math.abs(value - c);
if (absa <= absb && absa <= absc)
{
actline[p] = (byte) ((paeth + a) & 0xff);
}
else if (absb <= absc)
{
actline[p] = (byte) ((paeth + b) & 0xff);
}
else
{
actline[p] = (byte) ((paeth + c) & 0xff);
}
}
break;
default:
break;
}
System.arraycopy(actline, 0, lastline, 0, rowlength);
out.write(actline);
}
}
}
// get value from bit interval from a byte
static int getBitSeq(int by, int startBit, int bitSize)
{
int mask = ((1 << bitSize) - 1);
return (by >>> startBit) & mask;
}
// set value in a bit interval and return that value
static int calcSetBitSeq(int by, int startBit, int bitSize, int val)
{
int mask = ((1 << bitSize) - 1);
int truncatedVal = val & mask;
mask = ~(mask << startBit);
return (by & mask) | (truncatedVal << startBit);
}
}