Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 1 | // Copyright 2015 Google Inc. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | // |
| 15 | //////////////////////////////////////////////////////////////////////////////// |
| 16 | |
| 17 | #ifndef PIEX_TIFF_PARSER_H_ |
| 18 | #define PIEX_TIFF_PARSER_H_ |
| 19 | |
| 20 | #include <cstdint> |
| 21 | #include <memory> |
| 22 | #include <set> |
| 23 | #include <vector> |
| 24 | |
| 25 | #include "src/piex_types.h" |
| 26 | #include "src/tiff_directory/tiff_directory.h" |
| 27 | |
| 28 | namespace piex { |
| 29 | |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 30 | // Specifies the maximum number of pixels for thumbnails in each direction. |
Yujie Qin | be90819 | 2016-04-14 14:40:21 +0200 | [diff] [blame] | 31 | const int kThumbnailMaxDimension = 512; |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 32 | |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 33 | // Specifies all tags that might be of interest to get the preview data. |
Yujie Qin | 3eaa831 | 2016-01-15 16:22:20 +0100 | [diff] [blame] | 34 | enum GpsTags { |
| 35 | kGpsTagLatitudeRef = 1, |
| 36 | kGpsTagLatitude = 2, |
| 37 | kGpsTagLongitudeRef = 3, |
| 38 | kGpsTagLongitude = 4, |
| 39 | kGpsTagAltitudeRef = 5, |
| 40 | kGpsTagAltitude = 6, |
| 41 | kGpsTagTimeStamp = 7, |
| 42 | kGpsTagDateStamp = 29, |
| 43 | }; |
| 44 | |
| 45 | enum TiffTags { |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 46 | kExifTagColorSpace = 0xA001, |
| 47 | kExifTagDateTimeOriginal = 0x9003, |
| 48 | kExifTagDefaultCropSize = 0xC620, |
| 49 | kExifTagExposureTime = 0x829a, |
| 50 | kExifTagFnumber = 0x829d, |
| 51 | kExifTagFocalLength = 0x920A, |
| 52 | kExifTagGps = 0x8825, |
| 53 | kExifTagHeight = 0xA003, |
| 54 | kExifTagIsoSpeed = 0x8827, |
| 55 | kExifTagMakernotes = 0x927C, |
| 56 | kExifTagWidth = 0xA002, |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 57 | kOlymTagAspectFrame = 0x1113, |
| 58 | kOlymTagCameraSettings = 0x2020, |
| 59 | kOlymTagRawProcessing = 0x2040, |
| 60 | kPanaTagBottomBorder = 0x006, |
| 61 | kPanaTagIso = 0x0017, |
| 62 | kPanaTagJpegImage = 0x002E, |
| 63 | kPanaTagLeftBorder = 0x0005, |
| 64 | kPanaTagRightBorder = 0x007, |
| 65 | kPanaTagTopBorder = 0x0004, |
Yujie Qin | 3eaa831 | 2016-01-15 16:22:20 +0100 | [diff] [blame] | 66 | kPentaxTagColorSpace = 0x0037, |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 67 | kTiffTagArtist = 0x013B, |
| 68 | kTiffTagBitsPerSample = 0x0102, |
Eik Brauer | b415ce2 | 2016-02-01 12:26:23 +0100 | [diff] [blame] | 69 | kTiffTagCfaPatternDim = 0x828D, |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 70 | kTiffTagCompression = 0x0103, |
| 71 | kTiffTagDateTime = 0x0132, |
| 72 | kTiffTagExifIfd = 0x8769, |
| 73 | kTiffTagImageDescription = 0x010E, |
| 74 | kTiffTagImageLength = 0x0101, |
| 75 | kTiffTagImageWidth = 0x0100, |
| 76 | kTiffTagJpegByteCount = 0x0202, |
| 77 | kTiffTagJpegOffset = 0x0201, |
| 78 | kTiffTagMake = 0x010F, |
| 79 | kTiffTagModel = 0x0110, |
| 80 | kTiffTagOrientation = 0x0112, |
| 81 | kTiffTagPhotometric = 0x0106, |
| 82 | kTiffTagPlanarConfig = 0x011C, |
| 83 | kTiffTagResolutionUnit = 0x0128, |
| 84 | kTiffTagRowsPerStrip = 0x0116, |
| 85 | kTiffTagSamplesPerPixel = 0x0115, |
| 86 | kTiffTagSoftware = 0x0131, |
| 87 | kTiffTagStripByteCounts = 0x0117, |
| 88 | kTiffTagStripOffsets = 0x0111, |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 89 | kTiffTagSubFileType = 0x00FE, |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 90 | kTiffTagSubIfd = 0x014A, |
| 91 | kTiffTagTileByteCounts = 0x0145, |
| 92 | kTiffTagTileLength = 0x0143, |
| 93 | kTiffTagTileOffsets = 0x0144, |
| 94 | kTiffTagTileWidth = 0x0142, |
| 95 | kTiffTagXresolution = 0x011A, |
| 96 | kTiffTagYresolution = 0x011B, |
| 97 | }; |
| 98 | |
Eik Brauer | a954011 | 2016-01-27 13:25:41 +0100 | [diff] [blame] | 99 | typedef std::set<tiff_directory::TiffDirectory::Tag> TagSet; |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 100 | typedef std::vector<tiff_directory::TiffDirectory> IfdVector; |
| 101 | |
| 102 | struct TiffContent { |
| 103 | IfdVector tiff_directory; |
| 104 | std::unique_ptr<tiff_directory::TiffDirectory> exif_directory; |
| 105 | std::unique_ptr<tiff_directory::TiffDirectory> gps_directory; |
| 106 | }; |
| 107 | |
| 108 | // Reads 2 bytes, an unsigned 16bit from 'stream' at a certain 'offset'. The |
| 109 | // bytes get swapped according to the desired endianness returning true on |
| 110 | // success. Returns false when something is wrong. |
| 111 | bool Get16u(StreamInterface* stream, const std::uint32_t offset, |
| 112 | const tiff_directory::Endian& endian, std::uint16_t* value); |
| 113 | |
| 114 | // Reads 4 bytes, an unsigned 32bit 'value' from 'stream' at a certain 'offset'. |
| 115 | // The bytes get swapped according to the desired endianness returning true on |
| 116 | // success. Returns false when something is wrong. |
| 117 | bool Get32u(StreamInterface* stream, const std::uint32_t offset, |
| 118 | const tiff_directory::Endian& endian, std::uint32_t* value); |
| 119 | |
| 120 | // Retrieves a byte vector of size 'length' from 'stream' beginning at some |
| 121 | // 'offset' reading the data in chunks of one MiB. |
| 122 | // If 'error' is not set to kOk the returned value is invalid. |
| 123 | std::vector<std::uint8_t> GetData(const size_t offset, const size_t length, |
| 124 | StreamInterface* stream, Error* error); |
| 125 | |
| 126 | // Retrieves the endianness of TIFF compliant data at 'tiff_offset' from |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 127 | // 'stream' returning true on success. Returns false when something is wrong. |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 128 | bool GetEndianness(const std::uint32_t tiff_offset, StreamInterface* stream, |
| 129 | tiff_directory::Endian* endian); |
| 130 | |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 131 | // Retrieves an image from tiff_directory. Return false when something is wrong. |
| 132 | bool GetImageData(const tiff_directory::TiffDirectory& tiff_directory, |
| 133 | StreamInterface* stream, Image* image); |
| 134 | |
| 135 | // Retrieves the width and height from the jpeg image returning true on |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 136 | // success. Returns false when something is wrong. |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 137 | bool GetJpegDimensions(const std::uint32_t jpeg_offset, StreamInterface* stream, |
| 138 | std::uint16_t* width, std::uint16_t* height); |
| 139 | |
| 140 | // According to Tiff/EP a thumbnail has max 256 pixels per dimension. |
| 141 | // http://standardsproposals.bsigroup.com/Home/getPDF/567 |
| 142 | bool IsThumbnail(const Image& image, |
| 143 | const int max_dimension = kThumbnailMaxDimension); |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 144 | |
| 145 | // Parses through a Tiff IFD and writes all 'desired_tags' to a |
| 146 | // 'tiff_directory'. |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 147 | // Returns false if something with the Tiff data is wrong. |
| 148 | bool ParseDirectory(const std::uint32_t tiff_offset, |
| 149 | const std::uint32_t ifd_offset, |
| 150 | const tiff_directory::Endian endian, |
| 151 | const TagSet& desired_tags, StreamInterface* stream, |
| 152 | tiff_directory::TiffDirectory* tiff_directory, |
| 153 | std::uint32_t* next_ifd_offset); |
| 154 | |
| 155 | // Returns true if Exif orientation for the image can be obtained. False |
| 156 | // otherwise. |
| 157 | bool GetExifOrientation(StreamInterface* stream, const std::uint32_t offset, |
| 158 | std::uint32_t* orientation); |
| 159 | |
| 160 | // Reads the width and height of the full resolution image. The tag groups are |
| 161 | // exclusive. |
| 162 | bool GetFullDimension32(const tiff_directory::TiffDirectory& tiff_directory, |
| 163 | std::uint32_t* width, std::uint32_t* height); |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 164 | |
Yujie Qin | 8f540f6 | 2016-11-23 12:37:05 +0100 | [diff] [blame] | 165 | // Reads the width and height of the crop information if available. |
| 166 | // Returns false if an error occured. |
| 167 | bool GetFullCropDimension(const tiff_directory::TiffDirectory& tiff_directory, |
| 168 | std::uint32_t* width, std::uint32_t* height); |
| 169 | |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 170 | // Enables us to parse through data that complies to the Tiff/EP specification. |
| 171 | class TiffParser { |
| 172 | public: |
| 173 | // The caller owns 'stream' and is responsible to keep it alive while the |
| 174 | // TiffParser object is used. |
| 175 | explicit TiffParser(StreamInterface* stream); |
| 176 | TiffParser(StreamInterface* stream, const std::uint32_t offset); |
| 177 | |
| 178 | // Runs over the Tiff IFD, Exif IFD and subIFDs to get the preview image data. |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 179 | // Returns false if something with the Tiff tags is wrong. |
| 180 | bool GetPreviewImageData(const TiffContent& tiff_content, |
| 181 | PreviewImageData* image_metadata); |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 182 | |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 183 | // Returns false if called more that once or something with the Tiff data is |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 184 | // wrong. |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 185 | bool Parse(const TagSet& desired_tags, const std::uint16_t max_number_ifds, |
| 186 | TiffContent* tiff_content); |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 187 | |
| 188 | private: |
| 189 | // Disallow copy and assignment. |
| 190 | TiffParser(const TiffParser&) = delete; |
| 191 | TiffParser& operator=(const TiffParser&) = delete; |
| 192 | |
Yujie Qin | 2f5d8ea | 2016-04-06 12:55:08 +0200 | [diff] [blame] | 193 | bool ParseIfd(const std::uint32_t ifd_offset, const TagSet& desired_tags, |
| 194 | const std::uint16_t max_number_ifds, IfdVector* tiff_directory); |
| 195 | bool ParseGpsData(const tiff_directory::TiffDirectory* tiff_ifd, |
| 196 | TiffContent* tiff_content); |
Jaesung Chung | 2934553 | 2016-01-14 02:28:47 +0900 | [diff] [blame] | 197 | |
| 198 | StreamInterface* stream_ = nullptr; |
| 199 | std::uint32_t tiff_offset_ = 0; |
| 200 | tiff_directory::Endian endian_; |
| 201 | }; |
| 202 | |
| 203 | } // namespace piex |
| 204 | |
| 205 | #endif // PIEX_TIFF_PARSER_H_ |