Home | History | Annotate | Download | only in codec
      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 
     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 
     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 
     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 
     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  */
     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  */
    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  */
    117 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 
    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  */
    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  */
    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  */
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    597 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 
    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