You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2017/10/16 18:09:18 UTC
[5/8] pdfbox-jbig2 git commit: initial commit of the JBig2 ImageIO
plugin
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/image/Resizer.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/image/Resizer.java b/src/main/java/org/apache/pdfbox/jbig2/image/Resizer.java
new file mode 100644
index 0000000..3afc75f
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/image/Resizer.java
@@ -0,0 +1,404 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jbig2.image;
+
+import java.awt.Rectangle;
+import java.awt.image.WritableRaster;
+
+import org.apache.pdfbox.jbig2.Bitmap;
+import org.apache.pdfbox.jbig2.util.Utils;
+
+class Resizer {
+
+ static final class Mapping {
+ /** x and y scales */
+ final double scale;
+
+ /** x and y offset used by MAP, private fields */
+ final double offset = .5;
+
+ private final double a0;
+ private final double b0;
+
+ Mapping(double a0, double aw, double b0, double bw) {
+ this.a0 = a0;
+ this.b0 = b0;
+ scale = bw / aw;
+
+ if (scale <= 0.)
+ throw new IllegalArgumentException("Negative scales are not allowed");
+ }
+
+ Mapping(double scaleX) {
+ scale = scaleX;
+ a0 = b0 = 0;
+ }
+
+ double mapPixelCenter(final int b) {
+ return (b + offset - b0) / scale + a0;
+ }
+
+ double dstToSrc(final double b) {
+ return (b - b0) / scale + a0;
+ }
+
+ double srcToDst(final double a) {
+ return (a - a0) * scale + b0;
+ }
+ }
+
+ /**
+ * Order in which to apply filter
+ */
+ private enum Order {
+ AUTO, XY, YX
+ }
+
+ /** Error tolerance */
+ private static final double EPSILON = 1e-7;
+
+ /** Number of bits in filter coefficients */
+ private int weightBits = 14;
+
+ private int weightOne = 1 << weightBits;
+
+ /** Number of bits per channel */
+ private int bitsPerChannel[] = new int[]{
+ 8, 8, 8
+ };
+
+ private static final int NO_SHIFT[] = new int[16];
+
+ private int finalShift[] = new int[]{
+ 2 * weightBits - bitsPerChannel[0], 2 * weightBits - bitsPerChannel[1], 2 * weightBits - bitsPerChannel[2]
+ };
+
+ /**
+ * Is x an integer?
+ *
+ * @param x the double to check
+ * @return <code>true</code> if x is an integer, <code>false</code> if not.
+ */
+ private static boolean isInteger(final double x) {
+ return Math.abs(x - Math.floor(x + .5)) < EPSILON;
+ }
+
+ static final boolean debug = false;
+
+ /**
+ * Should filters be simplified if possible?
+ */
+ private final boolean coerce = true;
+
+ /**
+ * The order in which data is processed.
+ *
+ * @see Order
+ */
+ private final Order order = Order.AUTO;
+
+ /**
+ * Should zeros be trimmed in x filter weight tables?
+ */
+ private final boolean trimZeros = true;
+
+ private final Mapping mappingX;
+ private final Mapping mappingY;
+
+ /**
+ * Creates an instance of {@link Resizer} with one scale factor for both x and y directions.
+ *
+ * @param scale the scale factor for x and y direction
+ */
+ public Resizer(double scale) {
+ this(scale, scale);
+ }
+
+ /**
+ * Creates an instance of {@link Resizer} with a scale factor for each direction.
+ *
+ * @param scaleX the scale factor for x direction
+ * @param scaleY the scale factor for y direction
+ */
+ public Resizer(double scaleX, double scaleY) {
+ mappingX = new Mapping(scaleX);
+ mappingY = new Mapping(scaleY);
+ }
+
+ private Weighttab[] createXWeights(Rectangle srcBounds, final Rectangle dstBounds, final ParameterizedFilter filter) {
+ final int srcX0 = srcBounds.x;
+ final int srcX1 = srcBounds.x + srcBounds.width;
+
+ final int dstX0 = dstBounds.x;
+ final int dstX1 = dstBounds.x + dstBounds.width;
+
+ final Weighttab tabs[] = new Weighttab[dstBounds.width];
+ for (int dstX = dstX0; dstX < dstX1; dstX++) {
+ final double center = mappingX.mapPixelCenter(dstX);
+ tabs[dstX - dstX0] = new Weighttab(filter, weightOne, center, srcX0, srcX1 - 1, trimZeros);
+ }
+
+ return tabs;
+ }
+
+ /**
+ * Checks if our discrete sampling of an arbitrary continuous filter, parameterized by the filter
+ * spacing ({@link ParameterizedFilter#scale}), its radius ({@link ParameterizedFilter#support}),
+ * and the scale and offset of the coordinate mapping, causes the filter to reduce to point
+ * sampling.
+ * <p>
+ * It reduces if support is less than 1 pixel or if integer scale and translation, and filter is
+ * cardinal.
+ *
+ * @param filter the parameterized filter instance to be simplified
+ * @param scale the scale of the coordinate mapping
+ * @param offset the offset of the coordinate mapping
+ */
+ private ParameterizedFilter simplifyFilter(final ParameterizedFilter filter, final double scale, final double offset) {
+ if (coerce
+ && (filter.support <= .5 || filter.filter.cardinal && isInteger(1. / filter.scale)
+ && isInteger(1. / (scale * filter.scale)) && isInteger((offset / scale - .5) / filter.scale)))
+ return new ParameterizedFilter(new Filter.Point(), 1., .5, 1);
+
+ return filter;
+ }
+
+ /**
+ * Filtered zoom, x direction filtering before y direction filtering
+ * <p>
+ * Note: when calling {@link Resizer#createXWeights(Rectangle, Rectangle, ParameterizedFilter)},
+ * we can trim leading and trailing zeros from the x weight buffers as an optimization, but not
+ * for y weight buffers since the split formula is anticipating a constant amount of buffering of
+ * source scanlines; trimming zeros in y weight could cause feedback.
+ */
+ private void resizeXfirst(final Object src, final Rectangle srcBounds, final Object dst, final Rectangle dstBounds,
+ final ParameterizedFilter xFilter, final ParameterizedFilter yFilter) {
+ // source scanline buffer
+ final Scanline buffer = createScanline(src, dst, srcBounds.width);
+
+ // accumulator buffer
+ final Scanline accumulator = createScanline(src, dst, dstBounds.width);
+
+ // a sampled filter for source pixels for each dest x position
+ final Weighttab xWeights[] = createXWeights(srcBounds, dstBounds, xFilter);
+
+ // Circular buffer of active lines
+ final int yBufferSize = yFilter.width + 2;
+ final Scanline lineBuffer[] = new Scanline[yBufferSize];
+ for (int y = 0; y < yBufferSize; y++) {
+ lineBuffer[y] = createScanline(src, dst, dstBounds.width);
+ lineBuffer[y].y = -1; /* mark scanline as unread */
+ }
+
+ // range of source and destination scanlines in regions
+ final int srcY0 = srcBounds.y;
+ final int srcY1 = srcBounds.y + srcBounds.height;
+ final int dstY0 = dstBounds.y;
+ final int dstY1 = dstBounds.y + dstBounds.height;
+
+ int yFetched = -1; // used to assert no backtracking
+
+ // loop over dest scanlines
+ for (int dstY = dstY0; dstY < dstY1; dstY++) {
+ // a sampled filter for source pixels for each dest x position
+ final Weighttab yWeight = new Weighttab(yFilter, weightOne, mappingY.mapPixelCenter(dstY), srcY0, srcY1 - 1, true);
+
+ accumulator.clear();
+
+ // loop over source scanlines that contribute to this dest scanline
+ for (int srcY = yWeight.i0; srcY <= yWeight.i1; srcY++) {
+ final Scanline srcBuffer = lineBuffer[srcY % yBufferSize];
+
+ if (debug)
+ System.out.println(" abuf.y / ayf " + srcBuffer.y + " / " + srcY);
+
+ if (srcBuffer.y != srcY) {
+ // scanline needs to be fetched from src raster
+ srcBuffer.y = srcY;
+
+ if (srcY0 + srcY <= yFetched)
+ throw new AssertionError("Backtracking from line " + yFetched + " to " + (srcY0 + srcY));
+
+ buffer.fetch(srcBounds.x, srcY0 + srcY);
+
+ yFetched = srcY0 + srcY;
+
+ // filter it into the appropriate line of linebuf (xfilt)
+ buffer.filter(NO_SHIFT, bitsPerChannel, xWeights, srcBuffer);
+ }
+
+ // add weighted tbuf into accum (these do yfilt)
+ srcBuffer.accumulate(yWeight.weights[srcY - yWeight.i0], accumulator);
+ }
+
+ accumulator.shift(finalShift);
+ accumulator.store(dstBounds.x, dstY);
+ if (debug)
+ System.out.printf("\n");
+ }
+ }
+
+ /**
+ * Filtered zoom, y direction filtering before x direction filtering
+ * */
+ private void resizeYfirst(final Object src, final Rectangle srcBounds, final Object dst, final Rectangle dstBounds,
+ final ParameterizedFilter xFilter, final ParameterizedFilter yFilter) {
+ // destination scanline buffer
+ final Scanline buffer = createScanline(src, dst, dstBounds.width);
+
+ // accumulator buffer
+ final Scanline accumulator = createScanline(src, dst, srcBounds.width);
+
+ // a sampled filter for source pixels for each destination x position
+ final Weighttab xWeights[] = createXWeights(srcBounds, dstBounds, xFilter);
+
+ // Circular buffer of active lines
+ final int yBufferSize = yFilter.width + 2;
+ final Scanline lineBuffer[] = new Scanline[yBufferSize];
+ for (int y = 0; y < yBufferSize; y++) {
+ lineBuffer[y] = createScanline(src, dst, srcBounds.width);
+
+ // mark scanline as unread
+ lineBuffer[y].y = -1;
+ }
+
+ // range of source and destination scanlines in regions
+ final int srcY0 = srcBounds.y;
+ final int srcY1 = srcBounds.y + srcBounds.height;
+ final int dstY0 = dstBounds.y;
+ final int dstY1 = dstBounds.y + dstBounds.height;
+
+ // used to assert no backtracking
+ int yFetched = -1;
+
+ // loop over destination scanlines
+ for (int dstY = dstY0; dstY < dstY1; dstY++) {
+ // prepare a weighttab for destination y position by a single sampled filter for current y
+ // position
+ final Weighttab yWeight = new Weighttab(yFilter, weightOne, mappingY.mapPixelCenter(dstY), srcY0, srcY1 - 1, true);
+
+ accumulator.clear();
+
+ // loop over source scanlines that contribute to this destination scanline
+ for (int srcY = yWeight.i0; srcY <= yWeight.i1; srcY++) {
+ final Scanline srcBuffer = lineBuffer[srcY % yBufferSize];
+ if (srcBuffer.y != srcY) {
+ // scanline needs to be fetched from source raster
+ srcBuffer.y = srcY;
+
+ if (srcY0 + srcY <= yFetched)
+ throw new AssertionError("Backtracking from line " + yFetched + " to " + (srcY0 + srcY));
+
+ srcBuffer.fetch(srcBounds.x, srcY0 + srcY);
+
+ yFetched = srcY0 + srcY;
+ }
+
+ if (debug)
+ System.out.println(dstY + "[] += " + srcY + "[] * " + yWeight.weights[srcY - yWeight.i0]);
+
+ // add weighted source buffer into accumulator (these do y filter)
+ srcBuffer.accumulate(yWeight.weights[srcY - yWeight.i0], accumulator);
+ }
+
+ // and filter it into the appropriate line of line buffer (x filter)
+ accumulator.filter(bitsPerChannel, finalShift, xWeights, buffer);
+
+ // store destination scanline into destination raster
+ buffer.store(dstBounds.x, dstY);
+ if (debug)
+ System.out.printf("\n");
+ }
+ }
+
+ /**
+ * @param src Source object
+ * @param srcBounds Bounds of the source object
+ * @param dst Destination object
+ * @param dstBounds Bounds of the destination object
+ * @param xFilter The filter used for x direction filtering
+ * @param yFilter The filter used for y direction filtering
+ */
+ public void resize(final Object src, final Rectangle srcBounds, final Object dst, Rectangle dstBounds,
+ Filter xFilter, Filter yFilter) {
+ /*
+ * find scale of filter in a space (source space) when minifying, source scale=1/scale, but when
+ * magnifying, source scale=1
+ */
+ ParameterizedFilter xFilterParameterized = new ParameterizedFilter(xFilter, mappingX.scale);
+ ParameterizedFilter yFilterParameterized = new ParameterizedFilter(yFilter, mappingY.scale);
+
+ /* find valid destination window (transformed source + support margin) */
+ final Rectangle dstRegion = new Rectangle();
+ final int x1 = Utils.ceil(mappingX.srcToDst(srcBounds.x - xFilterParameterized.support) + EPSILON);
+ final int y1 = Utils.ceil(mappingY.srcToDst(srcBounds.y - yFilterParameterized.support) + EPSILON);
+ final int x2 = Utils.floor(mappingX.srcToDst(srcBounds.x + srcBounds.width + xFilterParameterized.support)
+ - EPSILON);
+ final int y2 = Utils.floor(mappingY.srcToDst(srcBounds.y + srcBounds.height + yFilterParameterized.support)
+ - EPSILON);
+ dstRegion.setFrameFromDiagonal(x1, y1, x2, y2);
+
+ if (dstBounds.x < dstRegion.x || dstBounds.getMaxX() > dstRegion.getMaxX() || dstBounds.y < dstRegion.y
+ || dstBounds.getMaxY() > dstRegion.getMaxY()) {
+ /* requested destination window lies outside the valid destination, so clip destination */
+ dstBounds = dstBounds.intersection(dstRegion);
+ }
+
+ if (srcBounds.isEmpty() || dstBounds.width <= 0 || dstBounds.height <= 0) {
+ return;
+ }
+
+ /* check for high-level simplifications of filter */
+ xFilterParameterized = simplifyFilter(xFilterParameterized, mappingX.scale, mappingX.offset);
+ yFilterParameterized = simplifyFilter(yFilterParameterized, mappingY.scale, mappingY.offset);
+
+ /*
+ * decide which filtering order (x->y or y->x) is faster for this mapping by counting
+ * convolution multiplies
+ */
+ final boolean orderXY = order != Order.AUTO
+ ? order == Order.XY
+ : dstBounds.width
+ * (srcBounds.height * xFilterParameterized.width + dstBounds.height * yFilterParameterized.width) < dstBounds.height
+ * (dstBounds.width * xFilterParameterized.width + srcBounds.width * yFilterParameterized.width);
+
+ // choose most efficient filtering order
+ if (orderXY) {
+ resizeXfirst(src, srcBounds, dst, dstBounds, xFilterParameterized, yFilterParameterized);
+ } else {
+ resizeYfirst(src, srcBounds, dst, dstBounds, xFilterParameterized, yFilterParameterized);
+ }
+ }
+
+ private static Scanline createScanline(final Object src, Object dst, final int length) {
+ if (src == null)
+ throw new IllegalArgumentException("src must not be null");
+
+ if (!(src instanceof Bitmap))
+ throw new IllegalArgumentException("src must be from type " + Bitmap.class.getName());
+
+ if (dst == null)
+ throw new IllegalArgumentException("dst must not be null");
+
+ if (!(dst instanceof WritableRaster))
+ throw new IllegalArgumentException("dst must be from type " + WritableRaster.class.getName());
+
+ return new BitmapScanline((Bitmap) src, (WritableRaster) dst, length);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/image/Scanline.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/image/Scanline.java b/src/main/java/org/apache/pdfbox/jbig2/image/Scanline.java
new file mode 100644
index 0000000..19f3565
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/image/Scanline.java
@@ -0,0 +1,558 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jbig2.image;
+
+import java.awt.image.MultiPixelPackedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
+
+
+abstract class Scanline {
+ public interface ScanlineFilter {
+ public void filter(int x, int y, int componentIndex, Object data, int length);
+ }
+
+
+ /**
+ * A Scanline for pixel interleaved byte data with three components. Although its name contains
+ * "BGR" it doesn't really matter how the components are organized, als long as there are three of
+ * them.
+ */
+ protected static final class ByteBGRScanline extends Scanline {
+ private final Raster srcRaster;
+ private final WritableRaster dstRaster;
+
+ private final int data[];
+
+ protected ByteBGRScanline(Raster src, WritableRaster dst, final int length) {
+ super(length);
+ srcRaster = src;
+ dstRaster = dst;
+
+ data = new int[3 * length];
+ }
+
+ @Override
+ protected void accumulate(final int weight, final Scanline dst) {
+ final ByteBGRScanline bcs = (ByteBGRScanline) dst;
+
+ final int abuf[] = data;
+ final int bbuf[] = bcs.data;
+
+ for (int b = 0; b < bbuf.length; b++)
+ bbuf[b] += weight * abuf[b];
+ }
+
+ @Override
+ protected void clear() {
+ final int[] b = data;
+ for (int j = 0; j < b.length; j++)
+ b[j] = 0;
+ }
+
+ @Override
+ protected void fetch(final int x, final int y) {
+ srcRaster.getPixels(x, y, length, 1, data);
+ }
+
+ @Override
+ protected void filter(final int[] preShift, final int[] postShift, final Weighttab[] tabs, final Scanline dst) {
+ final ByteBGRScanline bcs = (ByteBGRScanline) dst;
+ final int nx = dst.length;
+
+ // start sum at 1<<shift-1 for rounding
+ final int start[] = new int[]{
+ 1 << postShift[0] - 1, 1 << postShift[1] - 1, 1 << postShift[2] - 1
+ };
+ final int abuf[] = data;
+ final int bbuf[] = bcs.data;
+
+ // the next two blocks are duplicated except for the missing shift
+ // operation if preShift==0.
+ if (preShift[0] != 0 || preShift[1] != 0 || preShift[2] != 0)
+ for (int bp = 0, b = 0; b < nx; b++) {
+ final Weighttab wtab = tabs[b];
+ final int an = wtab.weights.length;
+
+ int sumr = start[0];
+ int sumg = start[1];
+ int sumb = start[2];
+ for (int wp = 0, ap = wtab.i0 * 3; wp < an && ap < abuf.length; wp++) {
+ final int w = wtab.weights[wp];
+ sumr += w * (abuf[ap++] >> preShift[0]);
+ sumg += w * (abuf[ap++] >> preShift[1]);
+ sumb += w * (abuf[ap++] >> preShift[2]);
+ }
+
+ int t = sumr >> postShift[0];
+ bbuf[bp++] = t < 0 ? 0 : t > 255 ? 255 : t;
+ t = sumg >> postShift[1];
+ bbuf[bp++] = t < 0 ? 0 : t > 255 ? 255 : t;
+ t = sumb >> postShift[2];
+ bbuf[bp++] = t < 0 ? 0 : t > 255 ? 255 : t;
+ }
+ else
+ for (int bp = 0, b = 0; b < nx; b++) {
+ final Weighttab wtab = tabs[b];
+ final int an = wtab.weights.length;
+
+ int sumr = start[0];
+ int sumg = start[1];
+ int sumb = start[2];
+ for (int wp = 0, ap = wtab.i0 * 3; wp < an && ap < abuf.length; wp++) {
+ final int w = wtab.weights[wp];
+ sumr += w * abuf[ap++];
+ sumg += w * abuf[ap++];
+ sumb += w * abuf[ap++];
+ }
+
+ bbuf[bp++] = sumr >> postShift[0];
+ bbuf[bp++] = sumg >> postShift[1];
+ bbuf[bp++] = sumb >> postShift[2];
+ }
+ }
+
+ @Override
+ protected void shift(final int[] shift) {
+ final int half[] = new int[]{
+ 1 << shift[0] - 1, 1 << shift[1] - 1, 1 << shift[2] - 1
+ };
+
+ final int abuf[] = data;
+
+ for (int b = 0; b < abuf.length;) {
+ for (int c = 0; c < 3; c++, b++) {
+ final int t = abuf[b] + half[c] >> shift[c];
+ abuf[b] = t < 0 ? 0 : t > 255 ? 255 : t;
+ }
+ }
+ }
+
+ @Override
+ protected void store(final int x, final int y) {
+ dstRaster.setPixels(x, y, length, 1, data);
+ }
+ }
+
+ /**
+ * A Scanline for packed integer pixels.
+ */
+ protected static final class IntegerSinglePixelPackedScanline extends Scanline {
+ private final Raster srcRaster;
+ private final WritableRaster dstRaster;
+
+ private final int data[];
+ private final int[] bitMasks;
+ private final int[] bitOffsets;
+ private final int componentCount;
+ private final SinglePixelPackedSampleModel srcSM;
+ private final int tmp[];
+
+ protected IntegerSinglePixelPackedScanline(Raster src, WritableRaster dst, final int length) {
+ super(length);
+ srcRaster = src;
+ dstRaster = dst;
+
+ srcSM = (SinglePixelPackedSampleModel) srcRaster.getSampleModel();
+
+ bitMasks = srcSM.getBitMasks();
+ bitOffsets = srcSM.getBitOffsets();
+ componentCount = bitMasks.length;
+
+ if (componentCount != bitOffsets.length || bitOffsets.length != srcSM.getNumBands())
+ throw new IllegalArgumentException("weird: getBitMasks().length != getBitOffsets().length");
+
+ tmp = new int[componentCount];
+
+ data = new int[componentCount * length];
+ }
+
+ @Override
+ protected void accumulate(final int weight, final Scanline dst) {
+ final IntegerSinglePixelPackedScanline ispps = (IntegerSinglePixelPackedScanline) dst;
+
+ final int abuf[] = data;
+ final int bbuf[] = ispps.data;
+
+ for (int b = 0; b < bbuf.length; b++)
+ bbuf[b] += weight * abuf[b];
+ }
+
+ @Override
+ protected void clear() {
+ final int[] b = data;
+ for (int j = 0; j < b.length; j++)
+ b[j] = 0;
+ }
+
+ @Override
+ protected void fetch(final int x, final int y) {
+ srcRaster.getPixels(x, y, length, 1, data);
+ }
+
+ @Override
+ protected void filter(final int[] preShift, final int[] postShift, final Weighttab[] tabs, final Scanline dst) {
+ final IntegerSinglePixelPackedScanline ispps = (IntegerSinglePixelPackedScanline) dst;
+ final int nx = dst.length;
+
+ // start sum at 1<<shift-1 for rounding
+ final int start[] = tmp;
+ for (int c = 0; c < componentCount; c++)
+ start[c] = 1 << postShift[c] - 1;
+
+ final int abuf[] = data;
+ final int bbuf[] = ispps.data;
+
+ // the next two blocks are duplicated except for the missing shift
+ // operation if preShift==0.
+ boolean hasPreShift = false;
+ for (int c = 0; c < componentCount && !hasPreShift; c++)
+ hasPreShift |= preShift[c] != 0;
+ if (hasPreShift)
+ for (int bp = 0, b = 0; b < nx; b++) {
+ final Weighttab wtab = tabs[b];
+ final int an = wtab.weights.length;
+
+ for (int c = 0; c < componentCount; c++) {
+ int sum = start[c];
+ for (int wp = 0, ap = wtab.i0 * componentCount + c; wp < an && ap < abuf.length; wp++, ap += componentCount)
+ sum += wtab.weights[wp] * (abuf[ap] >> preShift[c]);
+
+ final int t = sum >> postShift[c];
+ bbuf[bp++] = t < 0 ? 0 : t > 255 ? 255 : t;
+ }
+ }
+ else
+ for (int bp = 0, b = 0; b < nx; b++) {
+ final Weighttab wtab = tabs[b];
+ final int an = wtab.weights.length;
+
+ for (int c = 0; c < componentCount; c++) {
+ int sum = start[c];
+ for (int wp = 0, ap = wtab.i0 * componentCount + c; wp < an && ap < abuf.length; wp++, ap += componentCount)
+ sum += wtab.weights[wp] * abuf[ap];
+
+ bbuf[bp++] = sum >> postShift[c];
+ }
+ }
+ }
+
+ @Override
+ protected void shift(final int[] shift) {
+ final int half[] = tmp;
+ for (int c = 0; c < componentCount; c++)
+ half[c] = 1 << shift[c] - 1;
+
+ final int abuf[] = data;
+
+ for (int b = 0; b < abuf.length;) {
+ for (int c = 0; c < componentCount; c++, b++) {
+ final int t = abuf[b] + half[c] >> shift[c];
+ abuf[b] = t < 0 ? 0 : t > 255 ? 255 : t;
+ }
+ }
+ }
+
+ @Override
+ protected void store(final int x, final int y) {
+ dstRaster.setPixels(x, y, length, 1, data);
+ }
+ }
+
+ /**
+ * A Scanline for packed integer pixels.
+ */
+ protected static final class GenericRasterScanline extends Scanline {
+ private final Raster srcRaster;
+ private final WritableRaster dstRaster;
+
+ private final int componentCount;
+ private final int data[][];
+ private final SampleModel srcSM;
+ private final SampleModel dstSM;
+ private final int channelMask[];
+ private final int[] tmp;
+ private final ScanlineFilter inputFilter;
+
+ protected GenericRasterScanline(Raster src, WritableRaster dst, final int length, int bitsPerChannel[],
+ ScanlineFilter inputFilter) {
+ super(length);
+ srcRaster = src;
+ dstRaster = dst;
+ this.inputFilter = inputFilter;
+
+ srcSM = srcRaster.getSampleModel();
+ dstSM = dstRaster.getSampleModel();
+
+ componentCount = srcSM.getNumBands();
+
+ if (componentCount != dstSM.getNumBands())
+ throw new IllegalArgumentException("weird: src raster num bands != dst raster num bands");
+
+ tmp = new int[componentCount];
+
+ data = new int[componentCount][];
+ for (int i = 0; i < data.length; i++)
+ data[i] = new int[length];
+
+ channelMask = new int[componentCount];
+ for (int c = 0; c < componentCount; c++)
+ channelMask[c] = (1 << bitsPerChannel[c]) - 1;
+ }
+
+ @Override
+ protected void accumulate(final int weight, final Scanline dst) {
+ final GenericRasterScanline grs = (GenericRasterScanline) dst;
+
+ final int l = grs.data[0].length;
+ for (int c = 0; c < componentCount; c++) {
+ final int ac[] = data[c];
+ final int bc[] = grs.data[c];
+
+ for (int b = 0; b < l; b++)
+ bc[b] += weight * ac[b];
+ }
+ }
+
+ @Override
+ protected void clear() {
+ for (int c = 0; c < componentCount; c++) {
+ final int[] b = data[c];
+ for (int x = 0; x < b.length; x++)
+ b[x] = 0;
+ }
+ }
+
+ @Override
+ protected void fetch(final int x, final int y) {
+ for (int c = 0; c < componentCount; c++) {
+ srcRaster.getSamples(x, y, length, 1, c, data[c]);
+ if (null != inputFilter)
+ inputFilter.filter(x, y, c, data[c], length);
+ }
+ }
+
+ @Override
+ protected void filter(final int[] preShift, final int[] postShift, final Weighttab[] tabs, final Scanline dst) {
+ final GenericRasterScanline grs = (GenericRasterScanline) dst;
+ final int nx = dst.length;
+
+ // start sum at 1<<shift-1 for rounding
+ final int start[] = tmp;
+ for (int c = 0; c < componentCount; c++)
+ start[c] = 1 << postShift[c] - 1;
+
+ final int l = data[0].length;
+
+ // the next two blocks are duplicated except for the missing shift
+ // operation if preShift==0.
+ boolean hasPreShift = false;
+ for (int c = 0; c < componentCount && !hasPreShift; c++)
+ hasPreShift |= preShift[c] != 0;
+ if (hasPreShift)
+ for (int c = 0; c < componentCount; c++) {
+ final int ac[] = data[c];
+ final int bc[] = grs.data[c];
+ final int m = channelMask[c];
+ for (int b = 0; b < nx; b++) {
+ final Weighttab wtab = tabs[b];
+ final int an = wtab.weights.length;
+
+ int sum = start[c];
+ for (int wp = 0, ap = wtab.i0; wp < an && ap < l; wp++, ap++)
+ sum += wtab.weights[wp] * (ac[ap] >> preShift[c]);
+
+ final int t = sum >> postShift[c];
+ bc[b] = t < 0 ? 0 : t > m ? m : t;
+ }
+ }
+ else
+ for (int c = 0; c < componentCount; c++) {
+ final int ac[] = data[c];
+ final int bc[] = grs.data[c];
+
+ for (int b = 0; b < nx; b++) {
+ final Weighttab wtab = tabs[b];
+ final int an = wtab.weights.length;
+
+ int sum = start[c];
+ for (int wp = 0, ap = wtab.i0; wp < an && ap < l; wp++, ap++)
+ sum += wtab.weights[wp] * ac[ap];
+
+ bc[b] = sum >> postShift[c];
+ }
+ }
+ }
+
+ @Override
+ protected void shift(final int[] shift) {
+ final int half[] = tmp;
+ for (int c = 0; c < componentCount; c++)
+ half[c] = 1 << shift[c] - 1;
+
+ final int abuf[][] = data;
+
+ final int l = abuf[0].length;
+ for (int c = 0; c < componentCount; c++) {
+ final int ac[] = data[c];
+ final int m = channelMask[c];
+
+ for (int a = 0; a < l; a++) {
+ final int t = ac[a] + half[c] >> shift[c];
+ ac[a] = t < 0 ? 0 : t > m ? m : t;
+ }
+ }
+ }
+
+ @Override
+ protected void store(final int x, final int y) {
+ final int nx = length;
+ for (int c = 0; c < componentCount; c++)
+ dstRaster.setSamples(x, y, nx, 1, c, data[c]);
+ }
+ }
+
+ /**
+ * A Scanline for BiLevel input data ({@link MultiPixelPackedSampleModel}) to indexed output data
+ * (<code>sun.awt.image.ByteInterleavedRaster</code>).
+ */
+ protected static final class ByteBiLevelPackedScanline extends Scanline {
+ private final Raster srcRaster;
+ private final WritableRaster dstRaster;
+
+ private final int data[];
+
+ protected ByteBiLevelPackedScanline(Raster src, WritableRaster dst, final int length) {
+ super(length);
+ srcRaster = src;
+ dstRaster = dst;
+
+ data = new int[length];
+ }
+
+ @Override
+ protected void accumulate(final int weight, final Scanline dst) {
+ final ByteBiLevelPackedScanline bblps = (ByteBiLevelPackedScanline) dst;
+
+ final int abuf[] = data;
+ final int bbuf[] = bblps.data;
+
+ for (int b = 0; b < bbuf.length; b++)
+ bbuf[b] += weight * abuf[b];
+ }
+
+ @Override
+ protected void clear() {
+ final int[] b = data;
+ for (int j = 0; j < b.length; j++)
+ b[j] = 0;
+ }
+
+ @Override
+ protected void fetch(final int x, final int y) {
+ srcRaster.getPixels(x, y, length, 1, data);
+ for (int i = 0; i < length; i++)
+ if (data[i] != 0)
+ data[i] = 255;
+ }
+
+ @Override
+ protected void filter(final int[] preShift, final int[] postShift, final Weighttab[] tabs, final Scanline dst) {
+ final ByteBiLevelPackedScanline bblps = (ByteBiLevelPackedScanline) dst;
+ final int nx = dst.length;
+
+ // start sum at 1<<shift-1 for rounding
+ final int start = 1 << postShift[0] - 1;
+ final int abuf[] = data;
+ final int bbuf[] = bblps.data;
+
+ // the next two blocks are duplicated except for the missing shift
+ // operation if preShift==0.
+ final int preShift0 = preShift[0];
+ final int postShift0 = postShift[0];
+ if (preShift0 != 0)
+ for (int bp = 0, b = 0; b < nx; b++) {
+ final Weighttab wtab = tabs[b];
+ final int an = wtab.weights.length;
+
+ int sum = start;
+ for (int wp = 0, ap = wtab.i0; wp < an && ap < abuf.length; wp++) {
+ sum += wtab.weights[wp] * (abuf[ap++] >> preShift0);
+ }
+
+ final int t = sum >> postShift0;
+ bbuf[bp++] = t < 0 ? 0 : t > 255 ? 255 : t;
+ }
+ else
+ for (int bp = 0, b = 0; b < nx; b++) {
+ final Weighttab wtab = tabs[b];
+ final int an = wtab.weights.length;
+
+ int sum = start;
+ for (int wp = 0, ap = wtab.i0; wp < an && ap < abuf.length; wp++) {
+ sum += wtab.weights[wp] * abuf[ap++];
+ }
+
+ bbuf[bp++] = sum >> postShift0;
+ }
+ }
+
+ @Override
+ protected void shift(final int[] shift) {
+ final int shift0 = shift[0];
+ final int half = 1 << shift0 - 1;
+
+ final int abuf[] = data;
+
+ for (int b = 0; b < abuf.length; b++) {
+ final int t = abuf[b] + half >> shift0;
+ abuf[b] = t < 0 ? 0 : t > 255 ? 255 : t;
+ }
+ }
+
+ @Override
+ protected void store(final int x, final int y) {
+ dstRaster.setPixels(x, y, length, 1, data);
+ }
+ }
+
+ int y;
+ protected final int length;
+
+ protected Scanline(final int width) {
+ length = width;
+ }
+
+ protected final int getWidth() {
+ return length;
+ }
+
+ protected abstract void clear();
+
+ protected abstract void fetch(int x, int y);
+
+ protected abstract void filter(int[] preShift, int[] postShift, Weighttab[] xweights, Scanline dst);
+
+ protected abstract void accumulate(int weight, Scanline dst);
+
+ protected abstract void shift(int[] finalshift);
+
+ protected abstract void store(int x, int y);
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/image/Weighttab.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/image/Weighttab.java b/src/main/java/org/apache/pdfbox/jbig2/image/Weighttab.java
new file mode 100644
index 0000000..7cbaa5f
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/image/Weighttab.java
@@ -0,0 +1,114 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jbig2.image;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+import org.apache.pdfbox.jbig2.util.Utils;
+
+final class Weighttab {
+ final int weights[]; /* weight[i] goes with pixel at i0+i */
+ final int i0, i1; /* range of samples is [i0..i1-1] */
+
+ /*
+ * make_weighttab: sample the continuous filter, scaled by ap.scale and positioned at continuous
+ * source coordinate cen, for source coordinates in the range [0..len-1], writing the weights into
+ * wtab. Scale the weights so they sum to WEIGHTONE, and trim leading and trailing zeros if
+ * trimzeros!=0. b is the dest coordinate (for diagnostics).
+ */
+ public Weighttab(ParameterizedFilter pf, int weightOne, final double center, int a0, final int a1,
+ final boolean trimzeros) {
+ // find the source coord range of this positioned filter: [i0..i1-1] and clamp to input range
+ int i0 = max(pf.minIndex(center), a0);
+ int i1 = min(pf.maxIndex(center), a1);
+
+ // find scale factor sc to normalize the filter
+ double den = 0;
+ for (int i = i0; i <= i1; i++)
+ den += pf.eval(center, i);
+
+ // set sc so that sum of sc*func() is approximately WEIGHTONE
+ final double scale = den == 0. ? weightOne : weightOne / den;
+
+ // find range of non-zero samples
+ if (trimzeros) {
+ boolean stillzero = trimzeros;
+ int lastnonzero = 0;
+ for (int i = i0; i <= i1; i++) {
+ /* evaluate the filter function at p */
+ final double tr = Utils.clamp(scale * pf.eval(center, i), Short.MIN_VALUE, Short.MAX_VALUE);
+
+ final int t = (int) Math.floor(tr + .5);
+ if (stillzero && t == 0)
+ i0++; /* find first nonzero */
+ else {
+ stillzero = false;
+ if (t != 0)
+ lastnonzero = i; /* find last nonzero */
+ }
+ }
+
+ i1 = max(lastnonzero, i0);
+ }
+
+ // initialize weight table of appropriate length
+ weights = new int[i1 - i0 + 1];
+
+ // compute the discrete, sampled filter coefficients
+ int sum = 0;
+ for (int idx = 0, i = i0; i <= i1; i++) {
+ /* evaluate the filter function at p */
+ final double tr = Utils.clamp(scale * pf.eval(center, i), Short.MIN_VALUE, Short.MAX_VALUE);
+
+ final int t = (int) Math.floor(tr + .5);
+ weights[idx++] = t; /* add weight to table */
+ sum += t;
+ }
+
+ if (sum == 0) {
+ i1 = i0;
+ weights[0] = weightOne;
+ } else if (sum != weightOne) {
+ /*
+ * Fudge the center slightly to make sum=WEIGHTONE exactly. Is this the best way to normalize
+ * a discretely sampled continuous filter?
+ */
+ int i = (int) (center + .5);
+ if (i >= i1)
+ i = i1 - 1;
+ if (i < i0)
+ i = i0;
+ final int t = weightOne - sum;
+ if (Resizer.debug)
+ System.out.printf("[%d]+=%d ", i, t);
+ weights[i - i0] += t; /* fudge center sample */
+ }
+
+ this.i0 = i0 - a0;
+ this.i1 = i1 - a0;
+
+ if (Resizer.debug) {
+ System.out.printf("\t");
+ for (int idx = 0, i = i0; i < i1; i++, idx++)
+ System.out.printf("%5d ", weights[idx]);
+ System.out.printf("\n");
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/io/DefaultInputStreamFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/io/DefaultInputStreamFactory.java b/src/main/java/org/apache/pdfbox/jbig2/io/DefaultInputStreamFactory.java
new file mode 100644
index 0000000..b92b033
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/io/DefaultInputStreamFactory.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jbig2.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.imageio.stream.FileCacheImageInputStream;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.MemoryCacheImageInputStream;
+
+public class DefaultInputStreamFactory implements InputStreamFactory {
+
+ public ImageInputStream getInputStream(InputStream is) {
+ try {
+ return new FileCacheImageInputStream(is, null);
+ } catch (IOException e) {
+ return new MemoryCacheImageInputStream(is);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/io/InputStreamFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/io/InputStreamFactory.java b/src/main/java/org/apache/pdfbox/jbig2/io/InputStreamFactory.java
new file mode 100644
index 0000000..7b2e5c5
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/io/InputStreamFactory.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jbig2.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.imageio.stream.ImageInputStream;
+
+public interface InputStreamFactory {
+ public ImageInputStream getInputStream(InputStream is) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/io/SubInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/io/SubInputStream.java b/src/main/java/org/apache/pdfbox/jbig2/io/SubInputStream.java
new file mode 100644
index 0000000..a7782bc
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/io/SubInputStream.java
@@ -0,0 +1,150 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jbig2.io;
+
+import java.io.IOException;
+
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.ImageInputStreamImpl;
+
+/**
+ * A wrapper for an {@link ImageInputStream} which is able to provide a view of a specific part of
+ * the wrapped stream. Read accesses to the wrapped stream are synchronized, so that users of this
+ * stream need to deal with synchronization against other users of the same instance, but not
+ * against other users of the wrapped stream.
+ */
+public class SubInputStream extends ImageInputStreamImpl {
+
+ protected final ImageInputStream wrappedStream;
+
+ /**
+ * The position in the wrapped stream at which the window starts. Offset is an absolut value.
+ */
+ protected final long offset;
+
+ /**
+ * The length of the window. Length is an relative value.
+ */
+ protected final long length;
+
+ /**
+ * A buffer which is used to improve read performance.
+ */
+ private final byte buffer[] = new byte[4096];
+
+ /**
+ * Location of the first byte in the buffer with respect to the start of the stream.
+ */
+ long bufferBase;
+
+ /**
+ * Location of the last byte in the buffer with respect to the start of the stream.
+ */
+ long bufferTop;
+
+ /**
+ * Construct a new SubInputStream which provides a view of the wrapped stream.
+ *
+ * @param iis - The stream to be wrapped.
+ * @param offset - The absolute position in the wrapped stream at which the sub-stream starts.
+ * @param length - The length of the sub-stream.
+ */
+ public SubInputStream(ImageInputStream iis, long offset, long length) {
+ assert null != iis;
+ assert length >= 0;
+ assert offset >= 0;
+
+ this.wrappedStream = iis;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (streamPos >= length) {
+ return -1;
+ }
+
+ if (streamPos >= bufferTop || streamPos < bufferBase) {
+ if (!fillBuffer()) {
+ return -1;
+ }
+ }
+
+ int read = 0xff & buffer[(int) (streamPos - bufferBase)];
+
+ streamPos++;
+
+ return read;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (streamPos >= length) {
+ return -1;
+ }
+
+ synchronized (wrappedStream) {
+ if (wrappedStream.getStreamPosition() != streamPos + offset) {
+ wrappedStream.seek(streamPos + offset);
+ }
+
+ int toRead = (int) Math.min(len, length - streamPos);
+ int read = wrappedStream.read(b, off, toRead);
+ streamPos += read;
+
+ return read;
+ }
+ }
+
+ /**
+ * Fill the buffer at the current stream position.
+ *
+ * @throws IOException
+ * @return Boolean flag. {@code true} if successful, {@code false} if not.
+ */
+ private boolean fillBuffer() throws IOException {
+ synchronized (wrappedStream) {
+ if (wrappedStream.getStreamPosition() != streamPos + offset) {
+ wrappedStream.seek(streamPos + offset);
+ }
+
+ bufferBase = streamPos;
+ int toRead = (int) Math.min(buffer.length, length - streamPos);
+ int read = wrappedStream.read(buffer, 0, toRead);
+ bufferTop = bufferBase + read;
+
+ return read > 0;
+ }
+ }
+
+ @Override
+ public long length() {
+ return length;
+ }
+
+ /**
+ * Skips remaining bits in the current byte.
+ */
+ public void skipBits() {
+ if (bitOffset != 0) {
+ bitOffset = 0;
+ streamPos++;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/EndOfStripe.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/EndOfStripe.java b/src/main/java/org/apache/pdfbox/jbig2/segments/EndOfStripe.java
new file mode 100644
index 0000000..704137e
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/EndOfStripe.java
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jbig2.segments;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.jbig2.SegmentData;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.err.IntegerMaxValueException;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+
+/**
+ * This segment flags an end of stripe (see JBIG2 ISO standard, 7.4.9).
+ */
+public class EndOfStripe implements SegmentData {
+
+ private SubInputStream subInputStream;
+ private int lineNumber;
+
+ private void parseHeader() throws IOException, IntegerMaxValueException, InvalidHeaderValueException {
+ lineNumber = (int) (subInputStream.readBits(32) & 0xffffffff);
+ }
+
+ public void init(SegmentHeader header, SubInputStream sis) throws IntegerMaxValueException,
+ InvalidHeaderValueException, IOException {
+ this.subInputStream = sis;
+ parseHeader();
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+}
http://git-wip-us.apache.org/repos/asf/pdfbox-jbig2/blob/4619d28b/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRefinementRegion.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRefinementRegion.java b/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRefinementRegion.java
new file mode 100644
index 0000000..af59e7d
--- /dev/null
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/GenericRefinementRegion.java
@@ -0,0 +1,743 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jbig2.segments;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.jbig2.Bitmap;
+import org.apache.pdfbox.jbig2.Region;
+import org.apache.pdfbox.jbig2.SegmentHeader;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.ArithmeticDecoder;
+import org.apache.pdfbox.jbig2.decoder.arithmetic.CX;
+import org.apache.pdfbox.jbig2.err.IntegerMaxValueException;
+import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
+import org.apache.pdfbox.jbig2.io.SubInputStream;
+import org.apache.pdfbox.jbig2.util.log.Logger;
+import org.apache.pdfbox.jbig2.util.log.LoggerFactory;
+
+/**
+ * This class represents a generic refinement region and implements the procedure described in JBIG2
+ * ISO standard, 6.3 and 7.4.7.
+ */
+public class GenericRefinementRegion implements Region {
+ private static final Logger log = LoggerFactory.getLogger(GenericRefinementRegion.class);
+
+ public static abstract class Template {
+ protected abstract short form(short c1, short c2, short c3, short c4, short c5);
+
+ protected abstract void setIndex(CX cx);
+ }
+
+ private static class Template0 extends Template {
+
+ @Override
+ protected short form(short c1, short c2, short c3, short c4, short c5) {
+ return (short) ((c1 << 10) | (c2 << 7) | (c3 << 4) | (c4 << 1) | c5);
+ }
+
+ @Override
+ protected void setIndex(CX cx) {
+ // Figure 14, page 22
+ cx.setIndex(0x100);
+ }
+
+ }
+
+ private static class Template1 extends Template {
+
+ @Override
+ protected short form(short c1, short c2, short c3, short c4, short c5) {
+ return (short) (((c1 & 0x02) << 8) | (c2 << 6) | ((c3 & 0x03) << 4) | (c4 << 1) | c5);
+ }
+
+ @Override
+ protected void setIndex(CX cx) {
+ // Figure 15, page 22
+ cx.setIndex(0x080);
+ }
+
+ }
+
+ private static final Template T0 = new Template0();
+ private static final Template T1 = new Template1();
+
+ private SubInputStream subInputStream;
+
+ private SegmentHeader segmentHeader;
+
+ /** Region segment information flags, 7.4.1 */
+ private RegionSegmentInformation regionInfo;
+
+ /** Generic refinement region segment flags, 7.4.7.2 */
+ private boolean isTPGROn;
+ private short templateID;
+
+ private Template template;
+ /** Generic refinement region segment AT flags, 7.4.7.3 */
+ private short grAtX[];
+ private short grAtY[];
+
+ /** Decoded data as pixel values (use row stride/width to wrap line) */
+ private Bitmap regionBitmap;
+
+ /** Variables for decoding */
+ private Bitmap referenceBitmap;
+ private int referenceDX;
+ private int referenceDY;
+
+ private ArithmeticDecoder arithDecoder;
+ private CX cx;
+
+ /**
+ * If true, AT pixels are not on their nominal location and have to be overridden.
+ */
+ private boolean override;
+ private boolean[] grAtOverride;
+ public GenericRefinementRegion() {
+ }
+
+ public GenericRefinementRegion(final SubInputStream subInputStream) {
+ this.subInputStream = subInputStream;
+ this.regionInfo = new RegionSegmentInformation(subInputStream);
+ }
+
+ public GenericRefinementRegion(final SubInputStream subInputStream, final SegmentHeader segmentHeader) {
+ this.subInputStream = subInputStream;
+ this.segmentHeader = segmentHeader;
+ this.regionInfo = new RegionSegmentInformation(subInputStream);
+ }
+
+ /**
+ * Parses the flags described in JBIG2 ISO standard:
+ * <ul>
+ * <li>7.4.7.2 Generic refinement region segment flags</li>
+ * <li>7.4.7.3 Generic refinement refion segment AT flags</li>
+ * </ul>
+ *
+ * @throws IOException
+ */
+ private void parseHeader() throws IOException {
+ regionInfo.parseHeader();
+
+ /* Bit 2-7 */
+ subInputStream.readBits(6); // Dirty read...
+
+ /* Bit 1 */
+ if (subInputStream.readBit() == 1) {
+ isTPGROn = true;
+ }
+
+ /* Bit 0 */
+ templateID = (short) subInputStream.readBit();
+
+ switch (templateID){
+ case 0 :
+ template = T0;
+ readAtPixels();
+ break;
+ case 1 :
+ template = T1;
+ break;
+ }
+ }
+
+ private void readAtPixels() throws IOException {
+ grAtX = new short[2];
+ grAtY = new short[2];
+
+ /* Byte 0 */
+ grAtX[0] = subInputStream.readByte();
+ /* Byte 1 */
+ grAtY[0] = subInputStream.readByte();
+ /* Byte 2 */
+ grAtX[1] = subInputStream.readByte();
+ /* Byte 3 */
+ grAtY[1] = subInputStream.readByte();
+ }
+
+ /**
+ * Decode using a template and arithmetic coding, as described in 6.3.5.6
+ *
+ * @throws IOException
+ * @throws InvalidHeaderValueException
+ * @throws IntegerMaxValueException
+ */
+ public Bitmap getRegionBitmap() throws IOException, IntegerMaxValueException, InvalidHeaderValueException {
+ if (null == regionBitmap) {
+ /* 6.3.5.6 - 1) */
+ int isLineTypicalPredicted = 0;
+
+ if (referenceBitmap == null) {
+ // Get the reference bitmap, which is the base of refinement process
+ referenceBitmap = getGrReference();
+ }
+
+ if (arithDecoder == null) {
+ arithDecoder = new ArithmeticDecoder(subInputStream);
+ }
+
+ if (cx == null) {
+ cx = new CX(8192, 1);
+ }
+
+ /* 6.3.5.6 - 2) */
+ regionBitmap = new Bitmap(regionInfo.getBitmapWidth(), regionInfo.getBitmapHeight());
+
+ if (templateID == 0) {
+ // AT pixel may only occur in template 0
+ updateOverride();
+ }
+
+ final int paddedWidth = (regionBitmap.getWidth() + 7) & -8;
+ final int deltaRefStride = isTPGROn ? -referenceDY * referenceBitmap.getRowStride() : 0;
+ final int yOffset = deltaRefStride + 1;
+
+ /* 6.3.5.6 - 3 */
+ for (int y = 0; y < regionBitmap.getHeight(); y++) {
+ /* 6.3.5.6 - 3 b) */
+ if (isTPGROn) {
+ isLineTypicalPredicted ^= decodeSLTP();
+ }
+
+ if (isLineTypicalPredicted == 0) {
+ /* 6.3.5.6 - 3 c) */
+ decodeOptimized(y, regionBitmap.getWidth(), regionBitmap.getRowStride(), referenceBitmap.getRowStride(),
+ paddedWidth, deltaRefStride, yOffset);
+ } else {
+ /* 6.3.5.6 - 3 d) */
+ decodeTypicalPredictedLine(y, regionBitmap.getWidth(), regionBitmap.getRowStride(),
+ referenceBitmap.getRowStride(), paddedWidth, deltaRefStride);
+ }
+ }
+ }
+ /* 6.3.5.6 - 4) */
+ return regionBitmap;
+ }
+
+ private int decodeSLTP() throws IOException {
+ template.setIndex(cx);
+ return arithDecoder.decode(cx);
+ }
+
+ private Bitmap getGrReference() throws IntegerMaxValueException, InvalidHeaderValueException, IOException {
+ final SegmentHeader[] segments = segmentHeader.getRtSegments();
+ final Region region = (Region) segments[0].getSegmentData();
+
+ return region.getRegionBitmap();
+ }
+
+ private void decodeOptimized(final int lineNumber, final int width, final int rowStride, final int refRowStride,
+ final int paddedWidth, final int deltaRefStride, final int lineOffset) throws IOException {
+
+ // Offset of the reference bitmap with respect to the bitmap being decoded
+ // For example: if referenceDY = -1, y is 1 HIGHER that currY
+ final int currentLine = lineNumber - referenceDY;
+ final int referenceByteIndex = referenceBitmap.getByteIndex(Math.max(0, -referenceDX), currentLine);
+
+ final int byteIndex = regionBitmap.getByteIndex(Math.max(0, referenceDX), lineNumber);
+
+ switch (templateID){
+ case 0 :
+ decodeTemplate(lineNumber, width, rowStride, refRowStride, paddedWidth, deltaRefStride, lineOffset, byteIndex,
+ currentLine, referenceByteIndex, T0);
+ break;
+ case 1 :
+ decodeTemplate(lineNumber, width, rowStride, refRowStride, paddedWidth, deltaRefStride, lineOffset, byteIndex,
+ currentLine, referenceByteIndex, T1);
+ break;
+ }
+
+ }
+
+ private void decodeTemplate(final int lineNumber, final int width, final int rowStride, final int refRowStride,
+ final int paddedWidth, final int deltaRefStride, final int lineOffset, int byteIndex, final int currentLine,
+ int refByteIndex, Template templateFormation) throws IOException {
+ short c1, c2, c3, c4, c5;
+
+ int w1, w2, w3, w4;
+ w1 = w2 = w3 = w4 = 0;
+
+ if (currentLine >= 1 && (currentLine - 1) < referenceBitmap.getHeight())
+ w1 = referenceBitmap.getByteAsInteger(refByteIndex - refRowStride);
+ if (currentLine >= 0 && currentLine < referenceBitmap.getHeight())
+ w2 = referenceBitmap.getByteAsInteger(refByteIndex);
+ if (currentLine >= -1 && currentLine + 1 < referenceBitmap.getHeight())
+ w3 = referenceBitmap.getByteAsInteger(refByteIndex + refRowStride);
+ refByteIndex++;
+
+ if (lineNumber >= 1) {
+ w4 = regionBitmap.getByteAsInteger(byteIndex - rowStride);
+ }
+ byteIndex++;
+
+ final int modReferenceDX = referenceDX % 8;
+ final int shiftOffset = 6 + modReferenceDX;
+ final int modRefByteIdx = refByteIndex % refRowStride;
+
+ if (shiftOffset >= 0) {
+ c1 = (short) ((shiftOffset >= 8 ? 0 : w1 >>> shiftOffset) & 0x07);
+ c2 = (short) ((shiftOffset >= 8 ? 0 : w2 >>> shiftOffset) & 0x07);
+ c3 = (short) ((shiftOffset >= 8 ? 0 : w3 >>> shiftOffset) & 0x07);
+ if (shiftOffset == 6 && modRefByteIdx > 1) {
+ if (currentLine >= 1 && (currentLine - 1) < referenceBitmap.getHeight()) {
+ c1 |= referenceBitmap.getByteAsInteger(refByteIndex - refRowStride - 2) << 2 & 0x04;
+ }
+ if (currentLine >= 0 && currentLine < referenceBitmap.getHeight()) {
+ c2 |= referenceBitmap.getByteAsInteger(refByteIndex - 2) << 2 & 0x04;
+ }
+ if (currentLine >= -1 && currentLine + 1 < referenceBitmap.getHeight()) {
+ c3 |= referenceBitmap.getByteAsInteger(refByteIndex + refRowStride - 2) << 2 & 0x04;
+ }
+ }
+ if (shiftOffset == 0) {
+ w1 = w2 = w3 = 0;
+ if (modRefByteIdx < refRowStride - 1) {
+ if (currentLine >= 1 && (currentLine - 1) < referenceBitmap.getHeight())
+ w1 = referenceBitmap.getByteAsInteger(refByteIndex - refRowStride);
+ if (currentLine >= 0 && currentLine < referenceBitmap.getHeight())
+ w2 = referenceBitmap.getByteAsInteger(refByteIndex);
+ if (currentLine >= -1 && currentLine + 1 < referenceBitmap.getHeight())
+ w3 = referenceBitmap.getByteAsInteger(refByteIndex + refRowStride);
+ }
+ refByteIndex++;
+ }
+ } else {
+ c1 = (short) ((w1 << 1) & 0x07);
+ c2 = (short) ((w2 << 1) & 0x07);
+ c3 = (short) ((w3 << 1) & 0x07);
+ w1 = w2 = w3 = 0;
+ if (modRefByteIdx < refRowStride - 1) {
+ if (currentLine >= 1 && (currentLine - 1) < referenceBitmap.getHeight())
+ w1 = referenceBitmap.getByteAsInteger(refByteIndex - refRowStride);
+ if (currentLine >= 0 && currentLine < referenceBitmap.getHeight())
+ w2 = referenceBitmap.getByteAsInteger(refByteIndex);
+ if (currentLine >= -1 && currentLine + 1 < referenceBitmap.getHeight())
+ w3 = referenceBitmap.getByteAsInteger(refByteIndex + refRowStride);
+ refByteIndex++;
+ }
+ c1 |= (short) ((w1 >>> 7) & 0x07);
+ c2 |= (short) ((w2 >>> 7) & 0x07);
+ c3 |= (short) ((w3 >>> 7) & 0x07);
+ }
+
+ c4 = (short) (w4 >>> 6);
+ c5 = 0;
+
+ final int modBitsToTrim = (2 - modReferenceDX) % 8;
+ w1 <<= modBitsToTrim;
+ w2 <<= modBitsToTrim;
+ w3 <<= modBitsToTrim;
+
+ w4 <<= 2;
+
+ for (int x = 0; x < width; x++) {
+ final int minorX = x & 0x07;
+
+ final short tval = templateFormation.form(c1, c2, c3, c4, c5);
+
+ if (override) {
+ cx.setIndex(overrideAtTemplate0(tval, x, lineNumber,
+ regionBitmap.getByte(regionBitmap.getByteIndex(x, lineNumber)), minorX));
+ } else {
+ cx.setIndex(tval);
+ }
+ final int bit = arithDecoder.decode(cx);
+ regionBitmap.setPixel(x, lineNumber, (byte) bit);
+
+ c1 = (short) (((c1 << 1) | 0x01 & (w1 >>> 7)) & 0x07);
+ c2 = (short) (((c2 << 1) | 0x01 & (w2 >>> 7)) & 0x07);
+ c3 = (short) (((c3 << 1) | 0x01 & (w3 >>> 7)) & 0x07);
+ c4 = (short) (((c4 << 1) | 0x01 & (w4 >>> 7)) & 0x07);
+ c5 = (short) bit;
+
+ if ((x - referenceDX) % 8 == 5) {
+ if (((x - referenceDX) / 8) + 1 >= referenceBitmap.getRowStride()) {
+ w1 = w2 = w3 = 0;
+ } else {
+ if (currentLine >= 1 && (currentLine - 1 < referenceBitmap.getHeight())) {
+ w1 = referenceBitmap.getByteAsInteger(refByteIndex - refRowStride);
+ } else {
+ w1 = 0;
+ }
+ if (currentLine >= 0 && currentLine < referenceBitmap.getHeight()) {
+ w2 = referenceBitmap.getByteAsInteger(refByteIndex);
+ } else {
+ w2 = 0;
+ }
+ if (currentLine >= -1 && (currentLine + 1) < referenceBitmap.getHeight()) {
+ w3 = referenceBitmap.getByteAsInteger(refByteIndex + refRowStride);
+ } else {
+ w3 = 0;
+ }
+ }
+ refByteIndex++;
+ } else {
+ w1 <<= 1;
+ w2 <<= 1;
+ w3 <<= 1;
+ }
+
+ if (minorX == 5 && lineNumber >= 1) {
+ if ((x >> 3) + 1 >= regionBitmap.getRowStride()) {
+ w4 = 0;
+ } else {
+ w4 = regionBitmap.getByteAsInteger(byteIndex - rowStride);
+ }
+ byteIndex++;
+ } else {
+ w4 <<= 1;
+ }
+
+ }
+ }
+
+ private void updateOverride() {
+ if (grAtX == null || grAtY == null) {
+ log.info("AT pixels not set");
+ return;
+ }
+
+ if (grAtX.length != grAtY.length) {
+ log.info("AT pixel inconsistent");
+ return;
+ }
+
+ grAtOverride = new boolean[grAtX.length];
+
+ switch (templateID){
+ case 0 :
+ if (grAtX[0] != -1 && grAtY[0] != -1) {
+ grAtOverride[0] = true;
+ override = true;
+ }
+
+ if (grAtX[1] != -1 && grAtY[1] != -1) {
+ grAtOverride[1] = true;
+ override = true;
+ }
+ break;
+ case 1 :
+ override = false;
+ break;
+ }
+ }
+
+ private void decodeTypicalPredictedLine(final int lineNumber, final int width, final int rowStride,
+ final int refRowStride, final int paddedWidth, final int deltaRefStride) throws IOException {
+
+ // Offset of the reference bitmap with respect to the bitmap being
+ // decoded
+ // For example: if grReferenceDY = -1, y is 1 HIGHER that currY
+ final int currentLine = lineNumber - referenceDY;
+ final int refByteIndex = referenceBitmap.getByteIndex(0, currentLine);
+
+ final int byteIndex = regionBitmap.getByteIndex(0, lineNumber);
+
+ switch (templateID){
+ case 0 :
+ decodeTypicalPredictedLineTemplate0(lineNumber, width, rowStride, refRowStride, paddedWidth, deltaRefStride,
+ byteIndex, currentLine, refByteIndex);
+ break;
+ case 1 :
+ decodeTypicalPredictedLineTemplate1(lineNumber, width, rowStride, refRowStride, paddedWidth, deltaRefStride,
+ byteIndex, currentLine, refByteIndex);
+ break;
+ }
+ }
+
+ private void decodeTypicalPredictedLineTemplate0(final int lineNumber, final int width, final int rowStride,
+ final int refRowStride, final int paddedWidth, final int deltaRefStride, int byteIndex, final int currentLine,
+ int refByteIndex) throws IOException {
+ int context;
+ int overriddenContext;
+
+ int previousLine;
+ int previousReferenceLine;
+ int currentReferenceLine;
+ int nextReferenceLine;
+
+ if (lineNumber > 0) {
+ previousLine = regionBitmap.getByteAsInteger(byteIndex - rowStride);
+ } else {
+ previousLine = 0;
+ }
+
+ if (currentLine > 0 && currentLine <= referenceBitmap.getHeight()) {
+ previousReferenceLine = referenceBitmap.getByteAsInteger(refByteIndex - refRowStride + deltaRefStride) << 4;
+ } else {
+ previousReferenceLine = 0;
+ }
+
+ if (currentLine >= 0 && currentLine < referenceBitmap.getHeight()) {
+ currentReferenceLine = referenceBitmap.getByteAsInteger(refByteIndex + deltaRefStride) << 1;
+ } else {
+ currentReferenceLine = 0;
+ }
+
+ if (currentLine > -2 && currentLine < (referenceBitmap.getHeight() - 1)) {
+ nextReferenceLine = referenceBitmap.getByteAsInteger(refByteIndex + refRowStride + deltaRefStride);
+ } else {
+ nextReferenceLine = 0;
+ }
+
+ context = ((previousLine >> 5) & 0x6) | ((nextReferenceLine >> 2) & 0x30) | (currentReferenceLine & 0x180)
+ | (previousReferenceLine & 0xc00);
+
+ int nextByte;
+ for (int x = 0; x < paddedWidth; x = nextByte) {
+ byte result = 0;
+ nextByte = x + 8;
+ final int minorWidth = width - x > 8 ? 8 : width - x;
+ final boolean readNextByte = nextByte < width;
+ final boolean refReadNextByte = nextByte < referenceBitmap.getWidth();
+
+ final int yOffset = deltaRefStride + 1;
+
+ if (lineNumber > 0) {
+ previousLine = (previousLine << 8)
+ | (readNextByte ? regionBitmap.getByteAsInteger(byteIndex - rowStride + 1) : 0);
+ }
+
+ if (currentLine > 0 && currentLine <= referenceBitmap.getHeight()) {
+ previousReferenceLine = (previousReferenceLine << 8)
+ | (refReadNextByte ? referenceBitmap.getByteAsInteger(refByteIndex - refRowStride + yOffset) << 4 : 0);
+ }
+
+ if (currentLine >= 0 && currentLine < referenceBitmap.getHeight()) {
+ currentReferenceLine = (currentReferenceLine << 8)
+ | (refReadNextByte ? referenceBitmap.getByteAsInteger(refByteIndex + yOffset) << 1 : 0);
+ }
+
+ if (currentLine > -2 && currentLine < (referenceBitmap.getHeight() - 1)) {
+ nextReferenceLine = (nextReferenceLine << 8)
+ | (refReadNextByte ? referenceBitmap.getByteAsInteger(refByteIndex + refRowStride + yOffset) : 0);
+ }
+
+ for (int minorX = 0; minorX < minorWidth; minorX++) {
+ boolean isPixelTypicalPredicted = false;
+ int bit = 0;
+
+ // i)
+ final int bitmapValue = (context >> 4) & 0x1FF;
+
+ if (bitmapValue == 0x1ff) {
+ isPixelTypicalPredicted = true;
+ bit = 1;
+ } else if (bitmapValue == 0x00) {
+ isPixelTypicalPredicted = true;
+ bit = 0;
+ }
+
+ if (!isPixelTypicalPredicted) {
+ // iii) - is like 3 c) but for one pixel only
+
+ if (override) {
+ overriddenContext = overrideAtTemplate0(context, x + minorX, lineNumber, result, minorX);
+ cx.setIndex(overriddenContext);
+ } else {
+ cx.setIndex(context);
+ }
+ bit = arithDecoder.decode(cx);
+ }
+
+ final int toShift = 7 - minorX;
+ result |= bit << toShift;
+
+ context = ((context & 0xdb6) << 1) | bit | ((previousLine >> toShift + 5) & 0x002)
+ | ((nextReferenceLine >> toShift + 2) & 0x010) | ((currentReferenceLine >> toShift) & 0x080)
+ | ((previousReferenceLine >> toShift) & 0x400);
+ }
+ regionBitmap.setByte(byteIndex++, result);
+ refByteIndex++;
+ }
+ }
+
+ private void decodeTypicalPredictedLineTemplate1(final int lineNumber, final int width, final int rowStride,
+ final int refRowStride, final int paddedWidth, final int deltaRefStride, int byteIndex, final int currentLine,
+ int refByteIndex) throws IOException {
+ int context;
+ int grReferenceValue;
+
+ int previousLine;
+ int previousReferenceLine;
+ int currentReferenceLine;
+ int nextReferenceLine;
+
+ if (lineNumber > 0) {
+ previousLine = regionBitmap.getByteAsInteger(byteIndex - rowStride);
+ } else {
+ previousLine = 0;
+ }
+
+ if (currentLine > 0 && currentLine <= referenceBitmap.getHeight()) {
+ previousReferenceLine = referenceBitmap.getByteAsInteger(byteIndex - refRowStride + deltaRefStride) << 2;
+ } else {
+ previousReferenceLine = 0;
+ }
+
+ if (currentLine >= 0 && currentLine < referenceBitmap.getHeight()) {
+ currentReferenceLine = referenceBitmap.getByteAsInteger(byteIndex + deltaRefStride);
+ } else {
+ currentReferenceLine = 0;
+ }
+
+ if (currentLine > -2 && currentLine < (referenceBitmap.getHeight() - 1)) {
+ nextReferenceLine = referenceBitmap.getByteAsInteger(byteIndex + refRowStride + deltaRefStride);
+ } else {
+ nextReferenceLine = 0;
+ }
+
+ context = ((previousLine >> 5) & 0x6) | ((nextReferenceLine >> 2) & 0x30) | (currentReferenceLine & 0xc0)
+ | (previousReferenceLine & 0x200);
+
+ grReferenceValue = ((nextReferenceLine >> 2) & 0x70) | (currentReferenceLine & 0xc0)
+ | (previousReferenceLine & 0x700);
+
+ int nextByte;
+ for (int x = 0; x < paddedWidth; x = nextByte) {
+ byte result = 0;
+ nextByte = x + 8;
+ final int minorWidth = width - x > 8 ? 8 : width - x;
+ final boolean readNextByte = nextByte < width;
+ final boolean refReadNextByte = nextByte < referenceBitmap.getWidth();
+
+ final int yOffset = deltaRefStride + 1;
+
+ if (lineNumber > 0) {
+ previousLine = (previousLine << 8)
+ | (readNextByte ? regionBitmap.getByteAsInteger(byteIndex - rowStride + 1) : 0);
+ }
+
+ if (currentLine > 0 && currentLine <= referenceBitmap.getHeight()) {
+ previousReferenceLine = (previousReferenceLine << 8)
+ | (refReadNextByte ? referenceBitmap.getByteAsInteger(refByteIndex - refRowStride + yOffset) << 2 : 0);
+ }
+
+ if (currentLine >= 0 && currentLine < referenceBitmap.getHeight()) {
+ currentReferenceLine = (currentReferenceLine << 8)
+ | (refReadNextByte ? referenceBitmap.getByteAsInteger(refByteIndex + yOffset) : 0);
+ }
+
+ if (currentLine > -2 && currentLine < (referenceBitmap.getHeight() - 1)) {
+ nextReferenceLine = (nextReferenceLine << 8)
+ | (refReadNextByte ? referenceBitmap.getByteAsInteger(refByteIndex + refRowStride + yOffset) : 0);
+ }
+
+ for (int minorX = 0; minorX < minorWidth; minorX++) {
+ int bit = 0;
+
+ // i)
+ final int bitmapValue = (grReferenceValue >> 4) & 0x1ff;
+
+ if (bitmapValue == 0x1ff) {
+ bit = 1;
+ } else if (bitmapValue == 0x00) {
+ bit = 0;
+ } else {
+ cx.setIndex(context);
+ bit = arithDecoder.decode(cx);
+ }
+
+ final int toShift = 7 - minorX;
+ result |= bit << toShift;
+
+ context = ((context & 0x0d6) << 1) | bit | ((previousLine >> toShift + 5) & 0x002)
+ | ((nextReferenceLine >> toShift + 2) & 0x010) | ((currentReferenceLine >> toShift) & 0x040)
+ | ((previousReferenceLine >> toShift) & 0x200);
+
+ grReferenceValue = ((grReferenceValue & 0x0db) << 1) | ((nextReferenceLine >> toShift + 2) & 0x010)
+ | ((currentReferenceLine >> toShift) & 0x080) | ((previousReferenceLine >> toShift) & 0x400);
+ }
+ regionBitmap.setByte(byteIndex++, result);
+ refByteIndex++;
+ }
+ }
+
+ private int overrideAtTemplate0(int context, final int x, final int y, final int result, final int minorX)
+ throws IOException {
+ if (grAtOverride[0]) {
+ context &= 0xfff7;
+ if (grAtY[0] == 0 && grAtX[0] >= -minorX) {
+ context |= (result >> (7 - (minorX + grAtX[0])) & 0x1) << 3;
+ } else {
+ context |= getPixel(regionBitmap, x + grAtX[0], y + grAtY[0]) << 3;
+ }
+ }
+
+ if (grAtOverride[1]) {
+ context &= 0xefff;
+ if (grAtY[1] == 0 && grAtX[1] >= -minorX) {
+ context |= (result >> (7 - (minorX + grAtX[1])) & 0x1) << 12;
+ } else {
+ context |= getPixel(referenceBitmap, x + grAtX[1] + referenceDX, y + grAtY[1] + referenceDY) << 12;
+ }
+ }
+ return context;
+ }
+
+ private byte getPixel(final Bitmap b, final int x, final int y) throws IOException {
+ if (x < 0 || x >= b.getWidth()) {
+ return 0;
+ }
+ if (y < 0 || y >= b.getHeight()) {
+ return 0;
+ }
+
+ return b.getPixel(x, y);
+ }
+
+ public void init(final SegmentHeader header, final SubInputStream sis) throws IOException {
+ this.segmentHeader = header;
+ this.subInputStream = sis;
+ this.regionInfo = new RegionSegmentInformation(subInputStream);
+ parseHeader();
+ }
+
+ protected void setParameters(final CX cx, final ArithmeticDecoder arithmeticDecoder, final short grTemplate,
+ final int regionWidth, final int regionHeight, final Bitmap grReference, final int grReferenceDX,
+ final int grReferenceDY, final boolean isTPGRon, final short[] grAtX, final short[] grAtY) {
+
+ if (null != cx) {
+ this.cx = cx;
+ }
+
+ if (null != arithmeticDecoder) {
+ this.arithDecoder = arithmeticDecoder;
+ }
+
+ this.templateID = grTemplate;
+
+ this.regionInfo.setBitmapWidth(regionWidth);
+ this.regionInfo.setBitmapHeight(regionHeight);
+
+ this.referenceBitmap = grReference;
+ this.referenceDX = grReferenceDX;
+ this.referenceDY = grReferenceDY;
+
+ this.isTPGROn = isTPGRon;
+
+ this.grAtX = grAtX;
+ this.grAtY = grAtY;
+
+ this.regionBitmap = null;
+ }
+
+ public RegionSegmentInformation getRegionInfo() {
+ return regionInfo;
+ }
+}