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

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

Issue 1320273004: Revert of Scanline decoding for gifs (Closed) Base URL: https://skia.googlesource.com/skia.git@real-bmp-scan
Patch Set: Created 5 years, 3 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/SkScaledCodec.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2015 Google Inc. 2 * Copyright 2015 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkCodec_libgif.h" 8 #include "SkCodec_libgif.h"
9 #include "SkCodecPriv.h" 9 #include "SkCodecPriv.h"
10 #include "SkColorPriv.h" 10 #include "SkColorPriv.h"
11 #include "SkColorTable.h" 11 #include "SkColorTable.h"
12 #include "SkScaledCodec.h"
13 #include "SkStream.h" 12 #include "SkStream.h"
14 #include "SkSwizzler.h" 13 #include "SkSwizzler.h"
15 #include "SkUtils.h" 14 #include "SkUtils.h"
16 15
17 /* 16 /*
18 * Checks the start of the stream to see if the image is a gif 17 * Checks the start of the stream to see if the image is a gif
19 */ 18 */
20 bool SkGifCodec::IsGif(SkStream* stream) { 19 bool SkGifCodec::IsGif(SkStream* stream) {
21 char buf[GIF_STAMP_LEN]; 20 char buf[GIF_STAMP_LEN];
22 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { 21 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 return (int32_t) stream->read(out, size); 53 return (int32_t) stream->read(out, size);
55 } 54 }
56 55
57 /* 56 /*
58 * Open the gif file 57 * Open the gif file
59 */ 58 */
60 static GifFileType* open_gif(SkStream* stream) { 59 static GifFileType* open_gif(SkStream* stream) {
61 return DGifOpen(stream, read_bytes_callback, nullptr); 60 return DGifOpen(stream, read_bytes_callback, nullptr);
62 } 61 }
63 62
63 /*
64 * This function cleans up the gif object after the decode completes
65 * It is used in a SkAutoTCallIProc template
66 */
67 void SkGifCodec::CloseGif(GifFileType* gif) {
68 DGifCloseFile(gif, nullptr);
69 }
70
71 /*
72 * This function free extension data that has been saved to assist the image
73 * decoder
74 */
75 void SkGifCodec::FreeExtension(SavedImage* image) {
76 if (nullptr != image->ExtensionBlocks) {
77 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks);
78 }
79 }
80
64 /* 81 /*
65 * Check if a there is an index of the color table for a transparent pixel 82 * Check if a there is an index of the color table for a transparent pixel
66 */ 83 */
67 static uint32_t find_trans_index(const SavedImage& image) { 84 static uint32_t find_trans_index(const SavedImage& image) {
68 // If there is a transparent index specified, it will be contained in an 85 // If there is a transparent index specified, it will be contained in an
69 // extension block. We will loop through extension blocks in reverse order 86 // extension block. We will loop through extension blocks in reverse order
70 // to check the most recent extension blocks first. 87 // to check the most recent extension blocks first.
71 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) { 88 for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) {
72 // Get an extension block 89 // Get an extension block
73 const ExtensionBlock& extBlock = image.ExtensionBlocks[i]; 90 const ExtensionBlock& extBlock = image.ExtensionBlocks[i];
74 91
75 // Specifically, we need to check for a graphics control extension, 92 // Specifically, we need to check for a graphics control extension,
76 // which may contain transparency information. Also, note that a valid 93 // which may contain transparency information. Also, note that a valid
77 // graphics control extension is always four bytes. The fourth byte 94 // graphics control extension is always four bytes. The fourth byte
78 // is the transparent index (if it exists), so we need at least four 95 // is the transparent index (if it exists), so we need at least four
79 // bytes. 96 // bytes.
80 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >= 4) { 97 if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >= 4) {
98
81 // Check the transparent color flag which indicates whether a 99 // Check the transparent color flag which indicates whether a
82 // transparent index exists. It is the least significant bit of 100 // transparent index exists. It is the least significant bit of
83 // the first byte of the extension block. 101 // the first byte of the extension block.
84 if (1 == (extBlock.Bytes[0] & 1)) { 102 if (1 == (extBlock.Bytes[0] & 1)) {
103
85 // Use uint32_t to prevent sign extending 104 // Use uint32_t to prevent sign extending
86 return extBlock.Bytes[3]; 105 return extBlock.Bytes[3];
87 } 106 }
88 107
89 // There should only be one graphics control extension for the image frame 108 // There should only be one graphics control extension for the image frame
90 break; 109 break;
91 } 110 }
92 } 111 }
93 112
94 // Use maximum unsigned int (surely an invalid index) to indicate that a val id 113 // Use maximum unsigned int (surely an invalid index) to indicate that a val id
(...skipping 20 matching lines...) Expand all
115 } 134 }
116 // Third pass 135 // Third pass
117 if (encodedRow * 2 < height) { 136 if (encodedRow * 2 < height) {
118 return 2 + 4 * (encodedRow - ceil_div(height, 4)); 137 return 2 + 4 * (encodedRow - ceil_div(height, 4));
119 } 138 }
120 // Fourth pass 139 // Fourth pass
121 return 1 + 2 * (encodedRow - ceil_div(height, 2)); 140 return 1 + 2 * (encodedRow - ceil_div(height, 2));
122 } 141 }
123 142
124 /* 143 /*
125 * This function cleans up the gif object after the decode completes
126 * It is used in a SkAutoTCallIProc template
127 */
128 void SkGifCodec::CloseGif(GifFileType* gif) {
129 DGifCloseFile(gif, NULL);
130 }
131
132 /*
133 * This function free extension data that has been saved to assist the image
134 * decoder
135 */
136 void SkGifCodec::FreeExtension(SavedImage* image) {
137 if (NULL != image->ExtensionBlocks) {
138 GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks);
139 }
140 }
141
142 /*
143 * Read enough of the stream to initialize the SkGifCodec. 144 * Read enough of the stream to initialize the SkGifCodec.
144 * Returns a bool representing success or failure. 145 * Returns a bool representing success or failure.
145 * 146 *
146 * @param codecOut 147 * @param codecOut
147 * If it returned true, and codecOut was not nullptr, 148 * If it returned true, and codecOut was not nullptr,
148 * codecOut will be set to a new SkGifCodec. 149 * codecOut will be set to a new SkGifCodec.
149 * 150 *
150 * @param gifOut 151 * @param gifOut
151 * If it returned true, and codecOut was nullptr, 152 * If it returned true, and codecOut was nullptr,
152 * gifOut must be non-nullptr and gifOut will be set to a new 153 * gifOut must be non-nullptr and gifOut will be set to a new
153 * GifFileType pointer. 154 * GifFileType pointer.
154 * 155 *
155 * @param stream 156 * @param stream
156 * Deleted on failure. 157 * Deleted on failure.
157 * codecOut will take ownership of it in the case where we created a codec. 158 * codecOut will take ownership of it in the case where we created a codec.
158 * Ownership is unchanged when we returned a gifOut. 159 * Ownership is unchanged when we returned a gifOut.
159 * 160 *
160 */ 161 */
161 bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) { 162 bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) {
162 SkAutoTDelete<SkStream> streamDeleter(stream); 163 SkAutoTDelete<SkStream> streamDeleter(stream);
163 164
164 // Read gif header, logical screen descriptor, and global color table 165 // Read gif header, logical screen descriptor, and global color table
165 SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); 166 SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream));
166 167
167 if (nullptr == gif) { 168 if (nullptr == gif) {
168 gif_error("DGifOpen failed.\n"); 169 gif_error("DGifOpen failed.\n");
169 return false; 170 return false;
170 } 171 }
171 172
172 // Read through gif extensions to get to the image data. Set the
173 // transparent index based on the extension data.
174 uint32_t transIndex;
175 SkCodec::Result result = ReadUpToFirstImage(gif, &transIndex);
176 if (kSuccess != result){
177 return false;
178 }
179
180 // Read the image descriptor
181 if (GIF_ERROR == DGifGetImageDesc(gif)) {
182 return false;
183 }
184 // If reading the image descriptor is successful, the image count will be
185 // incremented.
186 SkASSERT(gif->ImageCount >= 1);
187
188 if (nullptr != codecOut) { 173 if (nullptr != codecOut) {
189 // Get fields from header 174 // Get fields from header
190 const int32_t width = gif->SWidth; 175 const int32_t width = gif->SWidth;
191 const int32_t height = gif->SHeight; 176 const int32_t height = gif->SHeight;
192 if (width <= 0 || height <= 0) { 177 if (width <= 0 || height <= 0) {
193 gif_error("Invalid dimensions.\n"); 178 gif_error("Invalid dimensions.\n");
194 return false; 179 return false;
195 } 180 }
196 181
197 // Determine the recommended alpha type. The transIndex might be valid if it less
198 // than 256. We are not certain that the index is valid until we proces s the color
199 // table, since some gifs have color tables with less than 256 colors. If
200 // there might be a valid transparent index, we must indicate that the i mage has
201 // alpha.
202 // In the case where we must support alpha, we have the option to set th e
203 // suggested alpha type to kPremul or kUnpremul. Both are valid since t he alpha
204 // component will always be 0xFF or the entire 32-bit pixel will be set to zero.
205 // We prefer kPremul because we support kPremul, and it is more efficien t to use
206 // kPremul directly even when kUnpremul is supported.
207 SkAlphaType alphaType = (transIndex < 256) ? kPremul_SkAlphaType : kOpaq ue_SkAlphaType;
208
209 // Return the codec 182 // Return the codec
210 // kIndex is the most natural color type for gifs, so we set this as 183 // kIndex is the most natural color type for gifs, so we set this as
211 // the default. 184 // the default.
185 // Many gifs specify a color table index for transparent pixels. Every
186 // other pixel is guaranteed to be opaque. Despite this, because of the
187 // possiblity of transparent pixels, we cannot assume that the image is
188 // opaque. We have the option to set the alpha type as kPremul or
189 // kUnpremul. Both are valid since the alpha component will always be
190 // 0xFF or the entire 32-bit pixel will be set to zero. We prefer
191 // kPremul because we support kPremul, and it is more efficient to
192 // use kPremul directly even when kUnpremul is supported.
212 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, 193 const SkImageInfo& imageInfo = SkImageInfo::Make(width, height,
213 kIndex_8_SkColorType, alphaType); 194 kIndex_8_SkColorType, k Premul_SkAlphaType);
214 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach (), transIndex); 195 *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach ());
215 } else { 196 } else {
216 SkASSERT(nullptr != gifOut); 197 SkASSERT(nullptr != gifOut);
217 streamDeleter.detach(); 198 streamDeleter.detach();
218 *gifOut = gif.detach(); 199 *gifOut = gif.detach();
219 } 200 }
220 return true; 201 return true;
221 } 202 }
222 203
223 /* 204 /*
224 * Assumes IsGif was called and returned true 205 * Assumes IsGif was called and returned true
225 * Creates a gif decoder 206 * Creates a gif decoder
226 * Reads enough of the stream to determine the image format 207 * Reads enough of the stream to determine the image format
227 */ 208 */
228 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { 209 SkCodec* SkGifCodec::NewFromStream(SkStream* stream) {
229 SkCodec* codec = nullptr; 210 SkCodec* codec = nullptr;
230 if (ReadHeader(stream, &codec, nullptr)) { 211 if (ReadHeader(stream, &codec, nullptr)) {
231 return codec; 212 return codec;
232 } 213 }
233 return nullptr; 214 return nullptr;
234 } 215 }
235 216
236 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType * gif, 217 SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType * gif)
237 uint32_t transIndex)
238 : INHERITED(srcInfo, stream) 218 : INHERITED(srcInfo, stream)
239 , fGif(gif) 219 , fGif(gif)
240 , fSrcBuffer(new uint8_t[this->getInfo().width()])
241 // If it is valid, fTransIndex will be used to set fFillIndex. We don't kno w if
242 // fTransIndex is valid until we process the color table, since fTransIndex may
243 // be greater than the size of the color table.
244 , fTransIndex(transIndex)
245 // Default fFillIndex is 0. We will overwrite this if fTransIndex is valid, or if
246 // there is a valid background color.
247 , fFillIndex(0)
248 , fFrameDims(SkIRect::MakeEmpty())
249 , fFrameIsSubset(false)
250 , fColorTable(NULL)
251 , fSwizzler(NULL)
252 {} 220 {}
253 221
254 bool SkGifCodec::onRewind() { 222 bool SkGifCodec::onRewind() {
255 GifFileType* gifOut = nullptr; 223 GifFileType* gifOut = nullptr;
256 if (!ReadHeader(this->stream(), nullptr, &gifOut)) { 224 if (!ReadHeader(this->stream(), nullptr, &gifOut)) {
257 return false; 225 return false;
258 } 226 }
259 227
260 SkASSERT(nullptr != gifOut); 228 SkASSERT(nullptr != gifOut);
261 fGif.reset(gifOut); 229 fGif.reset(gifOut);
262 return true; 230 return true;
263 } 231 }
264 232
265 SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* trans Index) { 233 /*
234 * Initiates the gif decode
235 */
236 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
237 void* dst, size_t dstRowBytes,
238 const Options& opts,
239 SkPMColor* inputColorPtr,
240 int* inputColorCount) {
241 // Rewind if necessary
242 if (!this->rewindIfNeeded()) {
243 return kCouldNotRewind;
244 }
245
246 // Check for valid input parameters
247 if (opts.fSubset) {
248 // Subsets are not supported.
249 return kUnimplemented;
250 }
251 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
252 return gif_error("Scaling not supported.\n", kInvalidScale);
253 }
254 if (!conversion_possible(dstInfo, this->getInfo())) {
255 return gif_error("Cannot convert input type to output type.\n", kInvalid Conversion);
256 }
257
266 // Use this as a container to hold information about any gif extension 258 // Use this as a container to hold information about any gif extension
267 // blocks. This generally stores transparency and animation instructions. 259 // blocks. This generally stores transparency and animation instructions.
268 SavedImage saveExt; 260 SavedImage saveExt;
269 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); 261 SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt);
270 saveExt.ExtensionBlocks = nullptr; 262 saveExt.ExtensionBlocks = nullptr;
271 saveExt.ExtensionBlockCount = 0; 263 saveExt.ExtensionBlockCount = 0;
272 GifByteType* extData; 264 GifByteType* extData;
273 int32_t extFunction; 265 int32_t extFunction;
274 266
275 // We will loop over components of gif images until we find an image. Once 267 // We will loop over components of gif images until we find an image. Once
276 // we find an image, we will decode and return it. While many gif files 268 // we find an image, we will decode and return it. While many gif files
277 // contain more than one image, we will simply decode the first image. 269 // contain more than one image, we will simply decode the first image.
270 const int32_t width = dstInfo.width();
271 const int32_t height = dstInfo.height();
278 GifRecordType recordType; 272 GifRecordType recordType;
279 do { 273 do {
280 // Get the current record type 274 // Get the current record type
281 if (GIF_ERROR == DGifGetRecordType(gif, &recordType)) { 275 if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) {
282 return gif_error("DGifGetRecordType failed.\n", kInvalidInput); 276 return gif_error("DGifGetRecordType failed.\n", kInvalidInput);
283 } 277 }
278
284 switch (recordType) { 279 switch (recordType) {
285 case IMAGE_DESC_RECORD_TYPE: { 280 case IMAGE_DESC_RECORD_TYPE: {
286 *transIndex = find_trans_index(saveExt); 281 // Read the image descriptor
282 if (GIF_ERROR == DGifGetImageDesc(fGif)) {
283 return gif_error("DGifGetImageDesc failed.\n", kInvalidInput );
284 }
285
286 // If reading the image descriptor is successful, the image
287 // count will be incremented
288 SkASSERT(fGif->ImageCount >= 1);
289 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1];
290
291 // Process the descriptor
292 const GifImageDesc& desc = image->ImageDesc;
293 int32_t imageLeft = desc.Left;
294 int32_t imageTop = desc.Top;
295 int32_t innerWidth = desc.Width;
296 int32_t innerHeight = desc.Height;
297 // Fail on non-positive dimensions
298 if (innerWidth <= 0 || innerHeight <= 0) {
299 return gif_error("Invalid dimensions for inner image.\n", kI nvalidInput);
300 }
301 // Treat the following cases as warnings and try to fix
302 if (innerWidth > width) {
303 gif_warning("Inner image too wide, shrinking.\n");
304 innerWidth = width;
305 imageLeft = 0;
306 } else if (imageLeft + innerWidth > width) {
307 gif_warning("Shifting inner image to left to fit.\n");
308 imageLeft = width - innerWidth;
309 } else if (imageLeft < 0) {
310 gif_warning("Shifting image to right to fit\n");
311 imageLeft = 0;
312 }
313 if (innerHeight > height) {
314 gif_warning("Inner image too tall, shrinking.\n");
315 innerHeight = height;
316 imageTop = 0;
317 } else if (imageTop + innerHeight > height) {
318 gif_warning("Shifting inner image up to fit.\n");
319 imageTop = height - innerHeight;
320 } else if (imageTop < 0) {
321 gif_warning("Shifting image down to fit\n");
322 imageTop = 0;
323 }
324
325 // Create a color table to store colors the giflib colorMap
326 SkPMColor alternateColorPtr[256];
327 SkPMColor* colorTable;
328 SkColorType dstColorType = dstInfo.colorType();
329 if (kIndex_8_SkColorType == dstColorType) {
330 SkASSERT(nullptr != inputColorPtr);
331 SkASSERT(nullptr != inputColorCount);
332 colorTable = inputColorPtr;
333 } else {
334 colorTable = alternateColorPtr;
335 }
336
337 // Set up the color table
338 uint32_t colorCount = 0;
339 // Allocate maximum storage to deal with invalid indices safely
340 const uint32_t maxColors = 256;
341 ColorMapObject* colorMap = fGif->Image.ColorMap;
342 // If there is no local color table, use the global color table
343 if (nullptr == colorMap) {
344 colorMap = fGif->SColorMap;
345 }
346 if (nullptr != colorMap) {
347 colorCount = colorMap->ColorCount;
348 SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPi xel)));
349 SkASSERT(colorCount <= 256);
350 for (uint32_t i = 0; i < colorCount; i++) {
351 colorTable[i] = SkPackARGB32(0xFF,
352 colorMap->Colors[i].Red,
353 colorMap->Colors[i].Green,
354 colorMap->Colors[i].Blue);
355 }
356 }
357
358 // This is used to fill unspecified pixels in the image data.
359 uint32_t fillIndex = fGif->SBackGroundColor;
360 ZeroInitialized zeroInit = opts.fZeroInitialized;
361
362 // Gifs have the option to specify the color at a single
363 // index of the color table as transparent.
364 {
365 // Get the transparent index. If the return value of this
366 // function is greater than the colorCount, we know that
367 // there is no valid transparent color in the color table.
368 // This occurs if there is no graphics control extension or
369 // if the index specified by the graphics control extension
370 // is out of range.
371 uint32_t transIndex = find_trans_index(saveExt);
372
373 if (transIndex < colorCount) {
374 colorTable[transIndex] = SK_ColorTRANSPARENT;
375 // If there is a transparent index, we also use this as
376 // the fill index.
377 fillIndex = transIndex;
378 } else if (fillIndex >= colorCount) {
379 // If the fill index is invalid, we default to 0. This
380 // behavior is unspecified but matches SkImageDecoder.
381 fillIndex = 0;
382 }
383 }
384
385 // Fill in the color table for indices greater than color count.
386 // This allows for predictable, safe behavior.
387 for (uint32_t i = colorCount; i < maxColors; i++) {
388 colorTable[i] = colorTable[fillIndex];
389 }
390
391 // Check if image is only a subset of the image frame
392 SkAutoTDelete<SkSwizzler> swizzler(nullptr);
393 if (innerWidth < width || innerHeight < height) {
394
395 // Modify the destination info
396 const SkImageInfo subsetDstInfo = dstInfo.makeWH(innerWidth, innerHeight);
397
398 // Fill the destination with the fill color
399 // FIXME: This may not be the behavior that we want for
400 // animated gifs where we draw on top of the
401 // previous frame.
402 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, fillInde x, colorTable,
403 zeroInit);
404
405 // Modify the dst pointer
406 const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(ds tColorType);
407 dst = SkTAddOffset<void*>(dst,
408 dstRowBytes * imageTop +
409 dstBytesPerPixel * imageLeft);
410
411 // Create the subset swizzler
412 swizzler.reset(SkSwizzler::CreateSwizzler(
413 SkSwizzler::kIndex, colorTable, subsetDstInfo,
414 zeroInit, this->getInfo()));
415 } else {
416 // Create the fully dimensional swizzler
417 swizzler.reset(SkSwizzler::CreateSwizzler(
418 SkSwizzler::kIndex, colorTable, dstInfo,
419 zeroInit, this->getInfo()));
420 }
421
422 // Stores output from dgiflib and input to the swizzler
423 SkAutoTDeleteArray<uint8_t> buffer(new uint8_t[innerWidth]);
424
425 // Check the interlace flag and iterate over rows of the input
426 if (fGif->Image.Interlace) {
427 for (int32_t y = 0; y < innerHeight; y++) {
428 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), innerWi dth)) {
429 // Recover from error by filling remainder of image
430 memset(buffer.get(), fillIndex, innerWidth);
431 for (; y < innerHeight; y++) {
432 void* dstRow = SkTAddOffset<void>(dst, dstRowByt es *
433 get_output_row_interlaced(y, innerHeight ));
434 swizzler->swizzle(dstRow, buffer.get());
435 }
436 return gif_error(SkStringPrintf(
437 "Could not decode line %d of %d.\n",
438 y, height - 1).c_str(), kIncompleteInput);
439 }
440 void* dstRow = SkTAddOffset<void>(dst,
441 dstRowBytes * get_output_row_interlaced(y, inner Height));
442 swizzler->swizzle(dstRow, buffer.get());
443 }
444 } else {
445 // Standard mode
446 void* dstRow = dst;
447 for (int32_t y = 0; y < innerHeight; y++) {
448 if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), innerWi dth)) {
449 SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes, inner Height - y,
450 fillIndex, colorTable, zeroInit);
451 return gif_error(SkStringPrintf(
452 "Could not decode line %d of %d.\n",
453 y, height - 1).c_str(), kIncompleteInput);
454 }
455 swizzler->swizzle(dstRow, buffer.get());
456 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
457 }
458 }
459
287 // FIXME: Gif files may have multiple images stored in a single 460 // FIXME: Gif files may have multiple images stored in a single
288 // file. This is most commonly used to enable 461 // file. This is most commonly used to enable
289 // animations. Since we are leaving animated gifs as a 462 // animations. Since we are leaving animated gifs as a
290 // TODO, we will return kSuccess after decoding the 463 // TODO, we will return kSuccess after decoding the
291 // first image in the file. This is the same behavior 464 // first image in the file. This is the same behavior
292 // as SkImageDecoder_libgif. 465 // as SkImageDecoder_libgif.
293 // 466 //
294 // Most times this works pretty well, but sometimes it 467 // Most times this works pretty well, but sometimes it
295 // doesn't. For example, I have an animated test image 468 // doesn't. For example, I have an animated test image
296 // where the first image in the file is 1x1, but the 469 // where the first image in the file is 1x1, but the
297 // subsequent images are meaningful. This currently 470 // subsequent images are meaningful. This currently
298 // displays the 1x1 image, which is not ideal. Right 471 // displays the 1x1 image, which is not ideal. Right
299 // now I am leaving this as an issue that will be 472 // now I am leaving this as an issue that will be
300 // addressed when we implement animated gifs. 473 // addressed when we implement animated gifs.
301 // 474 //
302 // It is also possible (not explicitly disallowed in the 475 // It is also possible (not explicitly disallowed in the
303 // specification) that gif files provide multiple 476 // specification) that gif files provide multiple
304 // images in a single file that are all meant to be 477 // images in a single file that are all meant to be
305 // displayed in the same frame together. I will 478 // displayed in the same frame together. I will
306 // currently leave this unimplemented until I find a 479 // currently leave this unimplemented until I find a
307 // test case that expects this behavior. 480 // test case that expects this behavior.
308 return kSuccess; 481 return kSuccess;
309 } 482 }
483
310 // Extensions are used to specify special properties of the image 484 // Extensions are used to specify special properties of the image
311 // such as transparency or animation. 485 // such as transparency or animation.
312 case EXTENSION_RECORD_TYPE: 486 case EXTENSION_RECORD_TYPE:
313 // Read extension data 487 // Read extension data
314 if (GIF_ERROR == DGifGetExtension(gif, &extFunction, &extData)) { 488 if (GIF_ERROR == DGifGetExtension(fGif, &extFunction, &extData)) {
315 return gif_error("Could not get extension.\n", kIncompleteIn put); 489 return gif_error("Could not get extension.\n", kIncompleteIn put);
316 } 490 }
317 491
318 // Create an extension block with our data 492 // Create an extension block with our data
319 while (nullptr != extData) { 493 while (nullptr != extData) {
320 // Add a single block 494 // Add a single block
321 if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBloc kCount, 495 if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBloc kCount,
322 &saveExt.ExtensionBloc ks, 496 &saveExt.ExtensionBloc ks,
323 extFunction, extData[0 ], &extData[1])) 497 extFunction, extData[0 ], &extData[1]))
324 { 498 {
325 return gif_error("Could not add extension block.\n", kIn completeInput); 499 return gif_error("Could not add extension block.\n", kIn completeInput);
326 } 500 }
327 // Move to the next block 501 // Move to the next block
328 if (GIF_ERROR == DGifGetExtensionNext(gif, &extData)) { 502 if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) {
329 return gif_error("Could not get next extension.\n", kInc ompleteInput); 503 return gif_error("Could not get next extension.\n", kInc ompleteInput);
330 } 504 }
331 } 505 }
332 break; 506 break;
333 507
334 // Signals the end of the gif file 508 // Signals the end of the gif file
335 case TERMINATE_RECORD_TYPE: 509 case TERMINATE_RECORD_TYPE:
336 break; 510 break;
337 511
338 default: 512 default:
339 // DGifGetRecordType returns an error if the record type does 513 // giflib returns an error code if the record type is not known.
340 // not match one of the above cases. This should not be 514 // We should catch this error immediately.
341 // reached.
342 SkASSERT(false); 515 SkASSERT(false);
343 break; 516 break;
344 } 517 }
345 } while (TERMINATE_RECORD_TYPE != recordType); 518 } while (TERMINATE_RECORD_TYPE != recordType);
346 519
347 return gif_error("Could not find any images to decode in gif file.\n", kInva lidInput); 520 return gif_error("Could not find any images to decode in gif file.\n", kInva lidInput);
348 } 521 }
349
350 /*
351 * A gif may contain many image frames, all of different sizes.
352 * This function checks if the frame dimensions are valid and corrects them if
353 * necessary.
354 */
355 bool SkGifCodec::setFrameDimensions(const GifImageDesc& desc) {
356 // Fail on non-positive dimensions
357 int32_t frameLeft = desc.Left;
358 int32_t frameTop = desc.Top;
359 int32_t frameWidth = desc.Width;
360 int32_t frameHeight = desc.Height;
361 int32_t height = this->getInfo().height();
362 int32_t width = this->getInfo().width();
363 if (frameWidth <= 0 || frameHeight <= 0) {
364 return false;
365 }
366
367 // Treat the following cases as warnings and try to fix
368 if (frameWidth > width) {
369 gif_warning("Image frame too wide, shrinking.\n");
370 frameWidth = width;
371 frameLeft = 0;
372 } else if (frameLeft + frameWidth > width) {
373 gif_warning("Shifting image frame to left to fit.\n");
374 frameLeft = width - frameWidth;
375 } else if (frameLeft < 0) {
376 gif_warning("Shifting image frame to right to fit\n");
377 frameLeft = 0;
378 }
379 if (frameHeight > height) {
380 gif_warning("Image frame too tall, shrinking.\n");
381 frameHeight = height;
382 frameTop = 0;
383 } else if (frameTop + frameHeight > height) {
384 gif_warning("Shifting image frame up to fit.\n");
385 frameTop = height - frameHeight;
386 } else if (frameTop < 0) {
387 gif_warning("Shifting image frame down to fit\n");
388 frameTop = 0;
389 }
390 fFrameDims.setXYWH(frameLeft, frameTop, frameWidth, frameHeight);
391
392 // Indicate if the frame dimensions do not match the header dimensions
393 if (this->getInfo().dimensions() != fFrameDims.size()) {
394 fFrameIsSubset = true;
395 }
396
397 return true;
398 }
399
400 void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* inp utColorPtr,
401 int* inputColorCount) {
402 // Set up our own color table
403 const uint32_t maxColors = 256;
404 SkPMColor colorPtr[256];
405 if (NULL != inputColorCount) {
406 // We set the number of colors to maxColors in order to ensure
407 // safe memory accesses. Otherwise, an invalid pixel could
408 // access memory outside of our color table array.
409 *inputColorCount = maxColors;
410 }
411
412 // Get local color table
413 ColorMapObject* colorMap = fGif->Image.ColorMap;
414 // If there is no local color table, use the global color table
415 if (NULL == colorMap) {
416 colorMap = fGif->SColorMap;
417 }
418
419 uint32_t colorCount = 0;
420 if (NULL != colorMap) {
421 colorCount = colorMap->ColorCount;
422 // giflib guarantees these properties
423 SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPixel)));
424 SkASSERT(colorCount <= 256);
425 for (uint32_t i = 0; i < colorCount; i++) {
426 colorPtr[i] = SkPackARGB32(0xFF, colorMap->Colors[i].Red,
427 colorMap->Colors[i].Green, colorMap->Colors[i].Blue);
428 }
429 }
430
431 // Gifs have the option to specify the color at a single index of the color
432 // table as transparent. If the transparent index is greater than the
433 // colorCount, we know that there is no valid transparent color in the color
434 // table. If there is not valid transparent index, we will try to use the
435 // backgroundIndex as the fill index. If the backgroundIndex is also not
436 // valid, we will let fFillIndex default to 0 (it is set to zero in the
437 // constructor). This behavior is not specified but matches
438 // SkImageDecoder_libgif.
439 uint32_t backgroundIndex = fGif->SBackGroundColor;
440 if (fTransIndex < colorCount) {
441 colorPtr[fTransIndex] = SK_ColorTRANSPARENT;
442 fFillIndex = fTransIndex;
443 } else if (backgroundIndex < colorCount) {
444 fFillIndex = backgroundIndex;
445 }
446
447 // Fill in the color table for indices greater than color count.
448 // This allows for predictable, safe behavior.
449 for (uint32_t i = colorCount; i < maxColors; i++) {
450 colorPtr[i] = colorPtr[fFillIndex];
451 }
452
453 fColorTable.reset(new SkColorTable(colorPtr, maxColors));
454 copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount) ;
455 }
456
457 SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColo r* inputColorPtr,
458 int* inputColorCount, const Options& opts) {
459 // Rewind if necessary
460 if (!this->rewindIfNeeded()) {
461 return kCouldNotRewind;
462 }
463
464 // Check for valid input parameters
465 if (opts.fSubset) {
466 // Subsets are not supported.
467 return kUnimplemented;
468 }
469 if (!conversion_possible(dstInfo, this->getInfo())) {
470 return gif_error("Cannot convert input type to output type.\n",
471 kInvalidConversion);
472 }
473
474
475 // We have asserted that the image count is at least one in ReadHeader().
476 SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1];
477 const GifImageDesc& desc = image->ImageDesc;
478
479 // Check that the frame dimensions are valid and set them
480 if(!this->setFrameDimensions(desc)) {
481 return gif_error("Invalid dimensions for image frame.\n", kInvalidInput) ;
482 }
483
484 // Initialize color table and copy to the client if necessary
485 this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount);
486 return kSuccess;
487 }
488
489 SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo,
490 ZeroInitialized zeroInit) {
491 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
492 fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex,
493 colorPtr, dstInfo, zeroInit, this->getInfo()));
494 if (nullptr != fSwizzler.get()) {
495 return kSuccess;
496 }
497 return kUnimplemented;
498 }
499
500 SkCodec::Result SkGifCodec::readRow() {
501 if (GIF_ERROR == DGifGetLine(fGif, fSrcBuffer.get(), fFrameDims.width())) {
502 return kIncompleteInput;
503 }
504 return kSuccess;
505 }
506
507 /*
508 * Initiates the gif decode
509 */
510 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
511 void* dst, size_t dstRowBytes,
512 const Options& opts,
513 SkPMColor* inputColorPtr,
514 int* inputColorCount) {
515 Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCoun t, opts);
516 if (kSuccess != result) {
517 return result;
518 }
519
520 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
521 return gif_error("Scaling not supported.\n", kInvalidScale);
522 }
523
524 // Initialize the swizzler
525 if (fFrameIsSubset) {
526 const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameDims.width(), fFr ameDims.height());
527 if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitia lized)) {
528 return gif_error("Could not initialize swizzler.\n", kUnimplemented) ;
529 }
530
531 // Fill the background
532 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
533 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, this->getInfo().height(),
534 fFillIndex, colorPtr, opts.fZeroInitialized);
535
536 // Modify the dst pointer
537 const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorT ype());
538 dst = SkTAddOffset<void*>(dst, dstRowBytes * fFrameDims.top() +
539 dstBytesPerPixel * fFrameDims.left());
540 } else {
541 if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized) ) {
542 return gif_error("Could not initialize swizzler.\n", kUnimplemented) ;
543 }
544 }
545
546 // Check the interlace flag and iterate over rows of the input
547 uint32_t width = fFrameDims.width();
548 uint32_t height = fFrameDims.height();
549 if (fGif->Image.Interlace) {
550 // In interlace mode, the rows of input are rearranged in
551 // the output image. We a helper function to help us
552 // rearrange the decoded rows.
553 for (uint32_t y = 0; y < height; y++) {
554 if (kSuccess != this->readRow()) {
555 // Recover from error by filling remainder of image
556 memset(fSrcBuffer.get(), fFillIndex, width);
557 for (; y < height; y++) {
558 void* dstRow = SkTAddOffset<void>(dst,
559 dstRowBytes * get_output_row_interlaced(y, height));
560 fSwizzler->swizzle(dstRow, fSrcBuffer.get());
561 }
562 return gif_error("Could not decode line.\n", kIncompleteInput);
563 }
564 void* dstRow = SkTAddOffset<void>(dst,
565 dstRowBytes * get_output_row_interlaced(y, height));
566 fSwizzler->swizzle(dstRow, fSrcBuffer.get());
567 }
568 } else {
569 // Standard mode
570 void* dstRow = dst;
571 for (uint32_t y = 0; y < height; y++) {
572 if (kSuccess != this->readRow()) {
573 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
574 SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes,
575 height - y, fFillIndex, colorPtr, opts.fZeroInitialized) ;
576 return gif_error("Could not decode line\n", kIncompleteInput);
577 }
578 fSwizzler->swizzle(dstRow, fSrcBuffer.get());
579 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
580 }
581 }
582 return kSuccess;
583 }
584
585 // TODO (msarett): skbug.com/3582
586 // Should we implement reallyHasAlpha? Or should we read extens ion blocks in the
587 // header? Or should we do both?
588
589 class SkGifScanlineDecoder : public SkScanlineDecoder {
590 public:
591 SkGifScanlineDecoder(const SkImageInfo& srcInfo, SkGifCodec* codec)
592 : INHERITED(srcInfo)
593 , fCodec(codec)
594 {}
595
596 SkEncodedFormat onGetEncodedFormat() const override {
597 return kGIF_SkEncodedFormat;
598 }
599
600 SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& opts,
601 SkPMColor inputColorPtr[], int* inputColorCount) ove rride {
602 SkCodec::Result result = fCodec->prepareToDecode(dstInfo, inputColorPtr, inputColorCount,
603 this->options());
604 if (SkCodec::kSuccess != result) {
605 return result;
606 }
607
608 // Check to see if scaling was requested.
609 if (dstInfo.dimensions() != this->getInfo().dimensions()) {
610 if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
611 return gif_error("Scaling not supported.\n", SkCodec::kInvalidSc ale);
612 }
613 }
614
615 // Initialize the swizzler
616 if (fCodec->fFrameIsSubset) {
617 int sampleX;
618 SkScaledCodec::ComputeSampleSize(dstInfo, fCodec->getInfo(), &sample X, NULL);
619 const SkImageInfo subsetDstInfo = dstInfo.makeWH(
620 get_scaled_dimension(fCodec->fFrameDims.width(), sampleX),
621 fCodec->fFrameDims.height());
622 if (SkCodec::kSuccess != fCodec->initializeSwizzler(subsetDstInfo,
623 opts.fZeroInitialized)) {
624 return gif_error("Could not initialize swizzler.\n", SkCodec::kU nimplemented);
625 }
626 } else {
627 if (SkCodec::kSuccess != fCodec->initializeSwizzler(dstInfo, opts.fZ eroInitialized)) {
628 return gif_error("Could not initialize swizzler.\n", SkCodec::kU nimplemented);
629 }
630 }
631
632 return SkCodec::kSuccess;
633 }
634
635 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) overri de {
636 if (fCodec->fFrameIsSubset) {
637 // Fill the requested rows
638 const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.get()) ;
639 SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count, fCodec->fFil lIndex,
640 colorPtr, this->options().fZeroInitialized);
641
642 // Do nothing for rows before the image frame
643 int rowsBeforeFrame = fCodec->fFrameDims.top() - INHERITED::getY();
644 if (rowsBeforeFrame > 0) {
645 count = SkTMin(0, count - rowsBeforeFrame);
646 dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame);
647 }
648
649 // Do nothing for rows after the image frame
650 int rowsAfterFrame = INHERITED::getY() + count - fCodec->fFrameDims. bottom();
651 if (rowsAfterFrame > 0) {
652 count = SkTMin(0, count - rowsAfterFrame);
653 }
654
655 // Adjust dst pointer for left offset
656 dst = SkTAddOffset<void>(dst, SkColorTypeBytesPerPixel(
657 this->dstInfo().colorType()) * fCodec->fFrameDims.left());
658 }
659
660 for (int i = 0; i < count; i++) {
661 if (SkCodec::kSuccess != fCodec->readRow()) {
662 const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.ge t());
663 SkSwizzler::Fill(dst, this->dstInfo(), rowBytes,
664 count - i, fCodec->fFillIndex, colorPtr,
665 this->options().fZeroInitialized);
666 return gif_error("Could not decode line\n", SkCodec::kIncomplete Input);
667 }
668 fCodec->fSwizzler->swizzle(dst, fCodec->fSrcBuffer.get());
669 dst = SkTAddOffset<void>(dst, rowBytes);
670 }
671 return SkCodec::kSuccess;
672 }
673
674 SkScanlineOrder onGetScanlineOrder() const override {
675 if (fCodec->fGif->Image.Interlace) {
676 return kOutOfOrder_SkScanlineOrder;
677 } else {
678 return kTopDown_SkScanlineOrder;
679 }
680 }
681
682 int onGetY() const override {
683 if (fCodec->fGif->Image.Interlace) {
684 return get_output_row_interlaced(INHERITED::onGetY(), this->dstInfo( ).height());
685 } else {
686 return INHERITED::onGetY();
687 }
688 }
689
690 private:
691 SkAutoTDelete<SkGifCodec> fCodec;
692
693 typedef SkScanlineDecoder INHERITED;
694 };
695
696 SkScanlineDecoder* SkGifCodec::NewSDFromStream(SkStream* stream) {
697 SkAutoTDelete<SkGifCodec> codec (static_cast<SkGifCodec*>(SkGifCodec::NewFro mStream(stream)));
698 if (!codec) {
699 return NULL;
700 }
701
702 const SkImageInfo& srcInfo = codec->getInfo();
703
704 return SkNEW_ARGS(SkGifScanlineDecoder, (srcInfo, codec.detach()));
705 }
OLDNEW
« no previous file with comments | « src/codec/SkCodec_libgif.h ('k') | src/codec/SkScaledCodec.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698