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 void encode(
int VALUE) {
206 final int[] EFUFCO = usingTable.
getEFUFCO();
207 final int[] EFUFSI = usingTable.
getEFUFSI();
208 int CODE = EFUFCO[VALUE];
209 int size = EFUFSI[VALUE];
211 writeBits(CODE,size);
214 private final int getValueOfRequestedLength(
int wantBits)
throws Exception {
215 getEnoughBits(wantBits);
216 final int value = currentBits;
218 if (copying) { writeBits(currentBits,haveBits); }
225 private int[] dcSignBitMask = { 0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000 };
226 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 };
228 private final int convertSignAndAmplitudeBitsToValue(
int value,
int length)
throws Exception {
232 if ((value & dcSignBitMask[length]) == 0) {
234 value = value - maxAmplitude[length];
240 private final int getNumberOfSignBits(
int value) {
252 private final int getBits(
int value,
int ssss) {
259 bits = value & maxAmplitude[ssss];
265 private final void writeEntropyCodedAllZeroACCoefficients() {
286 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 {
289 this.htByClassAndIdentifer = htByClassAndIdentifer;
290 this.qtByIdentifer = qtByIdentifer;
291 this.nMCUHorizontally = nMCUHorizontally;
292 this.redactionShapes = redactionShapes;
293 this.copying = copying;
295 this.decompressing = decompressing;
296 this.decompressedOutputPerComponent = decompressedOutput ==
null ? null : decompressedOutput.getDecompressedOutputPerComponent();
311 maxHorizontalSamplingFactor = max(HorizontalSamplingFactor);
313 maxVerticalSamplingFactor = max(VerticalSamplingFactor);
316 if (isLossless && decompressing) {
321 predictorForComponent =
new int[nComponents];
325 rowLength =
new int[nComponents];
326 currentRowNumber =
new int[nComponents];
327 positionWithinRow =
new int[nComponents];
328 rowNumberAtBeginningOfRestartInterval =
new int[nComponents];
329 previousReconstructedRow =
new int[nComponents][];
330 currentReconstructedRow =
new int[nComponents][];
331 for (
int c=0; c<nComponents; ++c) {
335 currentRowNumber[c] = 0;
336 positionWithinRow[c] = 0;
337 rowNumberAtBeginningOfRestartInterval[c] = 0;
338 previousReconstructedRow[c] =
new int[rowLength[c]];
339 currentReconstructedRow[c] =
new int[rowLength[c]];
343 predictorForFirstSample = 0;
344 predictorForComponent =
null;
345 predictorSelectionValue = 0;
347 currentRowNumber =
null;
348 positionWithinRow =
null;
349 rowNumberAtBeginningOfRestartInterval =
null;
350 previousReconstructedRow =
null;
351 currentReconstructedRow =
null;
354 if (dumping) dumpHuffmanTables();
358 private final int getOneLosslessValue(
int c,
int dcEntropyCodingTableSelector,
int colMCU,
int rowMCU)
throws Exception {
362 if (currentRowNumber[c] == rowNumberAtBeginningOfRestartInterval[c]) {
363 if (positionWithinRow[c] == 0) {
365 prediction = predictorForFirstSample;
369 prediction = currentReconstructedRow[c][positionWithinRow[c]-1];
372 else if (positionWithinRow[c] == 0) {
374 prediction = previousReconstructedRow[c][0];
377 switch(predictorSelectionValue) {
378 case 1: prediction = currentReconstructedRow[c][positionWithinRow[c]-1];
380 case 2: prediction = previousReconstructedRow[c][positionWithinRow[c]];
382 case 3: prediction = previousReconstructedRow[c][positionWithinRow[c]-1];
384 case 4: prediction = currentReconstructedRow[c][positionWithinRow[c]-1] + previousReconstructedRow[c][positionWithinRow[c]] - previousReconstructedRow[c][positionWithinRow[c]-1];
386 case 5: prediction = currentReconstructedRow[c][positionWithinRow[c]-1] + ((previousReconstructedRow[c][positionWithinRow[c]] - previousReconstructedRow[c][positionWithinRow[c]-1])>>1);
388 case 6: prediction = previousReconstructedRow[c][positionWithinRow[c]] + ((currentReconstructedRow[c][positionWithinRow[c]-1] - previousReconstructedRow[c][positionWithinRow[c]-1])>>1);
390 case 7: prediction = (currentReconstructedRow[c][positionWithinRow[c]-1] + previousReconstructedRow[c][positionWithinRow[c]])>>1;
393 throw new Exception(
"Unrecognized predictor selection value "+predictorSelectionValue);
399 usingTable = htByClassAndIdentifer.get(
"0+"+Integer.toString(dcEntropyCodingTableSelector));
401 final int ssss = decode();
407 else if (ssss == 16) {
411 final int dcBits = getValueOfRequestedLength(ssss);
412 dcValue = convertSignAndAmplitudeBitsToValue(dcBits,ssss);
416 int reconstructedValue = 0;
419 reconstructedValue = (dcValue + prediction) & 0x0000ffff;
423 currentReconstructedRow[c][positionWithinRow[c]] = reconstructedValue;
425 ++positionWithinRow[c];
426 if (positionWithinRow[c] >= rowLength[c]) {
428 positionWithinRow[c] = 0;
429 ++currentRowNumber[c];
430 int[] holdRow = previousReconstructedRow[c];
431 previousReconstructedRow[c] = currentReconstructedRow[c];
432 currentReconstructedRow[c] = holdRow;
436 return reconstructedValue;
441 private final int getOneDCTDataUnit(
int dcEntropyCodingTableSelector,
int acEntropyCodingTableSelector,
boolean redact,
int accumulatedDCDifferenceDuringRedaction)
throws Exception {
442 usingTable = htByClassAndIdentifer.get(
"0+"+Integer.toString(dcEntropyCodingTableSelector));
444 final boolean wasCopying = copying;
447 final int ssss = decode();
454 else if (ssss == 16) {
458 dcBits = getValueOfRequestedLength(ssss);
459 dcDIFF = convertSignAndAmplitudeBitsToValue(dcBits,ssss);
464 System.err.println(
"Redacting this DCTDataUnit - to accumulatedDCDifferenceDuringRedaction "+accumulatedDCDifferenceDuringRedaction+
" so far, adding this redacted dcDIFF "+dcDIFF);
465 accumulatedDCDifferenceDuringRedaction += dcDIFF;
469 System.err.println(
"Not redacting this DCTDataUnit - applying accumulatedDCDifferenceDuringRedaction "+accumulatedDCDifferenceDuringRedaction+
" to this non-redacted dcDIFF "+dcDIFF);
470 dcDIFF += accumulatedDCDifferenceDuringRedaction;
471 accumulatedDCDifferenceDuringRedaction = 0;
477 final int newSSSS = getNumberOfSignBits(dcDIFF);
478 final int newDCBits = getBits(dcDIFF,newSSSS);
480 System.err.println(
"For DC value "+dcDIFF+
" dec (0x"+Integer.toHexString(dcDIFF)+
") SSSS was "+ssss+
" is "+newSSSS+
", DCBits was "+dcBits+
" dec (0x"+Integer.toHexString(dcBits)+
") is "+newDCBits+
" dec (0x"+Integer.toHexString(newDCBits)+
")");
484 if (ssss > 0 && ssss < 16) {
485 writeBits(newDCBits,ssss);
490 copying = wasCopying;
493 usingTable = htByClassAndIdentifer.get(
"1+"+Integer.toString(acEntropyCodingTableSelector));
495 final boolean wasCopying = copying;
496 if (redact && copying) {
498 writeEntropyCodedAllZeroACCoefficients();
504 final int rrrrssss = decode();
509 else if (rrrrssss == 0xF0) {
515 final int rrrr = rrrrssss >>> 4;
516 final int ssss = rrrrssss & 0x0f;
518 final int acBits = getValueOfRequestedLength(ssss);
519 final int acValue = convertSignAndAmplitudeBitsToValue(acBits,ssss);
526 copying = wasCopying;
529 return accumulatedDCDifferenceDuringRedaction;
532 private final boolean redactionDecision(
int colMCU,
int rowMCU,
int thisHorizontalSamplingFactor,
int thisVerticalSamplingFactor,
int maxHorizontalSamplingFactor,
int maxVerticalSamplingFactor,
int h,
int v,Vector<Shape> redactionShapes) {
534 final int vMCUSize = 8 * maxVerticalSamplingFactor;
535 final int hMCUSize = 8 * maxHorizontalSamplingFactor;
538 final int hMCUOffset = colMCU * hMCUSize;
539 final int vMCUOffset = rowMCU * vMCUSize;
542 final int hBlockSize = 8 * maxHorizontalSamplingFactor/thisHorizontalSamplingFactor;
543 final int vBlockSize = 8 * maxVerticalSamplingFactor/thisVerticalSamplingFactor;
546 final int xBlock = hMCUOffset + h * hBlockSize;
547 final int yBlock = vMCUOffset + v * vBlockSize;
549 Rectangle blockShape =
new Rectangle(xBlock,yBlock,hBlockSize,vBlockSize);
552 boolean redact =
false;
553 if (redactionShapes !=
null) {
554 for (Shape redactionShape : redactionShapes) {
555 if (redactionShape.intersects(blockShape)) {
564 private final void writeDecompressedPixel(
int c,
int decompressedPixel)
throws IOException {
566 decompressedOutputPerComponent[c].
writeByte(decompressedPixel);
570 decompressedOutputPerComponent[c].
writeShort(decompressedPixel);
574 private final void getOneMinimumCodedUnit(
int nComponents,
int[] DCEntropyCodingTableSelector,
int[] ACEntropyCodingTableSelector,
int[] HorizontalSamplingFactor,
int[] VerticalSamplingFactor,
int maxHorizontalSamplingFactor,
int maxVerticalSamplingFactor,
int colMCU,
int rowMCU,
int[] accumulatedDCDifferenceDuringRedaction,Vector<Shape> redactionShapes)
throws Exception, IOException {
575 for (
int c=0; c<nComponents; ++c) {
577 for (
int v=0; v<VerticalSamplingFactor[c]; ++v) {
578 for (
int h=0; h<HorizontalSamplingFactor[c]; ++h) {
580 boolean redact = redactionDecision(colMCU,rowMCU,HorizontalSamplingFactor[c],VerticalSamplingFactor[c],maxHorizontalSamplingFactor,maxVerticalSamplingFactor,h,v,redactionShapes);
582 accumulatedDCDifferenceDuringRedaction[c] = getOneDCTDataUnit(DCEntropyCodingTableSelector[c],ACEntropyCodingTableSelector[c],redact,accumulatedDCDifferenceDuringRedaction[c]);
584 else if (isLossless) {
585 int decompressedPixel = getOneLosslessValue(c,DCEntropyCodingTableSelector[c],colMCU,rowMCU);
587 writeDecompressedPixel(c,decompressedPixel);
591 throw new Exception(
"Only DCT or Lossless processes supported (not "+Markers.getAbbreviation(sof.
getMarker())+
" "+Markers.getDescription(sof.
getMarker())+
")");
598 private static final int max(
int[] a) {
599 int m = Integer.MIN_VALUE;
616 public final byte[]
finish(
byte[] bytesToDecompress,
int mcuCount,
int mcuOffset)
throws Exception, IOException {
618 this.bytesToDecompress = bytesToDecompress;
619 availableBytes = this.bytesToDecompress.length;
625 initializeWriteBits();
628 if (rowNumberAtBeginningOfRestartInterval !=
null) {
629 for (
int c=0; c<nComponents; ++c) {
631 rowNumberAtBeginningOfRestartInterval[c] = currentRowNumber[c];
635 int[] accumulatedDCDifferenceDuringRedaction =
new int[nComponents];
636 for (
int c=0; c<nComponents; ++c) {
637 accumulatedDCDifferenceDuringRedaction[c] = 0;
641 for (
int mcu=0; mcu<mcuCount; ++mcu) {
642 int rowMCU = mcuOffset / nMCUHorizontally;
643 int colMCU = mcuOffset % nMCUHorizontally;
645 getOneMinimumCodedUnit(nComponents,DCEntropyCodingTableSelector,ACEntropyCodingTableSelector,HorizontalSamplingFactor,VerticalSamplingFactor,maxHorizontalSamplingFactor,maxVerticalSamplingFactor,colMCU,rowMCU,accumulatedDCDifferenceDuringRedaction,redactionShapes);
666 return copying ? copiedBytes.toByteArray() :
null;
669 private final void dumpHuffmanTables() {
670 System.err.print(
"\n");
671 for (
HuffmanTable ht : htByClassAndIdentifer.values()) {
672 System.err.print(ht.toString());
676 private final void dumpQuantizationTables() {
677 System.err.print(
"\n");
678 for (QuantizationTable qt : qtByIdentifer.values()) {
679 System.err.print(qt.toString());