blob: ca9c81271267427d47a67117c77f9d3cc85fc724 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/images/SkImageDecoder_libgif.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkMovie.h"
19#include "SkColor.h"
20#include "SkColorPriv.h"
21#include "SkStream.h"
22#include "SkTemplates.h"
23
24#include "gif_lib.h"
25
26class SkGIFMovie : public SkMovie {
27public:
28 SkGIFMovie(SkStream* stream);
29 virtual ~SkGIFMovie();
30
31protected:
32 virtual bool onGetInfo(Info*);
33 virtual bool onSetTime(SkMSec);
34 virtual bool onGetBitmap(SkBitmap*);
35
36private:
37 GifFileType* fGIF;
38 SavedImage* fCurrSavedImage;
39};
40
41SkMovie* SkMovie_GIF_Factory(SkStream* stream) {
42 char buf[GIF_STAMP_LEN];
43 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
44 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 ||
45 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
46 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
47 stream->rewind();
48 return SkNEW_ARGS(SkGIFMovie, (stream));
49 }
50 }
51 return NULL;
52}
53
54static int Decode(GifFileType* fileType, GifByteType* out, int size) {
55 SkStream* stream = (SkStream*) fileType->UserData;
56 return (int) stream->read(out, size);
57}
58
59SkGIFMovie::SkGIFMovie(SkStream* stream)
60{
61 fGIF = DGifOpen( stream, Decode );
62 if (NULL == fGIF)
63 return;
64
65 if (DGifSlurp(fGIF) != GIF_OK)
66 {
67 DGifCloseFile(fGIF);
68 fGIF = NULL;
69 }
70 fCurrSavedImage = NULL;
71}
72
73SkGIFMovie::~SkGIFMovie()
74{
75 if (fGIF)
76 DGifCloseFile(fGIF);
77}
78
79static SkMSec savedimage_duration(const SavedImage* image)
80{
81 for (int j = 0; j < image->ExtensionBlockCount; j++)
82 {
83 if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
84 {
85 int size = image->ExtensionBlocks[j].ByteCount;
86 SkASSERT(size >= 4);
87 const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
88 return ((b[2] << 8) | b[1]) * 10;
89 }
90 }
91 return 0;
92}
93
94bool SkGIFMovie::onGetInfo(Info* info)
95{
96 if (NULL == fGIF)
97 return false;
98
99 SkMSec dur = 0;
100 for (int i = 0; i < fGIF->ImageCount; i++)
101 dur += savedimage_duration(&fGIF->SavedImages[i]);
102
103 info->fDuration = dur;
104 info->fWidth = fGIF->SWidth;
105 info->fHeight = fGIF->SHeight;
106 info->fIsOpaque = false; // how to compute?
107 return true;
108}
109
110bool SkGIFMovie::onSetTime(SkMSec time)
111{
112 if (NULL == fGIF)
113 return false;
114
115 SkMSec dur = 0;
116 for (int i = 0; i < fGIF->ImageCount; i++)
117 {
118 dur += savedimage_duration(&fGIF->SavedImages[i]);
119 if (dur >= time)
120 {
121 SavedImage* prev = fCurrSavedImage;
122 fCurrSavedImage = &fGIF->SavedImages[i];
123 return prev != fCurrSavedImage;
124 }
125 }
126 fCurrSavedImage = &fGIF->SavedImages[fGIF->ImageCount - 1];
127 return true;
128}
129
130bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
131{
132 GifFileType* gif = fGIF;
133 if (NULL == gif)
134 return false;
135
136 // should we check for the Image cmap or the global (SColorMap) first?
137 ColorMapObject* cmap = gif->SColorMap;
138 if (cmap == NULL)
139 cmap = gif->Image.ColorMap;
140
141 if (cmap == NULL || gif->ImageCount < 1 || cmap->ColorCount != (1 << cmap->BitsPerPixel))
142 {
143 SkASSERT(!"bad colortable setup");
144 return false;
145 }
146
147 const int width = gif->SWidth;
148 const int height = gif->SHeight;
149 if (width <= 0 || height <= 0) {
150 return false;
151 }
152
153 SavedImage* gif_image = fCurrSavedImage;
154 SkBitmap::Config config = SkBitmap::kIndex8_Config;
155
156 SkColorTable* colorTable = SkNEW_ARGS(SkColorTable, (cmap->ColorCount));
157 SkAutoUnref aur(colorTable);
158
159 bm->setConfig(config, width, height, 0);
160 if (!bm->allocPixels(colorTable)) {
161 return false;
162 }
163
164 int transparent = -1;
165 for (int i = 0; i < gif_image->ExtensionBlockCount; ++i) {
166 ExtensionBlock* eb = gif_image->ExtensionBlocks + i;
167 if (eb->Function == 0xF9 &&
168 eb->ByteCount == 4) {
169 bool has_transparency = ((eb->Bytes[0] & 1) == 1);
170 if (has_transparency) {
171 transparent = (unsigned char)eb->Bytes[3];
172 }
173 }
174 }
175
176 SkPMColor* colorPtr = colorTable->lockColors();
177
178 if (transparent >= 0)
179 memset(colorPtr, 0, cmap->ColorCount * 4);
180 else
181 colorTable->setFlags(colorTable->getFlags() | SkColorTable::kColorsAreOpaque_Flag);
182
183 for (int index = 0; index < cmap->ColorCount; index++)
184 {
185 if (transparent != index)
186 colorPtr[index] = SkPackARGB32(0xFF, cmap->Colors[index].Red,
187 cmap->Colors[index].Green, cmap->Colors[index].Blue);
188 }
189 colorTable->unlockColors(true);
190
191 unsigned char* in = (unsigned char*)gif_image->RasterBits;
192 unsigned char* out = bm->getAddr8(0, 0);
193 if (gif->Image.Interlace) {
194
195 // deinterlace
196 int row;
197 // group 1 - every 8th row, starting with row 0
198 for (row = 0; row < height; row += 8) {
199 memcpy(out + width * row, in, width);
200 in += width;
201 }
202
203 // group 2 - every 8th row, starting with row 4
204 for (row = 4; row < height; row += 8) {
205 memcpy(out + width * row, in, width);
206 in += width;
207 }
208
209 // group 3 - every 4th row, starting with row 2
210 for (row = 2; row < height; row += 4) {
211 memcpy(out + width * row, in, width);
212 in += width;
213 }
214
215 for (row = 1; row < height; row += 2) {
216 memcpy(out + width * row, in, width);
217 in += width;
218 }
219
220 } else {
221 memcpy(out, in, width * height);
222 }
223 return true;
224}