blob: 086e4e703090c80f4d4f89a537966e9b881a63e4 [file]
/**
* 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.decoder;
import java.io.IOException;
import java.util.Objects;
import org.apache.pdfbox.jbig2.Bitmap;
import org.apache.pdfbox.jbig2.decoder.arithmetic.ArithmeticDecoder;
import org.apache.pdfbox.jbig2.decoder.arithmetic.CX;
/**
* Implements the JBIG2 Generic Refinement Region decoding procedure
* as defined in ITU-T T.88, §6.3 including the core decoding loop
* defined in §6.3.5.6.
*
* <p>This class is purely algorithmic. It has no knowledge of how its inputs
* are obtained and no dependency on segment headers or input streams.
* The entry point is the static {@link #decode decode()} method; callers
* supply the {@link ArithmeticDecoder} and {@link CX} context model directly,
* so they decide whether to create fresh instances or share existing ones:</p>
*
* <ul>
* <li><b>{@link org.apache.pdfbox.jbig2.segments.GenericRefinementRegion}</b> — creates its own
* {@code ArithmeticDecoder} and {@code CX}, then calls {@code decode()}.</li>
* <li><b>Symbol dictionary refinement (§6.5.8.2)</b> — passes the
* parent dictionary's shared {@code ArithmeticDecoder} and {@code CX}.</li>
* <li><b>Text region refinement (§6.4)</b> — passes the parent text
* region's shared {@code ArithmeticDecoder} and {@code CX}.</li>
* </ul>
*
* <p>This class cannot be instantiated by callers. The private constructor and
* the internal {@link #run} method exist solely to let the many private helper
* methods share state through fields rather than through long parameter lists.</p>
*/
public class GenericRefinementRegionDecodingProcedure
{
// -------------------------------------------------------------------------
// Template strategy — bit-context formation for each template type
// -------------------------------------------------------------------------
private static final int SLTP_CONTEXT_TEMPLATE0 = 0x100; // §6.3.5.6, Figure 14
private static final int SLTP_CONTEXT_TEMPLATE1 = 0x008; // §6.3.5.6, Figure 15
/**
* Encapsulates the template-specific operations: context bit formation
* ({@link #form}) and initial CX index selection ({@link #setIndex}).
*
* <p>The two concrete implementations ({@link #T0}, {@link #T1}) correspond
* to GRTEMPLATE values 0 and 1 as defined in §6.3.3 / Figures 14–15.</p>
*/
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)
{
cx.setIndex(SLTP_CONTEXT_TEMPLATE0);
}
}
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)
{
cx.setIndex(SLTP_CONTEXT_TEMPLATE1);
}
}
/** Singleton template 0 instance (stateless). */
public static final Template T0 = new Template0();
/** Singleton template 1 instance (stateless). */
public static final Template T1 = new Template1();
// -------------------------------------------------------------------------
// Decoder state — injected at construction
// -------------------------------------------------------------------------
private final ArithmeticDecoder arithDecoder;
private final CX cx;
// -------------------------------------------------------------------------
// Per-decode working state — reset by each decode() call
// -------------------------------------------------------------------------
private short templateID;
private Template template;
private Bitmap referenceBitmap;
private int referenceDX;
private int referenceDY;
private short[] grAtX;
private short[] grAtY;
private boolean override;
private boolean[] grAtOverride;
/** The bitmap being built; held as a field to avoid passing it everywhere. */
private Bitmap regionBitmap;
// -------------------------------------------------------------------------
// Construction — private; callers use the static decode() entry point
// -------------------------------------------------------------------------
private GenericRefinementRegionDecodingProcedure(final ArithmeticDecoder arithDecoder, final CX cx)
{
this.arithDecoder = arithDecoder;
this.cx = cx;
}
// -------------------------------------------------------------------------
// Public static API
// -------------------------------------------------------------------------
/**
* Executes the Generic Refinement Region decoding procedure (§6.3.5.6) and
* returns the decoded bitmap.
*
* <p>A short-lived instance is created internally so that the many private
* helper methods can share state through fields rather than through long
* parameter lists. No state survives the return of this method.</p>
*
* @param arithDecoder the arithmetic decoder — shared with the parent
* when called from a symbol dictionary or text region,
* or freshly created when called from a standalone segment;
* must not be {@code null}
* @param cx the context model — shared or fresh, as above;
* must not be {@code null}
* @param width decoded bitmap width (GRW); must be &gt; 0
* @param height decoded bitmap height (GRH); must be &gt; 0
* @param grTemplate template index: must be 0 or 1 (GRTEMPLATE)
* @param isTPGROn whether typical prediction is enabled (TPGRON)
* @param referenceBitmap the reference / base bitmap (GRREFERENCE);
* must not be {@code null}
* @param referenceDX horizontal offset of reference bitmap (GRREFERENCEDX)
* @param referenceDY vertical offset of reference bitmap (GRREFERENCEDY)
* @param grAtX AT pixel X offsets; required for {@code grTemplate == 0},
* must be a non-null array of length 2 in that case;
* ignored for {@code grTemplate == 1}
* @param grAtY AT pixel Y offsets; same requirements as {@code grAtX}
* @return the decoded bitmap
* @throws IllegalArgumentException if any parameter constraint above is violated
* @throws IOException if an underlying I/O operation fails
*/
public static Bitmap decode(final ArithmeticDecoder arithDecoder, final CX cx,
final int width, final int height, final short grTemplate,
final boolean isTPGROn, final Bitmap referenceBitmap,
final int referenceDX, final int referenceDY,
final short[] grAtX, final short[] grAtY) throws IOException
{
Objects.requireNonNull(arithDecoder, "arithDecoder must not be null");
Objects.requireNonNull(cx, "cx must not be null");
Objects.requireNonNull(referenceBitmap, "referenceBitmap must not be null");
if (grTemplate != 0 && grTemplate != 1)
{
throw new IllegalArgumentException(
"grTemplate must be 0 or 1, got: " + grTemplate);
}
if (grTemplate == 0 && (grAtX == null || grAtY == null
|| grAtX.length != 2 || grAtY.length != 2))
{
throw new IllegalArgumentException(
"grAtX and grAtY must be non-null arrays of length 2 for template 0");
}
if (width <= 0 || height <= 0)
{
throw new IllegalArgumentException(
"width and height must be > 0, got: " + width + "x" + height);
}
return new GenericRefinementRegionDecodingProcedure(arithDecoder, cx)
.run(width, height, grTemplate, isTPGROn, referenceBitmap,
referenceDX, referenceDY, grAtX, grAtY);
}
// -------------------------------------------------------------------------
// Private execution — all helpers share state through instance fields
// -------------------------------------------------------------------------
private Bitmap run(final int width, final int height, final short grTemplate,
final boolean isTPGROn, final Bitmap referenceBitmap,
final int referenceDX, final int referenceDY,
final short[] grAtX, final short[] grAtY) throws IOException
{
this.templateID = grTemplate;
this.template = (grTemplate == 0) ? T0 : T1;
this.referenceBitmap = referenceBitmap;
this.referenceDX = referenceDX;
this.referenceDY = referenceDY;
this.grAtX = grAtX;
this.grAtY = grAtY;
this.override = false;
this.grAtOverride = null;
/* 6.3.5.6 - 2) */
regionBitmap = new Bitmap(width, height);
if (templateID == 0)
{
// AT pixels are only relevant for template 0
updateOverride();
}
final int paddedWidth = (width + 7) & -8;
final int deltaRefStride = isTPGROn ? -referenceDY * referenceBitmap.getRowStride() : 0;
final int yOffset = deltaRefStride + 1;
/* 6.3.5.6 - 1) */
int isLineTypicalPredicted = 0; // LTP
/* 6.3.5.6 - 3) */
for (int y = 0; y < height; y++)
{
/* 6.3.5.6 - 3 b) */
if (isTPGROn)
{
isLineTypicalPredicted ^= decodeSLTP();
}
if (templateID == 1)
{
if (isLineTypicalPredicted == 0)
{
decodeLineExplicitT1(y, width);
}
else
{
decodeLineTPGRT1(y, width);
}
}
else
{
// existing template 0 paths unchanged
if (isLineTypicalPredicted == 0) {
decodeOptimized(y, width, regionBitmap.getRowStride(),
referenceBitmap.getRowStride(), paddedWidth, deltaRefStride, yOffset);
} else {
decodeTypicalPredictedLine(y, width, regionBitmap.getRowStride(),
referenceBitmap.getRowStride(), paddedWidth, deltaRefStride);
}
}
}
/* 6.3.5.6 - 4) */
return regionBitmap;
}
// -------------------------------------------------------------------------
// Pixel accessors — §6.3.5.2 out-of-bounds rule: all outside pixels = 0
// -------------------------------------------------------------------------
private int getPixelSafe(final Bitmap bitmap, final int x, final int y)
{
if (x < 0 || y < 0)
return 0;
if (x >= bitmap.getWidth() || y >= bitmap.getHeight())
return 0;
return bitmap.getPixel(x, y);
}
private int getReferenceBit(final int x, final int y)
{
return getPixelSafe(referenceBitmap, x - referenceDX, y - referenceDY);
}
private int getRegionBit(final int x, final int y)
{
return getPixelSafe(regionBitmap, x, y);
}
// -------------------------------------------------------------------------
// Template 1 context formation — §6.3.5.6, Figure 13
// Pixels gathered in reading order, GRREG before GRREFERENCE:
// GRREG: (x-2,y-1) (x-1,y-1) (x,y-1) (x-1,y) → bits 9-6
// GRREFERENCE:(x,y-1) (x-1,y) (x,y) (x+1,y) (x,y+1) (x+1,y+1) → bits 5-0
// Spec example: only ⊗=(x,y)=1 → CX="GR0000001000"=0x08 → bit 3 ✓
// -------------------------------------------------------------------------
private int buildContextT1(final int x, final int y)
{
return (getRegionBit(x - 1, y - 1) << 9)
| (getRegionBit(x, y - 1) << 8)
| (getRegionBit(x + 1, y - 1) << 7)
| (getRegionBit(x - 1, y ) << 6)
| (getReferenceBit(x , y - 1) << 5)
| (getReferenceBit(x - 1, y ) << 4)
| (getReferenceBit(x , y ) << 3)
| (getReferenceBit(x + 1, y ) << 2)
| (getReferenceBit(x, y + 1) << 1)
| (getReferenceBit(x + 1, y + 1));
}
// -------------------------------------------------------------------------
// Template 1 — explicit decode (LTP=0 path, §6.3.5.6 step 3c)
// -------------------------------------------------------------------------
private void decodeLineExplicitT1(final int y, final int width) throws IOException
{
for (int x = 0; x < width; x++)
{
cx.setIndex(buildContextT1(x, y));
regionBitmap.setPixel(x, y, (byte) arithDecoder.decode(cx));
}
}
// -------------------------------------------------------------------------
// Template 1 — typical prediction decode (LTP=1 path, §6.3.5.6 step 3d)
// TPGRPIX=1 when the 3×3 reference neighbourhood is uniform (§6.3.5.6 3d-i)
// -------------------------------------------------------------------------
private void decodeLineTPGRT1(final int y, final int width) throws IOException
{
for (int x = 0; x < width; x++)
{
final int center = getReferenceBit(x, y);
boolean uniform = true;
outer:
for (int dy = -1; dy <= 1; dy++)
{
for (int dx = -1; dx <= 1; dx++)
{
if (getReferenceBit(x + dx, y + dy) != center)
{
uniform = false;
break outer;
}
}
}
final int bit;
if (uniform)
{
bit = center;
}
else
{
cx.setIndex(buildContextT1(x, y));
bit = arithDecoder.decode(cx);
}
regionBitmap.setPixel(x, y, (byte) bit);
}
}
// -------------------------------------------------------------------------
// Private decoding helpers (§6.3.5.6 sub-steps)
// -------------------------------------------------------------------------
private int decodeSLTP() throws IOException
{
template.setIndex(cx);
return arithDecoder.decode(cx);
}
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 than 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;
default:
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)
{
return;
}
if (grAtX.length != grAtY.length)
{
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;
default:
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 than 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;
default:
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(refByteIndex - refRowStride + deltaRefStride) << 2;
}
else
{
previousReferenceLine = 0;
}
if (currentLine >= 0 && currentLine < referenceBitmap.getHeight())
{
currentReferenceLine = referenceBitmap.getByteAsInteger(refByteIndex + deltaRefStride);
}
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 & 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;
// i)
final int bitmapValue = (grReferenceValue >> 4) & 0x1ff;
switch (bitmapValue)
{
case 0x1ff:
bit = 1;
break;
case 0x00:
bit = 0;
break;
default:
cx.setIndex(context);
bit = arithDecoder.decode(cx);
break;
}
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)
{
if (grAtOverride[0])
{
context &= 0xfff7;
if (grAtY[0] == 0 && grAtX[0] >= -minorX)
{
context |= (result >> (7 - (minorX + grAtX[0])) & 0x1) << 3;
}
else
{
context |= getPixelSafe(regionBitmap, x + grAtX[0], y + grAtY[0]) << 3;
}
}
if (grAtOverride[1])
{
context &= 0xefff;
// 6.3.5.3
// The AT pixel RA2 can be located anywhere in the range (–128, –128) to (127, 127)
// in the reference bitmap. Make sure that we do use the reference bitmap.
context |= getPixelSafe(referenceBitmap, x + grAtX[1] + referenceDX,
y + grAtY[1] + referenceDY) << 12;
}
return context;
}
}