pixelmed
EntropyCodedSegment copy.java
Go to the documentation of this file.
1 /* Copyright (c) 2014-2015, David A. Clunie DBA Pixelmed Publishing. All rights reserved. */
2 
3 package com.pixelmed.codec.jpeg;
4 
5 import java.awt.Rectangle;
6 import java.awt.Shape;
7 
8 import java.io.ByteArrayOutputStream;
9 import java.io.IOException;
10 import java.io.OutputStream;
11 
12 import java.util.HashMap;
13 import java.util.Map;
14 import java.util.Vector;
15 
23 public class EntropyCodedSegment {
24 
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 $";
26 
27  private boolean copying;
28  private boolean decompressing;
29 
30  private OutputArrayOrStream[] decompressedOutputPerComponent;
31 
32  private boolean isHuffman;
33  private boolean isDCT;
34  private boolean isLossless;
35 
36  private ByteArrayOutputStream copiedBytes;
37 
38  private final MarkerSegmentSOS sos;
39  private final MarkerSegmentSOF sof;
40  private final Map<String,HuffmanTable> htByClassAndIdentifer;
41  private final Map<String,QuantizationTable> qtByIdentifer;
42 
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;
48 
49  private final int maxHorizontalSamplingFactor;
50  private final int maxVerticalSamplingFactor;
51 
52  private final int nMCUHorizontally;
53 
54  private final Vector<Shape> redactionShapes;
55 
56  // stuff for lossless decompression ...
57  private final int predictorForFirstSample;
58  private final int[] predictorForComponent;
59  private final int predictorSelectionValue;
60 
61  // these are class level and used by getOneLosslessValue() to maintain state (updates them) and initialized by constructor
62  private int[] rowNumberAtBeginningOfRestartInterval; // indexed by component number, not final since set at beginning of each
63  private final int[] rowLength; // indexed by component number
64  private final int[] currentRowNumber; // indexed by component number
65  private final int[] positionWithinRow; // indexed by component number
66  private final int[][] previousReconstructedRow; // indexed by component number, positionWithinRow
67  private final int[][] currentReconstructedRow; // indexed by component number, positionWithinRow
68 
69  // stuff for bit extraction ...
70  // copied from com.pixelmed.scpecg.HuffmanDecoder ...
71  private byte[] bytesToDecompress;
72  private int availableBytes;
73  private int byteIndex;
74  private int bitIndex;
75  private int currentByte;
76  private int currentBits;
77  private int haveBits;
78 
79  private static final int[] extractBitFromByteMask = { 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01 };
80 
81  private final void getEnoughBits(int wantBits) throws Exception {
82  while (haveBits < wantBits) {
83  if (bitIndex > 7) {
84  if (byteIndex < availableBytes) {
85  currentByte=bytesToDecompress[byteIndex++];
86 //System.err.println("currentByte["+byteIndex+"] now = 0x"+Integer.toHexString(currentByte&0xff)+" "+Integer.toBinaryString(currentByte&0xff));
87  bitIndex=0;
88  }
89  else {
90  throw new Exception("No more bits (having decompressed "+byteIndex+" dec bytes)");
91  }
92  }
93  int newBit = (currentByte & extractBitFromByteMask[bitIndex++]) == 0 ? 0 : 1;
94  currentBits = (currentBits << 1) + newBit;
95  ++haveBits;
96  }
97 //System.err.println("getEnoughBits(): returning "+haveBits+" bits "+Integer.toBinaryString(currentBits)+" (ending at byte "+byteIndex+" bit "+(bitIndex-1)+")");
98  }
99 
100  private int writeByte; // only contains meaningful content when writeBitIndex > 0
101  private int writeBitIndex; // 0 means ready to write 1st (high) bit to writeByte, 7 means ready to write last (low) bit to writeByte, will transiently (inside writeBits only) be 8 to signal new byte needed
102 
103  private final void initializeWriteBits() {
104  copiedBytes = new ByteArrayOutputStream();
105  writeByte = 0;
106  writeBitIndex = 0; // start writing into 1st (high) bit of writeByte
107  }
108 
109  private final void flushWriteBits() {
110  if (writeBitIndex > 0) {
111  // bits have been written to writeByte so need to pad it with 1s and write it
112  while (writeBitIndex < 8) {
113  writeByte = writeByte | extractBitFromByteMask[writeBitIndex];
114  ++writeBitIndex;
115  }
116  copiedBytes.write(writeByte);
117  if ((writeByte&0xff) == 0xff) {
118  copiedBytes.write(0); // stuffed zero byte after 0xff to prevent being considered marker
119  }
120  writeByte=0;
121  writeBitIndex=0;
122  }
123  // else have not written any bits to writeByte, so do nothing
124  }
125 
126  private final void writeBits(int bits,int nBits) {
127 //System.err.println("writeBits(): writing "+nBits+" bits "+Integer.toBinaryString(bits));
128  if (nBits > 0) {
129  for (int i=nBits-1; i>=0; --i) {
130  final int whichBitMask = 1 << i; // bits are "big endian"
131  final int bitIsSet = bits & whichBitMask; // zero or not zero
132  // do not need to check writeBitIndex before "writing" ... will always be "ready"
133  if (bitIsSet != 0) {
134  writeByte = writeByte | extractBitFromByteMask[writeBitIndex];
135  }
136  ++writeBitIndex;
137  if (writeBitIndex > 7) {
138 //System.err.println("writeBits(): wrote = 0x"+Integer.toHexString(writeByte&0xff)+" "+Integer.toBinaryString(writeByte&0xff));
139  copiedBytes.write(writeByte);
140  if ((writeByte&0xff) == 0xff) {
141  copiedBytes.write(0); // stuffed zero byte after 0xff to prevent being considered marker
142  }
143  writeByte=0;
144  writeBitIndex=0;
145  }
146  }
147  }
148  }
149 
150 
151 
152  private HuffmanTable usingTable = null;
153 
154 //int counter = 0;
155 
156  // Use 10918-1 F.2 Figure F.16 decode procedure
157 
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();
168 
169  int I=1;
170  getEnoughBits(I); // modifies currentBits
171  int CODE = currentBits;
172  while (I<MAXCODE.length && CODE > MAXCODE[I]) {
173  //while (CODE > MAXCODE[I]) {
174  ++I;
175 //System.err.println("I = "+I);
176  getEnoughBits(I); // modifies currentBits
177  CODE = currentBits;
178 //System.err.println("CODE "+Integer.toBinaryString(CODE));
179 //System.err.println("compare to MAXCODE[I] "+(I<MAXCODE.length ? Integer.toBinaryString(MAXCODE[I]) : "out of MAXCODE entries"));
180  }
181 //System.err.println("Decoded CODE "+Integer.toBinaryString(CODE)+" of length "+I);
182  int VALUE = 0;
183  if (I<MAXCODE.length) {
184  int J = VALPTR[I];
185 //System.err.println("Found VALPTR base "+J);
186  J = J + CODE - MINCODE[I];
187 //System.err.println("Found VALPTR offset by code "+J);
188  VALUE = HUFFVAL[J];
189 //System.err.println("Found VALUE "+VALUE+" dec (0x"+Integer.toHexString(VALUE)+")");
190 //System.err.println("HUFF_DECODE: "+VALUE+" COUNTER "+counter);
191 //++counter;
192  }
193  else {
194  //we have exceeded the maximum coded value specified :(
195  // copy IJG behavior in this situation from jdhuff.c "With garbage input we may reach the sentinel value l = 17" ... "fake a zero as the safest result"
196 //System.err.println("Bad Huffman code "+Integer.toBinaryString(CODE)+" so use VALUE "+VALUE+" dec (0x"+Integer.toHexString(VALUE)+")");
197  }
198  if (copying) { writeBits(currentBits,haveBits); }
199  currentBits=0;
200  haveBits=0;
201  return VALUE;
202  }
203 
204  private final void encode(int VALUE) {
205 //System.err.println("Given VALUE "+VALUE+" dec (0x"+Integer.toHexString(VALUE)+")");
206  final int[] EFUFCO = usingTable.getEFUFCO();
207  final int[] EFUFSI = usingTable.getEFUFSI();
208  int CODE = EFUFCO[VALUE];
209  int size = EFUFSI[VALUE];
210 //System.err.println("Encoding CODE "+Integer.toBinaryString(CODE)+" of length "+size);
211  writeBits(CODE,size);
212  }
213 
214  private final int getValueOfRequestedLength(int wantBits) throws Exception {
215  getEnoughBits(wantBits); // modifies currentBits
216  final int value = currentBits;
217 //System.err.println("getValueOfRequestedLength(): wantBits="+wantBits+" : Got value "+value+" dec (0x"+Integer.toHexString(value)+")");
218  if (copying) { writeBits(currentBits,haveBits); }
219  currentBits=0;
220  haveBits=0;
221  return value;
222  }
223 
224  // values above index 11 only occur for 12 bit process ...
225  private int[] dcSignBitMask = { 0x00/*na*/,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000 /*no entry for 16*/};
226  private int[] maxAmplitude = { 0/*na*/,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 /*no entry for 16*/};
227 
228  private final int convertSignAndAmplitudeBitsToValue(int value,int length) throws Exception {
229  // see P&M Table 11-1 page 190 and Table 11-4 page 193 (same for DC and AC)
230  if (length > 0) {
231 //System.err.println("dcSignBitMask = "+Integer.toHexString(dcSignBitMask[length]));
232  if ((value & dcSignBitMask[length]) == 0) {
233 //System.err.println("Have sign bit");
234  value = value - maxAmplitude[length];
235  }
236  }
237  return value;
238  }
239 
240  private final int getNumberOfSignBits(int value) {
241  int ssss = 0;
242  if (value < 0) {
243  value = - value;
244  }
245  while (value > 0) {
246  ++ssss;
247  value = value >> 1;
248  }
249  return ssss;
250  }
251 
252  private final int getBits(int value,int ssss) {
253  int bits = 0;
254  if (ssss > 0) {
255  if (value < 0) { // "if ... -ve, subtract 1 ... and append the SSSS low-order bits of this result" P&M p191
256  --value;
257  }
258  // else "if ... +ve, append the SSSS low-order bits" P&M p191
259  bits = value & maxAmplitude[ssss];
260  }
261  return bits;
262  }
263 
264 
265  private final void writeEntropyCodedAllZeroACCoefficients() {
266  // write a single EOB code, which is rrrrssss = 0x00;
267  writeBits(usingTable.getEOBCode(),usingTable.getEOBCodeLength());
268  }
269 
270 
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 {
287  this.sos = sos;
288  this.sof = sof;
289  this.htByClassAndIdentifer = htByClassAndIdentifer;
290  this.qtByIdentifer = qtByIdentifer;
291  this.nMCUHorizontally = nMCUHorizontally;
292  this.redactionShapes = redactionShapes;
293  this.copying = copying;
294  // dumping is not used other than in this constructor
295  this.decompressing = decompressing;
296  this.decompressedOutputPerComponent = decompressedOutput == null ? null : decompressedOutput.getDecompressedOutputPerComponent();
297 
298  this.isHuffman = Markers.isHuffman(sof.getMarker());
299  if (!isHuffman) {
300  throw new Exception("Only Huffman processes supported (not "+Markers.getAbbreviation(sof.getMarker())+" "+Markers.getDescription(sof.getMarker())+")");
301  }
302  this.isDCT = Markers.isDCT(sof.getMarker());
303  this.isLossless = Markers.isLossless(sof.getMarker());
304 
305  nComponents = sos.getNComponentsPerScan();
306  DCEntropyCodingTableSelector = sos.getDCEntropyCodingTableSelector();
307  ACEntropyCodingTableSelector = sos.getACEntropyCodingTableSelector();
308  HorizontalSamplingFactor = sof.getHorizontalSamplingFactor();
309  VerticalSamplingFactor = sof.getVerticalSamplingFactor();
310 
311  maxHorizontalSamplingFactor = max(HorizontalSamplingFactor);
312 //System.err.println("maxHorizontalSamplingFactor "+maxHorizontalSamplingFactor);
313  maxVerticalSamplingFactor = max(VerticalSamplingFactor);
314 //System.err.println("maxVerticalSamplingFactor "+maxVerticalSamplingFactor);
315 
316  if (isLossless && decompressing) {
317 //System.err.println("SamplePrecision "+sof.getSamplePrecision());
318 //System.err.println("SuccessiveApproximationBitPositionLowOrPointTransform "+sos.getSuccessiveApproximationBitPositionLowOrPointTransform());
319  predictorForFirstSample = 1 << (sof.getSamplePrecision() - sos.getSuccessiveApproximationBitPositionLowOrPointTransform() - 1);
320 //System.err.println("predictorForFirstSample "+predictorForFirstSample+" dec");
321  predictorForComponent = new int[nComponents];
322  predictorSelectionValue = sos.getStartOfSpectralOrPredictorSelection();
323 //System.err.println("predictorSelectionValue "+predictorSelectionValue);
324 
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) {
332  //rowLength[c] = sof.getNSamplesPerLine()/sof.getHorizontalSamplingFactor()[c];
333  rowLength[c] = (sof.getNSamplesPerLine()-1)/sof.getHorizontalSamplingFactor()[c]+1; // account for sampling of row lengths not an exact multiple of sampling factor ... hmmm :(
334 //System.err.println("rowLength["+c+"] "+rowLength[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]];
340  }
341  }
342  else {
343  predictorForFirstSample = 0; // silence uninitialized warnings
344  predictorForComponent = null;
345  predictorSelectionValue = 0;
346  rowLength = null;
347  currentRowNumber = null;
348  positionWithinRow = null;
349  rowNumberAtBeginningOfRestartInterval = null;
350  previousReconstructedRow = null;
351  currentReconstructedRow = null;
352  }
353 
354  if (dumping) dumpHuffmanTables();
355  //dumpQuantizationTables();
356  }
357 
358  private final int getOneLosslessValue(int c,int dcEntropyCodingTableSelector,int colMCU,int rowMCU) throws Exception {
359  // per P&M page 492 (DIS H-2)
360  int prediction = 0;
361  if (decompressing) {
362  if (currentRowNumber[c] == rowNumberAtBeginningOfRestartInterval[c]) { // will be true for first row since all rowNumberAtBeginningOfRestartInterval entries are initialized to zero
363  if (positionWithinRow[c] == 0) { // first sample of first row
364 //System.err.println("Component "+c+" first sample of first row or first row after beginning of restart interval ... use predictorForFirstSample");
365  prediction = predictorForFirstSample;
366  }
367  else {
368 //System.err.println("Component "+c+" other than first sample of first row or first row after beginning of restart interval ... use Ra (previous sample in row)");
369  prediction = currentReconstructedRow[c][positionWithinRow[c]-1]; // Ra
370  }
371  }
372  else if (positionWithinRow[c] == 0) { // first sample of subsequent rows
373 //System.err.println("Component "+c+" first sample of subsequent rows");
374  prediction = previousReconstructedRow[c][0]; // Rb for position 0
375  }
376  else {
377  switch(predictorSelectionValue) {
378  case 1: prediction = currentReconstructedRow[c][positionWithinRow[c]-1]; // Ra
379  break;
380  case 2: prediction = previousReconstructedRow[c][positionWithinRow[c]]; // Rb
381  break;
382  case 3: prediction = previousReconstructedRow[c][positionWithinRow[c]-1]; // Rc
383  break;
384  case 4: prediction = currentReconstructedRow[c][positionWithinRow[c]-1] + previousReconstructedRow[c][positionWithinRow[c]] - previousReconstructedRow[c][positionWithinRow[c]-1]; // Ra + Rb - Rc
385  break;
386  case 5: prediction = currentReconstructedRow[c][positionWithinRow[c]-1] + ((previousReconstructedRow[c][positionWithinRow[c]] - previousReconstructedRow[c][positionWithinRow[c]-1])>>1); // Ra + (Rb - Rc)/2
387  break;
388  case 6: prediction = previousReconstructedRow[c][positionWithinRow[c]] + ((currentReconstructedRow[c][positionWithinRow[c]-1] - previousReconstructedRow[c][positionWithinRow[c]-1])>>1); // Rb + (Ra - Rc)/2
389  break;
390  case 7: prediction = (currentReconstructedRow[c][positionWithinRow[c]-1] + previousReconstructedRow[c][positionWithinRow[c]])>>1; // (Ra+Rb)/2
391  break;
392  default:
393  throw new Exception("Unrecognized predictor selection value "+predictorSelectionValue);
394  }
395  }
396 //System.err.println("prediction ["+currentRowNumber[c]+","+positionWithinRow[c]+"] = "+prediction+" dec (0x"+Integer.toHexString(prediction)+")");
397  }
398 
399  usingTable = htByClassAndIdentifer.get("0+"+Integer.toString(dcEntropyCodingTableSelector));
400 
401  final int ssss = decode(); // number of DC bits encoded next
402  // see P&M Table 11-1 page 190
403  int dcValue = 0;
404  if (ssss == 0) {
405  dcValue = 0;
406  }
407  else if (ssss == 16) { // only occurs for lossless
408  dcValue = 32768;
409  }
410  else {
411  final int dcBits = getValueOfRequestedLength(ssss);
412  dcValue = convertSignAndAmplitudeBitsToValue(dcBits,ssss);
413  }
414 //System.err.println("encoded difference value ["+currentRowNumber[c]+","+positionWithinRow[c]+"] = "+dcValue+" dec (0x"+Integer.toHexString(dcValue)+")");
415 
416  int reconstructedValue = 0;
417 
418  if (decompressing) {
419  reconstructedValue = (dcValue + prediction) & 0x0000ffff;
420 
421 //System.err.println("reconstructedValue value ["+currentRowNumber[c]+","+positionWithinRow[c]+"] = "+reconstructedValue+" dec (0x"+Integer.toHexString(reconstructedValue)+")");
422 
423  currentReconstructedRow[c][positionWithinRow[c]] = reconstructedValue;
424 
425  ++positionWithinRow[c];
426  if (positionWithinRow[c] >= rowLength[c]) {
427 //System.err.println("Component "+c+" starting next row");
428  positionWithinRow[c] = 0;
429  ++currentRowNumber[c];
430  int[] holdRow = previousReconstructedRow[c];
431  previousReconstructedRow[c] = currentReconstructedRow[c];
432  currentReconstructedRow[c] = holdRow; // values do not matter, will be overwritten, saves deallocating and reallocating
433  }
434  }
435 
436  return reconstructedValue; // meaingless unless decompressing, but still need to have absorbed bits from input to stay in sync
437  }
438 
439  // A "data unit" is the "smallest logical unit that can be processed", which in the case of DCT-based processes is one 8x8 block of coefficients (P&M page 101)
440  // returns updated accumulatedDCDifferenceDuringRedaction
441  private final int getOneDCTDataUnit(int dcEntropyCodingTableSelector,int acEntropyCodingTableSelector,boolean redact,int accumulatedDCDifferenceDuringRedaction) throws Exception {
442  usingTable = htByClassAndIdentifer.get("0+"+Integer.toString(dcEntropyCodingTableSelector));
443  {
444  final boolean wasCopying = copying;
445  copying = false;
446  {
447  final int ssss = decode(); // number of DC bits encoded next
448  // see P&M Table 11-1 page 190
449  int dcDIFF = 0;
450  int dcBits = 0; // only need up here for later comparison with new computed values
451  if (ssss == 0) {
452  dcDIFF = 0;
453  }
454  else if (ssss == 16) { // only occurs for lossless
455  dcDIFF = 32768;
456  }
457  else {
458  dcBits = getValueOfRequestedLength(ssss);
459  dcDIFF = convertSignAndAmplitudeBitsToValue(dcBits,ssss);
460  }
461 //System.err.println("Got encoded DC DIFF "+dcDIFF+" dec (0x"+Integer.toHexString(dcDIFF)+")");
462 //System.err.println("accumulatedDCDifferenceDuringRedaction was "+accumulatedDCDifferenceDuringRedaction+" dec (0x"+Integer.toHexString(accumulatedDCDifferenceDuringRedaction)+")");
463  if (redact) {
464 System.err.println("Redacting this DCTDataUnit - to accumulatedDCDifferenceDuringRedaction "+accumulatedDCDifferenceDuringRedaction+" so far, adding this redacted dcDIFF "+dcDIFF);
465  accumulatedDCDifferenceDuringRedaction += dcDIFF; // track it
466  dcDIFF = 0;
467  }
468  else {
469 System.err.println("Not redacting this DCTDataUnit - applying accumulatedDCDifferenceDuringRedaction "+accumulatedDCDifferenceDuringRedaction+" to this non-redacted dcDIFF "+dcDIFF);
470  dcDIFF += accumulatedDCDifferenceDuringRedaction; // apply it
471  accumulatedDCDifferenceDuringRedaction = 0;
472  }
473 //System.err.println("accumulatedDCDifferenceDuringRedaction now "+accumulatedDCDifferenceDuringRedaction+" dec (0x"+Integer.toHexString(accumulatedDCDifferenceDuringRedaction)+")");
474 //System.err.println("Writing DC DIFF "+dcDIFF+" dec (0x"+Integer.toHexString(dcDIFF)+")");
475 
476  {
477  final int newSSSS = getNumberOfSignBits(dcDIFF);
478  final int newDCBits = getBits(dcDIFF,newSSSS);
479  //if (newSSSS != ssss || newDCBits != dcBits) {
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)+")");
481  //}
482  if (wasCopying) {
483  encode(ssss);
484  if (ssss > 0 && ssss < 16) {
485  writeBits(newDCBits,ssss);
486  }
487  }
488  }
489  }
490  copying = wasCopying;
491  }
492 
493  usingTable = htByClassAndIdentifer.get("1+"+Integer.toString(acEntropyCodingTableSelector));
494  {
495  final boolean wasCopying = copying;
496  if (redact && copying) {
497  copying = false;
498  writeEntropyCodedAllZeroACCoefficients();
499  }
500 
501  int i=1;
502  while (i<64) {
503  //System.err.println("AC ["+i+"]:");
504  final int rrrrssss = decode();
505  if (rrrrssss == 0) {
506  //System.err.println("AC ["+i+"]: "+"EOB");
507  break; // EOB
508  }
509  else if (rrrrssss == 0xF0) {
510  //System.err.println("AC ["+i+"]: "+"ZRL: 16 zeroes");
511  i+=16;
512  }
513  else {
514  // note that ssss of zero is not used for AC (unlike DC) in sequential mode
515  final int rrrr = rrrrssss >>> 4;
516  final int ssss = rrrrssss & 0x0f;
517  //System.err.println("AC ["+i+"]: rrrr="+rrrr+" ssss="+ssss);
518  final int acBits = getValueOfRequestedLength(ssss);
519  final int acValue = convertSignAndAmplitudeBitsToValue(acBits,ssss);
520  //System.err.println("AC ["+i+"]: "+rrrr+" zeroes then value "+acValue);
521  i+=rrrr; // the number of zeroes
522  ++i; // the value we read (ssss is always non-zero, so we always read something
523  }
524  }
525 
526  copying = wasCopying;
527  }
528 
529  return accumulatedDCDifferenceDuringRedaction;
530  }
531 
532  private final boolean redactionDecision(int colMCU,int rowMCU,int thisHorizontalSamplingFactor,int thisVerticalSamplingFactor,int maxHorizontalSamplingFactor,int maxVerticalSamplingFactor,int h,int v,Vector<Shape> redactionShapes) {
533  // only invoked for DCT so block size is always 8
534  final int vMCUSize = 8 * maxVerticalSamplingFactor;
535  final int hMCUSize = 8 * maxHorizontalSamplingFactor;
536 //System.err.println("MCUSize in pixels = "+hMCUSize+" * "+vMCUSize);
537 
538  final int hMCUOffset = colMCU * hMCUSize;
539  final int vMCUOffset = rowMCU * vMCUSize;
540 //System.err.println("MCUOffset in pixels = "+hMCUOffset+" * "+vMCUOffset);
541 
542  final int hBlockSize = 8 * maxHorizontalSamplingFactor/thisHorizontalSamplingFactor;
543  final int vBlockSize = 8 * maxVerticalSamplingFactor/thisVerticalSamplingFactor;
544 //System.err.println("BlockSize in pixels = "+hBlockSize+" * "+vBlockSize);
545 
546  final int xBlock = hMCUOffset + h * hBlockSize;
547  final int yBlock = vMCUOffset + v * vBlockSize;
548 
549  Rectangle blockShape = new Rectangle(xBlock,yBlock,hBlockSize,vBlockSize);
550 //System.err.println("blockShape "+blockShape);
551 
552  boolean redact = false;
553  if (redactionShapes != null) {
554  for (Shape redactionShape : redactionShapes) {
555  if (redactionShape.intersects(blockShape)) {
556  redact = true;
557  break;
558  }
559  }
560  }
561  return redact;
562  }
563 
564  private final void writeDecompressedPixel(int c,int decompressedPixel) throws IOException {
565  if (sof.getSamplePrecision() <= 8) {
566  decompressedOutputPerComponent[c].writeByte(decompressedPixel);
567  }
568  else {
569  // endianness handled by OutputArrayOrStream
570  decompressedOutputPerComponent[c].writeShort(decompressedPixel);
571  }
572  }
573 
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) {
576  // See discussion of interleaving of data units within MCUs in P&M section 7.3.5 pages 101-105; always interleaved in sequential mode
577  for (int v=0; v<VerticalSamplingFactor[c]; ++v) {
578  for (int h=0; h<HorizontalSamplingFactor[c]; ++h) {
579 //System.err.println("Component "+c+" v "+v+" h "+h);
580  boolean redact = redactionDecision(colMCU,rowMCU,HorizontalSamplingFactor[c],VerticalSamplingFactor[c],maxHorizontalSamplingFactor,maxVerticalSamplingFactor,h,v,redactionShapes);
581  if (isDCT) {
582  accumulatedDCDifferenceDuringRedaction[c] = getOneDCTDataUnit(DCEntropyCodingTableSelector[c],ACEntropyCodingTableSelector[c],redact,accumulatedDCDifferenceDuringRedaction[c]);
583  }
584  else if (isLossless) {
585  int decompressedPixel = getOneLosslessValue(c,DCEntropyCodingTableSelector[c],colMCU,rowMCU);
586  if (decompressing) {
587  writeDecompressedPixel(c,decompressedPixel);
588  }
589  }
590  else {
591  throw new Exception("Only DCT or Lossless processes supported (not "+Markers.getAbbreviation(sof.getMarker())+" "+Markers.getDescription(sof.getMarker())+")");
592  }
593  }
594  }
595  }
596  }
597 
598  private static final int max(int[] a) {
599  int m = Integer.MIN_VALUE;
600  for (int i : a) {
601  if (i > m) m = i;
602  }
603  return m;
604  }
605 
616  public final byte[] finish(byte[] bytesToDecompress,int mcuCount,int mcuOffset) throws Exception, IOException {
617 //System.err.println("****** EntropyCodedSeqment.finish()");
618  this.bytesToDecompress = bytesToDecompress;
619  availableBytes = this.bytesToDecompress.length;
620  byteIndex = 0;
621  bitIndex = 8; // force fetching byte the first time
622  haveBits = 0; // don't have any bits to start with
623 
624  if (copying) {
625  initializeWriteBits(); // will create a new ByteArrayOutputStream
626  }
627 
628  if (rowNumberAtBeginningOfRestartInterval != null) { // do not need to do this unless decompressing lossless
629  for (int c=0; c<nComponents; ++c) {
630 //System.err.println("Setting rowNumberAtBeginningOfRestartInterval["+c+"] to "+currentRowNumber[c]);
631  rowNumberAtBeginningOfRestartInterval[c] = currentRowNumber[c]; // for lossless decompression predictor selection
632  }
633  }
634 
635  int[] accumulatedDCDifferenceDuringRedaction = new int[nComponents];
636  for (int c=0; c<nComponents; ++c) {
637  accumulatedDCDifferenceDuringRedaction[c] = 0; // P&M p171 "At the beginning of the scan and ... each restart interval, PRED is initialized to 0 (is actually a neutral gray)"
638  }
639  //try {
640 
641  for (int mcu=0; mcu<mcuCount; ++mcu) {
642  int rowMCU = mcuOffset / nMCUHorizontally;
643  int colMCU = mcuOffset % nMCUHorizontally;
644 //System.err.println("MCU ("+rowMCU+","+colMCU+")");
645  getOneMinimumCodedUnit(nComponents,DCEntropyCodingTableSelector,ACEntropyCodingTableSelector,HorizontalSamplingFactor,VerticalSamplingFactor,maxHorizontalSamplingFactor,maxVerticalSamplingFactor,colMCU,rowMCU,accumulatedDCDifferenceDuringRedaction,redactionShapes);
646  ++mcuOffset;
647  }
648 
649 //System.err.println("Finished ...");
650 //System.err.println("availableBytes = "+availableBytes);
651 //System.err.println("byteIndex = "+byteIndex);
652 //System.err.println("bitIndex = "+bitIndex);
653 //System.err.println("currentByte = "+currentByte);
654 //System.err.println("currentBits = "+currentBits);
655 //System.err.println("haveBits = "+haveBits);
656 
657  //}
658  //catch (Exception e) {
659  // e.printStackTrace(System.err);
660  //}
661 
662  if (copying) {
663  flushWriteBits(); // will pad appropriately to byte boundary
664  }
665 
666  return copying ? copiedBytes.toByteArray() : null;
667  }
668 
669  private final void dumpHuffmanTables() {
670  System.err.print("\n");
671  for (HuffmanTable ht : htByClassAndIdentifer.values()) {
672  System.err.print(ht.toString());
673  }
674  }
675 
676  private final void dumpQuantizationTables() {
677  System.err.print("\n");
678  for (QuantizationTable qt : qtByIdentifer.values()) {
679  System.err.print(qt.toString());
680  }
681  }
682 
683 }
684 
com.pixelmed.codec.jpeg.MarkerSegmentSOS.getDCEntropyCodingTableSelector
int[] getDCEntropyCodingTableSelector()
Definition: MarkerSegmentSOS.java:25
com.pixelmed.codec.jpeg.HuffmanTable.getHUFFVAL
int[] getHUFFVAL()
Definition: HuffmanTable.java:50
com.pixelmed.codec.jpeg.OutputArrayOrStream.writeShort
void writeShort(int s)
Writes the specified short to this output.
Definition: OutputArrayOrStream.java:137
com.pixelmed.codec.jpeg.OutputArrayOrStream
Definition: OutputArrayOrStream.java:22
com.pixelmed.codec.jpeg.Markers.getAbbreviation
static final String getAbbreviation(int marker)
Definition: Markers.java:379
com.pixelmed.codec.jpeg.MarkerSegmentSOF.getHorizontalSamplingFactor
int[] getHorizontalSamplingFactor()
Definition: MarkerSegmentSOF.java:31
com.pixelmed.codec.jpeg.OutputArrayOrStream.writeByte
void writeByte(int b)
Writes the specified byte to this output.
Definition: OutputArrayOrStream.java:116
com.pixelmed.codec.jpeg.EntropyCodedSegment.finish
final byte[] finish(byte[] bytesToDecompress, int mcuCount, int mcuOffset)
Definition: EntropyCodedSegment copy.java:616
com.pixelmed.codec.jpeg.EntropyCodedSegment.EntropyCodedSegment
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)
Definition: EntropyCodedSegment copy.java:286
com.pixelmed.codec.jpeg.MarkerSegmentSOS
Definition: MarkerSegmentSOS.java:10
com.pixelmed.codec.jpeg.EntropyCodedSegment
Definition: EntropyCodedSegment copy.java:23
com.pixelmed.codec.jpeg.MarkerSegmentSOF.getSamplePrecision
int getSamplePrecision()
Definition: MarkerSegmentSOF.java:26
com.pixelmed.codec.jpeg.Parse
Definition: Parse.java:33
com.pixelmed.codec.jpeg.MarkerSegmentSOF.getVerticalSamplingFactor
int[] getVerticalSamplingFactor()
Definition: MarkerSegmentSOF.java:32
com.pixelmed.codec.jpeg.Markers.isDCT
static final boolean isDCT(int marker)
Definition: Markers.java:228
com.pixelmed.codec.jpeg.Markers.isLossless
static final boolean isLossless(int marker)
Definition: Markers.java:247
com.pixelmed.codec.jpeg.Markers
Definition: Markers.java:13
com.pixelmed.codec.jpeg.HuffmanTable.getVALPTR
int[] getVALPTR()
Definition: HuffmanTable.java:49
com.pixelmed.codec.jpeg.HuffmanTable
Definition: HuffmanTable.java:10
com.pixelmed.codec.jpeg.HuffmanTable.getMAXCODE
int[] getMAXCODE()
Definition: HuffmanTable.java:48
com.pixelmed.codec.jpeg.HuffmanTable.getMINCODE
int[] getMINCODE()
Definition: HuffmanTable.java:47
com.pixelmed.codec.jpeg.Markers.getDescription
static final String getDescription(int marker)
Definition: Markers.java:384
com.pixelmed.codec.jpeg.MarkerSegmentSOS.getNComponentsPerScan
int getNComponentsPerScan()
Definition: MarkerSegmentSOS.java:24
com.pixelmed.codec.jpeg.HuffmanTable.getEFUFSI
int[] getEFUFSI()
Definition: HuffmanTable.java:57
com.pixelmed.codec.jpeg.MarkerSegmentSOF
Definition: MarkerSegmentSOF.java:10
com.pixelmed.codec.jpeg.MarkerSegmentSOF.getMarker
int getMarker()
Definition: MarkerSegmentSOF.java:24
com.pixelmed.codec.jpeg.MarkerSegmentSOS.getACEntropyCodingTableSelector
int[] getACEntropyCodingTableSelector()
Definition: MarkerSegmentSOS.java:26
com.pixelmed.codec.jpeg.Parse.DecompressedOutput
Definition: Parse.java:59
com.pixelmed.codec.jpeg.Markers.isHuffman
static final boolean isHuffman(int marker)
Definition: Markers.java:211
com.pixelmed.codec.jpeg.MarkerSegmentSOS.getSuccessiveApproximationBitPositionLowOrPointTransform
int getSuccessiveApproximationBitPositionLowOrPointTransform()
Definition: MarkerSegmentSOS.java:28
com.pixelmed.codec.jpeg.MarkerSegmentSOF.getNSamplesPerLine
int getNSamplesPerLine()
Definition: MarkerSegmentSOF.java:28
com.pixelmed.codec.jpeg.HuffmanTable.getEFUFCO
int[] getEFUFCO()
Definition: HuffmanTable.java:56
com.pixelmed.codec.jpeg.MarkerSegmentSOS.getStartOfSpectralOrPredictorSelection
int getStartOfSpectralOrPredictorSelection()
Definition: MarkerSegmentSOS.java:27
com.pixelmed.codec.jpeg.HuffmanTable.getEOBCodeLength
int getEOBCodeLength()
Definition: HuffmanTable.java:64
com.pixelmed.codec.jpeg.HuffmanTable.getEOBCode
int getEOBCode()
Definition: HuffmanTable.java:63