source: josm/trunk/src/com/drew/metadata/exif/ExifThumbnailDirectory.java@ 8243

Last change on this file since 8243 was 8243, checked in by Don-vip, 9 years ago

fix #11359 - update to metadata-extractor 2.8.1

  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 11.5 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 */
21
22package com.drew.metadata.exif;
23
24import com.drew.lang.annotations.NotNull;
25import com.drew.lang.annotations.Nullable;
26import com.drew.metadata.MetadataException;
27
28import java.io.FileOutputStream;
29import java.io.IOException;
30import java.util.HashMap;
31
32/**
33 * One of several Exif directories. Otherwise known as IFD1, this directory holds information about an embedded thumbnail image.
34 *
35 * @author Drew Noakes https://drewnoakes.com
36 */
37public class ExifThumbnailDirectory extends ExifDirectoryBase
38{
39 /**
40 * The offset to thumbnail image bytes.
41 */
42 public static final int TAG_THUMBNAIL_OFFSET = 0x0201;
43 /**
44 * The size of the thumbnail image data in bytes.
45 */
46 public static final int TAG_THUMBNAIL_LENGTH = 0x0202;
47
48 /**
49 * Shows compression method for Thumbnail.
50 * 1 = Uncompressed
51 * 2 = CCITT 1D
52 * 3 = T4/Group 3 Fax
53 * 4 = T6/Group 4 Fax
54 * 5 = LZW
55 * 6 = JPEG (old-style)
56 * 7 = JPEG
57 * 8 = Adobe Deflate
58 * 9 = JBIG B&W
59 * 10 = JBIG Color
60 * 32766 = Next
61 * 32771 = CCIRLEW
62 * 32773 = PackBits
63 * 32809 = Thunderscan
64 * 32895 = IT8CTPAD
65 * 32896 = IT8LW
66 * 32897 = IT8MP
67 * 32898 = IT8BL
68 * 32908 = PixarFilm
69 * 32909 = PixarLog
70 * 32946 = Deflate
71 * 32947 = DCS
72 * 34661 = JBIG
73 * 34676 = SGILog
74 * 34677 = SGILog24
75 * 34712 = JPEG 2000
76 * 34713 = Nikon NEF Compressed
77 */
78 public static final int TAG_THUMBNAIL_COMPRESSION = 0x0103;
79
80 @NotNull
81 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
82
83 static
84 {
85 addExifTagNames(_tagNameMap);
86
87 _tagNameMap.put(TAG_THUMBNAIL_COMPRESSION, "Thumbnail Compression");
88 _tagNameMap.put(TAG_THUMBNAIL_OFFSET, "Thumbnail Offset");
89 _tagNameMap.put(TAG_THUMBNAIL_LENGTH, "Thumbnail Length");
90 }
91
92 @Nullable
93 private byte[] _thumbnailData;
94
95 public ExifThumbnailDirectory()
96 {
97 this.setDescriptor(new ExifThumbnailDescriptor(this));
98 }
99
100 @Override
101 @NotNull
102 public String getName()
103 {
104 return "Exif Thumbnail";
105 }
106
107 @Override
108 @NotNull
109 protected HashMap<Integer, String> getTagNameMap()
110 {
111 return _tagNameMap;
112 }
113
114 public boolean hasThumbnailData()
115 {
116 return _thumbnailData != null;
117 }
118
119 @Nullable
120 public byte[] getThumbnailData()
121 {
122 return _thumbnailData;
123 }
124
125 public void setThumbnailData(@Nullable byte[] data)
126 {
127 _thumbnailData = data;
128 }
129
130 public void writeThumbnail(@NotNull String filename) throws MetadataException, IOException
131 {
132 byte[] data = _thumbnailData;
133
134 if (data == null)
135 throw new MetadataException("No thumbnail data exists.");
136
137 FileOutputStream stream = null;
138 try {
139 stream = new FileOutputStream(filename);
140 stream.write(data);
141 } finally {
142 if (stream != null)
143 stream.close();
144 }
145 }
146
147/*
148 // This thumbnail extraction code is not complete, and is included to assist anyone who feels like looking into
149 // it. Please share any progress with the original author, and hence the community. Thanks.
150
151 public Image getThumbnailImage() throws MetadataException
152 {
153 if (!hasThumbnailData())
154 return null;
155
156 int compression = 0;
157 try {
158 compression = this.getInt(ExifSubIFDDirectory.TAG_COMPRESSION);
159 } catch (Throwable e) {
160 this.addError("Unable to determine thumbnail type " + e.getMessage());
161 }
162
163 final byte[] thumbnailBytes = getThumbnailData();
164
165 if (compression == ExifSubIFDDirectory.COMPRESSION_JPEG)
166 {
167 // JPEG Thumbnail
168 // operate directly on thumbnailBytes
169 return decodeBytesAsImage(thumbnailBytes);
170 }
171 else if (compression == ExifSubIFDDirectory.COMPRESSION_NONE)
172 {
173 // uncompressed thumbnail (raw RGB data)
174 if (!this.containsTag(ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION))
175 return null;
176
177 try
178 {
179 // If the image is RGB format, then convert it to a bitmap
180 final int photometricInterpretation = this.getInt(ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION);
181 if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_RGB)
182 {
183 // RGB
184 Image image = createImageFromRawRgb(thumbnailBytes);
185 return image;
186 }
187 else if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_YCBCR)
188 {
189 // YCbCr
190 Image image = createImageFromRawYCbCr(thumbnailBytes);
191 return image;
192 }
193 else if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_MONOCHROME)
194 {
195 // Monochrome
196 return null;
197 }
198 } catch (Throwable e) {
199 this.addError("Unable to extract thumbnail: " + e.getMessage());
200 }
201 }
202 return null;
203 }
204
205 /**
206 * Handle the YCbCr thumbnail encoding used by Ricoh RDC4200/4300, Fuji DS-7/300 and DX-5/7/9 cameras.
207 *
208 * At DX-5/7/9, YCbCrSubsampling(0x0212) has values of '2,1', PlanarConfiguration(0x011c) has a value '1'. So the
209 * data align of this image is below.
210 *
211 * Y(0,0),Y(1,0),Cb(0,0),Cr(0,0), Y(2,0),Y(3,0),Cb(2,0),Cr(3.0), Y(4,0),Y(5,0),Cb(4,0),Cr(4,0). . . .
212 *
213 * The numbers in parenthesis are pixel coordinates. DX series' YCbCrCoefficients(0x0211) has values '0.299/0.587/0.114',
214 * ReferenceBlackWhite(0x0214) has values '0,255,128,255,128,255'. Therefore to convert from Y/Cb/Cr to RGB is;
215 *
216 * B(0,0)=(Cb-128)*(2-0.114*2)+Y(0,0)
217 * R(0,0)=(Cr-128)*(2-0.299*2)+Y(0,0)
218 * G(0,0)=(Y(0,0)-0.114*B(0,0)-0.299*R(0,0))/0.587
219 *
220 * Horizontal subsampling is a value '2', so you can calculate B(1,0)/R(1,0)/G(1,0) by using the Y(1,0) and Cr(0,0)/Cb(0,0).
221 * Repeat this conversion by value of ImageWidth(0x0100) and ImageLength(0x0101).
222 *
223 * @param thumbnailBytes
224 * @return
225 * @throws com.drew.metadata.MetadataException
226 * /
227 private Image createImageFromRawYCbCr(byte[] thumbnailBytes) throws MetadataException
228 {
229 /*
230 Y = 0.257R + 0.504G + 0.098B + 16
231 Cb = -0.148R - 0.291G + 0.439B + 128
232 Cr = 0.439R - 0.368G - 0.071B + 128
233
234 G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)
235 R = 1.164(Y-16) + 1.596(Cr-128)
236 B = 1.164(Y-16) + 2.018(Cb-128)
237
238 R, G and B range from 0 to 255.
239 Y ranges from 16 to 235.
240 Cb and Cr range from 16 to 240.
241
242 http://www.faqs.org/faqs/graphics/colorspace-faq/
243 * /
244
245 int length = thumbnailBytes.length; // this.getInt(ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS);
246 final int imageWidth = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);
247 final int imageHeight = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);
248// final int headerLength = 54;
249// byte[] result = new byte[length + headerLength];
250// // Add a windows BMP header described:
251// // http://www.onicos.com/staff/iz/formats/bmp.html
252// result[0] = 'B';
253// result[1] = 'M'; // File Type identifier
254// result[3] = (byte)(result.length / 256);
255// result[2] = (byte)result.length;
256// result[10] = (byte)headerLength;
257// result[14] = 40; // MS Windows BMP header
258// result[18] = (byte)imageWidth;
259// result[22] = (byte)imageHeight;
260// result[26] = 1; // 1 Plane
261// result[28] = 24; // Colour depth
262// result[34] = (byte)length;
263// result[35] = (byte)(length / 256);
264
265 final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
266
267 // order is YCbCr and image is upside down, bitmaps are BGR
268//// for (int i = headerLength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3)
269// {
270// final int y = thumbnailBytes[dataOffset - 2] & 0xFF;
271// final int cb = thumbnailBytes[dataOffset - 1] & 0xFF;
272// final int cr = thumbnailBytes[dataOffset] & 0xFF;
273// if (y<16 || y>235 || cb<16 || cb>240 || cr<16 || cr>240)
274// "".toString();
275//
276// int g = (int)(1.164*(y-16) - 0.391*(cb-128) - 0.813*(cr-128));
277// int r = (int)(1.164*(y-16) + 1.596*(cr-128));
278// int b = (int)(1.164*(y-16) + 2.018*(cb-128));
279//
280//// result[i] = (byte)b;
281//// result[i + 1] = (byte)g;
282//// result[i + 2] = (byte)r;
283//
284// // TODO compose the image here
285// image.setRGB(1, 2, 3);
286// }
287
288 return image;
289 }
290
291 /**
292 * Creates a thumbnail image in (Windows) BMP format from raw RGB data.
293 * @param thumbnailBytes
294 * @return
295 * @throws com.drew.metadata.MetadataException
296 * /
297 private Image createImageFromRawRgb(byte[] thumbnailBytes) throws MetadataException
298 {
299 final int length = thumbnailBytes.length; // this.getInt(ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS);
300 final int imageWidth = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);
301 final int imageHeight = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);
302// final int headerLength = 54;
303// final byte[] result = new byte[length + headerLength];
304// // Add a windows BMP header described:
305// // http://www.onicos.com/staff/iz/formats/bmp.html
306// result[0] = 'B';
307// result[1] = 'M'; // File Type identifier
308// result[3] = (byte)(result.length / 256);
309// result[2] = (byte)result.length;
310// result[10] = (byte)headerLength;
311// result[14] = 40; // MS Windows BMP header
312// result[18] = (byte)imageWidth;
313// result[22] = (byte)imageHeight;
314// result[26] = 1; // 1 Plane
315// result[28] = 24; // Colour depth
316// result[34] = (byte)length;
317// result[35] = (byte)(length / 256);
318
319 final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
320
321 // order is RGB and image is upside down, bitmaps are BGR
322// for (int i = headerLength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3)
323// {
324// byte b = thumbnailBytes[dataOffset - 2];
325// byte g = thumbnailBytes[dataOffset - 1];
326// byte r = thumbnailBytes[dataOffset];
327//
328// // TODO compose the image here
329// image.setRGB(1, 2, 3);
330// }
331
332 return image;
333 }
334*/
335}
Note: See TracBrowser for help on using the repository browser.