Changeset 13061 in josm for trunk/src/com/drew/metadata/exif/ExifThumbnailDirectory.java
- Timestamp:
- 2017-10-30T22:46:09+01:00 (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/com/drew/metadata/exif/ExifThumbnailDirectory.java
r10862 r13061 1 1 /* 2 * Copyright 2002-201 6Drew Noakes2 * Copyright 2002-2017 Drew Noakes 3 3 * 4 4 * Licensed under the Apache License, Version 2.0 (the "License"); … … 22 22 package com.drew.metadata.exif; 23 23 24 import java.io.FileOutputStream;25 import java.io.IOException; 24 import com.drew.lang.annotations.NotNull; 25 26 26 import java.util.HashMap; 27 28 import com.drew.lang.annotations.NotNull;29 import com.drew.lang.annotations.Nullable;30 import com.drew.metadata.MetadataException;31 27 32 28 /** … … 35 31 * @author Drew Noakes https://drewnoakes.com 36 32 */ 33 @SuppressWarnings("WeakerAccess") 37 34 public class ExifThumbnailDirectory extends ExifDirectoryBase 38 35 { … … 46 43 public static final int TAG_THUMBNAIL_LENGTH = 0x0202; 47 44 45 /** 46 * @deprecated use {@link com.drew.metadata.exif.ExifDirectoryBase#TAG_COMPRESSION} instead. 47 */ 48 @Deprecated 49 public static final int TAG_THUMBNAIL_COMPRESSION = 0x0103; 50 48 51 @NotNull 49 52 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); … … 56 59 _tagNameMap.put(TAG_THUMBNAIL_LENGTH, "Thumbnail Length"); 57 60 } 58 59 @Nullable60 private byte[] _thumbnailData;61 61 62 62 public ExifThumbnailDirectory() … … 78 78 return _tagNameMap; 79 79 } 80 81 public boolean hasThumbnailData()82 {83 return _thumbnailData != null;84 }85 86 @Nullable87 public byte[] getThumbnailData()88 {89 return _thumbnailData;90 }91 92 public void setThumbnailData(@Nullable byte[] data)93 {94 _thumbnailData = data;95 }96 97 public void writeThumbnail(@NotNull String filename) throws MetadataException, IOException98 {99 byte[] data = _thumbnailData;100 101 if (data == null)102 throw new MetadataException("No thumbnail data exists.");103 104 FileOutputStream stream = null;105 try {106 stream = new FileOutputStream(filename);107 stream.write(data);108 } finally {109 if (stream != null)110 stream.close();111 }112 }113 114 /*115 // This thumbnail extraction code is not complete, and is included to assist anyone who feels like looking into116 // it. Please share any progress with the original author, and hence the community. Thanks.117 118 public Image getThumbnailImage() throws MetadataException119 {120 if (!hasThumbnailData())121 return null;122 123 int compression = 0;124 try {125 compression = this.getInt(ExifSubIFDDirectory.TAG_COMPRESSION);126 } catch (Throwable e) {127 this.addError("Unable to determine thumbnail type " + e.getMessage());128 }129 130 final byte[] thumbnailBytes = getThumbnailData();131 132 if (compression == ExifSubIFDDirectory.COMPRESSION_JPEG)133 {134 // JPEG Thumbnail135 // operate directly on thumbnailBytes136 return decodeBytesAsImage(thumbnailBytes);137 }138 else if (compression == ExifSubIFDDirectory.COMPRESSION_NONE)139 {140 // uncompressed thumbnail (raw RGB data)141 if (!this.containsTag(ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION))142 return null;143 144 try145 {146 // If the image is RGB format, then convert it to a bitmap147 final int photometricInterpretation = this.getInt(ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION);148 if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_RGB)149 {150 // RGB151 Image image = createImageFromRawRgb(thumbnailBytes);152 return image;153 }154 else if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_YCBCR)155 {156 // YCbCr157 Image image = createImageFromRawYCbCr(thumbnailBytes);158 return image;159 }160 else if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_MONOCHROME)161 {162 // Monochrome163 return null;164 }165 } catch (Throwable e) {166 this.addError("Unable to extract thumbnail: " + e.getMessage());167 }168 }169 return null;170 }171 172 /**173 * Handle the YCbCr thumbnail encoding used by Ricoh RDC4200/4300, Fuji DS-7/300 and DX-5/7/9 cameras.174 *175 * At DX-5/7/9, YCbCrSubsampling(0x0212) has values of '2,1', PlanarConfiguration(0x011c) has a value '1'. So the176 * data align of this image is below.177 *178 * 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). . . .179 *180 * The numbers in parenthesis are pixel coordinates. DX series' YCbCrCoefficients(0x0211) has values '0.299/0.587/0.114',181 * ReferenceBlackWhite(0x0214) has values '0,255,128,255,128,255'. Therefore to convert from Y/Cb/Cr to RGB is;182 *183 * B(0,0)=(Cb-128)*(2-0.114*2)+Y(0,0)184 * R(0,0)=(Cr-128)*(2-0.299*2)+Y(0,0)185 * G(0,0)=(Y(0,0)-0.114*B(0,0)-0.299*R(0,0))/0.587186 *187 * 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).188 * Repeat this conversion by value of ImageWidth(0x0100) and ImageLength(0x0101).189 *190 * @param thumbnailBytes191 * @return192 * @throws com.drew.metadata.MetadataException193 * /194 private Image createImageFromRawYCbCr(byte[] thumbnailBytes) throws MetadataException195 {196 /*197 Y = 0.257R + 0.504G + 0.098B + 16198 Cb = -0.148R - 0.291G + 0.439B + 128199 Cr = 0.439R - 0.368G - 0.071B + 128200 201 G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)202 R = 1.164(Y-16) + 1.596(Cr-128)203 B = 1.164(Y-16) + 2.018(Cb-128)204 205 R, G and B range from 0 to 255.206 Y ranges from 16 to 235.207 Cb and Cr range from 16 to 240.208 209 http://www.faqs.org/faqs/graphics/colorspace-faq/210 * /211 212 int length = thumbnailBytes.length; // this.getInt(ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS);213 final int imageWidth = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);214 final int imageHeight = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);215 // final int headerLength = 54;216 // byte[] result = new byte[length + headerLength];217 // // Add a windows BMP header described:218 // // http://www.onicos.com/staff/iz/formats/bmp.html219 // result[0] = 'B';220 // result[1] = 'M'; // File Type identifier221 // result[3] = (byte)(result.length / 256);222 // result[2] = (byte)result.length;223 // result[10] = (byte)headerLength;224 // result[14] = 40; // MS Windows BMP header225 // result[18] = (byte)imageWidth;226 // result[22] = (byte)imageHeight;227 // result[26] = 1; // 1 Plane228 // result[28] = 24; // Colour depth229 // result[34] = (byte)length;230 // result[35] = (byte)(length / 256);231 232 final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);233 234 // order is YCbCr and image is upside down, bitmaps are BGR235 //// for (int i = headerLength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3)236 // {237 // final int y = thumbnailBytes[dataOffset - 2] & 0xFF;238 // final int cb = thumbnailBytes[dataOffset - 1] & 0xFF;239 // final int cr = thumbnailBytes[dataOffset] & 0xFF;240 // if (y<16 || y>235 || cb<16 || cb>240 || cr<16 || cr>240)241 // "".toString();242 //243 // int g = (int)(1.164*(y-16) - 0.391*(cb-128) - 0.813*(cr-128));244 // int r = (int)(1.164*(y-16) + 1.596*(cr-128));245 // int b = (int)(1.164*(y-16) + 2.018*(cb-128));246 //247 //// result[i] = (byte)b;248 //// result[i + 1] = (byte)g;249 //// result[i + 2] = (byte)r;250 //251 // // TODO compose the image here252 // image.setRGB(1, 2, 3);253 // }254 255 return image;256 }257 258 /**259 * Creates a thumbnail image in (Windows) BMP format from raw RGB data.260 * @param thumbnailBytes261 * @return262 * @throws com.drew.metadata.MetadataException263 * /264 private Image createImageFromRawRgb(byte[] thumbnailBytes) throws MetadataException265 {266 final int length = thumbnailBytes.length; // this.getInt(ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS);267 final int imageWidth = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);268 final int imageHeight = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);269 // final int headerLength = 54;270 // final byte[] result = new byte[length + headerLength];271 // // Add a windows BMP header described:272 // // http://www.onicos.com/staff/iz/formats/bmp.html273 // result[0] = 'B';274 // result[1] = 'M'; // File Type identifier275 // result[3] = (byte)(result.length / 256);276 // result[2] = (byte)result.length;277 // result[10] = (byte)headerLength;278 // result[14] = 40; // MS Windows BMP header279 // result[18] = (byte)imageWidth;280 // result[22] = (byte)imageHeight;281 // result[26] = 1; // 1 Plane282 // result[28] = 24; // Colour depth283 // result[34] = (byte)length;284 // result[35] = (byte)(length / 256);285 286 final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);287 288 // order is RGB and image is upside down, bitmaps are BGR289 // for (int i = headerLength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3)290 // {291 // byte b = thumbnailBytes[dataOffset - 2];292 // byte g = thumbnailBytes[dataOffset - 1];293 // byte r = thumbnailBytes[dataOffset];294 //295 // // TODO compose the image here296 // image.setRGB(1, 2, 3);297 // }298 299 return image;300 }301 */302 80 }
Note:
See TracChangeset
for help on using the changeset viewer.