Chromium Code Reviews| 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 "SkScaledCodec.h" | |
| 12 #include "SkStream.h" | 13 #include "SkStream.h" |
| 13 #include "SkSwizzler.h" | 14 #include "SkSwizzler.h" |
| 14 #include "SkUtils.h" | 15 #include "SkUtils.h" |
| 15 | 16 |
| 16 /* | 17 /* |
| 17 * 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 |
| 18 */ | 19 */ |
| 19 bool SkGifCodec::IsGif(SkStream* stream) { | 20 bool SkGifCodec::IsGif(SkStream* stream) { |
| 20 char buf[GIF_STAMP_LEN]; | 21 char buf[GIF_STAMP_LEN]; |
| 21 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { | 22 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 53 return (int32_t) stream->read(out, size); | 54 return (int32_t) stream->read(out, size); |
| 54 } | 55 } |
| 55 | 56 |
| 56 /* | 57 /* |
| 57 * Open the gif file | 58 * Open the gif file |
| 58 */ | 59 */ |
| 59 static GifFileType* open_gif(SkStream* stream) { | 60 static GifFileType* open_gif(SkStream* stream) { |
| 60 return DGifOpen(stream, read_bytes_callback, nullptr); | 61 return DGifOpen(stream, read_bytes_callback, nullptr); |
| 61 } | 62 } |
| 62 | 63 |
| 63 /* | |
| 64 * This function cleans up the gif object after the decode completes | |
| 65 * It is used in a SkAutoTCallIProc template | |
| 66 */ | |
| 67 void SkGifCodec::CloseGif(GifFileType* gif) { | |
| 68 DGifCloseFile(gif, nullptr); | |
| 69 } | |
| 70 | |
| 71 /* | |
| 72 * This function free extension data that has been saved to assist the image | |
| 73 * decoder | |
| 74 */ | |
| 75 void SkGifCodec::FreeExtension(SavedImage* image) { | |
| 76 if (nullptr != image->ExtensionBlocks) { | |
| 77 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 /* | 64 /* |
| 82 * Check if a there is an index of the color table for a transparent pixel | 65 * Check if a there is an index of the color table for a transparent pixel |
| 83 */ | 66 */ |
| 84 static uint32_t find_trans_index(const SavedImage& image) { | 67 static uint32_t find_trans_index(const SavedImage& image) { |
| 85 // If there is a transparent index specified, it will be contained in an | 68 // If there is a transparent index specified, it will be contained in an |
| 86 // extension block. We will loop through extension blocks in reverse order | 69 // extension block. We will loop through extension blocks in reverse order |
| 87 // to check the most recent extension blocks first. | 70 // to check the most recent extension blocks first. |
| 88 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) { | 71 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) { |
| 89 // Get an extension block | 72 // Get an extension block |
| 90 const ExtensionBlock& extBlock = image.ExtensionBlocks[i]; | 73 const ExtensionBlock& extBlock = image.ExtensionBlocks[i]; |
| 91 | 74 |
| 92 // Specifically, we need to check for a graphics control extension, | 75 // Specifically, we need to check for a graphics control extension, |
| 93 // which may contain transparency information. Also, note that a valid | 76 // which may contain transparency information. Also, note that a valid |
| 94 // graphics control extension is always four bytes. The fourth byte | 77 // graphics control extension is always four bytes. The fourth byte |
| 95 // is the transparent index (if it exists), so we need at least four | 78 // is the transparent index (if it exists), so we need at least four |
| 96 // bytes. | 79 // bytes. |
| 97 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >= 4) { | 80 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >= 4) { |
| 98 | |
| 99 // Check the transparent color flag which indicates whether a | 81 // Check the transparent color flag which indicates whether a |
| 100 // transparent index exists. It is the least significant bit of | 82 // transparent index exists. It is the least significant bit of |
| 101 // the first byte of the extension block. | 83 // the first byte of the extension block. |
| 102 if (1 == (extBlock.Bytes[0] & 1)) { | 84 if (1 == (extBlock.Bytes[0] & 1)) { |
| 103 | |
| 104 // Use uint32_t to prevent sign extending | 85 // Use uint32_t to prevent sign extending |
| 105 return extBlock.Bytes[3]; | 86 return extBlock.Bytes[3]; |
| 106 } | 87 } |
| 107 | 88 |
| 108 // There should only be one graphics control extension for the image frame | 89 // There should only be one graphics control extension for the image frame |
| 109 break; | 90 break; |
| 110 } | 91 } |
| 111 } | 92 } |
| 112 | 93 |
| 113 // Use maximum unsigned int (surely an invalid index) to indicate that a val id | 94 // Use maximum unsigned int (surely an invalid index) to indicate that a val id |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 134 } | 115 } |
| 135 // Third pass | 116 // Third pass |
| 136 if (encodedRow * 2 < height) { | 117 if (encodedRow * 2 < height) { |
| 137 return 2 + 4 * (encodedRow - ceil_div(height, 4)); | 118 return 2 + 4 * (encodedRow - ceil_div(height, 4)); |
| 138 } | 119 } |
| 139 // Fourth pass | 120 // Fourth pass |
| 140 return 1 + 2 * (encodedRow - ceil_div(height, 2)); | 121 return 1 + 2 * (encodedRow - ceil_div(height, 2)); |
| 141 } | 122 } |
| 142 | 123 |
| 143 /* | 124 /* |
| 125 * This function cleans up the gif object after the decode completes | |
| 126 * It is used in a SkAutoTCallIProc template | |
| 127 */ | |
| 128 void SkGifCodec::CloseGif(GifFileType* gif) { | |
| 129 DGifCloseFile(gif, NULL); | |
| 130 } | |
| 131 | |
| 132 /* | |
| 133 * This function free extension data that has been saved to assist the image | |
| 134 * decoder | |
| 135 */ | |
| 136 void SkGifCodec::FreeExtension(SavedImage* image) { | |
| 137 if (NULL != image->ExtensionBlocks) { | |
| 138 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 /* | |
| 144 * Read enough of the stream to initialize the SkGifCodec. | 143 * Read enough of the stream to initialize the SkGifCodec. |
| 145 * Returns a bool representing success or failure. | 144 * Returns a bool representing success or failure. |
| 146 * | 145 * |
| 147 * @param codecOut | 146 * @param codecOut |
| 148 * If it returned true, and codecOut was not nullptr, | 147 * If it returned true, and codecOut was not nullptr, |
| 149 * codecOut will be set to a new SkGifCodec. | 148 * codecOut will be set to a new SkGifCodec. |
| 150 * | 149 * |
| 151 * @param gifOut | 150 * @param gifOut |
| 152 * If it returned true, and codecOut was nullptr, | 151 * If it returned true, and codecOut was nullptr, |
| 153 * gifOut must be non-nullptr and gifOut will be set to a new | 152 * gifOut must be non-nullptr and gifOut will be set to a new |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 184 // the default. | 183 // the default. |
| 185 // Many gifs specify a color table index for transparent pixels. Every | 184 // Many gifs specify a color table index for transparent pixels. Every |
| 186 // other pixel is guaranteed to be opaque. Despite this, because of the | 185 // other pixel is guaranteed to be opaque. Despite this, because of the |
| 187 // possiblity of transparent pixels, we cannot assume that the image is | 186 // possiblity of transparent pixels, we cannot assume that the image is |
| 188 // opaque. We have the option to set the alpha type as kPremul or | 187 // opaque. We have the option to set the alpha type as kPremul or |
| 189 // kUnpremul. Both are valid since the alpha component will always be | 188 // kUnpremul. Both are valid since the alpha component will always be |
| 190 // 0xFF or the entire 32-bit pixel will be set to zero. We prefer | 189 // 0xFF or the entire 32-bit pixel will be set to zero. We prefer |
| 191 // kPremul because we support kPremul, and it is more efficient to | 190 // kPremul because we support kPremul, and it is more efficient to |
| 192 // use kPremul directly even when kUnpremul is supported. | 191 // use kPremul directly even when kUnpremul is supported. |
| 193 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, | 192 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, |
| 194 kIndex_8_SkColorType, k Premul_SkAlphaType); | 193 kIndex_8_SkColorType, kPremul_SkAlphaType); |
| 195 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach ()); | 194 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach ()); |
| 196 } else { | 195 } else { |
| 197 SkASSERT(nullptr != gifOut); | 196 SkASSERT(nullptr != gifOut); |
| 198 streamDeleter.detach(); | 197 streamDeleter.detach(); |
| 199 *gifOut = gif.detach(); | 198 *gifOut = gif.detach(); |
| 200 } | 199 } |
| 201 return true; | 200 return true; |
| 202 } | 201 } |
| 203 | 202 |
| 204 /* | 203 /* |
| 205 * Assumes IsGif was called and returned true | 204 * Assumes IsGif was called and returned true |
| 206 * Creates a gif decoder | 205 * Creates a gif decoder |
| 207 * Reads enough of the stream to determine the image format | 206 * Reads enough of the stream to determine the image format |
| 208 */ | 207 */ |
| 209 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { | 208 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { |
| 210 SkCodec* codec = nullptr; | 209 SkCodec* codec = nullptr; |
| 211 if (ReadHeader(stream, &codec, nullptr)) { | 210 if (ReadHeader(stream, &codec, nullptr)) { |
| 212 return codec; | 211 return codec; |
| 213 } | 212 } |
| 214 return nullptr; | 213 return nullptr; |
| 215 } | 214 } |
| 216 | 215 |
| 217 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType * gif) | 216 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType * gif) |
| 218 : INHERITED(srcInfo, stream) | 217 : INHERITED(srcInfo, stream) |
| 219 , fGif(gif) | 218 , fGif(gif) |
| 219 , fSrcBuffer(SkNEW_ARRAY(uint8_t, this->getInfo().width())) | |
|
scroggo
2015/09/02 22:45:48
nit: new
msarett
2015/09/03 17:13:31
Done.
| |
| 220 , fFillIndex(SK_MaxU32) | |
|
scroggo
2015/09/02 22:45:48
Is this a sentinel value? Maybe it should be label
msarett
2015/09/03 17:13:31
This has changed a bit. Will add comments here.
| |
| 221 , fFrameDims(SkIRect::MakeEmpty()) | |
| 222 , fFrameIsSubset(false) | |
| 223 , fColorTable(NULL) | |
| 224 , fSwizzler(NULL) | |
| 220 {} | 225 {} |
| 221 | 226 |
| 222 bool SkGifCodec::onRewind() { | 227 bool SkGifCodec::onRewind() { |
| 223 GifFileType* gifOut = nullptr; | 228 GifFileType* gifOut = nullptr; |
| 224 if (!ReadHeader(this->stream(), nullptr, &gifOut)) { | 229 if (!ReadHeader(this->stream(), nullptr, &gifOut)) { |
| 225 return false; | 230 return false; |
| 226 } | 231 } |
| 227 | 232 |
| 228 SkASSERT(nullptr != gifOut); | 233 SkASSERT(nullptr != gifOut); |
| 229 fGif.reset(gifOut); | 234 fGif.reset(gifOut); |
| 230 return true; | 235 return true; |
| 231 } | 236 } |
| 232 | 237 |
| 233 /* | 238 SkCodec::Result SkGifCodec::readUpToFirstImage(uint32_t* transIndex) { |
| 234 * Initiates the gif decode | |
| 235 */ | |
| 236 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, | |
| 237 void* dst, size_t dstRowBytes, | |
| 238 const Options& opts, | |
| 239 SkPMColor* inputColorPtr, | |
| 240 int* inputColorCount) { | |
| 241 // Rewind if necessary | |
| 242 if (!this->rewindIfNeeded()) { | |
| 243 return kCouldNotRewind; | |
| 244 } | |
| 245 | |
| 246 // Check for valid input parameters | |
| 247 if (opts.fSubset) { | |
| 248 // Subsets are not supported. | |
| 249 return kUnimplemented; | |
| 250 } | |
| 251 if (dstInfo.dimensions() != this->getInfo().dimensions()) { | |
| 252 return gif_error("Scaling not supported.\n", kInvalidScale); | |
| 253 } | |
| 254 if (!conversion_possible(dstInfo, this->getInfo())) { | |
| 255 return gif_error("Cannot convert input type to output type.\n", kInvalid Conversion); | |
| 256 } | |
| 257 | |
| 258 // Use this as a container to hold information about any gif extension | 239 // Use this as a container to hold information about any gif extension |
| 259 // blocks. This generally stores transparency and animation instructions. | 240 // blocks. This generally stores transparency and animation instructions. |
| 260 SavedImage saveExt; | 241 SavedImage saveExt; |
| 261 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); | 242 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); |
| 262 saveExt.ExtensionBlocks = nullptr; | 243 saveExt.ExtensionBlocks = nullptr; |
| 263 saveExt.ExtensionBlockCount = 0; | 244 saveExt.ExtensionBlockCount = 0; |
| 264 GifByteType* extData; | 245 GifByteType* extData; |
| 265 int32_t extFunction; | 246 int32_t extFunction; |
| 266 | 247 |
| 267 // We will loop over components of gif images until we find an image. Once | 248 // We will loop over components of gif images until we find an image. Once |
| 268 // we find an image, we will decode and return it. While many gif files | 249 // we find an image, we will decode and return it. While many gif files |
| 269 // contain more than one image, we will simply decode the first image. | 250 // contain more than one image, we will simply decode the first image. |
| 270 const int32_t width = dstInfo.width(); | |
| 271 const int32_t height = dstInfo.height(); | |
| 272 GifRecordType recordType; | 251 GifRecordType recordType; |
| 273 do { | 252 do { |
| 274 // Get the current record type | 253 // Get the current record type |
| 275 if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) { | 254 if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) { |
| 276 return gif_error("DGifGetRecordType failed.\n", kInvalidInput); | 255 return gif_error("DGifGetRecordType failed.\n", kInvalidInput); |
| 277 } | 256 } |
| 278 | |
| 279 switch (recordType) { | 257 switch (recordType) { |
| 280 case IMAGE_DESC_RECORD_TYPE: { | 258 case IMAGE_DESC_RECORD_TYPE: { |
| 281 // Read the image descriptor | 259 *transIndex = find_trans_index(saveExt); |
| 282 if (GIF_ERROR == DGifGetImageDesc(fGif)) { | |
| 283 return gif_error("DGifGetImageDesc failed.\n", kInvalidInput ); | |
| 284 } | |
| 285 | |
| 286 // If reading the image descriptor is successful, the image | |
| 287 // count will be incremented | |
| 288 SkASSERT(fGif->ImageCount >= 1); | |
| 289 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1]; | |
| 290 | |
| 291 // Process the descriptor | |
| 292 const GifImageDesc& desc = image->ImageDesc; | |
| 293 int32_t imageLeft = desc.Left; | |
| 294 int32_t imageTop = desc.Top; | |
| 295 int32_t innerWidth = desc.Width; | |
| 296 int32_t innerHeight = desc.Height; | |
| 297 // Fail on non-positive dimensions | |
| 298 if (innerWidth <= 0 || innerHeight <= 0) { | |
| 299 return gif_error("Invalid dimensions for inner image.\n", kI nvalidInput); | |
| 300 } | |
| 301 // Treat the following cases as warnings and try to fix | |
| 302 if (innerWidth > width) { | |
| 303 gif_warning("Inner image too wide, shrinking.\n"); | |
| 304 innerWidth = width; | |
| 305 imageLeft = 0; | |
| 306 } else if (imageLeft + innerWidth > width) { | |
| 307 gif_warning("Shifting inner image to left to fit.\n"); | |
| 308 imageLeft = width - innerWidth; | |
| 309 } else if (imageLeft < 0) { | |
| 310 gif_warning("Shifting image to right to fit\n"); | |
| 311 imageLeft = 0; | |
| 312 } | |
| 313 if (innerHeight > height) { | |
| 314 gif_warning("Inner image too tall, shrinking.\n"); | |
| 315 innerHeight = height; | |
| 316 imageTop = 0; | |
| 317 } else if (imageTop + innerHeight > height) { | |
| 318 gif_warning("Shifting inner image up to fit.\n"); | |
| 319 imageTop = height - innerHeight; | |
| 320 } else if (imageTop < 0) { | |
| 321 gif_warning("Shifting image down to fit\n"); | |
| 322 imageTop = 0; | |
| 323 } | |
| 324 | |
| 325 // Create a color table to store colors the giflib colorMap | |
| 326 SkPMColor alternateColorPtr[256]; | |
| 327 SkPMColor* colorTable; | |
| 328 SkColorType dstColorType = dstInfo.colorType(); | |
| 329 if (kIndex_8_SkColorType == dstColorType) { | |
| 330 SkASSERT(nullptr != inputColorPtr); | |
| 331 SkASSERT(nullptr != inputColorCount); | |
| 332 colorTable = inputColorPtr; | |
| 333 } else { | |
| 334 colorTable = alternateColorPtr; | |
| 335 } | |
| 336 | |
| 337 // Set up the color table | |
| 338 uint32_t colorCount = 0; | |
| 339 // Allocate maximum storage to deal with invalid indices safely | |
| 340 const uint32_t maxColors = 256; | |
| 341 ColorMapObject* colorMap = fGif->Image.ColorMap; | |
| 342 // If there is no local color table, use the global color table | |
| 343 if (nullptr == colorMap) { | |
| 344 colorMap = fGif->SColorMap; | |
| 345 } | |
| 346 if (nullptr != colorMap) { | |
| 347 colorCount = colorMap->ColorCount; | |
| 348 SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPi xel))); | |
| 349 SkASSERT(colorCount <= 256); | |
| 350 for (uint32_t i = 0; i < colorCount; i++) { | |
| 351 colorTable[i] = SkPackARGB32(0xFF, | |
| 352 colorMap->Colors[i].Red, | |
| 353 colorMap->Colors[i].Green, | |
| 354 colorMap->Colors[i].Blue); | |
| 355 } | |
| 356 } | |
| 357 | |
| 358 // This is used to fill unspecified pixels in the image data. | |
| 359 uint32_t fillIndex = fGif->SBackGroundColor; | |
| 360 ZeroInitialized zeroInit = opts.fZeroInitialized; | |
| 361 | |
| 362 // Gifs have the option to specify the color at a single | |
| 363 // index of the color table as transparent. | |
| 364 { | |
| 365 // Get the transparent index. If the return value of this | |
| 366 // function is greater than the colorCount, we know that | |
| 367 // there is no valid transparent color in the color table. | |
| 368 // This occurs if there is no graphics control extension or | |
| 369 // if the index specified by the graphics control extension | |
| 370 // is out of range. | |
| 371 uint32_t transIndex = find_trans_index(saveExt); | |
| 372 | |
| 373 if (transIndex < colorCount) { | |
| 374 colorTable[transIndex] = SK_ColorTRANSPARENT; | |
| 375 // If there is a transparent index, we also use this as | |
| 376 // the fill index. | |
| 377 fillIndex = transIndex; | |
| 378 } else if (fillIndex >= colorCount) { | |
| 379 // If the fill index is invalid, we default to 0. This | |
| 380 // behavior is unspecified but matches SkImageDecoder. | |
| 381 fillIndex = 0; | |
| 382 } | |
| 383 } | |
| 384 | |
| 385 // Fill in the color table for indices greater than color count. | |
| 386 // This allows for predictable, safe behavior. | |
| 387 for (uint32_t i = colorCount; i < maxColors; i++) { | |
| 388 colorTable[i] = colorTable[fillIndex]; | |
| 389 } | |
| 390 | |
| 391 // Check if image is only a subset of the image frame | |
| 392 SkAutoTDelete<SkSwizzler> swizzler(nullptr); | |
| 393 if (innerWidth < width || innerHeight < height) { | |
| 394 | |
| 395 // Modify the destination info | |
| 396 const SkImageInfo subsetDstInfo = dstInfo.makeWH(innerWidth, innerHeight); | |
| 397 | |
| 398 // Fill the destination with the fill color | |
| 399 // FIXME: This may not be the behavior that we want for | |
| 400 // animated gifs where we draw on top of the | |
| 401 // previous frame. | |
| 402 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, fillInde x, colorTable, | |
| 403 zeroInit); | |
| 404 | |
| 405 // Modify the dst pointer | |
| 406 const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(ds tColorType); | |
| 407 dst = SkTAddOffset<void*>(dst, | |
| 408 dstRowBytes * imageTop + | |
| 409 dstBytesPerPixel * imageLeft); | |
| 410 | |
| 411 // Create the subset swizzler | |
| 412 swizzler.reset(SkSwizzler::CreateSwizzler( | |
| 413 SkSwizzler::kIndex, colorTable, subsetDstInfo, | |
| 414 zeroInit, this->getInfo())); | |
| 415 } else { | |
| 416 // Create the fully dimensional swizzler | |
| 417 swizzler.reset(SkSwizzler::CreateSwizzler( | |
| 418 SkSwizzler::kIndex, colorTable, dstInfo, | |
| 419 zeroInit, this->getInfo())); | |
| 420 } | |
| 421 | |
| 422 // Stores output from dgiflib and input to the swizzler | |
| 423 SkAutoTDeleteArray<uint8_t> buffer(new uint8_t[innerWidth]); | |
| 424 | |
| 425 // Check the interlace flag and iterate over rows of the input | |
| 426 if (fGif->Image.Interlace) { | |
| 427 for (int32_t y = 0; y < innerHeight; y++) { | |
| 428 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), innerWi dth)) { | |
| 429 // Recover from error by filling remainder of image | |
| 430 memset(buffer.get(), fillIndex, innerWidth); | |
| 431 for (; y < innerHeight; y++) { | |
| 432 void* dstRow = SkTAddOffset<void>(dst, dstRowByt es * | |
| 433 get_output_row_interlaced(y, innerHeight )); | |
| 434 swizzler->swizzle(dstRow, buffer.get()); | |
| 435 } | |
| 436 return gif_error(SkStringPrintf( | |
| 437 "Could not decode line %d of %d.\n", | |
| 438 y, height - 1).c_str(), kIncompleteInput); | |
| 439 } | |
| 440 void* dstRow = SkTAddOffset<void>(dst, | |
| 441 dstRowBytes * get_output_row_interlaced(y, inner Height)); | |
| 442 swizzler->swizzle(dstRow, buffer.get()); | |
| 443 } | |
| 444 } else { | |
| 445 // Standard mode | |
| 446 void* dstRow = dst; | |
| 447 for (int32_t y = 0; y < innerHeight; y++) { | |
| 448 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), innerWi dth)) { | |
| 449 SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes, inner Height - y, | |
| 450 fillIndex, colorTable, zeroInit); | |
| 451 return gif_error(SkStringPrintf( | |
| 452 "Could not decode line %d of %d.\n", | |
| 453 y, height - 1).c_str(), kIncompleteInput); | |
| 454 } | |
| 455 swizzler->swizzle(dstRow, buffer.get()); | |
| 456 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
| 457 } | |
| 458 } | |
| 459 | |
| 460 // FIXME: Gif files may have multiple images stored in a single | 260 // FIXME: Gif files may have multiple images stored in a single |
| 461 // file. This is most commonly used to enable | 261 // file. This is most commonly used to enable |
| 462 // animations. Since we are leaving animated gifs as a | 262 // animations. Since we are leaving animated gifs as a |
| 463 // TODO, we will return kSuccess after decoding the | 263 // TODO, we will return kSuccess after decoding the |
| 464 // first image in the file. This is the same behavior | 264 // first image in the file. This is the same behavior |
| 465 // as SkImageDecoder_libgif. | 265 // as SkImageDecoder_libgif. |
| 466 // | 266 // |
| 467 // Most times this works pretty well, but sometimes it | 267 // Most times this works pretty well, but sometimes it |
| 468 // doesn't. For example, I have an animated test image | 268 // doesn't. For example, I have an animated test image |
| 469 // where the first image in the file is 1x1, but the | 269 // where the first image in the file is 1x1, but the |
| 470 // subsequent images are meaningful. This currently | 270 // subsequent images are meaningful. This currently |
| 471 // displays the 1x1 image, which is not ideal. Right | 271 // displays the 1x1 image, which is not ideal. Right |
| 472 // now I am leaving this as an issue that will be | 272 // now I am leaving this as an issue that will be |
| 473 // addressed when we implement animated gifs. | 273 // addressed when we implement animated gifs. |
| 474 // | 274 // |
| 475 // It is also possible (not explicitly disallowed in the | 275 // It is also possible (not explicitly disallowed in the |
| 476 // specification) that gif files provide multiple | 276 // specification) that gif files provide multiple |
| 477 // images in a single file that are all meant to be | 277 // images in a single file that are all meant to be |
| 478 // displayed in the same frame together. I will | 278 // displayed in the same frame together. I will |
| 479 // currently leave this unimplemented until I find a | 279 // currently leave this unimplemented until I find a |
| 480 // test case that expects this behavior. | 280 // test case that expects this behavior. |
| 481 return kSuccess; | 281 return kSuccess; |
| 482 } | 282 } |
| 483 | |
| 484 // Extensions are used to specify special properties of the image | 283 // Extensions are used to specify special properties of the image |
| 485 // such as transparency or animation. | 284 // such as transparency or animation. |
| 486 case EXTENSION_RECORD_TYPE: | 285 case EXTENSION_RECORD_TYPE: |
| 487 // Read extension data | 286 // Read extension data |
| 488 if (GIF_ERROR == DGifGetExtension(fGif, &extFunction, &extData)) { | 287 if (GIF_ERROR == DGifGetExtension(fGif, &extFunction, &extData)) { |
| 489 return gif_error("Could not get extension.\n", kIncompleteIn put); | 288 return gif_error("Could not get extension.\n", kIncompleteIn put); |
| 490 } | 289 } |
| 491 | 290 |
| 492 // Create an extension block with our data | 291 // Create an extension block with our data |
| 493 while (nullptr != extData) { | 292 while (nullptr != extData) { |
| 494 // Add a single block | 293 // Add a single block |
| 495 if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBloc kCount, | 294 if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBloc kCount, |
| 496 &saveExt.ExtensionBloc ks, | 295 &saveExt.ExtensionBloc ks, |
| 497 extFunction, extData[0 ], &extData[1])) | 296 extFunction, extData[0 ], &extData[1])) |
| 498 { | 297 { |
| 499 return gif_error("Could not add extension block.\n", kIn completeInput); | 298 return gif_error("Could not add extension block.\n", kIn completeInput); |
| 500 } | 299 } |
| 501 // Move to the next block | 300 // Move to the next block |
| 502 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { | 301 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { |
| 503 return gif_error("Could not get next extension.\n", kInc ompleteInput); | 302 return gif_error("Could not get next extension.\n", kInc ompleteInput); |
| 504 } | 303 } |
| 505 } | 304 } |
| 506 break; | 305 break; |
| 507 | 306 |
| 508 // Signals the end of the gif file | 307 // Signals the end of the gif file |
| 509 case TERMINATE_RECORD_TYPE: | 308 case TERMINATE_RECORD_TYPE: |
| 510 break; | 309 break; |
| 511 | 310 |
| 512 default: | 311 default: |
| 513 // giflib returns an error code if the record type is not known. | 312 // DGifGetRecordType returns an error if the record type does |
| 514 // We should catch this error immediately. | 313 // not match one of the above cases. This should not be |
| 314 // reached. | |
| 515 SkASSERT(false); | 315 SkASSERT(false); |
| 516 break; | 316 break; |
| 517 } | 317 } |
| 518 } while (TERMINATE_RECORD_TYPE != recordType); | 318 } while (TERMINATE_RECORD_TYPE != recordType); |
| 519 | 319 |
| 520 return gif_error("Could not find any images to decode in gif file.\n", kInva lidInput); | 320 return gif_error("Could not find any images to decode in gif file.\n", kInva lidInput); |
| 521 } | 321 } |
| 322 | |
| 323 /* | |
| 324 * A gif may contain many image frames, all of different sizes. | |
| 325 * This function checks if the frame dimensions are valid and corrects them if | |
| 326 * necessary. | |
| 327 */ | |
| 328 bool SkGifCodec::setFrameDimensions(const GifImageDesc& desc) { | |
| 329 // Fail on non-positive dimensions | |
| 330 int32_t frameLeft = desc.Left; | |
| 331 int32_t frameTop = desc.Top; | |
| 332 int32_t frameWidth = desc.Width; | |
| 333 int32_t frameHeight = desc.Height; | |
| 334 int32_t height = this->getInfo().height(); | |
| 335 int32_t width = this->getInfo().width(); | |
| 336 if (frameWidth <= 0 || frameHeight <= 0) { | |
| 337 return false; | |
| 338 } | |
| 339 | |
| 340 // Treat the following cases as warnings and try to fix | |
| 341 if (frameWidth > width) { | |
| 342 gif_warning("Image frame too wide, shrinking.\n"); | |
| 343 frameWidth = width; | |
| 344 frameLeft = 0; | |
| 345 } else if (frameLeft + frameWidth > width) { | |
| 346 gif_warning("Shifting image frame to left to fit.\n"); | |
| 347 frameLeft = width - frameWidth; | |
| 348 } else if (frameLeft < 0) { | |
| 349 gif_warning("Shifting image frame to right to fit\n"); | |
| 350 frameLeft = 0; | |
| 351 } | |
| 352 if (frameHeight > height) { | |
| 353 gif_warning("Image frame too tall, shrinking.\n"); | |
| 354 frameHeight = height; | |
| 355 frameTop = 0; | |
| 356 } else if (frameTop + frameHeight > height) { | |
| 357 gif_warning("Shifting image frame up to fit.\n"); | |
| 358 frameTop = height - frameHeight; | |
| 359 } else if (frameTop < 0) { | |
| 360 gif_warning("Shifting image frame down to fit\n"); | |
| 361 frameTop = 0; | |
| 362 } | |
| 363 fFrameDims.setXYWH(frameLeft, frameTop, frameWidth, frameHeight); | |
| 364 | |
| 365 // Indicate if the frame dimensions do not match the header dimensions | |
| 366 if (this->getInfo().width() != fFrameDims.width() || | |
|
scroggo
2015/09/02 22:45:48
I think you can simplify this to
if (fFrameDims.s
msarett
2015/09/03 17:13:31
+1
| |
| 367 this->getInfo().height() != fFrameDims.height()) { | |
| 368 fFrameIsSubset = true; | |
| 369 } | |
| 370 | |
| 371 return true; | |
| 372 } | |
| 373 | |
| 374 void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* inp utColorPtr, | |
| 375 int* inputColorCount, uint32_t transIndex) { | |
| 376 // Set up our own color table | |
| 377 const uint32_t maxColors = 256; | |
| 378 SkPMColor colorPtr[256]; | |
| 379 if (NULL != inputColorCount) { | |
| 380 // We set the number of colors to maxColors in order to ensure | |
| 381 // safe memory accesses. Otherwise, an invalid pixel could | |
| 382 // access memory outside of our color table array. | |
| 383 *inputColorCount = maxColors; | |
| 384 } | |
| 385 | |
| 386 // Get local color table | |
| 387 ColorMapObject* colorMap = fGif->Image.ColorMap; | |
| 388 // If there is no local color table, use the global color table | |
| 389 if (NULL == colorMap) { | |
| 390 colorMap = fGif->SColorMap; | |
| 391 } | |
| 392 | |
| 393 uint32_t colorCount = 0; | |
| 394 if (NULL != colorMap) { | |
| 395 colorCount = colorMap->ColorCount; | |
| 396 // giflib guarantees these properties | |
| 397 SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPixel))); | |
| 398 SkASSERT(colorCount <= 256); | |
| 399 for (uint32_t i = 0; i < colorCount; i++) { | |
| 400 colorPtr[i] = SkPackARGB32(0xFF, colorMap->Colors[i].Red, | |
| 401 colorMap->Colors[i].Green, colorMap->Colors[i].Blue); | |
| 402 } | |
| 403 } | |
| 404 | |
| 405 // This is used to fill unspecified pixels in the image data. | |
| 406 uint32_t fillIndex = fGif->SBackGroundColor; | |
| 407 | |
| 408 // Gifs have the option to specify the color at a single index of the color | |
| 409 // table as transparent. If the transparent index is greater than the | |
| 410 // colorCount, we know that there is no valid transparent color in the | |
| 411 // color table. This occurs if there is no graphics control extension or | |
| 412 // if the index specified by the graphics control extension is out of range. | |
| 413 if (transIndex < colorCount) { | |
| 414 colorPtr[transIndex] = SK_ColorTRANSPARENT; | |
| 415 // If there is a transparent index, we also use this as the fill index. | |
| 416 fillIndex = transIndex; | |
| 417 } else if (fillIndex >= colorCount) { | |
| 418 // If the fill index is invalid, we default to 0. This behavior is | |
| 419 // unspecified but matches SkImageDecoder. | |
| 420 fillIndex = 0; | |
| 421 } | |
| 422 | |
| 423 // Fill in the color table for indices greater than color count. | |
| 424 // This allows for predictable, safe behavior. | |
| 425 for (uint32_t i = colorCount; i < maxColors; i++) { | |
| 426 colorPtr[i] = colorPtr[fillIndex]; | |
| 427 } | |
| 428 | |
| 429 fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorPtr, maxColors))); | |
|
scroggo
2015/09/02 22:45:48
nit: new
msarett
2015/09/03 17:13:31
Done.
| |
| 430 copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount) ; | |
| 431 fFillIndex = fillIndex; | |
| 432 } | |
| 433 | |
| 434 SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, | |
| 435 ZeroInitialized zeroInit) { | |
| 436 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | |
| 437 fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, | |
| 438 colorPtr, dstInfo, zeroInit, this->getInfo())); | |
| 439 if (NULL != fSwizzler.get()) { | |
|
scroggo
2015/09/02 22:45:48
nit: nullptr
msarett
2015/09/03 17:13:31
Done.
| |
| 440 return kSuccess; | |
| 441 } | |
| 442 return kUnimplemented; | |
| 443 } | |
| 444 | |
| 445 SkCodec::Result SkGifCodec::readRow() { | |
| 446 if (GIF_ERROR == DGifGetLine(fGif, fSrcBuffer.get(), fFrameDims.width())) { | |
| 447 return kIncompleteInput; | |
| 448 } | |
| 449 return kSuccess; | |
| 450 } | |
| 451 | |
| 452 /* | |
| 453 * Initiates the gif decode | |
| 454 */ | |
| 455 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, | |
| 456 void* dst, size_t dstRowBytes, | |
| 457 const Options& opts, | |
| 458 SkPMColor* inputColorPtr, | |
| 459 int* inputColorCount) { | |
| 460 // Rewind if necessary | |
| 461 if (!this->rewindIfNeeded()) { | |
| 462 return kCouldNotRewind; | |
| 463 } | |
| 464 | |
| 465 // Check for valid input parameters | |
| 466 if (opts.fSubset) { | |
| 467 // Subsets are not supported. | |
| 468 return kUnimplemented; | |
| 469 } | |
| 470 if (dstInfo.dimensions() != this->getInfo().dimensions()) { | |
| 471 return gif_error("Scaling not supported.\n", kInvalidScale); | |
| 472 } | |
| 473 if (!conversion_possible(dstInfo, this->getInfo())) { | |
| 474 return gif_error("Cannot convert input type to output type.\n", | |
| 475 kInvalidConversion); | |
| 476 } | |
| 477 | |
| 478 // Read through gif extensions to get to the image data. Set the | |
| 479 // transparent index based on the extension data. | |
| 480 uint32_t transIndex; | |
| 481 SkCodec::Result result = this->readUpToFirstImage(&transIndex); | |
| 482 if (kSuccess != result){ | |
| 483 return result; | |
| 484 } | |
| 485 | |
| 486 // Read the image descriptor | |
| 487 if (GIF_ERROR == DGifGetImageDesc(fGif)) { | |
| 488 return gif_error("DGifGetImageDesc failed.\n", kInvalidInput); | |
| 489 } | |
| 490 | |
| 491 // If reading the image descriptor is successful, the image count will be | |
| 492 // incremented | |
| 493 SkASSERT(fGif->ImageCount >= 1); | |
| 494 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1]; | |
| 495 const GifImageDesc& desc = image->ImageDesc; | |
| 496 | |
| 497 // Check that the frame dimensions are valid and set them | |
| 498 if(!this->setFrameDimensions(desc)) { | |
| 499 return gif_error("Invalid dimensions for image frame.\n", kInvalidInput) ; | |
| 500 } | |
| 501 | |
| 502 // Initialize color table and copy to the client if necessary | |
| 503 this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount, transInd ex); | |
| 504 | |
| 505 // Initialize the swizzler | |
| 506 if (fFrameIsSubset) { | |
| 507 const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameDims.width(), fFr ameDims.height()); | |
| 508 if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitia lized)) { | |
| 509 return gif_error("Could not initialize swizzler.\n", kUnimplemented) ; | |
| 510 } | |
| 511 | |
| 512 // Fill the background | |
| 513 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | |
| 514 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, this->getInfo().height(), | |
| 515 fFillIndex, colorPtr, opts.fZeroInitialized); | |
| 516 | |
| 517 // Modify the dst pointer | |
| 518 const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorT ype()); | |
| 519 dst = SkTAddOffset<void*>(dst, dstRowBytes * fFrameDims.top() + | |
| 520 dstBytesPerPixel * fFrameDims.left()); | |
| 521 } else { | |
| 522 if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized) ) { | |
| 523 return gif_error("Could not initialize swizzler.\n", kUnimplemented) ; | |
| 524 } | |
| 525 } | |
| 526 | |
| 527 // Check the interlace flag and iterate over rows of the input | |
| 528 uint32_t width = fFrameDims.width(); | |
| 529 uint32_t height = fFrameDims.height(); | |
| 530 if (fGif->Image.Interlace) { | |
| 531 // In interlace mode, the rows of input are rearranged in | |
| 532 // the output image. We use an iterator to take care of | |
|
scroggo
2015/09/02 22:45:48
no longer use an iterator
msarett
2015/09/03 17:13:31
Thanks, will change the comment.
| |
| 533 // the rearranging. | |
| 534 for (int32_t y = 0; y < height; y++) { | |
| 535 if (kSuccess != this->readRow()) { | |
| 536 // Recover from error by filling remainder of image | |
| 537 memset(fSrcBuffer.get(), fFillIndex, width); | |
| 538 for (; y < height; y++) { | |
| 539 void* dstRow = SkTAddOffset<void>(dst, | |
| 540 dstRowBytes * get_output_row_interlaced(y, height)); | |
| 541 fSwizzler->swizzle(dstRow, fSrcBuffer.get()); | |
| 542 } | |
| 543 return gif_error("Could not decode line.\n", kIncompleteInput); | |
| 544 } | |
| 545 void* dstRow = SkTAddOffset<void>(dst, | |
| 546 dstRowBytes * get_output_row_interlaced(y, height)); | |
| 547 fSwizzler->swizzle(dstRow, fSrcBuffer.get()); | |
| 548 } | |
| 549 } else { | |
| 550 // Standard mode | |
| 551 void* dstRow = dst; | |
| 552 for (int32_t y = 0; y < height; y++) { | |
| 553 if (kSuccess != this->readRow()) { | |
| 554 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | |
| 555 SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes, | |
| 556 height - y, fFillIndex, colorPtr, opts.fZeroInitialized) ; | |
| 557 return gif_error("Could not decode line\n", kIncompleteInput); | |
| 558 } | |
| 559 fSwizzler->swizzle(dstRow, fSrcBuffer.get()); | |
| 560 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
| 561 } | |
| 562 } | |
| 563 return kSuccess; | |
| 564 } | |
| 565 | |
| 566 // TODO (msarett): skbug.com/3582 | |
| 567 // Should we implement reallyHasAlpha? Or should we read extens ion blocks in the | |
| 568 // header? Or should we do both? | |
| 569 | |
| 570 class SkGifScanlineDecoder : public SkScanlineDecoder { | |
| 571 public: | |
| 572 SkGifScanlineDecoder(const SkImageInfo& srcInfo, SkGifCodec* codec) | |
| 573 : INHERITED(srcInfo) | |
| 574 , fCodec(codec) | |
| 575 {} | |
| 576 | |
| 577 SkEncodedFormat onGetEncodedFormat() const override { | |
| 578 return kGIF_SkEncodedFormat; | |
| 579 } | |
| 580 | |
| 581 SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& opts, | |
| 582 SkPMColor inputColorPtr[], int* inputColorCount) ove rride { | |
| 583 | |
| 584 // Rewind if necessary | |
| 585 if (!fCodec->rewindIfNeeded()) { | |
| 586 return SkCodec::kCouldNotRewind; | |
| 587 } | |
| 588 // Check for valid input parameters | |
| 589 if (opts.fSubset) { | |
| 590 // Subsets are not supported. | |
| 591 return SkCodec::kUnimplemented; | |
| 592 } | |
| 593 // Check to see if scaling was requested. | |
| 594 if (dstInfo.dimensions() != this->getInfo().dimensions()) { | |
| 595 if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { | |
| 596 return gif_error("Scaling not supported.\n", SkCodec::kInvalidSc ale); | |
| 597 } | |
| 598 } | |
| 599 if (!conversion_possible(dstInfo, this->getInfo())) { | |
| 600 return gif_error("Cannot convert input type to output type.\n", | |
| 601 SkCodec::kInvalidConversion); | |
| 602 } | |
| 603 | |
| 604 // Read through gif extensions to get to the image data | |
|
scroggo
2015/09/02 22:45:48
A lot of this code looks similar to code in onGetP
msarett
2015/09/03 17:13:30
This has been moved to ReadHeader(), so now it's s
| |
| 605 uint32_t transIndex; | |
| 606 SkCodec::Result result = fCodec->readUpToFirstImage(&transIndex); | |
| 607 if (SkCodec::kSuccess != result){ | |
| 608 return result; | |
| 609 } | |
| 610 | |
| 611 // Read the image descriptor | |
| 612 if (GIF_ERROR == DGifGetImageDesc(fCodec->fGif)) { | |
| 613 return gif_error("DGifGetImageDesc failed.\n", SkCodec::kInvalidInpu t); | |
| 614 } | |
| 615 | |
| 616 // If reading the image descriptor is successful, the image count will b e | |
| 617 // incremented | |
| 618 SkASSERT(fCodec->fGif->ImageCount >= 1); | |
| 619 SavedImage* image = &fCodec->fGif->SavedImages[fCodec->fGif->ImageCount - 1]; | |
| 620 const GifImageDesc& desc = image->ImageDesc; | |
| 621 | |
| 622 // Check that the frame dimensions are valid and set them | |
| 623 if(!fCodec->setFrameDimensions(desc)) { | |
| 624 return gif_error("Invalid dimensions for image frame.\n", SkCodec::k InvalidInput); | |
| 625 } | |
| 626 | |
| 627 // Initialize color table and copy to the client if necessary | |
| 628 fCodec->initializeColorTable(dstInfo, inputColorPtr, inputColorCount, tr ansIndex); | |
| 629 | |
| 630 // Initialize the swizzler | |
| 631 if (fCodec->fFrameIsSubset) { | |
| 632 int sampleX; | |
| 633 SkScaledCodec::ComputeSampleSize(dstInfo, fCodec->getInfo(), &sample X, NULL); | |
| 634 const SkImageInfo subsetDstInfo = dstInfo.makeWH( | |
| 635 get_scaled_dimension(fCodec->fFrameDims.width(), sampleX), | |
| 636 fCodec->fFrameDims.height()); | |
| 637 if (SkCodec::kSuccess != fCodec->initializeSwizzler(subsetDstInfo, | |
| 638 opts.fZeroInitialized)) { | |
| 639 return gif_error("Could not initialize swizzler.\n", SkCodec::kU nimplemented); | |
| 640 } | |
| 641 } else { | |
| 642 if (SkCodec::kSuccess != fCodec->initializeSwizzler(dstInfo, opts.fZ eroInitialized)) { | |
| 643 return gif_error("Could not initialize swizzler.\n", SkCodec::kU nimplemented); | |
| 644 } | |
| 645 } | |
| 646 | |
| 647 return SkCodec::kSuccess; | |
| 648 } | |
| 649 | |
| 650 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) overri de { | |
| 651 if (fCodec->fFrameIsSubset) { | |
| 652 // Fill the requested rows | |
| 653 const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.get()) ; | |
| 654 SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count, fCodec->fFil lIndex, | |
| 655 colorPtr, this->options().fZeroInitialized); | |
| 656 | |
| 657 // Do nothing for rows before the image frame | |
| 658 int rowsBeforeFrame = fCodec->fFrameDims.top() - INHERITED::getY(); | |
| 659 if (rowsBeforeFrame > 0) { | |
| 660 count = SkTMin(0, count - rowsBeforeFrame); | |
| 661 dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame); | |
| 662 } | |
| 663 | |
| 664 // Do nothing for rows after the image frame | |
| 665 int rowsAfterFrame = INHERITED::getY() + count - fCodec->fFrameDims. bottom(); | |
| 666 if (rowsAfterFrame > 0) { | |
| 667 count = SkTMin(0, count - rowsAfterFrame); | |
| 668 } | |
| 669 | |
| 670 // Adjust dst pointer for left offset | |
| 671 dst = SkTAddOffset<void>(dst, SkColorTypeBytesPerPixel( | |
| 672 this->dstInfo().colorType()) * fCodec->fFrameDims.left()); | |
| 673 } | |
| 674 | |
| 675 for (int i = 0; i < count; i++) { | |
| 676 if (SkCodec::kSuccess != fCodec->readRow()) { | |
| 677 const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.ge t()); | |
| 678 SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, | |
| 679 count - i, fCodec->fFillIndex, colorPtr, | |
| 680 this->options().fZeroInitialized); | |
| 681 return gif_error("Could not decode line\n", SkCodec::kIncomplete Input); | |
| 682 } | |
| 683 fCodec->fSwizzler->swizzle(dst, fCodec->fSrcBuffer.get()); | |
| 684 dst = SkTAddOffset<void>(dst, rowBytes); | |
| 685 } | |
| 686 return SkCodec::kSuccess; | |
| 687 } | |
| 688 | |
| 689 SkScanlineOrder onGetScanlineOrder() const override { | |
| 690 if (fCodec->fGif->Image.Interlace) { | |
| 691 return kOutOfOrder_SkScanlineOrder; | |
| 692 } else { | |
| 693 return kTopDown_SkScanlineOrder; | |
| 694 } | |
| 695 } | |
| 696 | |
| 697 int onGetY() const override { | |
| 698 if (fCodec->fGif->Image.Interlace) { | |
| 699 return get_output_row_interlaced(INHERITED::onGetY(), this->dstInfo ().height()); | |
|
scroggo
2015/09/02 22:45:48
nit: extra space here
msarett
2015/09/03 17:13:31
Done.
| |
| 700 } else { | |
| 701 return INHERITED::onGetY(); | |
| 702 } | |
| 703 } | |
| 704 | |
| 705 private: | |
| 706 SkAutoTDelete<SkGifCodec> fCodec; | |
| 707 | |
| 708 typedef SkScanlineDecoder INHERITED; | |
| 709 }; | |
| 710 | |
| 711 SkScanlineDecoder* SkGifCodec::NewSDFromStream(SkStream* stream) { | |
| 712 SkAutoTDelete<SkGifCodec> codec (static_cast<SkGifCodec*>(SkGifCodec::NewFro mStream(stream))); | |
| 713 if (!codec) { | |
| 714 return NULL; | |
| 715 } | |
| 716 | |
| 717 const SkImageInfo& srcInfo = codec->getInfo(); | |
| 718 | |
| 719 return SkNEW_ARGS(SkGifScanlineDecoder, (srcInfo, codec.detach())); | |
| 720 } | |
| OLD | NEW |