1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright 2006 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*d57664e9SAndroid Build Coastguard Worker * found in the LICENSE file.
6*d57664e9SAndroid Build Coastguard Worker */
7*d57664e9SAndroid Build Coastguard Worker
8*d57664e9SAndroid Build Coastguard Worker
9*d57664e9SAndroid Build Coastguard Worker #include "Movie.h"
10*d57664e9SAndroid Build Coastguard Worker #include "SkBitmap.h"
11*d57664e9SAndroid Build Coastguard Worker #include "SkColor.h"
12*d57664e9SAndroid Build Coastguard Worker #include "SkColorPriv.h"
13*d57664e9SAndroid Build Coastguard Worker #include "SkStream.h"
14*d57664e9SAndroid Build Coastguard Worker #include "SkTypes.h"
15*d57664e9SAndroid Build Coastguard Worker
16*d57664e9SAndroid Build Coastguard Worker #include "gif_lib.h"
17*d57664e9SAndroid Build Coastguard Worker
18*d57664e9SAndroid Build Coastguard Worker #include <log/log.h>
19*d57664e9SAndroid Build Coastguard Worker
20*d57664e9SAndroid Build Coastguard Worker #include <string.h>
21*d57664e9SAndroid Build Coastguard Worker
22*d57664e9SAndroid Build Coastguard Worker #if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
23*d57664e9SAndroid Build Coastguard Worker #define DGifCloseFile(a, b) DGifCloseFile(a)
24*d57664e9SAndroid Build Coastguard Worker #endif
25*d57664e9SAndroid Build Coastguard Worker
26*d57664e9SAndroid Build Coastguard Worker class GIFMovie : public Movie {
27*d57664e9SAndroid Build Coastguard Worker public:
28*d57664e9SAndroid Build Coastguard Worker explicit GIFMovie(SkStream* stream);
29*d57664e9SAndroid Build Coastguard Worker virtual ~GIFMovie();
30*d57664e9SAndroid Build Coastguard Worker
31*d57664e9SAndroid Build Coastguard Worker protected:
32*d57664e9SAndroid Build Coastguard Worker virtual bool onGetInfo(Info*);
33*d57664e9SAndroid Build Coastguard Worker virtual bool onSetTime(Movie::MSec);
34*d57664e9SAndroid Build Coastguard Worker virtual bool onGetBitmap(SkBitmap*);
35*d57664e9SAndroid Build Coastguard Worker
36*d57664e9SAndroid Build Coastguard Worker private:
37*d57664e9SAndroid Build Coastguard Worker GifFileType* fGIF;
38*d57664e9SAndroid Build Coastguard Worker int fCurrIndex;
39*d57664e9SAndroid Build Coastguard Worker int fLastDrawIndex;
40*d57664e9SAndroid Build Coastguard Worker SkBitmap fBackup;
41*d57664e9SAndroid Build Coastguard Worker SkColor fPaintingColor;
42*d57664e9SAndroid Build Coastguard Worker };
43*d57664e9SAndroid Build Coastguard Worker
Decode(GifFileType * fileType,GifByteType * out,int size)44*d57664e9SAndroid Build Coastguard Worker static int Decode(GifFileType* fileType, GifByteType* out, int size) {
45*d57664e9SAndroid Build Coastguard Worker SkStream* stream = (SkStream*) fileType->UserData;
46*d57664e9SAndroid Build Coastguard Worker return (int) stream->read(out, size);
47*d57664e9SAndroid Build Coastguard Worker }
48*d57664e9SAndroid Build Coastguard Worker
GIFMovie(SkStream * stream)49*d57664e9SAndroid Build Coastguard Worker GIFMovie::GIFMovie(SkStream* stream)
50*d57664e9SAndroid Build Coastguard Worker {
51*d57664e9SAndroid Build Coastguard Worker #if GIFLIB_MAJOR < 5
52*d57664e9SAndroid Build Coastguard Worker fGIF = DGifOpen( stream, Decode );
53*d57664e9SAndroid Build Coastguard Worker #else
54*d57664e9SAndroid Build Coastguard Worker fGIF = DGifOpen( stream, Decode, nullptr );
55*d57664e9SAndroid Build Coastguard Worker #endif
56*d57664e9SAndroid Build Coastguard Worker if (nullptr == fGIF)
57*d57664e9SAndroid Build Coastguard Worker return;
58*d57664e9SAndroid Build Coastguard Worker
59*d57664e9SAndroid Build Coastguard Worker if (DGifSlurp(fGIF) != GIF_OK)
60*d57664e9SAndroid Build Coastguard Worker {
61*d57664e9SAndroid Build Coastguard Worker DGifCloseFile(fGIF, nullptr);
62*d57664e9SAndroid Build Coastguard Worker fGIF = nullptr;
63*d57664e9SAndroid Build Coastguard Worker }
64*d57664e9SAndroid Build Coastguard Worker fCurrIndex = -1;
65*d57664e9SAndroid Build Coastguard Worker fLastDrawIndex = -1;
66*d57664e9SAndroid Build Coastguard Worker fPaintingColor = SkPackARGB32(0, 0, 0, 0);
67*d57664e9SAndroid Build Coastguard Worker }
68*d57664e9SAndroid Build Coastguard Worker
~GIFMovie()69*d57664e9SAndroid Build Coastguard Worker GIFMovie::~GIFMovie()
70*d57664e9SAndroid Build Coastguard Worker {
71*d57664e9SAndroid Build Coastguard Worker if (fGIF)
72*d57664e9SAndroid Build Coastguard Worker DGifCloseFile(fGIF, nullptr);
73*d57664e9SAndroid Build Coastguard Worker }
74*d57664e9SAndroid Build Coastguard Worker
savedimage_duration(const SavedImage * image)75*d57664e9SAndroid Build Coastguard Worker static Movie::MSec savedimage_duration(const SavedImage* image)
76*d57664e9SAndroid Build Coastguard Worker {
77*d57664e9SAndroid Build Coastguard Worker for (int j = 0; j < image->ExtensionBlockCount; j++)
78*d57664e9SAndroid Build Coastguard Worker {
79*d57664e9SAndroid Build Coastguard Worker if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
80*d57664e9SAndroid Build Coastguard Worker {
81*d57664e9SAndroid Build Coastguard Worker SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4);
82*d57664e9SAndroid Build Coastguard Worker const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
83*d57664e9SAndroid Build Coastguard Worker return ((b[2] << 8) | b[1]) * 10;
84*d57664e9SAndroid Build Coastguard Worker }
85*d57664e9SAndroid Build Coastguard Worker }
86*d57664e9SAndroid Build Coastguard Worker return 0;
87*d57664e9SAndroid Build Coastguard Worker }
88*d57664e9SAndroid Build Coastguard Worker
onGetInfo(Info * info)89*d57664e9SAndroid Build Coastguard Worker bool GIFMovie::onGetInfo(Info* info)
90*d57664e9SAndroid Build Coastguard Worker {
91*d57664e9SAndroid Build Coastguard Worker if (nullptr == fGIF)
92*d57664e9SAndroid Build Coastguard Worker return false;
93*d57664e9SAndroid Build Coastguard Worker
94*d57664e9SAndroid Build Coastguard Worker Movie::MSec dur = 0;
95*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < fGIF->ImageCount; i++)
96*d57664e9SAndroid Build Coastguard Worker dur += savedimage_duration(&fGIF->SavedImages[i]);
97*d57664e9SAndroid Build Coastguard Worker
98*d57664e9SAndroid Build Coastguard Worker info->fDuration = dur;
99*d57664e9SAndroid Build Coastguard Worker info->fWidth = fGIF->SWidth;
100*d57664e9SAndroid Build Coastguard Worker info->fHeight = fGIF->SHeight;
101*d57664e9SAndroid Build Coastguard Worker info->fIsOpaque = false; // how to compute?
102*d57664e9SAndroid Build Coastguard Worker return true;
103*d57664e9SAndroid Build Coastguard Worker }
104*d57664e9SAndroid Build Coastguard Worker
onSetTime(Movie::MSec time)105*d57664e9SAndroid Build Coastguard Worker bool GIFMovie::onSetTime(Movie::MSec time)
106*d57664e9SAndroid Build Coastguard Worker {
107*d57664e9SAndroid Build Coastguard Worker if (nullptr == fGIF)
108*d57664e9SAndroid Build Coastguard Worker return false;
109*d57664e9SAndroid Build Coastguard Worker
110*d57664e9SAndroid Build Coastguard Worker Movie::MSec dur = 0;
111*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < fGIF->ImageCount; i++)
112*d57664e9SAndroid Build Coastguard Worker {
113*d57664e9SAndroid Build Coastguard Worker dur += savedimage_duration(&fGIF->SavedImages[i]);
114*d57664e9SAndroid Build Coastguard Worker if (dur >= time)
115*d57664e9SAndroid Build Coastguard Worker {
116*d57664e9SAndroid Build Coastguard Worker fCurrIndex = i;
117*d57664e9SAndroid Build Coastguard Worker return fLastDrawIndex != fCurrIndex;
118*d57664e9SAndroid Build Coastguard Worker }
119*d57664e9SAndroid Build Coastguard Worker }
120*d57664e9SAndroid Build Coastguard Worker fCurrIndex = fGIF->ImageCount - 1;
121*d57664e9SAndroid Build Coastguard Worker return true;
122*d57664e9SAndroid Build Coastguard Worker }
123*d57664e9SAndroid Build Coastguard Worker
copyLine(uint32_t * dst,const unsigned char * src,const ColorMapObject * cmap,int transparent,int width)124*d57664e9SAndroid Build Coastguard Worker static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
125*d57664e9SAndroid Build Coastguard Worker int transparent, int width)
126*d57664e9SAndroid Build Coastguard Worker {
127*d57664e9SAndroid Build Coastguard Worker for (; width > 0; width--, src++, dst++) {
128*d57664e9SAndroid Build Coastguard Worker if (*src != transparent && *src < cmap->ColorCount) {
129*d57664e9SAndroid Build Coastguard Worker const GifColorType& col = cmap->Colors[*src];
130*d57664e9SAndroid Build Coastguard Worker *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
131*d57664e9SAndroid Build Coastguard Worker }
132*d57664e9SAndroid Build Coastguard Worker }
133*d57664e9SAndroid Build Coastguard Worker }
134*d57664e9SAndroid Build Coastguard Worker
135*d57664e9SAndroid Build Coastguard Worker #if GIFLIB_MAJOR < 5
copyInterlaceGroup(SkBitmap * bm,const unsigned char * & src,const ColorMapObject * cmap,int transparent,int copyWidth,int copyHeight,const GifImageDesc & imageDesc,int rowStep,int startRow)136*d57664e9SAndroid Build Coastguard Worker static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
137*d57664e9SAndroid Build Coastguard Worker const ColorMapObject* cmap, int transparent, int copyWidth,
138*d57664e9SAndroid Build Coastguard Worker int copyHeight, const GifImageDesc& imageDesc, int rowStep,
139*d57664e9SAndroid Build Coastguard Worker int startRow)
140*d57664e9SAndroid Build Coastguard Worker {
141*d57664e9SAndroid Build Coastguard Worker int row;
142*d57664e9SAndroid Build Coastguard Worker // every 'rowStep'th row, starting with row 'startRow'
143*d57664e9SAndroid Build Coastguard Worker for (row = startRow; row < copyHeight; row += rowStep) {
144*d57664e9SAndroid Build Coastguard Worker uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
145*d57664e9SAndroid Build Coastguard Worker copyLine(dst, src, cmap, transparent, copyWidth);
146*d57664e9SAndroid Build Coastguard Worker src += imageDesc.Width;
147*d57664e9SAndroid Build Coastguard Worker }
148*d57664e9SAndroid Build Coastguard Worker
149*d57664e9SAndroid Build Coastguard Worker // pad for rest height
150*d57664e9SAndroid Build Coastguard Worker src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
151*d57664e9SAndroid Build Coastguard Worker }
152*d57664e9SAndroid Build Coastguard Worker
blitInterlace(SkBitmap * bm,const SavedImage * frame,const ColorMapObject * cmap,int transparent)153*d57664e9SAndroid Build Coastguard Worker static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
154*d57664e9SAndroid Build Coastguard Worker int transparent)
155*d57664e9SAndroid Build Coastguard Worker {
156*d57664e9SAndroid Build Coastguard Worker int width = bm->width();
157*d57664e9SAndroid Build Coastguard Worker int height = bm->height();
158*d57664e9SAndroid Build Coastguard Worker GifWord copyWidth = frame->ImageDesc.Width;
159*d57664e9SAndroid Build Coastguard Worker if (frame->ImageDesc.Left + copyWidth > width) {
160*d57664e9SAndroid Build Coastguard Worker copyWidth = width - frame->ImageDesc.Left;
161*d57664e9SAndroid Build Coastguard Worker }
162*d57664e9SAndroid Build Coastguard Worker
163*d57664e9SAndroid Build Coastguard Worker GifWord copyHeight = frame->ImageDesc.Height;
164*d57664e9SAndroid Build Coastguard Worker if (frame->ImageDesc.Top + copyHeight > height) {
165*d57664e9SAndroid Build Coastguard Worker copyHeight = height - frame->ImageDesc.Top;
166*d57664e9SAndroid Build Coastguard Worker }
167*d57664e9SAndroid Build Coastguard Worker
168*d57664e9SAndroid Build Coastguard Worker // deinterlace
169*d57664e9SAndroid Build Coastguard Worker const unsigned char* src = (unsigned char*)frame->RasterBits;
170*d57664e9SAndroid Build Coastguard Worker
171*d57664e9SAndroid Build Coastguard Worker // group 1 - every 8th row, starting with row 0
172*d57664e9SAndroid Build Coastguard Worker copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);
173*d57664e9SAndroid Build Coastguard Worker
174*d57664e9SAndroid Build Coastguard Worker // group 2 - every 8th row, starting with row 4
175*d57664e9SAndroid Build Coastguard Worker copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);
176*d57664e9SAndroid Build Coastguard Worker
177*d57664e9SAndroid Build Coastguard Worker // group 3 - every 4th row, starting with row 2
178*d57664e9SAndroid Build Coastguard Worker copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);
179*d57664e9SAndroid Build Coastguard Worker
180*d57664e9SAndroid Build Coastguard Worker copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
181*d57664e9SAndroid Build Coastguard Worker }
182*d57664e9SAndroid Build Coastguard Worker #endif
183*d57664e9SAndroid Build Coastguard Worker
blitNormal(SkBitmap * bm,const SavedImage * frame,const ColorMapObject * cmap,int transparent)184*d57664e9SAndroid Build Coastguard Worker static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
185*d57664e9SAndroid Build Coastguard Worker int transparent)
186*d57664e9SAndroid Build Coastguard Worker {
187*d57664e9SAndroid Build Coastguard Worker int width = bm->width();
188*d57664e9SAndroid Build Coastguard Worker int height = bm->height();
189*d57664e9SAndroid Build Coastguard Worker const unsigned char* src = (unsigned char*)frame->RasterBits;
190*d57664e9SAndroid Build Coastguard Worker uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
191*d57664e9SAndroid Build Coastguard Worker GifWord copyWidth = frame->ImageDesc.Width;
192*d57664e9SAndroid Build Coastguard Worker if (frame->ImageDesc.Left + copyWidth > width) {
193*d57664e9SAndroid Build Coastguard Worker copyWidth = width - frame->ImageDesc.Left;
194*d57664e9SAndroid Build Coastguard Worker }
195*d57664e9SAndroid Build Coastguard Worker
196*d57664e9SAndroid Build Coastguard Worker GifWord copyHeight = frame->ImageDesc.Height;
197*d57664e9SAndroid Build Coastguard Worker if (frame->ImageDesc.Top + copyHeight > height) {
198*d57664e9SAndroid Build Coastguard Worker copyHeight = height - frame->ImageDesc.Top;
199*d57664e9SAndroid Build Coastguard Worker }
200*d57664e9SAndroid Build Coastguard Worker
201*d57664e9SAndroid Build Coastguard Worker for (; copyHeight > 0; copyHeight--) {
202*d57664e9SAndroid Build Coastguard Worker copyLine(dst, src, cmap, transparent, copyWidth);
203*d57664e9SAndroid Build Coastguard Worker src += frame->ImageDesc.Width;
204*d57664e9SAndroid Build Coastguard Worker dst += width;
205*d57664e9SAndroid Build Coastguard Worker }
206*d57664e9SAndroid Build Coastguard Worker }
207*d57664e9SAndroid Build Coastguard Worker
fillRect(SkBitmap * bm,GifWord left,GifWord top,GifWord width,GifWord height,uint32_t col)208*d57664e9SAndroid Build Coastguard Worker static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
209*d57664e9SAndroid Build Coastguard Worker uint32_t col)
210*d57664e9SAndroid Build Coastguard Worker {
211*d57664e9SAndroid Build Coastguard Worker int bmWidth = bm->width();
212*d57664e9SAndroid Build Coastguard Worker int bmHeight = bm->height();
213*d57664e9SAndroid Build Coastguard Worker uint32_t* dst = bm->getAddr32(left, top);
214*d57664e9SAndroid Build Coastguard Worker GifWord copyWidth = width;
215*d57664e9SAndroid Build Coastguard Worker if (left + copyWidth > bmWidth) {
216*d57664e9SAndroid Build Coastguard Worker copyWidth = bmWidth - left;
217*d57664e9SAndroid Build Coastguard Worker }
218*d57664e9SAndroid Build Coastguard Worker
219*d57664e9SAndroid Build Coastguard Worker GifWord copyHeight = height;
220*d57664e9SAndroid Build Coastguard Worker if (top + copyHeight > bmHeight) {
221*d57664e9SAndroid Build Coastguard Worker copyHeight = bmHeight - top;
222*d57664e9SAndroid Build Coastguard Worker }
223*d57664e9SAndroid Build Coastguard Worker
224*d57664e9SAndroid Build Coastguard Worker size_t bytes = copyWidth * SkColorTypeBytesPerPixel(bm->colorType());
225*d57664e9SAndroid Build Coastguard Worker for (; copyHeight > 0; copyHeight--) {
226*d57664e9SAndroid Build Coastguard Worker memset(dst, col, bytes);
227*d57664e9SAndroid Build Coastguard Worker dst += bmWidth;
228*d57664e9SAndroid Build Coastguard Worker }
229*d57664e9SAndroid Build Coastguard Worker }
230*d57664e9SAndroid Build Coastguard Worker
drawFrame(SkBitmap * bm,const SavedImage * frame,const ColorMapObject * cmap)231*d57664e9SAndroid Build Coastguard Worker static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
232*d57664e9SAndroid Build Coastguard Worker {
233*d57664e9SAndroid Build Coastguard Worker int transparent = -1;
234*d57664e9SAndroid Build Coastguard Worker
235*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
236*d57664e9SAndroid Build Coastguard Worker ExtensionBlock* eb = frame->ExtensionBlocks + i;
237*d57664e9SAndroid Build Coastguard Worker if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
238*d57664e9SAndroid Build Coastguard Worker eb->ByteCount == 4) {
239*d57664e9SAndroid Build Coastguard Worker bool has_transparency = ((eb->Bytes[0] & 1) == 1);
240*d57664e9SAndroid Build Coastguard Worker if (has_transparency) {
241*d57664e9SAndroid Build Coastguard Worker transparent = (unsigned char)eb->Bytes[3];
242*d57664e9SAndroid Build Coastguard Worker }
243*d57664e9SAndroid Build Coastguard Worker }
244*d57664e9SAndroid Build Coastguard Worker }
245*d57664e9SAndroid Build Coastguard Worker
246*d57664e9SAndroid Build Coastguard Worker if (frame->ImageDesc.ColorMap != nullptr) {
247*d57664e9SAndroid Build Coastguard Worker // use local color table
248*d57664e9SAndroid Build Coastguard Worker cmap = frame->ImageDesc.ColorMap;
249*d57664e9SAndroid Build Coastguard Worker }
250*d57664e9SAndroid Build Coastguard Worker
251*d57664e9SAndroid Build Coastguard Worker if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
252*d57664e9SAndroid Build Coastguard Worker ALOGD("bad colortable setup");
253*d57664e9SAndroid Build Coastguard Worker return;
254*d57664e9SAndroid Build Coastguard Worker }
255*d57664e9SAndroid Build Coastguard Worker
256*d57664e9SAndroid Build Coastguard Worker #if GIFLIB_MAJOR < 5
257*d57664e9SAndroid Build Coastguard Worker // before GIFLIB 5, de-interlacing wasn't done by library at load time
258*d57664e9SAndroid Build Coastguard Worker if (frame->ImageDesc.Interlace) {
259*d57664e9SAndroid Build Coastguard Worker blitInterlace(bm, frame, cmap, transparent);
260*d57664e9SAndroid Build Coastguard Worker return;
261*d57664e9SAndroid Build Coastguard Worker }
262*d57664e9SAndroid Build Coastguard Worker #endif
263*d57664e9SAndroid Build Coastguard Worker
264*d57664e9SAndroid Build Coastguard Worker blitNormal(bm, frame, cmap, transparent);
265*d57664e9SAndroid Build Coastguard Worker }
266*d57664e9SAndroid Build Coastguard Worker
checkIfWillBeCleared(const SavedImage * frame)267*d57664e9SAndroid Build Coastguard Worker static bool checkIfWillBeCleared(const SavedImage* frame)
268*d57664e9SAndroid Build Coastguard Worker {
269*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
270*d57664e9SAndroid Build Coastguard Worker ExtensionBlock* eb = frame->ExtensionBlocks + i;
271*d57664e9SAndroid Build Coastguard Worker if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
272*d57664e9SAndroid Build Coastguard Worker eb->ByteCount == 4) {
273*d57664e9SAndroid Build Coastguard Worker // check disposal method
274*d57664e9SAndroid Build Coastguard Worker int disposal = ((eb->Bytes[0] >> 2) & 7);
275*d57664e9SAndroid Build Coastguard Worker if (disposal == 2 || disposal == 3) {
276*d57664e9SAndroid Build Coastguard Worker return true;
277*d57664e9SAndroid Build Coastguard Worker }
278*d57664e9SAndroid Build Coastguard Worker }
279*d57664e9SAndroid Build Coastguard Worker }
280*d57664e9SAndroid Build Coastguard Worker return false;
281*d57664e9SAndroid Build Coastguard Worker }
282*d57664e9SAndroid Build Coastguard Worker
getTransparencyAndDisposalMethod(const SavedImage * frame,bool * trans,int * disposal)283*d57664e9SAndroid Build Coastguard Worker static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
284*d57664e9SAndroid Build Coastguard Worker {
285*d57664e9SAndroid Build Coastguard Worker *trans = false;
286*d57664e9SAndroid Build Coastguard Worker *disposal = 0;
287*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
288*d57664e9SAndroid Build Coastguard Worker ExtensionBlock* eb = frame->ExtensionBlocks + i;
289*d57664e9SAndroid Build Coastguard Worker if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
290*d57664e9SAndroid Build Coastguard Worker eb->ByteCount == 4) {
291*d57664e9SAndroid Build Coastguard Worker *trans = ((eb->Bytes[0] & 1) == 1);
292*d57664e9SAndroid Build Coastguard Worker *disposal = ((eb->Bytes[0] >> 2) & 7);
293*d57664e9SAndroid Build Coastguard Worker }
294*d57664e9SAndroid Build Coastguard Worker }
295*d57664e9SAndroid Build Coastguard Worker }
296*d57664e9SAndroid Build Coastguard Worker
297*d57664e9SAndroid Build Coastguard Worker // return true if area of 'target' is completely covers area of 'covered'
checkIfCover(const SavedImage * target,const SavedImage * covered)298*d57664e9SAndroid Build Coastguard Worker static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
299*d57664e9SAndroid Build Coastguard Worker {
300*d57664e9SAndroid Build Coastguard Worker if (target->ImageDesc.Left <= covered->ImageDesc.Left
301*d57664e9SAndroid Build Coastguard Worker && covered->ImageDesc.Left + covered->ImageDesc.Width <=
302*d57664e9SAndroid Build Coastguard Worker target->ImageDesc.Left + target->ImageDesc.Width
303*d57664e9SAndroid Build Coastguard Worker && target->ImageDesc.Top <= covered->ImageDesc.Top
304*d57664e9SAndroid Build Coastguard Worker && covered->ImageDesc.Top + covered->ImageDesc.Height <=
305*d57664e9SAndroid Build Coastguard Worker target->ImageDesc.Top + target->ImageDesc.Height) {
306*d57664e9SAndroid Build Coastguard Worker return true;
307*d57664e9SAndroid Build Coastguard Worker }
308*d57664e9SAndroid Build Coastguard Worker return false;
309*d57664e9SAndroid Build Coastguard Worker }
310*d57664e9SAndroid Build Coastguard Worker
disposeFrameIfNeeded(SkBitmap * bm,const SavedImage * cur,const SavedImage * next,SkBitmap * backup,SkColor color)311*d57664e9SAndroid Build Coastguard Worker static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
312*d57664e9SAndroid Build Coastguard Worker SkBitmap* backup, SkColor color)
313*d57664e9SAndroid Build Coastguard Worker {
314*d57664e9SAndroid Build Coastguard Worker // We can skip disposal process if next frame is not transparent
315*d57664e9SAndroid Build Coastguard Worker // and completely covers current area
316*d57664e9SAndroid Build Coastguard Worker bool curTrans;
317*d57664e9SAndroid Build Coastguard Worker int curDisposal;
318*d57664e9SAndroid Build Coastguard Worker getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
319*d57664e9SAndroid Build Coastguard Worker bool nextTrans;
320*d57664e9SAndroid Build Coastguard Worker int nextDisposal;
321*d57664e9SAndroid Build Coastguard Worker getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
322*d57664e9SAndroid Build Coastguard Worker if ((curDisposal == 2 || curDisposal == 3)
323*d57664e9SAndroid Build Coastguard Worker && (nextTrans || !checkIfCover(next, cur))) {
324*d57664e9SAndroid Build Coastguard Worker switch (curDisposal) {
325*d57664e9SAndroid Build Coastguard Worker // restore to background color
326*d57664e9SAndroid Build Coastguard Worker // -> 'background' means background under this image.
327*d57664e9SAndroid Build Coastguard Worker case 2:
328*d57664e9SAndroid Build Coastguard Worker fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
329*d57664e9SAndroid Build Coastguard Worker cur->ImageDesc.Width, cur->ImageDesc.Height,
330*d57664e9SAndroid Build Coastguard Worker color);
331*d57664e9SAndroid Build Coastguard Worker break;
332*d57664e9SAndroid Build Coastguard Worker
333*d57664e9SAndroid Build Coastguard Worker // restore to previous
334*d57664e9SAndroid Build Coastguard Worker case 3:
335*d57664e9SAndroid Build Coastguard Worker bm->swap(*backup);
336*d57664e9SAndroid Build Coastguard Worker break;
337*d57664e9SAndroid Build Coastguard Worker }
338*d57664e9SAndroid Build Coastguard Worker }
339*d57664e9SAndroid Build Coastguard Worker
340*d57664e9SAndroid Build Coastguard Worker // Save current image if next frame's disposal method == 3
341*d57664e9SAndroid Build Coastguard Worker if (nextDisposal == 3) {
342*d57664e9SAndroid Build Coastguard Worker const uint32_t* src = bm->getAddr32(0, 0);
343*d57664e9SAndroid Build Coastguard Worker uint32_t* dst = backup->getAddr32(0, 0);
344*d57664e9SAndroid Build Coastguard Worker int cnt = bm->width() * bm->height();
345*d57664e9SAndroid Build Coastguard Worker memcpy(dst, src, cnt*sizeof(uint32_t));
346*d57664e9SAndroid Build Coastguard Worker }
347*d57664e9SAndroid Build Coastguard Worker }
348*d57664e9SAndroid Build Coastguard Worker
onGetBitmap(SkBitmap * bm)349*d57664e9SAndroid Build Coastguard Worker bool GIFMovie::onGetBitmap(SkBitmap* bm)
350*d57664e9SAndroid Build Coastguard Worker {
351*d57664e9SAndroid Build Coastguard Worker const GifFileType* gif = fGIF;
352*d57664e9SAndroid Build Coastguard Worker if (nullptr == gif)
353*d57664e9SAndroid Build Coastguard Worker return false;
354*d57664e9SAndroid Build Coastguard Worker
355*d57664e9SAndroid Build Coastguard Worker if (gif->ImageCount < 1) {
356*d57664e9SAndroid Build Coastguard Worker return false;
357*d57664e9SAndroid Build Coastguard Worker }
358*d57664e9SAndroid Build Coastguard Worker
359*d57664e9SAndroid Build Coastguard Worker const int width = gif->SWidth;
360*d57664e9SAndroid Build Coastguard Worker const int height = gif->SHeight;
361*d57664e9SAndroid Build Coastguard Worker if (width <= 0 || height <= 0) {
362*d57664e9SAndroid Build Coastguard Worker return false;
363*d57664e9SAndroid Build Coastguard Worker }
364*d57664e9SAndroid Build Coastguard Worker
365*d57664e9SAndroid Build Coastguard Worker // no need to draw
366*d57664e9SAndroid Build Coastguard Worker if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
367*d57664e9SAndroid Build Coastguard Worker return true;
368*d57664e9SAndroid Build Coastguard Worker }
369*d57664e9SAndroid Build Coastguard Worker
370*d57664e9SAndroid Build Coastguard Worker int startIndex = fLastDrawIndex + 1;
371*d57664e9SAndroid Build Coastguard Worker if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
372*d57664e9SAndroid Build Coastguard Worker // first time
373*d57664e9SAndroid Build Coastguard Worker
374*d57664e9SAndroid Build Coastguard Worker startIndex = 0;
375*d57664e9SAndroid Build Coastguard Worker
376*d57664e9SAndroid Build Coastguard Worker // create bitmap
377*d57664e9SAndroid Build Coastguard Worker if (!bm->tryAllocN32Pixels(width, height)) {
378*d57664e9SAndroid Build Coastguard Worker return false;
379*d57664e9SAndroid Build Coastguard Worker }
380*d57664e9SAndroid Build Coastguard Worker // create bitmap for backup
381*d57664e9SAndroid Build Coastguard Worker if (!fBackup.tryAllocN32Pixels(width, height)) {
382*d57664e9SAndroid Build Coastguard Worker return false;
383*d57664e9SAndroid Build Coastguard Worker }
384*d57664e9SAndroid Build Coastguard Worker } else if (startIndex > fCurrIndex) {
385*d57664e9SAndroid Build Coastguard Worker // rewind to 1st frame for repeat
386*d57664e9SAndroid Build Coastguard Worker startIndex = 0;
387*d57664e9SAndroid Build Coastguard Worker }
388*d57664e9SAndroid Build Coastguard Worker
389*d57664e9SAndroid Build Coastguard Worker int lastIndex = fCurrIndex;
390*d57664e9SAndroid Build Coastguard Worker if (lastIndex < 0) {
391*d57664e9SAndroid Build Coastguard Worker // first time
392*d57664e9SAndroid Build Coastguard Worker lastIndex = 0;
393*d57664e9SAndroid Build Coastguard Worker } else if (lastIndex > fGIF->ImageCount - 1) {
394*d57664e9SAndroid Build Coastguard Worker // this block must not be reached.
395*d57664e9SAndroid Build Coastguard Worker lastIndex = fGIF->ImageCount - 1;
396*d57664e9SAndroid Build Coastguard Worker }
397*d57664e9SAndroid Build Coastguard Worker
398*d57664e9SAndroid Build Coastguard Worker SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
399*d57664e9SAndroid Build Coastguard Worker if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) {
400*d57664e9SAndroid Build Coastguard Worker const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor];
401*d57664e9SAndroid Build Coastguard Worker bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
402*d57664e9SAndroid Build Coastguard Worker }
403*d57664e9SAndroid Build Coastguard Worker
404*d57664e9SAndroid Build Coastguard Worker // draw each frames - not intelligent way
405*d57664e9SAndroid Build Coastguard Worker for (int i = startIndex; i <= lastIndex; i++) {
406*d57664e9SAndroid Build Coastguard Worker const SavedImage* cur = &fGIF->SavedImages[i];
407*d57664e9SAndroid Build Coastguard Worker if (i == 0) {
408*d57664e9SAndroid Build Coastguard Worker bool trans;
409*d57664e9SAndroid Build Coastguard Worker int disposal;
410*d57664e9SAndroid Build Coastguard Worker getTransparencyAndDisposalMethod(cur, &trans, &disposal);
411*d57664e9SAndroid Build Coastguard Worker if (!trans && gif->SColorMap != nullptr) {
412*d57664e9SAndroid Build Coastguard Worker fPaintingColor = bgColor;
413*d57664e9SAndroid Build Coastguard Worker } else {
414*d57664e9SAndroid Build Coastguard Worker fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
415*d57664e9SAndroid Build Coastguard Worker }
416*d57664e9SAndroid Build Coastguard Worker
417*d57664e9SAndroid Build Coastguard Worker bm->eraseColor(fPaintingColor);
418*d57664e9SAndroid Build Coastguard Worker fBackup.eraseColor(fPaintingColor);
419*d57664e9SAndroid Build Coastguard Worker } else {
420*d57664e9SAndroid Build Coastguard Worker // Dispose previous frame before move to next frame.
421*d57664e9SAndroid Build Coastguard Worker const SavedImage* prev = &fGIF->SavedImages[i-1];
422*d57664e9SAndroid Build Coastguard Worker disposeFrameIfNeeded(bm, prev, cur, &fBackup, fPaintingColor);
423*d57664e9SAndroid Build Coastguard Worker }
424*d57664e9SAndroid Build Coastguard Worker
425*d57664e9SAndroid Build Coastguard Worker // Draw frame
426*d57664e9SAndroid Build Coastguard Worker // We can skip this process if this index is not last and disposal
427*d57664e9SAndroid Build Coastguard Worker // method == 2 or method == 3
428*d57664e9SAndroid Build Coastguard Worker if (i == lastIndex || !checkIfWillBeCleared(cur)) {
429*d57664e9SAndroid Build Coastguard Worker drawFrame(bm, cur, gif->SColorMap);
430*d57664e9SAndroid Build Coastguard Worker }
431*d57664e9SAndroid Build Coastguard Worker }
432*d57664e9SAndroid Build Coastguard Worker
433*d57664e9SAndroid Build Coastguard Worker // save index
434*d57664e9SAndroid Build Coastguard Worker fLastDrawIndex = lastIndex;
435*d57664e9SAndroid Build Coastguard Worker return true;
436*d57664e9SAndroid Build Coastguard Worker }
437*d57664e9SAndroid Build Coastguard Worker
438*d57664e9SAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
439*d57664e9SAndroid Build Coastguard Worker
DecodeStream(SkStreamRewindable * stream)440*d57664e9SAndroid Build Coastguard Worker Movie* Movie::DecodeStream(SkStreamRewindable* stream) {
441*d57664e9SAndroid Build Coastguard Worker char buf[GIF_STAMP_LEN];
442*d57664e9SAndroid Build Coastguard Worker if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
443*d57664e9SAndroid Build Coastguard Worker if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 ||
444*d57664e9SAndroid Build Coastguard Worker memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
445*d57664e9SAndroid Build Coastguard Worker memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
446*d57664e9SAndroid Build Coastguard Worker // must rewind here, since our construct wants to re-read the data
447*d57664e9SAndroid Build Coastguard Worker stream->rewind();
448*d57664e9SAndroid Build Coastguard Worker return new GIFMovie(stream);
449*d57664e9SAndroid Build Coastguard Worker }
450*d57664e9SAndroid Build Coastguard Worker }
451*d57664e9SAndroid Build Coastguard Worker return nullptr;
452*d57664e9SAndroid Build Coastguard Worker }
453