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 }