blob: c475ec126e9ec14ef677a938977c4b735fcb7ca4 [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 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000023
reed@android.com8a1c16f2008-12-17 15:59:43 +000024protected:
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);
rmistry@google.comd6176b02012-08-23 18:14:13 +000043
reed@android.com8a1c16f2008-12-17 15:59:43 +000044 *value = n;
45 return true;
46}
47
48struct wbmp_head {
49 int fWidth;
50 int fHeight;
rmistry@google.comd6176b02012-08-23 18:14:13 +000051
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 bool init(SkStream* stream)
53 {
54 uint8_t data;
rmistry@google.comd6176b02012-08-23 18:14:13 +000055
reed@android.com8a1c16f2008-12-17 15:59:43 +000056 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};
rmistry@google.comd6176b02012-08-23 18:14:13 +000071
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;
rmistry@google.comd6176b02012-08-23 18:14:13 +000075
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 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 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000088
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 bits &= 7;
90 if (bits > 0) {
91 unsigned mask = *src;
92 do {
93 *dst++ = (mask >> 7) & 1;;
94 mask <<= 1;
rmistry@google.comd6176b02012-08-23 18:14:13 +000095 } while (--bits != 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 }
97}
98
reed@android.com8a1c16f2008-12-17 15:59:43 +000099bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
reed@android.com3f1f06a2010-03-03 21:04:12 +0000100 Mode mode)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101{
102 wbmp_head head;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000103
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 if (!head.init(stream)) {
105 return false;
106 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 int width = head.fWidth;
109 int height = head.fHeight;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000110
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 // assign these directly, in case we return kDimensions_Result
112 decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
113 decodedBitmap->setIsOpaque(true);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000114
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 if (SkImageDecoder::kDecodeBounds_Mode == mode)
116 return true;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000117
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
119 SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2));
120 SkAutoUnref aur(ct);
121
122 if (!this->allocPixelRef(decodedBitmap, ct)) {
123 return false;
124 }
125
126 SkAutoLockPixels alp(*decodedBitmap);
127
128 uint8_t* dst = decodedBitmap->getAddr8(0, 0);
129 // store the 1-bit valuess at the end of our pixels, so we won't stomp
130 // on them before we're read them. Just trying to avoid a temp allocation
131 size_t srcRB = SkAlign8(width) >> 3;
132 size_t srcSize = height * srcRB;
133 uint8_t* src = dst + decodedBitmap->getSize() - srcSize;
134 if (stream->read(src, srcSize) != srcSize) {
135 return false;
136 }
137
138 for (int y = 0; y < height; y++)
139 {
140 expand_bits_to_bytes(dst, src, width);
141 dst += decodedBitmap->rowBytes();
142 src += srcRB;
143 }
144
145 return true;
146}
147
reed@android.com00bf85a2009-01-22 13:04:56 +0000148///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000149DEFINE_DECODER_CREATOR(WBMPImageDecoder);
150///////////////////////////////////////////////////////////////////////////////
reed@android.com00bf85a2009-01-22 13:04:56 +0000151
152#include "SkTRegistry.h"
153
caryclark@google.com2a2cc202012-06-06 12:04:36 +0000154static SkImageDecoder* 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);