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