3 package com.pixelmed.codec.jpeg;
5 import java.awt.Rectangle;
8 import java.io.ByteArrayOutputStream;
9 import java.io.IOException;
10 import java.io.OutputStream;
12 import java.util.HashMap;
14 import java.util.Vector;
25 private static final String identString =
"@(#) $Header: /userland/cvs/codec/com/pixelmed/codec/jpeg/EntropyCodedSegment.java,v 1.24 2016/01/16 13:30:09 dclunie Exp $";
27 private boolean copying;
28 private boolean decompressing;
32 private boolean isHuffman;
33 private boolean isDCT;
34 private boolean isLossless;
36 private ByteArrayOutputStream copiedBytes;
40 private final Map<String,HuffmanTable> htByClassAndIdentifer;
41 private final Map<String,QuantizationTable> qtByIdentifer;
43 private final int nComponents;
44 private final int[] DCEntropyCodingTableSelector;
45 private final int[] ACEntropyCodingTableSelector;
46 private final int[] HorizontalSamplingFactor;
47 private final int[] VerticalSamplingFactor;
49 private final int maxHorizontalSamplingFactor;
50 private final int maxVerticalSamplingFactor;
52 private final int nMCUHorizontally;
54 private final Vector<Shape> redactionShapes;
57 private final int predictorForFirstSample;
58 private final int[] predictorForComponent;
59 private final int predictorSelectionValue;
62 private int[] rowNumberAtBeginningOfRestartInterval;
63 private final int[] rowLength;
64 private final int[] currentRowNumber;
65 private final int[] positionWithinRow;
66 private final int[][] previousReconstructedRow;
67 private final int[][] currentReconstructedRow;
71 private byte[] bytesToDecompress;
72 private int availableBytes;
73 private int byteIndex;
75 private int currentByte;
76 private int currentBits;
79 private static final int[] extractBitFromByteMask = { 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01 };
81 private final void getEnoughBits(
int wantBits)
throws Exception {
82 while (haveBits < wantBits) {
84 if (byteIndex < availableBytes) {
85 currentByte=bytesToDecompress[byteIndex++];
90 throw new Exception(
"No more bits (having decompressed "+byteIndex+
" dec bytes)");
93 int newBit = (currentByte & extractBitFromByteMask[bitIndex++]) == 0 ? 0 : 1;
94 currentBits = (currentBits << 1) + newBit;
100 private int writeByte;
101 private int writeBitIndex;
103 private final void initializeWriteBits() {
104 copiedBytes =
new ByteArrayOutputStream();
109 private final void flushWriteBits() {
110 if (writeBitIndex > 0) {
112 while (writeBitIndex < 8) {
113 writeByte = writeByte | extractBitFromByteMask[writeBitIndex];
116 copiedBytes.write(writeByte);
117 if ((writeByte&0xff) == 0xff) {
118 copiedBytes.write(0);
126 private final void writeBits(
int bits,
int nBits) {
129 for (
int i=nBits-1; i>=0; --i) {
130 final int whichBitMask = 1 << i;
131 final int bitIsSet = bits & whichBitMask;
134 writeByte = writeByte | extractBitFromByteMask[writeBitIndex];
137 if (writeBitIndex > 7) {
139 copiedBytes.write(writeByte);
140 if ((writeByte&0xff) == 0xff) {
141 copiedBytes.write(0);
163 private final int decode()
throws Exception {
164 final int[] MINCODE = usingTable.
getMINCODE();
165 final int[] MAXCODE = usingTable.
getMAXCODE();
166 final int[] VALPTR = usingTable.
getVALPTR();
167 final int[] HUFFVAL = usingTable.
getHUFFVAL();
171 int CODE = currentBits;
172 while (I<MAXCODE.length && CODE > MAXCODE[I]) {
183 if (I<MAXCODE.length) {
186 J = J + CODE - MINCODE[I];
198 if (copying) { writeBits(currentBits,haveBits); }
204 private final int getValueOfRequestedLength(
int wantBits)
throws Exception {
205 getEnoughBits(wantBits);
206 final int value = currentBits;
208 if (copying) { writeBits(currentBits,haveBits); }
215 private int[] dcSignBitMask = { 0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000 };
216 private int[] maxAmplitude = { 0,0x02-1,0x04-1,0x08-1,0x10-1,0x20-1,0x40-1,0x80-1,0x100-1,0x200-1,0x400-1,0x800-1,0x1000-1,0x2000-1,0x4000-1,0x8000-1 };
218 private final int convertSignAndAmplitudeBitsToValue(
int value,
int length)
throws Exception {
222 if ((value & dcSignBitMask[length]) == 0) {
224 value = value - maxAmplitude[length];
230 private final void writeEntropyCodedAllZeroACCoefficients() {
251 public EntropyCodedSegment(
MarkerSegmentSOS sos,
MarkerSegmentSOF sof,Map<String,HuffmanTable> htByClassAndIdentifer,Map<String,QuantizationTable> qtByIdentifer,
int nMCUHorizontally,Vector<Shape> redactionShapes,
boolean copying,
boolean dumping,
boolean decompressing,
Parse.
DecompressedOutput decompressedOutput)
throws Exception {
254 this.htByClassAndIdentifer = htByClassAndIdentifer;
255 this.qtByIdentifer = qtByIdentifer;
256 this.nMCUHorizontally = nMCUHorizontally;
257 this.redactionShapes = redactionShapes;
258 this.copying = copying;
260 this.decompressing = decompressing;
261 this.decompressedOutputPerComponent = decompressedOutput == null ? null : decompressedOutput.getDecompressedOutputPerComponent();
276 maxHorizontalSamplingFactor = max(HorizontalSamplingFactor);
278 maxVerticalSamplingFactor = max(VerticalSamplingFactor);
281 if (isLossless && decompressing) {
286 predictorForComponent =
new int[nComponents];
290 rowLength =
new int[nComponents];
291 currentRowNumber =
new int[nComponents];
292 positionWithinRow =
new int[nComponents];
293 rowNumberAtBeginningOfRestartInterval =
new int[nComponents];
294 previousReconstructedRow =
new int[nComponents][];
295 currentReconstructedRow =
new int[nComponents][];
296 for (
int c=0; c<nComponents; ++c) {
300 currentRowNumber[c] = 0;
301 positionWithinRow[c] = 0;
302 rowNumberAtBeginningOfRestartInterval[c] = 0;
303 previousReconstructedRow[c] =
new int[rowLength[c]];
304 currentReconstructedRow[c] =
new int[rowLength[c]];
308 predictorForFirstSample = 0;
309 predictorForComponent = null;
310 predictorSelectionValue = 0;
312 currentRowNumber = null;
313 positionWithinRow = null;
314 rowNumberAtBeginningOfRestartInterval = null;
315 previousReconstructedRow = null;
316 currentReconstructedRow = null;
319 if (dumping) dumpHuffmanTables();
323 private final int getOneLosslessValue(
int c,
int dcEntropyCodingTableSelector,
int colMCU,
int rowMCU)
throws Exception {
327 if (currentRowNumber[c] == rowNumberAtBeginningOfRestartInterval[c]) {
328 if (positionWithinRow[c] == 0) {
330 prediction = predictorForFirstSample;
334 prediction = currentReconstructedRow[c][positionWithinRow[c]-1];
337 else if (positionWithinRow[c] == 0) {
339 prediction = previousReconstructedRow[c][0];
342 switch(predictorSelectionValue) {
343 case 1: prediction = currentReconstructedRow[c][positionWithinRow[c]-1];
345 case 2: prediction = previousReconstructedRow[c][positionWithinRow[c]];
347 case 3: prediction = previousReconstructedRow[c][positionWithinRow[c]-1];
349 case 4: prediction = currentReconstructedRow[c][positionWithinRow[c]-1] + previousReconstructedRow[c][positionWithinRow[c]] - previousReconstructedRow[c][positionWithinRow[c]-1];
351 case 5: prediction = currentReconstructedRow[c][positionWithinRow[c]-1] + ((previousReconstructedRow[c][positionWithinRow[c]] - previousReconstructedRow[c][positionWithinRow[c]-1])>>1);
353 case 6: prediction = previousReconstructedRow[c][positionWithinRow[c]] + ((currentReconstructedRow[c][positionWithinRow[c]-1] - previousReconstructedRow[c][positionWithinRow[c]-1])>>1);
355 case 7: prediction = (currentReconstructedRow[c][positionWithinRow[c]-1] + previousReconstructedRow[c][positionWithinRow[c]])>>1;
358 throw new Exception(
"Unrecognized predictor selection value "+predictorSelectionValue);
364 usingTable = htByClassAndIdentifer.get(
"0+"+Integer.toString(dcEntropyCodingTableSelector));
366 final int ssss = decode();
372 else if (ssss == 16) {
376 final int dcBits = getValueOfRequestedLength(ssss);
377 dcValue = convertSignAndAmplitudeBitsToValue(dcBits,ssss);
381 int reconstructedValue = 0;
384 reconstructedValue = (dcValue + prediction) & 0x0000ffff;
388 currentReconstructedRow[c][positionWithinRow[c]] = reconstructedValue;
390 ++positionWithinRow[c];
391 if (positionWithinRow[c] >= rowLength[c]) {
393 positionWithinRow[c] = 0;
394 ++currentRowNumber[c];
395 int[] holdRow = previousReconstructedRow[c];
396 previousReconstructedRow[c] = currentReconstructedRow[c];
397 currentReconstructedRow[c] = holdRow;
401 return reconstructedValue;
405 private final void getOneDCTDataUnit(
int dcEntropyCodingTableSelector,
int acEntropyCodingTableSelector,
boolean redact)
throws Exception {
406 usingTable = htByClassAndIdentifer.get(
"0+"+Integer.toString(dcEntropyCodingTableSelector));
408 final int ssss = decode();
414 else if (ssss == 16) {
418 final int dcBits = getValueOfRequestedLength(ssss);
419 dcValue = convertSignAndAmplitudeBitsToValue(dcBits,ssss);
424 usingTable = htByClassAndIdentifer.get(
"1+"+Integer.toString(acEntropyCodingTableSelector));
426 final boolean restoreCopying = copying;
427 if (redact && copying) {
429 writeEntropyCodedAllZeroACCoefficients();
435 final int rrrrssss = decode();
440 else if (rrrrssss == 0xF0) {
446 final int rrrr = rrrrssss >>> 4;
447 final int ssss = rrrrssss & 0x0f;
449 final int acBits = getValueOfRequestedLength(ssss);
450 final int acValue = convertSignAndAmplitudeBitsToValue(acBits,ssss);
457 copying = restoreCopying;
460 private final boolean redactionDecision(
int colMCU,
int rowMCU,
int thisHorizontalSamplingFactor,
int thisVerticalSamplingFactor,
int maxHorizontalSamplingFactor,
int maxVerticalSamplingFactor,
int h,
int v,Vector<Shape> redactionShapes) {
462 final int vMCUSize = 8 * maxVerticalSamplingFactor;
463 final int hMCUSize = 8 * maxHorizontalSamplingFactor;
466 final int hMCUOffset = colMCU * hMCUSize;
467 final int vMCUOffset = rowMCU * vMCUSize;
470 final int hBlockSize = 8 * maxHorizontalSamplingFactor/thisHorizontalSamplingFactor;
471 final int vBlockSize = 8 * maxVerticalSamplingFactor/thisVerticalSamplingFactor;
474 final int xBlock = hMCUOffset + h * hBlockSize;
475 final int yBlock = vMCUOffset + v * vBlockSize;
477 Rectangle blockShape =
new Rectangle(xBlock,yBlock,hBlockSize,vBlockSize);
480 boolean redact =
false;
481 if (redactionShapes != null) {
482 for (Shape redactionShape : redactionShapes) {
483 if (redactionShape.intersects(blockShape)) {
492 private final void writeDecompressedPixel(
int c,
int decompressedPixel)
throws IOException {
494 decompressedOutputPerComponent[c].
writeByte(decompressedPixel);
498 decompressedOutputPerComponent[c].
writeShort(decompressedPixel);
502 private final void getOneMinimumCodedUnit(
int nComponents,
int[] DCEntropyCodingTableSelector,
int[] ACEntropyCodingTableSelector,
int[] HorizontalSamplingFactor,
int[] VerticalSamplingFactor,
int maxHorizontalSamplingFactor,
int maxVerticalSamplingFactor,
int colMCU,
int rowMCU,Vector<Shape> redactionShapes)
throws Exception, IOException {
503 for (
int c=0; c<nComponents; ++c) {
505 for (
int v=0; v<VerticalSamplingFactor[c]; ++v) {
506 for (
int h=0; h<HorizontalSamplingFactor[c]; ++h) {
508 boolean redact = redactionDecision(colMCU,rowMCU,HorizontalSamplingFactor[c],VerticalSamplingFactor[c],maxHorizontalSamplingFactor,maxVerticalSamplingFactor,h,v,redactionShapes);
510 getOneDCTDataUnit(DCEntropyCodingTableSelector[c],ACEntropyCodingTableSelector[c],redact);
512 else if (isLossless) {
513 int decompressedPixel = getOneLosslessValue(c,DCEntropyCodingTableSelector[c],colMCU,rowMCU);
515 writeDecompressedPixel(c,decompressedPixel);
526 private static final int max(
int[] a) {
527 int m = Integer.MIN_VALUE;
544 public final byte[]
finish(byte[] bytesToDecompress,
int mcuCount,
int mcuOffset)
throws Exception, IOException {
545 this.bytesToDecompress = bytesToDecompress;
546 availableBytes = this.bytesToDecompress.length;
552 initializeWriteBits();
555 if (rowNumberAtBeginningOfRestartInterval != null) {
556 for (
int c=0; c<nComponents; ++c) {
558 rowNumberAtBeginningOfRestartInterval[c] = currentRowNumber[c];
563 for (
int mcu=0; mcu<mcuCount; ++mcu) {
564 int rowMCU = mcuOffset / nMCUHorizontally;
565 int colMCU = mcuOffset % nMCUHorizontally;
567 getOneMinimumCodedUnit(nComponents,DCEntropyCodingTableSelector,ACEntropyCodingTableSelector,HorizontalSamplingFactor,VerticalSamplingFactor,maxHorizontalSamplingFactor,maxVerticalSamplingFactor,colMCU,rowMCU,redactionShapes);
588 return copying ? copiedBytes.toByteArray() : null;
591 private final void dumpHuffmanTables() {
592 System.err.print(
"\n");
593 for (
HuffmanTable ht : htByClassAndIdentifer.values()) {
594 System.err.print(ht.toString());
598 private final void dumpQuantizationTables() {
599 System.err.print(
"\n");
601 System.err.print(qt.toString());
int [] getVerticalSamplingFactor()
EntropyCodedSegment(MarkerSegmentSOS sos, MarkerSegmentSOF sof, Map< String, HuffmanTable > htByClassAndIdentifer, Map< String, QuantizationTable > qtByIdentifer, int nMCUHorizontally, Vector< Shape > redactionShapes, boolean copying, boolean dumping, boolean decompressing, Parse.DecompressedOutput decompressedOutput)
int [] getACEntropyCodingTableSelector()
int getStartOfSpectralOrPredictorSelection()
static final boolean isDCT(int marker)
int getSuccessiveApproximationBitPositionLowOrPointTransform()
static final String getDescription(int marker)
int [] getHorizontalSamplingFactor()
static final boolean isHuffman(int marker)
int [] getDCEntropyCodingTableSelector()
static final boolean isLossless(int marker)
void writeByte(int b)
Writes the specified byte to this output.
static final String getAbbreviation(int marker)
int getNComponentsPerScan()
final byte [] finish(byte[] bytesToDecompress, int mcuCount, int mcuOffset)
void writeShort(int s)
Writes the specified short to this output.