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 51 static 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 */ 59 int 60 SplashStreamGifInputFunc(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 76 int 77 SplashDecodeGif(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 int numLines; 86 int numPassLines; 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 217 srcFormat.colorMap = colorMapBuf; 218 srcFormat.depthBytes = 1; 219 srcFormat.byteOrder = BYTE_ORDER_NATIVE; 220 srcFormat.transparentColor = transparentColor; 221 srcFormat.fixedBits = QUAD_ALPHA_MASK; // fixed 100% alpha 222 srcFormat.premultiplied = 0; 223 224 /* Number of source lines for current pass */ 225 numPassLines = desc->Height; 226 /* Number of lines that fits to dest buffer */ 227 numLines = ch; 228 229 initRect(&srcRect, 0, 0, desc->Width, numLines, 1, 230 desc->Width, pSrc, &srcFormat); 231 232 if (numLines > 0) { 233 initRect(&dstRect, cx, cy, cw, 234 numLines , 1, stride, pBitmapBits, &splash->imageFormat); 235 236 pSrc += convertRect(&srcRect, &dstRect, CVT_ALPHATEST); 237 } 238 // skip extra source data 239 pSrc += (numPassLines - numLines) * srcRect.stride; 240 } 241 242 // now dispose of the previous frame correctly 243 244 splash->frames[imageIndex].bitmapBits = 245 (rgbquad_t *) malloc(bufferSize); // bufferSize is safe (checked above) 246 if (!splash->frames[imageIndex].bitmapBits) { 247 free(pBitmapBits); 248 free(pOldBitmapBits); 249 /* Assuming that callee will take care of splash frames we have already allocated */ 250 return 0; 251 } 252 memcpy(splash->frames[imageIndex].bitmapBits, pBitmapBits, bufferSize); 253 254 SplashInitFrameShape(splash, imageIndex); 255 256 splash->frames[imageIndex].delay = frameDelay * 10; // 100ths of second to milliseconds 257 switch (disposeMethod) { 258 case GIF_DISPOSE_LEAVE: 259 memcpy(pOldBitmapBits, pBitmapBits, bufferSize); 260 break; 261 case GIF_DISPOSE_NONE: 262 break; 263 case GIF_DISPOSE_BACKGND: 264 { 265 ImageRect dstRect; 266 rgbquad_t fillColor = 0; // 0 is transparent 267 268 if (transparentColor < 0) { 269 fillColor= MAKE_QUAD_GIF( 270 colorMap->Colors[gif->SBackGroundColor], 0xff); 271 } 272 initRect(&dstRect, 273 cx, cy, cw, ch, 274 1, stride, 275 pBitmapBits, &splash->imageFormat); 276 fillRect(fillColor, &dstRect); 277 } 278 break; 279 case GIF_DISPOSE_RESTORE: 280 { 281 int lineSize = cw * splash->imageFormat.depthBytes; 282 if (lineSize > 0) { 283 int lineOffset = cx * splash->imageFormat.depthBytes; 284 int lineIndex = cy * stride + lineOffset; 285 for (j=0; j<ch; j++) { 286 memcpy(pBitmapBits + lineIndex, pOldBitmapBits + lineIndex, 287 lineSize); 288 lineIndex += stride; 289 } 290 } 291 } 292 break; 293 } 294 } 295 296 free(pBitmapBits); 297 free(pOldBitmapBits); 298 299 #if GIFLIB_MAJOR > 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) 300 if (DGifCloseFile(gif, NULL) == GIF_ERROR) { 301 return 0; 302 } 303 #else 304 DGifCloseFile(gif); 305 #endif 306 307 return 1; 308 } 309 310 int 311 SplashDecodeGifStream(Splash * splash, SplashStream * stream) 312 { 313 #if GIFLIB_MAJOR >= 5 314 GifFileType *gif = DGifOpen((void *) stream, SplashStreamGifInputFunc, NULL); 315 #else 316 GifFileType *gif = DGifOpen((void *) stream, SplashStreamGifInputFunc); 317 #endif 318 319 if (!gif) 320 return 0; 321 return SplashDecodeGif(splash, gif); 322 }