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

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

Powered by Google App Engine
This is Rietveld 408576698