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 "SkCodecPriv.h"
10 #include "SkColorPriv.h"
11 #include "SkColorTable.h"
12 #include "SkData.h"
13 #include "SkStream.h"
14 #include "SkWbmpCodec.h"
15 
16 // Each bit represents a pixel, so width is actually a number of bits.
17 // A row will always be stored in bytes, so we round width up to the
18 // nearest multiple of 8 to get the number of bits actually in the row.
19 // We then divide by 8 to convert to bytes.
get_src_row_bytes(int width)20 static inline size_t get_src_row_bytes(int width) {
21     return SkAlign8(width) >> 3;
22 }
23 
setup_color_table(SkColorType colorType,SkPMColor * colorPtr,int * colorCount)24 static inline void setup_color_table(SkColorType colorType,
25         SkPMColor* colorPtr, int* colorCount) {
26     if (kIndex_8_SkColorType == colorType) {
27         colorPtr[0] = SK_ColorBLACK;
28         colorPtr[1] = SK_ColorWHITE;
29         *colorCount = 2;
30     }
31 }
32 
valid_color_type(const SkImageInfo & dstInfo)33 static inline bool valid_color_type(const SkImageInfo& dstInfo) {
34     switch (dstInfo.colorType()) {
35         case kRGBA_8888_SkColorType:
36         case kBGRA_8888_SkColorType:
37         case kIndex_8_SkColorType:
38         case kGray_8_SkColorType:
39         case kRGB_565_SkColorType:
40             return true;
41         case kRGBA_F16_SkColorType:
42             return dstInfo.colorSpace() && dstInfo.colorSpace()->gammaIsLinear();
43         default:
44             return false;
45     }
46 }
47 
read_byte(SkStream * stream,uint8_t * data)48 static bool read_byte(SkStream* stream, uint8_t* data)
49 {
50     return stream->read(data, 1) == 1;
51 }
52 
53 // http://en.wikipedia.org/wiki/Variable-length_quantity
read_mbf(SkStream * stream,uint64_t * value)54 static bool read_mbf(SkStream* stream, uint64_t* value) {
55     uint64_t n = 0;
56     uint8_t data;
57     const uint64_t kLimit = 0xFE00000000000000;
58     SkASSERT(kLimit == ~((~static_cast<uint64_t>(0)) >> 7));
59     do {
60         if (n & kLimit) { // Will overflow on shift by 7.
61             return false;
62         }
63         if (stream->read(&data, 1) != 1) {
64             return false;
65         }
66         n = (n << 7) | (data & 0x7F);
67     } while (data & 0x80);
68     *value = n;
69     return true;
70 }
71 
read_header(SkStream * stream,SkISize * size)72 static bool read_header(SkStream* stream, SkISize* size) {
73     {
74         uint8_t data;
75         if (!read_byte(stream, &data) || data != 0) { // unknown type
76             return false;
77         }
78         if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header
79             return false;
80         }
81     }
82 
83     uint64_t width, height;
84     if (!read_mbf(stream, &width) || width > 0xFFFF || !width) {
85         return false;
86     }
87     if (!read_mbf(stream, &height) || height > 0xFFFF || !height) {
88         return false;
89     }
90     if (size) {
91         *size = SkISize::Make(SkToS32(width), SkToS32(height));
92     }
93     return true;
94 }
95 
onRewind()96 bool SkWbmpCodec::onRewind() {
97     return read_header(this->stream(), nullptr);
98 }
99 
initializeSwizzler(const SkImageInfo & info,const SkPMColor * ctable,const Options & opts)100 SkSwizzler* SkWbmpCodec::initializeSwizzler(const SkImageInfo& info, const SkPMColor* ctable,
101         const Options& opts) {
102     return SkSwizzler::CreateSwizzler(this->getEncodedInfo(), ctable, info, opts);
103 }
104 
readRow(uint8_t * row)105 bool SkWbmpCodec::readRow(uint8_t* row) {
106     return this->stream()->read(row, fSrcRowBytes) == fSrcRowBytes;
107 }
108 
SkWbmpCodec(int width,int height,const SkEncodedInfo & info,SkStream * stream)109 SkWbmpCodec::SkWbmpCodec(int width, int height, const SkEncodedInfo& info, SkStream* stream)
110     : INHERITED(width, height, info, stream, SkColorSpace::MakeSRGB())
111     , fSrcRowBytes(get_src_row_bytes(this->getInfo().width()))
112     , fSwizzler(nullptr)
113     , fColorTable(nullptr)
114 {}
115 
onGetEncodedFormat() const116 SkEncodedImageFormat SkWbmpCodec::onGetEncodedFormat() const {
117     return SkEncodedImageFormat::kWBMP;
118 }
119 
onGetPixels(const SkImageInfo & info,void * dst,size_t rowBytes,const Options & options,SkPMColor ctable[],int * ctableCount,int * rowsDecoded)120 SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
121                                          void* dst,
122                                          size_t rowBytes,
123                                          const Options& options,
124                                          SkPMColor ctable[],
125                                          int* ctableCount,
126                                          int* rowsDecoded) {
127     if (options.fSubset) {
128         // Subsets are not supported.
129         return kUnimplemented;
130     }
131 
132     if (!valid_color_type(info) || !valid_alpha(info.alphaType(), this->getInfo().alphaType())) {
133         return kInvalidConversion;
134     }
135 
136     // Prepare a color table if necessary
137     setup_color_table(info.colorType(), ctable, ctableCount);
138 
139     // Initialize the swizzler
140     std::unique_ptr<SkSwizzler> swizzler(this->initializeSwizzler(info, ctable, options));
141     SkASSERT(swizzler);
142 
143     // Perform the decode
144     SkISize size = info.dimensions();
145     SkAutoTMalloc<uint8_t> src(fSrcRowBytes);
146     void* dstRow = dst;
147     for (int y = 0; y < size.height(); ++y) {
148         if (!this->readRow(src.get())) {
149             *rowsDecoded = y;
150             return kIncompleteInput;
151         }
152         swizzler->swizzle(dstRow, src.get());
153         dstRow = SkTAddOffset<void>(dstRow, rowBytes);
154     }
155     return kSuccess;
156 }
157 
IsWbmp(const void * buffer,size_t bytesRead)158 bool SkWbmpCodec::IsWbmp(const void* buffer, size_t bytesRead) {
159     SkMemoryStream stream(buffer, bytesRead, false);
160     return read_header(&stream, nullptr);
161 }
162 
NewFromStream(SkStream * stream)163 SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) {
164     std::unique_ptr<SkStream> streamDeleter(stream);
165     SkISize size;
166     if (!read_header(stream, &size)) {
167         return nullptr;
168     }
169     SkEncodedInfo info = SkEncodedInfo::Make(SkEncodedInfo::kGray_Color,
170             SkEncodedInfo::kOpaque_Alpha, 1);
171     return new SkWbmpCodec(size.width(), size.height(), info, streamDeleter.release());
172 }
173 
onGetScanlines(void * dst,int count,size_t dstRowBytes)174 int SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
175     void* dstRow = dst;
176     for (int y = 0; y < count; ++y) {
177         if (!this->readRow(fSrcBuffer.get())) {
178             return y;
179         }
180         fSwizzler->swizzle(dstRow, fSrcBuffer.get());
181         dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
182     }
183     return count;
184 }
185 
onSkipScanlines(int count)186 bool SkWbmpCodec::onSkipScanlines(int count) {
187     const size_t bytesToSkip = count * fSrcRowBytes;
188     return this->stream()->skip(bytesToSkip) == bytesToSkip;
189 }
190 
onStartScanlineDecode(const SkImageInfo & dstInfo,const Options & options,SkPMColor inputColorTable[],int * inputColorCount)191 SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
192         const Options& options, SkPMColor inputColorTable[], int* inputColorCount) {
193     if (options.fSubset) {
194         // Subsets are not supported.
195         return kUnimplemented;
196     }
197 
198     if (!valid_color_type(dstInfo) ||
199         !valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType()))
200     {
201         return kInvalidConversion;
202     }
203 
204     // Fill in the color table
205     setup_color_table(dstInfo.colorType(), inputColorTable, inputColorCount);
206 
207     // Copy the color table to a pointer that can be owned by the scanline decoder
208     if (kIndex_8_SkColorType == dstInfo.colorType()) {
209         fColorTable.reset(new SkColorTable(inputColorTable, 2));
210     }
211 
212     // Initialize the swizzler
213     fSwizzler.reset(this->initializeSwizzler(dstInfo, get_color_ptr(fColorTable.get()), options));
214     SkASSERT(fSwizzler);
215 
216     fSrcBuffer.reset(fSrcRowBytes);
217 
218     return kSuccess;
219 }
220