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 "SkColorPriv.h"
     10 #include "SkStream.h"
     11 #include "SkCodec_wbmp.h"
     12 
     13 // http://en.wikipedia.org/wiki/Variable-length_quantity
     14 static bool read_mbf(SkStream* stream, uint64_t* value) {
     15     uint64_t n = 0;
     16     uint8_t data;
     17     const uint64_t kLimit = 0xFE00000000000000;
     18     SkASSERT(kLimit == ~((~static_cast<uint64_t>(0)) >> 7));
     19     do {
     20         if (n & kLimit) { // Will overflow on shift by 7.
     21             return false;
     22         }
     23         if (stream->read(&data, 1) != 1) {
     24             return false;
     25         }
     26         n = (n << 7) | (data & 0x7F);
     27     } while (data & 0x80);
     28     *value = n;
     29     return true;
     30 }
     31 
     32 static bool read_header(SkStream* stream, SkISize* size) {
     33     uint64_t width, height;
     34     uint16_t data;
     35     if (stream->read(&data, 2) != 2 || data != 0) {
     36         return false;
     37     }
     38     if (!read_mbf(stream, &width) || width > 0xFFFF || !width) {
     39         return false;
     40     }
     41     if (!read_mbf(stream, &height) || width > 0xFFFF || !height) {
     42         return false;
     43     }
     44     if (size) {
     45         *size = SkISize::Make(SkToS32(width), SkToS32(height));
     46     }
     47     return true;
     48 }
     49 
     50 #define BLACK SkPackARGB32NoCheck(0xFF, 0, 0, 0)
     51 #define WHITE SkPackARGB32NoCheck(0xFF, 0xFF, 0xFF, 0xFF)
     52 
     53 #define GRAYSCALE_BLACK 0
     54 #define GRAYSCALE_WHITE 0xFF
     55 
     56 #define RGB565_BLACK 0
     57 #define RGB565_WHITE 0xFFFF
     58 
     59 static SkPMColor bit_to_pmcolor(U8CPU bit) { return bit ? WHITE : BLACK; }
     60 
     61 static uint8_t bit_to_bit(U8CPU bit) { return bit; }
     62 
     63 static uint8_t bit_to_grayscale(U8CPU bit) {
     64     return bit ? GRAYSCALE_WHITE : GRAYSCALE_BLACK;
     65 }
     66 
     67 static uint16_t bit_to_rgb565(U8CPU bit) {
     68     return bit ? RGB565_WHITE : RGB565_BLACK;
     69 }
     70 
     71 typedef void (*ExpandProc)(uint8_t*, const uint8_t*, int);
     72 
     73 // TODO(halcanary): Add this functionality (grayscale and indexed output) to
     74 //                  SkSwizzler and use it here.
     75 template <typename T, T (*TRANSFORM)(U8CPU)>
     76 static void expand_bits_to_T(uint8_t* dstptr, const uint8_t* src, int bits) {
     77     T* dst = reinterpret_cast<T*>(dstptr);
     78     int bytes = bits >> 3;
     79     for (int i = 0; i < bytes; i++) {
     80         U8CPU mask = *src++;
     81         for (int j = 0; j < 8; j++) {
     82             dst[j] = TRANSFORM((mask >> (7 - j)) & 1);
     83         }
     84         dst += 8;
     85     }
     86     bits &= 7;
     87     if (bits > 0) {
     88         U8CPU mask = *src;
     89         do {
     90             *dst++ = TRANSFORM((mask >> 7) & 1);
     91             mask <<= 1;
     92         } while (--bits != 0);
     93     }
     94 }
     95 
     96 SkWbmpCodec::SkWbmpCodec(const SkImageInfo& info, SkStream* stream)
     97     : INHERITED(info, stream) {}
     98 
     99 SkEncodedFormat SkWbmpCodec::onGetEncodedFormat() const {
    100     return kWBMP_SkEncodedFormat;
    101 }
    102 
    103 SkImageGenerator::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
    104                                                   void* pixels,
    105                                                   size_t rowBytes,
    106                                                   const Options&,
    107                                                   SkPMColor ctable[],
    108                                                   int* ctableCount) {
    109     SkCodec::RewindState rewindState = this->rewindIfNeeded();
    110     if (rewindState == kCouldNotRewind_RewindState) {
    111         return SkImageGenerator::kCouldNotRewind;
    112     } else if (rewindState == kRewound_RewindState) {
    113         (void)read_header(this->stream(), NULL);
    114     }
    115     if (info.dimensions() != this->getInfo().dimensions()) {
    116         return SkImageGenerator::kInvalidScale;
    117     }
    118     ExpandProc proc = NULL;
    119     switch (info.colorType()) {
    120         case kGray_8_SkColorType:
    121             proc = expand_bits_to_T<uint8_t, bit_to_grayscale>;
    122             break;
    123         case kN32_SkColorType:
    124             proc = expand_bits_to_T<SkPMColor, bit_to_pmcolor>;
    125             break;
    126         case kIndex_8_SkColorType:
    127             ctable[0] = BLACK;
    128             ctable[1] = WHITE;
    129             *ctableCount = 2;
    130             proc = expand_bits_to_T<uint8_t, bit_to_bit>;
    131             break;
    132         case kRGB_565_SkColorType:
    133             proc = expand_bits_to_T<uint16_t, bit_to_rgb565>;
    134             break;
    135         default:
    136             return SkImageGenerator::kInvalidConversion;
    137     }
    138     SkISize size = info.dimensions();
    139     uint8_t* dst = static_cast<uint8_t*>(pixels);
    140     size_t srcRowBytes = SkAlign8(size.width()) >> 3;
    141     SkAutoTMalloc<uint8_t> src(srcRowBytes);
    142     for (int y = 0; y < size.height(); ++y) {
    143         if (this->stream()->read(src.get(), srcRowBytes) != srcRowBytes) {
    144             return SkImageGenerator::kIncompleteInput;
    145         }
    146         proc(dst, src.get(), size.width());
    147         dst += rowBytes;
    148     }
    149     return SkImageGenerator::kSuccess;
    150 }
    151 
    152 bool SkWbmpCodec::IsWbmp(SkStream* stream) {
    153     return read_header(stream, NULL);
    154 }
    155 
    156 SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) {
    157     SkAutoTDelete<SkStream> streamDeleter(stream);
    158     SkISize size;
    159     if (!read_header(stream, &size)) {
    160         return NULL;
    161     }
    162     SkImageInfo info =
    163             SkImageInfo::Make(size.width(), size.height(), kGray_8_SkColorType,
    164                               kOpaque_SkAlphaType);
    165     return SkNEW_ARGS(SkWbmpCodec, (info, streamDeleter.detach()));
    166 }
    167