OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 } | |
OLD | NEW |