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() == kJPEG_SkEncodedFormat) {
     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 
     79     SkIRect* subset = options.fSubset;
     80     if (!subset || subset->size() == this->codec()->getInfo().dimensions()) {
     81         if (this->codec()->dimensionsSupported(info.dimensions())) {
     82             return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions,
     83                     options.fColorPtr, options.fColorCount);
     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     // Start the scanline decode.
    105     SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
    106             scaledSize.height());
    107     codecOptions.fSubset = &scanlineSubset;
    108     SkCodec::Result result = this->codec()->startScanlineDecode(info.makeWH(scaledSize.width(),
    109             scaledSize.height()), &codecOptions, options.fColorPtr, options.fColorCount);
    110     if (SkCodec::kSuccess != result) {
    111         return result;
    112     }
    113 
    114     // At this point, we are only concerned with subsetting.  Either no scale was
    115     // requested, or the this->codec() is handling the scale.
    116     switch (this->codec()->getScanlineOrder()) {
    117         case SkCodec::kTopDown_SkScanlineOrder:
    118         case SkCodec::kNone_SkScanlineOrder: {
    119             if (!this->codec()->skipScanlines(scaledSubsetY)) {
    120                 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
    121                         scaledSubsetHeight, 0);
    122                 return SkCodec::kIncompleteInput;
    123             }
    124 
    125             int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes);
    126             if (decodedLines != scaledSubsetHeight) {
    127                 return SkCodec::kIncompleteInput;
    128             }
    129             return SkCodec::kSuccess;
    130         }
    131         default:
    132             SkASSERT(false);
    133             return SkCodec::kUnimplemented;
    134     }
    135 }
    136 
    137 
    138 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels,
    139         size_t rowBytes, const AndroidOptions& options) {
    140     // We should only call this function when sampling.
    141     SkASSERT(options.fSampleSize > 1);
    142 
    143     // Create options struct for the codec.
    144     SkCodec::Options sampledOptions;
    145     sampledOptions.fZeroInitialized = options.fZeroInitialized;
    146 
    147     // FIXME: This was already called by onGetAndroidPixels. Can we reduce that?
    148     int sampleSize = options.fSampleSize;
    149     int nativeSampleSize;
    150     SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize);
    151 
    152     // Check if there is a subset.
    153     SkIRect subset;
    154     int subsetY = 0;
    155     int subsetWidth = nativeSize.width();
    156     int subsetHeight = nativeSize.height();
    157     if (options.fSubset) {
    158         // We will need to know about subsetting in the y-dimension in order to use the
    159         // scanline decoder.
    160         // Update the subset to account for scaling done by this->codec().
    161         SkIRect* subsetPtr = options.fSubset;
    162 
    163         // Do the divide ourselves, instead of calling get_scaled_dimension. If
    164         // X and Y are 0, they should remain 0, rather than being upgraded to 1
    165         // due to being smaller than the sampleSize.
    166         const int subsetX = subsetPtr->x() / nativeSampleSize;
    167         subsetY = subsetPtr->y() / nativeSampleSize;
    168 
    169         subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize);
    170         subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize);
    171 
    172         // The scanline decoder only needs to be aware of subsetting in the x-dimension.
    173         subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height());
    174         sampledOptions.fSubset = ⊂
    175     }
    176 
    177     // Start the scanline decode.
    178     SkCodec::Result result = this->codec()->startScanlineDecode(
    179             info.makeWH(nativeSize.width(), nativeSize.height()), &sampledOptions,
    180             options.fColorPtr, options.fColorCount);
    181     if (SkCodec::kSuccess != result) {
    182         return result;
    183     }
    184 
    185     SkSampler* sampler = this->codec()->getSampler(true);
    186     if (!sampler) {
    187         return SkCodec::kUnimplemented;
    188     }
    189 
    190     // Since we guarantee that output dimensions are always at least one (even if the sampleSize
    191     // is greater than a given dimension), the input sampleSize is not always the sampleSize that
    192     // we use in practice.
    193     const int sampleX = subsetWidth / info.width();
    194     const int sampleY = subsetHeight / info.height();
    195     if (sampler->setSampleX(sampleX) != info.width()) {
    196         return SkCodec::kInvalidScale;
    197     }
    198     if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
    199         return SkCodec::kInvalidScale;
    200     }
    201 
    202     const int samplingOffsetY = get_start_coord(sampleY);
    203     const int startY = samplingOffsetY + subsetY;
    204     int dstHeight = info.height();
    205     switch(this->codec()->getScanlineOrder()) {
    206         case SkCodec::kTopDown_SkScanlineOrder: {
    207             if (!this->codec()->skipScanlines(startY)) {
    208                 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
    209                         dstHeight, 0);
    210                 return SkCodec::kIncompleteInput;
    211             }
    212             void* pixelPtr = pixels;
    213             for (int y = 0; y < dstHeight; y++) {
    214                 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
    215                     this->codec()->fillIncompleteImage(info, pixels, rowBytes,
    216                             options.fZeroInitialized, dstHeight, y + 1);
    217                     return SkCodec::kIncompleteInput;
    218                 }
    219                 if (y < dstHeight - 1) {
    220                     if (!this->codec()->skipScanlines(sampleY - 1)) {
    221                         this->codec()->fillIncompleteImage(info, pixels, rowBytes,
    222                                 options.fZeroInitialized, dstHeight, y + 1);
    223                         return SkCodec::kIncompleteInput;
    224                     }
    225                 }
    226                 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
    227             }
    228             return SkCodec::kSuccess;
    229         }
    230         case SkCodec::kOutOfOrder_SkScanlineOrder:
    231         case SkCodec::kBottomUp_SkScanlineOrder: {
    232             // Note that these modes do not support subsetting.
    233             SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight);
    234             int y;
    235             for (y = 0; y < nativeSize.height(); y++) {
    236                 int srcY = this->codec()->nextScanline();
    237                 if (is_coord_necessary(srcY, sampleY, dstHeight)) {
    238                     void* pixelPtr = SkTAddOffset<void>(pixels,
    239                             rowBytes * get_dst_coord(srcY, sampleY));
    240                     if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
    241                         break;
    242                     }
    243                 } else {
    244                     if (!this->codec()->skipScanlines(1)) {
    245                         break;
    246                     }
    247                 }
    248             }
    249 
    250             if (nativeSize.height() == y) {
    251                 return SkCodec::kSuccess;
    252             }
    253 
    254             // We handle filling uninitialized memory here instead of using this->codec().
    255             // this->codec() does not know that we are sampling.
    256             const uint32_t fillValue = this->codec()->getFillValue(info.colorType());
    257             const SkImageInfo fillInfo = info.makeWH(info.width(), 1);
    258             for (; y < nativeSize.height(); y++) {
    259                 int srcY = this->codec()->outputScanline(y);
    260                 if (!is_coord_necessary(srcY, sampleY, dstHeight)) {
    261                     continue;
    262                 }
    263 
    264                 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY));
    265                 SkSampler::Fill(fillInfo, rowPtr, rowBytes, fillValue, options.fZeroInitialized);
    266             }
    267             return SkCodec::kIncompleteInput;
    268         }
    269         case SkCodec::kNone_SkScanlineOrder: {
    270             const int linesNeeded = subsetHeight - samplingOffsetY;
    271             SkAutoTMalloc<uint8_t> storage(linesNeeded * rowBytes);
    272             uint8_t* storagePtr = storage.get();
    273 
    274             if (!this->codec()->skipScanlines(startY)) {
    275                 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
    276                         dstHeight, 0);
    277                 return SkCodec::kIncompleteInput;
    278             }
    279             int scanlines = this->codec()->getScanlines(storagePtr, linesNeeded, rowBytes);
    280 
    281             for (int y = 0; y < dstHeight; y++) {
    282                 memcpy(pixels, storagePtr, info.minRowBytes());
    283                 storagePtr += sampleY * rowBytes;
    284                 pixels = SkTAddOffset<void>(pixels, rowBytes);
    285             }
    286 
    287             if (scanlines < dstHeight) {
    288                 // this->codec() has already handled filling uninitialized memory.
    289                 return SkCodec::kIncompleteInput;
    290             }
    291             return SkCodec::kSuccess;
    292         }
    293         default:
    294             SkASSERT(false);
    295             return SkCodec::kUnimplemented;
    296     }
    297 }
    298