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