xref: /aosp_15_r20/frameworks/base/libs/hwui/jni/GIFMovie.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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