• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <string.h>
18 #include "JNIHelpers.h"
19 #include "utils/log.h"
20 #include "utils/math.h"
21 
22 #include "FrameSequence_gif.h"
23 
24 #define GIF_DEBUG 0
25 
26 // These constants are chosen to imitate common browser behavior
27 // Note that 0 delay is undefined behavior in the gif standard
28 static const long MIN_DELAY_MS = 20;
29 static const long DEFAULT_DELAY_MS = 100;
30 
streamReader(GifFileType * fileType,GifByteType * out,int size)31 static int streamReader(GifFileType* fileType, GifByteType* out, int size) {
32     Stream* stream = (Stream*) fileType->UserData;
33     return (int) stream->read(out, size);
34 }
35 
gifColorToColor8888(const GifColorType & color)36 static Color8888 gifColorToColor8888(const GifColorType& color) {
37     return ARGB_TO_COLOR8888(0xff, color.Red, color.Green, color.Blue);
38 }
39 
getDelayMs(GraphicsControlBlock & gcb)40 static long getDelayMs(GraphicsControlBlock& gcb) {
41     long delayMs = gcb.DelayTime * 10;
42     if (delayMs < MIN_DELAY_MS) {
43         return DEFAULT_DELAY_MS;
44     }
45     return delayMs;
46 }
47 
willBeCleared(const GraphicsControlBlock & gcb)48 static bool willBeCleared(const GraphicsControlBlock& gcb) {
49     return gcb.DisposalMode == DISPOSE_BACKGROUND || gcb.DisposalMode == DISPOSE_PREVIOUS;
50 }
51 
52 ////////////////////////////////////////////////////////////////////////////////
53 // Frame sequence
54 ////////////////////////////////////////////////////////////////////////////////
55 
FrameSequence_gif(Stream * stream)56 FrameSequence_gif::FrameSequence_gif(Stream* stream) :
57         mLoopCount(1), mBgColor(TRANSPARENT), mPreservedFrames(NULL), mRestoringFrames(NULL) {
58     mGif = DGifOpen(stream, streamReader, NULL);
59     if (!mGif) {
60         ALOGW("Gif load failed");
61         return;
62     }
63 
64     if (DGifSlurp(mGif) != GIF_OK) {
65         ALOGW("Gif slurp failed");
66         DGifCloseFile(mGif);
67         mGif = NULL;
68         return;
69     }
70 
71     long durationMs = 0;
72     int lastUnclearedFrame = -1;
73     mPreservedFrames = new bool[mGif->ImageCount];
74     mRestoringFrames = new int[mGif->ImageCount];
75 
76     GraphicsControlBlock gcb;
77     for (int i = 0; i < mGif->ImageCount; i++) {
78         const SavedImage& image = mGif->SavedImages[i];
79 
80         // find the loop extension pair
81         for (int j = 0; (j + 1) < image.ExtensionBlockCount; j++) {
82             ExtensionBlock* eb1 = image.ExtensionBlocks + j;
83             ExtensionBlock* eb2 = image.ExtensionBlocks + j + 1;
84             if (eb1->Function == APPLICATION_EXT_FUNC_CODE
85                     // look for "NETSCAPE2.0" app extension
86                     && eb1->ByteCount == 11
87                     && !memcmp((const char*)(eb1->Bytes), "NETSCAPE2.0", 11)
88                     // verify extension contents and get loop count
89                     && eb2->Function == CONTINUE_EXT_FUNC_CODE
90                     && eb2->ByteCount == 3
91                     && eb2->Bytes[0] == 1) {
92                 mLoopCount = (int)(eb2->Bytes[2] << 8) + (int)(eb2->Bytes[1]);
93             }
94         }
95 
96         DGifSavedExtensionToGCB(mGif, i, &gcb);
97 
98         // timing
99         durationMs += getDelayMs(gcb);
100 
101         // preserve logic
102         mPreservedFrames[i] = false;
103         mRestoringFrames[i] = -1;
104         if (gcb.DisposalMode == DISPOSE_PREVIOUS && lastUnclearedFrame >= 0) {
105             mPreservedFrames[lastUnclearedFrame] = true;
106             mRestoringFrames[i] = lastUnclearedFrame;
107         }
108         if (!willBeCleared(gcb)) {
109             lastUnclearedFrame = i;
110         }
111     }
112 
113 #if GIF_DEBUG
114     ALOGD("FrameSequence_gif created with size %d %d, frames %d dur %ld",
115             mGif->SWidth, mGif->SHeight, mGif->ImageCount, durationMs);
116     for (int i = 0; i < mGif->ImageCount; i++) {
117         DGifSavedExtensionToGCB(mGif, i, &gcb);
118         ALOGD("    Frame %d - must preserve %d, restore point %d, trans color %d",
119                 i, mPreservedFrames[i], mRestoringFrames[i], gcb.TransparentColor);
120     }
121 #endif
122 
123     if (mGif->SColorMap) {
124         // calculate bg color
125         GraphicsControlBlock gcb;
126         DGifSavedExtensionToGCB(mGif, 0, &gcb);
127         if (gcb.TransparentColor == NO_TRANSPARENT_COLOR) {
128             mBgColor = gifColorToColor8888(mGif->SColorMap->Colors[mGif->SBackGroundColor]);
129         }
130     }
131 }
132 
~FrameSequence_gif()133 FrameSequence_gif::~FrameSequence_gif() {
134     if (mGif) {
135         DGifCloseFile(mGif);
136     }
137     delete[] mPreservedFrames;
138     delete[] mRestoringFrames;
139 }
140 
createState() const141 FrameSequenceState* FrameSequence_gif::createState() const {
142     return new FrameSequenceState_gif(*this);
143 }
144 
145 ////////////////////////////////////////////////////////////////////////////////
146 // draw helpers
147 ////////////////////////////////////////////////////////////////////////////////
148 
149 // return true if area of 'target' is completely covers area of 'covered'
checkIfCover(const GifImageDesc & target,const GifImageDesc & covered)150 static bool checkIfCover(const GifImageDesc& target, const GifImageDesc& covered) {
151     return target.Left <= covered.Left
152             && covered.Left + covered.Width <= target.Left + target.Width
153             && target.Top <= covered.Top
154             && covered.Top + covered.Height <= target.Top + target.Height;
155 }
156 
copyLine(Color8888 * dst,const unsigned char * src,const ColorMapObject * cmap,int transparent,int width)157 static void copyLine(Color8888* dst, const unsigned char* src, const ColorMapObject* cmap,
158                      int transparent, int width) {
159     for (; width > 0; width--, src++, dst++) {
160         if (*src != transparent) {
161             *dst = gifColorToColor8888(cmap->Colors[*src]);
162         }
163     }
164 }
165 
setLineColor(Color8888 * dst,Color8888 color,int width)166 static void setLineColor(Color8888* dst, Color8888 color, int width) {
167     for (; width > 0; width--, dst++) {
168         *dst = color;
169     }
170 }
171 
getCopySize(const GifImageDesc & imageDesc,int maxWidth,int maxHeight,GifWord & copyWidth,GifWord & copyHeight)172 static void getCopySize(const GifImageDesc& imageDesc, int maxWidth, int maxHeight,
173         GifWord& copyWidth, GifWord& copyHeight) {
174     copyWidth = imageDesc.Width;
175     if (imageDesc.Left + copyWidth > maxWidth) {
176         copyWidth = maxWidth - imageDesc.Left;
177     }
178     copyHeight = imageDesc.Height;
179     if (imageDesc.Top + copyHeight > maxHeight) {
180         copyHeight = maxHeight - imageDesc.Top;
181     }
182 }
183 
184 ////////////////////////////////////////////////////////////////////////////////
185 // Frame sequence state
186 ////////////////////////////////////////////////////////////////////////////////
187 
FrameSequenceState_gif(const FrameSequence_gif & frameSequence)188 FrameSequenceState_gif::FrameSequenceState_gif(const FrameSequence_gif& frameSequence) :
189     mFrameSequence(frameSequence), mPreserveBuffer(NULL), mPreserveBufferFrame(-1) {
190 }
191 
~FrameSequenceState_gif()192 FrameSequenceState_gif::~FrameSequenceState_gif() {
193        delete[] mPreserveBuffer;
194 }
195 
savePreserveBuffer(Color8888 * outputPtr,int outputPixelStride,int frameNr)196 void FrameSequenceState_gif::savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr) {
197     if (frameNr == mPreserveBufferFrame) return;
198 
199     mPreserveBufferFrame = frameNr;
200     const int width = mFrameSequence.getWidth();
201     const int height = mFrameSequence.getHeight();
202     if (!mPreserveBuffer) {
203         mPreserveBuffer = new Color8888[width * height];
204     }
205     for (int y = 0; y < height; y++) {
206         memcpy(mPreserveBuffer + width * y,
207                 outputPtr + outputPixelStride * y,
208                 width * 4);
209     }
210 }
211 
restorePreserveBuffer(Color8888 * outputPtr,int outputPixelStride)212 void FrameSequenceState_gif::restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride) {
213     const int width = mFrameSequence.getWidth();
214     const int height = mFrameSequence.getHeight();
215     if (!mPreserveBuffer) {
216         ALOGD("preserve buffer not allocated! ah!");
217         return;
218     }
219     for (int y = 0; y < height; y++) {
220         memcpy(outputPtr + outputPixelStride * y,
221                 mPreserveBuffer + width * y,
222                 width * 4);
223     }
224 }
225 
drawFrame(int frameNr,Color8888 * outputPtr,int outputPixelStride,int previousFrameNr)226 long FrameSequenceState_gif::drawFrame(int frameNr,
227         Color8888* outputPtr, int outputPixelStride, int previousFrameNr) {
228 
229     GifFileType* gif = mFrameSequence.getGif();
230     if (!gif) {
231         ALOGD("Cannot drawFrame, mGif is NULL");
232         return -1;
233     }
234 
235 #if GIF_DEBUG
236     ALOGD("      drawFrame on %p nr %d on addr %p, previous frame nr %d",
237             this, frameNr, outputPtr, previousFrameNr);
238 #endif
239 
240     const int height = mFrameSequence.getHeight();
241     const int width = mFrameSequence.getWidth();
242 
243     GraphicsControlBlock gcb;
244 
245     int start = max(previousFrameNr + 1, 0);
246 
247     for (int i = max(start - 1, 0); i < frameNr; i++) {
248         int neededPreservedFrame = mFrameSequence.getRestoringFrame(i);
249         if (neededPreservedFrame >= 0 && (mPreserveBufferFrame != neededPreservedFrame)) {
250 #if GIF_DEBUG
251             ALOGD("frame %d needs frame %d preserved, but %d is currently, so drawing from scratch",
252                     i, neededPreservedFrame, mPreserveBufferFrame);
253 #endif
254             start = 0;
255         }
256     }
257 
258     for (int i = start; i <= frameNr; i++) {
259         DGifSavedExtensionToGCB(gif, i, &gcb);
260         const SavedImage& frame = gif->SavedImages[i];
261 
262 #if GIF_DEBUG
263         bool frameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR;
264         ALOGD("producing frame %d, drawing frame %d (opaque %d, disp %d, del %d)",
265                 frameNr, i, frameOpaque, gcb.DisposalMode, gcb.DelayTime);
266 #endif
267         if (i == 0) {
268             //clear bitmap
269             Color8888 bgColor = mFrameSequence.getBackgroundColor();
270             for (int y = 0; y < height; y++) {
271                 for (int x = 0; x < width; x++) {
272                     outputPtr[y * outputPixelStride + x] = bgColor;
273                 }
274             }
275         } else {
276             GraphicsControlBlock prevGcb;
277             DGifSavedExtensionToGCB(gif, i - 1, &prevGcb);
278             const SavedImage& prevFrame = gif->SavedImages[i - 1];
279             bool prevFrameDisposed = willBeCleared(prevGcb);
280 
281             bool newFrameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR;
282             bool prevFrameCompletelyCovered = newFrameOpaque
283                     && checkIfCover(frame.ImageDesc, prevFrame.ImageDesc);
284 
285             if (prevFrameDisposed && !prevFrameCompletelyCovered) {
286                 switch (prevGcb.DisposalMode) {
287                 case DISPOSE_BACKGROUND: {
288                     Color8888* dst = outputPtr + prevFrame.ImageDesc.Left +
289                             prevFrame.ImageDesc.Top * outputPixelStride;
290 
291                     GifWord copyWidth, copyHeight;
292                     getCopySize(prevFrame.ImageDesc, width, height, copyWidth, copyHeight);
293                     for (; copyHeight > 0; copyHeight--) {
294                         setLineColor(dst, TRANSPARENT, copyWidth);
295                         dst += outputPixelStride;
296                     }
297                 } break;
298                 case DISPOSE_PREVIOUS: {
299                     restorePreserveBuffer(outputPtr, outputPixelStride);
300                 } break;
301                 }
302             }
303 
304             if (mFrameSequence.getPreservedFrame(i - 1)) {
305                 // currently drawn frame will be restored by a following DISPOSE_PREVIOUS draw, so
306                 // we preserve it
307                 savePreserveBuffer(outputPtr, outputPixelStride, i - 1);
308             }
309         }
310 
311         bool willBeCleared = gcb.DisposalMode == DISPOSE_BACKGROUND
312                 || gcb.DisposalMode == DISPOSE_PREVIOUS;
313         if (i == frameNr || !willBeCleared) {
314             const ColorMapObject* cmap = gif->SColorMap;
315             if (frame.ImageDesc.ColorMap) {
316                 cmap = frame.ImageDesc.ColorMap;
317             }
318 
319             if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
320                 ALOGW("Warning: potentially corrupt color map");
321             }
322 
323             const unsigned char* src = (unsigned char*)frame.RasterBits;
324             Color8888* dst = outputPtr + frame.ImageDesc.Left +
325                     frame.ImageDesc.Top * outputPixelStride;
326             GifWord copyWidth, copyHeight;
327             getCopySize(frame.ImageDesc, width, height, copyWidth, copyHeight);
328             for (; copyHeight > 0; copyHeight--) {
329                 copyLine(dst, src, cmap, gcb.TransparentColor, copyWidth);
330                 src += frame.ImageDesc.Width;
331                 dst += outputPixelStride;
332             }
333         }
334     }
335 
336     // return last frame's delay
337     const int maxFrame = gif->ImageCount;
338     const int lastFrame = (frameNr + maxFrame - 1) % maxFrame;
339     DGifSavedExtensionToGCB(gif, lastFrame, &gcb);
340     return getDelayMs(gcb);
341 }
342 
343 ////////////////////////////////////////////////////////////////////////////////
344 // Registry
345 ////////////////////////////////////////////////////////////////////////////////
346 
347 #include "Registry.h"
348 
isGif(void * header,int header_size)349 static bool isGif(void* header, int header_size) {
350     return !memcmp(GIF_STAMP, header, GIF_STAMP_LEN)
351             || !memcmp(GIF87_STAMP, header, GIF_STAMP_LEN)
352             || !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN);
353 }
354 
createFramesequence(Stream * stream)355 static FrameSequence* createFramesequence(Stream* stream) {
356     return new FrameSequence_gif(stream);
357 }
358 
359 static RegistryEntry gEntry = {
360         GIF_STAMP_LEN,
361         isGif,
362         createFramesequence,
363         NULL,
364 };
365 static Registry gRegister(gEntry);
366 
367