Path: blob/master/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c
41152 views
/*1* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include "splashscreen_impl.h"26#include "splashscreen_gfx.h"2728#include <gif_lib.h>2930#include "sizecalc.h"3132#define GIF_TRANSPARENT 0x0133#define GIF_USER_INPUT 0x0234#define GIF_DISPOSE_MASK 0x0735#define GIF_DISPOSE_SHIFT 23637#define GIF_NOT_TRANSPARENT -13839#define GIF_DISPOSE_NONE 0 // No disposal specified. The decoder is40// not required to take any action.41#define GIF_DISPOSE_LEAVE 1 // Do not dispose. The graphic is to be left42// in place.43#define GIF_DISPOSE_BACKGND 2 // Restore to background color. The area used by the44// graphic must be restored to the background color.4546#define GIF_DISPOSE_RESTORE 3 // Restore to previous. The decoder is required to47// restore the area overwritten by the graphic with48// what was there prior to rendering the graphic.4950static const char szNetscape20ext[11] = "NETSCAPE2.0";5152#define NSEXT_LOOP 0x01 // Loop Count field code5354// convert libungif samples to our ones55#define MAKE_QUAD_GIF(c,a) MAKE_QUAD((c).Red, (c).Green, (c).Blue, (unsigned)(a))5657/* stdio FILE* and memory input functions for libungif */58int59SplashStreamGifInputFunc(GifFileType * gif, GifByteType * buf, int n)60{61SplashStream* io = (SplashStream*)gif->UserData;62int rc = io->read(io, buf, n);63return rc;64}6566/* These macro help to ensure that we only take part of frame that fits into67logical screen. */6869/* Ensure that p belongs to [pmin, pmax) interval. Returns fixed point (if fix is needed) */70#define FIX_POINT(p, pmin, pmax) ( ((p) < (pmin)) ? (pmin) : (((p) > (pmax)) ? (pmax) : (p)))71/* Ensures that line starting at point p does not exceed boundary pmax.72Returns fixed length (if fix is needed) */73#define FIX_LENGTH(p, len, pmax) ( ((p) + (len)) > (pmax) ? ((pmax) - (p)) : (len))7475int76SplashDecodeGif(Splash * splash, GifFileType * gif)77{78int stride;79int bufferSize;80byte_t *pBitmapBits, *pOldBitmapBits;81int i, j;82int imageIndex;83int cx, cy, cw, ch; /* clamped coordinates */84const int interlacedOffset[] = { 0, 4, 2, 1, 0 }; /* The way Interlaced image should. */85const int interlacedJumps[] = { 8, 8, 4, 2, 1 }; /* be read - offsets and jumps... */8687if (DGifSlurp(gif) == GIF_ERROR) {88return 0;89}9091SplashCleanup(splash);9293if (!SAFE_TO_ALLOC(gif->SWidth, splash->imageFormat.depthBytes)) {94return 0;95}96stride = gif->SWidth * splash->imageFormat.depthBytes;97if (splash->byteAlignment > 1)98stride =99(stride + splash->byteAlignment - 1) & ~(splash->byteAlignment - 1);100101if (!SAFE_TO_ALLOC(gif->SHeight, stride)) {102return 0;103}104105if (!SAFE_TO_ALLOC(gif->ImageCount, sizeof(SplashImage*))) {106return 0;107}108bufferSize = stride * gif->SHeight;109pBitmapBits = (byte_t *) malloc(bufferSize);110if (!pBitmapBits) {111return 0;112}113pOldBitmapBits = (byte_t *) malloc(bufferSize);114if (!pOldBitmapBits) {115free(pBitmapBits);116return 0;117}118memset(pBitmapBits, 0, bufferSize);119120splash->width = gif->SWidth;121splash->height = gif->SHeight;122splash->frameCount = gif->ImageCount;123splash->frames = (SplashImage *)124SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(SplashImage), gif->ImageCount);125if (!splash->frames) {126free(pBitmapBits);127free(pOldBitmapBits);128return 0;129}130memset(splash->frames, 0, sizeof(SplashImage) * gif->ImageCount);131splash->loopCount = 1;132133for (imageIndex = 0; imageIndex < gif->ImageCount; imageIndex++) {134SavedImage *image = &(gif->SavedImages[imageIndex]);135GifImageDesc *desc = &(image->ImageDesc);136ColorMapObject *colorMap =137desc->ColorMap ? desc->ColorMap : gif->SColorMap;138139int transparentColor = -1;140int frameDelay = 100;141int disposeMethod = GIF_DISPOSE_RESTORE;142int colorCount = 0;143rgbquad_t colorMapBuf[SPLASH_COLOR_MAP_SIZE];144145cx = FIX_POINT(desc->Left, 0, gif->SWidth);146cy = FIX_POINT(desc->Top, 0, gif->SHeight);147cw = FIX_LENGTH(desc->Left, desc->Width, gif->SWidth);148ch = FIX_LENGTH(desc->Top, desc->Height, gif->SHeight);149150if (colorMap) {151if (colorMap->ColorCount <= SPLASH_COLOR_MAP_SIZE) {152colorCount = colorMap->ColorCount;153} else {154colorCount = SPLASH_COLOR_MAP_SIZE;155}156}157158/* the code below is loosely based around gif extension processing from win32 libungif sample */159160for (i = 0; i < image->ExtensionBlockCount; i++) {161byte_t *pExtension = (byte_t *) image->ExtensionBlocks[i].Bytes;162unsigned size = image->ExtensionBlocks[i].ByteCount;163164switch (image->ExtensionBlocks[i].Function) {165case GRAPHICS_EXT_FUNC_CODE:166{167int flag = pExtension[0];168169frameDelay = (((int)pExtension[2]) << 8) | pExtension[1];170if (frameDelay < 10)171frameDelay = 10;172if (flag & GIF_TRANSPARENT) {173transparentColor = pExtension[3];174} else {175transparentColor = GIF_NOT_TRANSPARENT;176}177disposeMethod =178(flag >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;179break;180}181case APPLICATION_EXT_FUNC_CODE:182{183if (size == sizeof(szNetscape20ext)184&& memcmp(pExtension, szNetscape20ext, size) == 0) {185int iSubCode;186187if (++i >= image->ExtensionBlockCount)188break;189pExtension = (byte_t *) image->ExtensionBlocks[i].Bytes;190if (image->ExtensionBlocks[i].ByteCount != 3)191break;192iSubCode = pExtension[0] & 0x07;193if (iSubCode == NSEXT_LOOP) {194splash->loopCount =195(pExtension[1] | (((int)pExtension[2]) << 8)) - 1;196}197}198break;199}200default:201break;202}203}204205if (colorMap) {206for (i = 0; i < colorCount; i++) {207colorMapBuf[i] = MAKE_QUAD_GIF(colorMap->Colors[i], 0xff);208}209}210{211212byte_t *pSrc = image->RasterBits;213ImageFormat srcFormat;214ImageRect srcRect, dstRect;215int pass = 4, npass = 5;216217#if GIFLIB_MAJOR < 5218/* Interlaced gif support is broken in giflib < 5219so we need to work around this */220if (desc->Interlace) {221pass = 0;222npass = 4;223}224#endif225226srcFormat.colorMap = colorMapBuf;227srcFormat.depthBytes = 1;228srcFormat.byteOrder = BYTE_ORDER_NATIVE;229srcFormat.transparentColor = transparentColor;230srcFormat.fixedBits = QUAD_ALPHA_MASK; // fixed 100% alpha231srcFormat.premultiplied = 0;232233for (; pass < npass; ++pass) {234int jump = interlacedJumps[pass];235int ofs = interlacedOffset[pass];236/* Number of source lines for current pass */237int numPassLines = (desc->Height + jump - ofs - 1) / jump;238/* Number of lines that fits to dest buffer */239int numLines = (ch + jump - ofs - 1) / jump;240241initRect(&srcRect, 0, 0, desc->Width, numLines, 1,242desc->Width, pSrc, &srcFormat);243244if (numLines > 0) {245initRect(&dstRect, cx, cy + ofs, cw,246numLines , jump, stride, pBitmapBits, &splash->imageFormat);247248pSrc += convertRect(&srcRect, &dstRect, CVT_ALPHATEST);249}250// skip extra source data251pSrc += (numPassLines - numLines) * srcRect.stride;252}253}254255// now dispose of the previous frame correctly256257splash->frames[imageIndex].bitmapBits =258(rgbquad_t *) malloc(bufferSize); // bufferSize is safe (checked above)259if (!splash->frames[imageIndex].bitmapBits) {260free(pBitmapBits);261free(pOldBitmapBits);262/* Assuming that callee will take care of splash frames we have already allocated */263return 0;264}265memcpy(splash->frames[imageIndex].bitmapBits, pBitmapBits, bufferSize);266267SplashInitFrameShape(splash, imageIndex);268269splash->frames[imageIndex].delay = frameDelay * 10; // 100ths of second to milliseconds270switch (disposeMethod) {271case GIF_DISPOSE_LEAVE:272memcpy(pOldBitmapBits, pBitmapBits, bufferSize);273break;274case GIF_DISPOSE_NONE:275break;276case GIF_DISPOSE_BACKGND:277{278ImageRect dstRect;279rgbquad_t fillColor = 0; // 0 is transparent280281if (transparentColor < 0) {282fillColor= MAKE_QUAD_GIF(283colorMap->Colors[gif->SBackGroundColor], 0xff);284}285initRect(&dstRect,286cx, cy, cw, ch,2871, stride,288pBitmapBits, &splash->imageFormat);289fillRect(fillColor, &dstRect);290}291break;292case GIF_DISPOSE_RESTORE:293{294int lineSize = cw * splash->imageFormat.depthBytes;295if (lineSize > 0) {296int lineOffset = cx * splash->imageFormat.depthBytes;297int lineIndex = cy * stride + lineOffset;298for (j=0; j<ch; j++) {299memcpy(pBitmapBits + lineIndex, pOldBitmapBits + lineIndex,300lineSize);301lineIndex += stride;302}303}304}305break;306}307}308309free(pBitmapBits);310free(pOldBitmapBits);311312#if GIFLIB_MAJOR > 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1)313if (DGifCloseFile(gif, NULL) == GIF_ERROR) {314return 0;315}316#else317DGifCloseFile(gif);318#endif319320return 1;321}322323int324SplashDecodeGifStream(Splash * splash, SplashStream * stream)325{326#if GIFLIB_MAJOR >= 5327GifFileType *gif = DGifOpen((void *) stream, SplashStreamGifInputFunc, NULL);328#else329GifFileType *gif = DGifOpen((void *) stream, SplashStreamGifInputFunc);330#endif331332if (!gif)333return 0;334return SplashDecodeGif(splash, gif);335}336337338