| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2015 Google Inc. | |
| 3 * | |
| 4 * Use of this source code is governed by a BSD-style license that can be | |
| 5 * found in the LICENSE file. | |
| 6 */ | |
| 7 | |
| 8 #include "SkCodec_libgif.h" | |
| 9 #include "SkCodecPriv.h" | |
| 10 #include "SkColorPriv.h" | |
| 11 #include "SkColorTable.h" | |
| 12 #include "SkStream.h" | |
| 13 #include "SkSwizzler.h" | |
| 14 #include "SkUtils.h" | |
| 15 | |
| 16 /* | |
| 17 * Checks the start of the stream to see if the image is a gif | |
| 18 */ | |
| 19 bool SkGifCodec::IsGif(const void* buf, size_t bytesRead) { | |
| 20 if (bytesRead >= GIF_STAMP_LEN) { | |
| 21 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || | |
| 22 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || | |
| 23 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) | |
| 24 { | |
| 25 return true; | |
| 26 } | |
| 27 } | |
| 28 return false; | |
| 29 } | |
| 30 | |
| 31 /* | |
| 32 * Error function | |
| 33 */ | |
| 34 static SkCodec::Result gif_error(const char* msg, SkCodec::Result result = SkCod
ec::kInvalidInput) { | |
| 35 SkCodecPrintf("Gif Error: %s\n", msg); | |
| 36 return result; | |
| 37 } | |
| 38 | |
| 39 | |
| 40 /* | |
| 41 * Read function that will be passed to gif_lib | |
| 42 */ | |
| 43 static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, int3
2_t size) { | |
| 44 SkStream* stream = (SkStream*) fileType->UserData; | |
| 45 return (int32_t) stream->read(out, size); | |
| 46 } | |
| 47 | |
| 48 /* | |
| 49 * Open the gif file | |
| 50 */ | |
| 51 static GifFileType* open_gif(SkStream* stream) { | |
| 52 return DGifOpen(stream, read_bytes_callback, nullptr); | |
| 53 } | |
| 54 | |
| 55 /* | |
| 56 * Check if a there is an index of the color table for a transparent pixel | |
| 57 */ | |
| 58 static uint32_t find_trans_index(const SavedImage& image) { | |
| 59 // If there is a transparent index specified, it will be contained in an | |
| 60 // extension block. We will loop through extension blocks in reverse order | |
| 61 // to check the most recent extension blocks first. | |
| 62 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) { | |
| 63 // Get an extension block | |
| 64 const ExtensionBlock& extBlock = image.ExtensionBlocks[i]; | |
| 65 | |
| 66 // Specifically, we need to check for a graphics control extension, | |
| 67 // which may contain transparency information. Also, note that a valid | |
| 68 // graphics control extension is always four bytes. The fourth byte | |
| 69 // is the transparent index (if it exists), so we need at least four | |
| 70 // bytes. | |
| 71 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >=
4) { | |
| 72 // Check the transparent color flag which indicates whether a | |
| 73 // transparent index exists. It is the least significant bit of | |
| 74 // the first byte of the extension block. | |
| 75 if (1 == (extBlock.Bytes[0] & 1)) { | |
| 76 // Use uint32_t to prevent sign extending | |
| 77 return extBlock.Bytes[3]; | |
| 78 } | |
| 79 | |
| 80 // There should only be one graphics control extension for the image
frame | |
| 81 break; | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 // Use maximum unsigned int (surely an invalid index) to indicate that a val
id | |
| 86 // index was not found. | |
| 87 return SK_MaxU32; | |
| 88 } | |
| 89 | |
| 90 inline uint32_t ceil_div(uint32_t a, uint32_t b) { | |
| 91 return (a + b - 1) / b; | |
| 92 } | |
| 93 | |
| 94 /* | |
| 95 * Gets the output row corresponding to the encoded row for interlaced gifs | |
| 96 */ | |
| 97 inline uint32_t get_output_row_interlaced(uint32_t encodedRow, uint32_t height)
{ | |
| 98 SkASSERT(encodedRow < height); | |
| 99 // First pass | |
| 100 if (encodedRow * 8 < height) { | |
| 101 return encodedRow * 8; | |
| 102 } | |
| 103 // Second pass | |
| 104 if (encodedRow * 4 < height) { | |
| 105 return 4 + 8 * (encodedRow - ceil_div(height, 8)); | |
| 106 } | |
| 107 // Third pass | |
| 108 if (encodedRow * 2 < height) { | |
| 109 return 2 + 4 * (encodedRow - ceil_div(height, 4)); | |
| 110 } | |
| 111 // Fourth pass | |
| 112 return 1 + 2 * (encodedRow - ceil_div(height, 2)); | |
| 113 } | |
| 114 | |
| 115 /* | |
| 116 * This function cleans up the gif object after the decode completes | |
| 117 * It is used in a SkAutoTCallIProc template | |
| 118 */ | |
| 119 void SkGifCodec::CloseGif(GifFileType* gif) { | |
| 120 DGifCloseFile(gif, NULL); | |
| 121 } | |
| 122 | |
| 123 /* | |
| 124 * This function free extension data that has been saved to assist the image | |
| 125 * decoder | |
| 126 */ | |
| 127 void SkGifCodec::FreeExtension(SavedImage* image) { | |
| 128 if (NULL != image->ExtensionBlocks) { | |
| 129 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 /* | |
| 134 * Read enough of the stream to initialize the SkGifCodec. | |
| 135 * Returns a bool representing success or failure. | |
| 136 * | |
| 137 * @param codecOut | |
| 138 * If it returned true, and codecOut was not nullptr, | |
| 139 * codecOut will be set to a new SkGifCodec. | |
| 140 * | |
| 141 * @param gifOut | |
| 142 * If it returned true, and codecOut was nullptr, | |
| 143 * gifOut must be non-nullptr and gifOut will be set to a new | |
| 144 * GifFileType pointer. | |
| 145 * | |
| 146 * @param stream | |
| 147 * Deleted on failure. | |
| 148 * codecOut will take ownership of it in the case where we created a codec. | |
| 149 * Ownership is unchanged when we returned a gifOut. | |
| 150 * | |
| 151 */ | |
| 152 bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType**
gifOut) { | |
| 153 SkAutoTDelete<SkStream> streamDeleter(stream); | |
| 154 | |
| 155 // Read gif header, logical screen descriptor, and global color table | |
| 156 SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); | |
| 157 | |
| 158 if (nullptr == gif) { | |
| 159 gif_error("DGifOpen failed.\n"); | |
| 160 return false; | |
| 161 } | |
| 162 | |
| 163 // Read through gif extensions to get to the image data. Set the | |
| 164 // transparent index based on the extension data. | |
| 165 uint32_t transIndex; | |
| 166 SkCodec::Result result = ReadUpToFirstImage(gif, &transIndex); | |
| 167 if (kSuccess != result){ | |
| 168 return false; | |
| 169 } | |
| 170 | |
| 171 // Read the image descriptor | |
| 172 if (GIF_ERROR == DGifGetImageDesc(gif)) { | |
| 173 return false; | |
| 174 } | |
| 175 // If reading the image descriptor is successful, the image count will be | |
| 176 // incremented. | |
| 177 SkASSERT(gif->ImageCount >= 1); | |
| 178 | |
| 179 if (nullptr != codecOut) { | |
| 180 SkISize size; | |
| 181 SkIRect frameRect; | |
| 182 if (!GetDimensions(gif, &size, &frameRect)) { | |
| 183 gif_error("Invalid gif size.\n"); | |
| 184 return false; | |
| 185 } | |
| 186 bool frameIsSubset = (size != frameRect.size()); | |
| 187 | |
| 188 // Determine the recommended alpha type. The transIndex might be valid
if it less | |
| 189 // than 256. We are not certain that the index is valid until we proces
s the color | |
| 190 // table, since some gifs have color tables with less than 256 colors.
If | |
| 191 // there might be a valid transparent index, we must indicate that the i
mage has | |
| 192 // alpha. | |
| 193 // In the case where we must support alpha, we have the option to set th
e | |
| 194 // suggested alpha type to kPremul or kUnpremul. Both are valid since t
he alpha | |
| 195 // component will always be 0xFF or the entire 32-bit pixel will be set
to zero. | |
| 196 // We prefer kPremul because we support kPremul, and it is more efficien
t to use | |
| 197 // kPremul directly even when kUnpremul is supported. | |
| 198 SkAlphaType alphaType = (transIndex < 256) ? kPremul_SkAlphaType : kOpaq
ue_SkAlphaType; | |
| 199 | |
| 200 // Return the codec | |
| 201 // kIndex is the most natural color type for gifs, so we set this as | |
| 202 // the default. | |
| 203 SkImageInfo imageInfo = SkImageInfo::Make(size.width(), size.height(), k
Index_8_SkColorType, | |
| 204 alphaType); | |
| 205 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach
(), transIndex, | |
| 206 frameRect, frameIsSubset); | |
| 207 } else { | |
| 208 SkASSERT(nullptr != gifOut); | |
| 209 streamDeleter.detach(); | |
| 210 *gifOut = gif.detach(); | |
| 211 } | |
| 212 return true; | |
| 213 } | |
| 214 | |
| 215 /* | |
| 216 * Assumes IsGif was called and returned true | |
| 217 * Creates a gif decoder | |
| 218 * Reads enough of the stream to determine the image format | |
| 219 */ | |
| 220 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { | |
| 221 SkCodec* codec = nullptr; | |
| 222 if (ReadHeader(stream, &codec, nullptr)) { | |
| 223 return codec; | |
| 224 } | |
| 225 return nullptr; | |
| 226 } | |
| 227 | |
| 228 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType
* gif, | |
| 229 uint32_t transIndex, const SkIRect& frameRect, bool frameIsSubset) | |
| 230 : INHERITED(srcInfo, stream) | |
| 231 , fGif(gif) | |
| 232 , fSrcBuffer(new uint8_t[this->getInfo().width()]) | |
| 233 , fFrameRect(frameRect) | |
| 234 // If it is valid, fTransIndex will be used to set fFillIndex. We don't kno
w if | |
| 235 // fTransIndex is valid until we process the color table, since fTransIndex
may | |
| 236 // be greater than the size of the color table. | |
| 237 , fTransIndex(transIndex) | |
| 238 // Default fFillIndex is 0. We will overwrite this if fTransIndex is valid,
or if | |
| 239 // there is a valid background color. | |
| 240 , fFillIndex(0) | |
| 241 , fFrameIsSubset(frameIsSubset) | |
| 242 , fSwizzler(NULL) | |
| 243 , fColorTable(NULL) | |
| 244 {} | |
| 245 | |
| 246 bool SkGifCodec::onRewind() { | |
| 247 GifFileType* gifOut = nullptr; | |
| 248 if (!ReadHeader(this->stream(), nullptr, &gifOut)) { | |
| 249 return false; | |
| 250 } | |
| 251 | |
| 252 SkASSERT(nullptr != gifOut); | |
| 253 fGif.reset(gifOut); | |
| 254 return true; | |
| 255 } | |
| 256 | |
| 257 SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* trans
Index) { | |
| 258 // Use this as a container to hold information about any gif extension | |
| 259 // blocks. This generally stores transparency and animation instructions. | |
| 260 SavedImage saveExt; | |
| 261 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); | |
| 262 saveExt.ExtensionBlocks = nullptr; | |
| 263 saveExt.ExtensionBlockCount = 0; | |
| 264 GifByteType* extData; | |
| 265 int32_t extFunction; | |
| 266 | |
| 267 // 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 | |
| 269 // contain more than one image, we will simply decode the first image. | |
| 270 GifRecordType recordType; | |
| 271 do { | |
| 272 // Get the current record type | |
| 273 if (GIF_ERROR == DGifGetRecordType(gif, &recordType)) { | |
| 274 return gif_error("DGifGetRecordType failed.\n", kInvalidInput); | |
| 275 } | |
| 276 switch (recordType) { | |
| 277 case IMAGE_DESC_RECORD_TYPE: { | |
| 278 *transIndex = find_trans_index(saveExt); | |
| 279 | |
| 280 // FIXME: Gif files may have multiple images stored in a single | |
| 281 // file. This is most commonly used to enable | |
| 282 // animations. Since we are leaving animated gifs as a | |
| 283 // TODO, we will return kSuccess after decoding the | |
| 284 // first image in the file. This is the same behavior | |
| 285 // as SkImageDecoder_libgif. | |
| 286 // | |
| 287 // Most times this works pretty well, but sometimes it | |
| 288 // doesn't. For example, I have an animated test image | |
| 289 // where the first image in the file is 1x1, but the | |
| 290 // subsequent images are meaningful. This currently | |
| 291 // displays the 1x1 image, which is not ideal. Right | |
| 292 // now I am leaving this as an issue that will be | |
| 293 // addressed when we implement animated gifs. | |
| 294 // | |
| 295 // It is also possible (not explicitly disallowed in the | |
| 296 // specification) that gif files provide multiple | |
| 297 // images in a single file that are all meant to be | |
| 298 // displayed in the same frame together. I will | |
| 299 // currently leave this unimplemented until I find a | |
| 300 // test case that expects this behavior. | |
| 301 return kSuccess; | |
| 302 } | |
| 303 // Extensions are used to specify special properties of the image | |
| 304 // such as transparency or animation. | |
| 305 case EXTENSION_RECORD_TYPE: | |
| 306 // Read extension data | |
| 307 if (GIF_ERROR == DGifGetExtension(gif, &extFunction, &extData))
{ | |
| 308 return gif_error("Could not get extension.\n", kIncompleteIn
put); | |
| 309 } | |
| 310 | |
| 311 // Create an extension block with our data | |
| 312 while (nullptr != extData) { | |
| 313 // Add a single block | |
| 314 if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBloc
kCount, | |
| 315 &saveExt.ExtensionBloc
ks, | |
| 316 extFunction, extData[0
], &extData[1])) | |
| 317 { | |
| 318 return gif_error("Could not add extension block.\n", kIn
completeInput); | |
| 319 } | |
| 320 // Move to the next block | |
| 321 if (GIF_ERROR == DGifGetExtensionNext(gif, &extData)) { | |
| 322 return gif_error("Could not get next extension.\n", kInc
ompleteInput); | |
| 323 } | |
| 324 } | |
| 325 break; | |
| 326 | |
| 327 // Signals the end of the gif file | |
| 328 case TERMINATE_RECORD_TYPE: | |
| 329 break; | |
| 330 | |
| 331 default: | |
| 332 // DGifGetRecordType returns an error if the record type does | |
| 333 // not match one of the above cases. This should not be | |
| 334 // reached. | |
| 335 SkASSERT(false); | |
| 336 break; | |
| 337 } | |
| 338 } while (TERMINATE_RECORD_TYPE != recordType); | |
| 339 | |
| 340 return gif_error("Could not find any images to decode in gif file.\n", kInva
lidInput); | |
| 341 } | |
| 342 | |
| 343 bool SkGifCodec::GetDimensions(GifFileType* gif, SkISize* size, SkIRect* frameRe
ct) { | |
| 344 // Get the encoded dimension values | |
| 345 SavedImage* image = &gif->SavedImages[gif->ImageCount - 1]; | |
| 346 const GifImageDesc& desc = image->ImageDesc; | |
| 347 int frameLeft = desc.Left; | |
| 348 int frameTop = desc.Top; | |
| 349 int frameWidth = desc.Width; | |
| 350 int frameHeight = desc.Height; | |
| 351 int width = gif->SWidth; | |
| 352 int height = gif->SHeight; | |
| 353 | |
| 354 // Ensure that the decode dimensions are large enough to contain the frame | |
| 355 width = SkTMax(width, frameWidth + frameLeft); | |
| 356 height = SkTMax(height, frameHeight + frameTop); | |
| 357 | |
| 358 // All of these dimensions should be positive, as they are encoded as unsign
ed 16-bit integers. | |
| 359 // It is unclear why giflib casts them to ints. We will go ahead and check
that they are | |
| 360 // in fact positive. | |
| 361 if (frameLeft < 0 || frameTop < 0 || frameWidth < 0 || frameHeight < 0 || wi
dth <= 0 || | |
| 362 height <= 0) { | |
| 363 return false; | |
| 364 } | |
| 365 | |
| 366 frameRect->setXYWH(frameLeft, frameTop, frameWidth, frameHeight); | |
| 367 size->set(width, height); | |
| 368 return true; | |
| 369 } | |
| 370 | |
| 371 void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* inp
utColorPtr, | |
| 372 int* inputColorCount) { | |
| 373 // Set up our own color table | |
| 374 const uint32_t maxColors = 256; | |
| 375 SkPMColor colorPtr[256]; | |
| 376 if (NULL != inputColorCount) { | |
| 377 // We set the number of colors to maxColors in order to ensure | |
| 378 // safe memory accesses. Otherwise, an invalid pixel could | |
| 379 // access memory outside of our color table array. | |
| 380 *inputColorCount = maxColors; | |
| 381 } | |
| 382 | |
| 383 // Get local color table | |
| 384 ColorMapObject* colorMap = fGif->Image.ColorMap; | |
| 385 // If there is no local color table, use the global color table | |
| 386 if (NULL == colorMap) { | |
| 387 colorMap = fGif->SColorMap; | |
| 388 } | |
| 389 | |
| 390 uint32_t colorCount = 0; | |
| 391 if (NULL != colorMap) { | |
| 392 colorCount = colorMap->ColorCount; | |
| 393 // giflib guarantees these properties | |
| 394 SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPixel))); | |
| 395 SkASSERT(colorCount <= 256); | |
| 396 for (uint32_t i = 0; i < colorCount; i++) { | |
| 397 colorPtr[i] = SkPackARGB32(0xFF, colorMap->Colors[i].Red, | |
| 398 colorMap->Colors[i].Green, colorMap->Colors[i].Blue); | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 // Gifs have the option to specify the color at a single index of the color | |
| 403 // table as transparent. If the transparent index is greater than the | |
| 404 // colorCount, we know that there is no valid transparent color in the color | |
| 405 // table. If there is not valid transparent index, we will try to use the | |
| 406 // backgroundIndex as the fill index. If the backgroundIndex is also not | |
| 407 // valid, we will let fFillIndex default to 0 (it is set to zero in the | |
| 408 // constructor). This behavior is not specified but matches | |
| 409 // SkImageDecoder_libgif. | |
| 410 uint32_t backgroundIndex = fGif->SBackGroundColor; | |
| 411 if (fTransIndex < colorCount) { | |
| 412 colorPtr[fTransIndex] = SK_ColorTRANSPARENT; | |
| 413 fFillIndex = fTransIndex; | |
| 414 } else if (backgroundIndex < colorCount) { | |
| 415 fFillIndex = backgroundIndex; | |
| 416 } | |
| 417 | |
| 418 // Fill in the color table for indices greater than color count. | |
| 419 // This allows for predictable, safe behavior. | |
| 420 for (uint32_t i = colorCount; i < maxColors; i++) { | |
| 421 colorPtr[i] = colorPtr[fFillIndex]; | |
| 422 } | |
| 423 | |
| 424 fColorTable.reset(new SkColorTable(colorPtr, maxColors)); | |
| 425 copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount)
; | |
| 426 } | |
| 427 | |
| 428 SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColo
r* inputColorPtr, | |
| 429 int* inputColorCount, const Options& opts) { | |
| 430 // Check for valid input parameters | |
| 431 if (!conversion_possible(dstInfo, this->getInfo())) { | |
| 432 return gif_error("Cannot convert input type to output type.\n", | |
| 433 kInvalidConversion); | |
| 434 } | |
| 435 | |
| 436 // Initialize color table and copy to the client if necessary | |
| 437 this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount); | |
| 438 | |
| 439 return this->initializeSwizzler(dstInfo, opts); | |
| 440 } | |
| 441 | |
| 442 SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, const
Options& opts) { | |
| 443 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | |
| 444 const SkIRect* frameRect = fFrameIsSubset ? &fFrameRect : nullptr; | |
| 445 fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, colorPtr, dst
Info, opts, | |
| 446 frameRect)); | |
| 447 | |
| 448 if (nullptr != fSwizzler.get()) { | |
| 449 return kSuccess; | |
| 450 } | |
| 451 return kUnimplemented; | |
| 452 } | |
| 453 | |
| 454 bool SkGifCodec::readRow() { | |
| 455 return GIF_ERROR != DGifGetLine(fGif, fSrcBuffer.get(), fFrameRect.width()); | |
| 456 } | |
| 457 | |
| 458 /* | |
| 459 * Initiates the gif decode | |
| 460 */ | |
| 461 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, | |
| 462 void* dst, size_t dstRowBytes, | |
| 463 const Options& opts, | |
| 464 SkPMColor* inputColorPtr, | |
| 465 int* inputColorCount, | |
| 466 int* rowsDecoded) { | |
| 467 Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCoun
t, opts); | |
| 468 if (kSuccess != result) { | |
| 469 return result; | |
| 470 } | |
| 471 | |
| 472 if (dstInfo.dimensions() != this->getInfo().dimensions()) { | |
| 473 return gif_error("Scaling not supported.\n", kInvalidScale); | |
| 474 } | |
| 475 | |
| 476 // Initialize the swizzler | |
| 477 if (fFrameIsSubset) { | |
| 478 // Fill the background | |
| 479 SkSampler::Fill(dstInfo, dst, dstRowBytes, | |
| 480 this->getFillValue(dstInfo.colorType(), dstInfo.alphaType()), | |
| 481 opts.fZeroInitialized); | |
| 482 } | |
| 483 | |
| 484 // Iterate over rows of the input | |
| 485 for (int y = fFrameRect.top(); y < fFrameRect.bottom(); y++) { | |
| 486 if (!this->readRow()) { | |
| 487 *rowsDecoded = y; | |
| 488 return gif_error("Could not decode line.\n", kIncompleteInput); | |
| 489 } | |
| 490 void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * this->outputScanlin
e(y)); | |
| 491 fSwizzler->swizzle(dstRow, fSrcBuffer.get()); | |
| 492 } | |
| 493 return kSuccess; | |
| 494 } | |
| 495 | |
| 496 // FIXME: This is similar to the implementation for bmp and png. Can we share m
ore code or | |
| 497 // possibly make this non-virtual? | |
| 498 uint32_t SkGifCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType
) const { | |
| 499 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | |
| 500 return get_color_table_fill_value(colorType, colorPtr, fFillIndex); | |
| 501 } | |
| 502 | |
| 503 SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, | |
| 504 const SkCodec::Options& opts, SkPMColor inputColorPtr[], int* inputColor
Count) { | |
| 505 return this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, this->
options()); | |
| 506 } | |
| 507 | |
| 508 void SkGifCodec::handleScanlineFrame(int count, int* rowsBeforeFrame, int* rowsI
nFrame) { | |
| 509 if (fFrameIsSubset) { | |
| 510 const int currRow = this->currScanline(); | |
| 511 | |
| 512 // The number of rows that remain to be skipped before reaching rows tha
t we | |
| 513 // actually must decode into. | |
| 514 // This must be at least zero. We also make sure that it is less than o
r | |
| 515 // equal to count, since we will skip at most count rows. | |
| 516 *rowsBeforeFrame = SkTMin(count, SkTMax(0, fFrameRect.top() - currRow)); | |
| 517 | |
| 518 // Rows left to decode once we reach the start of the frame. | |
| 519 const int rowsLeft = count - *rowsBeforeFrame; | |
| 520 | |
| 521 // Count the number of that extend beyond the bottom of the frame. We d
o not | |
| 522 // need to decode into these rows. | |
| 523 const int rowsAfterFrame = SkTMax(0, currRow + rowsLeft - fFrameRect.bot
tom()); | |
| 524 | |
| 525 // Set the actual number of source rows that we need to decode. | |
| 526 *rowsInFrame = rowsLeft - rowsAfterFrame; | |
| 527 } else { | |
| 528 *rowsBeforeFrame = 0; | |
| 529 *rowsInFrame = count; | |
| 530 } | |
| 531 } | |
| 532 | |
| 533 int SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { | |
| 534 int rowsBeforeFrame; | |
| 535 int rowsInFrame; | |
| 536 this->handleScanlineFrame(count, &rowsBeforeFrame, &rowsInFrame); | |
| 537 | |
| 538 if (fFrameIsSubset) { | |
| 539 // Fill the requested rows | |
| 540 SkImageInfo fillInfo = this->dstInfo().makeWH(this->dstInfo().width(), c
ount); | |
| 541 uint32_t fillValue = this->onGetFillValue(this->dstInfo().colorType(), | |
| 542 this->dstInfo().alphaType()); | |
| 543 fSwizzler->fill(fillInfo, dst, rowBytes, fillValue, this->options().fZer
oInitialized); | |
| 544 | |
| 545 // Start to write pixels at the start of the image frame | |
| 546 dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame); | |
| 547 } | |
| 548 | |
| 549 for (int i = 0; i < rowsInFrame; i++) { | |
| 550 if (!this->readRow()) { | |
| 551 return i + rowsBeforeFrame; | |
| 552 } | |
| 553 fSwizzler->swizzle(dst, fSrcBuffer.get()); | |
| 554 dst = SkTAddOffset<void>(dst, rowBytes); | |
| 555 } | |
| 556 | |
| 557 return count; | |
| 558 } | |
| 559 | |
| 560 bool SkGifCodec::onSkipScanlines(int count) { | |
| 561 int rowsBeforeFrame; | |
| 562 int rowsInFrame; | |
| 563 this->handleScanlineFrame(count, &rowsBeforeFrame, &rowsInFrame); | |
| 564 | |
| 565 for (int i = 0; i < rowsInFrame; i++) { | |
| 566 if (!this->readRow()) { | |
| 567 return false; | |
| 568 } | |
| 569 } | |
| 570 | |
| 571 return true; | |
| 572 } | |
| 573 | |
| 574 SkCodec::SkScanlineOrder SkGifCodec::onGetScanlineOrder() const { | |
| 575 if (fGif->Image.Interlace) { | |
| 576 return kOutOfOrder_SkScanlineOrder; | |
| 577 } | |
| 578 return kTopDown_SkScanlineOrder; | |
| 579 } | |
| 580 | |
| 581 int SkGifCodec::onOutputScanline(int inputScanline) const { | |
| 582 if (fGif->Image.Interlace) { | |
| 583 if (inputScanline < fFrameRect.top() || inputScanline >= fFrameRect.bott
om()) { | |
| 584 return inputScanline; | |
| 585 } | |
| 586 return get_output_row_interlaced(inputScanline - fFrameRect.top(), fFram
eRect.height()) + | |
| 587 fFrameRect.top(); | |
| 588 } | |
| 589 return inputScanline; | |
| 590 } | |
| OLD | NEW |