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;
+  }
+}