blob: db40f595fa74433776784bf3182c454d76aca3a0 [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:
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000020 virtual Format getFormat() const SK_OVERRIDE {
reed@android.com8a1c16f2008-12-17 15:59:43 +000021 return kWBMP_Format;
22 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000023
reed@android.com8a1c16f2008-12-17 15:59:43 +000024protected:
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000025 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
26
27private:
28 typedef SkImageDecoder INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +000029};
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);
rmistry@google.comd6176b02012-08-23 18:14:13 +000046
reed@android.com8a1c16f2008-12-17 15:59:43 +000047 *value = n;
48 return true;
49}
50
51struct wbmp_head {
52 int fWidth;
53 int fHeight;
rmistry@google.comd6176b02012-08-23 18:14:13 +000054
reed@android.com8a1c16f2008-12-17 15:59:43 +000055 bool init(SkStream* stream)
56 {
57 uint8_t data;
rmistry@google.comd6176b02012-08-23 18:14:13 +000058
reed@android.com8a1c16f2008-12-17 15:59:43 +000059 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};
rmistry@google.comd6176b02012-08-23 18:14:13 +000074
reed@android.com8a1c16f2008-12-17 15:59:43 +000075static void expand_bits_to_bytes(uint8_t dst[], const uint8_t src[], int bits)
76{
77 int bytes = bits >> 3;
rmistry@google.comd6176b02012-08-23 18:14:13 +000078
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 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 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000091
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 bits &= 7;
93 if (bits > 0) {
94 unsigned mask = *src;
95 do {
96 *dst++ = (mask >> 7) & 1;;
97 mask <<= 1;
rmistry@google.comd6176b02012-08-23 18:14:13 +000098 } while (--bits != 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 }
100}
101
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
reed@android.com3f1f06a2010-03-03 21:04:12 +0000103 Mode mode)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104{
105 wbmp_head head;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 if (!head.init(stream)) {
108 return false;
109 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000110
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 int width = head.fWidth;
112 int height = head.fHeight;
skia.committer@gmail.comc49cabf2013-03-15 07:05:19 +0000113
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000114 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
115 decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
116 decodedBitmap->setIsOpaque(true);
117 return true;
118 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000119
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000120 // No Bitmap reuse supported for this format
121 if (!decodedBitmap->isNull()) {
122 return false;
123 }
124
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
126 decodedBitmap->setIsOpaque(true);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000127
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
129 SkColorTable* ct = SkNEW_ARGS(SkColorTable, (colors, 2));
130 SkAutoUnref aur(ct);
131
132 if (!this->allocPixelRef(decodedBitmap, ct)) {
133 return false;
134 }
135
136 SkAutoLockPixels alp(*decodedBitmap);
137
138 uint8_t* dst = decodedBitmap->getAddr8(0, 0);
139 // store the 1-bit valuess at the end of our pixels, so we won't stomp
140 // on them before we're read them. Just trying to avoid a temp allocation
141 size_t srcRB = SkAlign8(width) >> 3;
142 size_t srcSize = height * srcRB;
143 uint8_t* src = dst + decodedBitmap->getSize() - srcSize;
144 if (stream->read(src, srcSize) != srcSize) {
145 return false;
146 }
147
148 for (int y = 0; y < height; y++)
149 {
150 expand_bits_to_bytes(dst, src, width);
151 dst += decodedBitmap->rowBytes();
152 src += srcRB;
153 }
154
155 return true;
156}
157
reed@android.com00bf85a2009-01-22 13:04:56 +0000158///////////////////////////////////////////////////////////////////////////////
robertphillips@google.comec51cb82012-03-23 18:13:47 +0000159DEFINE_DECODER_CREATOR(WBMPImageDecoder);
160///////////////////////////////////////////////////////////////////////////////
reed@android.com00bf85a2009-01-22 13:04:56 +0000161
162#include "SkTRegistry.h"
163
caryclark@google.com2a2cc202012-06-06 12:04:36 +0000164static SkImageDecoder* sk_wbmp_dfactory(SkStream* stream) {
reed@android.com00bf85a2009-01-22 13:04:56 +0000165 wbmp_head head;
166
167 if (head.init(stream)) {
168 return SkNEW(SkWBMPImageDecoder);
169 }
170 return NULL;
171}
172
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000173static SkImageDecoder::Format get_format_wbmp(SkStream* stream) {
174 wbmp_head head;
175 if (head.init(stream)) {
176 return SkImageDecoder::kWBMP_Format;
177 }
178 return SkImageDecoder::kUnknown_Format;
179}
180
robertphillips@google.com8570b5c2012-03-20 17:40:58 +0000181static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_wbmp_dfactory);
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000182static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_wbmp);