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 "SkBmpCodec.h"
      9 #include "SkCodec.h"
     10 #include "SkCodecPriv.h"
     11 #include "SkData.h"
     12 #include "SkGifCodec.h"
     13 #include "SkIcoCodec.h"
     14 #include "SkJpegCodec.h"
     15 #ifdef SK_CODEC_DECODES_PNG
     16 #include "SkPngCodec.h"
     17 #endif
     18 #include "SkRawCodec.h"
     19 #include "SkStream.h"
     20 #include "SkWbmpCodec.h"
     21 #include "SkWebpCodec.h"
     22 
     23 struct DecoderProc {
     24     bool (*IsFormat)(const void*, size_t);
     25     SkCodec* (*NewFromStream)(SkStream*);
     26 };
     27 
     28 static const DecoderProc gDecoderProcs[] = {
     29 #ifdef SK_CODEC_DECODES_JPEG
     30     { SkJpegCodec::IsJpeg, SkJpegCodec::NewFromStream },
     31 #endif
     32 #ifdef SK_CODEC_DECODES_WEBP
     33     { SkWebpCodec::IsWebp, SkWebpCodec::NewFromStream },
     34 #endif
     35 #ifdef SK_CODEC_DECODES_GIF
     36     { SkGifCodec::IsGif, SkGifCodec::NewFromStream },
     37 #endif
     38 #ifdef SK_CODEC_DECODES_PNG
     39     { SkIcoCodec::IsIco, SkIcoCodec::NewFromStream },
     40 #endif
     41     { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream },
     42     { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream }
     43 };
     44 
     45 size_t SkCodec::MinBufferedBytesNeeded() {
     46     return WEBP_VP8_HEADER_SIZE;
     47 }
     48 
     49 SkCodec* SkCodec::NewFromStream(SkStream* stream,
     50                                 SkPngChunkReader* chunkReader) {
     51     if (!stream) {
     52         return nullptr;
     53     }
     54 
     55     SkAutoTDelete<SkStream> streamDeleter(stream);
     56 
     57     // 14 is enough to read all of the supported types.
     58     const size_t bytesToRead = 14;
     59     SkASSERT(bytesToRead <= MinBufferedBytesNeeded());
     60 
     61     char buffer[bytesToRead];
     62     size_t bytesRead = stream->peek(buffer, bytesToRead);
     63 
     64     // It is also possible to have a complete image less than bytesToRead bytes
     65     // (e.g. a 1 x 1 wbmp), meaning peek() would return less than bytesToRead.
     66     // Assume that if bytesRead < bytesToRead, but > 0, the stream is shorter
     67     // than bytesToRead, so pass that directly to the decoder.
     68     // It also is possible the stream uses too small a buffer for peeking, but
     69     // we trust the caller to use a large enough buffer.
     70 
     71     if (0 == bytesRead) {
     72         // TODO: After implementing peek in CreateJavaOutputStreamAdaptor.cpp, this
     73         // printf could be useful to notice failures.
     74         // SkCodecPrintf("Encoded image data failed to peek!\n");
     75 
     76         // It is possible the stream does not support peeking, but does support
     77         // rewinding.
     78         // Attempt to read() and pass the actual amount read to the decoder.
     79         bytesRead = stream->read(buffer, bytesToRead);
     80         if (!stream->rewind()) {
     81             SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n");
     82             return nullptr;
     83         }
     84     }
     85 
     86     // PNG is special, since we want to be able to supply an SkPngChunkReader.
     87     // But this code follows the same pattern as the loop.
     88 #ifdef SK_CODEC_DECODES_PNG
     89     if (SkPngCodec::IsPng(buffer, bytesRead)) {
     90         return SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader);
     91     } else
     92 #endif
     93     {
     94         for (DecoderProc proc : gDecoderProcs) {
     95             if (proc.IsFormat(buffer, bytesRead)) {
     96                 return proc.NewFromStream(streamDeleter.detach());
     97             }
     98         }
     99 
    100 #ifdef SK_CODEC_DECODES_RAW
    101         // Try to treat the input as RAW if all the other checks failed.
    102         return SkRawCodec::NewFromStream(streamDeleter.detach());
    103 #endif
    104     }
    105 
    106     return nullptr;
    107 }
    108 
    109 SkCodec* SkCodec::NewFromData(SkData* data, SkPngChunkReader* reader) {
    110     if (!data) {
    111         return nullptr;
    112     }
    113     return NewFromStream(new SkMemoryStream(data), reader);
    114 }
    115 
    116 SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream)
    117     : fSrcInfo(info)
    118     , fStream(stream)
    119     , fNeedsRewind(false)
    120     , fDstInfo()
    121     , fOptions()
    122     , fCurrScanline(-1)
    123 {}
    124 
    125 SkCodec::~SkCodec() {}
    126 
    127 bool SkCodec::rewindIfNeeded() {
    128     if (!fStream) {
    129         // Some codecs do not have a stream, but they hold others that do. They
    130         // must handle rewinding themselves.
    131         return true;
    132     }
    133 
    134     // Store the value of fNeedsRewind so we can update it. Next read will
    135     // require a rewind.
    136     const bool needsRewind = fNeedsRewind;
    137     fNeedsRewind = true;
    138     if (!needsRewind) {
    139         return true;
    140     }
    141 
    142     // startScanlineDecode will need to be called before decoding scanlines.
    143     fCurrScanline = -1;
    144 
    145     if (!fStream->rewind()) {
    146         return false;
    147     }
    148 
    149     return this->onRewind();
    150 }
    151 
    152 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
    153                                    const Options* options, SkPMColor ctable[], int* ctableCount) {
    154     if (kUnknown_SkColorType == info.colorType()) {
    155         return kInvalidConversion;
    156     }
    157     if (nullptr == pixels) {
    158         return kInvalidParameters;
    159     }
    160     if (rowBytes < info.minRowBytes()) {
    161         return kInvalidParameters;
    162     }
    163 
    164     if (kIndex_8_SkColorType == info.colorType()) {
    165         if (nullptr == ctable || nullptr == ctableCount) {
    166             return kInvalidParameters;
    167         }
    168     } else {
    169         if (ctableCount) {
    170             *ctableCount = 0;
    171         }
    172         ctableCount = nullptr;
    173         ctable = nullptr;
    174     }
    175 
    176     if (!this->rewindIfNeeded()) {
    177         return kCouldNotRewind;
    178     }
    179 
    180     // Default options.
    181     Options optsStorage;
    182     if (nullptr == options) {
    183         options = &optsStorage;
    184     } else if (options->fSubset) {
    185         SkIRect subset(*options->fSubset);
    186         if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
    187             // FIXME: How to differentiate between not supporting subset at all
    188             // and not supporting this particular subset?
    189             return kUnimplemented;
    190         }
    191     }
    192 
    193     // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
    194     // because it supports arbitrary scaling/subset combinations.
    195     if (!this->dimensionsSupported(info.dimensions())) {
    196         return kInvalidScale;
    197     }
    198 
    199     // On an incomplete decode, the subclass will specify the number of scanlines that it decoded
    200     // successfully.
    201     int rowsDecoded = 0;
    202     const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount,
    203             &rowsDecoded);
    204 
    205     if ((kIncompleteInput == result || kSuccess == result) && ctableCount) {
    206         SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
    207     }
    208 
    209     // A return value of kIncompleteInput indicates a truncated image stream.
    210     // In this case, we will fill any uninitialized memory with a default value.
    211     // Some subclasses will take care of filling any uninitialized memory on
    212     // their own.  They indicate that all of the memory has been filled by
    213     // setting rowsDecoded equal to the height.
    214     if (kIncompleteInput == result && rowsDecoded != info.height()) {
    215         this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(),
    216                 rowsDecoded);
    217     }
    218 
    219     return result;
    220 }
    221 
    222 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
    223     return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr);
    224 }
    225 
    226 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo,
    227         const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
    228     // Reset fCurrScanline in case of failure.
    229     fCurrScanline = -1;
    230     // Ensure that valid color ptrs are passed in for kIndex8 color type
    231     if (kIndex_8_SkColorType == dstInfo.colorType()) {
    232         if (nullptr == ctable || nullptr == ctableCount) {
    233             return SkCodec::kInvalidParameters;
    234         }
    235     } else {
    236         if (ctableCount) {
    237             *ctableCount = 0;
    238         }
    239         ctableCount = nullptr;
    240         ctable = nullptr;
    241     }
    242 
    243     if (!this->rewindIfNeeded()) {
    244         return kCouldNotRewind;
    245     }
    246 
    247     // Set options.
    248     Options optsStorage;
    249     if (nullptr == options) {
    250         options = &optsStorage;
    251     } else if (options->fSubset) {
    252         SkIRect size = SkIRect::MakeSize(dstInfo.dimensions());
    253         if (!size.contains(*options->fSubset)) {
    254             return kInvalidInput;
    255         }
    256 
    257         // We only support subsetting in the x-dimension for scanline decoder.
    258         // Subsetting in the y-dimension can be accomplished using skipScanlines().
    259         if (options->fSubset->top() != 0 || options->fSubset->height() != dstInfo.height()) {
    260             return kInvalidInput;
    261         }
    262     }
    263 
    264     // FIXME: Support subsets somehow?
    265     if (!this->dimensionsSupported(dstInfo.dimensions())) {
    266         return kInvalidScale;
    267     }
    268 
    269     const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount);
    270     if (result != SkCodec::kSuccess) {
    271         return result;
    272     }
    273 
    274     fCurrScanline = 0;
    275     fDstInfo = dstInfo;
    276     fOptions = *options;
    277     return kSuccess;
    278 }
    279 
    280 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo) {
    281     return this->startScanlineDecode(dstInfo, nullptr, nullptr, nullptr);
    282 }
    283 
    284 int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
    285     if (fCurrScanline < 0) {
    286         return 0;
    287     }
    288 
    289     SkASSERT(!fDstInfo.isEmpty());
    290     if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
    291         return 0;
    292     }
    293 
    294     const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes);
    295     if (linesDecoded < countLines) {
    296         this->fillIncompleteImage(this->dstInfo(), dst, rowBytes, this->options().fZeroInitialized,
    297                 countLines, linesDecoded);
    298     }
    299     fCurrScanline += countLines;
    300     return linesDecoded;
    301 }
    302 
    303 bool SkCodec::skipScanlines(int countLines) {
    304     if (fCurrScanline < 0) {
    305         return false;
    306     }
    307 
    308     SkASSERT(!fDstInfo.isEmpty());
    309     if (countLines < 0 || fCurrScanline + countLines > fDstInfo.height()) {
    310         // Arguably, we could just skip the scanlines which are remaining,
    311         // and return true. We choose to return false so the client
    312         // can catch their bug.
    313         return false;
    314     }
    315 
    316     bool result = this->onSkipScanlines(countLines);
    317     fCurrScanline += countLines;
    318     return result;
    319 }
    320 
    321 int SkCodec::outputScanline(int inputScanline) const {
    322     SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height());
    323     return this->onOutputScanline(inputScanline);
    324 }
    325 
    326 int SkCodec::onOutputScanline(int inputScanline) const {
    327     switch (this->getScanlineOrder()) {
    328         case kTopDown_SkScanlineOrder:
    329         case kNone_SkScanlineOrder:
    330             return inputScanline;
    331         case kBottomUp_SkScanlineOrder:
    332             return this->getInfo().height() - inputScanline - 1;
    333         default:
    334             // This case indicates an interlaced gif and is implemented by SkGifCodec.
    335             SkASSERT(false);
    336             return 0;
    337     }
    338 }
    339 
    340 static void fill_proc(const SkImageInfo& info, void* dst, size_t rowBytes,
    341         uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit, SkSampler* sampler) {
    342     if (sampler) {
    343         sampler->fill(info, dst, rowBytes, colorOrIndex, zeroInit);
    344     } else {
    345         SkSampler::Fill(info, dst, rowBytes, colorOrIndex, zeroInit);
    346     }
    347 }
    348 
    349 void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
    350         ZeroInitialized zeroInit, int linesRequested, int linesDecoded) {
    351 
    352     void* fillDst;
    353     const uint32_t fillValue = this->getFillValue(info.colorType());
    354     const int linesRemaining = linesRequested - linesDecoded;
    355     SkSampler* sampler = this->getSampler(false);
    356 
    357     int fillWidth = info.width();
    358     if (fOptions.fSubset) {
    359         fillWidth = fOptions.fSubset->width();
    360     }
    361 
    362     switch (this->getScanlineOrder()) {
    363         case kTopDown_SkScanlineOrder:
    364         case kNone_SkScanlineOrder: {
    365             const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining);
    366             fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes);
    367             fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
    368             break;
    369         }
    370         case kBottomUp_SkScanlineOrder: {
    371             fillDst = dst;
    372             const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining);
    373             fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
    374             break;
    375         }
    376         case kOutOfOrder_SkScanlineOrder: {
    377             SkASSERT(1 == linesRequested || this->getInfo().height() == linesRequested);
    378             const SkImageInfo fillInfo = info.makeWH(fillWidth, 1);
    379             for (int srcY = linesDecoded; srcY < linesRequested; srcY++) {
    380                 fillDst = SkTAddOffset<void>(dst, this->outputScanline(srcY) * rowBytes);
    381                 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
    382             }
    383             break;
    384         }
    385     }
    386 }
    387