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

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,
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(this->computeNumColors(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 SkPMColor colorTable[256];
114 if (this->bitsPerPixel() <= 8) {
115 // Inform the caller of the number of colors
116 uint32_t maxColors = 1 << this->bitsPerPixel();
117 if (NULL != numColors) {
118 // We set the number of colors to maxColors in order to ensure
119 // safe memory accesses. Otherwise, an invalid pixel could
120 // access memory outside of our color table array.
121 *numColors = maxColors;
122 }
123
124 // Read the color table from the stream
125 colorBytes = fNumColors * fBytesPerColor;
126 SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
127 if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
128 SkCodecPrintf("Error: unable to read color table.\n");
129 return false;
130 }
131
132 // Choose the proper packing function
133 SkPMColor (*packARGB) (uint32_t, uint32_t, uint32_t, uint32_t);
134 switch (alphaType) {
135 case kOpaque_SkAlphaType:
136 case kUnpremul_SkAlphaType:
137 packARGB = &SkPackARGB32NoCheck;
138 break;
139 case kPremul_SkAlphaType:
140 packARGB = &SkPreMultiplyARGB;
141 break;
142 default:
143 // This should not be reached because conversion possible
144 // should fail if the alpha type is not one of the above
145 // values.
146 SkASSERT(false);
147 packARGB = NULL;
148 break;
149 }
150
151 // Fill in the color table
152 uint32_t i = 0;
153 for (; i < fNumColors; i++) {
154 uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
155 uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
156 uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
157 uint8_t alpha;
158 if (kOpaque_SkAlphaType == alphaType) {
159 alpha = 0xFF;
160 } else {
161 alpha = get_byte(cBuffer.get(), i*fBytesPerColor + 3);
162 }
163 colorTable[i] = packARGB(alpha, red, green, blue);
164 }
165
166 // To avoid segmentation faults on bad pixel data, fill the end of the
167 // color table with black. This is the same the behavior as the
168 // chromium decoder.
169 for (; i < maxColors; i++) {
170 colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
171 }
172
173 // Set the color table
174 fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorTable, maxColors)));
175 }
176
177 // Bmp-in-Ico files do not use an offset to indicate where the pixel data
178 // begins. Pixel data always begins immediately after the color table.
179 if (!fInIco) {
180 // Check that we have not read past the pixel array offset
181 if(fOffset < colorBytes) {
182 // This may occur on OS 2.1 and other old versions where the color
183 // table defaults to max size, and the bmp tries to use a smaller
184 // color table. This is invalid, and our decision is to indicate
185 // an error, rather than try to guess the intended size of the
186 // color table.
187 SkCodecPrintf("Error: pixel data offset less than color table size.\ n");
188 return false;
189 }
190
191 // After reading the color table, skip to the start of the pixel array
192 if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
193 SkCodecPrintf("Error: unable to skip to image data.\n");
194 return false;
195 }
196 }
197
198 // Return true on success
199 return true;
200 }
201
202 static const SkPMColor* get_color_ptr(SkColorTable* colorTable) {
203 return NULL != colorTable ? colorTable->readColors() : NULL;
204 }
205
206 bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo,
207 const Options& opts) {
208 // Allocate space for a row buffer
209 const size_t rowBytes = SkAlign4(compute_row_bytes(dstInfo.width(), this->bi tsPerPixel()));
210 fSrcBuffer.reset(SkNEW_ARRAY(uint8_t, rowBytes));
211
212 // Get swizzler configuration
213 SkSwizzler::SrcConfig config;
214 switch (this->bitsPerPixel()) {
215 case 1:
216 config = SkSwizzler::kIndex1;
217 break;
218 case 2:
219 config = SkSwizzler::kIndex2;
220 break;
221 case 4:
222 config = SkSwizzler::kIndex4;
223 break;
224 case 8:
225 config = SkSwizzler::kIndex;
226 break;
227 case 24:
228 config = SkSwizzler::kBGR;
229 break;
230 case 32:
231 if (kOpaque_SkAlphaType == dstInfo.alphaType()) {
232 config = SkSwizzler::kBGRX;
233 } else {
234 config = SkSwizzler::kBGRA;
235 }
236 break;
237 default:
238 SkASSERT(false);
239 return kInvalidInput;
240 }
241
242 // Get a pointer to the color table if it exists
243 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
244
245 // Create swizzler
246 fSwizzler.reset(SkSwizzler::CreateSwizzler(config,
247 colorPtr, dstInfo, opts.fZeroInitialized));
248
249 if (NULL == fSwizzler.get()) {
250 return false;
251 }
252 return true;
253 }
254
255 /*
256 * Choose a fill for failures due to an incomplete image. We will use zero as
257 * the default palette index, black for opaque images, and transparent for
258 * non-opaque images.
259 */
260 static uint32_t get_fill_color_or_index(uint16_t bitsPerPixels, SkAlphaType alph aType) {
261 uint32_t fillColorOrIndex;
262 switch (bitsPerPixels) {
263 case 1:
264 case 2:
265 case 4:
266 case 8:
267 fillColorOrIndex = 0;
268 break;
269 case 24:
270 fillColorOrIndex = SK_ColorBLACK;
271 break;
272 case 32:
273 if (kOpaque_SkAlphaType == alphaType) {
274 fillColorOrIndex = SK_ColorBLACK;
275 } else {
276 fillColorOrIndex = SK_ColorTRANSPARENT;
277 }
278 break;
279 default:
280 SkASSERT(false);
281 return 0;
282 }
283 return fillColorOrIndex;
284 }
285
286 /*
287 * Performs the bitmap decoding for standard input format
288 */
289 SkCodec::Result SkBmpStandardCodec::decode(const SkImageInfo& dstInfo,
290 void* dst, size_t dstRowBytes,
291 const Options& opts) {
292 // Set constant values
293 const int width = dstInfo.width();
294 const int height = dstInfo.height();
295 const size_t rowBytes = SkAlign4(compute_row_bytes(width, this->bitsPerPixel ()));
296
297 // Iterate over rows of the image
298 for (int y = 0; y < height; y++) {
299 // Read a row of the input
300 if (this->stream()->read(fSrcBuffer.get(), rowBytes) != rowBytes) {
301 SkCodecPrintf("Warning: incomplete input stream.\n");
302 // Fill the destination image on failure
303 // Get the fill color/index and check if it is 0
304 uint32_t fillColorOrIndex = get_fill_color_or_index(this->bitsPerPix el(),
305 dstInfo.alphaType());
306 bool zeroFill = (0 == fillColorOrIndex);
307
308 if (kNo_ZeroInitialized == opts.fZeroInitialized || !zeroFill) {
309 // Get a pointer to the color table if it exists
310 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
311
312 void* dstStart = this->getDstStartRow(dst, dstRowBytes, y);
313 SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height( ) - y,
314 fillColorOrIndex, colorPtr);
315 }
316 return kIncompleteInput;
317 }
318
319 // Decode the row in destination format
320 uint32_t row;
321 if (SkBmpCodec::kTopDown_RowOrder == this->rowOrder()) {
322 row = y;
323 } else {
324 row = height - 1 - y;
325 }
326
327 void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes);
328 fSwizzler->swizzle(dstRow, fSrcBuffer.get());
329 }
330
331 // Finally, apply the AND mask for bmp-in-ico images
332 if (fInIco) {
333 // The AND mask is always 1 bit per pixel
334 const size_t rowBytes = SkAlign4(compute_row_bytes(width, 1));
335
336 SkPMColor* dstPtr = (SkPMColor*) dst;
337 for (int y = 0; y < height; y++) {
338 // The srcBuffer will at least be large enough
339 if (stream()->read(fSrcBuffer.get(), rowBytes) != rowBytes) {
340 SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n");
341 return kIncompleteInput;
342 }
343
344 int row;
345 if (SkBmpCodec::kBottomUp_RowOrder == this->rowOrder()) {
346 row = height - y - 1;
347 } else {
348 row = y;
349 }
350
351 SkPMColor* dstRow =
352 SkTAddOffset<SkPMColor>(dstPtr, row * dstRowBytes);
353
354 for (int x = 0; x < width; x++) {
355 int quotient;
356 int modulus;
357 SkTDivMod(x, 8, &quotient, &modulus);
358 uint32_t shift = 7 - modulus;
359 uint32_t alphaBit =
360 (fSrcBuffer.get()[quotient] >> shift) & 0x1;
361 dstRow[x] &= alphaBit - 1;
362 }
363 }
364 }
365
366 // Finished decoding the entire image
367 return kSuccess;
368 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698