3 package com.pixelmed.codec.jpeg;
5 import java.awt.Rectangle;
8 import java.io.ByteArrayOutputStream;
10 import java.io.FileInputStream;
11 import java.io.FileOutputStream;
12 import java.io.InputStream;
13 import java.io.IOException;
14 import java.io.OutputStream;
16 import java.nio.ByteOrder;
18 import java.util.HashMap;
20 import java.util.Vector;
35 private static final String identString =
"@(#) $Header: /userland/cvs/codec/com/pixelmed/codec/jpeg/Parse.java,v 1.18 2017/03/21 17:42:24 dclunie Exp $";
37 private static int getLargestSamplingFactor(
int[] factors) {
39 for (
int factor : factors) {
40 if (factor > largest) {
47 private static final void writeMarkerAndLength(OutputStream out,
int marker,
int length)
throws IOException {
49 out.write(marker&0xff);
50 out.write((length>>>8)&0xff);
51 out.write(length&0xff);
54 private static final void writeVariableLengthMarkerSegment(OutputStream out,
int marker,
int length,byte[] b)
throws IOException {
55 writeMarkerAndLength(out,marker,length);
56 out.write(b,0,length-2);
60 private int nComponents;
62 private File fileBasis;
63 private ByteOrder order;
72 this.fileBasis = fileBasis;
79 nComponents = sof.getNComponentsInFrame();
81 if (fileBasis == null) {
82 int length = sof.getNSamplesPerLine() * sof.getNLines();
83 for (
int c=0; c<nComponents; ++c) {
85 if (sof.getSamplePrecision() <= 8) {
94 if (nComponents == 1) {
95 decompressedOutputPerComponent[0] =
new OutputArrayOrStream(
new FileOutputStream(fileBasis),order);
98 File parent = fileBasis.getParentFile();
99 String baseFileName = fileBasis.getName();
102 int periodPosition = baseFileName.lastIndexOf(
'.');
103 if (periodPosition > -1) {
104 if (periodPosition > 0) {
105 prefix = baseFileName.substring(0,periodPosition);
110 suffix = baseFileName.substring(periodPosition);
113 prefix = baseFileName;
116 for (
int c=0; c<nComponents; ++c) {
117 String componentFileName = prefix + c + suffix;
119 decompressedOutputPerComponent[c] =
new OutputArrayOrStream(
new FileOutputStream(
new File(parent,componentFileName)),order);
125 public void close() throws IOException {
126 for (
int c=0; c<nComponents; ++c) {
127 decompressedOutputPerComponent[c].
close();
136 private Map<String,HuffmanTable> htByClassAndIdentifer;
137 private Map<String,QuantizationTable> qtByIdentifer;
147 this.htByClassAndIdentifer = htByClassAndIdentifer;
148 this.qtByIdentifer = qtByIdentifer;
170 boolean dumping = copiedRedactedOutputStream == null && decompressedOutput == null;
172 boolean copying = copiedRedactedOutputStream != null;
173 boolean decompressing = decompressedOutput != null;
177 ByteArrayOutputStream byteAccumulator = null;
181 Map<String,HuffmanTable> htByClassAndIdentifer =
new HashMap<String,HuffmanTable>();
182 Map<String,QuantizationTable> qtByIdentifer =
new HashMap<String,QuantizationTable>();
183 int restartinterval = 0;
186 int nMCUHorizontally = 0;
187 int nMCUVertically = 0;
188 int mcuCountPerEntropyCodedSegment = 0;
191 int markerprefix = in.read();
194 boolean sawEOF =
false;
195 if (markerprefix == -1) {
196 if (dumping) System.err.print(
"End of file\n");
200 if (markerprefix != 0xff) {
201 if (byteAccumulator == null) {
203 byteAccumulator =
new ByteArrayOutputStream();
205 byteAccumulator.write(markerprefix);
207 markerprefix=in.read();
212 if (dumping) System.err.print(
"End of file immediately after marker flag 0xff ... presumably was padding\n");
215 else if (marker == 0xff) {
222 else if (marker == 0) {
223 if (dumping) System.err.print(
"Offset "+
Utilities.
toPaddedHexString(offset,4)+
" Encoded 0xff in entropy-coded segment followed by stuffed zero byte\n");
224 if (byteAccumulator == null) {
226 byteAccumulator =
new ByteArrayOutputStream();
228 byteAccumulator.write(markerprefix);
229 markerprefix=in.read();
238 if (byteAccumulator != null) {
244 throw new Exception(
"Error - compressed data without preceding SOF marker segment");
256 nMCUVertically = (sof.
getNLines()-1)/verticalSamplesPerMCU + 1;
260 mcuCountPerEntropyCodedSegment = (restartinterval == 0) ? nMCUHorizontally * nMCUVertically : restartinterval;
264 ecs =
new EntropyCodedSegment(sos,sof,htByClassAndIdentifer,qtByIdentifer,nMCUHorizontally,redactionShapes,copying,dumping,decompressing,decompressedOutput);
266 byte[] bytesToDecompress = byteAccumulator.toByteArray();
269 int mcuStillNeeded = (nMCUHorizontally * nMCUVertically) - mcuOffset;
271 int mcuNeededThisInterval = mcuCountPerEntropyCodedSegment > mcuStillNeeded ? mcuStillNeeded : mcuCountPerEntropyCodedSegment;
273 byte[] bytesToCopy = ecs.
finish(bytesToDecompress,mcuNeededThisInterval,mcuOffset);
275 copiedRedactedOutputStream.write(bytesToCopy);
277 byteAccumulator = null;
278 mcuOffset += mcuCountPerEntropyCodedSegment;
285 copiedRedactedOutputStream.write(0xff); copiedRedactedOutputStream.write(
Markers.
EOI);
307 byte[] b =
new byte[length-2];
308 int count = in.read(b,0,length-2);
309 if (count != length-2) {
310 throw new Exception(
"Error - couldn't read variable length parameter sequence at Offset "+
Utilities.
toPaddedHexString(offset,4));
316 if (dumping) System.err.print(sos);
317 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
334 if (dumping) System.err.print(sof);
335 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
336 if (decompressing) decompressedOutput.configureDecompressedOutput(sof);
341 if (dumping) System.err.print(dht);
342 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
347 if (dumping) System.err.print(dqt);
348 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
356 else if (length == 5) {
359 else if (length == 6) {
366 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
373 else if (length == 5) {
376 else if (length == 6) {
383 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
395 StringBuffer magicbuf =
new StringBuffer();
396 for (
int i=0; i<b.length && b[i] != 0; ++i) {
397 magicbuf.append(Character.valueOf((
char)b[i]));
399 magic = magicbuf.toString();
401 if (dumping) System.err.print(magic);
402 if (marker ==
Markers.
APP0 && magic.equals(
"JFIF")) {
407 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
411 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
417 if (dumping) System.err.print(
"Warning - variable length marker without \"zero\" length (really 2)");
422 if (copying) { copiedRedactedOutputStream.write(0xff); copiedRedactedOutputStream.write(marker&0xff);}
425 if (dumping) System.err.print(
"\n");
436 int value = in.read();
440 if (copying) { writeMarkerAndLength(copiedRedactedOutputStream,marker,length); copiedRedactedOutputStream.write(value&0xff); }
453 if (copying) { writeMarkerAndLength(copiedRedactedOutputStream,marker,length); copiedRedactedOutputStream.write((value>>>8)&0xff); copiedRedactedOutputStream.write(value&0xff); }
461 throw new Exception(
"Error - fixed length marker with unexpected length "+length+
" at Offset "+
Utilities.
toPaddedHexString(offset,4));
466 if (dumping) System.err.print(
"\n");
467 markerprefix=in.read();
471 copiedRedactedOutputStream.close();
474 decompressedOutput.close();
492 return parse(in,copiedRedactedOutputStream,redactionShapes,null);
503 public static void main(String arg[]) {
505 InputStream in =
new FileInputStream(arg[0]);
506 OutputStream copiedCompressedOutput = arg.length > 1 && arg[1].length() > 0 ?
new FileOutputStream(arg[1]) : null;
508 long startTime = System.currentTimeMillis();
509 parse(in,copiedCompressedOutput,null,decompressedOutput);
510 long currentTime = System.currentTimeMillis();
511 long runTime = currentTime-startTime;
512 System.err.println(
"Took = "+runTime+
" ms");
514 catch (Exception e) {
515 e.printStackTrace(System.err);
static final int extract16be(byte[] b, int offset)
int [] getVerticalSamplingFactor()
MarkerSegmentSOS getSOS()
MarkerSegmentsFoundDuringParse(MarkerSegmentSOS sos, MarkerSegmentSOF sof, Map< String, HuffmanTable > htByClassAndIdentifer, Map< String, QuantizationTable > qtByIdentifer)
void addToMapByIdentifier(Map< String, QuantizationTable > qtByIdentifer)
static void main(String arg[])
static final boolean isDCT(int marker)
static final int isFixedLengthJPEGSegment(int marker)
static final boolean isVariableLengthJPEGSegment(int marker)
static final String getDescription(int marker)
static MarkerSegmentsFoundDuringParse parse(InputStream in, OutputStream copiedRedactedOutputStream, Vector< Shape > redactionShapes)
void configureDecompressedOutput(MarkerSegmentSOF sof)
Map< String, QuantizationTable > getQuantizationTableByIdentifer()
void addToMapByClassAndIdentifier(Map< String, HuffmanTable > htByClassAndIdentifer)
OutputArrayOrStream [] getDecompressedOutputPerComponent()
int [] getHorizontalSamplingFactor()
void allocateByteArray(int length)
static MarkerSegmentsFoundDuringParse parse(InputStream in, OutputStream copiedRedactedOutputStream, Vector< Shape > redactionShapes, DecompressedOutput decompressedOutput)
static final boolean isNoLengthJPEGSegment(int marker)
Map< String, HuffmanTable > getHuffmanTableByClassAndIdentifer()
DecompressedOutput(File fileBasis, ByteOrder order)
MarkerSegmentSOF getSOF()
static final long extract24be(byte[] b, int offset)
void allocateShortArray(int length)
static String toPaddedHexString(int i, int length)
static final long extract32be(byte[] b, int offset)
static final String getAbbreviation(int marker)
final byte [] finish(byte[] bytesToDecompress, int mcuCount, int mcuOffset)
static final int read16be(InputStream in)