OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkCodec.h" | 8 #include "SkCodec.h" |
9 #include "SkMSAN.h" | 9 #include "SkMSAN.h" |
10 #include "SkJpegCodec.h" | 10 #include "SkJpegCodec.h" |
(...skipping 11 matching lines...) Expand all Loading... | |
22 extern "C" { | 22 extern "C" { |
23 #include "jerror.h" | 23 #include "jerror.h" |
24 #include "jpeglib.h" | 24 #include "jpeglib.h" |
25 } | 25 } |
26 | 26 |
27 bool SkJpegCodec::IsJpeg(const void* buffer, size_t bytesRead) { | 27 bool SkJpegCodec::IsJpeg(const void* buffer, size_t bytesRead) { |
28 static const uint8_t jpegSig[] = { 0xFF, 0xD8, 0xFF }; | 28 static const uint8_t jpegSig[] = { 0xFF, 0xD8, 0xFF }; |
29 return bytesRead >= 3 && !memcmp(buffer, jpegSig, sizeof(jpegSig)); | 29 return bytesRead >= 3 && !memcmp(buffer, jpegSig, sizeof(jpegSig)); |
30 } | 30 } |
31 | 31 |
32 static uint32_t get_endian_short(const uint8_t* data, bool littleEndian) { | |
33 if (littleEndian) { | |
34 return (data[1] << 8) | (data[0]); | |
35 } | |
36 | |
37 return (data[0] << 8) | (data[1]); | |
38 } | |
39 | |
40 static uint16_t get_endian_int(const uint8_t* data, bool littleEndian) { | |
41 if (littleEndian) { | |
42 return (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | (data[0]); | |
43 } | |
44 | |
45 return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]); | |
46 } | |
47 | |
48 const uint32_t kExifHeaderSize = 14; | |
49 const uint32_t kICCHeaderSize = 14; | |
50 const uint32_t kExifMarker = JPEG_APP0 + 1; | |
51 const uint32_t kICCMarker = JPEG_APP0 + 2; | |
52 | |
53 static bool is_orientation_marker(jpeg_marker_struct* marker, SkCodec::Orientati on* orientation) { | |
54 if (kExifMarker != marker->marker || marker->data_length < kExifHeaderSize) { | |
55 return false; | |
56 } | |
57 | |
58 const uint8_t* data = marker->data; | |
59 static const uint8_t kExifSig[] { 'E', 'x', 'i', 'f', '\0' }; | |
60 if (memcmp(data, kExifSig, sizeof(kExifSig))) { | |
61 return false; | |
62 } | |
63 | |
64 // II indicates Intel (little endian) and MM indicates motorola (big endian) . | |
scroggo
2016/03/18 22:38:17
This could potentially be a common method. This al
msarett
2016/03/21 14:28:45
Done.
| |
65 if (('I' != data[6] || 'I' != data[7]) && ('M' != data[6] || 'M' != data[7]) ) { | |
66 return false; | |
67 } | |
68 bool littleEndian = ('I' == data[6]); | |
scroggo
2016/03/18 22:38:17
nit: could be const.
msarett
2016/03/21 14:28:45
Acknowledged. I've changed the implementation.
| |
69 | |
70 // Get the offset from the start of the marker. | |
71 // Account for 'E', 'x', 'i', 'f', '\0', '<fill byte>'. | |
72 uint32_t offset = get_endian_int(data + 10, littleEndian); | |
73 offset += sizeof(kExifSig) + 1; | |
74 | |
75 // Require that the marker is at least large enough to contain the number of entries. | |
76 if (marker->data_length < offset + 2) { | |
77 return false; | |
78 } | |
79 uint32_t numEntries = get_endian_short(data + offset, littleEndian); | |
80 | |
81 // Tag (2 bytes), Datatype (2 bytes), Number of elements (4 bytes), Data (4 bytes) | |
82 const uint32_t kEntrySize = 12; | |
83 numEntries = SkTMin(numEntries, (marker->data_length - offset - 2) / kEntryS ize); | |
84 | |
85 // Advance the data to the start of the entries. | |
86 data += offset + 2; | |
87 | |
88 const uint16_t kOrientationTag = 0x112; | |
89 const uint16_t kOrientationType = 3; | |
90 for (uint32_t i = 0; i < numEntries; i++, data += kEntrySize) { | |
91 uint16_t tag = get_endian_short(data, littleEndian); | |
92 uint16_t type = get_endian_short(data + 2, littleEndian); | |
93 uint32_t count = get_endian_int(data + 4, littleEndian); | |
94 if (kOrientationTag == tag && kOrientationType == type && 1 == count) { | |
95 uint16_t val = get_endian_short(data + 8, littleEndian); | |
96 if (0 < val && val <= SkCodec::kLast_Orientation) { | |
97 *orientation = (SkCodec::Orientation) val; | |
98 return true; | |
99 } | |
100 } | |
101 } | |
102 | |
103 return false; | |
104 } | |
105 | |
106 static SkCodec::Orientation get_exif_orientation(jpeg_decompress_struct* dinfo) { | |
107 SkCodec::Orientation orientation; | |
108 for (jpeg_marker_struct* marker = dinfo->marker_list; marker; marker = marke r->next) { | |
109 if (is_orientation_marker(marker, &orientation)) { | |
110 return orientation; | |
111 } | |
112 } | |
113 | |
114 return SkCodec::kDefault_Orientation; | |
115 } | |
116 | |
117 static bool is_icc_marker(jpeg_marker_struct* marker) { | |
118 if (kICCMarker != marker->marker || marker->data_length < kICCHeaderSize) { | |
119 return nullptr; | |
120 } | |
121 | |
122 static const uint8_t kICCSig[] { 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I' , 'L', 'E', '\0' }; | |
123 if (memcmp(marker->data, kICCSig, sizeof(kICCSig))) { | |
scroggo
2016/03/18 22:38:17
Why not
return !memcmp(...)
msarett
2016/03/21 14:28:45
Agreed. Fixing this.
| |
124 return false; | |
125 } | |
126 | |
127 return true; | |
128 } | |
129 | |
130 /* | |
131 * ICC profiles may be stored using a sequence of multiple markers. We obtain t he ICC profile | |
132 * in two steps: | |
133 * (1) Discover all ICC profile markers and verify that they are numbered pr operly. | |
134 * (2) Copy the data from each marker into a contiguous ICC profile. | |
135 */ | |
136 static sk_sp<SkColorSpace> get_icc_profile(jpeg_decompress_struct* dinfo) { | |
137 // Note that 256 will be enough storage space since each markerIndex is stor ed in 8-bits. | |
138 jpeg_marker_struct* markerSequence[256]; | |
139 memset(markerSequence, 0, sizeof(markerSequence)); | |
140 uint8_t numMarkers = 0; | |
141 size_t totalBytes = 0; | |
142 | |
143 // Discover any ICC markers and verify that they are numbered properly. | |
144 for (jpeg_marker_struct* marker = dinfo->marker_list; marker; marker = marke r->next) { | |
145 if (is_icc_marker(marker)) { | |
146 // Verify that numMarkers is valid and consistent. | |
147 if (0 == numMarkers) { | |
148 numMarkers = marker->data[13]; | |
149 if (0 == numMarkers) { | |
150 SkCodecPrintf("ICC Profile Error: numMarkers must be greater than zero.\n"); | |
151 return nullptr; | |
152 } | |
153 } else if (numMarkers != marker->data[13]) { | |
154 SkCodecPrintf("ICC Profile Error: numMarkers must be consistent. \n"); | |
155 return nullptr; | |
156 } | |
157 | |
158 // Verify that the markerIndex is valid and unique. Note that zero is not | |
159 // a valid index. | |
160 uint8_t markerIndex = marker->data[12]; | |
161 if (markerIndex == 0 || markerIndex > numMarkers) { | |
162 SkCodecPrintf("ICC Profile Error: markerIndex is invalid.\n"); | |
163 return nullptr; | |
164 } | |
165 if (markerSequence[markerIndex]) { | |
166 SkCodecPrintf("ICC Profile Error: Duplicate value of markerIndex .\n"); | |
167 return nullptr; | |
168 } | |
169 markerSequence[markerIndex] = marker; | |
170 SkASSERT(marker->data_length >= kICCHeaderSize); | |
171 totalBytes += marker->data_length - kICCHeaderSize; | |
172 } | |
173 } | |
174 | |
175 if (0 == totalBytes) { | |
176 // No non-empty ICC profile markers were found. | |
177 return nullptr; | |
178 } | |
179 | |
180 // Combine the ICC marker data into a contiguous profile. | |
181 SkAutoMalloc iccData(totalBytes); | |
182 void* dst = iccData.get(); | |
183 for (uint32_t i = 1; i <= numMarkers; i++) { | |
184 jpeg_marker_struct* marker = markerSequence[i]; | |
185 if (!marker) { | |
186 SkCodecPrintf("ICC Profile Error: Missing marker %d of %d.\n", i, nu mMarkers); | |
187 return nullptr; | |
188 } | |
189 | |
190 void* src = SkTAddOffset<void>(marker->data, kICCHeaderSize); | |
191 size_t bytes = marker->data_length - kICCHeaderSize; | |
192 memcpy(dst, src, bytes); | |
193 dst = SkTAddOffset<void>(dst, bytes); | |
194 } | |
195 | |
196 return SkColorSpace::NewICC(iccData.get(), totalBytes); | |
197 } | |
198 | |
32 bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, | 199 bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, |
33 JpegDecoderMgr** decoderMgrOut) { | 200 JpegDecoderMgr** decoderMgrOut) { |
34 | 201 |
35 // Create a JpegDecoderMgr to own all of the decompress information | 202 // Create a JpegDecoderMgr to own all of the decompress information |
36 SkAutoTDelete<JpegDecoderMgr> decoderMgr(new JpegDecoderMgr(stream)); | 203 SkAutoTDelete<JpegDecoderMgr> decoderMgr(new JpegDecoderMgr(stream)); |
37 | 204 |
38 // libjpeg errors will be caught and reported here | 205 // libjpeg errors will be caught and reported here |
39 if (setjmp(decoderMgr->getJmpBuf())) { | 206 if (setjmp(decoderMgr->getJmpBuf())) { |
40 return decoderMgr->returnFalse("setjmp"); | 207 return decoderMgr->returnFalse("setjmp"); |
41 } | 208 } |
42 | 209 |
43 // Initialize the decompress info and the source manager | 210 // Initialize the decompress info and the source manager |
44 decoderMgr->init(); | 211 decoderMgr->init(); |
45 | 212 |
213 // Instruct jpeg library to save the markers that we care about. Since | |
214 // the orientation and color profile will not change, we can skip this | |
215 // step on rewinds. | |
216 if (codecOut) { | |
217 jpeg_save_markers(decoderMgr->dinfo(), kExifMarker, 0xFFFF); | |
218 jpeg_save_markers(decoderMgr->dinfo(), kICCMarker, 0xFFFF); | |
219 } | |
220 | |
46 // Read the jpeg header | 221 // Read the jpeg header |
47 if (JPEG_HEADER_OK != jpeg_read_header(decoderMgr->dinfo(), true)) { | 222 if (JPEG_HEADER_OK != jpeg_read_header(decoderMgr->dinfo(), true)) { |
48 return decoderMgr->returnFalse("read_header"); | 223 return decoderMgr->returnFalse("read_header"); |
49 } | 224 } |
50 | 225 |
51 if (nullptr != codecOut) { | 226 if (codecOut) { |
52 // Recommend the color type to decode to | 227 // Recommend the color type to decode to |
53 const SkColorType colorType = decoderMgr->getColorType(); | 228 const SkColorType colorType = decoderMgr->getColorType(); |
54 | 229 |
55 // Create image info object and the codec | 230 // Create image info object and the codec |
56 const SkImageInfo& imageInfo = SkImageInfo::Make(decoderMgr->dinfo()->im age_width, | 231 const SkImageInfo& imageInfo = SkImageInfo::Make(decoderMgr->dinfo()->im age_width, |
57 decoderMgr->dinfo()->image_height, colorType, kOpaque_SkAlphaTyp e); | 232 decoderMgr->dinfo()->image_height, colorType, kOpaque_SkAlphaTyp e); |
58 *codecOut = new SkJpegCodec(imageInfo, stream, decoderMgr.release()); | 233 |
234 Orientation orientation = get_exif_orientation(decoderMgr->dinfo()); | |
235 sk_sp<SkColorSpace> colorSpace = get_icc_profile(decoderMgr->dinfo()); | |
236 | |
237 *codecOut = new SkJpegCodec(imageInfo, stream, decoderMgr.release(), col orSpace, | |
238 orientation); | |
59 } else { | 239 } else { |
60 SkASSERT(nullptr != decoderMgrOut); | 240 SkASSERT(nullptr != decoderMgrOut); |
61 *decoderMgrOut = decoderMgr.release(); | 241 *decoderMgrOut = decoderMgr.release(); |
62 } | 242 } |
63 return true; | 243 return true; |
64 } | 244 } |
65 | 245 |
66 SkCodec* SkJpegCodec::NewFromStream(SkStream* stream) { | 246 SkCodec* SkJpegCodec::NewFromStream(SkStream* stream) { |
67 SkAutoTDelete<SkStream> streamDeleter(stream); | 247 SkAutoTDelete<SkStream> streamDeleter(stream); |
68 SkCodec* codec = nullptr; | 248 SkCodec* codec = nullptr; |
69 if (ReadHeader(stream, &codec, nullptr)) { | 249 if (ReadHeader(stream, &codec, nullptr)) { |
70 // Codec has taken ownership of the stream, we do not need to delete it | 250 // Codec has taken ownership of the stream, we do not need to delete it |
71 SkASSERT(codec); | 251 SkASSERT(codec); |
72 streamDeleter.release(); | 252 streamDeleter.release(); |
73 return codec; | 253 return codec; |
74 } | 254 } |
75 return nullptr; | 255 return nullptr; |
76 } | 256 } |
77 | 257 |
78 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, | 258 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, |
79 JpegDecoderMgr* decoderMgr) | 259 JpegDecoderMgr* decoderMgr, sk_sp<SkColorSpace> colorSpace, Orientation orientation) |
80 : INHERITED(srcInfo, stream) | 260 : INHERITED(srcInfo, stream, colorSpace, orientation) |
81 , fDecoderMgr(decoderMgr) | 261 , fDecoderMgr(decoderMgr) |
82 , fReadyState(decoderMgr->dinfo()->global_state) | 262 , fReadyState(decoderMgr->dinfo()->global_state) |
83 , fSwizzlerSubset(SkIRect::MakeEmpty()) | 263 , fSwizzlerSubset(SkIRect::MakeEmpty()) |
84 {} | 264 {} |
85 | 265 |
86 /* | 266 /* |
87 * Return the row bytes of a particular image type and width | 267 * Return the row bytes of a particular image type and width |
88 */ | 268 */ |
89 static size_t get_row_bytes(const j_decompress_ptr dinfo) { | 269 static size_t get_row_bytes(const j_decompress_ptr dinfo) { |
90 #ifdef TURBO_HAS_565 | 270 #ifdef TURBO_HAS_565 |
(...skipping 638 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
729 | 909 |
730 JDIMENSION linesRead = jpeg_read_raw_data(dinfo, yuv, numRowsPerBlock); | 910 JDIMENSION linesRead = jpeg_read_raw_data(dinfo, yuv, numRowsPerBlock); |
731 if (linesRead < remainingRows) { | 911 if (linesRead < remainingRows) { |
732 // FIXME: Handle incomplete YUV decodes without signalling an error. | 912 // FIXME: Handle incomplete YUV decodes without signalling an error. |
733 return kInvalidInput; | 913 return kInvalidInput; |
734 } | 914 } |
735 } | 915 } |
736 | 916 |
737 return kSuccess; | 917 return kSuccess; |
738 } | 918 } |
OLD | NEW |