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.19 2018/08/30 10:23:04 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);
338 throw new Exception(
"Error - cannot decompress anything other than Huffman Lossless Sequential");
340 decompressedOutput.configureDecompressedOutput(sof);
346 if (dumping) System.err.print(dht);
347 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
352 if (dumping) System.err.print(dqt);
353 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
361 else if (length == 5) {
364 else if (length == 6) {
371 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
378 else if (length == 5) {
381 else if (length == 6) {
388 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
400 StringBuffer magicbuf =
new StringBuffer();
401 for (
int i=0; i<b.length && b[i] != 0; ++i) {
402 magicbuf.append(Character.valueOf((
char)b[i]));
404 magic = magicbuf.toString();
406 if (dumping) System.err.print(magic);
407 if (marker ==
Markers.
APP0 && magic.equals(
"JFIF")) {
412 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
416 if (copying) writeVariableLengthMarkerSegment(copiedRedactedOutputStream,marker,length,b);
422 if (dumping) System.err.print(
"Warning - variable length marker without \"zero\" length (really 2)");
427 if (copying) { copiedRedactedOutputStream.write(0xff); copiedRedactedOutputStream.write(marker&0xff);}
430 if (dumping) System.err.print(
"\n");
441 int value = in.read();
445 if (copying) { writeMarkerAndLength(copiedRedactedOutputStream,marker,length); copiedRedactedOutputStream.write(value&0xff); }
458 if (copying) { writeMarkerAndLength(copiedRedactedOutputStream,marker,length); copiedRedactedOutputStream.write((value>>>8)&0xff); copiedRedactedOutputStream.write(value&0xff); }
466 throw new Exception(
"Error - fixed length marker with unexpected length "+length+
" at Offset "+
Utilities.
toPaddedHexString(offset,4));
471 if (dumping) System.err.print(
"\n");
472 markerprefix=in.read();
476 copiedRedactedOutputStream.close();
479 decompressedOutput.close();
497 return parse(in,copiedRedactedOutputStream,redactionShapes,
null);
508 public static void main(String arg[]) {
510 InputStream in =
new FileInputStream(arg[0]);
511 OutputStream copiedCompressedOutput = arg.length > 1 && arg[1].length() > 0 ?
new FileOutputStream(arg[1]) :
null;
513 long startTime = System.currentTimeMillis();
514 parse(in,copiedCompressedOutput,
null,decompressedOutput);
515 long currentTime = System.currentTimeMillis();
516 long runTime = currentTime-startTime;
517 System.err.println(
"Took = "+runTime+
" ms");
519 catch (Exception e) {
520 e.printStackTrace(System.err);