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

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: SkBmpCodec is a parent class of the bmp codecs 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 // 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, &quotient, &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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698