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