blob: a938f5fc48fac46ece1f929597ae5844675d3917 [file] [log] [blame]
msarett8c8f22a2015-04-01 06:58:48 -07001/*
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
msarett8c8f22a2015-04-01 06:58:48 -07008#include "SkCodecPriv.h"
9#include "SkColorPriv.h"
10#include "SkColorTable.h"
msarett1a464672016-01-07 13:17:19 -080011#include "SkGifCodec.h"
msarett8c8f22a2015-04-01 06:58:48 -070012#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 */
scroggodb30be22015-12-08 18:54:13 -080019bool SkGifCodec::IsGif(const void* buf, size_t bytesRead) {
20 if (bytesRead >= GIF_STAMP_LEN) {
msarett8c8f22a2015-04-01 06:58:48 -070021 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 ||
bungeman0153dea2015-08-27 16:43:42 -070022 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
23 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0)
24 {
msarett8c8f22a2015-04-01 06:58:48 -070025 return true;
26 }
27 }
28 return false;
29}
30
31/*
msarett8c8f22a2015-04-01 06:58:48 -070032 * Error function
33 */
bungeman0153dea2015-08-27 16:43:42 -070034static SkCodec::Result gif_error(const char* msg, SkCodec::Result result = SkCodec::kInvalidInput) {
msarett8c8f22a2015-04-01 06:58:48 -070035 SkCodecPrintf("Gif Error: %s\n", msg);
36 return result;
37}
38
39
40/*
41 * Read function that will be passed to gif_lib
42 */
bungeman0153dea2015-08-27 16:43:42 -070043static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, int32_t size) {
msarett8c8f22a2015-04-01 06:58:48 -070044 SkStream* stream = (SkStream*) fileType->UserData;
45 return (int32_t) stream->read(out, size);
46}
47
48/*
49 * Open the gif file
50 */
51static GifFileType* open_gif(SkStream* stream) {
halcanary96fcdcc2015-08-27 07:41:13 -070052 return DGifOpen(stream, read_bytes_callback, nullptr);
msarett8c8f22a2015-04-01 06:58:48 -070053}
54
msarett8c8f22a2015-04-01 06:58:48 -070055/*
56 * Check if a there is an index of the color table for a transparent pixel
57 */
58static 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.
bungeman0153dea2015-08-27 16:43:42 -070071 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >= 4) {
msarett8c8f22a2015-04-01 06:58:48 -070072 // 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)) {
msarett8c8f22a2015-04-01 06:58:48 -070076 // 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 valid
86 // index was not found.
87 return SK_MaxU32;
88}
89
msarette6dd0042015-10-09 11:07:34 -070090inline uint32_t ceil_div(uint32_t a, uint32_t b) {
msarettd02b99f2015-08-28 07:36:55 -070091 return (a + b - 1) / b;
92}
93
94/*
95 * Gets the output row corresponding to the encoded row for interlaced gifs
96 */
msarette6dd0042015-10-09 11:07:34 -070097inline uint32_t get_output_row_interlaced(uint32_t encodedRow, uint32_t height) {
msarettd02b99f2015-08-28 07:36:55 -070098 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
msarett8c8f22a2015-04-01 06:58:48 -0700115/*
msarett10522ff2015-09-07 08:54:01 -0700116 * This function cleans up the gif object after the decode completes
117 * It is used in a SkAutoTCallIProc template
118 */
119void 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 */
127void SkGifCodec::FreeExtension(SavedImage* image) {
128 if (NULL != image->ExtensionBlocks) {
129 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks);
130 }
131}
132
133/*
msarett438b2ad2015-04-09 12:43:10 -0700134 * Read enough of the stream to initialize the SkGifCodec.
135 * Returns a bool representing success or failure.
136 *
137 * @param codecOut
halcanary96fcdcc2015-08-27 07:41:13 -0700138 * If it returned true, and codecOut was not nullptr,
msarett438b2ad2015-04-09 12:43:10 -0700139 * codecOut will be set to a new SkGifCodec.
140 *
141 * @param gifOut
halcanary96fcdcc2015-08-27 07:41:13 -0700142 * If it returned true, and codecOut was nullptr,
143 * gifOut must be non-nullptr and gifOut will be set to a new
msarett438b2ad2015-04-09 12:43:10 -0700144 * 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 */
152bool 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
halcanary96fcdcc2015-08-27 07:41:13 -0700158 if (nullptr == gif) {
msarett438b2ad2015-04-09 12:43:10 -0700159 gif_error("DGifOpen failed.\n");
160 return false;
161 }
162
msarett10522ff2015-09-07 08:54:01 -0700163 // 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
halcanary96fcdcc2015-08-27 07:41:13 -0700179 if (nullptr != codecOut) {
msarett4aa02d82015-10-06 07:46:02 -0700180 SkISize size;
181 SkIRect frameRect;
182 if (!GetDimensions(gif, &size, &frameRect)) {
183 gif_error("Invalid gif size.\n");
msarettf724b992015-10-15 06:41:06 -0700184 return false;
msarett438b2ad2015-04-09 12:43:10 -0700185 }
msarett4aa02d82015-10-06 07:46:02 -0700186 bool frameIsSubset = (size != frameRect.size());
msarett438b2ad2015-04-09 12:43:10 -0700187
msarett10522ff2015-09-07 08:54:01 -0700188 // 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 process 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 image has
192 // alpha.
193 // In the case where we must support alpha, we have the option to set the
194 // suggested alpha type to kPremul or kUnpremul. Both are valid since the 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 efficient to use
197 // kPremul directly even when kUnpremul is supported.
198 SkAlphaType alphaType = (transIndex < 256) ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
199
msarett438b2ad2015-04-09 12:43:10 -0700200 // Return the codec
201 // kIndex is the most natural color type for gifs, so we set this as
202 // the default.
msarett4aa02d82015-10-06 07:46:02 -0700203 SkImageInfo imageInfo = SkImageInfo::Make(size.width(), size.height(), kIndex_8_SkColorType,
204 alphaType);
205 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach(), transIndex,
206 frameRect, frameIsSubset);
msarett438b2ad2015-04-09 12:43:10 -0700207 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700208 SkASSERT(nullptr != gifOut);
msarett438b2ad2015-04-09 12:43:10 -0700209 streamDeleter.detach();
210 *gifOut = gif.detach();
211 }
212 return true;
213}
214
215/*
msarett8c8f22a2015-04-01 06:58:48 -0700216 * Assumes IsGif was called and returned true
217 * Creates a gif decoder
218 * Reads enough of the stream to determine the image format
219 */
220SkCodec* SkGifCodec::NewFromStream(SkStream* stream) {
halcanary96fcdcc2015-08-27 07:41:13 -0700221 SkCodec* codec = nullptr;
222 if (ReadHeader(stream, &codec, nullptr)) {
msarett438b2ad2015-04-09 12:43:10 -0700223 return codec;
msarett8c8f22a2015-04-01 06:58:48 -0700224 }
halcanary96fcdcc2015-08-27 07:41:13 -0700225 return nullptr;
msarett8c8f22a2015-04-01 06:58:48 -0700226}
227
msarett10522ff2015-09-07 08:54:01 -0700228SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType* gif,
msarett4aa02d82015-10-06 07:46:02 -0700229 uint32_t transIndex, const SkIRect& frameRect, bool frameIsSubset)
msarett8c8f22a2015-04-01 06:58:48 -0700230 : INHERITED(srcInfo, stream)
231 , fGif(gif)
msarett10522ff2015-09-07 08:54:01 -0700232 , fSrcBuffer(new uint8_t[this->getInfo().width()])
msarettf724b992015-10-15 06:41:06 -0700233 , fFrameRect(frameRect)
msarett10522ff2015-09-07 08:54:01 -0700234 // If it is valid, fTransIndex will be used to set fFillIndex. We don't know 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)
msarett4aa02d82015-10-06 07:46:02 -0700241 , fFrameIsSubset(frameIsSubset)
msarett10522ff2015-09-07 08:54:01 -0700242 , fSwizzler(NULL)
msarettf724b992015-10-15 06:41:06 -0700243 , fColorTable(NULL)
msarett8c8f22a2015-04-01 06:58:48 -0700244{}
245
scroggob427db12015-08-12 07:24:13 -0700246bool SkGifCodec::onRewind() {
halcanary96fcdcc2015-08-27 07:41:13 -0700247 GifFileType* gifOut = nullptr;
248 if (!ReadHeader(this->stream(), nullptr, &gifOut)) {
scroggob427db12015-08-12 07:24:13 -0700249 return false;
250 }
251
halcanary96fcdcc2015-08-27 07:41:13 -0700252 SkASSERT(nullptr != gifOut);
scroggob427db12015-08-12 07:24:13 -0700253 fGif.reset(gifOut);
254 return true;
255}
256
msarett10522ff2015-09-07 08:54:01 -0700257SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex) {
msarett8c8f22a2015-04-01 06:58:48 -0700258 // 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);
halcanary96fcdcc2015-08-27 07:41:13 -0700262 saveExt.ExtensionBlocks = nullptr;
msarett8c8f22a2015-04-01 06:58:48 -0700263 saveExt.ExtensionBlockCount = 0;
264 GifByteType* extData;
msarett8c8f22a2015-04-01 06:58:48 -0700265 int32_t extFunction;
msarett8c8f22a2015-04-01 06:58:48 -0700266
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.
msarett8c8f22a2015-04-01 06:58:48 -0700270 GifRecordType recordType;
271 do {
272 // Get the current record type
msarett10522ff2015-09-07 08:54:01 -0700273 if (GIF_ERROR == DGifGetRecordType(gif, &recordType)) {
msarett8c8f22a2015-04-01 06:58:48 -0700274 return gif_error("DGifGetRecordType failed.\n", kInvalidInput);
275 }
msarett8c8f22a2015-04-01 06:58:48 -0700276 switch (recordType) {
277 case IMAGE_DESC_RECORD_TYPE: {
msarett10522ff2015-09-07 08:54:01 -0700278 *transIndex = find_trans_index(saveExt);
msarett4aa02d82015-10-06 07:46:02 -0700279
msarett8c8f22a2015-04-01 06:58:48 -0700280 // 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 }
msarett8c8f22a2015-04-01 06:58:48 -0700303 // 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
msarett10522ff2015-09-07 08:54:01 -0700307 if (GIF_ERROR == DGifGetExtension(gif, &extFunction, &extData)) {
bungeman0153dea2015-08-27 16:43:42 -0700308 return gif_error("Could not get extension.\n", kIncompleteInput);
msarett8c8f22a2015-04-01 06:58:48 -0700309 }
310
311 // Create an extension block with our data
halcanary96fcdcc2015-08-27 07:41:13 -0700312 while (nullptr != extData) {
msarett8c8f22a2015-04-01 06:58:48 -0700313 // Add a single block
bungeman0153dea2015-08-27 16:43:42 -0700314 if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBlockCount,
315 &saveExt.ExtensionBlocks,
316 extFunction, extData[0], &extData[1]))
317 {
318 return gif_error("Could not add extension block.\n", kIncompleteInput);
msarett8c8f22a2015-04-01 06:58:48 -0700319 }
320 // Move to the next block
msarett10522ff2015-09-07 08:54:01 -0700321 if (GIF_ERROR == DGifGetExtensionNext(gif, &extData)) {
bungeman0153dea2015-08-27 16:43:42 -0700322 return gif_error("Could not get next extension.\n", kIncompleteInput);
msarett8c8f22a2015-04-01 06:58:48 -0700323 }
msarett8c8f22a2015-04-01 06:58:48 -0700324 }
325 break;
326
327 // Signals the end of the gif file
328 case TERMINATE_RECORD_TYPE:
329 break;
330
331 default:
msarett10522ff2015-09-07 08:54:01 -0700332 // DGifGetRecordType returns an error if the record type does
333 // not match one of the above cases. This should not be
334 // reached.
msarett8c8f22a2015-04-01 06:58:48 -0700335 SkASSERT(false);
336 break;
337 }
338 } while (TERMINATE_RECORD_TYPE != recordType);
339
bungeman0153dea2015-08-27 16:43:42 -0700340 return gif_error("Could not find any images to decode in gif file.\n", kInvalidInput);
msarett8c8f22a2015-04-01 06:58:48 -0700341}
msarett10522ff2015-09-07 08:54:01 -0700342
msarett4aa02d82015-10-06 07:46:02 -0700343bool SkGifCodec::GetDimensions(GifFileType* gif, SkISize* size, SkIRect* frameRect) {
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 unsigned 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 || width <= 0 ||
362 height <= 0) {
msarett10522ff2015-09-07 08:54:01 -0700363 return false;
364 }
365
msarett4aa02d82015-10-06 07:46:02 -0700366 frameRect->setXYWH(frameLeft, frameTop, frameWidth, frameHeight);
367 size->set(width, height);
msarett10522ff2015-09-07 08:54:01 -0700368 return true;
369}
370
371void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
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
428SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
429 int* inputColorCount, const Options& opts) {
msarett10522ff2015-09-07 08:54:01 -0700430 // Check for valid input parameters
msarett10522ff2015-09-07 08:54:01 -0700431 if (!conversion_possible(dstInfo, this->getInfo())) {
432 return gif_error("Cannot convert input type to output type.\n",
433 kInvalidConversion);
434 }
435
msarett10522ff2015-09-07 08:54:01 -0700436 // Initialize color table and copy to the client if necessary
437 this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount);
msarett5af4e0b2015-11-17 11:18:03 -0800438
439 return this->initializeSwizzler(dstInfo, opts);
msarett10522ff2015-09-07 08:54:01 -0700440}
441
msarettfdb47572015-10-13 12:50:14 -0700442SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) {
msarett10522ff2015-09-07 08:54:01 -0700443 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
msarett5af4e0b2015-11-17 11:18:03 -0800444 const SkIRect* frameRect = fFrameIsSubset ? &fFrameRect : nullptr;
445 fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, colorPtr, dstInfo, opts,
446 frameRect));
447
msarett10522ff2015-09-07 08:54:01 -0700448 if (nullptr != fSwizzler.get()) {
449 return kSuccess;
450 }
451 return kUnimplemented;
452}
453
msarette6dd0042015-10-09 11:07:34 -0700454bool SkGifCodec::readRow() {
455 return GIF_ERROR != DGifGetLine(fGif, fSrcBuffer.get(), fFrameRect.width());
msarett10522ff2015-09-07 08:54:01 -0700456}
457
458/*
459 * Initiates the gif decode
460 */
461SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
462 void* dst, size_t dstRowBytes,
463 const Options& opts,
464 SkPMColor* inputColorPtr,
msarette6dd0042015-10-09 11:07:34 -0700465 int* inputColorCount,
466 int* rowsDecoded) {
msarett10522ff2015-09-07 08:54:01 -0700467 Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, 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) {
msarett10522ff2015-09-07 08:54:01 -0700478 // Fill the background
scroggoc5560be2016-02-03 09:42:42 -0800479 SkSampler::Fill(dstInfo, dst, dstRowBytes, this->getFillValue(dstInfo.colorType()),
msarette6dd0042015-10-09 11:07:34 -0700480 opts.fZeroInitialized);
msarett10522ff2015-09-07 08:54:01 -0700481 }
482
msarette6dd0042015-10-09 11:07:34 -0700483 // Iterate over rows of the input
msarett5af4e0b2015-11-17 11:18:03 -0800484 for (int y = fFrameRect.top(); y < fFrameRect.bottom(); y++) {
msarette6dd0042015-10-09 11:07:34 -0700485 if (!this->readRow()) {
486 *rowsDecoded = y;
487 return gif_error("Could not decode line.\n", kIncompleteInput);
msarett10522ff2015-09-07 08:54:01 -0700488 }
msarette6dd0042015-10-09 11:07:34 -0700489 void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * this->outputScanline(y));
490 fSwizzler->swizzle(dstRow, fSrcBuffer.get());
msarett10522ff2015-09-07 08:54:01 -0700491 }
492 return kSuccess;
493}
494
msarette6dd0042015-10-09 11:07:34 -0700495// FIXME: This is similar to the implementation for bmp and png. Can we share more code or
496// possibly make this non-virtual?
scroggoc5560be2016-02-03 09:42:42 -0800497uint32_t SkGifCodec::onGetFillValue(SkColorType colorType) const {
msarette6dd0042015-10-09 11:07:34 -0700498 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
499 return get_color_table_fill_value(colorType, colorPtr, fFillIndex);
500}
501
scroggo46c57472015-09-30 08:57:13 -0700502SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
503 const SkCodec::Options& opts, SkPMColor inputColorPtr[], int* inputColorCount) {
msarett5af4e0b2015-11-17 11:18:03 -0800504 return this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, this->options());
scroggo46c57472015-09-30 08:57:13 -0700505}
msarett10522ff2015-09-07 08:54:01 -0700506
msarett72261c02015-11-19 15:29:26 -0800507void SkGifCodec::handleScanlineFrame(int count, int* rowsBeforeFrame, int* rowsInFrame) {
508 if (fFrameIsSubset) {
msarettcb0d5c92015-12-03 12:23:43 -0800509 const int currRow = this->currScanline();
msarett72261c02015-11-19 15:29:26 -0800510
511 // The number of rows that remain to be skipped before reaching rows that we
512 // actually must decode into.
513 // This must be at least zero. We also make sure that it is less than or
514 // equal to count, since we will skip at most count rows.
515 *rowsBeforeFrame = SkTMin(count, SkTMax(0, fFrameRect.top() - currRow));
516
517 // Rows left to decode once we reach the start of the frame.
518 const int rowsLeft = count - *rowsBeforeFrame;
519
520 // Count the number of that extend beyond the bottom of the frame. We do not
521 // need to decode into these rows.
522 const int rowsAfterFrame = SkTMax(0, currRow + rowsLeft - fFrameRect.bottom());
523
524 // Set the actual number of source rows that we need to decode.
525 *rowsInFrame = rowsLeft - rowsAfterFrame;
526 } else {
527 *rowsBeforeFrame = 0;
528 *rowsInFrame = count;
529 }
530}
531
msarette6dd0042015-10-09 11:07:34 -0700532int SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
msarett72261c02015-11-19 15:29:26 -0800533 int rowsBeforeFrame;
534 int rowsInFrame;
535 this->handleScanlineFrame(count, &rowsBeforeFrame, &rowsInFrame);
536
scroggo46c57472015-09-30 08:57:13 -0700537 if (fFrameIsSubset) {
538 // Fill the requested rows
msarette6dd0042015-10-09 11:07:34 -0700539 SkImageInfo fillInfo = this->dstInfo().makeWH(this->dstInfo().width(), count);
scroggoc5560be2016-02-03 09:42:42 -0800540 uint32_t fillValue = this->onGetFillValue(this->dstInfo().colorType());
msarett5af4e0b2015-11-17 11:18:03 -0800541 fSwizzler->fill(fillInfo, dst, rowBytes, fillValue, this->options().fZeroInitialized);
scroggo46c57472015-09-30 08:57:13 -0700542
msarett72261c02015-11-19 15:29:26 -0800543 // Start to write pixels at the start of the image frame
msarett4aa02d82015-10-06 07:46:02 -0700544 dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame);
msarett10522ff2015-09-07 08:54:01 -0700545 }
546
msarette6dd0042015-10-09 11:07:34 -0700547 for (int i = 0; i < rowsInFrame; i++) {
548 if (!this->readRow()) {
549 return i + rowsBeforeFrame;
msarett10522ff2015-09-07 08:54:01 -0700550 }
scroggo46c57472015-09-30 08:57:13 -0700551 fSwizzler->swizzle(dst, fSrcBuffer.get());
552 dst = SkTAddOffset<void>(dst, rowBytes);
msarett10522ff2015-09-07 08:54:01 -0700553 }
msarette6dd0042015-10-09 11:07:34 -0700554
555 return count;
msarett10522ff2015-09-07 08:54:01 -0700556}
scroggo46c57472015-09-30 08:57:13 -0700557
msarett72261c02015-11-19 15:29:26 -0800558bool SkGifCodec::onSkipScanlines(int count) {
559 int rowsBeforeFrame;
560 int rowsInFrame;
561 this->handleScanlineFrame(count, &rowsBeforeFrame, &rowsInFrame);
562
563 for (int i = 0; i < rowsInFrame; i++) {
564 if (!this->readRow()) {
565 return false;
566 }
567 }
568
569 return true;
570}
571
scroggo46c57472015-09-30 08:57:13 -0700572SkCodec::SkScanlineOrder SkGifCodec::onGetScanlineOrder() const {
573 if (fGif->Image.Interlace) {
574 return kOutOfOrder_SkScanlineOrder;
scroggo46c57472015-09-30 08:57:13 -0700575 }
msarette6dd0042015-10-09 11:07:34 -0700576 return kTopDown_SkScanlineOrder;
scroggo46c57472015-09-30 08:57:13 -0700577}
578
msarette6dd0042015-10-09 11:07:34 -0700579int SkGifCodec::onOutputScanline(int inputScanline) const {
scroggo46c57472015-09-30 08:57:13 -0700580 if (fGif->Image.Interlace) {
msarette6dd0042015-10-09 11:07:34 -0700581 if (inputScanline < fFrameRect.top() || inputScanline >= fFrameRect.bottom()) {
582 return inputScanline;
msarett4aa02d82015-10-06 07:46:02 -0700583 }
msarett5af4e0b2015-11-17 11:18:03 -0800584 return get_output_row_interlaced(inputScanline - fFrameRect.top(), fFrameRect.height()) +
585 fFrameRect.top();
scroggo46c57472015-09-30 08:57:13 -0700586 }
msarette6dd0042015-10-09 11:07:34 -0700587 return inputScanline;
scroggo46c57472015-09-30 08:57:13 -0700588}