1
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkImageDecoder.h"
11#include "SkColor.h"
12#include "SkColorPriv.h"
13#include "SkMath.h"
14#include "SkStream.h"
15#include "SkTemplates.h"
16#include "SkUtils.h"
17
18class SkWBMPImageDecoder : public SkImageDecoder {
19public:
20    virtual Format getFormat() const SK_OVERRIDE {
21        return kWBMP_Format;
22    }
23
24protected:
25    virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
26
27private:
28    typedef SkImageDecoder INHERITED;
29};
30
31static bool read_byte(SkStream* stream, uint8_t* data)
32{
33    return stream->read(data, 1) == 1;
34}
35
36static bool read_mbf(SkStream* stream, int* value)
37{
38    int n = 0;
39    uint8_t data;
40    do {
41        if (!read_byte(stream, &data)) {
42            return false;
43        }
44        n = (n << 7) | (data & 0x7F);
45    } while (data & 0x80);
46
47    *value = n;
48    return true;
49}
50
51struct wbmp_head {
52    int fWidth;
53    int fHeight;
54
55    bool init(SkStream* stream)
56    {
57        uint8_t data;
58
59        if (!read_byte(stream, &data) || data != 0) { // unknown type
60            return false;
61        }
62        if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header
63            return false;
64        }
65        if (!read_mbf(stream, &fWidth) || (unsigned)fWidth > 0xFFFF) {
66            return false;
67        }
68        if (!read_mbf(stream, &fHeight) || (unsigned)fHeight > 0xFFFF) {
69            return false;
70        }
71        return fWidth != 0 && fHeight != 0;
72    }
73};
74
75static void expand_bits_to_bytes(uint8_t dst[], const uint8_t src[], int bits)
76{
77    int bytes = bits >> 3;
78
79    for (int i = 0; i < bytes; i++) {
80        unsigned mask = *src++;
81        dst[0] = (mask >> 7) & 1;
82        dst[1] = (mask >> 6) & 1;
83        dst[2] = (mask >> 5) & 1;
84        dst[3] = (mask >> 4) & 1;
85        dst[4] = (mask >> 3) & 1;
86        dst[5] = (mask >> 2) & 1;
87        dst[6] = (mask >> 1) & 1;
88        dst[7] = (mask >> 0) & 1;
89        dst += 8;
90    }
91
92    bits &= 7;
93    if (bits > 0) {
94        unsigned mask = *src;
95        do {
96            *dst++ = (mask >> 7) & 1;;
97            mask <<= 1;
98        } while (--bits != 0);
99    }
100}
101
102SkImageDecoder::Result SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
103                                                    Mode mode)
104{
105    wbmp_head   head;
106
107    if (!head.init(stream)) {
108        return kFailure;
109    }
110
111    int width = head.fWidth;
112    int height = head.fHeight;
113
114    decodedBitmap->setInfo(SkImageInfo::Make(width, height,
115                                             kIndex_8_SkColorType, kOpaque_SkAlphaType));
116
117    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
118        return kSuccess;
119    }
120
121    const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
122    SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2, kOpaque_SkAlphaType));
123    SkAutoUnref   aur(ct);
124
125    if (!this->allocPixelRef(decodedBitmap, ct)) {
126        return kFailure;
127    }
128
129    SkAutoLockPixels alp(*decodedBitmap);
130
131    uint8_t* dst = decodedBitmap->getAddr8(0, 0);
132    // store the 1-bit valuess at the end of our pixels, so we won't stomp
133    // on them before we're read them. Just trying to avoid a temp allocation
134    size_t srcRB = SkAlign8(width) >> 3;
135    size_t srcSize = height * srcRB;
136    uint8_t* src = dst + decodedBitmap->getSize() - srcSize;
137    if (stream->read(src, srcSize) != srcSize) {
138        return kFailure;
139    }
140
141    for (int y = 0; y < height; y++)
142    {
143        expand_bits_to_bytes(dst, src, width);
144        dst += decodedBitmap->rowBytes();
145        src += srcRB;
146    }
147
148    return kSuccess;
149}
150
151///////////////////////////////////////////////////////////////////////////////
152DEFINE_DECODER_CREATOR(WBMPImageDecoder);
153///////////////////////////////////////////////////////////////////////////////
154
155static SkImageDecoder* sk_wbmp_dfactory(SkStreamRewindable* stream) {
156    wbmp_head   head;
157
158    if (head.init(stream)) {
159        return SkNEW(SkWBMPImageDecoder);
160    }
161    return NULL;
162}
163
164static SkImageDecoder::Format get_format_wbmp(SkStreamRewindable* stream) {
165    wbmp_head head;
166    if (head.init(stream)) {
167        return SkImageDecoder::kWBMP_Format;
168    }
169    return SkImageDecoder::kUnknown_Format;
170}
171
172static SkImageDecoder_DecodeReg gDReg(sk_wbmp_dfactory);
173static SkImageDecoder_FormatReg gFormatReg(get_format_wbmp);
174