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