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

Side by Side Diff: src/codec/SkBmpRLECodec.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 "SkBmpRLECodec.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 SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream,
48 uint16_t bitsPerPixel, uint32_t numColors,
49 uint32_t bytesPerColor, uint32_t offset,
50 SkBmpCodec::RowOrder rowOrder, size_t RLEBytes)
51 : INHERITED(info, stream)
52 , fBitsPerPixel(bitsPerPixel)
53 , fColorTable(NULL)
54 , fNumColors(numColors)
55 , fBytesPerColor(bytesPerColor)
56 , fOffset(offset)
57 , fRowOrder(rowOrder)
58 , fStreamBuffer(SkNEW_ARRAY(uint8_t, RLEBytes))
59 , fRLEBytes(RLEBytes)
60 , fCurrRLEByte(0)
61 {}
62
63 /*
64 * Initiates the bitmap decode
65 */
66 SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
67 void* dst, size_t dstRowBytes,
68 const Options& opts,
69 SkPMColor* inputColorPtr,
70 int* inputColorCount) {
71 // Check for proper input and output formats
scroggo 2015/07/31 15:05:43 This comment seems to not apply to the code next t
msarett 2015/08/03 22:52:35 I'll eliminate it.
72 SkCodec::RewindState rewindState = this->rewindIfNeeded();
scroggo 2015/07/31 15:05:43 This code should be shared between the codecs. Aga
msarett 2015/08/03 22:52:35 It's not quite so simple because of isIco, but I a
73 if (rewindState == kCouldNotRewind_RewindState) {
74 return kCouldNotRewind;
75 } else if (rewindState == kRewound_RewindState) {
76 if (!SkBmpCodec::ReadHeader(this->stream(), false, NULL)) {
77 return kCouldNotRewind;
78 }
79 }
80 if (opts.fSubset) {
81 // Subsets are not supported.
82 return kUnimplemented;
83 }
84 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
85 SkCodecPrintf("Error: scaling not supported.\n");
86 return kInvalidScale;
87 }
88 if (!conversion_possible(dstInfo, this->getInfo())) {
89 SkCodecPrintf("Error: cannot convert input type to output type.\n");
90 return kInvalidConversion;
91 }
92
93 // Create the color table if necessary and prepare the stream for decode
94 // Note that if it is non-NULL, inputColorCount will be modified
95 if (!this->createColorTable(dstInfo.alphaType(), inputColorCount)) {
96 SkCodecPrintf("Error: could not create color table.\n");
97 return kInvalidInput;
98 }
99
100 // Copy the color table to the client if necessary
101 copy_color_table(dstInfo, fColorTable, inputColorPtr, inputColorCount);
102
103 // Initialize a swizzler if necessary
104 if (!this->initializeStreamBuffer()) {
105 SkCodecPrintf("Error: cannot initialize swizzler.\n");
106 return kInvalidConversion;
107 }
108
109 // Perform the decode
110 return decode(dstInfo, dst, dstRowBytes, opts);
111 }
112
113 /*
114 * Process the color table for the bmp input
115 */
116 bool SkBmpRLECodec::createColorTable(SkAlphaType alphaType, int* numColors) {
117 // Allocate memory for color table
118 uint32_t colorBytes = 0;
119 uint32_t maxColors = 0;
120 SkPMColor colorTable[256];
121 if (fBitsPerPixel <= 8) {
122 // Zero is a default for maxColors
123 // Also set fNumColors to maxColors when it is too large
124 maxColors = 1 << fBitsPerPixel;
125 if (fNumColors == 0 || fNumColors >= maxColors) {
126 fNumColors = maxColors;
127 }
128
129 // Inform the caller of the number of colors
130 if (NULL != numColors) {
131 // We set the number of colors to maxColors in order to ensure
132 // safe memory accesses. Otherwise, an invalid pixel could
133 // access memory outside of our color table array.
134 *numColors = maxColors;
135 }
136
137 // Read the color table from the stream
138 colorBytes = fNumColors * fBytesPerColor;
139 SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
140 if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
141 SkCodecPrintf("Error: unable to read color table.\n");
142 return false;
143 }
144
145 // Fill in the color table
146 uint32_t i = 0;
147 for (; i < fNumColors; i++) {
148 uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
149 uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
150 uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
151 colorTable[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
152 }
153
154 // To avoid segmentation faults on bad pixel data, fill the end of the
155 // color table with black. This is the same the behavior as the
156 // chromium decoder.
157 for (; i < maxColors; i++) {
158 colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
159 }
160
161 // Set the color table
162 fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorTable, maxColors)));
163 }
164
165 // Check that we have not read past the pixel array offset
166 if(fOffset < colorBytes) {
167 // This may occur on OS 2.1 and other old versions where the color
168 // table defaults to max size, and the bmp tries to use a smaller
169 // color table. This is invalid, and our decision is to indicate
170 // an error, rather than try to guess the intended size of the
171 // color table.
172 SkCodecPrintf("Error: pixel data offset less than color table size.\n");
173 return false;
174 }
175
176 // After reading the color table, skip to the start of the pixel array
177 if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
178 SkCodecPrintf("Error: unable to skip to image data.\n");
179 return false;
180 }
181
182 // Return true on success
183 return true;
184 }
185
186 bool SkBmpRLECodec::initializeStreamBuffer() {
187 // Setup a buffer to contain the full input stream
188 size_t totalBytes = this->stream()->read(fStreamBuffer.get(), fRLEBytes);
189 if (totalBytes < fRLEBytes) {
190 fRLEBytes = totalBytes;
191 SkCodecPrintf("Warning: incomplete RLE file.\n");
192 }
193 if (fRLEBytes == 0) {
194 SkCodecPrintf("Error: could not read RLE image data.\n");
195 return false;
196 }
197 return true;
198 }
199
200 /*
201 * Set an RLE pixel using the color table
202 */
203 void SkBmpRLECodec::setPixel(void* dst, size_t dstRowBytes,
204 const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
205 uint8_t index) {
206 // Set the row
207 int height = dstInfo.height();
208 int row;
209 if (SkBmpCodec::kBottomUp_RowOrder == fRowOrder) {
210 row = height - y - 1;
211 } else {
212 row = y;
213 }
214
215 // Set the pixel based on destination color type
216 switch (dstInfo.colorType()) {
217 case kN32_SkColorType: {
218 SkPMColor* dstRow = SkTAddOffset<SkPMColor>((SkPMColor*) dst,
219 row * (int) dstRowBytes);
220 dstRow[x] = fColorTable->operator[](index);
221 break;
222 }
223 default:
224 // This case should not be reached. We should catch an invalid
225 // color type when we check that the conversion is possible.
226 SkASSERT(false);
227 break;
228 }
229 }
230
231 /*
232 * Set an RLE pixel from R, G, B values
233 */
234 void SkBmpRLECodec::setRGBPixel(void* dst, size_t dstRowBytes,
235 const SkImageInfo& dstInfo, uint32_t x,
236 uint32_t y, uint8_t red, uint8_t green,
237 uint8_t blue) {
238 // Set the row
239 int height = dstInfo.height();
240 int row;
241 if (SkBmpCodec::kBottomUp_RowOrder == fRowOrder) {
242 row = height - y - 1;
243 } else {
244 row = y;
245 }
246
247 // Set the pixel based on destination color type
248 switch (dstInfo.colorType()) {
249 case kN32_SkColorType: {
250 SkPMColor* dstRow = SkTAddOffset<SkPMColor>((SkPMColor*) dst,
251 row * (int) dstRowBytes);
252 dstRow[x] = SkPackARGB32NoCheck(0xFF, red, green, blue);
253 break;
254 }
255 default:
256 // This case should not be reached. We should catch an invalid
257 // color type when we check that the conversion is possible.
258 SkASSERT(false);
259 break;
260 }
261 }
262
263 /*
264 * Performs the bitmap decoding for RLE input format
265 * RLE decoding is performed all at once, rather than a one row at a time
266 */
267 SkCodec::Result SkBmpRLECodec::decode(const SkImageInfo& dstInfo,
268 void* dst, size_t dstRowBytes,
269 const Options& opts) {
270 // Set RLE flags
271 static const uint8_t RLE_ESCAPE = 0;
272 static const uint8_t RLE_EOL = 0;
273 static const uint8_t RLE_EOF = 1;
274 static const uint8_t RLE_DELTA = 2;
275
276 // Set constant values
277 const int width = dstInfo.width();
278 const int height = dstInfo.height();
279
280 // Destination parameters
281 int x = 0;
282 int y = 0;
283
284 // Set the background as transparent. Then, if the RLE code skips pixels,
285 // the skipped pixels will be transparent.
286 // Because of the need for transparent pixels, kN32 is the only color
287 // type that makes sense for the destination format.
288 SkASSERT(kN32_SkColorType == dstInfo.colorType());
289 if (kNo_ZeroInitialized == opts.fZeroInitialized) {
290 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, SK_ColorTRANSPARENT, NULL);
291 }
292
293 while (true) {
294 // If we have reached a row that is beyond the requested height, we have
295 // succeeded.
296 if (y >= height) {
297 // It would be better to check for the EOF marker before returning
298 // success, but we may be performing a scanline decode, which
299 // may require us to stop before decoding the full height.
300 return kSuccess;
301 }
302
303 // Every entry takes at least two bytes
304 if ((int) fRLEBytes - fCurrRLEByte < 2) {
305 SkCodecPrintf("Warning: incomplete RLE input.\n");
306 return kIncompleteInput;
307 }
308
309 // Read the next two bytes. These bytes have different meanings
310 // depending on their values. In the first interpretation, the first
311 // byte is an escape flag and the second byte indicates what special
312 // task to perform.
313 const uint8_t flag = fStreamBuffer.get()[fCurrRLEByte++];
314 const uint8_t task = fStreamBuffer.get()[fCurrRLEByte++];
315
316 // Perform decoding
317 if (RLE_ESCAPE == flag) {
318 switch (task) {
319 case RLE_EOL:
320 x = 0;
321 y++;
322 break;
323 case RLE_EOF:
324 return kSuccess;
325 case RLE_DELTA: {
326 // Two bytes are needed to specify delta
327 if ((int) fRLEBytes - fCurrRLEByte < 2) {
328 SkCodecPrintf("Warning: incomplete RLE input\n");
329 return kIncompleteInput;
330 }
331 // Modify x and y
332 const uint8_t dx = fStreamBuffer.get()[fCurrRLEByte++];
333 const uint8_t dy = fStreamBuffer.get()[fCurrRLEByte++];
334 x += dx;
335 y += dy;
336 if (x > width || y > height) {
337 SkCodecPrintf("Warning: invalid RLE input 1.\n");
338 return kIncompleteInput;
339 }
340 break;
341 }
342 default: {
343 // If task does not match any of the above signals, it
344 // indicates that we have a sequence of non-RLE pixels.
345 // Furthermore, the value of task is equal to the number
346 // of pixels to interpret.
347 uint8_t numPixels = task;
348 const size_t rowBytes = compute_row_bytes(numPixels,
349 fBitsPerPixel);
350 // Abort if setting numPixels moves us off the edge of the
351 // image. Also abort if there are not enough bytes
352 // remaining in the stream to set numPixels.
353 if (x + numPixels > width ||
354 (int) fRLEBytes - fCurrRLEByte < SkAlign2(rowBytes)) {
355 SkCodecPrintf("Warning: invalid RLE input 2.\n");
356 return kIncompleteInput;
357 }
358 // Set numPixels number of pixels
359 while (numPixels > 0) {
360 switch(fBitsPerPixel) {
361 case 4: {
362 SkASSERT(fCurrRLEByte < fRLEBytes);
363 uint8_t val = fStreamBuffer.get()[fCurrRLEByte++ ];
364 setPixel(dst, dstRowBytes, dstInfo, x++,
365 y, val >> 4);
366 numPixels--;
367 if (numPixels != 0) {
368 setPixel(dst, dstRowBytes, dstInfo,
369 x++, y, val & 0xF);
370 numPixels--;
371 }
372 break;
373 }
374 case 8:
375 SkASSERT(fCurrRLEByte < fRLEBytes);
376 setPixel(dst, dstRowBytes, dstInfo, x++,
377 y, fStreamBuffer.get()[fCurrRLEByte++]);
378 numPixels--;
379 break;
380 case 24: {
381 SkASSERT(fCurrRLEByte + 2 < fRLEBytes);
382 uint8_t blue = fStreamBuffer.get()[fCurrRLEByte+ +];
383 uint8_t green = fStreamBuffer.get()[fCurrRLEByte ++];
384 uint8_t red = fStreamBuffer.get()[fCurrRLEByte++ ];
385 setRGBPixel(dst, dstRowBytes, dstInfo,
386 x++, y, red, green, blue);
387 numPixels--;
388 }
389 default:
390 SkASSERT(false);
391 return kInvalidInput;
392 }
393 }
394 // Skip a byte if necessary to maintain alignment
395 if (!SkIsAlign2(rowBytes)) {
396 fCurrRLEByte++;
397 }
398 break;
399 }
400 }
401 } else {
402 // If the first byte read is not a flag, it indicates the number of
403 // pixels to set in RLE mode.
404 const uint8_t numPixels = flag;
405 const int endX = SkTMin<int>(x + numPixels, width);
406
407 if (24 == fBitsPerPixel) {
408 // In RLE24, the second byte read is part of the pixel color.
409 // There are two more required bytes to finish encoding the
410 // color.
411 if ((int) fRLEBytes - fCurrRLEByte < 2) {
412 SkCodecPrintf("Warning: incomplete RLE input\n");
413 return kIncompleteInput;
414 }
415
416 // Fill the pixels up to endX with the specified color
417 uint8_t blue = task;
418 uint8_t green = fStreamBuffer.get()[fCurrRLEByte++];
419 uint8_t red = fStreamBuffer.get()[fCurrRLEByte++];
420 while (x < endX) {
421 setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red,
422 green, blue);
423 }
424 } else {
425 // In RLE8 or RLE4, the second byte read gives the index in the
426 // color table to look up the pixel color.
427 // RLE8 has one color index that gets repeated
428 // RLE4 has two color indexes in the upper and lower 4 bits of
429 // the bytes, which are alternated
430 uint8_t indices[2] = { task, task };
431 if (4 == fBitsPerPixel) {
432 indices[0] >>= 4;
433 indices[1] &= 0xf;
434 }
435
436 // Set the indicated number of pixels
437 for (int which = 0; x < endX; x++) {
438 setPixel(dst, dstRowBytes, dstInfo, x, y,
439 indices[which]);
440 which = !which;
441 }
442 }
443 }
444 }
445 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698