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