1/*
2 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include "splashscreen_impl.h"
27#include "splashscreen_gfx.h"
28
29#include <gif_lib.h>
30
31#include "sizecalc.h"
32
33#define GIF_TRANSPARENT     0x01
34#define GIF_USER_INPUT      0x02
35#define GIF_DISPOSE_MASK    0x07
36#define GIF_DISPOSE_SHIFT   2
37
38#define GIF_NOT_TRANSPARENT -1
39
40#define GIF_DISPOSE_NONE    0   // No disposal specified. The decoder is
41                                // not required to take any action.
42#define GIF_DISPOSE_LEAVE   1   // Do not dispose. The graphic is to be left
43                                // in place.
44#define GIF_DISPOSE_BACKGND 2   // Restore to background color. The area used by the
45                                // graphic must be restored to the background color.
46
47#define GIF_DISPOSE_RESTORE 3   // Restore to previous. The decoder is required to
48                                // restore the area overwritten by the graphic with
49                                // what was there prior to rendering the graphic.
50
51static const char szNetscape20ext[11] = "NETSCAPE2.0";
52
53#define NSEXT_LOOP      0x01    // Loop Count field code
54
55// convert libungif samples to our ones
56#define MAKE_QUAD_GIF(c,a) MAKE_QUAD((c).Red, (c).Green, (c).Blue, (unsigned)(a))
57
58/* stdio FILE* and memory input functions for libungif */
59int
60SplashStreamGifInputFunc(GifFileType * gif, GifByteType * buf, int n)
61{
62    SplashStream* io = (SplashStream*)gif->UserData;
63    int rc = io->read(io, buf, n);
64    return rc;
65}
66
67/* These macro help to ensure that we only take part of frame that fits into
68   logical screen. */
69
70/* Ensure that p belongs to [pmin, pmax) interval. Returns fixed point (if fix is needed) */
71#define FIX_POINT(p, pmin, pmax) ( ((p) < (pmin)) ? (pmin) : (((p) > (pmax)) ? (pmax) : (p)))
72/* Ensures that line starting at point p does not exceed boundary pmax.
73   Returns fixed length (if fix is needed) */
74#define FIX_LENGTH(p, len, pmax) ( ((p) + (len)) > (pmax) ? ((pmax) - (p)) : (len))
75
76int
77SplashDecodeGif(Splash * splash, GifFileType * gif)
78{
79    int stride;
80    int bufferSize;
81    byte_t *pBitmapBits, *pOldBitmapBits;
82    int i, j;
83    int imageIndex;
84    int cx, cy, cw, ch; /* clamped coordinates */
85    const int interlacedOffset[] = { 0, 4, 2, 1, 0 };   /* The way Interlaced image should. */
86    const int interlacedJumps[] = { 8, 8, 4, 2, 1 };    /* be read - offsets and jumps... */
87
88    if (DGifSlurp(gif) == GIF_ERROR) {
89        return 0;
90    }
91
92    SplashCleanup(splash);
93
94    if (!SAFE_TO_ALLOC(gif->SWidth, splash->imageFormat.depthBytes)) {
95        return 0;
96    }
97    stride = gif->SWidth * splash->imageFormat.depthBytes;
98    if (splash->byteAlignment > 1)
99        stride =
100            (stride + splash->byteAlignment - 1) & ~(splash->byteAlignment - 1);
101
102    if (!SAFE_TO_ALLOC(gif->SHeight, stride)) {
103        return 0;
104    }
105
106    if (!SAFE_TO_ALLOC(gif->ImageCount, sizeof(SplashImage*))) {
107        return 0;
108    }
109    bufferSize = stride * gif->SHeight;
110    pBitmapBits = (byte_t *) malloc(bufferSize);
111    if (!pBitmapBits) {
112        return 0;
113    }
114    pOldBitmapBits = (byte_t *) malloc(bufferSize);
115    if (!pOldBitmapBits) {
116        free(pBitmapBits);
117        return 0;
118    }
119    memset(pBitmapBits, 0, bufferSize);
120
121    splash->width = gif->SWidth;
122    splash->height = gif->SHeight;
123    splash->frameCount = gif->ImageCount;
124    splash->frames = (SplashImage *)
125        SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(SplashImage), gif->ImageCount);
126    if (!splash->frames) {
127      free(pBitmapBits);
128      free(pOldBitmapBits);
129      return 0;
130    }
131    memset(splash->frames, 0, sizeof(SplashImage) * gif->ImageCount);
132    splash->loopCount = 1;
133
134    for (imageIndex = 0; imageIndex < gif->ImageCount; imageIndex++) {
135        SavedImage *image = &(gif->SavedImages[imageIndex]);
136        GifImageDesc *desc = &(image->ImageDesc);
137        ColorMapObject *colorMap =
138            desc->ColorMap ? desc->ColorMap : gif->SColorMap;
139
140        int transparentColor = -1;
141        int frameDelay = 100;
142        int disposeMethod = GIF_DISPOSE_RESTORE;
143        int colorCount = 0;
144        rgbquad_t colorMapBuf[SPLASH_COLOR_MAP_SIZE];
145
146        cx = FIX_POINT(desc->Left, 0, gif->SWidth);
147        cy = FIX_POINT(desc->Top, 0, gif->SHeight);
148        cw = FIX_LENGTH(desc->Left, desc->Width, gif->SWidth);
149        ch = FIX_LENGTH(desc->Top, desc->Height, gif->SHeight);
150
151        if (colorMap) {
152            if (colorMap->ColorCount <= SPLASH_COLOR_MAP_SIZE) {
153                colorCount = colorMap->ColorCount;
154            } else  {
155                colorCount = SPLASH_COLOR_MAP_SIZE;
156            }
157        }
158
159        /* the code below is loosely based around gif extension processing from win32 libungif sample */
160
161        for (i = 0; i < image->ExtensionBlockCount; i++) {
162            byte_t *pExtension = (byte_t *) image->ExtensionBlocks[i].Bytes;
163            unsigned size = image->ExtensionBlocks[i].ByteCount;
164
165            switch (image->ExtensionBlocks[i].Function) {
166            case GRAPHICS_EXT_FUNC_CODE:
167                {
168                    int flag = pExtension[0];
169
170                    frameDelay = (((int)pExtension[2]) << 8) | pExtension[1];
171                    if (frameDelay < 10)
172                        frameDelay = 10;
173                    if (flag & GIF_TRANSPARENT) {
174                        transparentColor = pExtension[3];
175                    } else {
176                        transparentColor = GIF_NOT_TRANSPARENT;
177                    }
178                    disposeMethod =
179                        (flag >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;
180                    break;
181                }
182            case APPLICATION_EXT_FUNC_CODE:
183                {
184                    if (size == sizeof(szNetscape20ext)
185                        && memcmp(pExtension, szNetscape20ext, size) == 0) {
186                        int iSubCode;
187
188                        if (++i >= image->ExtensionBlockCount)
189                            break;
190                        pExtension = (byte_t *) image->ExtensionBlocks[i].Bytes;
191                        if (image->ExtensionBlocks[i].ByteCount != 3)
192                            break;
193                        iSubCode = pExtension[0] & 0x07;
194                        if (iSubCode == NSEXT_LOOP) {
195                            splash->loopCount =
196                                (pExtension[1] | (((int)pExtension[2]) << 8)) - 1;
197                        }
198                    }
199                    break;
200                }
201            default:
202                break;
203            }
204        }
205
206        if (colorMap) {
207            for (i = 0; i < colorCount; i++) {
208                colorMapBuf[i] = MAKE_QUAD_GIF(colorMap->Colors[i], 0xff);
209            }
210        }
211        {
212
213            byte_t *pSrc = image->RasterBits;
214            ImageFormat srcFormat;
215            ImageRect srcRect, dstRect;
216            int pass = 4, npass = 5;
217
218#if GIFLIB_MAJOR < 5
219            /* Interlaced gif support is broken in giflib < 5
220               so we need to work around this */
221            if (desc->Interlace) {
222                pass = 0;
223                npass = 4;
224            }
225#endif
226
227            srcFormat.colorMap = colorMapBuf;
228            srcFormat.depthBytes = 1;
229            srcFormat.byteOrder = BYTE_ORDER_NATIVE;
230            srcFormat.transparentColor = transparentColor;
231            srcFormat.fixedBits = QUAD_ALPHA_MASK;      // fixed 100% alpha
232            srcFormat.premultiplied = 0;
233
234            for (; pass < npass; ++pass) {
235                int jump = interlacedJumps[pass];
236                int ofs = interlacedOffset[pass];
237                /* Number of source lines for current pass */
238                int numPassLines = (desc->Height + jump - ofs - 1) / jump;
239                /* Number of lines that fits to dest buffer */
240                int numLines = (ch + jump - ofs - 1) / jump;
241
242                initRect(&srcRect, 0, 0, desc->Width, numLines, 1,
243                    desc->Width, pSrc, &srcFormat);
244
245                if (numLines > 0) {
246                    initRect(&dstRect, cx, cy + ofs, cw,
247                             numLines , jump, stride, pBitmapBits, &splash->imageFormat);
248
249                    pSrc += convertRect(&srcRect, &dstRect, CVT_ALPHATEST);
250                }
251                // skip extra source data
252                pSrc += (numPassLines - numLines) * srcRect.stride;
253            }
254        }
255
256        // now dispose of the previous frame correctly
257
258        splash->frames[imageIndex].bitmapBits =
259            (rgbquad_t *) malloc(bufferSize); // bufferSize is safe (checked above)
260        if (!splash->frames[imageIndex].bitmapBits) {
261            free(pBitmapBits);
262            free(pOldBitmapBits);
263            /* Assuming that callee will take care of splash frames we have already allocated */
264            return 0;
265        }
266        memcpy(splash->frames[imageIndex].bitmapBits, pBitmapBits, bufferSize);
267
268        SplashInitFrameShape(splash, imageIndex);
269
270        splash->frames[imageIndex].delay = frameDelay * 10;     // 100ths of second to milliseconds
271        switch (disposeMethod) {
272        case GIF_DISPOSE_LEAVE:
273            memcpy(pOldBitmapBits, pBitmapBits, bufferSize);
274            break;
275        case GIF_DISPOSE_NONE:
276            break;
277        case GIF_DISPOSE_BACKGND:
278            {
279                ImageRect dstRect;
280                rgbquad_t fillColor = 0;                        // 0 is transparent
281
282                if (transparentColor < 0) {
283                    fillColor= MAKE_QUAD_GIF(
284                        colorMap->Colors[gif->SBackGroundColor], 0xff);
285                }
286                initRect(&dstRect,
287                         cx, cy, cw, ch,
288                         1, stride,
289                         pBitmapBits, &splash->imageFormat);
290                fillRect(fillColor, &dstRect);
291            }
292            break;
293        case GIF_DISPOSE_RESTORE:
294            {
295                int lineSize = cw * splash->imageFormat.depthBytes;
296                if (lineSize > 0) {
297                    int lineOffset = cx * splash->imageFormat.depthBytes;
298                    int lineIndex = cy * stride + lineOffset;
299                    for (j=0; j<ch; j++) {
300                        memcpy(pBitmapBits + lineIndex, pOldBitmapBits + lineIndex,
301                               lineSize);
302                        lineIndex += stride;
303                    }
304                }
305            }
306            break;
307        }
308    }
309
310    free(pBitmapBits);
311    free(pOldBitmapBits);
312
313#if GIFLIB_MAJOR > 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1)
314    if (DGifCloseFile(gif, NULL) == GIF_ERROR) {
315        return 0;
316    }
317#else
318    DGifCloseFile(gif);
319#endif
320
321    return 1;
322}
323
324int
325SplashDecodeGifStream(Splash * splash, SplashStream * stream)
326{
327#if GIFLIB_MAJOR >= 5
328    GifFileType *gif = DGifOpen((void *) stream, SplashStreamGifInputFunc, NULL);
329#else
330    GifFileType *gif = DGifOpen((void *) stream, SplashStreamGifInputFunc);
331#endif
332
333    if (!gif)
334        return 0;
335    return SplashDecodeGif(splash, gif);
336}
337