View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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          // return false to exit before reading image data.
34          boolean beginSos();
35  
36          // return false to exit traversal.
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          // case JPEG_APP0 :
48          // return "JPEG_APP0";
49          // case JPEG_APP0_MARKER :
50          // return "JPEG_APP0_MARKER";
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             // return false to exit before reading image data.
127             @Override
128             public boolean beginSos() {
129                 return true;
130             }
131 
132             // return false to exit traversal.
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 }