• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkCodec.h"
9 #include "SkMSAN.h"
10 #include "SkJpegCodec.h"
11 #include "SkJpegDecoderMgr.h"
12 #include "SkJpegUtility_codec.h"
13 #include "SkCodecPriv.h"
14 #include "SkColorPriv.h"
15 #include "SkStream.h"
16 #include "SkTemplates.h"
17 #include "SkTypes.h"
18 
19 // stdio is needed for libjpeg-turbo
20 #include <stdio.h>
21 
22 extern "C" {
23     #include "jerror.h"
24     #include "jpeglib.h"
25 }
26 
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 };
29     return bytesRead >= 3 && !memcmp(buffer, jpegSig, sizeof(jpegSig));
30 }
31 
ReadHeader(SkStream * stream,SkCodec ** codecOut,JpegDecoderMgr ** decoderMgrOut)32 bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
33         JpegDecoderMgr** decoderMgrOut) {
34 
35     // Create a JpegDecoderMgr to own all of the decompress information
36     SkAutoTDelete<JpegDecoderMgr> decoderMgr(new JpegDecoderMgr(stream));
37 
38     // libjpeg errors will be caught and reported here
39     if (setjmp(decoderMgr->getJmpBuf())) {
40         return decoderMgr->returnFalse("setjmp");
41     }
42 
43     // Initialize the decompress info and the source manager
44     decoderMgr->init();
45 
46     // Read the jpeg header
47     if (JPEG_HEADER_OK != jpeg_read_header(decoderMgr->dinfo(), true)) {
48         return decoderMgr->returnFalse("read_header");
49     }
50 
51     if (nullptr != codecOut) {
52         // Recommend the color type to decode to
53         const SkColorType colorType = decoderMgr->getColorType();
54 
55         // Create image info object and the codec
56         const SkImageInfo& imageInfo = SkImageInfo::Make(decoderMgr->dinfo()->image_width,
57                 decoderMgr->dinfo()->image_height, colorType, kOpaque_SkAlphaType);
58         *codecOut = new SkJpegCodec(imageInfo, stream, decoderMgr.detach());
59     } else {
60         SkASSERT(nullptr != decoderMgrOut);
61         *decoderMgrOut = decoderMgr.detach();
62     }
63     return true;
64 }
65 
NewFromStream(SkStream * stream)66 SkCodec* SkJpegCodec::NewFromStream(SkStream* stream) {
67     SkAutoTDelete<SkStream> streamDeleter(stream);
68     SkCodec* codec = nullptr;
69     if (ReadHeader(stream,  &codec, nullptr)) {
70         // Codec has taken ownership of the stream, we do not need to delete it
71         SkASSERT(codec);
72         streamDeleter.detach();
73         return codec;
74     }
75     return nullptr;
76 }
77 
SkJpegCodec(const SkImageInfo & srcInfo,SkStream * stream,JpegDecoderMgr * decoderMgr)78 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream,
79         JpegDecoderMgr* decoderMgr)
80     : INHERITED(srcInfo, stream)
81     , fDecoderMgr(decoderMgr)
82     , fReadyState(decoderMgr->dinfo()->global_state)
83     , fSrcRow(nullptr)
84     , fSwizzlerSubset(SkIRect::MakeEmpty())
85 {}
86 
87 /*
88  * Return the row bytes of a particular image type and width
89  */
get_row_bytes(const j_decompress_ptr dinfo)90 static size_t get_row_bytes(const j_decompress_ptr dinfo) {
91 #ifdef TURBO_HAS_565
92     const size_t colorBytes = (dinfo->out_color_space == JCS_RGB565) ? 2 :
93             dinfo->out_color_components;
94 #else
95     const size_t colorBytes = dinfo->out_color_components;
96 #endif
97     return dinfo->output_width * colorBytes;
98 
99 }
100 
101 /*
102  *  Calculate output dimensions based on the provided factors.
103  *
104  *  Not to be used on the actual jpeg_decompress_struct used for decoding, since it will
105  *  incorrectly modify num_components.
106  */
calc_output_dimensions(jpeg_decompress_struct * dinfo,unsigned int num,unsigned int denom)107 void calc_output_dimensions(jpeg_decompress_struct* dinfo, unsigned int num, unsigned int denom) {
108     dinfo->num_components = 0;
109     dinfo->scale_num = num;
110     dinfo->scale_denom = denom;
111     jpeg_calc_output_dimensions(dinfo);
112 }
113 
114 /*
115  * Return a valid set of output dimensions for this decoder, given an input scale
116  */
onGetScaledDimensions(float desiredScale) const117 SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const {
118     // libjpeg-turbo supports scaling by 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1, so we will
119     // support these as well
120     unsigned int num;
121     unsigned int denom = 8;
122     if (desiredScale >= 0.9375) {
123         num = 8;
124     } else if (desiredScale >= 0.8125) {
125         num = 7;
126     } else if (desiredScale >= 0.6875f) {
127         num = 6;
128     } else if (desiredScale >= 0.5625f) {
129         num = 5;
130     } else if (desiredScale >= 0.4375f) {
131         num = 4;
132     } else if (desiredScale >= 0.3125f) {
133         num = 3;
134     } else if (desiredScale >= 0.1875f) {
135         num = 2;
136     } else {
137         num = 1;
138     }
139 
140     // Set up a fake decompress struct in order to use libjpeg to calculate output dimensions
141     jpeg_decompress_struct dinfo;
142     sk_bzero(&dinfo, sizeof(dinfo));
143     dinfo.image_width = this->getInfo().width();
144     dinfo.image_height = this->getInfo().height();
145     dinfo.global_state = fReadyState;
146     calc_output_dimensions(&dinfo, num, denom);
147 
148     // Return the calculated output dimensions for the given scale
149     return SkISize::Make(dinfo.output_width, dinfo.output_height);
150 }
151 
onRewind()152 bool SkJpegCodec::onRewind() {
153     JpegDecoderMgr* decoderMgr = nullptr;
154     if (!ReadHeader(this->stream(), nullptr, &decoderMgr)) {
155         return fDecoderMgr->returnFalse("could not rewind");
156     }
157     SkASSERT(nullptr != decoderMgr);
158     fDecoderMgr.reset(decoderMgr);
159 
160     fSwizzler.reset(nullptr);
161     fSrcRow = nullptr;
162     fStorage.free();
163 
164     return true;
165 }
166 
167 /*
168  * Checks if the conversion between the input image and the requested output
169  * image has been implemented
170  * Sets the output color space
171  */
setOutputColorSpace(const SkImageInfo & dst)172 bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dst) {
173     const SkImageInfo& src = this->getInfo();
174 
175     // Ensure that the profile type is unchanged
176     if (dst.profileType() != src.profileType()) {
177         return false;
178     }
179 
180     if (kUnknown_SkAlphaType == dst.alphaType()) {
181         return false;
182     }
183 
184     if (kOpaque_SkAlphaType != dst.alphaType()) {
185         SkCodecPrintf("Warning: an opaque image should be decoded as opaque "
186                       "- it is being decoded as non-opaque, which will draw slower\n");
187     }
188 
189     // Check if we will decode to CMYK because a conversion to RGBA is not supported
190     J_COLOR_SPACE colorSpace = fDecoderMgr->dinfo()->jpeg_color_space;
191     bool isCMYK = JCS_CMYK == colorSpace || JCS_YCCK == colorSpace;
192 
193     // Check for valid color types and set the output color space
194     switch (dst.colorType()) {
195         case kN32_SkColorType:
196             if (isCMYK) {
197                 fDecoderMgr->dinfo()->out_color_space = JCS_CMYK;
198             } else {
199 #ifdef LIBJPEG_TURBO_VERSION
200             // Check the byte ordering of the RGBA color space for the
201             // current platform
202     #ifdef SK_PMCOLOR_IS_RGBA
203             fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA;
204     #else
205             fDecoderMgr->dinfo()->out_color_space = JCS_EXT_BGRA;
206     #endif
207 #else
208             fDecoderMgr->dinfo()->out_color_space = JCS_RGB;
209 #endif
210             }
211             return true;
212         case kRGB_565_SkColorType:
213             if (isCMYK) {
214                 fDecoderMgr->dinfo()->out_color_space = JCS_CMYK;
215             } else {
216 #ifdef TURBO_HAS_565
217                 fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE;
218                 fDecoderMgr->dinfo()->out_color_space = JCS_RGB565;
219 #else
220                 fDecoderMgr->dinfo()->out_color_space = JCS_RGB;
221 #endif
222             }
223             return true;
224         case kGray_8_SkColorType:
225             if (isCMYK) {
226                 return false;
227             } else {
228                 // We will enable decodes to gray even if the image is color because this is
229                 // much faster than decoding to color and then converting
230                 fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE;
231             }
232             return true;
233         default:
234             return false;
235     }
236 }
237 
238 /*
239  * Checks if we can natively scale to the requested dimensions and natively scales the
240  * dimensions if possible
241  */
onDimensionsSupported(const SkISize & size)242 bool SkJpegCodec::onDimensionsSupported(const SkISize& size) {
243     if (setjmp(fDecoderMgr->getJmpBuf())) {
244         return fDecoderMgr->returnFalse("onDimensionsSupported/setjmp");
245     }
246 
247     const unsigned int dstWidth = size.width();
248     const unsigned int dstHeight = size.height();
249 
250     // Set up a fake decompress struct in order to use libjpeg to calculate output dimensions
251     // FIXME: Why is this necessary?
252     jpeg_decompress_struct dinfo;
253     sk_bzero(&dinfo, sizeof(dinfo));
254     dinfo.image_width = this->getInfo().width();
255     dinfo.image_height = this->getInfo().height();
256     dinfo.global_state = fReadyState;
257 
258     // libjpeg-turbo can scale to 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1
259     unsigned int num = 8;
260     const unsigned int denom = 8;
261     calc_output_dimensions(&dinfo, num, denom);
262     while (dinfo.output_width != dstWidth || dinfo.output_height != dstHeight) {
263 
264         // Return a failure if we have tried all of the possible scales
265         if (1 == num || dstWidth > dinfo.output_width || dstHeight > dinfo.output_height) {
266             return false;
267         }
268 
269         // Try the next scale
270         num -= 1;
271         calc_output_dimensions(&dinfo, num, denom);
272     }
273 
274     fDecoderMgr->dinfo()->scale_num = num;
275     fDecoderMgr->dinfo()->scale_denom = denom;
276     return true;
277 }
278 
279 /*
280  * Performs the jpeg decode
281  */
onGetPixels(const SkImageInfo & dstInfo,void * dst,size_t dstRowBytes,const Options & options,SkPMColor *,int *,int * rowsDecoded)282 SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
283                                          void* dst, size_t dstRowBytes,
284                                          const Options& options, SkPMColor*, int*,
285                                          int* rowsDecoded) {
286     if (options.fSubset) {
287         // Subsets are not supported.
288         return kUnimplemented;
289     }
290 
291     // Get a pointer to the decompress info since we will use it quite frequently
292     jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo();
293 
294     // Set the jump location for libjpeg errors
295     if (setjmp(fDecoderMgr->getJmpBuf())) {
296         return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
297     }
298 
299     // Check if we can decode to the requested destination and set the output color space
300     if (!this->setOutputColorSpace(dstInfo)) {
301         return fDecoderMgr->returnFailure("conversion_possible", kInvalidConversion);
302     }
303 
304     // Now, given valid output dimensions, we can start the decompress
305     if (!jpeg_start_decompress(dinfo)) {
306         return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
307     }
308 
309     // The recommended output buffer height should always be 1 in high quality modes.
310     // If it's not, we want to know because it means our strategy is not optimal.
311     SkASSERT(1 == dinfo->rec_outbuf_height);
312 
313     J_COLOR_SPACE colorSpace = dinfo->out_color_space;
314     if (JCS_CMYK == colorSpace || JCS_RGB == colorSpace) {
315         this->initializeSwizzler(dstInfo, options);
316     }
317 
318     // Perform the decode a single row at a time
319     uint32_t dstHeight = dstInfo.height();
320 
321     JSAMPLE* dstRow;
322     if (fSwizzler) {
323         // write data to storage row, then sample using swizzler
324         dstRow = fSrcRow;
325     } else {
326         // write data directly to dst
327         dstRow = (JSAMPLE*) dst;
328     }
329 
330     for (uint32_t y = 0; y < dstHeight; y++) {
331         // Read rows of the image
332         uint32_t lines = jpeg_read_scanlines(dinfo, &dstRow, 1);
333         sk_msan_mark_initialized(dstRow, dstRow + dstRowBytes, "skbug.com/4550");
334 
335         // If we cannot read enough rows, assume the input is incomplete
336         if (lines != 1) {
337             *rowsDecoded = y;
338 
339             return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput);
340         }
341 
342         if (fSwizzler) {
343             // use swizzler to sample row
344             fSwizzler->swizzle(dst, dstRow);
345             dst = SkTAddOffset<JSAMPLE>(dst, dstRowBytes);
346         } else {
347             dstRow = SkTAddOffset<JSAMPLE>(dstRow, dstRowBytes);
348         }
349     }
350 
351     return kSuccess;
352 }
353 
initializeSwizzler(const SkImageInfo & dstInfo,const Options & options)354 void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options) {
355     SkSwizzler::SrcConfig srcConfig = SkSwizzler::kUnknown;
356     if (JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) {
357         srcConfig = SkSwizzler::kCMYK;
358     } else {
359         // If the out_color_space is not CMYK, the only reason we would need a swizzler is
360         // for sampling and/or subsetting.
361         switch (dstInfo.colorType()) {
362             case kGray_8_SkColorType:
363                 srcConfig = SkSwizzler::kNoOp8;
364                 break;
365             case kN32_SkColorType:
366                 srcConfig = SkSwizzler::kNoOp32;
367                 break;
368             case kRGB_565_SkColorType:
369                 srcConfig = SkSwizzler::kNoOp16;
370                 break;
371             default:
372                 // This function should only be called if the colorType is supported by jpeg
373                 SkASSERT(false);
374         }
375     }
376 
377     if (JCS_RGB == fDecoderMgr->dinfo()->out_color_space) {
378         srcConfig = SkSwizzler::kRGB;
379     }
380 
381     Options swizzlerOptions = options;
382     if (options.fSubset) {
383         // Use fSwizzlerSubset if this is a subset decode.  This is necessary in the case
384         // where libjpeg-turbo provides a subset and then we need to subset it further.
385         // Also, verify that fSwizzlerSubset is initialized and valid.
386         SkASSERT(!fSwizzlerSubset.isEmpty() && fSwizzlerSubset.x() <= options.fSubset->x() &&
387                 fSwizzlerSubset.width() == options.fSubset->width());
388         swizzlerOptions.fSubset = &fSwizzlerSubset;
389     }
390     fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, dstInfo, swizzlerOptions));
391     SkASSERT(fSwizzler);
392     fStorage.reset(get_row_bytes(fDecoderMgr->dinfo()));
393     fSrcRow = fStorage.get();
394 }
395 
getSampler(bool createIfNecessary)396 SkSampler* SkJpegCodec::getSampler(bool createIfNecessary) {
397     if (!createIfNecessary || fSwizzler) {
398         SkASSERT(!fSwizzler || (fSrcRow && fStorage.get() == fSrcRow));
399         return fSwizzler;
400     }
401 
402     this->initializeSwizzler(this->dstInfo(), this->options());
403     return fSwizzler;
404 }
405 
onStartScanlineDecode(const SkImageInfo & dstInfo,const Options & options,SkPMColor ctable[],int * ctableCount)406 SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
407         const Options& options, SkPMColor ctable[], int* ctableCount) {
408     // Set the jump location for libjpeg errors
409     if (setjmp(fDecoderMgr->getJmpBuf())) {
410         SkCodecPrintf("setjmp: Error from libjpeg\n");
411         return kInvalidInput;
412     }
413 
414     // Check if we can decode to the requested destination and set the output color space
415     if (!this->setOutputColorSpace(dstInfo)) {
416         return kInvalidConversion;
417     }
418 
419     // Now, given valid output dimensions, we can start the decompress
420     if (!jpeg_start_decompress(fDecoderMgr->dinfo())) {
421         SkCodecPrintf("start decompress failed\n");
422         return kInvalidInput;
423     }
424 
425     if (options.fSubset) {
426         fSwizzlerSubset = *options.fSubset;
427     }
428 
429 #ifdef TURBO_HAS_CROP
430     if (options.fSubset) {
431         uint32_t startX = options.fSubset->x();
432         uint32_t width = options.fSubset->width();
433 
434         // libjpeg-turbo may need to align startX to a multiple of the IDCT
435         // block size.  If this is the case, it will decrease the value of
436         // startX to the appropriate alignment and also increase the value
437         // of width so that the right edge of the requested subset remains
438         // the same.
439         jpeg_crop_scanline(fDecoderMgr->dinfo(), &startX, &width);
440 
441         SkASSERT(startX <= (uint32_t) options.fSubset->x());
442         SkASSERT(width >= (uint32_t) options.fSubset->width());
443         SkASSERT(startX + width >= (uint32_t) options.fSubset->right());
444 
445         // Instruct the swizzler (if it is necessary) to further subset the
446         // output provided by libjpeg-turbo.
447         //
448         // We set this here (rather than in the if statement below), so that
449         // if (1) we don't need a swizzler for the subset, and (2) we need a
450         // swizzler for CMYK, the swizzler will still use the proper subset
451         // dimensions.
452         //
453         // Note that the swizzler will ignore the y and height parameters of
454         // the subset.  Since the scanline decoder (and the swizzler) handle
455         // one row at a time, only the subsetting in the x-dimension matters.
456         fSwizzlerSubset.setXYWH(options.fSubset->x() - startX, 0,
457                 options.fSubset->width(), options.fSubset->height());
458 
459         // We will need a swizzler if libjpeg-turbo cannot provide the exact
460         // subset that we request.
461         if (startX != (uint32_t) options.fSubset->x() ||
462                 width != (uint32_t) options.fSubset->width()) {
463             this->initializeSwizzler(dstInfo, options);
464         }
465     }
466 
467     // Make sure we have a swizzler if we are converting from CMYK.
468     if (!fSwizzler && JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) {
469         this->initializeSwizzler(dstInfo, options);
470     }
471 #else
472     // We will need a swizzler if we are performing a subset decode or
473     // converting from CMYK.
474     J_COLOR_SPACE colorSpace = fDecoderMgr->dinfo()->out_color_space;
475     if (options.fSubset || JCS_CMYK == colorSpace || JCS_RGB == colorSpace) {
476         this->initializeSwizzler(dstInfo, options);
477     }
478 #endif
479 
480     return kSuccess;
481 }
482 
onGetScanlines(void * dst,int count,size_t dstRowBytes)483 int SkJpegCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
484     // Set the jump location for libjpeg errors
485     if (setjmp(fDecoderMgr->getJmpBuf())) {
486         return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
487     }
488     // Read rows one at a time
489     JSAMPLE* dstRow;
490     size_t srcRowBytes = get_row_bytes(fDecoderMgr->dinfo());
491     if (fSwizzler) {
492         // write data to storage row, then sample using swizzler
493         dstRow = fSrcRow;
494     } else {
495         // write data directly to dst
496         SkASSERT(count == 1 || dstRowBytes >= srcRowBytes);
497         dstRow = (JSAMPLE*) dst;
498     }
499 
500     for (int y = 0; y < count; y++) {
501         // Read row of the image
502         uint32_t rowsDecoded = jpeg_read_scanlines(fDecoderMgr->dinfo(), &dstRow, 1);
503         sk_msan_mark_initialized(dstRow, dstRow + srcRowBytes, "skbug.com/4550");
504         if (rowsDecoded != 1) {
505             fDecoderMgr->dinfo()->output_scanline = this->dstInfo().height();
506             return y;
507         }
508 
509         if (fSwizzler) {
510             // use swizzler to sample row
511             fSwizzler->swizzle(dst, dstRow);
512             dst = SkTAddOffset<JSAMPLE>(dst, dstRowBytes);
513         } else {
514             dstRow = SkTAddOffset<JSAMPLE>(dstRow, dstRowBytes);
515         }
516     }
517     return count;
518 }
519 
onSkipScanlines(int count)520 bool SkJpegCodec::onSkipScanlines(int count) {
521     // Set the jump location for libjpeg errors
522     if (setjmp(fDecoderMgr->getJmpBuf())) {
523         return fDecoderMgr->returnFalse("setjmp");
524     }
525 
526 #ifdef TURBO_HAS_SKIP
527     return (uint32_t) count == jpeg_skip_scanlines(fDecoderMgr->dinfo(), count);
528 #else
529     if (!fSrcRow) {
530         fStorage.reset(get_row_bytes(fDecoderMgr->dinfo()));
531         fSrcRow = fStorage.get();
532     }
533 
534     for (int y = 0; y < count; y++) {
535         if (1 != jpeg_read_scanlines(fDecoderMgr->dinfo(), &fSrcRow, 1)) {
536             return false;
537         }
538     }
539     return true;
540 #endif
541 }
542 
is_yuv_supported(jpeg_decompress_struct * dinfo)543 static bool is_yuv_supported(jpeg_decompress_struct* dinfo) {
544     // Scaling is not supported in raw data mode.
545     SkASSERT(dinfo->scale_num == dinfo->scale_denom);
546 
547     // I can't imagine that this would ever change, but we do depend on it.
548     static_assert(8 == DCTSIZE, "DCTSIZE (defined in jpeg library) should always be 8.");
549 
550     if (JCS_YCbCr != dinfo->jpeg_color_space) {
551         return false;
552     }
553 
554     SkASSERT(3 == dinfo->num_components);
555     SkASSERT(dinfo->comp_info);
556 
557     // It is possible to perform a YUV decode for any combination of
558     // horizontal and vertical sampling that is supported by
559     // libjpeg/libjpeg-turbo.  However, we will start by supporting only the
560     // common cases (where U and V have samp_factors of one).
561     //
562     // The definition of samp_factor is kind of the opposite of what SkCodec
563     // thinks of as a sampling factor.  samp_factor is essentially a
564     // multiplier, and the larger the samp_factor is, the more samples that
565     // there will be.  Ex:
566     //     U_plane_width = image_width * (U_h_samp_factor / max_h_samp_factor)
567     //
568     // Supporting cases where the samp_factors for U or V were larger than
569     // that of Y would be an extremely difficult change, given that clients
570     // allocate memory as if the size of the Y plane is always the size of the
571     // image.  However, this case is very, very rare.
572     if (!(1 == dinfo->comp_info[1].h_samp_factor) &&
573          (1 == dinfo->comp_info[1].v_samp_factor) &&
574          (1 == dinfo->comp_info[2].h_samp_factor) &&
575          (1 == dinfo->comp_info[2].v_samp_factor)) {
576         return false;
577     }
578 
579     // Support all common cases of Y samp_factors.
580     // TODO (msarett): As mentioned above, it would be possible to support
581     //                 more combinations of samp_factors.  The issues are:
582     //                 (1) Are there actually any images that are not covered
583     //                     by these cases?
584     //                 (2) How much complexity would be added to the
585     //                     implementation in order to support these rare
586     //                     cases?
587     int hSampY = dinfo->comp_info[0].h_samp_factor;
588     int vSampY = dinfo->comp_info[0].v_samp_factor;
589     return (1 == hSampY && 1 == vSampY) ||
590            (2 == hSampY && 1 == vSampY) ||
591            (2 == hSampY && 2 == vSampY) ||
592            (1 == hSampY && 2 == vSampY) ||
593            (4 == hSampY && 1 == vSampY) ||
594            (4 == hSampY && 2 == vSampY);
595 }
596 
onQueryYUV8(YUVSizeInfo * sizeInfo,SkYUVColorSpace * colorSpace) const597 bool SkJpegCodec::onQueryYUV8(YUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const {
598     jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo();
599     if (!is_yuv_supported(dinfo)) {
600         return false;
601     }
602 
603     sizeInfo->fYSize.set(dinfo->comp_info[0].downsampled_width,
604                          dinfo->comp_info[0].downsampled_height);
605     sizeInfo->fUSize.set(dinfo->comp_info[1].downsampled_width,
606                          dinfo->comp_info[1].downsampled_height);
607     sizeInfo->fVSize.set(dinfo->comp_info[2].downsampled_width,
608                          dinfo->comp_info[2].downsampled_height);
609     sizeInfo->fYWidthBytes = dinfo->comp_info[0].width_in_blocks * DCTSIZE;
610     sizeInfo->fUWidthBytes = dinfo->comp_info[1].width_in_blocks * DCTSIZE;
611     sizeInfo->fVWidthBytes = dinfo->comp_info[2].width_in_blocks * DCTSIZE;
612 
613     if (colorSpace) {
614         *colorSpace = kJPEG_SkYUVColorSpace;
615     }
616 
617     return true;
618 }
619 
onGetYUV8Planes(const YUVSizeInfo & sizeInfo,void * pixels[3])620 SkCodec::Result SkJpegCodec::onGetYUV8Planes(const YUVSizeInfo& sizeInfo, void* pixels[3]) {
621     YUVSizeInfo defaultInfo;
622 
623     // This will check is_yuv_supported(), so we don't need to here.
624     bool supportsYUV = this->onQueryYUV8(&defaultInfo, nullptr);
625     if (!supportsYUV || sizeInfo.fYSize != defaultInfo.fYSize ||
626             sizeInfo.fUSize != defaultInfo.fUSize ||
627             sizeInfo.fVSize != defaultInfo.fVSize ||
628             sizeInfo.fYWidthBytes < defaultInfo.fYWidthBytes ||
629             sizeInfo.fUWidthBytes < defaultInfo.fUWidthBytes ||
630             sizeInfo.fVWidthBytes < defaultInfo.fVWidthBytes) {
631         return fDecoderMgr->returnFailure("onGetYUV8Planes", kInvalidInput);
632     }
633 
634     // Set the jump location for libjpeg errors
635     if (setjmp(fDecoderMgr->getJmpBuf())) {
636         return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
637     }
638 
639     // Get a pointer to the decompress info since we will use it quite frequently
640     jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo();
641 
642     dinfo->raw_data_out = TRUE;
643     if (!jpeg_start_decompress(dinfo)) {
644         return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
645     }
646 
647     // A previous implementation claims that the return value of is_yuv_supported()
648     // may change after calling jpeg_start_decompress().  It looks to me like this
649     // was caused by a bug in the old code, but we'll be safe and check here.
650     SkASSERT(is_yuv_supported(dinfo));
651 
652     // Currently, we require that the Y plane dimensions match the image dimensions
653     // and that the U and V planes are the same dimensions.
654     SkASSERT(sizeInfo.fUSize == sizeInfo.fVSize);
655     SkASSERT((uint32_t) sizeInfo.fYSize.width() == dinfo->output_width &&
656             (uint32_t) sizeInfo.fYSize.height() == dinfo->output_height);
657 
658     // Build a JSAMPIMAGE to handle output from libjpeg-turbo.  A JSAMPIMAGE has
659     // a 2-D array of pixels for each of the components (Y, U, V) in the image.
660     // Cheat Sheet:
661     //     JSAMPIMAGE == JSAMPLEARRAY* == JSAMPROW** == JSAMPLE***
662     JSAMPARRAY yuv[3];
663 
664     // Set aside enough space for pointers to rows of Y, U, and V.
665     JSAMPROW rowptrs[2 * DCTSIZE + DCTSIZE + DCTSIZE];
666     yuv[0] = &rowptrs[0];           // Y rows (DCTSIZE or 2 * DCTSIZE)
667     yuv[1] = &rowptrs[2 * DCTSIZE]; // U rows (DCTSIZE)
668     yuv[2] = &rowptrs[3 * DCTSIZE]; // V rows (DCTSIZE)
669 
670     // Initialize rowptrs.
671     int numYRowsPerBlock = DCTSIZE * dinfo->comp_info[0].v_samp_factor;
672     for (int i = 0; i < numYRowsPerBlock; i++) {
673         rowptrs[i] = SkTAddOffset<JSAMPLE>(pixels[0], i * sizeInfo.fYWidthBytes);
674     }
675     for (int i = 0; i < DCTSIZE; i++) {
676         rowptrs[i + 2 * DCTSIZE] = SkTAddOffset<JSAMPLE>(pixels[1], i * sizeInfo.fUWidthBytes);
677         rowptrs[i + 3 * DCTSIZE] = SkTAddOffset<JSAMPLE>(pixels[2], i * sizeInfo.fVWidthBytes);
678     }
679 
680     // After each loop iteration, we will increment pointers to Y, U, and V.
681     size_t blockIncrementY = numYRowsPerBlock * sizeInfo.fYWidthBytes;
682     size_t blockIncrementU = DCTSIZE * sizeInfo.fUWidthBytes;
683     size_t blockIncrementV = DCTSIZE * sizeInfo.fVWidthBytes;
684 
685     uint32_t numRowsPerBlock = numYRowsPerBlock;
686 
687     // We intentionally round down here, as this first loop will only handle
688     // full block rows.  As a special case at the end, we will handle any
689     // remaining rows that do not make up a full block.
690     const int numIters = dinfo->output_height / numRowsPerBlock;
691     for (int i = 0; i < numIters; i++) {
692         JDIMENSION linesRead = jpeg_read_raw_data(dinfo, yuv, numRowsPerBlock);
693         if (linesRead < numRowsPerBlock) {
694             // FIXME: Handle incomplete YUV decodes without signalling an error.
695             return kInvalidInput;
696         }
697 
698         // Update rowptrs.
699         for (int i = 0; i < numYRowsPerBlock; i++) {
700             rowptrs[i] += blockIncrementY;
701         }
702         for (int i = 0; i < DCTSIZE; i++) {
703             rowptrs[i + 2 * DCTSIZE] += blockIncrementU;
704             rowptrs[i + 3 * DCTSIZE] += blockIncrementV;
705         }
706     }
707 
708     uint32_t remainingRows = dinfo->output_height - dinfo->output_scanline;
709     SkASSERT(remainingRows == dinfo->output_height % numRowsPerBlock);
710     SkASSERT(dinfo->output_scanline == numIters * numRowsPerBlock);
711     if (remainingRows > 0) {
712         // libjpeg-turbo needs memory to be padded by the block sizes.  We will fulfill
713         // this requirement using a dummy row buffer.
714         // FIXME: Should SkCodec have an extra memory buffer that can be shared among
715         //        all of the implementations that use temporary/garbage memory?
716         SkAutoTMalloc<JSAMPLE> dummyRow(sizeInfo.fYWidthBytes);
717         for (int i = remainingRows; i < numYRowsPerBlock; i++) {
718             rowptrs[i] = dummyRow.get();
719         }
720         int remainingUVRows = dinfo->comp_info[1].downsampled_height - DCTSIZE * numIters;
721         for (int i = remainingUVRows; i < DCTSIZE; i++) {
722             rowptrs[i + 2 * DCTSIZE] = dummyRow.get();
723             rowptrs[i + 3 * DCTSIZE] = dummyRow.get();
724         }
725 
726         JDIMENSION linesRead = jpeg_read_raw_data(dinfo, yuv, numRowsPerBlock);
727         if (linesRead < remainingRows) {
728             // FIXME: Handle incomplete YUV decodes without signalling an error.
729             return kInvalidInput;
730         }
731     }
732 
733     return kSuccess;
734 }
735