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" |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
52 static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, | 52 static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, |
53 int32_t size) { | 53 int32_t size) { |
54 SkStream* stream = (SkStream*) fileType->UserData; | 54 SkStream* stream = (SkStream*) fileType->UserData; |
55 return (int32_t) stream->read(out, size); | 55 return (int32_t) stream->read(out, size); |
56 } | 56 } |
57 | 57 |
58 /* | 58 /* |
59 * Open the gif file | 59 * Open the gif file |
60 */ | 60 */ |
61 static GifFileType* open_gif(SkStream* stream) { | 61 static GifFileType* open_gif(SkStream* stream) { |
62 return DGifOpen(stream, read_bytes_callback, NULL); | 62 return DGifOpen(stream, read_bytes_callback, nullptr); |
63 } | 63 } |
64 | 64 |
65 /* | 65 /* |
66 * This function cleans up the gif object after the decode completes | 66 * This function cleans up the gif object after the decode completes |
67 * It is used in a SkAutoTCallIProc template | 67 * It is used in a SkAutoTCallIProc template |
68 */ | 68 */ |
69 void SkGifCodec::CloseGif(GifFileType* gif) { | 69 void SkGifCodec::CloseGif(GifFileType* gif) { |
70 DGifCloseFile(gif, NULL); | 70 DGifCloseFile(gif, nullptr); |
71 } | 71 } |
72 | 72 |
73 /* | 73 /* |
74 * This function free extension data that has been saved to assist the image | 74 * This function free extension data that has been saved to assist the image |
75 * decoder | 75 * decoder |
76 */ | 76 */ |
77 void SkGifCodec::FreeExtension(SavedImage* image) { | 77 void SkGifCodec::FreeExtension(SavedImage* image) { |
78 if (NULL != image->ExtensionBlocks) { | 78 if (nullptr != image->ExtensionBlocks) { |
79 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); | 79 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); |
80 } | 80 } |
81 } | 81 } |
82 | 82 |
83 /* | 83 /* |
84 * Check if a there is an index of the color table for a transparent pixel | 84 * Check if a there is an index of the color table for a transparent pixel |
85 */ | 85 */ |
86 static uint32_t find_trans_index(const SavedImage& image) { | 86 static uint32_t find_trans_index(const SavedImage& image) { |
87 // If there is a transparent index specified, it will be contained in an | 87 // If there is a transparent index specified, it will be contained in an |
88 // extension block. We will loop through extension blocks in reverse order | 88 // extension block. We will loop through extension blocks in reverse order |
(...skipping 27 matching lines...) Expand all Loading... |
116 // Use maximum unsigned int (surely an invalid index) to indicate that a val
id | 116 // Use maximum unsigned int (surely an invalid index) to indicate that a val
id |
117 // index was not found. | 117 // index was not found. |
118 return SK_MaxU32; | 118 return SK_MaxU32; |
119 } | 119 } |
120 | 120 |
121 /* | 121 /* |
122 * Read enough of the stream to initialize the SkGifCodec. | 122 * Read enough of the stream to initialize the SkGifCodec. |
123 * Returns a bool representing success or failure. | 123 * Returns a bool representing success or failure. |
124 * | 124 * |
125 * @param codecOut | 125 * @param codecOut |
126 * If it returned true, and codecOut was not NULL, | 126 * If it returned true, and codecOut was not nullptr, |
127 * codecOut will be set to a new SkGifCodec. | 127 * codecOut will be set to a new SkGifCodec. |
128 * | 128 * |
129 * @param gifOut | 129 * @param gifOut |
130 * If it returned true, and codecOut was NULL, | 130 * If it returned true, and codecOut was nullptr, |
131 * gifOut must be non-NULL and gifOut will be set to a new | 131 * gifOut must be non-nullptr and gifOut will be set to a new |
132 * GifFileType pointer. | 132 * GifFileType pointer. |
133 * | 133 * |
134 * @param stream | 134 * @param stream |
135 * Deleted on failure. | 135 * Deleted on failure. |
136 * codecOut will take ownership of it in the case where we created a codec. | 136 * codecOut will take ownership of it in the case where we created a codec. |
137 * Ownership is unchanged when we returned a gifOut. | 137 * Ownership is unchanged when we returned a gifOut. |
138 * | 138 * |
139 */ | 139 */ |
140 bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType**
gifOut) { | 140 bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType**
gifOut) { |
141 SkAutoTDelete<SkStream> streamDeleter(stream); | 141 SkAutoTDelete<SkStream> streamDeleter(stream); |
142 | 142 |
143 // Read gif header, logical screen descriptor, and global color table | 143 // Read gif header, logical screen descriptor, and global color table |
144 SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); | 144 SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); |
145 | 145 |
146 if (NULL == gif) { | 146 if (nullptr == gif) { |
147 gif_error("DGifOpen failed.\n"); | 147 gif_error("DGifOpen failed.\n"); |
148 return false; | 148 return false; |
149 } | 149 } |
150 | 150 |
151 if (NULL != codecOut) { | 151 if (nullptr != codecOut) { |
152 // Get fields from header | 152 // Get fields from header |
153 const int32_t width = gif->SWidth; | 153 const int32_t width = gif->SWidth; |
154 const int32_t height = gif->SHeight; | 154 const int32_t height = gif->SHeight; |
155 if (width <= 0 || height <= 0) { | 155 if (width <= 0 || height <= 0) { |
156 gif_error("Invalid dimensions.\n"); | 156 gif_error("Invalid dimensions.\n"); |
157 return false; | 157 return false; |
158 } | 158 } |
159 | 159 |
160 // Return the codec | 160 // Return the codec |
161 // kIndex is the most natural color type for gifs, so we set this as | 161 // kIndex is the most natural color type for gifs, so we set this as |
162 // the default. | 162 // the default. |
163 // Many gifs specify a color table index for transparent pixels. Every | 163 // Many gifs specify a color table index for transparent pixels. Every |
164 // other pixel is guaranteed to be opaque. Despite this, because of the | 164 // other pixel is guaranteed to be opaque. Despite this, because of the |
165 // possiblity of transparent pixels, we cannot assume that the image is | 165 // possiblity of transparent pixels, we cannot assume that the image is |
166 // opaque. We have the option to set the alpha type as kPremul or | 166 // opaque. We have the option to set the alpha type as kPremul or |
167 // kUnpremul. Both are valid since the alpha component will always be | 167 // kUnpremul. Both are valid since the alpha component will always be |
168 // 0xFF or the entire 32-bit pixel will be set to zero. We prefer | 168 // 0xFF or the entire 32-bit pixel will be set to zero. We prefer |
169 // kPremul because we support kPremul, and it is more efficient to | 169 // kPremul because we support kPremul, and it is more efficient to |
170 // use kPremul directly even when kUnpremul is supported. | 170 // use kPremul directly even when kUnpremul is supported. |
171 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, | 171 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, |
172 kIndex_8_SkColorType, kPremul_SkAlphaType); | 172 kIndex_8_SkColorType, kPremul_SkAlphaType); |
173 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach
()); | 173 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach
()); |
174 } else { | 174 } else { |
175 SkASSERT(NULL != gifOut); | 175 SkASSERT(nullptr != gifOut); |
176 streamDeleter.detach(); | 176 streamDeleter.detach(); |
177 *gifOut = gif.detach(); | 177 *gifOut = gif.detach(); |
178 } | 178 } |
179 return true; | 179 return true; |
180 } | 180 } |
181 | 181 |
182 /* | 182 /* |
183 * Assumes IsGif was called and returned true | 183 * Assumes IsGif was called and returned true |
184 * Creates a gif decoder | 184 * Creates a gif decoder |
185 * Reads enough of the stream to determine the image format | 185 * Reads enough of the stream to determine the image format |
186 */ | 186 */ |
187 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { | 187 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { |
188 SkCodec* codec = NULL; | 188 SkCodec* codec = nullptr; |
189 if (ReadHeader(stream, &codec, NULL)) { | 189 if (ReadHeader(stream, &codec, nullptr)) { |
190 return codec; | 190 return codec; |
191 } | 191 } |
192 return NULL; | 192 return nullptr; |
193 } | 193 } |
194 | 194 |
195 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, | 195 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, |
196 GifFileType* gif) | 196 GifFileType* gif) |
197 : INHERITED(srcInfo, stream) | 197 : INHERITED(srcInfo, stream) |
198 , fGif(gif) | 198 , fGif(gif) |
199 {} | 199 {} |
200 | 200 |
201 bool SkGifCodec::onRewind() { | 201 bool SkGifCodec::onRewind() { |
202 GifFileType* gifOut = NULL; | 202 GifFileType* gifOut = nullptr; |
203 if (!ReadHeader(this->stream(), NULL, &gifOut)) { | 203 if (!ReadHeader(this->stream(), nullptr, &gifOut)) { |
204 return false; | 204 return false; |
205 } | 205 } |
206 | 206 |
207 SkASSERT(NULL != gifOut); | 207 SkASSERT(nullptr != gifOut); |
208 fGif.reset(gifOut); | 208 fGif.reset(gifOut); |
209 return true; | 209 return true; |
210 } | 210 } |
211 | 211 |
212 /* | 212 /* |
213 * Initiates the gif decode | 213 * Initiates the gif decode |
214 */ | 214 */ |
215 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, | 215 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
216 void* dst, size_t dstRowBytes, | 216 void* dst, size_t dstRowBytes, |
217 const Options& opts, | 217 const Options& opts, |
(...skipping 14 matching lines...) Expand all Loading... |
232 } | 232 } |
233 if (!conversion_possible(dstInfo, this->getInfo())) { | 233 if (!conversion_possible(dstInfo, this->getInfo())) { |
234 return gif_error("Cannot convert input type to output type.\n", | 234 return gif_error("Cannot convert input type to output type.\n", |
235 kInvalidConversion); | 235 kInvalidConversion); |
236 } | 236 } |
237 | 237 |
238 // Use this as a container to hold information about any gif extension | 238 // Use this as a container to hold information about any gif extension |
239 // blocks. This generally stores transparency and animation instructions. | 239 // blocks. This generally stores transparency and animation instructions. |
240 SavedImage saveExt; | 240 SavedImage saveExt; |
241 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); | 241 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); |
242 saveExt.ExtensionBlocks = NULL; | 242 saveExt.ExtensionBlocks = nullptr; |
243 saveExt.ExtensionBlockCount = 0; | 243 saveExt.ExtensionBlockCount = 0; |
244 GifByteType* extData; | 244 GifByteType* extData; |
245 int32_t extFunction; | 245 int32_t extFunction; |
246 | 246 |
247 // We will loop over components of gif images until we find an image. Once | 247 // We will loop over components of gif images until we find an image. Once |
248 // we find an image, we will decode and return it. While many gif files | 248 // we find an image, we will decode and return it. While many gif files |
249 // contain more than one image, we will simply decode the first image. | 249 // contain more than one image, we will simply decode the first image. |
250 const int32_t width = dstInfo.width(); | 250 const int32_t width = dstInfo.width(); |
251 const int32_t height = dstInfo.height(); | 251 const int32_t height = dstInfo.height(); |
252 GifRecordType recordType; | 252 GifRecordType recordType; |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
302 } else if (imageTop < 0) { | 302 } else if (imageTop < 0) { |
303 gif_warning("Shifting image down to fit\n"); | 303 gif_warning("Shifting image down to fit\n"); |
304 imageTop = 0; | 304 imageTop = 0; |
305 } | 305 } |
306 | 306 |
307 // Create a color table to store colors the giflib colorMap | 307 // Create a color table to store colors the giflib colorMap |
308 SkPMColor alternateColorPtr[256]; | 308 SkPMColor alternateColorPtr[256]; |
309 SkPMColor* colorTable; | 309 SkPMColor* colorTable; |
310 SkColorType dstColorType = dstInfo.colorType(); | 310 SkColorType dstColorType = dstInfo.colorType(); |
311 if (kIndex_8_SkColorType == dstColorType) { | 311 if (kIndex_8_SkColorType == dstColorType) { |
312 SkASSERT(NULL != inputColorPtr); | 312 SkASSERT(nullptr != inputColorPtr); |
313 SkASSERT(NULL != inputColorCount); | 313 SkASSERT(nullptr != inputColorCount); |
314 colorTable = inputColorPtr; | 314 colorTable = inputColorPtr; |
315 } else { | 315 } else { |
316 colorTable = alternateColorPtr; | 316 colorTable = alternateColorPtr; |
317 } | 317 } |
318 | 318 |
319 // Set up the color table | 319 // Set up the color table |
320 uint32_t colorCount = 0; | 320 uint32_t colorCount = 0; |
321 // Allocate maximum storage to deal with invalid indices safely | 321 // Allocate maximum storage to deal with invalid indices safely |
322 const uint32_t maxColors = 256; | 322 const uint32_t maxColors = 256; |
323 ColorMapObject* colorMap = fGif->Image.ColorMap; | 323 ColorMapObject* colorMap = fGif->Image.ColorMap; |
324 // If there is no local color table, use the global color table | 324 // If there is no local color table, use the global color table |
325 if (NULL == colorMap) { | 325 if (nullptr == colorMap) { |
326 colorMap = fGif->SColorMap; | 326 colorMap = fGif->SColorMap; |
327 } | 327 } |
328 if (NULL != colorMap) { | 328 if (nullptr != colorMap) { |
329 colorCount = colorMap->ColorCount; | 329 colorCount = colorMap->ColorCount; |
330 SkASSERT(colorCount == | 330 SkASSERT(colorCount == |
331 (unsigned) (1 << (colorMap->BitsPerPixel))); | 331 (unsigned) (1 << (colorMap->BitsPerPixel))); |
332 SkASSERT(colorCount <= 256); | 332 SkASSERT(colorCount <= 256); |
333 for (uint32_t i = 0; i < colorCount; i++) { | 333 for (uint32_t i = 0; i < colorCount; i++) { |
334 colorTable[i] = SkPackARGB32(0xFF, | 334 colorTable[i] = SkPackARGB32(0xFF, |
335 colorMap->Colors[i].Red, | 335 colorMap->Colors[i].Red, |
336 colorMap->Colors[i].Green, | 336 colorMap->Colors[i].Green, |
337 colorMap->Colors[i].Blue); | 337 colorMap->Colors[i].Blue); |
338 } | 338 } |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
373 kYes_ZeroInitialized == zeroInit; | 373 kYes_ZeroInitialized == zeroInit; |
374 | 374 |
375 | 375 |
376 // Fill in the color table for indices greater than color count. | 376 // Fill in the color table for indices greater than color count. |
377 // This allows for predictable, safe behavior. | 377 // This allows for predictable, safe behavior. |
378 for (uint32_t i = colorCount; i < maxColors; i++) { | 378 for (uint32_t i = colorCount; i < maxColors; i++) { |
379 colorTable[i] = colorTable[fillIndex]; | 379 colorTable[i] = colorTable[fillIndex]; |
380 } | 380 } |
381 | 381 |
382 // Check if image is only a subset of the image frame | 382 // Check if image is only a subset of the image frame |
383 SkAutoTDelete<SkSwizzler> swizzler(NULL); | 383 SkAutoTDelete<SkSwizzler> swizzler(nullptr); |
384 if (innerWidth < width || innerHeight < height) { | 384 if (innerWidth < width || innerHeight < height) { |
385 | 385 |
386 // Modify the destination info | 386 // Modify the destination info |
387 const SkImageInfo subsetDstInfo = | 387 const SkImageInfo subsetDstInfo = |
388 dstInfo.makeWH(innerWidth, innerHeight); | 388 dstInfo.makeWH(innerWidth, innerHeight); |
389 | 389 |
390 // Fill the destination with the fill color | 390 // Fill the destination with the fill color |
391 // FIXME: This may not be the behavior that we want for | 391 // FIXME: This may not be the behavior that we want for |
392 // animated gifs where we draw on top of the | 392 // animated gifs where we draw on top of the |
393 // previous frame. | 393 // previous frame. |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
490 // such as transparency or animation. | 490 // such as transparency or animation. |
491 case EXTENSION_RECORD_TYPE: | 491 case EXTENSION_RECORD_TYPE: |
492 // Read extension data | 492 // Read extension data |
493 if (GIF_ERROR == | 493 if (GIF_ERROR == |
494 DGifGetExtension(fGif, &extFunction, &extData)) { | 494 DGifGetExtension(fGif, &extFunction, &extData)) { |
495 return gif_error("Could not get extension.\n", | 495 return gif_error("Could not get extension.\n", |
496 kIncompleteInput); | 496 kIncompleteInput); |
497 } | 497 } |
498 | 498 |
499 // Create an extension block with our data | 499 // Create an extension block with our data |
500 while (NULL != extData) { | 500 while (nullptr != extData) { |
501 // Add a single block | 501 // Add a single block |
502 if (GIF_ERROR == | 502 if (GIF_ERROR == |
503 GifAddExtensionBlock(&saveExt.ExtensionBlockCount, | 503 GifAddExtensionBlock(&saveExt.ExtensionBlockCount, |
504 &saveExt.ExtensionBlocks, extFunction, extData[0], | 504 &saveExt.ExtensionBlocks, extFunction, extData[0], |
505 &extData[1])) { | 505 &extData[1])) { |
506 return gif_error("Could not add extension block.\n", | 506 return gif_error("Could not add extension block.\n", |
507 kIncompleteInput); | 507 kIncompleteInput); |
508 } | 508 } |
509 // Move to the next block | 509 // Move to the next block |
510 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { | 510 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { |
(...skipping 11 matching lines...) Expand all Loading... |
522 // giflib returns an error code if the record type is not known. | 522 // giflib returns an error code if the record type is not known. |
523 // We should catch this error immediately. | 523 // We should catch this error immediately. |
524 SkASSERT(false); | 524 SkASSERT(false); |
525 break; | 525 break; |
526 } | 526 } |
527 } while (TERMINATE_RECORD_TYPE != recordType); | 527 } while (TERMINATE_RECORD_TYPE != recordType); |
528 | 528 |
529 return gif_error("Could not find any images to decode in gif file.\n", | 529 return gif_error("Could not find any images to decode in gif file.\n", |
530 kInvalidInput); | 530 kInvalidInput); |
531 } | 531 } |
OLD | NEW |