| 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 |