blob: 018789155879f6ee7c38a7140567bbc5a112516a [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
8#include "SkCodec_libgif.h"
9#include "SkCodecPriv.h"
10#include "SkColorPriv.h"
11#include "SkColorTable.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 */
19bool SkGifCodec::IsGif(SkStream* stream) {
20 char buf[GIF_STAMP_LEN];
21 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
22 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 ||
bungeman0153dea2015-08-27 16:43:42 -070023 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
24 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0)
25 {
msarett8c8f22a2015-04-01 06:58:48 -070026 return true;
27 }
28 }
29 return false;
30}
31
32/*
33 * Warning reporting function
34 */
35static void gif_warning(const char* msg) {
36 SkCodecPrintf("Gif Warning: %s\n", msg);
37}
38
39/*
40 * Error function
41 */
bungeman0153dea2015-08-27 16:43:42 -070042static SkCodec::Result gif_error(const char* msg, SkCodec::Result result = SkCodec::kInvalidInput) {
msarett8c8f22a2015-04-01 06:58:48 -070043 SkCodecPrintf("Gif Error: %s\n", msg);
44 return result;
45}
46
47
48/*
49 * Read function that will be passed to gif_lib
50 */
bungeman0153dea2015-08-27 16:43:42 -070051static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, int32_t size) {
msarett8c8f22a2015-04-01 06:58:48 -070052 SkStream* stream = (SkStream*) fileType->UserData;
53 return (int32_t) stream->read(out, size);
54}
55
56/*
57 * Open the gif file
58 */
59static GifFileType* open_gif(SkStream* stream) {
halcanary96fcdcc2015-08-27 07:41:13 -070060 return DGifOpen(stream, read_bytes_callback, nullptr);
msarett8c8f22a2015-04-01 06:58:48 -070061}
62
msarett8c8f22a2015-04-01 06:58:48 -070063/*
64 * Check if a there is an index of the color table for a transparent pixel
65 */
66static uint32_t find_trans_index(const SavedImage& image) {
67 // If there is a transparent index specified, it will be contained in an
68 // extension block. We will loop through extension blocks in reverse order
69 // to check the most recent extension blocks first.
70 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) {
71 // Get an extension block
72 const ExtensionBlock& extBlock = image.ExtensionBlocks[i];
73
74 // Specifically, we need to check for a graphics control extension,
75 // which may contain transparency information. Also, note that a valid
76 // graphics control extension is always four bytes. The fourth byte
77 // is the transparent index (if it exists), so we need at least four
78 // bytes.
bungeman0153dea2015-08-27 16:43:42 -070079 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >= 4) {
msarett8c8f22a2015-04-01 06:58:48 -070080 // Check the transparent color flag which indicates whether a
81 // transparent index exists. It is the least significant bit of
82 // the first byte of the extension block.
83 if (1 == (extBlock.Bytes[0] & 1)) {
msarett8c8f22a2015-04-01 06:58:48 -070084 // Use uint32_t to prevent sign extending
85 return extBlock.Bytes[3];
86 }
87
88 // There should only be one graphics control extension for the image frame
89 break;
90 }
91 }
92
93 // Use maximum unsigned int (surely an invalid index) to indicate that a valid
94 // index was not found.
95 return SK_MaxU32;
96}
97
msarette6dd0042015-10-09 11:07:34 -070098inline uint32_t ceil_div(uint32_t a, uint32_t b) {
msarettd02b99f2015-08-28 07:36:55 -070099 return (a + b - 1) / b;
100}
101
102/*
103 * Gets the output row corresponding to the encoded row for interlaced gifs
104 */
msarette6dd0042015-10-09 11:07:34 -0700105inline uint32_t get_output_row_interlaced(uint32_t encodedRow, uint32_t height) {
msarettd02b99f2015-08-28 07:36:55 -0700106 SkASSERT(encodedRow < height);
107 // First pass
108 if (encodedRow * 8 < height) {
109 return encodedRow * 8;
110 }
111 // Second pass
112 if (encodedRow * 4 < height) {
113 return 4 + 8 * (encodedRow - ceil_div(height, 8));
114 }
115 // Third pass
116 if (encodedRow * 2 < height) {
117 return 2 + 4 * (encodedRow - ceil_div(height, 4));
118 }
119 // Fourth pass
120 return 1 + 2 * (encodedRow - ceil_div(height, 2));
121}
122
msarett8c8f22a2015-04-01 06:58:48 -0700123/*
msarett10522ff2015-09-07 08:54:01 -0700124 * This function cleans up the gif object after the decode completes
125 * It is used in a SkAutoTCallIProc template
126 */
127void SkGifCodec::CloseGif(GifFileType* gif) {
128 DGifCloseFile(gif, NULL);
129}
130
131/*
132 * This function free extension data that has been saved to assist the image
133 * decoder
134 */
135void SkGifCodec::FreeExtension(SavedImage* image) {
136 if (NULL != image->ExtensionBlocks) {
137 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks);
138 }
139}
140
141/*
msarett438b2ad2015-04-09 12:43:10 -0700142 * Read enough of the stream to initialize the SkGifCodec.
143 * Returns a bool representing success or failure.
144 *
145 * @param codecOut
halcanary96fcdcc2015-08-27 07:41:13 -0700146 * If it returned true, and codecOut was not nullptr,
msarett438b2ad2015-04-09 12:43:10 -0700147 * codecOut will be set to a new SkGifCodec.
148 *
149 * @param gifOut
halcanary96fcdcc2015-08-27 07:41:13 -0700150 * If it returned true, and codecOut was nullptr,
151 * gifOut must be non-nullptr and gifOut will be set to a new
msarett438b2ad2015-04-09 12:43:10 -0700152 * GifFileType pointer.
153 *
154 * @param stream
155 * Deleted on failure.
156 * codecOut will take ownership of it in the case where we created a codec.
157 * Ownership is unchanged when we returned a gifOut.
158 *
159 */
160bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) {
161 SkAutoTDelete<SkStream> streamDeleter(stream);
162
163 // Read gif header, logical screen descriptor, and global color table
164 SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream));
165
halcanary96fcdcc2015-08-27 07:41:13 -0700166 if (nullptr == gif) {
msarett438b2ad2015-04-09 12:43:10 -0700167 gif_error("DGifOpen failed.\n");
168 return false;
169 }
170
msarett10522ff2015-09-07 08:54:01 -0700171 // Read through gif extensions to get to the image data. Set the
172 // transparent index based on the extension data.
173 uint32_t transIndex;
174 SkCodec::Result result = ReadUpToFirstImage(gif, &transIndex);
175 if (kSuccess != result){
176 return false;
177 }
178
179 // Read the image descriptor
180 if (GIF_ERROR == DGifGetImageDesc(gif)) {
181 return false;
182 }
183 // If reading the image descriptor is successful, the image count will be
184 // incremented.
185 SkASSERT(gif->ImageCount >= 1);
186
halcanary96fcdcc2015-08-27 07:41:13 -0700187 if (nullptr != codecOut) {
msarett4aa02d82015-10-06 07:46:02 -0700188 SkISize size;
189 SkIRect frameRect;
190 if (!GetDimensions(gif, &size, &frameRect)) {
191 gif_error("Invalid gif size.\n");
msarett438b2ad2015-04-09 12:43:10 -0700192 }
msarett4aa02d82015-10-06 07:46:02 -0700193 bool frameIsSubset = (size != frameRect.size());
msarett438b2ad2015-04-09 12:43:10 -0700194
msarett10522ff2015-09-07 08:54:01 -0700195 // Determine the recommended alpha type. The transIndex might be valid if it less
196 // than 256. We are not certain that the index is valid until we process the color
197 // table, since some gifs have color tables with less than 256 colors. If
198 // there might be a valid transparent index, we must indicate that the image has
199 // alpha.
200 // In the case where we must support alpha, we have the option to set the
201 // suggested alpha type to kPremul or kUnpremul. Both are valid since the alpha
202 // component will always be 0xFF or the entire 32-bit pixel will be set to zero.
203 // We prefer kPremul because we support kPremul, and it is more efficient to use
204 // kPremul directly even when kUnpremul is supported.
205 SkAlphaType alphaType = (transIndex < 256) ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
206
msarett438b2ad2015-04-09 12:43:10 -0700207 // Return the codec
208 // kIndex is the most natural color type for gifs, so we set this as
209 // the default.
msarett4aa02d82015-10-06 07:46:02 -0700210 SkImageInfo imageInfo = SkImageInfo::Make(size.width(), size.height(), kIndex_8_SkColorType,
211 alphaType);
212 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach(), transIndex,
213 frameRect, frameIsSubset);
msarett438b2ad2015-04-09 12:43:10 -0700214 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700215 SkASSERT(nullptr != gifOut);
msarett438b2ad2015-04-09 12:43:10 -0700216 streamDeleter.detach();
217 *gifOut = gif.detach();
218 }
219 return true;
220}
221
222/*
msarett8c8f22a2015-04-01 06:58:48 -0700223 * Assumes IsGif was called and returned true
224 * Creates a gif decoder
225 * Reads enough of the stream to determine the image format
226 */
227SkCodec* SkGifCodec::NewFromStream(SkStream* stream) {
halcanary96fcdcc2015-08-27 07:41:13 -0700228 SkCodec* codec = nullptr;
229 if (ReadHeader(stream, &codec, nullptr)) {
msarett438b2ad2015-04-09 12:43:10 -0700230 return codec;
msarett8c8f22a2015-04-01 06:58:48 -0700231 }
halcanary96fcdcc2015-08-27 07:41:13 -0700232 return nullptr;
msarett8c8f22a2015-04-01 06:58:48 -0700233}
234
msarett10522ff2015-09-07 08:54:01 -0700235SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType* gif,
msarett4aa02d82015-10-06 07:46:02 -0700236 uint32_t transIndex, const SkIRect& frameRect, bool frameIsSubset)
msarett8c8f22a2015-04-01 06:58:48 -0700237 : INHERITED(srcInfo, stream)
238 , fGif(gif)
msarett10522ff2015-09-07 08:54:01 -0700239 , fSrcBuffer(new uint8_t[this->getInfo().width()])
240 // If it is valid, fTransIndex will be used to set fFillIndex. We don't know if
241 // fTransIndex is valid until we process the color table, since fTransIndex may
242 // be greater than the size of the color table.
243 , fTransIndex(transIndex)
244 // Default fFillIndex is 0. We will overwrite this if fTransIndex is valid, or if
245 // there is a valid background color.
246 , fFillIndex(0)
msarett4aa02d82015-10-06 07:46:02 -0700247 , fFrameRect(frameRect)
248 , fFrameIsSubset(frameIsSubset)
msarett10522ff2015-09-07 08:54:01 -0700249 , fColorTable(NULL)
250 , fSwizzler(NULL)
msarett8c8f22a2015-04-01 06:58:48 -0700251{}
252
scroggob427db12015-08-12 07:24:13 -0700253bool SkGifCodec::onRewind() {
halcanary96fcdcc2015-08-27 07:41:13 -0700254 GifFileType* gifOut = nullptr;
255 if (!ReadHeader(this->stream(), nullptr, &gifOut)) {
scroggob427db12015-08-12 07:24:13 -0700256 return false;
257 }
258
halcanary96fcdcc2015-08-27 07:41:13 -0700259 SkASSERT(nullptr != gifOut);
scroggob427db12015-08-12 07:24:13 -0700260 fGif.reset(gifOut);
261 return true;
262}
263
msarett10522ff2015-09-07 08:54:01 -0700264SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex) {
msarett8c8f22a2015-04-01 06:58:48 -0700265 // Use this as a container to hold information about any gif extension
266 // blocks. This generally stores transparency and animation instructions.
267 SavedImage saveExt;
268 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt);
halcanary96fcdcc2015-08-27 07:41:13 -0700269 saveExt.ExtensionBlocks = nullptr;
msarett8c8f22a2015-04-01 06:58:48 -0700270 saveExt.ExtensionBlockCount = 0;
271 GifByteType* extData;
msarett8c8f22a2015-04-01 06:58:48 -0700272 int32_t extFunction;
msarett8c8f22a2015-04-01 06:58:48 -0700273
274 // We will loop over components of gif images until we find an image. Once
275 // we find an image, we will decode and return it. While many gif files
276 // contain more than one image, we will simply decode the first image.
msarett8c8f22a2015-04-01 06:58:48 -0700277 GifRecordType recordType;
278 do {
279 // Get the current record type
msarett10522ff2015-09-07 08:54:01 -0700280 if (GIF_ERROR == DGifGetRecordType(gif, &recordType)) {
msarett8c8f22a2015-04-01 06:58:48 -0700281 return gif_error("DGifGetRecordType failed.\n", kInvalidInput);
282 }
msarett8c8f22a2015-04-01 06:58:48 -0700283 switch (recordType) {
284 case IMAGE_DESC_RECORD_TYPE: {
msarett10522ff2015-09-07 08:54:01 -0700285 *transIndex = find_trans_index(saveExt);
msarett4aa02d82015-10-06 07:46:02 -0700286
msarett8c8f22a2015-04-01 06:58:48 -0700287 // FIXME: Gif files may have multiple images stored in a single
288 // file. This is most commonly used to enable
289 // animations. Since we are leaving animated gifs as a
290 // TODO, we will return kSuccess after decoding the
291 // first image in the file. This is the same behavior
292 // as SkImageDecoder_libgif.
293 //
294 // Most times this works pretty well, but sometimes it
295 // doesn't. For example, I have an animated test image
296 // where the first image in the file is 1x1, but the
297 // subsequent images are meaningful. This currently
298 // displays the 1x1 image, which is not ideal. Right
299 // now I am leaving this as an issue that will be
300 // addressed when we implement animated gifs.
301 //
302 // It is also possible (not explicitly disallowed in the
303 // specification) that gif files provide multiple
304 // images in a single file that are all meant to be
305 // displayed in the same frame together. I will
306 // currently leave this unimplemented until I find a
307 // test case that expects this behavior.
308 return kSuccess;
309 }
msarett8c8f22a2015-04-01 06:58:48 -0700310 // Extensions are used to specify special properties of the image
311 // such as transparency or animation.
312 case EXTENSION_RECORD_TYPE:
313 // Read extension data
msarett10522ff2015-09-07 08:54:01 -0700314 if (GIF_ERROR == DGifGetExtension(gif, &extFunction, &extData)) {
bungeman0153dea2015-08-27 16:43:42 -0700315 return gif_error("Could not get extension.\n", kIncompleteInput);
msarett8c8f22a2015-04-01 06:58:48 -0700316 }
317
318 // Create an extension block with our data
halcanary96fcdcc2015-08-27 07:41:13 -0700319 while (nullptr != extData) {
msarett8c8f22a2015-04-01 06:58:48 -0700320 // Add a single block
bungeman0153dea2015-08-27 16:43:42 -0700321 if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBlockCount,
322 &saveExt.ExtensionBlocks,
323 extFunction, extData[0], &extData[1]))
324 {
325 return gif_error("Could not add extension block.\n", kIncompleteInput);
msarett8c8f22a2015-04-01 06:58:48 -0700326 }
327 // Move to the next block
msarett10522ff2015-09-07 08:54:01 -0700328 if (GIF_ERROR == DGifGetExtensionNext(gif, &extData)) {
bungeman0153dea2015-08-27 16:43:42 -0700329 return gif_error("Could not get next extension.\n", kIncompleteInput);
msarett8c8f22a2015-04-01 06:58:48 -0700330 }
msarett8c8f22a2015-04-01 06:58:48 -0700331 }
332 break;
333
334 // Signals the end of the gif file
335 case TERMINATE_RECORD_TYPE:
336 break;
337
338 default:
msarett10522ff2015-09-07 08:54:01 -0700339 // DGifGetRecordType returns an error if the record type does
340 // not match one of the above cases. This should not be
341 // reached.
msarett8c8f22a2015-04-01 06:58:48 -0700342 SkASSERT(false);
343 break;
344 }
345 } while (TERMINATE_RECORD_TYPE != recordType);
346
bungeman0153dea2015-08-27 16:43:42 -0700347 return gif_error("Could not find any images to decode in gif file.\n", kInvalidInput);
msarett8c8f22a2015-04-01 06:58:48 -0700348}
msarett10522ff2015-09-07 08:54:01 -0700349
msarett4aa02d82015-10-06 07:46:02 -0700350bool SkGifCodec::GetDimensions(GifFileType* gif, SkISize* size, SkIRect* frameRect) {
351 // Get the encoded dimension values
352 SavedImage* image = &gif->SavedImages[gif->ImageCount - 1];
353 const GifImageDesc& desc = image->ImageDesc;
354 int frameLeft = desc.Left;
355 int frameTop = desc.Top;
356 int frameWidth = desc.Width;
357 int frameHeight = desc.Height;
358 int width = gif->SWidth;
359 int height = gif->SHeight;
360
361 // Ensure that the decode dimensions are large enough to contain the frame
362 width = SkTMax(width, frameWidth + frameLeft);
363 height = SkTMax(height, frameHeight + frameTop);
364
365 // All of these dimensions should be positive, as they are encoded as unsigned 16-bit integers.
366 // It is unclear why giflib casts them to ints. We will go ahead and check that they are
367 // in fact positive.
368 if (frameLeft < 0 || frameTop < 0 || frameWidth < 0 || frameHeight < 0 || width <= 0 ||
369 height <= 0) {
msarett10522ff2015-09-07 08:54:01 -0700370 return false;
371 }
372
msarett4aa02d82015-10-06 07:46:02 -0700373 frameRect->setXYWH(frameLeft, frameTop, frameWidth, frameHeight);
374 size->set(width, height);
msarett10522ff2015-09-07 08:54:01 -0700375 return true;
376}
377
378void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
379 int* inputColorCount) {
380 // Set up our own color table
381 const uint32_t maxColors = 256;
382 SkPMColor colorPtr[256];
383 if (NULL != inputColorCount) {
384 // We set the number of colors to maxColors in order to ensure
385 // safe memory accesses. Otherwise, an invalid pixel could
386 // access memory outside of our color table array.
387 *inputColorCount = maxColors;
388 }
389
390 // Get local color table
391 ColorMapObject* colorMap = fGif->Image.ColorMap;
392 // If there is no local color table, use the global color table
393 if (NULL == colorMap) {
394 colorMap = fGif->SColorMap;
395 }
396
397 uint32_t colorCount = 0;
398 if (NULL != colorMap) {
399 colorCount = colorMap->ColorCount;
400 // giflib guarantees these properties
401 SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPixel)));
402 SkASSERT(colorCount <= 256);
403 for (uint32_t i = 0; i < colorCount; i++) {
404 colorPtr[i] = SkPackARGB32(0xFF, colorMap->Colors[i].Red,
405 colorMap->Colors[i].Green, colorMap->Colors[i].Blue);
406 }
407 }
408
409 // Gifs have the option to specify the color at a single index of the color
410 // table as transparent. If the transparent index is greater than the
411 // colorCount, we know that there is no valid transparent color in the color
412 // table. If there is not valid transparent index, we will try to use the
413 // backgroundIndex as the fill index. If the backgroundIndex is also not
414 // valid, we will let fFillIndex default to 0 (it is set to zero in the
415 // constructor). This behavior is not specified but matches
416 // SkImageDecoder_libgif.
417 uint32_t backgroundIndex = fGif->SBackGroundColor;
418 if (fTransIndex < colorCount) {
419 colorPtr[fTransIndex] = SK_ColorTRANSPARENT;
420 fFillIndex = fTransIndex;
421 } else if (backgroundIndex < colorCount) {
422 fFillIndex = backgroundIndex;
423 }
424
425 // Fill in the color table for indices greater than color count.
426 // This allows for predictable, safe behavior.
427 for (uint32_t i = colorCount; i < maxColors; i++) {
428 colorPtr[i] = colorPtr[fFillIndex];
429 }
430
431 fColorTable.reset(new SkColorTable(colorPtr, maxColors));
432 copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount);
433}
434
435SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
436 int* inputColorCount, const Options& opts) {
msarett10522ff2015-09-07 08:54:01 -0700437 // Check for valid input parameters
msarett10522ff2015-09-07 08:54:01 -0700438 if (!conversion_possible(dstInfo, this->getInfo())) {
439 return gif_error("Cannot convert input type to output type.\n",
440 kInvalidConversion);
441 }
442
msarett10522ff2015-09-07 08:54:01 -0700443 // Initialize color table and copy to the client if necessary
444 this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount);
445 return kSuccess;
446}
447
msarettfdb47572015-10-13 12:50:14 -0700448SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) {
msarett10522ff2015-09-07 08:54:01 -0700449 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
msarettfdb47572015-10-13 12:50:14 -0700450 fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, colorPtr, dstInfo, opts));
msarett10522ff2015-09-07 08:54:01 -0700451 if (nullptr != fSwizzler.get()) {
452 return kSuccess;
453 }
454 return kUnimplemented;
455}
456
msarette6dd0042015-10-09 11:07:34 -0700457bool SkGifCodec::readRow() {
458 return GIF_ERROR != DGifGetLine(fGif, fSrcBuffer.get(), fFrameRect.width());
msarett10522ff2015-09-07 08:54:01 -0700459}
460
461/*
462 * Initiates the gif decode
463 */
464SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
465 void* dst, size_t dstRowBytes,
466 const Options& opts,
467 SkPMColor* inputColorPtr,
msarette6dd0042015-10-09 11:07:34 -0700468 int* inputColorCount,
469 int* rowsDecoded) {
msarett10522ff2015-09-07 08:54:01 -0700470 Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, opts);
471 if (kSuccess != result) {
472 return result;
473 }
474
475 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
476 return gif_error("Scaling not supported.\n", kInvalidScale);
477 }
478
479 // Initialize the swizzler
480 if (fFrameIsSubset) {
msarett4aa02d82015-10-06 07:46:02 -0700481 const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameRect.width(), fFrameRect.height());
msarettfdb47572015-10-13 12:50:14 -0700482 if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts)) {
msarett10522ff2015-09-07 08:54:01 -0700483 return gif_error("Could not initialize swizzler.\n", kUnimplemented);
484 }
485
486 // Fill the background
msarette6dd0042015-10-09 11:07:34 -0700487 SkSampler::Fill(dstInfo, dst, dstRowBytes,
488 this->getFillValue(dstInfo.colorType(), dstInfo.alphaType()),
489 opts.fZeroInitialized);
msarett10522ff2015-09-07 08:54:01 -0700490
491 // Modify the dst pointer
492 const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorType());
msarett4aa02d82015-10-06 07:46:02 -0700493 dst = SkTAddOffset<void*>(dst, dstRowBytes * fFrameRect.top() +
494 dstBytesPerPixel * fFrameRect.left());
msarett10522ff2015-09-07 08:54:01 -0700495 } else {
msarettfdb47572015-10-13 12:50:14 -0700496 if (kSuccess != this->initializeSwizzler(dstInfo, opts)) {
msarett10522ff2015-09-07 08:54:01 -0700497 return gif_error("Could not initialize swizzler.\n", kUnimplemented);
498 }
499 }
500
msarette6dd0042015-10-09 11:07:34 -0700501 // Iterate over rows of the input
msarett4aa02d82015-10-06 07:46:02 -0700502 uint32_t height = fFrameRect.height();
msarette6dd0042015-10-09 11:07:34 -0700503 for (uint32_t y = 0; y < height; y++) {
504 if (!this->readRow()) {
505 *rowsDecoded = y;
506 return gif_error("Could not decode line.\n", kIncompleteInput);
msarett10522ff2015-09-07 08:54:01 -0700507 }
msarette6dd0042015-10-09 11:07:34 -0700508 void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * this->outputScanline(y));
509 fSwizzler->swizzle(dstRow, fSrcBuffer.get());
msarett10522ff2015-09-07 08:54:01 -0700510 }
511 return kSuccess;
512}
513
msarette6dd0042015-10-09 11:07:34 -0700514// FIXME: This is similar to the implementation for bmp and png. Can we share more code or
515// possibly make this non-virtual?
516uint32_t SkGifCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const {
517 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
518 return get_color_table_fill_value(colorType, colorPtr, fFillIndex);
519}
520
scroggo46c57472015-09-30 08:57:13 -0700521SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
522 const SkCodec::Options& opts, SkPMColor inputColorPtr[], int* inputColorCount) {
msarette6dd0042015-10-09 11:07:34 -0700523
524 Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, this->options());
scroggo46c57472015-09-30 08:57:13 -0700525 if (kSuccess != result) {
526 return result;
msarett10522ff2015-09-07 08:54:01 -0700527 }
528
scroggo46c57472015-09-30 08:57:13 -0700529 // Initialize the swizzler
530 if (fFrameIsSubset) {
msarett4aa02d82015-10-06 07:46:02 -0700531 const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameRect.width(), fFrameRect.height());
msarettfdb47572015-10-13 12:50:14 -0700532 if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts)) {
scroggo46c57472015-09-30 08:57:13 -0700533 return gif_error("Could not initialize swizzler.\n", kUnimplemented);
534 }
535 } else {
msarettfdb47572015-10-13 12:50:14 -0700536 if (kSuccess != this->initializeSwizzler(dstInfo, opts)) {
scroggo46c57472015-09-30 08:57:13 -0700537 return gif_error("Could not initialize swizzler.\n", kUnimplemented);
538 }
539 }
msarett10522ff2015-09-07 08:54:01 -0700540
scroggo46c57472015-09-30 08:57:13 -0700541 return kSuccess;
542}
msarett10522ff2015-09-07 08:54:01 -0700543
msarette6dd0042015-10-09 11:07:34 -0700544int SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
545 int rowsBeforeFrame = 0;
546 int rowsAfterFrame = 0;
547 int rowsInFrame = count;
scroggo46c57472015-09-30 08:57:13 -0700548 if (fFrameIsSubset) {
549 // Fill the requested rows
msarette6dd0042015-10-09 11:07:34 -0700550 SkImageInfo fillInfo = this->dstInfo().makeWH(this->dstInfo().width(), count);
551 uint32_t fillValue = this->onGetFillValue(this->dstInfo().colorType(),
552 this->dstInfo().alphaType());
553 SkSampler::Fill(fillInfo, dst, rowBytes, fillValue, this->options().fZeroInitialized);
scroggo46c57472015-09-30 08:57:13 -0700554
555 // Do nothing for rows before the image frame
msarette6dd0042015-10-09 11:07:34 -0700556 rowsBeforeFrame = SkTMax(0, fFrameRect.top() - this->INHERITED::nextScanline());
557 rowsInFrame = SkTMax(0, rowsInFrame - rowsBeforeFrame);
msarett4aa02d82015-10-06 07:46:02 -0700558 dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame);
scroggo46c57472015-09-30 08:57:13 -0700559
560 // Do nothing for rows after the image frame
msarette6dd0042015-10-09 11:07:34 -0700561 rowsAfterFrame = SkTMax(0,
562 this->INHERITED::nextScanline() + rowsInFrame - fFrameRect.bottom());
563 rowsInFrame = SkTMax(0, rowsInFrame - rowsAfterFrame);
msarett10522ff2015-09-07 08:54:01 -0700564
msarett4aa02d82015-10-06 07:46:02 -0700565 // Adjust dst pointer for left offset
566 int offset = SkColorTypeBytesPerPixel(this->dstInfo().colorType()) * fFrameRect.left();
567 dst = SkTAddOffset<void>(dst, offset);
msarett10522ff2015-09-07 08:54:01 -0700568 }
569
msarette6dd0042015-10-09 11:07:34 -0700570 for (int i = 0; i < rowsInFrame; i++) {
571 if (!this->readRow()) {
572 return i + rowsBeforeFrame;
msarett10522ff2015-09-07 08:54:01 -0700573 }
scroggo46c57472015-09-30 08:57:13 -0700574 fSwizzler->swizzle(dst, fSrcBuffer.get());
575 dst = SkTAddOffset<void>(dst, rowBytes);
msarett10522ff2015-09-07 08:54:01 -0700576 }
msarette6dd0042015-10-09 11:07:34 -0700577
578 return count;
msarett10522ff2015-09-07 08:54:01 -0700579}
scroggo46c57472015-09-30 08:57:13 -0700580
581SkCodec::SkScanlineOrder SkGifCodec::onGetScanlineOrder() const {
582 if (fGif->Image.Interlace) {
583 return kOutOfOrder_SkScanlineOrder;
scroggo46c57472015-09-30 08:57:13 -0700584 }
msarette6dd0042015-10-09 11:07:34 -0700585 return kTopDown_SkScanlineOrder;
scroggo46c57472015-09-30 08:57:13 -0700586}
587
msarette6dd0042015-10-09 11:07:34 -0700588int SkGifCodec::onOutputScanline(int inputScanline) const {
scroggo46c57472015-09-30 08:57:13 -0700589 if (fGif->Image.Interlace) {
msarette6dd0042015-10-09 11:07:34 -0700590 if (inputScanline < fFrameRect.top() || inputScanline >= fFrameRect.bottom()) {
591 return inputScanline;
msarett4aa02d82015-10-06 07:46:02 -0700592 }
msarette6dd0042015-10-09 11:07:34 -0700593 return get_output_row_interlaced(inputScanline - fFrameRect.top(), fFrameRect.height());
scroggo46c57472015-09-30 08:57:13 -0700594 }
msarette6dd0042015-10-09 11:07:34 -0700595 return inputScanline;
scroggo46c57472015-09-30 08:57:13 -0700596}