• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
read_mbf(SkStream * stream,uint64_t * value)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 
read_header(SkStream * stream,SkISize * size)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 
bit_to_pmcolor(U8CPU bit)59 static SkPMColor bit_to_pmcolor(U8CPU bit) { return bit ? WHITE : BLACK; }
60 
bit_to_bit(U8CPU bit)61 static uint8_t bit_to_bit(U8CPU bit) { return bit; }
62 
bit_to_grayscale(U8CPU bit)63 static uint8_t bit_to_grayscale(U8CPU bit) {
64     return bit ? GRAYSCALE_WHITE : GRAYSCALE_BLACK;
65 }
66 
bit_to_rgb565(U8CPU bit)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)>
expand_bits_to_T(uint8_t * dstptr,const uint8_t * src,int bits)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 
SkWbmpCodec(const SkImageInfo & info,SkStream * stream)96 SkWbmpCodec::SkWbmpCodec(const SkImageInfo& info, SkStream* stream)
97     : INHERITED(info, stream) {}
98 
onGetEncodedFormat() const99 SkEncodedFormat SkWbmpCodec::onGetEncodedFormat() const {
100     return kWBMP_SkEncodedFormat;
101 }
102 
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options &,SkPMColor ctable[],int * ctableCount)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 
IsWbmp(SkStream * stream)152 bool SkWbmpCodec::IsWbmp(SkStream* stream) {
153     return read_header(stream, NULL);
154 }
155 
NewFromStream(SkStream * stream)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