Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(36)

Side by Side Diff: src/codec/SkBmpStandardCodec.cpp

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

Powered by Google App Engine
This is Rietveld 408576698