Chromium Code Reviews| 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 |