Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: src/codec/SkCodec_libgif.cpp

Issue 1567863003: Rename SkGifCodec, SkIcoCodec, SkWbmpCodec (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/codec/SkCodec_libgif.h ('k') | src/codec/SkCodec_libico.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « src/codec/SkCodec_libgif.h ('k') | src/codec/SkCodec_libico.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698