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 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 if (desc->Interlace) { 220 pass = 0; 221 npass = 4; 222 } 223 #endif 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); // bufferSize is safe (checked above) 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 #if GIFLIB_MAJOR > 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) 312 if (DGifCloseFile(gif, NULL) == GIF_ERROR) { 313 return 0; 314 } 315 #else 316 DGifCloseFile(gif); 317 #endif 318 319 return 1; 320 } 321 322 int 323 SplashDecodeGifStream(Splash * splash, SplashStream * stream) 324 { 325 #if GIFLIB_MAJOR >= 5 326 GifFileType *gif = DGifOpen((void *) stream, SplashStreamGifInputFunc, NULL); 327 #else 328 GifFileType *gif = DGifOpen((void *) stream, SplashStreamGifInputFunc); 329 #endif 330 331 if (!gif) 332 return 0; 333 return SplashDecodeGif(splash, gif); 334 }