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 "SkStream.h" | 13 #include "SkStream.h" |
14 #include "SkSwizzler.h" | 14 #include "SkSwizzler.h" |
15 #include "SkUtils.h" | 15 #include "SkUtils.h" |
16 | 16 |
17 /* | 17 /* |
18 * Checks the start of the stream to see if the image is a gif | 18 * Checks the start of the stream to see if the image is a gif |
19 */ | 19 */ |
20 bool SkGifCodec::IsGif(SkStream* stream) { | 20 bool SkGifCodec::IsGif(SkStream* stream) { |
21 char buf[GIF_STAMP_LEN]; | 21 char buf[GIF_STAMP_LEN]; |
22 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { | 22 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { |
23 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || | 23 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || |
24 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || | 24 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || |
25 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { | 25 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) |
| 26 { |
26 return true; | 27 return true; |
27 } | 28 } |
28 } | 29 } |
29 return false; | 30 return false; |
30 } | 31 } |
31 | 32 |
32 /* | 33 /* |
33 * Warning reporting function | 34 * Warning reporting function |
34 */ | 35 */ |
35 static void gif_warning(const char* msg) { | 36 static void gif_warning(const char* msg) { |
36 SkCodecPrintf("Gif Warning: %s\n", msg); | 37 SkCodecPrintf("Gif Warning: %s\n", msg); |
37 } | 38 } |
38 | 39 |
39 /* | 40 /* |
40 * Error function | 41 * Error function |
41 */ | 42 */ |
42 static SkCodec::Result gif_error(const char* msg, | 43 static SkCodec::Result gif_error(const char* msg, SkCodec::Result result = SkCod
ec::kInvalidInput) { |
43 SkCodec::Result result = SkCodec::kInvalidInput) { | |
44 SkCodecPrintf("Gif Error: %s\n", msg); | 44 SkCodecPrintf("Gif Error: %s\n", msg); |
45 return result; | 45 return result; |
46 } | 46 } |
47 | 47 |
48 | 48 |
49 /* | 49 /* |
50 * Read function that will be passed to gif_lib | 50 * Read function that will be passed to gif_lib |
51 */ | 51 */ |
52 static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, | 52 static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, int3
2_t size) { |
53 int32_t size) { | |
54 SkStream* stream = (SkStream*) fileType->UserData; | 53 SkStream* stream = (SkStream*) fileType->UserData; |
55 return (int32_t) stream->read(out, size); | 54 return (int32_t) stream->read(out, size); |
56 } | 55 } |
57 | 56 |
58 /* | 57 /* |
59 * Open the gif file | 58 * Open the gif file |
60 */ | 59 */ |
61 static GifFileType* open_gif(SkStream* stream) { | 60 static GifFileType* open_gif(SkStream* stream) { |
62 return DGifOpen(stream, read_bytes_callback, nullptr); | 61 return DGifOpen(stream, read_bytes_callback, nullptr); |
63 } | 62 } |
(...skipping 25 matching lines...) Expand all Loading... |
89 // to check the most recent extension blocks first. | 88 // to check the most recent extension blocks first. |
90 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) { | 89 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) { |
91 // Get an extension block | 90 // Get an extension block |
92 const ExtensionBlock& extBlock = image.ExtensionBlocks[i]; | 91 const ExtensionBlock& extBlock = image.ExtensionBlocks[i]; |
93 | 92 |
94 // Specifically, we need to check for a graphics control extension, | 93 // Specifically, we need to check for a graphics control extension, |
95 // which may contain transparency information. Also, note that a valid | 94 // which may contain transparency information. Also, note that a valid |
96 // graphics control extension is always four bytes. The fourth byte | 95 // graphics control extension is always four bytes. The fourth byte |
97 // is the transparent index (if it exists), so we need at least four | 96 // is the transparent index (if it exists), so we need at least four |
98 // bytes. | 97 // bytes. |
99 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && | 98 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >=
4) { |
100 extBlock.ByteCount >= 4) { | |
101 | 99 |
102 // Check the transparent color flag which indicates whether a | 100 // Check the transparent color flag which indicates whether a |
103 // transparent index exists. It is the least significant bit of | 101 // transparent index exists. It is the least significant bit of |
104 // the first byte of the extension block. | 102 // the first byte of the extension block. |
105 if (1 == (extBlock.Bytes[0] & 1)) { | 103 if (1 == (extBlock.Bytes[0] & 1)) { |
106 | 104 |
107 // Use uint32_t to prevent sign extending | 105 // Use uint32_t to prevent sign extending |
108 return extBlock.Bytes[3]; | 106 return extBlock.Bytes[3]; |
109 } | 107 } |
110 | 108 |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 // the default. | 160 // the default. |
163 // Many gifs specify a color table index for transparent pixels. Every | 161 // Many gifs specify a color table index for transparent pixels. Every |
164 // other pixel is guaranteed to be opaque. Despite this, because of the | 162 // other pixel is guaranteed to be opaque. Despite this, because of the |
165 // possiblity of transparent pixels, we cannot assume that the image is | 163 // 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 | 164 // 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 | 165 // 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 | 166 // 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 | 167 // kPremul because we support kPremul, and it is more efficient to |
170 // use kPremul directly even when kUnpremul is supported. | 168 // use kPremul directly even when kUnpremul is supported. |
171 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, | 169 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, |
172 kIndex_8_SkColorType, kPremul_SkAlphaType); | 170 kIndex_8_SkColorType, k
Premul_SkAlphaType); |
173 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach
()); | 171 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach
()); |
174 } else { | 172 } else { |
175 SkASSERT(nullptr != gifOut); | 173 SkASSERT(nullptr != gifOut); |
176 streamDeleter.detach(); | 174 streamDeleter.detach(); |
177 *gifOut = gif.detach(); | 175 *gifOut = gif.detach(); |
178 } | 176 } |
179 return true; | 177 return true; |
180 } | 178 } |
181 | 179 |
182 /* | 180 /* |
183 * Assumes IsGif was called and returned true | 181 * Assumes IsGif was called and returned true |
184 * Creates a gif decoder | 182 * Creates a gif decoder |
185 * Reads enough of the stream to determine the image format | 183 * Reads enough of the stream to determine the image format |
186 */ | 184 */ |
187 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { | 185 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { |
188 SkCodec* codec = nullptr; | 186 SkCodec* codec = nullptr; |
189 if (ReadHeader(stream, &codec, nullptr)) { | 187 if (ReadHeader(stream, &codec, nullptr)) { |
190 return codec; | 188 return codec; |
191 } | 189 } |
192 return nullptr; | 190 return nullptr; |
193 } | 191 } |
194 | 192 |
195 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, | 193 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType
* gif) |
196 GifFileType* gif) | |
197 : INHERITED(srcInfo, stream) | 194 : INHERITED(srcInfo, stream) |
198 , fGif(gif) | 195 , fGif(gif) |
199 {} | 196 {} |
200 | 197 |
201 bool SkGifCodec::onRewind() { | 198 bool SkGifCodec::onRewind() { |
202 GifFileType* gifOut = nullptr; | 199 GifFileType* gifOut = nullptr; |
203 if (!ReadHeader(this->stream(), nullptr, &gifOut)) { | 200 if (!ReadHeader(this->stream(), nullptr, &gifOut)) { |
204 return false; | 201 return false; |
205 } | 202 } |
206 | 203 |
(...skipping 17 matching lines...) Expand all Loading... |
224 | 221 |
225 // Check for valid input parameters | 222 // Check for valid input parameters |
226 if (opts.fSubset) { | 223 if (opts.fSubset) { |
227 // Subsets are not supported. | 224 // Subsets are not supported. |
228 return kUnimplemented; | 225 return kUnimplemented; |
229 } | 226 } |
230 if (dstInfo.dimensions() != this->getInfo().dimensions()) { | 227 if (dstInfo.dimensions() != this->getInfo().dimensions()) { |
231 return gif_error("Scaling not supported.\n", kInvalidScale); | 228 return gif_error("Scaling not supported.\n", kInvalidScale); |
232 } | 229 } |
233 if (!conversion_possible(dstInfo, this->getInfo())) { | 230 if (!conversion_possible(dstInfo, this->getInfo())) { |
234 return gif_error("Cannot convert input type to output type.\n", | 231 return gif_error("Cannot convert input type to output type.\n", kInvalid
Conversion); |
235 kInvalidConversion); | |
236 } | 232 } |
237 | 233 |
238 // Use this as a container to hold information about any gif extension | 234 // Use this as a container to hold information about any gif extension |
239 // blocks. This generally stores transparency and animation instructions. | 235 // blocks. This generally stores transparency and animation instructions. |
240 SavedImage saveExt; | 236 SavedImage saveExt; |
241 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); | 237 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); |
242 saveExt.ExtensionBlocks = nullptr; | 238 saveExt.ExtensionBlocks = nullptr; |
243 saveExt.ExtensionBlockCount = 0; | 239 saveExt.ExtensionBlockCount = 0; |
244 GifByteType* extData; | 240 GifByteType* extData; |
245 int32_t extFunction; | 241 int32_t extFunction; |
246 | 242 |
247 // We will loop over components of gif images until we find an image. Once | 243 // 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 | 244 // 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. | 245 // contain more than one image, we will simply decode the first image. |
250 const int32_t width = dstInfo.width(); | 246 const int32_t width = dstInfo.width(); |
251 const int32_t height = dstInfo.height(); | 247 const int32_t height = dstInfo.height(); |
252 GifRecordType recordType; | 248 GifRecordType recordType; |
253 do { | 249 do { |
254 // Get the current record type | 250 // Get the current record type |
255 if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) { | 251 if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) { |
256 return gif_error("DGifGetRecordType failed.\n", kInvalidInput); | 252 return gif_error("DGifGetRecordType failed.\n", kInvalidInput); |
257 } | 253 } |
258 | 254 |
259 switch (recordType) { | 255 switch (recordType) { |
260 case IMAGE_DESC_RECORD_TYPE: { | 256 case IMAGE_DESC_RECORD_TYPE: { |
261 // Read the image descriptor | 257 // Read the image descriptor |
262 if (GIF_ERROR == DGifGetImageDesc(fGif)) { | 258 if (GIF_ERROR == DGifGetImageDesc(fGif)) { |
263 return gif_error("DGifGetImageDesc failed.\n", | 259 return gif_error("DGifGetImageDesc failed.\n", kInvalidInput
); |
264 kInvalidInput); | |
265 } | 260 } |
266 | 261 |
267 // If reading the image descriptor is successful, the image | 262 // If reading the image descriptor is successful, the image |
268 // count will be incremented | 263 // count will be incremented |
269 SkASSERT(fGif->ImageCount >= 1); | 264 SkASSERT(fGif->ImageCount >= 1); |
270 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1]; | 265 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1]; |
271 | 266 |
272 // Process the descriptor | 267 // Process the descriptor |
273 const GifImageDesc& desc = image->ImageDesc; | 268 const GifImageDesc& desc = image->ImageDesc; |
274 int32_t imageLeft = desc.Left; | 269 int32_t imageLeft = desc.Left; |
275 int32_t imageTop = desc.Top; | 270 int32_t imageTop = desc.Top; |
276 int32_t innerWidth = desc.Width; | 271 int32_t innerWidth = desc.Width; |
277 int32_t innerHeight = desc.Height; | 272 int32_t innerHeight = desc.Height; |
278 // Fail on non-positive dimensions | 273 // Fail on non-positive dimensions |
279 if (innerWidth <= 0 || innerHeight <= 0) { | 274 if (innerWidth <= 0 || innerHeight <= 0) { |
280 return gif_error("Invalid dimensions for inner image.\n", | 275 return gif_error("Invalid dimensions for inner image.\n", kI
nvalidInput); |
281 kInvalidInput); | |
282 } | 276 } |
283 // Treat the following cases as warnings and try to fix | 277 // Treat the following cases as warnings and try to fix |
284 if (innerWidth > width) { | 278 if (innerWidth > width) { |
285 gif_warning("Inner image too wide, shrinking.\n"); | 279 gif_warning("Inner image too wide, shrinking.\n"); |
286 innerWidth = width; | 280 innerWidth = width; |
287 imageLeft = 0; | 281 imageLeft = 0; |
288 } else if (imageLeft + innerWidth > width) { | 282 } else if (imageLeft + innerWidth > width) { |
289 gif_warning("Shifting inner image to left to fit.\n"); | 283 gif_warning("Shifting inner image to left to fit.\n"); |
290 imageLeft = width - innerWidth; | 284 imageLeft = width - innerWidth; |
291 } else if (imageLeft < 0) { | 285 } else if (imageLeft < 0) { |
(...skipping 28 matching lines...) Expand all Loading... |
320 uint32_t colorCount = 0; | 314 uint32_t colorCount = 0; |
321 // Allocate maximum storage to deal with invalid indices safely | 315 // Allocate maximum storage to deal with invalid indices safely |
322 const uint32_t maxColors = 256; | 316 const uint32_t maxColors = 256; |
323 ColorMapObject* colorMap = fGif->Image.ColorMap; | 317 ColorMapObject* colorMap = fGif->Image.ColorMap; |
324 // If there is no local color table, use the global color table | 318 // If there is no local color table, use the global color table |
325 if (nullptr == colorMap) { | 319 if (nullptr == colorMap) { |
326 colorMap = fGif->SColorMap; | 320 colorMap = fGif->SColorMap; |
327 } | 321 } |
328 if (nullptr != colorMap) { | 322 if (nullptr != colorMap) { |
329 colorCount = colorMap->ColorCount; | 323 colorCount = colorMap->ColorCount; |
330 SkASSERT(colorCount == | 324 SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPi
xel))); |
331 (unsigned) (1 << (colorMap->BitsPerPixel))); | |
332 SkASSERT(colorCount <= 256); | 325 SkASSERT(colorCount <= 256); |
333 for (uint32_t i = 0; i < colorCount; i++) { | 326 for (uint32_t i = 0; i < colorCount; i++) { |
334 colorTable[i] = SkPackARGB32(0xFF, | 327 colorTable[i] = SkPackARGB32(0xFF, |
335 colorMap->Colors[i].Red, | 328 colorMap->Colors[i].Red, |
336 colorMap->Colors[i].Green, | 329 colorMap->Colors[i].Green, |
337 colorMap->Colors[i].Blue); | 330 colorMap->Colors[i].Blue); |
338 } | 331 } |
339 } | 332 } |
340 | 333 |
341 // This is used to fill unspecified pixels in the image data. | 334 // This is used to fill unspecified pixels in the image data. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
377 // This allows for predictable, safe behavior. | 370 // This allows for predictable, safe behavior. |
378 for (uint32_t i = colorCount; i < maxColors; i++) { | 371 for (uint32_t i = colorCount; i < maxColors; i++) { |
379 colorTable[i] = colorTable[fillIndex]; | 372 colorTable[i] = colorTable[fillIndex]; |
380 } | 373 } |
381 | 374 |
382 // Check if image is only a subset of the image frame | 375 // Check if image is only a subset of the image frame |
383 SkAutoTDelete<SkSwizzler> swizzler(nullptr); | 376 SkAutoTDelete<SkSwizzler> swizzler(nullptr); |
384 if (innerWidth < width || innerHeight < height) { | 377 if (innerWidth < width || innerHeight < height) { |
385 | 378 |
386 // Modify the destination info | 379 // Modify the destination info |
387 const SkImageInfo subsetDstInfo = | 380 const SkImageInfo subsetDstInfo = dstInfo.makeWH(innerWidth,
innerHeight); |
388 dstInfo.makeWH(innerWidth, innerHeight); | |
389 | 381 |
390 // Fill the destination with the fill color | 382 // Fill the destination with the fill color |
391 // FIXME: This may not be the behavior that we want for | 383 // FIXME: This may not be the behavior that we want for |
392 // animated gifs where we draw on top of the | 384 // animated gifs where we draw on top of the |
393 // previous frame. | 385 // previous frame. |
394 if (!skipBackground) { | 386 if (!skipBackground) { |
395 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, | 387 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, fill
Index, colorTable); |
396 fillIndex, colorTable); | |
397 } | 388 } |
398 | 389 |
399 // Modify the dst pointer | 390 // Modify the dst pointer |
400 const int32_t dstBytesPerPixel = | 391 const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(ds
tColorType); |
401 SkColorTypeBytesPerPixel(dstColorType); | |
402 dst = SkTAddOffset<void*>(dst, | 392 dst = SkTAddOffset<void*>(dst, |
403 dstRowBytes * imageTop + | 393 dstRowBytes * imageTop + |
404 dstBytesPerPixel * imageLeft); | 394 dstBytesPerPixel * imageLeft); |
405 | 395 |
406 // Create the subset swizzler | 396 // Create the subset swizzler |
407 swizzler.reset(SkSwizzler::CreateSwizzler( | 397 swizzler.reset(SkSwizzler::CreateSwizzler( |
408 SkSwizzler::kIndex, colorTable, subsetDstInfo, | 398 SkSwizzler::kIndex, colorTable, subsetDstInfo, |
409 zeroInit, this->getInfo())); | 399 zeroInit, this->getInfo())); |
410 } else { | 400 } else { |
411 // Create the fully dimensional swizzler | 401 // Create the fully dimensional swizzler |
412 swizzler.reset(SkSwizzler::CreateSwizzler( | 402 swizzler.reset(SkSwizzler::CreateSwizzler( |
413 SkSwizzler::kIndex, colorTable, dstInfo, | 403 SkSwizzler::kIndex, colorTable, dstInfo, |
414 zeroInit, this->getInfo())); | 404 zeroInit, this->getInfo())); |
415 } | 405 } |
416 | 406 |
417 // Stores output from dgiflib and input to the swizzler | 407 // Stores output from dgiflib and input to the swizzler |
418 SkAutoTDeleteArray<uint8_t> buffer(new uint8_t[innerWidth]); | 408 SkAutoTDeleteArray<uint8_t> buffer(new uint8_t[innerWidth]); |
419 | 409 |
420 // Check the interlace flag and iterate over rows of the input | 410 // Check the interlace flag and iterate over rows of the input |
421 if (fGif->Image.Interlace) { | 411 if (fGif->Image.Interlace) { |
422 // In interlace mode, the rows of input are rearranged in | 412 // In interlace mode, the rows of input are rearranged in |
423 // the output image. We use an iterator to take care of | 413 // the output image. We use an iterator to take care of |
424 // the rearranging. | 414 // the rearranging. |
425 SkGifInterlaceIter iter(innerHeight); | 415 SkGifInterlaceIter iter(innerHeight); |
426 for (int32_t y = 0; y < innerHeight; y++) { | 416 for (int32_t y = 0; y < innerHeight; y++) { |
427 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), | 417 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), innerWi
dth)) { |
428 innerWidth)) { | |
429 // Recover from error by filling remainder of image | 418 // Recover from error by filling remainder of image |
430 if (!skipBackground) { | 419 if (!skipBackground) { |
431 memset(buffer.get(), fillIndex, innerWidth); | 420 memset(buffer.get(), fillIndex, innerWidth); |
432 for (; y < innerHeight; y++) { | 421 for (; y < innerHeight; y++) { |
433 void* dstRow = SkTAddOffset<void>(dst, | 422 void* dstRow = SkTAddOffset<void>(dst, |
434 dstRowBytes * iter.nextY()); | 423 dstRowByte
s * iter.nextY()); |
435 swizzler->swizzle(dstRow, buffer.get()); | 424 swizzler->swizzle(dstRow, buffer.get()); |
436 } | 425 } |
437 } | 426 } |
438 return gif_error(SkStringPrintf( | 427 return gif_error(SkStringPrintf( |
439 "Could not decode line %d of %d.\n", | 428 "Could not decode line %d of %d.\n", |
440 y, height - 1).c_str(), kIncompleteInput); | 429 y, height - 1).c_str(), kIncompleteInput); |
441 } | 430 } |
442 void* dstRow = SkTAddOffset<void>( | 431 void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * ite
r.nextY()); |
443 dst, dstRowBytes * iter.nextY()); | |
444 swizzler->swizzle(dstRow, buffer.get()); | 432 swizzler->swizzle(dstRow, buffer.get()); |
445 } | 433 } |
446 } else { | 434 } else { |
447 // Standard mode | 435 // Standard mode |
448 void* dstRow = dst; | 436 void* dstRow = dst; |
449 for (int32_t y = 0; y < innerHeight; y++) { | 437 for (int32_t y = 0; y < innerHeight; y++) { |
450 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), | 438 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), innerWi
dth)) { |
451 innerWidth)) { | |
452 if (!skipBackground) { | 439 if (!skipBackground) { |
453 SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes, | 440 SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes, |
454 innerHeight - y, fillIndex, colorTable); | 441 innerHeight - y, fillIndex, col
orTable); |
455 } | 442 } |
456 return gif_error(SkStringPrintf( | 443 return gif_error(SkStringPrintf( |
457 "Could not decode line %d of %d.\n", | 444 "Could not decode line %d of %d.\n", |
458 y, height - 1).c_str(), kIncompleteInput); | 445 y, height - 1).c_str(), kIncompleteInput); |
459 } | 446 } |
460 swizzler->swizzle(dstRow, buffer.get()); | 447 swizzler->swizzle(dstRow, buffer.get()); |
461 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | 448 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
462 } | 449 } |
463 } | 450 } |
464 | 451 |
(...skipping 18 matching lines...) Expand all Loading... |
483 // displayed in the same frame together. I will | 470 // displayed in the same frame together. I will |
484 // currently leave this unimplemented until I find a | 471 // currently leave this unimplemented until I find a |
485 // test case that expects this behavior. | 472 // test case that expects this behavior. |
486 return kSuccess; | 473 return kSuccess; |
487 } | 474 } |
488 | 475 |
489 // Extensions are used to specify special properties of the image | 476 // Extensions are used to specify special properties of the image |
490 // such as transparency or animation. | 477 // such as transparency or animation. |
491 case EXTENSION_RECORD_TYPE: | 478 case EXTENSION_RECORD_TYPE: |
492 // Read extension data | 479 // Read extension data |
493 if (GIF_ERROR == | 480 if (GIF_ERROR == DGifGetExtension(fGif, &extFunction, &extData))
{ |
494 DGifGetExtension(fGif, &extFunction, &extData)) { | 481 return gif_error("Could not get extension.\n", kIncompleteIn
put); |
495 return gif_error("Could not get extension.\n", | |
496 kIncompleteInput); | |
497 } | 482 } |
498 | 483 |
499 // Create an extension block with our data | 484 // Create an extension block with our data |
500 while (nullptr != extData) { | 485 while (nullptr != extData) { |
501 // Add a single block | 486 // Add a single block |
502 if (GIF_ERROR == | 487 if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBloc
kCount, |
503 GifAddExtensionBlock(&saveExt.ExtensionBlockCount, | 488 &saveExt.ExtensionBloc
ks, |
504 &saveExt.ExtensionBlocks, extFunction, extData[0], | 489 extFunction, extData[0
], &extData[1])) |
505 &extData[1])) { | 490 { |
506 return gif_error("Could not add extension block.\n", | 491 return gif_error("Could not add extension block.\n", kIn
completeInput); |
507 kIncompleteInput); | |
508 } | 492 } |
509 // Move to the next block | 493 // Move to the next block |
510 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { | 494 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { |
511 return gif_error("Could not get next extension.\n", | 495 return gif_error("Could not get next extension.\n", kInc
ompleteInput); |
512 kIncompleteInput); | |
513 } | 496 } |
514 } | 497 } |
515 break; | 498 break; |
516 | 499 |
517 // Signals the end of the gif file | 500 // Signals the end of the gif file |
518 case TERMINATE_RECORD_TYPE: | 501 case TERMINATE_RECORD_TYPE: |
519 break; | 502 break; |
520 | 503 |
521 default: | 504 default: |
522 // giflib returns an error code if the record type is not known. | 505 // giflib returns an error code if the record type is not known. |
523 // We should catch this error immediately. | 506 // We should catch this error immediately. |
524 SkASSERT(false); | 507 SkASSERT(false); |
525 break; | 508 break; |
526 } | 509 } |
527 } while (TERMINATE_RECORD_TYPE != recordType); | 510 } while (TERMINATE_RECORD_TYPE != recordType); |
528 | 511 |
529 return gif_error("Could not find any images to decode in gif file.\n", | 512 return gif_error("Could not find any images to decode in gif file.\n", kInva
lidInput); |
530 kInvalidInput); | |
531 } | 513 } |
OLD | NEW |