source: josm/trunk/src/com/drew/imaging/jpeg/JpegSegmentReader.java@ 8917

Last change on this file since 8917 was 8132, checked in by Don-vip, 11 years ago

fix #11162 - update to metadata-extractor 2.7.2

File size: 6.7 KB
Line 
1/*
2 * Copyright 2002-2015 Drew Noakes
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * More information about this project is available at:
17 *
18 * https://drewnoakes.com/code/exif/
19 * https://github.com/drewnoakes/metadata-extractor
20 */
21package com.drew.imaging.jpeg;
22
23import com.drew.lang.SequentialReader;
24import com.drew.lang.StreamReader;
25import com.drew.lang.annotations.NotNull;
26import com.drew.lang.annotations.Nullable;
27
28import java.io.File;
29import java.io.FileInputStream;
30import java.io.IOException;
31import java.util.HashSet;
32import java.util.Set;
33
34/**
35 * Performs read functions of JPEG files, returning specific file segments.
36 * <p>
37 * JPEG files are composed of a sequence of consecutive JPEG 'segments'. Each is identified by one of a set of byte
38 * values, modelled in the {@link JpegSegmentType} enumeration. Use <code>readSegments</code> to read out the some
39 * or all segments into a {@link JpegSegmentData} object, from which the raw JPEG segment byte arrays may be accessed.
40 *
41 * @author Drew Noakes https://drewnoakes.com
42 */
43public class JpegSegmentReader
44{
45 /**
46 * Private, because this segment crashes my algorithm, and searching for it doesn't work (yet).
47 */
48 private static final byte SEGMENT_SOS = (byte) 0xDA;
49
50 /**
51 * Private, because one wouldn't search for it.
52 */
53 private static final byte MARKER_EOI = (byte) 0xD9;
54
55 /**
56 * Processes the provided JPEG data, and extracts the specified JPEG segments into a {@link JpegSegmentData} object.
57 * <p>
58 * Will not return SOS (start of scan) or EOI (end of image) segments.
59 *
60 * @param file a {@link File} from which the JPEG data will be read.
61 * @param segmentTypes the set of JPEG segments types that are to be returned. If this argument is <code>null</code>
62 * then all found segment types are returned.
63 */
64 @NotNull
65 public static JpegSegmentData readSegments(@NotNull File file, @Nullable Iterable<JpegSegmentType> segmentTypes) throws JpegProcessingException, IOException
66 {
67 FileInputStream stream = null;
68 try {
69 stream = new FileInputStream(file);
70 return readSegments(new StreamReader(stream), segmentTypes);
71 } finally {
72 if (stream != null) {
73 stream.close();
74 }
75 }
76 }
77
78 /**
79 * Processes the provided JPEG data, and extracts the specified JPEG segments into a {@link JpegSegmentData} object.
80 * <p>
81 * Will not return SOS (start of scan) or EOI (end of image) segments.
82 *
83 * @param reader a {@link SequentialReader} from which the JPEG data will be read. It must be positioned at the
84 * beginning of the JPEG data stream.
85 * @param segmentTypes the set of JPEG segments types that are to be returned. If this argument is <code>null</code>
86 * then all found segment types are returned.
87 */
88 @NotNull
89 public static JpegSegmentData readSegments(@NotNull final SequentialReader reader, @Nullable Iterable<JpegSegmentType> segmentTypes) throws JpegProcessingException, IOException
90 {
91 // Must be big-endian
92 assert (reader.isMotorolaByteOrder());
93
94 // first two bytes should be JPEG magic number
95 final int magicNumber = reader.getUInt16();
96 if (magicNumber != 0xFFD8) {
97 throw new JpegProcessingException("JPEG data is expected to begin with 0xFFD8 (ÿØ) not 0x" + Integer.toHexString(magicNumber));
98 }
99
100 Set<Byte> segmentTypeBytes = null;
101 if (segmentTypes != null) {
102 segmentTypeBytes = new HashSet<Byte>();
103 for (JpegSegmentType segmentType : segmentTypes) {
104 segmentTypeBytes.add(segmentType.byteValue);
105 }
106 }
107
108 JpegSegmentData segmentData = new JpegSegmentData();
109
110 do {
111 // Find the segment marker. Markers are zero or more 0xFF bytes, followed
112 // by a 0xFF and then a byte not equal to 0x00 or 0xFF.
113
114 final short segmentIdentifier = reader.getUInt8();
115
116 // We must have at least one 0xFF byte
117 if (segmentIdentifier != 0xFF)
118 throw new JpegProcessingException("Expected JPEG segment start identifier 0xFF, not 0x" + Integer.toHexString(segmentIdentifier).toUpperCase());
119
120 // Read until we have a non-0xFF byte. This identifies the segment type.
121 byte segmentType = reader.getInt8();
122 while (segmentType == (byte)0xFF)
123 segmentType = reader.getInt8();
124
125 if (segmentType == 0)
126 throw new JpegProcessingException("Expected non-zero byte as part of JPEG marker identifier");
127
128 if (segmentType == SEGMENT_SOS) {
129 // The 'Start-Of-Scan' segment's length doesn't include the image data, instead would
130 // have to search for the two bytes: 0xFF 0xD9 (EOI).
131 // It comes last so simply return at this point
132 return segmentData;
133 }
134
135 if (segmentType == MARKER_EOI) {
136 // the 'End-Of-Image' segment -- this should never be found in this fashion
137 return segmentData;
138 }
139
140 // next 2-bytes are <segment-size>: [high-byte] [low-byte]
141 int segmentLength = reader.getUInt16();
142
143 // segment length includes size bytes, so subtract two
144 segmentLength -= 2;
145
146 if (segmentLength < 0)
147 throw new JpegProcessingException("JPEG segment size would be less than zero");
148
149 // Check whether we are interested in this segment
150 if (segmentTypeBytes == null || segmentTypeBytes.contains(segmentType)) {
151 byte[] segmentBytes = reader.getBytes(segmentLength);
152 assert (segmentLength == segmentBytes.length);
153 segmentData.addSegment(segmentType, segmentBytes);
154 } else {
155 // Some if the JPEG is truncated, just return what data we've already gathered
156 if (!reader.trySkip(segmentLength)) {
157 return segmentData;
158 }
159 }
160
161 } while (true);
162 }
163
164 private JpegSegmentReader() throws Exception
165 {
166 throw new Exception("Not intended for instantiation.");
167 }
168}
Note: See TracBrowser for help on using the repository browser.