OLD | NEW |
---|---|
(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 "SkCodec_libgif.h" | |
9 #include "SkCodecPriv.h" | |
10 #include "SkColorPriv.h" | |
11 #include "SkColorTable.h" | |
12 #include "SkGifInterlaceIter.h" | |
13 #include "SkStream.h" | |
14 #include "SkSwizzler.h" | |
15 #include "SkUtils.h" | |
16 | |
17 /* | |
18 * Checks the start of the stream to see if the image is a gif | |
19 */ | |
20 bool SkGifCodec::IsGif(SkStream* stream) { | |
21 char buf[GIF_STAMP_LEN]; | |
22 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { | |
23 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || | |
24 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || | |
25 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { | |
26 return true; | |
27 } | |
28 } | |
29 return false; | |
30 } | |
31 | |
32 /* | |
33 * Warning reporting function | |
34 */ | |
35 static void gif_warning(const char* msg) { | |
36 SkCodecPrintf("Gif Warning: %s\n", msg); | |
37 } | |
38 | |
39 /* | |
40 * Error function | |
41 */ | |
42 static SkCodec::Result gif_error(const char* msg, | |
43 SkCodec::Result result = SkCodec::kInvalidInput) { | |
44 SkCodecPrintf("Gif Error: %s\n", msg); | |
45 return result; | |
46 } | |
47 | |
48 | |
49 /* | |
50 * Read function that will be passed to gif_lib | |
51 */ | |
52 static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, | |
53 int32_t size) { | |
54 SkStream* stream = (SkStream*) fileType->UserData; | |
55 return (int32_t) stream->read(out, size); | |
56 } | |
57 | |
58 /* | |
59 * Open the gif file | |
60 */ | |
61 static GifFileType* open_gif(SkStream* stream) { | |
62 #if GIFLIB_MAJOR < 5 | |
63 return DGifOpen(stream, read_bytes_callback); | |
64 #else | |
65 return DGifOpen(stream, read_bytes_callback, NULL); | |
66 #endif | |
67 } | |
68 | |
69 /* | |
70 * This function cleans up the gif object after the decode completes | |
71 * It is used in a SkAutoTCallIProc template | |
72 */ | |
73 int32_t SkGifCodec::CloseGif(GifFileType* gif) { | |
74 #if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0) | |
75 return DGifCloseFile(gif); | |
76 #else | |
77 return DGifCloseFile(gif, NULL); | |
78 #endif | |
79 } | |
80 | |
81 /* | |
82 * This function free extension data that has been saved to assist the image | |
83 * decoder | |
84 */ | |
85 void SkGifCodec::FreeExtension(SavedImage* image) { | |
86 if (NULL != image->ExtensionBlocks) { | |
87 #if GIFLIB_MAJOR < 5 | |
88 FreeExtension(image); | |
89 #else | |
90 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); | |
91 #endif | |
92 } | |
93 } | |
94 | |
95 /* | |
96 * Check if a there is an index of the color table for a transparent pixel | |
97 */ | |
98 static int32_t find_trans_index(const SavedImage& image, uint32_t colorCount) { | |
99 // If there is a transparent index specified, it will be contained in an | |
100 // extension block. | |
101 for (int32_t i = 0; i < image.ExtensionBlockCount; i++) { | |
102 // Get an extension block | |
103 const ExtensionBlock& extBlock = image.ExtensionBlocks[i]; | |
104 | |
105 // Specifically, we need to check for a graphics control extension, | |
106 // which may contain transparency information. Also, note that a valid | |
107 // graphics control extension is always four bytes. The fourth byte | |
108 // is the transparent index (if it exists), so we need at least four | |
109 // bytes. | |
110 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && | |
111 extBlock.ByteCount >= 4) { | |
112 | |
113 // Check the transparent color flag which indicates whether a | |
114 // transparent index exists. It is the least significant bit of | |
115 // the first byte of the extension block. | |
116 if (1 == (extBlock.Bytes[0] & 1)) { | |
117 | |
118 // Use uint32_t to prevent sign extending | |
119 uint32_t transIndex = extBlock.Bytes[3]; | |
120 if (transIndex < colorCount) { | |
121 return transIndex; | |
122 } | |
123 } | |
scroggo
2015/03/31 18:39:56
nit: Could there be multiple graphics control exte
msarett
2015/03/31 20:11:40
There can be multiple GCEs because there can be mu
scroggo
2015/03/31 20:39:11
I wouldn't worry about reversing this. Since you'r
| |
124 } | |
125 } | |
126 | |
127 // Flag indicating that a valid index was not found | |
128 return -1; | |
129 } | |
130 | |
131 /* | |
132 * Assumes IsGif was called and returned true | |
133 * Creates a gif decoder | |
134 * Reads enough of the stream to determine the image format | |
135 */ | |
136 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { | |
137 // Read gif header, logical screen descriptor, and global color table | |
138 SkAutoTCallIProc<GifFileType, CloseGif> gif(open_gif(stream)); | |
139 | |
140 if (NULL == gif) { | |
141 gif_error("DGifOpen failed.\n"); | |
142 return NULL; | |
143 } | |
144 | |
145 // Get fields from header | |
146 const int32_t width = gif->SWidth; | |
147 const int32_t height = gif->SHeight; | |
148 if (width <= 0 || height <= 0) { | |
149 gif_error("Invalid dimensions.\n"); | |
150 return NULL; | |
151 } | |
152 | |
153 // Return the codec | |
154 // kIndex is the most natural color type for gifs, so we set this as | |
155 // the default. | |
156 // Many gifs specify a color table index for transparent pixels. Every | |
157 // other pixel is guaranteed to be opaque. Despite this, because of the | |
158 // possiblity of transparent pixels, we cannot assume that the image is | |
159 // opaque. However, we can at least mark it as kPremul, since pixels will | |
scroggo
2015/03/31 18:39:56
Maybe a note that it could be treated as unpremul,
msarett
2015/03/31 20:11:41
Agreed
| |
160 // either have a 0xFF alpha component or be completely zeroed. | |
161 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, | |
162 kIndex_8_SkColorType, kPremul_SkAlphaType); | |
163 return SkNEW_ARGS(SkGifCodec, (imageInfo, stream, gif.detach())); | |
164 } | |
165 | |
166 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, | |
167 GifFileType* gif) | |
168 : INHERITED(srcInfo, stream) | |
169 , fGif(gif) | |
170 {} | |
171 | |
172 /* | |
173 * Checks if the conversion between the input image and the requested output | |
174 * image has been implemented | |
175 */ | |
176 static bool conversion_possible(const SkImageInfo& dst, | |
177 const SkImageInfo& src) { | |
178 // Ensure that the profile type is unchanged | |
179 if (dst.profileType() != src.profileType()) { | |
180 return false; | |
181 } | |
182 | |
183 // Check for supported color and alpha types | |
184 switch (dst.colorType()) { | |
185 case kN32_SkColorType: | |
186 return src.alphaType() == dst.alphaType() || | |
187 (kPremul_SkAlphaType == dst.alphaType() && | |
188 kUnpremul_SkAlphaType == src.alphaType()); | |
scroggo
2015/03/31 18:39:57
The source will always be premul, right? And it co
msarett
2015/03/31 20:11:40
I agree and fixed the return statement.
We discus
| |
189 default: | |
190 return false; | |
191 } | |
192 } | |
193 | |
194 /* | |
195 * Initiates the gif decode | |
196 */ | |
197 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, | |
198 void* dst, size_t dstRowBytes, | |
199 const Options& opts, SkPMColor*, int*) { | |
200 // Check for valid input parameters | |
201 if (!this->rewindIfNeeded()) { | |
202 return kCouldNotRewind; | |
203 } | |
204 if (dstInfo.dimensions() != this->getInfo().dimensions()) { | |
205 return gif_error("Scaling not supported.\n", kInvalidScale); | |
206 } | |
207 if (!conversion_possible(dstInfo, this->getInfo())) { | |
208 return gif_error("Cannot convert input type to output type.\n", | |
209 kInvalidConversion); | |
210 } | |
211 | |
212 // Use this as a container to hold information about any gif extension | |
213 // blocks. This generally stores transparency and animation instructions. | |
214 SavedImage saveExt; | |
215 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); | |
216 saveExt.ExtensionBlocks = NULL; | |
217 saveExt.ExtensionBlockCount = 0; | |
218 GifByteType* extData; | |
219 #if GIFLIB_MAJOR >= 5 | |
220 int32_t extFunction; | |
221 #endif | |
222 | |
223 // We will loop over components of gif images until we find an image. Once | |
224 // we find an image, we will decode and return it. While many gif files | |
225 // contain more than one image, we will simply decode the first image. | |
226 const int32_t width = dstInfo.width(); | |
227 const int32_t height = dstInfo.height(); | |
228 GifRecordType recordType = UNDEFINED_RECORD_TYPE; | |
229 while (TERMINATE_RECORD_TYPE != recordType) { | |
scroggo
2015/03/31 18:39:56
Is it possible that a file never contains TERMINAT
msarett
2015/03/31 20:11:40
Yes this will always find a way to fail inside or
| |
230 // Get the current record type | |
231 if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) { | |
232 return gif_error("DGifGetRecordType failed.\n", kInvalidInput); | |
233 } | |
234 | |
235 switch (recordType) { | |
236 case IMAGE_DESC_RECORD_TYPE: { | |
237 // Read the image descriptor | |
238 if (GIF_ERROR == DGifGetImageDesc(fGif)) { | |
239 return gif_error("DGifGetImageDesc failed.\n", | |
240 kInvalidInput); | |
241 } | |
242 | |
243 // If reading the image descriptor is successful, the image | |
244 // count will be incremented | |
245 SkASSERT(fGif->ImageCount >= 1); | |
246 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1]; | |
scroggo
2015/03/31 18:39:56
It looks like we're using the last image? Why not
msarett
2015/03/31 20:11:40
We are always using and returning the first image.
| |
247 | |
248 // Process the descriptor | |
249 const GifImageDesc& desc = image->ImageDesc; | |
250 int32_t imageLeft = desc.Left; | |
251 int32_t imageTop = desc.Top; | |
252 int32_t innerWidth = desc.Width; | |
253 int32_t innerHeight = desc.Height; | |
254 // Fail on non-positive dimensions | |
255 if (innerWidth <= 0 || innerHeight <= 0) { | |
256 return gif_error("Invalid dimensions for inner image.\n", | |
257 kInvalidInput); | |
258 } | |
259 // Treat the following cases as warnings and try to fix | |
260 if (innerWidth > width) { | |
261 gif_warning("Inner image too wide, shrinking.\n"); | |
262 innerWidth = width; | |
263 imageLeft = 0; | |
264 } else if (imageLeft + innerWidth > width) { | |
265 gif_warning("Shifting inner image to left to fit.\n"); | |
266 imageLeft = width - innerWidth; | |
267 } else if (imageLeft < 0) { | |
268 gif_warning("Shifting image to right to fit\n"); | |
269 imageLeft = 0; | |
270 } | |
271 if (innerHeight > height) { | |
272 gif_warning("Inner image too tall, shrinking.\n"); | |
273 innerHeight = height; | |
274 imageTop = 0; | |
275 } else if (imageTop + innerHeight > height) { | |
276 gif_warning("Shifting inner image up to fit.\n"); | |
277 imageTop = height - innerHeight; | |
278 } else if (imageTop < 0) { | |
279 gif_warning("Shifting image down to fit\n"); | |
280 imageTop = 0; | |
281 } | |
282 | |
283 // Set up the color table | |
284 uint32_t colorCount = 0; | |
285 // Allocate maximum storage to deal with invalid indices safely | |
286 const uint32_t maxColors = 256; | |
287 SkPMColor colorTable[maxColors]; | |
288 ColorMapObject* colorMap = fGif->Image.ColorMap; | |
289 // If there is no local color table, use the global color table | |
290 if (NULL == colorMap) { | |
291 colorMap = fGif->SColorMap; | |
292 } | |
293 if (NULL != colorMap) { | |
294 colorCount = colorMap->ColorCount; | |
scroggo
2015/03/31 18:39:56
Is ColorCount created by giflib (therefore reliabl
msarett
2015/03/31 20:11:40
colorCount is reliable. giflib calculates it and
| |
295 SkASSERT(colorCount == | |
296 (unsigned) (1 << (colorMap->BitsPerPixel))); | |
297 SkASSERT(colorCount <= 256); | |
298 for (uint32_t i = 0; i < colorCount; i++) { | |
299 colorTable[i] = SkPackARGB32(0xFF, | |
300 colorMap->Colors[i].Red, | |
scroggo
2015/03/31 18:39:56
nit: This should line up with 0xFF or be indented
msarett
2015/03/31 20:11:40
Done.
| |
301 colorMap->Colors[i].Green, | |
302 colorMap->Colors[i].Blue); | |
303 } | |
304 } | |
305 | |
306 // This is used to fill unspecified pixels in the image data. | |
307 uint32_t fillIndex = fGif->SBackGroundColor; | |
308 bool fillBackground = true; | |
309 ZeroInitialized zeroInit = opts.fZeroInitialized; | |
310 | |
311 // Gifs have the option to specify the color at a single | |
312 // index of the color table as transparent. | |
313 { | |
314 int32_t transIndex = find_trans_index(saveExt, colorCount); | |
scroggo
2015/03/31 18:39:56
nit: What if we compared against colorCount here,
msarett
2015/03/31 20:11:41
I think this is much better, and it is now possibl
| |
315 | |
316 // If the background is already zeroed and we have a valid | |
317 // transparent index, we do not need to fill the background. | |
318 if (transIndex >= 0) { | |
319 colorTable[transIndex] = SK_ColorTRANSPARENT; | |
320 // If there is a transparent index, we also use this as | |
321 // the fill index. | |
322 fillIndex = transIndex; | |
323 fillBackground = (kYes_ZeroInitialized != zeroInit); | |
324 } else if (fillIndex >= colorCount) { | |
325 // If the fill index is invalid, we default to 0. This | |
326 // behavior is unspecified but matches SkImageDecoder. | |
327 fillIndex = 0; | |
328 } | |
329 } | |
330 | |
331 // Fill in the color table for indices greater than color count. | |
332 // This allows for predictable, safe behavior. | |
333 for (uint32_t i = colorCount; i < maxColors; i++) { | |
334 colorTable[i] = colorTable[fillIndex]; | |
335 } | |
336 | |
337 // Check if image is only a subset of the image frame | |
338 SkAutoTDelete<SkSwizzler> swizzler(NULL); | |
339 if (innerWidth < width || innerHeight < height) { | |
340 | |
341 // Modify the destination info | |
342 const SkImageInfo subsetDstInfo = | |
343 dstInfo.makeWH(innerWidth, innerHeight); | |
344 | |
345 // Fill the destination with the fill color | |
346 // FIXME: This may not be the behavior that we want for | |
347 // animated gifs where we draw on top of the | |
348 // previous frame. | |
349 SkColorType dstColorType = dstInfo.colorType(); | |
350 if (fillBackground) { | |
351 switch (dstColorType) { | |
352 case kN32_SkColorType: | |
353 sk_memset32((SkPMColor*) dst, | |
354 colorTable[fillIndex], | |
355 ((int) dstRowBytes) * height | |
356 / sizeof(SkPMColor)); | |
357 break; | |
358 default: | |
359 SkASSERT(false); | |
360 break; | |
361 } | |
362 } | |
363 | |
364 // Modify the dst pointer | |
365 const int32_t dstBytesPerPixel = | |
366 SkColorTypeBytesPerPixel(dstColorType); | |
367 void* subsetDst = SkTAddOffset<void*>(dst, | |
368 dstRowBytes * imageTop + | |
369 dstBytesPerPixel * imageLeft); | |
370 | |
371 // Create the subset swizzler | |
372 swizzler.reset(SkSwizzler::CreateSwizzler( | |
373 SkSwizzler::kIndex, colorTable, subsetDstInfo, | |
374 subsetDst, dstRowBytes, zeroInit)); | |
375 } else { | |
376 // Create the fully dimensional swizzler | |
377 swizzler.reset(SkSwizzler::CreateSwizzler( | |
378 SkSwizzler::kIndex, colorTable, dstInfo, dst, | |
379 dstRowBytes, zeroInit)); | |
380 } | |
381 | |
382 // Stores output from dgiflib and input to the swizzler | |
383 SkAutoTDeleteArray<uint8_t> | |
384 buffer(SkNEW_ARRAY(uint8_t, innerWidth)); | |
385 | |
386 // Check the interlace flag and iterate over rows of the input | |
387 if (fGif->Image.Interlace) { | |
388 // In interlace mode, the rows of input are rearranged in | |
389 // the output image. We use an iterator to take care of | |
390 // the rearranging. | |
391 SkGifInterlaceIter iter(innerHeight); | |
392 for (int32_t y = 0; y < innerHeight; y++) { | |
393 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), | |
394 innerWidth)) { | |
395 // Recover from error by filling remainder of image | |
396 if (fillBackground) { | |
397 memset(buffer.get(), fillIndex, innerWidth); | |
398 for (; y < innerHeight; y++) { | |
399 swizzler->next(buffer.get(), iter.nextY()); | |
400 } | |
401 } | |
402 return gif_error(SkStringPrintf( | |
403 "Could not decode line %d of %d.\n", | |
404 y, height - 1).c_str(), kIncompleteInput); | |
405 } | |
406 swizzler->next(buffer.get(), iter.nextY()); | |
407 } | |
408 } else { | |
409 // Standard mode | |
410 for (int32_t y = 0; y < innerHeight; y++) { | |
411 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), | |
412 innerWidth)) { | |
413 if (fillBackground) { | |
414 SkPMColor* dstPtr = (SkPMColor*) SkTAddOffset | |
415 <void*>(dst, y * dstRowBytes); | |
416 sk_memset32(dstPtr, colorTable[fillIndex], | |
417 (height - y) * ((int) dstRowBytes) | |
418 / sizeof(SkPMColor)); | |
419 } | |
420 return gif_error(SkStringPrintf( | |
421 "Could not decode line %d of %d.\n", | |
422 y, height - 1).c_str(), kIncompleteInput); | |
423 } | |
424 swizzler->next(buffer.get()); | |
425 } | |
426 } | |
427 | |
428 // FIXME: Gif files may have multiple images stored in a single | |
429 // file. This is most commonly used to enable | |
430 // animations. Since we are leaving animated gifs as a | |
431 // TODO, we will return kSuccess after decoding the | |
432 // first image in the file. This is the same behavior | |
433 // as SkImageDecoder_libgif. | |
434 // | |
435 // Most times this works pretty well, but sometimes it | |
436 // doesn't. For example, I have an animated test image | |
437 // where the first image in the file is 1x1, but the | |
438 // subsequent images are meaningful. This currently | |
439 // displays the 1x1 image, which is not ideal. Right | |
440 // now I am leaving this as an issue that will be | |
441 // addressed when we implement animated gifs. | |
442 // | |
443 // It is also possible (not explicitly disallowed in the | |
444 // specification) that gif files provide multiple | |
445 // images in a single file that are all meant to be | |
446 // displayed in the same frame together. I will | |
447 // currently leave this unimplemented until I find a | |
448 // test case that expects this behavior. | |
449 return kSuccess; | |
450 } | |
451 | |
452 // Extensions are used to specify special properties of the image | |
453 // such as transparency or animation. | |
454 case EXTENSION_RECORD_TYPE: | |
scroggo
2015/03/31 18:39:57
Does this part add the extension that provides the
msarett
2015/03/31 20:11:40
Yes.
| |
455 // Read extension data | |
456 #if GIFLIB_MAJOR < 5 | |
457 if (GIF_ERROR == | |
458 DGifGetExtension(fGif, &saveExt.Function, &extData)) { | |
459 #else | |
460 if (GIF_ERROR == | |
461 DGifGetExtension(fGif, &extFunction, &extData)) { | |
462 #endif | |
463 return gif_error("Could not get extension.\n", | |
464 kIncompleteInput); | |
465 } | |
466 | |
467 // Create an extension block with our data | |
468 while (NULL != extData) { | |
469 // Add a single block | |
470 #if GIFLIB_MAJOR < 5 | |
471 if (GIF_ERROR == AddExtensionBlock(&saveExt, extData[0], | |
472 &extData[1])) { | |
473 #else | |
474 if (GIF_ERROR == | |
475 GifAddExtensionBlock(&saveExt.ExtensionBlockCount, | |
476 &saveExt.ExtensionBlocks, extFunction, extData[0], | |
477 &extData[1])) { | |
478 #endif | |
479 return gif_error("Could not add extension block.\n", | |
480 kIncompleteInput); | |
481 } | |
482 // Move to the next block | |
483 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { | |
484 return gif_error("Could not get next extension.\n", | |
485 kIncompleteInput); | |
486 } | |
487 #if GIFLIB_MAJOR < 5 | |
488 saveExt.Function = 0; | |
489 #endif | |
490 } | |
491 break; | |
492 | |
493 // Signals the end of the gif file | |
494 case TERMINATE_RECORD_TYPE: | |
scroggo
2015/03/31 18:39:56
Any reason to single this out from default? The be
msarett
2015/03/31 20:11:40
The default case should never be reached. I will
| |
495 break; | |
496 | |
497 default: | |
498 break; | |
499 } | |
500 } | |
501 | |
502 return gif_error("Could not find any images to decode in gif file.\n", | |
503 kInvalidInput); | |
504 } | |
OLD | NEW |