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 |