1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.formats.jpeg;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21
22 import org.apache.commons.imaging.ImagingException;
23 import org.apache.commons.imaging.bytesource.ByteSource;
24 import org.apache.commons.imaging.common.BinaryFileParser;
25 import org.apache.commons.imaging.common.BinaryFunctions;
26 import org.apache.commons.imaging.common.ByteConversions;
27 import org.apache.commons.imaging.internal.Debug;
28 import org.apache.commons.io.IOUtils;
29
30 public class JpegUtils extends BinaryFileParser {
31 public interface Visitor {
32
33 boolean beginSos();
34
35
36 boolean visitSegment(int marker, byte[] markerBytes, int segmentLength, byte[] segmentLengthBytes, byte[] segmentData)
37 throws ImagingException, IOException;
38
39 void visitSos(int marker, byte[] markerBytes, byte[] imageData);
40 }
41
42 public static String getMarkerName(final int marker) {
43 switch (marker) {
44 case JpegConstants.SOS_MARKER:
45 return "SOS_MARKER";
46
47
48
49
50 case JpegConstants.JPEG_APP1_MARKER:
51 return "JPEG_APP1_MARKER";
52 case JpegConstants.JPEG_APP2_MARKER:
53 return "JPEG_APP2_MARKER";
54 case JpegConstants.JPEG_APP13_MARKER:
55 return "JPEG_APP13_MARKER";
56 case JpegConstants.JPEG_APP14_MARKER:
57 return "JPEG_APP14_MARKER";
58 case JpegConstants.JPEG_APP15_MARKER:
59 return "JPEG_APP15_MARKER";
60 case JpegConstants.JFIF_MARKER:
61 return "JFIF_MARKER";
62 case JpegConstants.SOF0_MARKER:
63 return "SOF0_MARKER";
64 case JpegConstants.SOF1_MARKER:
65 return "SOF1_MARKER";
66 case JpegConstants.SOF2_MARKER:
67 return "SOF2_MARKER";
68 case JpegConstants.SOF3_MARKER:
69 return "SOF3_MARKER";
70 case JpegConstants.DHT_MARKER:
71 return "SOF4_MARKER";
72 case JpegConstants.SOF5_MARKER:
73 return "SOF5_MARKER";
74 case JpegConstants.SOF6_MARKER:
75 return "SOF6_MARKER";
76 case JpegConstants.SOF7_MARKER:
77 return "SOF7_MARKER";
78 case JpegConstants.SOF8_MARKER:
79 return "SOF8_MARKER";
80 case JpegConstants.SOF9_MARKER:
81 return "SOF9_MARKER";
82 case JpegConstants.SOF10_MARKER:
83 return "SOF10_MARKER";
84 case JpegConstants.SOF11_MARKER:
85 return "SOF11_MARKER";
86 case JpegConstants.DAC_MARKER:
87 return "DAC_MARKER";
88 case JpegConstants.SOF13_MARKER:
89 return "SOF13_MARKER";
90 case JpegConstants.SOF14_MARKER:
91 return "SOF14_MARKER";
92 case JpegConstants.SOF15_MARKER:
93 return "SOF15_MARKER";
94 case JpegConstants.DQT_MARKER:
95 return "DQT_MARKER";
96 case JpegConstants.DRI_MARKER:
97 return "DRI_MARKER";
98 case JpegConstants.RST0_MARKER:
99 return "RST0_MARKER";
100 case JpegConstants.RST1_MARKER:
101 return "RST1_MARKER";
102 case JpegConstants.RST2_MARKER:
103 return "RST2_MARKER";
104 case JpegConstants.RST3_MARKER:
105 return "RST3_MARKER";
106 case JpegConstants.RST4_MARKER:
107 return "RST4_MARKER";
108 case JpegConstants.RST5_MARKER:
109 return "RST5_MARKER";
110 case JpegConstants.RST6_MARKER:
111 return "RST6_MARKER";
112 case JpegConstants.RST7_MARKER:
113 return "RST7_MARKER";
114 default:
115 return "Unknown";
116 }
117 }
118
119
120
121
122 public JpegUtils() {
123
124 }
125
126 public void dumpJfif(final ByteSource byteSource) throws ImagingException, IOException {
127 final Visitor visitor = new Visitor() {
128
129 @Override
130 public boolean beginSos() {
131 return true;
132 }
133
134
135 @Override
136 public boolean visitSegment(final int marker, final byte[] markerBytes, final int segmentLength, final byte[] segmentLengthBytes,
137 final byte[] segmentData) {
138 Debug.debug("Segment marker: " + Integer.toHexString(marker) + " (" + getMarkerName(marker) + "), " + segmentData.length
139 + " bytes of segment data.");
140 return true;
141 }
142
143 @Override
144 public void visitSos(final int marker, final byte[] markerBytes, final byte[] imageData) {
145 Debug.debug("SOS marker. " + imageData.length + " bytes of image data.");
146 Debug.debug("");
147 }
148 };
149
150 traverseJfif(byteSource, visitor);
151 }
152
153 public void traverseJfif(final ByteSource byteSource, final Visitor visitor) throws ImagingException, IOException {
154 try (InputStream is = byteSource.getInputStream()) {
155 BinaryFunctions.readAndVerifyBytes(is, JpegConstants.SOI, "Not a Valid JPEG File: doesn't begin with 0xffd8");
156
157 int markerCount;
158 for (markerCount = 0; true; markerCount++) {
159 final byte[] markerBytes = new byte[2];
160 do {
161 markerBytes[0] = markerBytes[1];
162 markerBytes[1] = BinaryFunctions.readByte("marker", is, "Could not read marker");
163 } while ((0xff & markerBytes[0]) != 0xff || (0xff & markerBytes[1]) == 0xff);
164 final int marker = (0xff & markerBytes[0]) << 8 | 0xff & markerBytes[1];
165
166 if (marker == JpegConstants.EOI_MARKER || marker == JpegConstants.SOS_MARKER) {
167 if (!visitor.beginSos()) {
168 return;
169 }
170
171 final byte[] imageData = IOUtils.toByteArray(is);
172 visitor.visitSos(marker, markerBytes, imageData);
173 break;
174 }
175
176 final byte[] segmentLengthBytes = BinaryFunctions.readBytes("segmentLengthBytes", is, 2, "segmentLengthBytes");
177 final int segmentLength = ByteConversions.toUInt16(segmentLengthBytes, getByteOrder());
178 if (segmentLength < 2) {
179 throw new ImagingException("Invalid segment size");
180 }
181
182 final byte[] segmentData = BinaryFunctions.readBytes("Segment Data", is, segmentLength - 2, "Invalid Segment: insufficient data");
183
184 if (!visitor.visitSegment(marker, markerBytes, segmentLength, segmentLengthBytes, segmentData)) {
185 return;
186 }
187 }
188
189 Debug.debug(markerCount + " markers");
190 }
191 }
192 }