blob: 262cf547cda40d4dde0dc5a6e5411fc8ff160b2c [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
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 {
21 return kWBMP_Format;
22 }
23
24protected:
reed@android.com3f1f06a2010-03-03 21:04:12 +000025 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +000026};
27
28static bool read_byte(SkStream* stream, uint8_t* data)
29{
30 return stream->read(data, 1) == 1;
31}
32
33static bool read_mbf(SkStream* stream, int* value)
34{
35 int n = 0;
36 uint8_t data;
37 do {
38 if (!read_byte(stream, &data)) {
39 return false;
40 }
41 n = (n << 7) | (data & 0x7F);
42 } while (data & 0x80);
43
44 *value = n;
45 return true;
46}
47
48struct wbmp_head {
49 int fWidth;
50 int fHeight;
51
52 bool init(SkStream* stream)
53 {
54 uint8_t data;
55
56 if (!read_byte(stream, &data) || data != 0) { // unknown type
57 return false;
58 }
59 if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header
60 return false;
61 }
62 if (!read_mbf(stream, &fWidth) || (unsigned)fWidth > 0xFFFF) {
63 return false;
64 }
65 if (!read_mbf(stream, &fHeight) || (unsigned)fHeight > 0xFFFF) {
66 return false;
67 }
68 return fWidth != 0 && fHeight != 0;
69 }
70};
71
reed@android.com8a1c16f2008-12-17 15:59:43 +000072static void expand_bits_to_bytes(uint8_t dst[], const uint8_t src[], int bits)
73{
74 int bytes = bits >> 3;
75
76 for (int i = 0; i < bytes; i++) {
77 unsigned mask = *src++;
78 dst[0] = (mask >> 7) & 1;
79 dst[1] = (mask >> 6) & 1;
80 dst[2] = (mask >> 5) & 1;
81 dst[3] = (mask >> 4) & 1;
82 dst[4] = (mask >> 3) & 1;
83 dst[5] = (mask >> 2) & 1;
84 dst[6] = (mask >> 1) & 1;
85 dst[7] = (mask >> 0) & 1;
86 dst += 8;
87 }
88
89 bits &= 7;
90 if (bits > 0) {
91 unsigned mask = *src;
92 do {
93 *dst++ = (mask >> 7) & 1;;
94 mask <<= 1;
95 } while (--bits != 0);
96 }
97}
98
99#define SkAlign8(x) (((x) + 7) & ~7)
100
101bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
reed@android.com3f1f06a2010-03-03 21:04:12 +0000102 Mode mode)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103{
104 wbmp_head head;
105
106 if (!head.init(stream)) {
107 return false;
108 }
109
110 int width = head.fWidth;
111 int height = head.fHeight;
112
113 // assign these directly, in case we return kDimensions_Result
114 decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
115 decodedBitmap->setIsOpaque(true);
116
117 if (SkImageDecoder::kDecodeBounds_Mode == mode)
118 return true;
119
120 const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
121 SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2));
122 SkAutoUnref aur(ct);
123
124 if (!this->allocPixelRef(decodedBitmap, ct)) {
125 return false;
126 }
127
128 SkAutoLockPixels alp(*decodedBitmap);
129
130 uint8_t* dst = decodedBitmap->getAddr8(0, 0);
131 // store the 1-bit valuess at the end of our pixels, so we won't stomp
132 // on them before we're read them. Just trying to avoid a temp allocation
133 size_t srcRB = SkAlign8(width) >> 3;
134 size_t srcSize = height * srcRB;
135 uint8_t* src = dst + decodedBitmap->getSize() - srcSize;
136 if (stream->read(src, srcSize) != srcSize) {
137 return false;
138 }
139
140 for (int y = 0; y < height; y++)
141 {
142 expand_bits_to_bytes(dst, src, width);
143 dst += decodedBitmap->rowBytes();
144 src += srcRB;
145 }
146
147 return true;
148}
149
reed@android.com00bf85a2009-01-22 13:04:56 +0000150///////////////////////////////////////////////////////////////////////////////
151
152#include "SkTRegistry.h"
153
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000154SkImageDecoder* sk_wbmp_dfactory(SkStream* stream) {
reed@android.com00bf85a2009-01-22 13:04:56 +0000155 wbmp_head head;
156
157 if (head.init(stream)) {
158 return SkNEW(SkWBMPImageDecoder);
159 }
160 return NULL;
161}
162
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000163static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_wbmp_dfactory);
reed@android.com00bf85a2009-01-22 13:04:56 +0000164