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 "SkCodecPriv.h"
     10 #include "SkMath.h"
     11 #include "SkSampledCodec.h"
     12 #include "SkSampler.h"
     13 #include "SkTemplates.h"
     14 
     15 SkSampledCodec::SkSampledCodec(SkCodec* codec)
     16     : INHERITED(codec)
     17 {}
     18 
     19 SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const {
     20     SkISize preSampledSize = this->codec()->getInfo().dimensions();
     21     int sampleSize = *sampleSizePtr;
     22     SkASSERT(sampleSize > 1);
     23 
     24     if (nativeSampleSize) {
     25         *nativeSampleSize = 1;
     26     }
     27 
     28     // Only JPEG supports native downsampling.
     29     if (this->codec()->getEncodedFormat() == SkEncodedImageFormat::kJPEG) {
     30         // See if libjpeg supports this scale directly
     31         switch (sampleSize) {
     32             case 2:
     33             case 4:
     34             case 8:
     35                 // This class does not need to do any sampling.
     36                 *sampleSizePtr = 1;
     37                 return this->codec()->getScaledDimensions(get_scale_from_sample_size(sampleSize));
     38             default:
     39                 break;
     40         }
     41 
     42         // Check if sampleSize is a multiple of something libjpeg can support.
     43         int remainder;
     44         const int sampleSizes[] = { 8, 4, 2 };
     45         for (int supportedSampleSize : sampleSizes) {
     46             int actualSampleSize;
     47             SkTDivMod(sampleSize, supportedSampleSize, &actualSampleSize, &remainder);
     48             if (0 == remainder) {
     49                 float scale = get_scale_from_sample_size(supportedSampleSize);
     50 
     51                 // this->codec() will scale to this size.
     52                 preSampledSize = this->codec()->getScaledDimensions(scale);
     53 
     54                 // And then this class will sample it.
     55                 *sampleSizePtr = actualSampleSize;
     56                 if (nativeSampleSize) {
     57                     *nativeSampleSize = supportedSampleSize;
     58                 }
     59                 break;
     60             }
     61         }
     62     }
     63 
     64     return preSampledSize;
     65 }
     66 
     67 SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const {
     68     const SkISize size = this->accountForNativeScaling(&sampleSize);
     69     return SkISize::Make(get_scaled_dimension(size.width(), sampleSize),
     70                          get_scaled_dimension(size.height(), sampleSize));
     71 }
     72 
     73 SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels,
     74         size_t rowBytes, const AndroidOptions& options) {
     75     // Create an Options struct for the codec.
     76     SkCodec::Options codecOptions;
     77     codecOptions.fZeroInitialized = options.fZeroInitialized;
     78     codecOptions.fPremulBehavior = SkTransferFunctionBehavior::kIgnore;
     79 
     80     SkIRect* subset = options.fSubset;
     81     if (!subset || subset->size() == this->codec()->getInfo().dimensions()) {
     82         if (this->codec()->dimensionsSupported(info.dimensions())) {
     83             return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions,
     84                     options.fColorPtr, options.fColorCount);
     85         }
     86 
     87         // If the native codec does not support the requested scale, scale by sampling.
     88         return this->sampledDecode(info, pixels, rowBytes, options);
     89     }
     90 
     91     // We are performing a subset decode.
     92     int sampleSize = options.fSampleSize;
     93     SkISize scaledSize = this->getSampledDimensions(sampleSize);
     94     if (!this->codec()->dimensionsSupported(scaledSize)) {
     95         // If the native codec does not support the requested scale, scale by sampling.
     96         return this->sampledDecode(info, pixels, rowBytes, options);
     97     }
     98 
     99     // Calculate the scaled subset bounds.
    100     int scaledSubsetX = subset->x() / sampleSize;
    101     int scaledSubsetY = subset->y() / sampleSize;
    102     int scaledSubsetWidth = info.width();
    103     int scaledSubsetHeight = info.height();
    104 
    105     const SkImageInfo scaledInfo = info.makeWH(scaledSize.width(), scaledSize.height());
    106 
    107     {
    108         // Although startScanlineDecode expects the bottom and top to match the
    109         // SkImageInfo, startIncrementalDecode uses them to determine which rows to
    110         // decode.
    111         SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubsetY,
    112                                                       scaledSubsetWidth, scaledSubsetHeight);
    113         codecOptions.fSubset = &incrementalSubset;
    114         const SkCodec::Result startResult = this->codec()->startIncrementalDecode(
    115                 scaledInfo, pixels, rowBytes, &codecOptions,
    116                 options.fColorPtr, options.fColorCount);
    117         if (SkCodec::kSuccess == startResult) {
    118             int rowsDecoded;
    119             const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded);
    120             if (incResult == SkCodec::kSuccess) {
    121                 return SkCodec::kSuccess;
    122             }
    123             SkASSERT(SkCodec::kIncompleteInput == incResult);
    124 
    125             // FIXME: Can zero initialized be read from SkCodec::fOptions?
    126             this->codec()->fillIncompleteImage(scaledInfo, pixels, rowBytes,
    127                     options.fZeroInitialized, scaledSubsetHeight, rowsDecoded);
    128             return SkCodec::kIncompleteInput;
    129         } else if (startResult != SkCodec::kUnimplemented) {
    130             return startResult;
    131         }
    132         // Otherwise fall down to use the old scanline decoder.
    133         // codecOptions.fSubset will be reset below, so it will not continue to
    134         // point to the object that is no longer on the stack.
    135     }
    136 
    137     // Start the scanline decode.
    138     SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
    139             scaledSize.height());
    140     codecOptions.fSubset = &scanlineSubset;
    141 
    142     SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo,
    143             &codecOptions, options.fColorPtr, options.fColorCount);
    144     if (SkCodec::kSuccess != result) {
    145         return result;
    146     }
    147 
    148     // At this point, we are only concerned with subsetting.  Either no scale was
    149     // requested, or the this->codec() is handling the scale.
    150     // Note that subsetting is only supported for kTopDown, so this code will not be
    151     // reached for other orders.
    152     SkASSERT(this->codec()->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder);
    153     if (!this->codec()->skipScanlines(scaledSubsetY)) {
    154         this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
    155                 scaledSubsetHeight, 0);
    156         return SkCodec::kIncompleteInput;
    157     }
    158 
    159     int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes);
    160     if (decodedLines != scaledSubsetHeight) {
    161         return SkCodec::kIncompleteInput;
    162     }
    163     return SkCodec::kSuccess;
    164 }
    165 
    166 
    167 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels,
    168         size_t rowBytes, const AndroidOptions& options) {
    169     // We should only call this function when sampling.
    170     SkASSERT(options.fSampleSize > 1);
    171 
    172     // Create options struct for the codec.
    173     SkCodec::Options sampledOptions;
    174     sampledOptions.fZeroInitialized = options.fZeroInitialized;
    175     sampledOptions.fPremulBehavior = SkTransferFunctionBehavior::kIgnore;
    176 
    177     // FIXME: This was already called by onGetAndroidPixels. Can we reduce that?
    178     int sampleSize = options.fSampleSize;
    179     int nativeSampleSize;
    180     SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize);
    181 
    182     // Check if there is a subset.
    183     SkIRect subset;
    184     int subsetY = 0;
    185     int subsetWidth = nativeSize.width();
    186     int subsetHeight = nativeSize.height();
    187     if (options.fSubset) {
    188         // We will need to know about subsetting in the y-dimension in order to use the
    189         // scanline decoder.
    190         // Update the subset to account for scaling done by this->codec().
    191         const SkIRect* subsetPtr = options.fSubset;
    192 
    193         // Do the divide ourselves, instead of calling get_scaled_dimension. If
    194         // X and Y are 0, they should remain 0, rather than being upgraded to 1
    195         // due to being smaller than the sampleSize.
    196         const int subsetX = subsetPtr->x() / nativeSampleSize;
    197         subsetY = subsetPtr->y() / nativeSampleSize;
    198 
    199         subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize);
    200         subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize);
    201 
    202         // The scanline decoder only needs to be aware of subsetting in the x-dimension.
    203         subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height());
    204         sampledOptions.fSubset = ⊂
    205     }
    206 
    207     // Since we guarantee that output dimensions are always at least one (even if the sampleSize
    208     // is greater than a given dimension), the input sampleSize is not always the sampleSize that
    209     // we use in practice.
    210     const int sampleX = subsetWidth / info.width();
    211     const int sampleY = subsetHeight / info.height();
    212 
    213     const int samplingOffsetY = get_start_coord(sampleY);
    214     const int startY = samplingOffsetY + subsetY;
    215     const int dstHeight = info.height();
    216 
    217     const SkImageInfo nativeInfo = info.makeWH(nativeSize.width(), nativeSize.height());
    218 
    219     {
    220         // Although startScanlineDecode expects the bottom and top to match the
    221         // SkImageInfo, startIncrementalDecode uses them to determine which rows to
    222         // decode.
    223         SkCodec::Options incrementalOptions = sampledOptions;
    224         SkIRect incrementalSubset;
    225         if (sampledOptions.fSubset) {
    226             incrementalSubset.fTop = subsetY;
    227             incrementalSubset.fBottom = subsetY + subsetHeight;
    228             incrementalSubset.fLeft = sampledOptions.fSubset->fLeft;
    229             incrementalSubset.fRight = sampledOptions.fSubset->fRight;
    230             incrementalOptions.fSubset = &incrementalSubset;
    231         }
    232         const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo,
    233                 pixels, rowBytes, &incrementalOptions, options.fColorPtr, options.fColorCount);
    234         if (SkCodec::kSuccess == startResult) {
    235             SkSampler* sampler = this->codec()->getSampler(true);
    236             if (!sampler) {
    237                 return SkCodec::kUnimplemented;
    238             }
    239 
    240             if (sampler->setSampleX(sampleX) != info.width()) {
    241                 return SkCodec::kInvalidScale;
    242             }
    243             if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
    244                 return SkCodec::kInvalidScale;
    245             }
    246 
    247             sampler->setSampleY(sampleY);
    248 
    249             int rowsDecoded;
    250             const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded);
    251             if (incResult == SkCodec::kSuccess) {
    252                 return SkCodec::kSuccess;
    253             }
    254             SkASSERT(incResult == SkCodec::kIncompleteInput);
    255 
    256             SkASSERT(rowsDecoded <= info.height());
    257             this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
    258                                                info.height(), rowsDecoded);
    259             return SkCodec::kIncompleteInput;
    260         } else if (startResult != SkCodec::kUnimplemented) {
    261             return startResult;
    262         } // kUnimplemented means use the old method.
    263     }
    264 
    265     // Start the scanline decode.
    266     SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo,
    267             &sampledOptions, options.fColorPtr, options.fColorCount);
    268     if (SkCodec::kSuccess != result) {
    269         return result;
    270     }
    271 
    272     SkSampler* sampler = this->codec()->getSampler(true);
    273     if (!sampler) {
    274         return SkCodec::kUnimplemented;
    275     }
    276 
    277     if (sampler->setSampleX(sampleX) != info.width()) {
    278         return SkCodec::kInvalidScale;
    279     }
    280     if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
    281         return SkCodec::kInvalidScale;
    282     }
    283 
    284     switch(this->codec()->getScanlineOrder()) {
    285         case SkCodec::kTopDown_SkScanlineOrder: {
    286             if (!this->codec()->skipScanlines(startY)) {
    287                 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
    288                         dstHeight, 0);
    289                 return SkCodec::kIncompleteInput;
    290             }
    291             void* pixelPtr = pixels;
    292             for (int y = 0; y < dstHeight; y++) {
    293                 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
    294                     this->codec()->fillIncompleteImage(info, pixels, rowBytes,
    295                             options.fZeroInitialized, dstHeight, y + 1);
    296                     return SkCodec::kIncompleteInput;
    297                 }
    298                 if (y < dstHeight - 1) {
    299                     if (!this->codec()->skipScanlines(sampleY - 1)) {
    300                         this->codec()->fillIncompleteImage(info, pixels, rowBytes,
    301                                 options.fZeroInitialized, dstHeight, y + 1);
    302                         return SkCodec::kIncompleteInput;
    303                     }
    304                 }
    305                 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
    306             }
    307             return SkCodec::kSuccess;
    308         }
    309         case SkCodec::kBottomUp_SkScanlineOrder: {
    310             // Note that these modes do not support subsetting.
    311             SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight);
    312             int y;
    313             for (y = 0; y < nativeSize.height(); y++) {
    314                 int srcY = this->codec()->nextScanline();
    315                 if (is_coord_necessary(srcY, sampleY, dstHeight)) {
    316                     void* pixelPtr = SkTAddOffset<void>(pixels,
    317                             rowBytes * get_dst_coord(srcY, sampleY));
    318                     if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
    319                         break;
    320                     }
    321                 } else {
    322                     if (!this->codec()->skipScanlines(1)) {
    323                         break;
    324                     }
    325                 }
    326             }
    327 
    328             if (nativeSize.height() == y) {
    329                 return SkCodec::kSuccess;
    330             }
    331 
    332             // We handle filling uninitialized memory here instead of using this->codec().
    333             // this->codec() does not know that we are sampling.
    334             const uint64_t fillValue = this->codec()->getFillValue(info);
    335             const SkImageInfo fillInfo = info.makeWH(info.width(), 1);
    336             for (; y < nativeSize.height(); y++) {
    337                 int srcY = this->codec()->outputScanline(y);
    338                 if (!is_coord_necessary(srcY, sampleY, dstHeight)) {
    339                     continue;
    340                 }
    341 
    342                 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY));
    343                 SkSampler::Fill(fillInfo, rowPtr, rowBytes, fillValue, options.fZeroInitialized);
    344             }
    345             return SkCodec::kIncompleteInput;
    346         }
    347         default:
    348             SkASSERT(false);
    349             return SkCodec::kUnimplemented;
    350     }
    351 }
    352