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 "SkCodecPriv.h"
     10 #include "SkColorData.h"
     11 #include "SkData.h"
     12 #include "SkIcoCodec.h"
     13 #include "SkPngCodec.h"
     14 #include "SkStream.h"
     15 #include "SkTDArray.h"
     16 #include "SkTSort.h"
     17 
     18 /*
     19  * Checks the start of the stream to see if the image is an Ico or Cur
     20  */
     21 bool SkIcoCodec::IsIco(const void* buffer, size_t bytesRead) {
     22     const char icoSig[] = { '\x00', '\x00', '\x01', '\x00' };
     23     const char curSig[] = { '\x00', '\x00', '\x02', '\x00' };
     24     return bytesRead >= sizeof(icoSig) &&
     25             (!memcmp(buffer, icoSig, sizeof(icoSig)) ||
     26             !memcmp(buffer, curSig, sizeof(curSig)));
     27 }
     28 
     29 std::unique_ptr<SkCodec> SkIcoCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
     30                                                     Result* result) {
     31     // Header size constants
     32     constexpr uint32_t kIcoDirectoryBytes = 6;
     33     constexpr uint32_t kIcoDirEntryBytes = 16;
     34 
     35     // Read the directory header
     36     std::unique_ptr<uint8_t[]> dirBuffer(new uint8_t[kIcoDirectoryBytes]);
     37     if (stream->read(dirBuffer.get(), kIcoDirectoryBytes) != kIcoDirectoryBytes) {
     38         SkCodecPrintf("Error: unable to read ico directory header.\n");
     39         *result = kIncompleteInput;
     40         return nullptr;
     41     }
     42 
     43     // Process the directory header
     44     const uint16_t numImages = get_short(dirBuffer.get(), 4);
     45     if (0 == numImages) {
     46         SkCodecPrintf("Error: No images embedded in ico.\n");
     47         *result = kInvalidInput;
     48         return nullptr;
     49     }
     50 
     51     // This structure is used to represent the vital information about entries
     52     // in the directory header.  We will obtain this information for each
     53     // directory entry.
     54     struct Entry {
     55         uint32_t offset;
     56         uint32_t size;
     57     };
     58     SkAutoFree dirEntryBuffer(sk_malloc_canfail(sizeof(Entry) * numImages));
     59     if (!dirEntryBuffer) {
     60         SkCodecPrintf("Error: OOM allocating ICO directory for %i images.\n",
     61                       numImages);
     62         *result = kInternalError;
     63         return nullptr;
     64     }
     65     auto* directoryEntries = reinterpret_cast<Entry*>(dirEntryBuffer.get());
     66 
     67     // Iterate over directory entries
     68     for (uint32_t i = 0; i < numImages; i++) {
     69         uint8_t entryBuffer[kIcoDirEntryBytes];
     70         if (stream->read(entryBuffer, kIcoDirEntryBytes) != kIcoDirEntryBytes) {
     71             SkCodecPrintf("Error: Dir entries truncated in ico.\n");
     72             *result = kIncompleteInput;
     73             return nullptr;
     74         }
     75 
     76         // The directory entry contains information such as width, height,
     77         // bits per pixel, and number of colors in the color palette.  We will
     78         // ignore these fields since they are repeated in the header of the
     79         // embedded image.  In the event of an inconsistency, we would always
     80         // defer to the value in the embedded header anyway.
     81 
     82         // Specifies the size of the embedded image, including the header
     83         uint32_t size = get_int(entryBuffer, 8);
     84 
     85         // Specifies the offset of the embedded image from the start of file.
     86         // It does not indicate the start of the pixel data, but rather the
     87         // start of the embedded image header.
     88         uint32_t offset = get_int(entryBuffer, 12);
     89 
     90         // Save the vital fields
     91         directoryEntries[i].offset = offset;
     92         directoryEntries[i].size = size;
     93     }
     94 
     95     // Default Result, if no valid embedded codecs are found.
     96     *result = kInvalidInput;
     97 
     98     // It is "customary" that the embedded images will be stored in order of
     99     // increasing offset.  However, the specification does not indicate that
    100     // they must be stored in this order, so we will not trust that this is the
    101     // case.  Here we sort the embedded images by increasing offset.
    102     struct EntryLessThan {
    103         bool operator() (Entry a, Entry b) const {
    104             return a.offset < b.offset;
    105         }
    106     };
    107     EntryLessThan lessThan;
    108     SkTQSort(directoryEntries, &directoryEntries[numImages - 1], lessThan);
    109 
    110     // Now will construct a candidate codec for each of the embedded images
    111     uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes;
    112     std::unique_ptr<SkTArray<std::unique_ptr<SkCodec>, true>> codecs(
    113             new (SkTArray<std::unique_ptr<SkCodec>, true>)(numImages));
    114     for (uint32_t i = 0; i < numImages; i++) {
    115         uint32_t offset = directoryEntries[i].offset;
    116         uint32_t size = directoryEntries[i].size;
    117 
    118         // Ensure that the offset is valid
    119         if (offset < bytesRead) {
    120             SkCodecPrintf("Warning: invalid ico offset.\n");
    121             continue;
    122         }
    123 
    124         // If we cannot skip, assume we have reached the end of the stream and
    125         // stop trying to make codecs
    126         if (stream->skip(offset - bytesRead) != offset - bytesRead) {
    127             SkCodecPrintf("Warning: could not skip to ico offset.\n");
    128             break;
    129         }
    130         bytesRead = offset;
    131 
    132         // Create a new stream for the embedded codec
    133         SkAutoFree buffer(sk_malloc_canfail(size));
    134         if (!buffer) {
    135             SkCodecPrintf("Warning: OOM trying to create embedded stream.\n");
    136             break;
    137         }
    138 
    139         if (stream->read(buffer.get(), size) != size) {
    140             SkCodecPrintf("Warning: could not create embedded stream.\n");
    141             *result = kIncompleteInput;
    142             break;
    143         }
    144 
    145         sk_sp<SkData> data(SkData::MakeFromMalloc(buffer.release(), size));
    146         auto embeddedStream = SkMemoryStream::Make(data);
    147         bytesRead += size;
    148 
    149         // Check if the embedded codec is bmp or png and create the codec
    150         std::unique_ptr<SkCodec> codec;
    151         Result dummyResult;
    152         if (SkPngCodec::IsPng((const char*) data->bytes(), data->size())) {
    153             codec = SkPngCodec::MakeFromStream(std::move(embeddedStream), &dummyResult);
    154         } else {
    155             codec = SkBmpCodec::MakeFromIco(std::move(embeddedStream), &dummyResult);
    156         }
    157 
    158         // Save a valid codec
    159         if (nullptr != codec) {
    160             codecs->push_back().reset(codec.release());
    161         }
    162     }
    163 
    164     // Recognize if there are no valid codecs
    165     if (0 == codecs->count()) {
    166         SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n");
    167         return nullptr;
    168     }
    169 
    170     // Use the largest codec as a "suggestion" for image info
    171     size_t maxSize = 0;
    172     int maxIndex = 0;
    173     for (int i = 0; i < codecs->count(); i++) {
    174         SkImageInfo info = codecs->operator[](i)->getInfo();
    175         size_t size = info.computeMinByteSize();
    176 
    177         if (size > maxSize) {
    178             maxSize = size;
    179             maxIndex = i;
    180         }
    181     }
    182     int width = codecs->operator[](maxIndex)->getInfo().width();
    183     int height = codecs->operator[](maxIndex)->getInfo().height();
    184     SkEncodedInfo info = codecs->operator[](maxIndex)->getEncodedInfo();
    185     SkColorSpace* colorSpace = codecs->operator[](maxIndex)->getInfo().colorSpace();
    186 
    187     *result = kSuccess;
    188     // The original stream is no longer needed, because the embedded codecs own their
    189     // own streams.
    190     return std::unique_ptr<SkCodec>(new SkIcoCodec(width, height, info, codecs.release(),
    191                                                    sk_ref_sp(colorSpace)));
    192 }
    193 
    194 /*
    195  * Creates an instance of the decoder
    196  * Called only by NewFromStream
    197  */
    198 SkIcoCodec::SkIcoCodec(int width, int height, const SkEncodedInfo& info,
    199                        SkTArray<std::unique_ptr<SkCodec>, true>* codecs,
    200                        sk_sp<SkColorSpace> colorSpace)
    201     // The source SkColorSpaceXform::ColorFormat will not be used. The embedded
    202     // codec's will be used instead.
    203     : INHERITED(width, height, info, SkColorSpaceXform::ColorFormat(), nullptr,
    204                 std::move(colorSpace))
    205     , fEmbeddedCodecs(codecs)
    206     , fCurrCodec(nullptr)
    207 {}
    208 
    209 /*
    210  * Chooses the best dimensions given the desired scale
    211  */
    212 SkISize SkIcoCodec::onGetScaledDimensions(float desiredScale) const {
    213     // We set the dimensions to the largest candidate image by default.
    214     // Regardless of the scale request, this is the largest image that we
    215     // will decode.
    216     int origWidth = this->getInfo().width();
    217     int origHeight = this->getInfo().height();
    218     float desiredSize = desiredScale * origWidth * origHeight;
    219     // At least one image will have smaller error than this initial value
    220     float minError = ((float) (origWidth * origHeight)) - desiredSize + 1.0f;
    221     int32_t minIndex = -1;
    222     for (int32_t i = 0; i < fEmbeddedCodecs->count(); i++) {
    223         int width = fEmbeddedCodecs->operator[](i)->getInfo().width();
    224         int height = fEmbeddedCodecs->operator[](i)->getInfo().height();
    225         float error = SkTAbs(((float) (width * height)) - desiredSize);
    226         if (error < minError) {
    227             minError = error;
    228             minIndex = i;
    229         }
    230     }
    231     SkASSERT(minIndex >= 0);
    232 
    233     return fEmbeddedCodecs->operator[](minIndex)->getInfo().dimensions();
    234 }
    235 
    236 int SkIcoCodec::chooseCodec(const SkISize& requestedSize, int startIndex) {
    237     SkASSERT(startIndex >= 0);
    238 
    239     // FIXME: Cache the index from onGetScaledDimensions?
    240     for (int i = startIndex; i < fEmbeddedCodecs->count(); i++) {
    241         if (fEmbeddedCodecs->operator[](i)->getInfo().dimensions() == requestedSize) {
    242             return i;
    243         }
    244     }
    245 
    246     return -1;
    247 }
    248 
    249 bool SkIcoCodec::onDimensionsSupported(const SkISize& dim) {
    250     return this->chooseCodec(dim, 0) >= 0;
    251 }
    252 
    253 /*
    254  * Initiates the Ico decode
    255  */
    256 SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo,
    257                                         void* dst, size_t dstRowBytes,
    258                                         const Options& opts,
    259                                         int* rowsDecoded) {
    260     if (opts.fSubset) {
    261         // Subsets are not supported.
    262         return kUnimplemented;
    263     }
    264 
    265     int index = 0;
    266     SkCodec::Result result = kInvalidScale;
    267     while (true) {
    268         index = this->chooseCodec(dstInfo.dimensions(), index);
    269         if (index < 0) {
    270             break;
    271         }
    272 
    273         SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index).get();
    274         result = embeddedCodec->getPixels(dstInfo, dst, dstRowBytes, &opts);
    275         switch (result) {
    276             case kSuccess:
    277             case kIncompleteInput:
    278                 // The embedded codec will handle filling incomplete images, so we will indicate
    279                 // that all of the rows are initialized.
    280                 *rowsDecoded = dstInfo.height();
    281                 return result;
    282             default:
    283                 // Continue trying to find a valid embedded codec on a failed decode.
    284                 break;
    285         }
    286 
    287         index++;
    288     }
    289 
    290     SkCodecPrintf("Error: No matching candidate image in ico.\n");
    291     return result;
    292 }
    293 
    294 SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
    295         const SkCodec::Options& options) {
    296     int index = 0;
    297     SkCodec::Result result = kInvalidScale;
    298     while (true) {
    299         index = this->chooseCodec(dstInfo.dimensions(), index);
    300         if (index < 0) {
    301             break;
    302         }
    303 
    304         SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index).get();
    305         result = embeddedCodec->startScanlineDecode(dstInfo, &options);
    306         if (kSuccess == result) {
    307             fCurrCodec = embeddedCodec;
    308             return result;
    309         }
    310 
    311         index++;
    312     }
    313 
    314     SkCodecPrintf("Error: No matching candidate image in ico.\n");
    315     return result;
    316 }
    317 
    318 int SkIcoCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
    319     SkASSERT(fCurrCodec);
    320     return fCurrCodec->getScanlines(dst, count, rowBytes);
    321 }
    322 
    323 bool SkIcoCodec::onSkipScanlines(int count) {
    324     SkASSERT(fCurrCodec);
    325     return fCurrCodec->skipScanlines(count);
    326 }
    327 
    328 SkCodec::Result SkIcoCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
    329         void* pixels, size_t rowBytes, const SkCodec::Options& options) {
    330     int index = 0;
    331     while (true) {
    332         index = this->chooseCodec(dstInfo.dimensions(), index);
    333         if (index < 0) {
    334             break;
    335         }
    336 
    337         SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index).get();
    338         switch (embeddedCodec->startIncrementalDecode(dstInfo,
    339                 pixels, rowBytes, &options)) {
    340             case kSuccess:
    341                 fCurrCodec = embeddedCodec;
    342                 return kSuccess;
    343             case kUnimplemented:
    344                 // FIXME: embeddedCodec is a BMP. If scanline decoding would work,
    345                 // return kUnimplemented so that SkSampledCodec will fall through
    346                 // to use the scanline decoder.
    347                 // Note that calling startScanlineDecode will require an extra
    348                 // rewind. The embedded codec has an SkMemoryStream, which is
    349                 // cheap to rewind, though it will do extra work re-reading the
    350                 // header.
    351                 // Also note that we pass nullptr for Options. This is because
    352                 // Options that are valid for incremental decoding may not be
    353                 // valid for scanline decoding.
    354                 // Once BMP supports incremental decoding this workaround can go
    355                 // away.
    356                 if (embeddedCodec->startScanlineDecode(dstInfo) == kSuccess) {
    357                     return kUnimplemented;
    358                 }
    359                 // Move on to the next embedded codec.
    360                 break;
    361             default:
    362                 break;
    363         }
    364 
    365         index++;
    366     }
    367 
    368     SkCodecPrintf("Error: No matching candidate image in ico.\n");
    369     return kInvalidScale;
    370 }
    371 
    372 SkCodec::Result SkIcoCodec::onIncrementalDecode(int* rowsDecoded) {
    373     SkASSERT(fCurrCodec);
    374     return fCurrCodec->incrementalDecode(rowsDecoded);
    375 }
    376 
    377 SkCodec::SkScanlineOrder SkIcoCodec::onGetScanlineOrder() const {
    378     // FIXME: This function will possibly return the wrong value if it is called
    379     //        before startScanlineDecode()/startIncrementalDecode().
    380     if (fCurrCodec) {
    381         return fCurrCodec->getScanlineOrder();
    382     }
    383 
    384     return INHERITED::onGetScanlineOrder();
    385 }
    386 
    387 SkSampler* SkIcoCodec::getSampler(bool createIfNecessary) {
    388     if (fCurrCodec) {
    389         return fCurrCodec->getSampler(createIfNecessary);
    390     }
    391 
    392     return nullptr;
    393 }
    394