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