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

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: 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,
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, &quotient, &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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698