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