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