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

Side by Side Diff: src/codec/SkCodec_libgif.cpp

Issue 1305123002: Scanline decoding for gifs (Closed) Base URL: https://skia.googlesource.com/skia.git@real-bmp-scan
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
1 /* 1 /*
2 * Copyright 2015 Google Inc. 2 * Copyright 2015 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkCodec_libgif.h" 8 #include "SkCodec_libgif.h"
9 #include "SkCodecPriv.h" 9 #include "SkCodecPriv.h"
10 #include "SkColorPriv.h" 10 #include "SkColorPriv.h"
11 #include "SkColorTable.h" 11 #include "SkColorTable.h"
12 #include "SkGifInterlaceIter.h" 12 #include "SkGifInterlaceIter.h"
13 #include "SkScaledCodec.h"
13 #include "SkStream.h" 14 #include "SkStream.h"
14 #include "SkSwizzler.h" 15 #include "SkSwizzler.h"
15 #include "SkUtils.h" 16 #include "SkUtils.h"
16 17
17 /* 18 /*
18 * Checks the start of the stream to see if the image is a gif 19 * Checks the start of the stream to see if the image is a gif
19 */ 20 */
20 bool SkGifCodec::IsGif(SkStream* stream) { 21 bool SkGifCodec::IsGif(SkStream* stream) {
21 char buf[GIF_STAMP_LEN]; 22 char buf[GIF_STAMP_LEN];
22 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { 23 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
55 return (int32_t) stream->read(out, size); 56 return (int32_t) stream->read(out, size);
56 } 57 }
57 58
58 /* 59 /*
59 * Open the gif file 60 * Open the gif file
60 */ 61 */
61 static GifFileType* open_gif(SkStream* stream) { 62 static GifFileType* open_gif(SkStream* stream) {
62 return DGifOpen(stream, read_bytes_callback, NULL); 63 return DGifOpen(stream, read_bytes_callback, NULL);
63 } 64 }
64 65
65 /*
66 * This function cleans up the gif object after the decode completes
67 * It is used in a SkAutoTCallIProc template
68 */
69 void SkGifCodec::CloseGif(GifFileType* gif) {
70 #if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
71 DGifCloseFile(gif);
72 #else
73 DGifCloseFile(gif, NULL);
74 #endif
75 }
76
77 /*
78 * This function free extension data that has been saved to assist the image
79 * decoder
80 */
81 void SkGifCodec::FreeExtension(SavedImage* image) {
82 if (NULL != image->ExtensionBlocks) {
83 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks);
84 }
85 }
86
87 /* 66 /*
88 * Check if a there is an index of the color table for a transparent pixel 67 * Check if a there is an index of the color table for a transparent pixel
89 */ 68 */
90 static uint32_t find_trans_index(const SavedImage& image) { 69 static uint32_t find_trans_index(const SavedImage& image) {
91 // If there is a transparent index specified, it will be contained in an 70 // If there is a transparent index specified, it will be contained in an
92 // extension block. We will loop through extension blocks in reverse order 71 // extension block. We will loop through extension blocks in reverse order
93 // to check the most recent extension blocks first. 72 // to check the most recent extension blocks first.
94 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) { 73 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) {
95 // Get an extension block 74 // Get an extension block
96 const ExtensionBlock& extBlock = image.ExtensionBlocks[i]; 75 const ExtensionBlock& extBlock = image.ExtensionBlocks[i];
97 76
98 // Specifically, we need to check for a graphics control extension, 77 // Specifically, we need to check for a graphics control extension,
99 // which may contain transparency information. Also, note that a valid 78 // which may contain transparency information. Also, note that a valid
100 // graphics control extension is always four bytes. The fourth byte 79 // graphics control extension is always four bytes. The fourth byte
101 // is the transparent index (if it exists), so we need at least four 80 // is the transparent index (if it exists), so we need at least four
102 // bytes. 81 // bytes.
103 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && 82 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >= 4) {
104 extBlock.ByteCount >= 4) {
105
106 // Check the transparent color flag which indicates whether a 83 // Check the transparent color flag which indicates whether a
107 // transparent index exists. It is the least significant bit of 84 // transparent index exists. It is the least significant bit of
108 // the first byte of the extension block. 85 // the first byte of the extension block.
109 if (1 == (extBlock.Bytes[0] & 1)) { 86 if (1 == (extBlock.Bytes[0] & 1)) {
110
111 // Use uint32_t to prevent sign extending 87 // Use uint32_t to prevent sign extending
112 return extBlock.Bytes[3]; 88 return extBlock.Bytes[3];
113 } 89 }
114 90
115 // There should only be one graphics control extension for the image frame 91 // There should only be one graphics control extension for the image frame
116 break; 92 break;
117 } 93 }
118 } 94 }
119 95
120 // Use maximum unsigned int (surely an invalid index) to indicate that a val id 96 // Use maximum unsigned int (surely an invalid index) to indicate that a val id
121 // index was not found. 97 // index was not found.
122 return SK_MaxU32; 98 return SK_MaxU32;
123 } 99 }
124 100
101 /*
msarett 2015/08/24 23:20:13 These have simply been rearranged.
102 * This function cleans up the gif object after the decode completes
103 * It is used in a SkAutoTCallIProc template
104 */
105 void SkGifCodec::CloseGif(GifFileType* gif) {
106 #if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
107 DGifCloseFile(gif);
108 #else
109 DGifCloseFile(gif, NULL);
110 #endif
111 }
112
113 /*
114 * This function free extension data that has been saved to assist the image
115 * decoder
116 */
117 void SkGifCodec::FreeExtension(SavedImage* image) {
118 if (NULL != image->ExtensionBlocks) {
119 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks);
120 }
121 }
122
125 /* 123 /*
126 * Read enough of the stream to initialize the SkGifCodec. 124 * Read enough of the stream to initialize the SkGifCodec.
127 * Returns a bool representing success or failure. 125 * Returns a bool representing success or failure.
128 * 126 *
129 * @param codecOut 127 * @param codecOut
130 * If it returned true, and codecOut was not NULL, 128 * If it returned true, and codecOut was not NULL,
131 * codecOut will be set to a new SkGifCodec. 129 * codecOut will be set to a new SkGifCodec.
132 * 130 *
133 * @param gifOut 131 * @param gifOut
134 * If it returned true, and codecOut was NULL, 132 * If it returned true, and codecOut was NULL,
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 if (ReadHeader(stream, &codec, NULL)) { 191 if (ReadHeader(stream, &codec, NULL)) {
194 return codec; 192 return codec;
195 } 193 }
196 return NULL; 194 return NULL;
197 } 195 }
198 196
199 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, 197 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream,
200 GifFileType* gif) 198 GifFileType* gif)
201 : INHERITED(srcInfo, stream) 199 : INHERITED(srcInfo, stream)
202 , fGif(gif) 200 , fGif(gif)
201 , fSrcBuffer(SkNEW_ARRAY(uint8_t, this->getInfo().width()))
msarett 2015/08/24 23:20:12 This is a behavior change. We will allocate extra
scroggo 2015/08/27 16:35:49 Is this used in onGetPixels, or just in scanline d
msarett 2015/09/01 17:50:15 This is shared by both.
202 , fFillIndex(SK_MaxU32)
203 , fFrameDims(SkIRect::MakeEmpty())
204 , fFrameIsSubset(false)
205 , fColorTable(NULL)
206 , fSwizzler(NULL)
203 {} 207 {}
204 208
205 bool SkGifCodec::onRewind() { 209 bool SkGifCodec::onRewind() {
206 GifFileType* gifOut = NULL; 210 GifFileType* gifOut = NULL;
207 if (!ReadHeader(this->stream(), NULL, &gifOut)) { 211 if (!ReadHeader(this->stream(), NULL, &gifOut)) {
208 return false; 212 return false;
209 } 213 }
210 214
211 SkASSERT(NULL != gifOut); 215 SkASSERT(NULL != gifOut);
212 fGif.reset(gifOut); 216 fGif.reset(gifOut);
213 return true; 217 return true;
214 } 218 }
215 219
216 /* 220 SkCodec::Result SkGifCodec::readUpToFirstImage(uint32_t* transIndex) {
msarett 2015/08/24 23:20:13 Code moved from original onGetPixels(). We now ca
217 * Initiates the gif decode
218 */
219 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
220 void* dst, size_t dstRowBytes,
221 const Options& opts,
222 SkPMColor* inputColorPtr,
223 int* inputColorCount) {
224 // Rewind if necessary
225 if (!this->rewindIfNeeded()) {
226 return kCouldNotRewind;
227 }
228
229 // Check for valid input parameters
230 if (opts.fSubset) {
231 // Subsets are not supported.
232 return kUnimplemented;
233 }
234 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
235 return gif_error("Scaling not supported.\n", kInvalidScale);
236 }
237 if (!conversion_possible(dstInfo, this->getInfo())) {
238 return gif_error("Cannot convert input type to output type.\n",
239 kInvalidConversion);
240 }
241
242 // Use this as a container to hold information about any gif extension 221 // Use this as a container to hold information about any gif extension
243 // blocks. This generally stores transparency and animation instructions. 222 // blocks. This generally stores transparency and animation instructions.
244 SavedImage saveExt; 223 SavedImage saveExt;
245 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); 224 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt);
246 saveExt.ExtensionBlocks = NULL; 225 saveExt.ExtensionBlocks = NULL;
247 saveExt.ExtensionBlockCount = 0; 226 saveExt.ExtensionBlockCount = 0;
248 GifByteType* extData; 227 GifByteType* extData;
249 int32_t extFunction; 228 int32_t extFunction;
250 229
251 // We will loop over components of gif images until we find an image. Once 230 // We will loop over components of gif images until we find an image. Once
252 // we find an image, we will decode and return it. While many gif files 231 // we find an image, we will decode and return it. While many gif files
253 // contain more than one image, we will simply decode the first image. 232 // contain more than one image, we will simply decode the first image.
254 const int32_t width = dstInfo.width();
255 const int32_t height = dstInfo.height();
256 GifRecordType recordType; 233 GifRecordType recordType;
257 do { 234 do {
258 // Get the current record type 235 // Get the current record type
259 if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) { 236 if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) {
260 return gif_error("DGifGetRecordType failed.\n", kInvalidInput); 237 return gif_error("DGifGetRecordType failed.\n", kInvalidInput);
261 } 238 }
262
263 switch (recordType) { 239 switch (recordType) {
264 case IMAGE_DESC_RECORD_TYPE: { 240 case IMAGE_DESC_RECORD_TYPE: {
265 // Read the image descriptor 241 *transIndex = find_trans_index(saveExt);
266 if (GIF_ERROR == DGifGetImageDesc(fGif)) {
267 return gif_error("DGifGetImageDesc failed.\n",
268 kInvalidInput);
269 }
270
271 // If reading the image descriptor is successful, the image
272 // count will be incremented
273 SkASSERT(fGif->ImageCount >= 1);
274 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1];
275
276 // Process the descriptor
277 const GifImageDesc& desc = image->ImageDesc;
278 int32_t imageLeft = desc.Left;
279 int32_t imageTop = desc.Top;
280 int32_t innerWidth = desc.Width;
281 int32_t innerHeight = desc.Height;
282 // Fail on non-positive dimensions
283 if (innerWidth <= 0 || innerHeight <= 0) {
284 return gif_error("Invalid dimensions for inner image.\n",
285 kInvalidInput);
286 }
287 // Treat the following cases as warnings and try to fix
288 if (innerWidth > width) {
289 gif_warning("Inner image too wide, shrinking.\n");
290 innerWidth = width;
291 imageLeft = 0;
292 } else if (imageLeft + innerWidth > width) {
293 gif_warning("Shifting inner image to left to fit.\n");
294 imageLeft = width - innerWidth;
295 } else if (imageLeft < 0) {
296 gif_warning("Shifting image to right to fit\n");
297 imageLeft = 0;
298 }
299 if (innerHeight > height) {
300 gif_warning("Inner image too tall, shrinking.\n");
301 innerHeight = height;
302 imageTop = 0;
303 } else if (imageTop + innerHeight > height) {
304 gif_warning("Shifting inner image up to fit.\n");
305 imageTop = height - innerHeight;
306 } else if (imageTop < 0) {
307 gif_warning("Shifting image down to fit\n");
308 imageTop = 0;
309 }
310
311 // Create a color table to store colors the giflib colorMap
312 SkPMColor alternateColorPtr[256];
313 SkPMColor* colorTable;
314 SkColorType dstColorType = dstInfo.colorType();
315 if (kIndex_8_SkColorType == dstColorType) {
316 SkASSERT(NULL != inputColorPtr);
317 SkASSERT(NULL != inputColorCount);
318 colorTable = inputColorPtr;
319 } else {
320 colorTable = alternateColorPtr;
321 }
322
323 // Set up the color table
324 uint32_t colorCount = 0;
325 // Allocate maximum storage to deal with invalid indices safely
326 const uint32_t maxColors = 256;
327 ColorMapObject* colorMap = fGif->Image.ColorMap;
328 // If there is no local color table, use the global color table
329 if (NULL == colorMap) {
330 colorMap = fGif->SColorMap;
331 }
332 if (NULL != colorMap) {
333 colorCount = colorMap->ColorCount;
334 SkASSERT(colorCount ==
335 (unsigned) (1 << (colorMap->BitsPerPixel)));
336 SkASSERT(colorCount <= 256);
337 for (uint32_t i = 0; i < colorCount; i++) {
338 colorTable[i] = SkPackARGB32(0xFF,
339 colorMap->Colors[i].Red,
340 colorMap->Colors[i].Green,
341 colorMap->Colors[i].Blue);
342 }
343 }
344
345 // This is used to fill unspecified pixels in the image data.
346 uint32_t fillIndex = fGif->SBackGroundColor;
347 ZeroInitialized zeroInit = opts.fZeroInitialized;
348
349 // Gifs have the option to specify the color at a single
350 // index of the color table as transparent.
351 {
352 // Get the transparent index. If the return value of this
353 // function is greater than the colorCount, we know that
354 // there is no valid transparent color in the color table.
355 // This occurs if there is no graphics control extension or
356 // if the index specified by the graphics control extension
357 // is out of range.
358 uint32_t transIndex = find_trans_index(saveExt);
359
360 if (transIndex < colorCount) {
361 colorTable[transIndex] = SK_ColorTRANSPARENT;
362 // If there is a transparent index, we also use this as
363 // the fill index.
364 fillIndex = transIndex;
365 } else if (fillIndex >= colorCount) {
366 // If the fill index is invalid, we default to 0. This
367 // behavior is unspecified but matches SkImageDecoder.
368 fillIndex = 0;
369 }
370 }
371
372 // Fill in the color table for indices greater than color count.
373 // This allows for predictable, safe behavior.
374 for (uint32_t i = colorCount; i < maxColors; i++) {
375 colorTable[i] = colorTable[fillIndex];
376 }
377
378 // Check if image is only a subset of the image frame
379 SkAutoTDelete<SkSwizzler> swizzler(NULL);
380 if (innerWidth < width || innerHeight < height) {
381
382 // Modify the destination info
383 const SkImageInfo subsetDstInfo =
384 dstInfo.makeWH(innerWidth, innerHeight);
385
386 // Fill the destination with the fill color
387 // FIXME: This may not be the behavior that we want for
388 // animated gifs where we draw on top of the
389 // previous frame.
390 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height,
391 fillIndex, colorTable, zeroInit);
392
393 // Modify the dst pointer
394 const int32_t dstBytesPerPixel =
395 SkColorTypeBytesPerPixel(dstColorType);
396 dst = SkTAddOffset<void*>(dst,
397 dstRowBytes * imageTop +
398 dstBytesPerPixel * imageLeft);
399
400 // Create the subset swizzler
401 swizzler.reset(SkSwizzler::CreateSwizzler(
402 SkSwizzler::kIndex, colorTable, subsetDstInfo,
403 zeroInit, this->getInfo()));
404 } else {
405 // Create the fully dimensional swizzler
406 swizzler.reset(SkSwizzler::CreateSwizzler(
407 SkSwizzler::kIndex, colorTable, dstInfo,
408 zeroInit, this->getInfo()));
409 }
410
411 // Stores output from dgiflib and input to the swizzler
412 SkAutoTDeleteArray<uint8_t>
413 buffer(SkNEW_ARRAY(uint8_t, innerWidth));
414
415 // Check the interlace flag and iterate over rows of the input
416 if (fGif->Image.Interlace) {
417 // In interlace mode, the rows of input are rearranged in
418 // the output image. We use an iterator to take care of
419 // the rearranging.
420 SkGifInterlaceIter iter(innerHeight);
421 for (int32_t y = 0; y < innerHeight; y++) {
422 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(),
423 innerWidth)) {
424 // Recover from error by filling remainder of image
425 memset(buffer.get(), fillIndex, innerWidth);
426 for (; y < innerHeight; y++) {
427 void* dstRow = SkTAddOffset<void>(dst,
428 dstRowBytes * iter.nextY());
429 swizzler->swizzle(dstRow, buffer.get());
430 }
431 return gif_error(SkStringPrintf(
432 "Could not decode line %d of %d.\n",
433 y, height - 1).c_str(), kIncompleteInput);
434 }
435 void* dstRow = SkTAddOffset<void>(
436 dst, dstRowBytes * iter.nextY());
437 swizzler->swizzle(dstRow, buffer.get());
438 }
439 } else {
440 // Standard mode
441 void* dstRow = dst;
442 for (int32_t y = 0; y < innerHeight; y++) {
443 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(),
444 innerWidth)) {
445 SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes,
446 innerHeight - y, fillIndex, colorTable,
447 zeroInit);
448 return gif_error(SkStringPrintf(
449 "Could not decode line %d of %d.\n",
450 y, height - 1).c_str(), kIncompleteInput);
451 }
452 swizzler->swizzle(dstRow, buffer.get());
453 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
454 }
455 }
456
457 // FIXME: Gif files may have multiple images stored in a single 242 // FIXME: Gif files may have multiple images stored in a single
458 // file. This is most commonly used to enable 243 // file. This is most commonly used to enable
459 // animations. Since we are leaving animated gifs as a 244 // animations. Since we are leaving animated gifs as a
460 // TODO, we will return kSuccess after decoding the 245 // TODO, we will return kSuccess after decoding the
461 // first image in the file. This is the same behavior 246 // first image in the file. This is the same behavior
462 // as SkImageDecoder_libgif. 247 // as SkImageDecoder_libgif.
463 // 248 //
464 // Most times this works pretty well, but sometimes it 249 // Most times this works pretty well, but sometimes it
465 // doesn't. For example, I have an animated test image 250 // doesn't. For example, I have an animated test image
466 // where the first image in the file is 1x1, but the 251 // where the first image in the file is 1x1, but the
467 // subsequent images are meaningful. This currently 252 // subsequent images are meaningful. This currently
468 // displays the 1x1 image, which is not ideal. Right 253 // displays the 1x1 image, which is not ideal. Right
469 // now I am leaving this as an issue that will be 254 // now I am leaving this as an issue that will be
470 // addressed when we implement animated gifs. 255 // addressed when we implement animated gifs.
471 // 256 //
472 // It is also possible (not explicitly disallowed in the 257 // It is also possible (not explicitly disallowed in the
473 // specification) that gif files provide multiple 258 // specification) that gif files provide multiple
474 // images in a single file that are all meant to be 259 // images in a single file that are all meant to be
475 // displayed in the same frame together. I will 260 // displayed in the same frame together. I will
476 // currently leave this unimplemented until I find a 261 // currently leave this unimplemented until I find a
477 // test case that expects this behavior. 262 // test case that expects this behavior.
478 return kSuccess; 263 return kSuccess;
479 } 264 }
480
481 // Extensions are used to specify special properties of the image 265 // Extensions are used to specify special properties of the image
482 // such as transparency or animation. 266 // such as transparency or animation.
483 case EXTENSION_RECORD_TYPE: 267 case EXTENSION_RECORD_TYPE:
484 // Read extension data 268 // Read extension data
485 if (GIF_ERROR == 269 if (GIF_ERROR ==
486 DGifGetExtension(fGif, &extFunction, &extData)) { 270 DGifGetExtension(fGif, &extFunction, &extData)) {
487 return gif_error("Could not get extension.\n", 271 return gif_error("Could not get extension.\n",
488 kIncompleteInput); 272 kIncompleteInput);
489 } 273 }
490 274
491 // Create an extension block with our data 275 // Create an extension block with our data
492 while (NULL != extData) { 276 while (NULL != extData) {
493 // Add a single block 277 // Add a single block
494 if (GIF_ERROR == 278 if (GIF_ERROR ==
495 GifAddExtensionBlock(&saveExt.ExtensionBlockCount, 279 GifAddExtensionBlock(&saveExt.ExtensionBlockCount,
496 &saveExt.ExtensionBlocks, extFunction, extData[0], 280 &saveExt.ExtensionBlocks, extFunction, extData[0],
497 &extData[1])) { 281 &extData[1])) {
498 return gif_error("Could not add extension block.\n", 282 return gif_error("Could not add extension block.\n",
499 kIncompleteInput); 283 kIncompleteInput);
500 } 284 }
501 // Move to the next block 285 // Move to the next block
502 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { 286 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) {
503 return gif_error("Could not get next extension.\n", 287 return gif_error("Could not get next extension.\n", kInc ompleteInput);
504 kIncompleteInput);
505 } 288 }
506 } 289 }
507 break; 290 break;
508 291
509 // Signals the end of the gif file 292 // Signals the end of the gif file
510 case TERMINATE_RECORD_TYPE: 293 case TERMINATE_RECORD_TYPE:
511 break; 294 break;
512 295
513 default: 296 default:
514 // giflib returns an error code if the record type is not known. 297 // giflib returns an error code if the record type is not known.
515 // We should catch this error immediately. 298 // This should not be reached.
scroggo 2015/08/27 16:35:49 So DGifGetRecordType would have returned GIF_ERROR
msarett 2015/09/01 17:50:15 Yes I'll improve the comment.
516 SkASSERT(false); 299 SkASSERT(false);
517 break; 300 break;
518 } 301 }
519 } while (TERMINATE_RECORD_TYPE != recordType); 302 } while (TERMINATE_RECORD_TYPE != recordType);
520 303
521 return gif_error("Could not find any images to decode in gif file.\n", 304 return gif_error("Could not find any images to decode in gif file.\n", kInva lidInput);
522 kInvalidInput); 305 }
523 } 306
307 /*
308 * A gif may contain many image frames, all of different sizes.
309 * This function checks if the frame dimensions are valid and corrects them if
310 * necessary.
311 */
312 bool SkGifCodec::setFrameDimensions(const GifImageDesc& desc) {
msarett 2015/08/24 23:20:13 Code moved from the original onGetPixels(). fFram
313 // Fail on non-positive dimensions
314 int32_t frameLeft = desc.Left;
315 int32_t frameTop = desc.Top;
316 int32_t frameWidth = desc.Width;
317 int32_t frameHeight = desc.Height;
318 int32_t height = this->getInfo().height();
319 int32_t width = this->getInfo().width();
320 if (frameWidth <= 0 || frameHeight <= 0) {
321 return false;
322 }
323
324 // Treat the following cases as warnings and try to fix
325 if (frameWidth > width) {
326 gif_warning("Image frame too wide, shrinking.\n");
327 frameWidth = width;
328 frameLeft = 0;
329 } else if (frameLeft + frameWidth > width) {
330 gif_warning("Shifting image frame to left to fit.\n");
331 frameLeft = width - frameWidth;
332 } else if (frameLeft < 0) {
333 gif_warning("Shifting image frame to right to fit\n");
334 frameLeft = 0;
335 }
336 if (frameHeight > height) {
337 gif_warning("Image frame too tall, shrinking.\n");
338 frameHeight = height;
339 frameTop = 0;
340 } else if (frameTop + frameHeight > height) {
341 gif_warning("Shifting image frame up to fit.\n");
342 frameTop = height - frameHeight;
343 } else if (frameTop < 0) {
344 gif_warning("Shifting image frame down to fit\n");
345 frameTop = 0;
346 }
347 fFrameDims.setXYWH(frameLeft, frameTop, frameWidth, frameHeight);
348
349 // Indicate if the frame dimensions do not match the header dimensions
350 if (this->getInfo().width() != fFrameDims.width() ||
351 this->getInfo().height() != fFrameDims.height()) {
352 fFrameIsSubset = true;
353 }
354
355 return true;
356 }
357
358 void SkGifCodec::initializeColorTable(int* inputColorCount, uint32_t transIndex) {
msarett 2015/08/24 23:20:13 Code moved from the original onGetPixels().
359 // Set up our own color table
360 const uint32_t maxColors = 256;
361 SkPMColor colorPtr[256];
362 if (NULL != inputColorCount) {
363 // We set the number of colors to maxColors in order to ensure
364 // safe memory accesses. Otherwise, an invalid pixel could
365 // access memory outside of our color table array.
366 *inputColorCount = maxColors;
367 }
368
369 // Get local color table
370 ColorMapObject* colorMap = fGif->Image.ColorMap;
371 // If there is no local color table, use the global color table
372 if (NULL == colorMap) {
373 colorMap = fGif->SColorMap;
374 }
375
376 uint32_t colorCount = 0;
377 if (NULL != colorMap) {
378 colorCount = colorMap->ColorCount;
379 SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPixel)));
380 SkASSERT(colorCount <= 256);
msarett 2015/08/24 23:20:13 Not sure if we should add a Release check here. g
scroggo 2015/08/27 16:35:49 I think we should trust giflib in this case. I can
381 for (uint32_t i = 0; i < colorCount; i++) {
382 colorPtr[i] = SkPackARGB32(0xFF, colorMap->Colors[i].Red,
383 colorMap->Colors[i].Green, colorMap->Colors[i].Blue);
384 }
385 }
386
387 // This is used to fill unspecified pixels in the image data.
388 uint32_t fillIndex = fGif->SBackGroundColor;
389
390 // Gifs have the option to specify the color at a single index of the color
391 // table as transparent. If the transparent index is greater than the
392 // colorCount, we know that there is no valid transparent color in the
393 // color table. This occurs if there is no graphics control extension or
394 // if the index specified by the graphics control extension is out of range.
395 if (transIndex < colorCount) {
396 colorPtr[transIndex] = SK_ColorTRANSPARENT;
397 // If there is a transparent index, we also use this as the fill index.
398 fillIndex = transIndex;
399 } else if (fillIndex >= colorCount) {
400 // If the fill index is invalid, we default to 0. This behavior is
401 // unspecified but matches SkImageDecoder.
402 fillIndex = 0;
403 }
404
405 // Fill in the color table for indices greater than color count.
406 // This allows for predictable, safe behavior.
407 for (uint32_t i = colorCount; i < maxColors; i++) {
408 colorPtr[i] = colorPtr[fillIndex];
409 }
410
411 fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorPtr, maxColors)));
412 fFillIndex = fillIndex;
413 }
414
415 SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo,
416 ZeroInitialized zeroInit) {
417 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
418 fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex,
419 colorPtr, dstInfo, zeroInit, this->getInfo()));
420 if (NULL != fSwizzler.get()) {
421 return kSuccess;
422 }
423 return kUnimplemented;
424 }
425
426 SkCodec::Result SkGifCodec::readRow() {
427 if (GIF_ERROR == DGifGetLine(fGif, fSrcBuffer.get(), fFrameDims.width())) {
428 return kIncompleteInput;
429 }
430 return kSuccess;
431 }
432
433 /*
434 * Initiates the gif decode
435 */
436 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
437 void* dst, size_t dstRowBytes,
438 const Options& opts,
439 SkPMColor* inputColorPtr,
440 int* inputColorCount) {
441 // Rewind if necessary
442 if (!this->rewindIfNeeded()) {
443 return kCouldNotRewind;
444 }
445
446 // Check for valid input parameters
447 if (opts.fSubset) {
448 // Subsets are not supported.
449 return kUnimplemented;
450 }
451 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
452 return gif_error("Scaling not supported.\n", kInvalidScale);
453 }
454 if (!conversion_possible(dstInfo, this->getInfo())) {
455 return gif_error("Cannot convert input type to output type.\n",
456 kInvalidConversion);
457 }
458
459 // Read through gif extensions to get to the image data. Set the
460 // transparent index based on the extension data.
461 uint32_t transIndex;
462 SkCodec::Result result = this->readUpToFirstImage(&transIndex);
463 if (kSuccess != result){
464 return result;
465 }
466
467 // Read the image descriptor
468 if (GIF_ERROR == DGifGetImageDesc(fGif)) {
469 return gif_error("DGifGetImageDesc failed.\n", kInvalidInput);
470 }
471
472 // If reading the image descriptor is successful, the image count will be
473 // incremented
474 SkASSERT(fGif->ImageCount >= 1);
msarett 2015/08/24 23:20:13 Looks like we also rely on giflib behaving properl
475 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1];
476 const GifImageDesc& desc = image->ImageDesc;
477
478 // Check that the frame dimensions are valid and set them
479 if(!this->setFrameDimensions(desc)) {
480 return gif_error("Invalid dimensions for image frame.\n", kInvalidInput) ;
481 }
482
483 // Initialize color table and copy to the client if necessary
484 this->initializeColorTable(inputColorCount, transIndex);
485 copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount) ;
486
487 // Initialize the swizzler
488 if (fFrameIsSubset) {
489 const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameDims.width(), fFr ameDims.height());
490 if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitia lized)) {
491 return gif_error("Could not initialize swizzler.\n", kUnimplemented) ;
492 }
493
494 // Fill the background
495 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
496 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, this->getInfo().height(),
497 fFillIndex, colorPtr, opts.fZeroInitialized);
498
499 // Modify the dst pointer
500 const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorT ype());
501 dst = SkTAddOffset<void*>(dst, dstRowBytes * fFrameDims.top() +
502 dstBytesPerPixel * fFrameDims.left());
503 } else {
504 if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized) ) {
505 return gif_error("Could not initialize swizzler.\n", kUnimplemented) ;
506 }
507 }
508
509 // Check the interlace flag and iterate over rows of the input
510 if (fGif->Image.Interlace) {
511 // In interlace mode, the rows of input are rearranged in
512 // the output image. We use an iterator to take care of
513 // the rearranging.
514 SkGifInterlaceIter iter(fFrameDims.height());
515 for (int32_t y = 0; y < fFrameDims.height(); y++) {
516 if (kSuccess != this->readRow()) {
517 // Recover from error by filling remainder of image
518 memset(fSrcBuffer.get(), fFillIndex, fFrameDims.width());
519 for (; y < fFrameDims.height(); y++) {
520 void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * iter.ne xtY());
521 fSwizzler->swizzle(dstRow, fSrcBuffer.get());
522 }
523 return gif_error("Could not decode line.\n", kIncompleteInput);
524 }
525 void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * iter.nextY());
526 fSwizzler->swizzle(dstRow, fSrcBuffer.get());
527 }
528 } else {
529 // Standard mode
530 void* dstRow = dst;
531 for (int32_t y = 0; y < fFrameDims.height(); y++) {
532 if (kSuccess != this->readRow()) {
533 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
534 SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes,
535 fFrameDims.height() - y, fFillIndex, colorPtr,
536 opts.fZeroInitialized);
537 return gif_error("Could not decode line\n", kIncompleteInput);
538 }
539 fSwizzler->swizzle(dstRow, fSrcBuffer.get());
540 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
541 }
542 }
543 return kSuccess;
544 }
545
546 class SkGifScanlineDecoder : public SkScanlineDecoder {
547 public:
548 SkGifScanlineDecoder(const SkImageInfo& srcInfo, SkGifCodec* codec)
549 : INHERITED(srcInfo)
550 , fCodec(codec)
551 , fIter(-1)
552 {}
553
554 SkEncodedFormat onGetEncodedFormat() const override {
555 return kGIF_SkEncodedFormat;
556 }
557
558 SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& opts,
559 SkPMColor inputColorPtr[], int* inputColorCount) ove rride {
560
561 // Rewind if necessary
562 if (!fCodec->rewindIfNeeded()) {
563 return SkCodec::kCouldNotRewind;
564 }
565 // Check for valid input parameters
566 if (opts.fSubset) {
567 // Subsets are not supported.
568 return SkCodec::kUnimplemented;
569 }
570 // Check to see if scaling was requested.
571 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
572 if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
573 return gif_error("Scaling not supported.\n", SkCodec::kInvalidSc ale);
574 }
575 }
576 if (!conversion_possible(dstInfo, this->getInfo())) {
577 return gif_error("Cannot convert input type to output type.\n",
578 SkCodec::kInvalidConversion);
579 }
580
581 // Read through gif extensions to get to the image data
582 uint32_t transIndex;
583 SkCodec::Result result = fCodec->readUpToFirstImage(&transIndex);
584 if (SkCodec::kSuccess != result){
585 return result;
586 }
587
588 // Read the image descriptor
589 if (GIF_ERROR == DGifGetImageDesc(fCodec->fGif)) {
590 return gif_error("DGifGetImageDesc failed.\n", SkCodec::kInvalidInpu t);
591 }
592
593 // If reading the image descriptor is successful, the image count will b e
594 // incremented
595 SkASSERT(fCodec->fGif->ImageCount >= 1);
596 SavedImage* image = &fCodec->fGif->SavedImages[fCodec->fGif->ImageCount - 1];
597 const GifImageDesc& desc = image->ImageDesc;
598
599 // Check that the frame dimensions are valid and set them
600 if(!fCodec->setFrameDimensions(desc)) {
601 return gif_error("Invalid dimensions for image frame.\n", SkCodec::k InvalidInput);
602 }
603 SkGifInterlaceIter iter(fCodec->fFrameDims.height());
604 fIter = iter;
msarett 2015/08/24 23:20:13 This is unfortunate. We don't know fCodec->fFrame
scroggo 2015/08/27 16:35:49 What is the problem? It seems like the constructor
msarett 2015/09/01 17:50:15 This was fixed in the rebase - we now deal with in
605
606 // Initialize color table and copy to the client if necessary
607 fCodec->initializeColorTable(inputColorCount, transIndex);
608 copy_color_table(dstInfo, fCodec->fColorTable, inputColorPtr, inputColor Count);
609
610 // Initialize the swizzler
611 if (fCodec->fFrameIsSubset) {
612 int sampleX;
613 SkScaledCodec::ComputeSampleSize(dstInfo, fCodec->getInfo(), &sample X, NULL);
614 const SkImageInfo subsetDstInfo = dstInfo.makeWH(
615 SkScaledCodec::GetScaledDimension(fCodec->fFrameDims.width() , sampleX),
616 fCodec->fFrameDims.height());
617 if (SkCodec::kSuccess != fCodec->initializeSwizzler(subsetDstInfo, o pts.fZeroInitialized)) {
scroggo 2015/08/27 16:35:49 nit: line too long
msarett 2015/09/01 17:50:15 Done.
618 return gif_error("Could not initialize swizzler.\n", SkCodec::kU nimplemented);
619 }
620 } else {
621 if (SkCodec::kSuccess != fCodec->initializeSwizzler(dstInfo, opts.fZ eroInitialized)) {
622 return gif_error("Could not initialize swizzler.\n", SkCodec::kU nimplemented);
623 }
624 }
625
626 return SkCodec::kSuccess;
627 }
628
629 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) overri de {
630 if (fCodec->fFrameIsSubset) {
631 // Fill the requested rows
632 const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.get()) ;
633 SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count, fCodec->fFil lIndex, colorPtr, this->options().fZeroInitialized);
scroggo 2015/08/27 16:35:49 nit: line too long
msarett 2015/09/01 17:50:15 Done.
634
635 // Do nothing for rows before the image frame
636 int rowsBeforeFrame = fCodec->fFrameDims.top() - INHERITED::getY();
637 if (rowsBeforeFrame > 0) {
638 count = SkTMin(0, count - rowsBeforeFrame);
639 dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame);
640 }
641
642 // Do nothing for rows after the image frame
643 int rowsAfterFrame = INHERITED::getY() + count - fCodec->fFrameDims. bottom();
644 if (rowsAfterFrame > 0) {
645 count = SkTMin(0, count - rowsAfterFrame);
646 }
647
648 // Adjust dst pointer for left offset
649 dst = SkTAddOffset<void>(dst, SkColorTypeBytesPerPixel(this->dstInfo ().colorType()) * fCodec->fFrameDims.left());
scroggo 2015/08/27 16:35:49 nit line too long
msarett 2015/09/01 17:50:15 Done.
650 }
651
652 for (int i = 0; i < count; i++) {
653 if (SkCodec::kSuccess != fCodec->readRow()) {
654 const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.ge t());
655 SkSwizzler::Fill(dst, this->dstInfo(), rowBytes,
656 fCodec->fFrameDims.height() - i, fCodec->fFillIndex, col orPtr,
657 this->options().fZeroInitialized);
658 return gif_error("Could not decode line\n", SkCodec::kIncomplete Input);
659 }
660 fCodec->fSwizzler->swizzle(dst, fCodec->fSrcBuffer.get());
661 dst = SkTAddOffset<void>(dst, rowBytes);
662 }
663 return SkCodec::kSuccess;
664 }
665
666 SkScanlineOrder onGetScanlineOrder() const override {
667 if (fCodec->fGif->Image.Interlace) {
668 return kOutOfOrder_SkScanlineOrder;
669 } else {
670 return kTopDown_SkScanlineOrder;
671 }
672 }
673
674 int onGetY() override {
675 if (fCodec->fGif->Image.Interlace) {
676 return fIter.nextY();
scroggo 2015/08/27 16:35:49 This seems a little weird - it seems like we're de
msarett 2015/09/01 17:50:15 Acknowledged. We may end up using this approach f
677 } else {
678 return INHERITED::getY();
679 }
680 }
681
682 private:
683 SkAutoTDelete<SkGifCodec> fCodec;
684 SkGifInterlaceIter fIter;
685
686 typedef SkScanlineDecoder INHERITED;
687 };
688
689 SkScanlineDecoder* SkGifCodec::NewSDFromStream(SkStream* stream) {
690 SkAutoTDelete<SkGifCodec> codec (static_cast<SkGifCodec*>(SkGifCodec::NewFro mStream(stream)));
691 if (!codec) {
692 return NULL;
693 }
694
695 const SkImageInfo& srcInfo = codec->getInfo();
696
697 return SkNEW_ARGS(SkGifScanlineDecoder, (srcInfo, codec.detach()));
698 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698