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     if (mGif->SColorMap) {
115         // calculate bg color
116         GraphicsControlBlock gcb;
117         DGifSavedExtensionToGCB(mGif, 0, &gcb);
118         if (gcb.TransparentColor == NO_TRANSPARENT_COLOR) {
119             mBgColor = gifColorToColor8888(mGif->SColorMap->Colors[mGif->SBackGroundColor]);
120         }
121     }
122 }
123 
~FrameSequence_gif()124 FrameSequence_gif::~FrameSequence_gif() {
125     if (mGif) {
126         DGifCloseFile(mGif, NULL);
127     }
128     delete[] mPreservedFrames;
129     delete[] mRestoringFrames;
130 }
131 
createState() const132 FrameSequenceState* FrameSequence_gif::createState() const {
133     return new FrameSequenceState_gif(*this);
134 }
135 
136 ////////////////////////////////////////////////////////////////////////////////
137 // draw helpers
138 ////////////////////////////////////////////////////////////////////////////////
139 
140 // return true if area of 'target' is completely covers area of 'covered'
checkIfCover(const GifImageDesc & target,const GifImageDesc & covered)141 static bool checkIfCover(const GifImageDesc& target, const GifImageDesc& covered) {
142     return target.Left <= covered.Left
143             && covered.Left + covered.Width <= target.Left + target.Width
144             && target.Top <= covered.Top
145             && covered.Top + covered.Height <= target.Top + target.Height;
146 }
147 
copyLine(Color8888 * dst,const unsigned char * src,const ColorMapObject * cmap,int transparent,int width)148 static void copyLine(Color8888* dst, const unsigned char* src, const ColorMapObject* cmap,
149                      int transparent, int width) {
150     for (; width > 0; width--, src++, dst++) {
151         if (*src != transparent) {
152             *dst = gifColorToColor8888(cmap->Colors[*src]);
153         }
154     }
155 }
156 
setLineColor(Color8888 * dst,Color8888 color,int width)157 static void setLineColor(Color8888* dst, Color8888 color, int width) {
158     for (; width > 0; width--, dst++) {
159         *dst = color;
160     }
161 }
162 
getCopySize(const GifImageDesc & imageDesc,int maxWidth,int maxHeight,GifWord & copyWidth,GifWord & copyHeight)163 static void getCopySize(const GifImageDesc& imageDesc, int maxWidth, int maxHeight,
164         GifWord& copyWidth, GifWord& copyHeight) {
165     copyWidth = imageDesc.Width;
166     if (imageDesc.Left + copyWidth > maxWidth) {
167         copyWidth = maxWidth - imageDesc.Left;
168     }
169     copyHeight = imageDesc.Height;
170     if (imageDesc.Top + copyHeight > maxHeight) {
171         copyHeight = maxHeight - imageDesc.Top;
172     }
173 }
174 
175 ////////////////////////////////////////////////////////////////////////////////
176 // Frame sequence state
177 ////////////////////////////////////////////////////////////////////////////////
178 
FrameSequenceState_gif(const FrameSequence_gif & frameSequence)179 FrameSequenceState_gif::FrameSequenceState_gif(const FrameSequence_gif& frameSequence) :
180     mFrameSequence(frameSequence), mPreserveBuffer(NULL), mPreserveBufferFrame(-1) {
181 }
182 
~FrameSequenceState_gif()183 FrameSequenceState_gif::~FrameSequenceState_gif() {
184        delete[] mPreserveBuffer;
185 }
186 
savePreserveBuffer(Color8888 * outputPtr,int outputPixelStride,int frameNr)187 void FrameSequenceState_gif::savePreserveBuffer(Color8888* outputPtr, int outputPixelStride, int frameNr) {
188     if (frameNr == mPreserveBufferFrame) return;
189 
190     mPreserveBufferFrame = frameNr;
191     const int width = mFrameSequence.getWidth();
192     const int height = mFrameSequence.getHeight();
193     if (!mPreserveBuffer) {
194         mPreserveBuffer = new Color8888[width * height];
195     }
196     for (int y = 0; y < height; y++) {
197         memcpy(mPreserveBuffer + width * y,
198                 outputPtr + outputPixelStride * y,
199                 width * 4);
200     }
201 }
202 
restorePreserveBuffer(Color8888 * outputPtr,int outputPixelStride)203 void FrameSequenceState_gif::restorePreserveBuffer(Color8888* outputPtr, int outputPixelStride) {
204     const int width = mFrameSequence.getWidth();
205     const int height = mFrameSequence.getHeight();
206     if (!mPreserveBuffer) {
207         ALOGD("preserve buffer not allocated! ah!");
208         return;
209     }
210     for (int y = 0; y < height; y++) {
211         memcpy(outputPtr + outputPixelStride * y,
212                 mPreserveBuffer + width * y,
213                 width * 4);
214     }
215 }
216 
drawFrame(int frameNr,Color8888 * outputPtr,int outputPixelStride,int previousFrameNr)217 long FrameSequenceState_gif::drawFrame(int frameNr,
218         Color8888* outputPtr, int outputPixelStride, int previousFrameNr) {
219 
220     GifFileType* gif = mFrameSequence.getGif();
221     if (!gif) {
222         ALOGD("Cannot drawFrame, mGif is NULL");
223         return -1;
224     }
225 
226 #if GIF_DEBUG
227     ALOGD("      drawFrame on %p nr %d on addr %p, previous frame nr %d",
228             this, frameNr, outputPtr, previousFrameNr);
229 #endif
230 
231     const int height = mFrameSequence.getHeight();
232     const int width = mFrameSequence.getWidth();
233 
234     GraphicsControlBlock gcb;
235 
236     int start = max(previousFrameNr + 1, 0);
237 
238     for (int i = max(start - 1, 0); i < frameNr; i++) {
239         int neededPreservedFrame = mFrameSequence.getRestoringFrame(i);
240         if (neededPreservedFrame >= 0 && (mPreserveBufferFrame != neededPreservedFrame)) {
241 #if GIF_DEBUG
242             ALOGD("frame %d needs frame %d preserved, but %d is currently, so drawing from scratch",
243                     i, neededPreservedFrame, mPreserveBufferFrame);
244 #endif
245             start = 0;
246         }
247     }
248 
249     for (int i = start; i <= frameNr; i++) {
250         DGifSavedExtensionToGCB(gif, i, &gcb);
251         const SavedImage& frame = gif->SavedImages[i];
252 
253 #if GIF_DEBUG
254         bool frameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR;
255         ALOGD("producing frame %d, drawing frame %d (opaque %d, disp %d, del %d)",
256                 frameNr, i, frameOpaque, gcb.DisposalMode, gcb.DelayTime);
257 #endif
258         if (i == 0) {
259             //clear bitmap
260             Color8888 bgColor = mFrameSequence.getBackgroundColor();
261             for (int y = 0; y < height; y++) {
262                 for (int x = 0; x < width; x++) {
263                     outputPtr[y * outputPixelStride + x] = bgColor;
264                 }
265             }
266         } else {
267             GraphicsControlBlock prevGcb;
268             DGifSavedExtensionToGCB(gif, i - 1, &prevGcb);
269             const SavedImage& prevFrame = gif->SavedImages[i - 1];
270             bool prevFrameDisposed = willBeCleared(prevGcb);
271 
272             bool newFrameOpaque = gcb.TransparentColor == NO_TRANSPARENT_COLOR;
273             bool prevFrameCompletelyCovered = newFrameOpaque
274                     && checkIfCover(frame.ImageDesc, prevFrame.ImageDesc);
275 
276             if (prevFrameDisposed && !prevFrameCompletelyCovered) {
277                 switch (prevGcb.DisposalMode) {
278                 case DISPOSE_BACKGROUND: {
279                     Color8888* dst = outputPtr + prevFrame.ImageDesc.Left +
280                             prevFrame.ImageDesc.Top * outputPixelStride;
281 
282                     GifWord copyWidth, copyHeight;
283                     getCopySize(prevFrame.ImageDesc, width, height, copyWidth, copyHeight);
284                     for (; copyHeight > 0; copyHeight--) {
285                         setLineColor(dst, TRANSPARENT, copyWidth);
286                         dst += outputPixelStride;
287                     }
288                 } break;
289                 case DISPOSE_PREVIOUS: {
290                     restorePreserveBuffer(outputPtr, outputPixelStride);
291                 } break;
292                 }
293             }
294 
295             if (mFrameSequence.getPreservedFrame(i - 1)) {
296                 // currently drawn frame will be restored by a following DISPOSE_PREVIOUS draw, so
297                 // we preserve it
298                 savePreserveBuffer(outputPtr, outputPixelStride, i - 1);
299             }
300         }
301 
302         bool willBeCleared = gcb.DisposalMode == DISPOSE_BACKGROUND
303                 || gcb.DisposalMode == DISPOSE_PREVIOUS;
304         if (i == frameNr || !willBeCleared) {
305             const ColorMapObject* cmap = gif->SColorMap;
306             if (frame.ImageDesc.ColorMap) {
307                 cmap = frame.ImageDesc.ColorMap;
308             }
309 
310             if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
311                 ALOGW("Warning: potentially corrupt color map");
312             }
313 
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     // return last frame's delay
328     const int maxFrame = gif->ImageCount;
329     const int lastFrame = (frameNr + maxFrame - 1) % maxFrame;
330     DGifSavedExtensionToGCB(gif, lastFrame, &gcb);
331     return getDelayMs(gcb);
332 }
333 
334 ////////////////////////////////////////////////////////////////////////////////
335 // Registry
336 ////////////////////////////////////////////////////////////////////////////////
337 
338 #include "Registry.h"
339 
isGif(void * header,int header_size)340 static bool isGif(void* header, int header_size) {
341     return !memcmp(GIF_STAMP, header, GIF_STAMP_LEN)
342             || !memcmp(GIF87_STAMP, header, GIF_STAMP_LEN)
343             || !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN);
344 }
345 
acceptsBuffers()346 static bool acceptsBuffers() {
347     return false;
348 }
349 
createFramesequence(Stream * stream)350 static FrameSequence* createFramesequence(Stream* stream) {
351     return new FrameSequence_gif(stream);
352 }
353 
354 static RegistryEntry gEntry = {
355         GIF_STAMP_LEN,
356         isGif,
357         createFramesequence,
358         NULL,
359         acceptsBuffers,
360 };
361 static Registry gRegister(gEntry);
362 
363