OLD | NEW |
---|---|
(Empty) | |
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 "SkBmpStandardCodec.h" | |
9 #include "SkCodecPriv.h" | |
10 #include "SkColorPriv.h" | |
11 #include "SkScanlineDecoder.h" | |
12 #include "SkStream.h" | |
13 | |
14 /* | |
15 * Checks if the conversion between the input image and the requested output | |
16 * image has been implemented | |
17 */ | |
18 static bool conversion_possible(const SkImageInfo& dst, | |
scroggo
2015/07/31 15:05:44
This function looks like it can be shared? (Possib
msarett
2015/08/03 22:52:35
Umm not quite. I tried to share it. The best I c
| |
19 const SkImageInfo& src) { | |
20 // Ensure that the profile type is unchanged | |
21 if (dst.profileType() != src.profileType()) { | |
22 return false; | |
23 } | |
24 | |
25 // Ensure the alpha type is valid | |
26 if (!valid_alpha(dst.alphaType(), src.alphaType())) { | |
27 // In order to correct invalid transparent decodes, we will always | |
28 // allow dst alpha type to be opaque. | |
29 if (kOpaque_SkAlphaType != dst.alphaType()) { | |
30 return false; | |
31 } | |
32 SkCodecPrintf("Warning: The client should not request an opaque " | |
33 "decode if we indicate that the encoded data is not opaque. " | |
34 "We should only hit this case on the automatic correction " | |
35 "of invalid transparent decodes.\n"); | |
36 } | |
37 | |
38 // Check for supported color types | |
39 switch (dst.colorType()) { | |
40 // Allow output to kN32 from any type of input | |
41 case kN32_SkColorType: | |
42 return true; | |
43 // Allow output to kIndex_8 from compatible inputs | |
44 case kIndex_8_SkColorType: | |
45 return kIndex_8_SkColorType == src.colorType(); | |
46 default: | |
47 return false; | |
48 } | |
49 } | |
50 | |
51 /* | |
52 * Creates an instance of the decoder | |
53 * Called only by NewFromStream | |
54 */ | |
55 SkBmpStandardCodec::SkBmpStandardCodec(const SkImageInfo& info, SkStream* stream , | |
56 uint16_t bitsPerPixel, uint32_t numColors , | |
57 uint32_t bytesPerColor, uint32_t offset, | |
58 SkBmpCodec::RowOrder rowOrder, bool isIco ) | |
59 : INHERITED(info, stream) | |
60 , fBitsPerPixel(bitsPerPixel) | |
61 , fColorTable(NULL) | |
62 , fNumColors(numColors) | |
63 , fBytesPerColor(bytesPerColor) | |
64 , fOffset(offset) | |
65 , fRowOrder(rowOrder) | |
66 , fSwizzler(NULL) | |
67 , fSrcBuffer(NULL) | |
68 , fIsIco(isIco) | |
69 , fIsTransparent(true) | |
70 {} | |
71 | |
72 /* | |
73 * Initiates the bitmap decode | |
74 */ | |
75 SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo, | |
76 void* dst, size_t dstRowBytes, | |
77 const Options& opts, | |
78 SkPMColor* inputColorPtr, | |
79 int* inputColorCount) { | |
80 // Check for proper input and output formats | |
81 SkCodec::RewindState rewindState = this->rewindIfNeeded(); | |
82 if (rewindState == kCouldNotRewind_RewindState) { | |
83 return kCouldNotRewind; | |
84 } else if (rewindState == kRewound_RewindState) { | |
85 if (!SkBmpCodec::ReadHeader(this->stream(), fIsIco, NULL)) { | |
86 return kCouldNotRewind; | |
87 } | |
88 } | |
89 if (opts.fSubset) { | |
90 // Subsets are not supported. | |
91 return kUnimplemented; | |
92 } | |
93 if (dstInfo.dimensions() != this->getInfo().dimensions()) { | |
94 SkCodecPrintf("Error: scaling not supported.\n"); | |
95 return kInvalidScale; | |
96 } | |
97 if (!conversion_possible(dstInfo, this->getInfo())) { | |
98 SkCodecPrintf("Error: cannot convert input type to output type.\n"); | |
99 return kInvalidConversion; | |
100 } | |
101 | |
102 // Create the color table if necessary and prepare the stream for decode | |
103 // Note that if it is non-NULL, inputColorCount will be modified | |
104 if (!this->createColorTable(dstInfo.alphaType(), inputColorCount)) { | |
105 SkCodecPrintf("Error: could not create color table.\n"); | |
106 return kInvalidInput; | |
107 } | |
108 | |
109 // Copy the color table to the client if necessary | |
110 copy_color_table(dstInfo, fColorTable, inputColorPtr, inputColorCount); | |
111 | |
112 // Initialize a swizzler if necessary | |
113 if (!this->initializeSwizzler(dstInfo, opts)) { | |
114 SkCodecPrintf("Error: cannot initialize swizzler.\n"); | |
115 return kInvalidConversion; | |
116 } | |
117 | |
118 // Perform the decode | |
119 SkCodec::Result result = decode(dstInfo, dst, dstRowBytes, opts); | |
120 | |
121 // Fix the decode, if neceessary | |
122 if (fIsTransparent) { | |
123 result = SkBmpCodec::FixTransparentDecode(dst, dstRowBytes, | |
124 inputColorPtr, inputColorCount, this->stream()->duplicate()); | |
125 } | |
126 | |
127 return result; | |
128 } | |
129 | |
130 /* | |
131 * Process the color table for the bmp input | |
132 */ | |
133 bool SkBmpStandardCodec::createColorTable(SkAlphaType alphaType, int* numColors ) { | |
134 // Allocate memory for color table | |
135 uint32_t colorBytes = 0; | |
136 uint32_t maxColors = 0; | |
137 SkPMColor colorTable[256]; | |
138 if (fBitsPerPixel <= 8) { | |
139 // Zero is a default for maxColors | |
140 // Also set fNumColors to maxColors when it is too large | |
141 maxColors = 1 << fBitsPerPixel; | |
142 if (fNumColors == 0 || fNumColors >= maxColors) { | |
143 fNumColors = maxColors; | |
144 } | |
145 | |
146 // Inform the caller of the number of colors | |
147 if (NULL != numColors) { | |
148 // We set the number of colors to maxColors in order to ensure | |
149 // safe memory accesses. Otherwise, an invalid pixel could | |
150 // access memory outside of our color table array. | |
151 *numColors = maxColors; | |
152 } | |
153 | |
154 // Read the color table from the stream | |
155 colorBytes = fNumColors * fBytesPerColor; | |
156 SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes)); | |
157 if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) { | |
158 SkCodecPrintf("Error: unable to read color table.\n"); | |
159 return false; | |
160 } | |
161 | |
162 // Choose the proper packing function | |
163 SkPMColor (*packARGB) (uint32_t, uint32_t, uint32_t, uint32_t); | |
164 switch (alphaType) { | |
165 case kOpaque_SkAlphaType: | |
166 case kUnpremul_SkAlphaType: | |
167 packARGB = &SkPackARGB32NoCheck; | |
168 break; | |
169 case kPremul_SkAlphaType: | |
170 packARGB = &SkPreMultiplyARGB; | |
171 break; | |
172 default: | |
173 // This should not be reached because conversion possible | |
174 // should fail if the alpha type is not one of the above | |
175 // values. | |
176 SkASSERT(false); | |
177 packARGB = NULL; | |
178 break; | |
179 } | |
180 | |
181 // Fill in the color table | |
182 uint32_t i = 0; | |
183 for (; i < fNumColors; i++) { | |
184 uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor); | |
185 uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1); | |
186 uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2); | |
187 uint8_t alpha; | |
188 if (kOpaque_SkAlphaType == alphaType) { | |
189 alpha = 0xFF; | |
190 } else { | |
191 alpha = get_byte(cBuffer.get(), i*fBytesPerColor + 3); | |
192 } | |
193 colorTable[i] = packARGB(alpha, red, green, blue); | |
194 } | |
195 | |
196 // To avoid segmentation faults on bad pixel data, fill the end of the | |
197 // color table with black. This is the same the behavior as the | |
198 // chromium decoder. | |
199 for (; i < maxColors; i++) { | |
200 colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0); | |
201 } | |
202 | |
203 // Set the color table | |
204 fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorTable, maxColors))); | |
205 } | |
206 | |
207 // Bmp-in-Ico files do not use an offset to indicate where the pixel data | |
208 // begins. Pixel data always begins immediately after the color table. | |
209 if (!fIsIco) { | |
210 // Check that we have not read past the pixel array offset | |
211 if(fOffset < colorBytes) { | |
212 // This may occur on OS 2.1 and other old versions where the color | |
213 // table defaults to max size, and the bmp tries to use a smaller | |
214 // color table. This is invalid, and our decision is to indicate | |
215 // an error, rather than try to guess the intended size of the | |
216 // color table. | |
217 SkCodecPrintf("Error: pixel data offset less than color table size.\ n"); | |
218 return false; | |
219 } | |
220 | |
221 // After reading the color table, skip to the start of the pixel array | |
222 if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) { | |
223 SkCodecPrintf("Error: unable to skip to image data.\n"); | |
224 return false; | |
225 } | |
226 } | |
227 | |
228 // Return true on success | |
229 return true; | |
230 } | |
231 | |
232 static const SkPMColor* get_color_ptr(SkColorTable* colorTable) { | |
233 return NULL != colorTable ? colorTable->readColors() : NULL; | |
234 } | |
235 | |
236 bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, | |
237 const Options& opts) { | |
238 // Allocate space for a row buffer | |
239 const size_t rowBytes = SkAlign4(compute_row_bytes(dstInfo.width(), fBitsPer Pixel)); | |
240 fSrcBuffer.reset(SkNEW_ARRAY(uint8_t, rowBytes)); | |
241 | |
242 // Get swizzler configuration | |
243 SkSwizzler::SrcConfig config; | |
244 switch (fBitsPerPixel) { | |
245 case 1: | |
246 config = SkSwizzler::kIndex1; | |
247 break; | |
248 case 2: | |
249 config = SkSwizzler::kIndex2; | |
250 break; | |
251 case 4: | |
252 config = SkSwizzler::kIndex4; | |
253 break; | |
254 case 8: | |
255 config = SkSwizzler::kIndex; | |
256 break; | |
257 case 24: | |
258 config = SkSwizzler::kBGR; | |
259 break; | |
260 case 32: | |
261 if (kOpaque_SkAlphaType == dstInfo.alphaType()) { | |
262 config = SkSwizzler::kBGRX; | |
263 } else { | |
264 config = SkSwizzler::kBGRA; | |
265 } | |
266 break; | |
267 default: | |
268 SkASSERT(false); | |
269 return kInvalidInput; | |
270 } | |
271 | |
272 // Get a pointer to the color table if it exists | |
273 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | |
274 | |
275 // Create swizzler | |
276 fSwizzler.reset(SkSwizzler::CreateSwizzler(config, | |
277 colorPtr, dstInfo, opts.fZeroInitialized)); | |
278 | |
279 if (NULL == fSwizzler.get()) { | |
280 return false; | |
281 } | |
282 return true; | |
283 } | |
284 | |
285 /* | |
286 * Choose a fill for failures due to an incomplete image. We will use zero as | |
287 * the default palette index, black for opaque images, and transparent for | |
288 * non-opaque images. | |
289 */ | |
290 static uint32_t get_fill_color_or_index(uint16_t bitsPerPixels, SkAlphaType alph aType) { | |
291 uint32_t fillColorOrIndex; | |
292 switch (bitsPerPixels) { | |
293 case 1: | |
294 case 2: | |
295 case 4: | |
296 case 8: | |
297 fillColorOrIndex = 0; | |
298 break; | |
299 case 24: | |
300 fillColorOrIndex = SK_ColorBLACK; | |
301 break; | |
302 case 32: | |
303 if (kOpaque_SkAlphaType == alphaType) { | |
304 fillColorOrIndex = SK_ColorBLACK; | |
305 } else { | |
306 fillColorOrIndex = SK_ColorTRANSPARENT; | |
307 } | |
308 break; | |
309 default: | |
310 SkASSERT(false); | |
311 return 0; | |
312 } | |
313 return fillColorOrIndex; | |
314 } | |
315 | |
316 /* | |
317 * Performs the bitmap decoding for standard input format | |
318 */ | |
319 SkCodec::Result SkBmpStandardCodec::decode(const SkImageInfo& dstInfo, | |
320 void* dst, size_t dstRowBytes, | |
321 const Options& opts) { | |
322 // Set constant values | |
323 const int width = dstInfo.width(); | |
324 const int height = dstInfo.height(); | |
325 const size_t rowBytes = SkAlign4(compute_row_bytes(width, fBitsPerPixel)); | |
326 | |
327 // Iterate over rows of the image | |
328 for (int y = 0; y < height; y++) { | |
329 // Read a row of the input | |
330 if (this->stream()->read(fSrcBuffer.get(), rowBytes) != rowBytes) { | |
331 SkCodecPrintf("Warning: incomplete input stream.\n"); | |
332 // Fill the destination image on failure | |
333 // Get the fill color/index and check if it is 0 | |
334 uint32_t fillColorOrIndex = get_fill_color_or_index(fBitsPerPixel, d stInfo.alphaType()); | |
335 bool zeroFill = (0 == fillColorOrIndex); | |
336 | |
337 if (kNo_ZeroInitialized == opts.fZeroInitialized || !zeroFill) { | |
338 // Get a pointer to the color table if it exists | |
339 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | |
340 | |
341 void* dstStart = get_dst_start_row(dst, dstRowBytes, y, fRowOrde r); | |
342 SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height( ) - y, | |
343 fillColorOrIndex, colorPtr); | |
344 } | |
345 return kIncompleteInput; | |
346 } | |
347 | |
348 // Decode the row in destination format | |
349 uint32_t row; | |
350 if (SkBmpCodec::kTopDown_RowOrder == fRowOrder) { | |
351 row = y; | |
352 } else { | |
353 row = height - 1 - y; | |
354 } | |
355 | |
356 void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes); | |
357 SkSwizzler::ResultAlpha r = fSwizzler->swizzle(dstRow, fSrcBuffer.get()) ; | |
358 fIsTransparent &= SkSwizzler::IsTransparent(r); | |
359 } | |
360 | |
361 // Finally, apply the AND mask for bmp-in-ico images | |
362 if (fIsIco) { | |
363 // The AND mask is always 1 bit per pixel | |
364 const size_t rowBytes = SkAlign4(compute_row_bytes(width, 1)); | |
365 | |
366 SkPMColor* dstPtr = (SkPMColor*) dst; | |
367 for (int y = 0; y < height; y++) { | |
368 // The srcBuffer will at least be large enough | |
369 if (stream()->read(fSrcBuffer.get(), rowBytes) != rowBytes) { | |
370 SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n"); | |
371 return kIncompleteInput; | |
372 } | |
373 | |
374 int row; | |
375 if (SkBmpCodec::kBottomUp_RowOrder == fRowOrder) { | |
376 row = height - y - 1; | |
377 } else { | |
378 row = y; | |
379 } | |
380 | |
381 SkPMColor* dstRow = | |
382 SkTAddOffset<SkPMColor>(dstPtr, row * dstRowBytes); | |
383 | |
384 for (int x = 0; x < width; x++) { | |
385 int quotient; | |
386 int modulus; | |
387 SkTDivMod(x, 8, "ient, &modulus); | |
388 uint32_t shift = 7 - modulus; | |
389 uint32_t alphaBit = | |
390 (fSrcBuffer.get()[quotient] >> shift) & 0x1; | |
391 dstRow[x] &= alphaBit - 1; | |
392 } | |
393 } | |
394 } | |
395 | |
396 // Finished decoding the entire image | |
397 return kSuccess; | |
398 } | |
OLD | NEW |