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