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