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 "SkBmpStandardCodec.h"
      9 #include "SkCodecPriv.h"
     10 #include "SkColorPriv.h"
     11 #include "SkStream.h"
     12 
     13 /*
     14  * Creates an instance of the decoder
     15  * Called only by NewFromStream
     16  */
     17 SkBmpStandardCodec::SkBmpStandardCodec(const SkImageInfo& info, SkStream* stream,
     18                                        uint16_t bitsPerPixel, uint32_t numColors,
     19                                        uint32_t bytesPerColor, uint32_t offset,
     20                                        SkCodec::SkScanlineOrder rowOrder,
     21                                        bool isOpaque, bool inIco)
     22     : INHERITED(info, stream, bitsPerPixel, rowOrder)
     23     , fColorTable(nullptr)
     24     , fNumColors(numColors)
     25     , fBytesPerColor(bytesPerColor)
     26     , fOffset(offset)
     27     , fSwizzler(nullptr)
     28     , fSrcBuffer(new uint8_t [this->srcRowBytes()])
     29     , fIsOpaque(isOpaque)
     30     , fInIco(inIco)
     31     , fAndMaskRowBytes(fInIco ? SkAlign4(compute_row_bytes(this->getInfo().width(), 1)) : 0)
     32 {}
     33 
     34 /*
     35  * Initiates the bitmap decode
     36  */
     37 SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
     38                                         void* dst, size_t dstRowBytes,
     39                                         const Options& opts,
     40                                         SkPMColor* inputColorPtr,
     41                                         int* inputColorCount,
     42                                         int* rowsDecoded) {
     43     if (opts.fSubset) {
     44         // Subsets are not supported.
     45         return kUnimplemented;
     46     }
     47     if (dstInfo.dimensions() != this->getInfo().dimensions()) {
     48         SkCodecPrintf("Error: scaling not supported.\n");
     49         return kInvalidScale;
     50     }
     51     if (!conversion_possible(dstInfo, this->getInfo())) {
     52         SkCodecPrintf("Error: cannot convert input type to output type.\n");
     53         return kInvalidConversion;
     54     }
     55 
     56     Result result = this->prepareToDecode(dstInfo, opts, inputColorPtr, inputColorCount);
     57     if (kSuccess != result) {
     58         return result;
     59     }
     60     int rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts);
     61     if (rows != dstInfo.height()) {
     62         *rowsDecoded = rows;
     63         return kIncompleteInput;
     64     }
     65     return kSuccess;
     66 }
     67 
     68 /*
     69  * Process the color table for the bmp input
     70  */
     71  bool SkBmpStandardCodec::createColorTable(SkAlphaType dstAlphaType, int* numColors) {
     72     // Allocate memory for color table
     73     uint32_t colorBytes = 0;
     74     SkPMColor colorTable[256];
     75     if (this->bitsPerPixel() <= 8) {
     76         // Inform the caller of the number of colors
     77         uint32_t maxColors = 1 << this->bitsPerPixel();
     78         if (nullptr != numColors) {
     79             // We set the number of colors to maxColors in order to ensure
     80             // safe memory accesses.  Otherwise, an invalid pixel could
     81             // access memory outside of our color table array.
     82             *numColors = maxColors;
     83         }
     84         // Don't bother reading more than maxColors.
     85         const uint32_t numColorsToRead =
     86             fNumColors == 0 ? maxColors : SkTMin(fNumColors, maxColors);
     87 
     88         // Read the color table from the stream
     89         colorBytes = numColorsToRead * fBytesPerColor;
     90         SkAutoTDeleteArray<uint8_t> cBuffer(new uint8_t[colorBytes]);
     91         if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
     92             SkCodecPrintf("Error: unable to read color table.\n");
     93             return false;
     94         }
     95 
     96         // Choose the proper packing function
     97         SkPMColor (*packARGB) (uint32_t, uint32_t, uint32_t, uint32_t);
     98         if (fIsOpaque || kUnpremul_SkAlphaType == dstAlphaType) {
     99             packARGB = &SkPackARGB32NoCheck;
    100         } else {
    101             packARGB = &SkPremultiplyARGBInline;
    102         }
    103 
    104         // Fill in the color table
    105         uint32_t i = 0;
    106         for (; i < numColorsToRead; i++) {
    107             uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
    108             uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
    109             uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
    110             uint8_t alpha;
    111             if (fIsOpaque) {
    112                 alpha = 0xFF;
    113             } else {
    114                 alpha = get_byte(cBuffer.get(), i*fBytesPerColor + 3);
    115             }
    116             colorTable[i] = packARGB(alpha, red, green, blue);
    117         }
    118 
    119         // To avoid segmentation faults on bad pixel data, fill the end of the
    120         // color table with black.  This is the same the behavior as the
    121         // chromium decoder.
    122         for (; i < maxColors; i++) {
    123             colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
    124         }
    125 
    126         // Set the color table
    127         fColorTable.reset(new SkColorTable(colorTable, maxColors));
    128     }
    129 
    130     // Bmp-in-Ico files do not use an offset to indicate where the pixel data
    131     // begins.  Pixel data always begins immediately after the color table.
    132     if (!fInIco) {
    133         // Check that we have not read past the pixel array offset
    134         if(fOffset < colorBytes) {
    135             // This may occur on OS 2.1 and other old versions where the color
    136             // table defaults to max size, and the bmp tries to use a smaller
    137             // color table.  This is invalid, and our decision is to indicate
    138             // an error, rather than try to guess the intended size of the
    139             // color table.
    140             SkCodecPrintf("Error: pixel data offset less than color table size.\n");
    141             return false;
    142         }
    143 
    144         // After reading the color table, skip to the start of the pixel array
    145         if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
    146             SkCodecPrintf("Error: unable to skip to image data.\n");
    147             return false;
    148         }
    149     }
    150 
    151     // Return true on success
    152     return true;
    153 }
    154 
    155 void SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) {
    156     // Get swizzler configuration
    157     SkSwizzler::SrcConfig config = SkSwizzler::kUnknown;
    158     switch (this->bitsPerPixel()) {
    159         case 1:
    160             config = SkSwizzler::kIndex1;
    161             break;
    162         case 2:
    163             config = SkSwizzler::kIndex2;
    164             break;
    165         case 4:
    166             config = SkSwizzler::kIndex4;
    167             break;
    168         case 8:
    169             config = SkSwizzler::kIndex;
    170             break;
    171         case 24:
    172             config = SkSwizzler::kBGR;
    173             break;
    174         case 32:
    175             if (fIsOpaque) {
    176                 config = SkSwizzler::kBGRX;
    177             } else {
    178                 config = SkSwizzler::kBGRA;
    179             }
    180             break;
    181         default:
    182             SkASSERT(false);
    183     }
    184 
    185     // Get a pointer to the color table if it exists
    186     const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
    187 
    188     // Create swizzler
    189     fSwizzler.reset(SkSwizzler::CreateSwizzler(config, colorPtr, dstInfo, opts));
    190     SkASSERT(fSwizzler);
    191 }
    192 
    193 SkCodec::Result SkBmpStandardCodec::prepareToDecode(const SkImageInfo& dstInfo,
    194         const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
    195     // Create the color table if necessary and prepare the stream for decode
    196     // Note that if it is non-NULL, inputColorCount will be modified
    197     if (!this->createColorTable(dstInfo.alphaType(), inputColorCount)) {
    198         SkCodecPrintf("Error: could not create color table.\n");
    199         return SkCodec::kInvalidInput;
    200     }
    201 
    202     // Copy the color table to the client if necessary
    203     copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount);
    204 
    205     // Initialize a swizzler
    206     this->initializeSwizzler(dstInfo, options);
    207     return SkCodec::kSuccess;
    208 }
    209 
    210 /*
    211  * Performs the bitmap decoding for standard input format
    212  */
    213 int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
    214         const Options& opts) {
    215     // Iterate over rows of the image
    216     const int height = dstInfo.height();
    217     for (int y = 0; y < height; y++) {
    218         // Read a row of the input
    219         if (this->stream()->read(fSrcBuffer.get(), this->srcRowBytes()) != this->srcRowBytes()) {
    220             SkCodecPrintf("Warning: incomplete input stream.\n");
    221             return y;
    222         }
    223 
    224         // Decode the row in destination format
    225         uint32_t row = this->getDstRow(y, dstInfo.height());
    226 
    227         void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes);
    228         fSwizzler->swizzle(dstRow, fSrcBuffer.get());
    229     }
    230 
    231     if (fInIco) {
    232         const int startScanline = this->currScanline();
    233         if (startScanline < 0) {
    234             // We are not performing a scanline decode.
    235             // Just decode the entire ICO mask and return.
    236             decodeIcoMask(this->stream(), dstInfo, dst, dstRowBytes);
    237             return height;
    238         }
    239 
    240         // In order to perform a scanline ICO decode, we must be able
    241         // to skip ahead in the stream in order to apply the AND mask
    242         // to the requested scanlines.
    243         // We will do this by taking advantage of the fact that
    244         // SkIcoCodec always uses a SkMemoryStream as its underlying
    245         // representation of the stream.
    246         const void* memoryBase = this->stream()->getMemoryBase();
    247         SkASSERT(nullptr != memoryBase);
    248         SkASSERT(this->stream()->hasLength());
    249         SkASSERT(this->stream()->hasPosition());
    250 
    251         const size_t length = this->stream()->getLength();
    252         const size_t currPosition = this->stream()->getPosition();
    253 
    254         // Calculate how many bytes we must skip to reach the AND mask.
    255         const int remainingScanlines = this->getInfo().height() - startScanline - height;
    256         const size_t bytesToSkip = remainingScanlines * this->srcRowBytes() +
    257                 startScanline * fAndMaskRowBytes;
    258         const size_t subStreamStartPosition = currPosition + bytesToSkip;
    259         if (subStreamStartPosition >= length) {
    260             // FIXME: How can we indicate that this decode was actually incomplete?
    261             return height;
    262         }
    263 
    264         // Create a subStream to pass to decodeIcoMask().  It is useful to encapsulate
    265         // the memory base into a stream in order to safely handle incomplete images
    266         // without reading out of bounds memory.
    267         const void* subStreamMemoryBase = SkTAddOffset<const void>(memoryBase,
    268                 subStreamStartPosition);
    269         const size_t subStreamLength = length - subStreamStartPosition;
    270         // This call does not transfer ownership of the subStreamMemoryBase.
    271         SkMemoryStream subStream(subStreamMemoryBase, subStreamLength, false);
    272 
    273         // FIXME: If decodeIcoMask does not succeed, is there a way that we can
    274         //        indicate the decode was incomplete?
    275         decodeIcoMask(&subStream, dstInfo, dst, dstRowBytes);
    276     }
    277 
    278     return height;
    279 }
    280 
    281 void SkBmpStandardCodec::decodeIcoMask(SkStream* stream, const SkImageInfo& dstInfo,
    282         void* dst, size_t dstRowBytes) {
    283     // BMP in ICO have transparency, so this cannot be 565, and this mask
    284     // prevents us from using kIndex8. The below code depends on the output
    285     // being an SkPMColor.
    286     SkASSERT(dstInfo.colorType() == kN32_SkColorType);
    287 
    288     // If we are sampling, make sure that we only mask the sampled pixels.
    289     // We do not need to worry about sampling in the y-dimension because that
    290     // should be handled by SkSampledCodec.
    291     const int sampleX = fSwizzler->sampleX();
    292     const int sampledWidth = get_scaled_dimension(this->getInfo().width(), sampleX);
    293     const int srcStartX = get_start_coord(sampleX);
    294 
    295 
    296     SkPMColor* dstPtr = (SkPMColor*) dst;
    297     for (int y = 0; y < dstInfo.height(); y++) {
    298         // The srcBuffer will at least be large enough
    299         if (stream->read(fSrcBuffer.get(), fAndMaskRowBytes) != fAndMaskRowBytes) {
    300             SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n");
    301             return;
    302         }
    303 
    304         int row = this->getDstRow(y, dstInfo.height());
    305 
    306         SkPMColor* dstRow =
    307                 SkTAddOffset<SkPMColor>(dstPtr, row * dstRowBytes);
    308 
    309         int srcX = srcStartX;
    310         for (int dstX = 0; dstX < sampledWidth; dstX++) {
    311             int quotient;
    312             int modulus;
    313             SkTDivMod(srcX, 8, &quotient, &modulus);
    314             uint32_t shift = 7 - modulus;
    315             uint32_t alphaBit = (fSrcBuffer.get()[quotient] >> shift) & 0x1;
    316             dstRow[dstX] &= alphaBit - 1;
    317             srcX += sampleX;
    318         }
    319     }
    320 }
    321 
    322 uint32_t SkBmpStandardCodec::onGetFillValue(SkColorType colorType) const {
    323     const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
    324     if (colorPtr) {
    325         return get_color_table_fill_value(colorType, colorPtr, 0);
    326     }
    327     return INHERITED::onGetFillValue(colorType);
    328 }
    329