mirror of
https://github.com/joel16/VITAlbum.git
synced 2025-02-20 02:31:37 +00:00
textures: Use giflib instead of libnsgif for decoding gifs
This commit is contained in:
parent
705eaa677c
commit
90efecf12b
@ -28,9 +28,9 @@ set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d PARENTAL_LEVEL=1")
|
||||
|
||||
include_directories(
|
||||
${VITASDK}/arm-vita-eabi/include/freetype2/
|
||||
libs/giflib
|
||||
libs/imgui
|
||||
libs/libnsbmp
|
||||
libs/libnsgif
|
||||
libs/libtiff
|
||||
libs
|
||||
include
|
||||
@ -48,8 +48,6 @@ add_executable(${PROJECT_NAME}
|
||||
libs/imgui/imgui_widgets.cpp
|
||||
libs/imgui/misc/freetype/imgui_freetype.cpp
|
||||
libs/libnsbmp/libnsbmp.c
|
||||
libs/libnsgif/libnsgif.c
|
||||
libs/libnsgif/lzw.c
|
||||
source/config.cpp
|
||||
source/fs.cpp
|
||||
source/gui.cpp
|
||||
@ -68,6 +66,7 @@ add_executable(${PROJECT_NAME}
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
freetype
|
||||
gif
|
||||
tiff
|
||||
webpdemux
|
||||
webp
|
||||
|
303
libs/giflib/gif_lib.h
Normal file
303
libs/giflib/gif_lib.h
Normal file
@ -0,0 +1,303 @@
|
||||
/******************************************************************************
|
||||
|
||||
gif_lib.h - service library for decoding and encoding GIF images
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef _GIF_LIB_H_
|
||||
#define _GIF_LIB_H_ 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define GIFLIB_MAJOR 5
|
||||
#define GIFLIB_MINOR 2
|
||||
#define GIFLIB_RELEASE 1
|
||||
|
||||
#define GIF_ERROR 0
|
||||
#define GIF_OK 1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */
|
||||
#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1
|
||||
#define GIF_VERSION_POS 3 /* Version first character in stamp. */
|
||||
#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */
|
||||
#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */
|
||||
|
||||
typedef unsigned char GifPixelType;
|
||||
typedef unsigned char *GifRowType;
|
||||
typedef unsigned char GifByteType;
|
||||
typedef unsigned int GifPrefixType;
|
||||
typedef int GifWord;
|
||||
|
||||
typedef struct GifColorType {
|
||||
GifByteType Red, Green, Blue;
|
||||
} GifColorType;
|
||||
|
||||
typedef struct ColorMapObject {
|
||||
int ColorCount;
|
||||
int BitsPerPixel;
|
||||
bool SortFlag;
|
||||
GifColorType *Colors; /* on malloc(3) heap */
|
||||
} ColorMapObject;
|
||||
|
||||
typedef struct GifImageDesc {
|
||||
GifWord Left, Top, Width, Height; /* Current image dimensions. */
|
||||
bool Interlace; /* Sequential/Interlaced lines. */
|
||||
ColorMapObject *ColorMap; /* The local color map */
|
||||
} GifImageDesc;
|
||||
|
||||
typedef struct ExtensionBlock {
|
||||
int ByteCount;
|
||||
GifByteType *Bytes; /* on malloc(3) heap */
|
||||
int Function; /* The block function code */
|
||||
#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */
|
||||
#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */
|
||||
#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */
|
||||
#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */
|
||||
#define APPLICATION_EXT_FUNC_CODE 0xff /* application block (GIF89) */
|
||||
} ExtensionBlock;
|
||||
|
||||
typedef struct SavedImage {
|
||||
GifImageDesc ImageDesc;
|
||||
GifByteType *RasterBits; /* on malloc(3) heap */
|
||||
int ExtensionBlockCount; /* Count of extensions before image */
|
||||
ExtensionBlock *ExtensionBlocks; /* Extensions before image */
|
||||
} SavedImage;
|
||||
|
||||
typedef struct GifFileType {
|
||||
GifWord SWidth, SHeight; /* Size of virtual canvas */
|
||||
GifWord SColorResolution; /* How many colors can we generate? */
|
||||
GifWord SBackGroundColor; /* Background color for virtual canvas */
|
||||
GifByteType AspectByte; /* Used to compute pixel aspect ratio */
|
||||
ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */
|
||||
int ImageCount; /* Number of current image (both APIs) */
|
||||
GifImageDesc Image; /* Current image (low-level API) */
|
||||
SavedImage *SavedImages; /* Image sequence (high-level API) */
|
||||
int ExtensionBlockCount; /* Count extensions past last image */
|
||||
ExtensionBlock *ExtensionBlocks; /* Extensions past last image */
|
||||
int Error; /* Last error condition reported */
|
||||
void *UserData; /* hook to attach user data (TVT) */
|
||||
void *Private; /* Don't mess with this! */
|
||||
} GifFileType;
|
||||
|
||||
#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0)
|
||||
|
||||
typedef enum {
|
||||
UNDEFINED_RECORD_TYPE,
|
||||
SCREEN_DESC_RECORD_TYPE,
|
||||
IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */
|
||||
EXTENSION_RECORD_TYPE, /* Begin with '!' */
|
||||
TERMINATE_RECORD_TYPE /* Begin with ';' */
|
||||
} GifRecordType;
|
||||
|
||||
/* func type to read gif data from arbitrary sources (TVT) */
|
||||
typedef int (*InputFunc) (GifFileType *, GifByteType *, int);
|
||||
|
||||
/* func type to write gif data to arbitrary targets.
|
||||
* Returns count of bytes written. (MRB)
|
||||
*/
|
||||
typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int);
|
||||
|
||||
/******************************************************************************
|
||||
GIF89 structures
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct GraphicsControlBlock {
|
||||
int DisposalMode;
|
||||
#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */
|
||||
#define DISPOSE_DO_NOT 1 /* Leave image in place */
|
||||
#define DISPOSE_BACKGROUND 2 /* Set area too background color */
|
||||
#define DISPOSE_PREVIOUS 3 /* Restore to previous content */
|
||||
bool UserInputFlag; /* User confirmation required before disposal */
|
||||
int DelayTime; /* pre-display delay in 0.01sec units */
|
||||
int TransparentColor; /* Palette index for transparency, -1 if none */
|
||||
#define NO_TRANSPARENT_COLOR -1
|
||||
} GraphicsControlBlock;
|
||||
|
||||
/******************************************************************************
|
||||
GIF encoding routines
|
||||
******************************************************************************/
|
||||
|
||||
/* Main entry points */
|
||||
GifFileType *EGifOpenFileName(const char *GifFileName,
|
||||
const bool GifTestExistence, int *Error);
|
||||
GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error);
|
||||
GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error);
|
||||
int EGifSpew(GifFileType * GifFile);
|
||||
const char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */
|
||||
int EGifCloseFile(GifFileType *GifFile, int *ErrorCode);
|
||||
|
||||
#define E_GIF_SUCCEEDED 0
|
||||
#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */
|
||||
#define E_GIF_ERR_WRITE_FAILED 2
|
||||
#define E_GIF_ERR_HAS_SCRN_DSCR 3
|
||||
#define E_GIF_ERR_HAS_IMAG_DSCR 4
|
||||
#define E_GIF_ERR_NO_COLOR_MAP 5
|
||||
#define E_GIF_ERR_DATA_TOO_BIG 6
|
||||
#define E_GIF_ERR_NOT_ENOUGH_MEM 7
|
||||
#define E_GIF_ERR_DISK_IS_FULL 8
|
||||
#define E_GIF_ERR_CLOSE_FAILED 9
|
||||
#define E_GIF_ERR_NOT_WRITEABLE 10
|
||||
|
||||
/* These are legacy. You probably do not want to call them directly */
|
||||
int EGifPutScreenDesc(GifFileType *GifFile,
|
||||
const int GifWidth, const int GifHeight,
|
||||
const int GifColorRes,
|
||||
const int GifBackGround,
|
||||
const ColorMapObject *GifColorMap);
|
||||
int EGifPutImageDesc(GifFileType *GifFile,
|
||||
const int GifLeft, const int GifTop,
|
||||
const int GifWidth, const int GifHeight,
|
||||
const bool GifInterlace,
|
||||
const ColorMapObject *GifColorMap);
|
||||
void EGifSetGifVersion(GifFileType *GifFile, const bool gif89);
|
||||
int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine,
|
||||
int GifLineLen);
|
||||
int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel);
|
||||
int EGifPutComment(GifFileType *GifFile, const char *GifComment);
|
||||
int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode);
|
||||
int EGifPutExtensionBlock(GifFileType *GifFile,
|
||||
const int GifExtLen, const void *GifExtension);
|
||||
int EGifPutExtensionTrailer(GifFileType *GifFile);
|
||||
int EGifPutExtension(GifFileType *GifFile, const int GifExtCode,
|
||||
const int GifExtLen,
|
||||
const void *GifExtension);
|
||||
int EGifPutCode(GifFileType *GifFile, int GifCodeSize,
|
||||
const GifByteType *GifCodeBlock);
|
||||
int EGifPutCodeNext(GifFileType *GifFile,
|
||||
const GifByteType *GifCodeBlock);
|
||||
|
||||
/******************************************************************************
|
||||
GIF decoding routines
|
||||
******************************************************************************/
|
||||
|
||||
/* Main entry points */
|
||||
GifFileType *DGifOpenFileName(const char *GifFileName, int *Error);
|
||||
GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error);
|
||||
int DGifSlurp(GifFileType * GifFile);
|
||||
GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */
|
||||
int DGifCloseFile(GifFileType * GifFile, int *ErrorCode);
|
||||
|
||||
#define D_GIF_SUCCEEDED 0
|
||||
#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */
|
||||
#define D_GIF_ERR_READ_FAILED 102
|
||||
#define D_GIF_ERR_NOT_GIF_FILE 103
|
||||
#define D_GIF_ERR_NO_SCRN_DSCR 104
|
||||
#define D_GIF_ERR_NO_IMAG_DSCR 105
|
||||
#define D_GIF_ERR_NO_COLOR_MAP 106
|
||||
#define D_GIF_ERR_WRONG_RECORD 107
|
||||
#define D_GIF_ERR_DATA_TOO_BIG 108
|
||||
#define D_GIF_ERR_NOT_ENOUGH_MEM 109
|
||||
#define D_GIF_ERR_CLOSE_FAILED 110
|
||||
#define D_GIF_ERR_NOT_READABLE 111
|
||||
#define D_GIF_ERR_IMAGE_DEFECT 112
|
||||
#define D_GIF_ERR_EOF_TOO_SOON 113
|
||||
|
||||
/* These are legacy. You probably do not want to call them directly */
|
||||
int DGifGetScreenDesc(GifFileType *GifFile);
|
||||
int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType);
|
||||
int DGifGetImageHeader(GifFileType *GifFile);
|
||||
int DGifGetImageDesc(GifFileType *GifFile);
|
||||
int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen);
|
||||
int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel);
|
||||
int DGifGetExtension(GifFileType *GifFile, int *GifExtCode,
|
||||
GifByteType **GifExtension);
|
||||
int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension);
|
||||
int DGifGetCode(GifFileType *GifFile, int *GifCodeSize,
|
||||
GifByteType **GifCodeBlock);
|
||||
int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock);
|
||||
int DGifGetLZCodes(GifFileType *GifFile, int *GifCode);
|
||||
const char *DGifGetGifVersion(GifFileType *GifFile);
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Error handling and reporting.
|
||||
******************************************************************************/
|
||||
extern const char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */
|
||||
|
||||
/*****************************************************************************
|
||||
Everything below this point is new after version 1.2, supporting `slurp
|
||||
mode' for doing I/O in two big belts with all the image-bashing in core.
|
||||
******************************************************************************/
|
||||
|
||||
/******************************************************************************
|
||||
Color map handling from gif_alloc.c
|
||||
******************************************************************************/
|
||||
|
||||
extern ColorMapObject *GifMakeMapObject(int ColorCount,
|
||||
const GifColorType *ColorMap);
|
||||
extern void GifFreeMapObject(ColorMapObject *Object);
|
||||
extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1,
|
||||
const ColorMapObject *ColorIn2,
|
||||
GifPixelType ColorTransIn2[]);
|
||||
extern int GifBitSize(int n);
|
||||
|
||||
/******************************************************************************
|
||||
Support for the in-core structures allocation (slurp mode).
|
||||
******************************************************************************/
|
||||
|
||||
extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]);
|
||||
extern int GifAddExtensionBlock(int *ExtensionBlock_Count,
|
||||
ExtensionBlock **ExtensionBlocks,
|
||||
int Function,
|
||||
unsigned int Len, unsigned char ExtData[]);
|
||||
extern void GifFreeExtensions(int *ExtensionBlock_Count,
|
||||
ExtensionBlock **ExtensionBlocks);
|
||||
extern SavedImage *GifMakeSavedImage(GifFileType *GifFile,
|
||||
const SavedImage *CopyFrom);
|
||||
extern void GifFreeSavedImages(GifFileType *GifFile);
|
||||
|
||||
/******************************************************************************
|
||||
5.x functions for GIF89 graphics control blocks
|
||||
******************************************************************************/
|
||||
|
||||
int DGifExtensionToGCB(const size_t GifExtensionLength,
|
||||
const GifByteType *GifExtension,
|
||||
GraphicsControlBlock *GCB);
|
||||
size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
|
||||
GifByteType *GifExtension);
|
||||
|
||||
int DGifSavedExtensionToGCB(GifFileType *GifFile,
|
||||
int ImageIndex,
|
||||
GraphicsControlBlock *GCB);
|
||||
int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
|
||||
GifFileType *GifFile,
|
||||
int ImageIndex);
|
||||
|
||||
/******************************************************************************
|
||||
The library's internal utility font
|
||||
******************************************************************************/
|
||||
|
||||
#define GIF_FONT_WIDTH 8
|
||||
#define GIF_FONT_HEIGHT 8
|
||||
extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH];
|
||||
|
||||
extern void GifDrawText8x8(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const char *legend, const int color);
|
||||
|
||||
extern void GifDrawBox(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const int w, const int d, const int color);
|
||||
|
||||
extern void GifDrawRectangle(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const int w, const int d, const int color);
|
||||
|
||||
extern void GifDrawBoxedText8x8(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const char *legend,
|
||||
const int border, const int bg, const int fg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* _GIF_LIB_H */
|
||||
|
||||
/* end */
|
BIN
libs/lib/libgif.a
Normal file
BIN
libs/lib/libgif.a
Normal file
Binary file not shown.
@ -1,20 +0,0 @@
|
||||
Copyright (C) 2004 Richard Wilson
|
||||
Copyright (C) 2008 Sean Fox
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
@ -1,191 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>
|
||||
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
|
||||
*
|
||||
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
|
||||
* Licenced under the MIT License,
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Interface to progressive animated GIF file decoding.
|
||||
*/
|
||||
|
||||
#ifndef _LIBNSGIF_H_
|
||||
#define _LIBNSGIF_H_
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/* Error return values */
|
||||
typedef enum {
|
||||
GIF_WORKING = 1,
|
||||
GIF_OK = 0,
|
||||
GIF_INSUFFICIENT_FRAME_DATA = -1,
|
||||
GIF_FRAME_DATA_ERROR = -2,
|
||||
GIF_INSUFFICIENT_DATA = -3,
|
||||
GIF_DATA_ERROR = -4,
|
||||
GIF_INSUFFICIENT_MEMORY = -5,
|
||||
GIF_FRAME_NO_DISPLAY = -6,
|
||||
GIF_END_OF_FRAME = -7
|
||||
} gif_result;
|
||||
|
||||
/** GIF frame data */
|
||||
typedef struct gif_frame {
|
||||
/** whether the frame should be displayed/animated */
|
||||
bool display;
|
||||
/** delay (in cs) before animating the frame */
|
||||
unsigned int frame_delay;
|
||||
|
||||
/* Internal members are listed below */
|
||||
|
||||
/** offset (in bytes) to the GIF frame data */
|
||||
unsigned int frame_pointer;
|
||||
/** whether the frame has previously been used */
|
||||
bool virgin;
|
||||
/** whether the frame is totally opaque */
|
||||
bool opaque;
|
||||
/** whether a forcable screen redraw is required */
|
||||
bool redraw_required;
|
||||
/** how the previous frame should be disposed; affects plotting */
|
||||
unsigned char disposal_method;
|
||||
/** whether we acknoledge transparency */
|
||||
bool transparency;
|
||||
/** the index designating a transparent pixel */
|
||||
unsigned char transparency_index;
|
||||
/** x co-ordinate of redraw rectangle */
|
||||
unsigned int redraw_x;
|
||||
/** y co-ordinate of redraw rectangle */
|
||||
unsigned int redraw_y;
|
||||
/** width of redraw rectangle */
|
||||
unsigned int redraw_width;
|
||||
/** height of redraw rectangle */
|
||||
unsigned int redraw_height;
|
||||
} gif_frame;
|
||||
|
||||
/* API for Bitmap callbacks */
|
||||
typedef void* (*gif_bitmap_cb_create)(int width, int height);
|
||||
typedef void (*gif_bitmap_cb_destroy)(void *bitmap);
|
||||
typedef unsigned char* (*gif_bitmap_cb_get_buffer)(void *bitmap);
|
||||
typedef void (*gif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
|
||||
typedef bool (*gif_bitmap_cb_test_opaque)(void *bitmap);
|
||||
typedef void (*gif_bitmap_cb_modified)(void *bitmap);
|
||||
|
||||
/** Bitmap callbacks function table */
|
||||
typedef struct gif_bitmap_callback_vt {
|
||||
/** Create a bitmap. */
|
||||
gif_bitmap_cb_create bitmap_create;
|
||||
/** Free a bitmap. */
|
||||
gif_bitmap_cb_destroy bitmap_destroy;
|
||||
/** Return a pointer to the pixel data in a bitmap. */
|
||||
gif_bitmap_cb_get_buffer bitmap_get_buffer;
|
||||
|
||||
/* Members below are optional */
|
||||
|
||||
/** Sets whether a bitmap should be plotted opaque. */
|
||||
gif_bitmap_cb_set_opaque bitmap_set_opaque;
|
||||
/** Tests whether a bitmap has an opaque alpha channel. */
|
||||
gif_bitmap_cb_test_opaque bitmap_test_opaque;
|
||||
/** The bitmap image has changed, so flush any persistant cache. */
|
||||
gif_bitmap_cb_modified bitmap_modified;
|
||||
} gif_bitmap_callback_vt;
|
||||
|
||||
/** GIF animation data */
|
||||
typedef struct gif_animation {
|
||||
/** LZW decode context */
|
||||
void *lzw_ctx;
|
||||
/** callbacks for bitmap functions */
|
||||
gif_bitmap_callback_vt bitmap_callbacks;
|
||||
/** pointer to GIF data */
|
||||
unsigned char *gif_data;
|
||||
/** width of GIF (may increase during decoding) */
|
||||
unsigned int width;
|
||||
/** heigth of GIF (may increase during decoding) */
|
||||
unsigned int height;
|
||||
/** number of frames decoded */
|
||||
unsigned int frame_count;
|
||||
/** number of frames partially decoded */
|
||||
unsigned int frame_count_partial;
|
||||
/** decoded frames */
|
||||
gif_frame *frames;
|
||||
/** current frame decoded to bitmap */
|
||||
int decoded_frame;
|
||||
/** currently decoded image; stored as bitmap from bitmap_create callback */
|
||||
void *frame_image;
|
||||
/** number of times to loop animation */
|
||||
int loop_count;
|
||||
|
||||
/* Internal members are listed below */
|
||||
|
||||
/** current index into GIF data */
|
||||
unsigned int buffer_position;
|
||||
/** total number of bytes of GIF data available */
|
||||
unsigned int buffer_size;
|
||||
/** current number of frame holders */
|
||||
unsigned int frame_holders;
|
||||
/** index in the colour table for the background colour */
|
||||
unsigned int background_index;
|
||||
/** image aspect ratio (ignored) */
|
||||
unsigned int aspect_ratio;
|
||||
/** size of colour table (in entries) */
|
||||
unsigned int colour_table_size;
|
||||
/** whether the GIF has a global colour table */
|
||||
bool global_colours;
|
||||
/** global colour table */
|
||||
unsigned int *global_colour_table;
|
||||
/** local colour table */
|
||||
unsigned int *local_colour_table;
|
||||
} gif_animation;
|
||||
|
||||
/**
|
||||
* Initialises necessary gif_animation members.
|
||||
*/
|
||||
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
|
||||
|
||||
/**
|
||||
* Initialises any workspace held by the animation and attempts to decode
|
||||
* any information that hasn't already been decoded.
|
||||
* If an error occurs, all previously decoded frames are retained.
|
||||
*
|
||||
* @return Error return value.
|
||||
* - GIF_FRAME_DATA_ERROR for GIF frame data error
|
||||
* - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process
|
||||
* any more frames
|
||||
* - GIF_INSUFFICIENT_MEMORY for memory error
|
||||
* - GIF_DATA_ERROR for GIF error
|
||||
* - GIF_INSUFFICIENT_DATA for insufficient data to do anything
|
||||
* - GIF_OK for successful decoding
|
||||
* - GIF_WORKING for successful decoding if more frames are expected
|
||||
*/
|
||||
gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);
|
||||
|
||||
/**
|
||||
* Decodes a GIF frame.
|
||||
*
|
||||
* @return Error return value. If a frame does not contain any image data,
|
||||
* GIF_OK is returned and gif->current_error is set to
|
||||
* GIF_FRAME_NO_DISPLAY
|
||||
* - GIF_FRAME_DATA_ERROR for GIF frame data error
|
||||
* - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
|
||||
* - GIF_DATA_ERROR for GIF error (invalid frame header)
|
||||
* - GIF_INSUFFICIENT_DATA for insufficient data to do anything
|
||||
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
|
||||
* - GIF_OK for successful decoding
|
||||
*/
|
||||
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame);
|
||||
|
||||
/**
|
||||
* Releases any workspace held by a gif
|
||||
*/
|
||||
void gif_finalise(gif_animation *gif);
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,377 +0,0 @@
|
||||
/*
|
||||
* This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
|
||||
* Licensed under the MIT License,
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Copyright 2017 Michael Drake <michael.drake@codethink.co.uk>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lzw.h"
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief LZW decompression (implementation)
|
||||
*
|
||||
* Decoder for GIF LZW data.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Context for reading LZW data.
|
||||
*
|
||||
* LZW data is split over multiple sub-blocks. Each sub-block has a
|
||||
* byte at the start, which says the sub-block size, and then the data.
|
||||
* Zero-size sub-blocks have no data, and the biggest sub-block size is
|
||||
* 255, which means there are 255 bytes of data following the sub-block
|
||||
* size entry.
|
||||
*
|
||||
* Note that an individual LZW code can be split over up to three sub-blocks.
|
||||
*/
|
||||
struct lzw_read_ctx {
|
||||
const uint8_t *data; /**< Pointer to start of input data */
|
||||
uint32_t data_len; /**< Input data length */
|
||||
uint32_t data_sb_next; /**< Offset to sub-block size */
|
||||
|
||||
const uint8_t *sb_data; /**< Pointer to current sub-block in data */
|
||||
uint32_t sb_bit; /**< Current bit offset in sub-block */
|
||||
uint32_t sb_bit_count; /**< Bit count in sub-block */
|
||||
};
|
||||
|
||||
/**
|
||||
* LZW dictionary entry.
|
||||
*
|
||||
* Records in the dictionary are composed of 1 or more entries.
|
||||
* Entries point to previous entries which can be followed to compose
|
||||
* the complete record. To compose the record in reverse order, take
|
||||
* the `last_value` from each entry, and move to the previous entry.
|
||||
* If the previous_entry's index is < the current clear_code, then it
|
||||
* is the last entry in the record.
|
||||
*/
|
||||
struct lzw_dictionary_entry {
|
||||
uint8_t last_value; /**< Last value for record ending at entry. */
|
||||
uint8_t first_value; /**< First value for entry's record. */
|
||||
uint16_t previous_entry; /**< Offset in dictionary to previous entry. */
|
||||
};
|
||||
|
||||
/**
|
||||
* LZW decompression context.
|
||||
*/
|
||||
struct lzw_ctx {
|
||||
/** Input reading context */
|
||||
struct lzw_read_ctx input;
|
||||
|
||||
uint32_t previous_code; /**< Code read from input previously. */
|
||||
uint32_t previous_code_first; /**< First value of previous code. */
|
||||
|
||||
uint32_t initial_code_size; /**< Starting LZW code size. */
|
||||
uint32_t current_code_size; /**< Current LZW code size. */
|
||||
uint32_t current_code_size_max; /**< Max code value for current size. */
|
||||
|
||||
uint32_t clear_code; /**< Special Clear code value */
|
||||
uint32_t eoi_code; /**< Special End of Information code value */
|
||||
|
||||
uint32_t current_entry; /**< Next position in table to fill. */
|
||||
|
||||
/** Output value stack. */
|
||||
uint8_t stack_base[1 << LZW_CODE_MAX];
|
||||
|
||||
/** LZW decode dictionary. Generated during decode. */
|
||||
struct lzw_dictionary_entry table[1 << LZW_CODE_MAX];
|
||||
};
|
||||
|
||||
|
||||
/* Exported function, documented in lzw.h */
|
||||
lzw_result lzw_context_create(struct lzw_ctx **ctx)
|
||||
{
|
||||
struct lzw_ctx *c = malloc(sizeof(*c));
|
||||
if (c == NULL) {
|
||||
return LZW_NO_MEM;
|
||||
}
|
||||
|
||||
*ctx = c;
|
||||
return LZW_OK;
|
||||
}
|
||||
|
||||
|
||||
/* Exported function, documented in lzw.h */
|
||||
void lzw_context_destroy(struct lzw_ctx *ctx)
|
||||
{
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Advance the context to the next sub-block in the input data.
|
||||
*
|
||||
* \param[in] ctx LZW reading context, updated on success.
|
||||
* \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
|
||||
*/
|
||||
static lzw_result lzw__block_advance(struct lzw_read_ctx *ctx)
|
||||
{
|
||||
uint32_t block_size;
|
||||
uint32_t next_block_pos = ctx->data_sb_next;
|
||||
const uint8_t *data_next = ctx->data + next_block_pos;
|
||||
|
||||
if (next_block_pos >= ctx->data_len) {
|
||||
return LZW_NO_DATA;
|
||||
}
|
||||
|
||||
block_size = *data_next;
|
||||
|
||||
if ((next_block_pos + block_size) >= ctx->data_len) {
|
||||
return LZW_NO_DATA;
|
||||
}
|
||||
|
||||
ctx->sb_bit = 0;
|
||||
ctx->sb_bit_count = block_size * 8;
|
||||
|
||||
if (block_size == 0) {
|
||||
ctx->data_sb_next += 1;
|
||||
return LZW_OK_EOD;
|
||||
}
|
||||
|
||||
ctx->sb_data = data_next + 1;
|
||||
ctx->data_sb_next += block_size + 1;
|
||||
|
||||
return LZW_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next LZW code of given size from the raw input data.
|
||||
*
|
||||
* Reads codes from the input data stream coping with GIF data sub-blocks.
|
||||
*
|
||||
* \param[in] ctx LZW reading context, updated.
|
||||
* \param[in] code_size Size of LZW code to get from data.
|
||||
* \param[out] code_out Returns an LZW code on success.
|
||||
* \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
|
||||
*/
|
||||
static inline lzw_result lzw__next_code(
|
||||
struct lzw_read_ctx *ctx,
|
||||
uint8_t code_size,
|
||||
uint32_t *code_out)
|
||||
{
|
||||
uint32_t code = 0;
|
||||
uint8_t current_bit = ctx->sb_bit & 0x7;
|
||||
uint8_t byte_advance = (current_bit + code_size) >> 3;
|
||||
|
||||
assert(byte_advance <= 2);
|
||||
|
||||
if (ctx->sb_bit + code_size <= ctx->sb_bit_count) {
|
||||
/* Fast path: code fully inside this sub-block */
|
||||
const uint8_t *data = ctx->sb_data + (ctx->sb_bit >> 3);
|
||||
switch (byte_advance) {
|
||||
case 2: code |= data[2] << 16; /* Fall through */
|
||||
case 1: code |= data[1] << 8; /* Fall through */
|
||||
case 0: code |= data[0] << 0;
|
||||
}
|
||||
ctx->sb_bit += code_size;
|
||||
} else {
|
||||
/* Slow path: code spans sub-blocks */
|
||||
uint8_t byte = 0;
|
||||
uint8_t bits_remaining_0 = (code_size < (8 - current_bit)) ?
|
||||
code_size : (8 - current_bit);
|
||||
uint8_t bits_remaining_1 = code_size - bits_remaining_0;
|
||||
uint8_t bits_used[3] = {
|
||||
[0] = bits_remaining_0,
|
||||
[1] = bits_remaining_1 < 8 ? bits_remaining_1 : 8,
|
||||
[2] = bits_remaining_1 - 8,
|
||||
};
|
||||
|
||||
while (true) {
|
||||
const uint8_t *data = ctx->sb_data;
|
||||
lzw_result res;
|
||||
|
||||
/* Get any data from end of this sub-block */
|
||||
while (byte <= byte_advance &&
|
||||
ctx->sb_bit < ctx->sb_bit_count) {
|
||||
code |= data[ctx->sb_bit >> 3] << (byte << 3);
|
||||
ctx->sb_bit += bits_used[byte];
|
||||
byte++;
|
||||
}
|
||||
|
||||
/* Check if we have all we need */
|
||||
if (byte > byte_advance) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move to next sub-block */
|
||||
res = lzw__block_advance(ctx);
|
||||
if (res != LZW_OK) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*code_out = (code >> current_bit) & ((1 << code_size) - 1);
|
||||
return LZW_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear LZW code dictionary.
|
||||
*
|
||||
* \param[in] ctx LZW reading context, updated.
|
||||
* \param[out] stack_pos_out Returns current stack position.
|
||||
* \return LZW_OK or error code.
|
||||
*/
|
||||
static lzw_result lzw__clear_codes(
|
||||
struct lzw_ctx *ctx,
|
||||
const uint8_t ** const stack_pos_out)
|
||||
{
|
||||
uint32_t code;
|
||||
uint8_t *stack_pos;
|
||||
|
||||
/* Reset dictionary building context */
|
||||
ctx->current_code_size = ctx->initial_code_size + 1;
|
||||
ctx->current_code_size_max = (1 << ctx->current_code_size) - 1;;
|
||||
ctx->current_entry = (1 << ctx->initial_code_size) + 2;
|
||||
|
||||
/* There might be a sequence of clear codes, so process them all */
|
||||
do {
|
||||
lzw_result res = lzw__next_code(&ctx->input,
|
||||
ctx->current_code_size, &code);
|
||||
if (res != LZW_OK) {
|
||||
return res;
|
||||
}
|
||||
} while (code == ctx->clear_code);
|
||||
|
||||
/* The initial code must be from the initial dictionary. */
|
||||
if (code > ctx->clear_code) {
|
||||
return LZW_BAD_ICODE;
|
||||
}
|
||||
|
||||
/* Record this initial code as "previous" code, needed during decode. */
|
||||
ctx->previous_code = code;
|
||||
ctx->previous_code_first = code;
|
||||
|
||||
/* Reset the stack, and add first non-clear code added as first item. */
|
||||
stack_pos = ctx->stack_base;
|
||||
*stack_pos++ = code;
|
||||
|
||||
*stack_pos_out = stack_pos;
|
||||
return LZW_OK;
|
||||
}
|
||||
|
||||
|
||||
/* Exported function, documented in lzw.h */
|
||||
lzw_result lzw_decode_init(
|
||||
struct lzw_ctx *ctx,
|
||||
const uint8_t *compressed_data,
|
||||
uint32_t compressed_data_len,
|
||||
uint32_t compressed_data_pos,
|
||||
uint8_t code_size,
|
||||
const uint8_t ** const stack_base_out,
|
||||
const uint8_t ** const stack_pos_out)
|
||||
{
|
||||
struct lzw_dictionary_entry *table = ctx->table;
|
||||
|
||||
/* Initialise the input reading context */
|
||||
ctx->input.data = compressed_data;
|
||||
ctx->input.data_len = compressed_data_len;
|
||||
ctx->input.data_sb_next = compressed_data_pos;
|
||||
|
||||
ctx->input.sb_bit = 0;
|
||||
ctx->input.sb_bit_count = 0;
|
||||
|
||||
/* Initialise the dictionary building context */
|
||||
ctx->initial_code_size = code_size;
|
||||
|
||||
ctx->clear_code = (1 << code_size) + 0;
|
||||
ctx->eoi_code = (1 << code_size) + 1;
|
||||
|
||||
/* Initialise the standard dictionary entries */
|
||||
for (uint32_t i = 0; i < ctx->clear_code; ++i) {
|
||||
table[i].first_value = i;
|
||||
table[i].last_value = i;
|
||||
}
|
||||
|
||||
*stack_base_out = ctx->stack_base;
|
||||
return lzw__clear_codes(ctx, stack_pos_out);
|
||||
}
|
||||
|
||||
|
||||
/* Exported function, documented in lzw.h */
|
||||
lzw_result lzw_decode(struct lzw_ctx *ctx,
|
||||
const uint8_t ** const stack_pos_out)
|
||||
{
|
||||
lzw_result res;
|
||||
uint32_t code_new;
|
||||
uint32_t code_out;
|
||||
uint8_t last_value;
|
||||
uint8_t *stack_pos = ctx->stack_base;
|
||||
uint32_t clear_code = ctx->clear_code;
|
||||
uint32_t current_entry = ctx->current_entry;
|
||||
struct lzw_dictionary_entry * const table = ctx->table;
|
||||
|
||||
/* Get a new code from the input */
|
||||
res = lzw__next_code(&ctx->input, ctx->current_code_size, &code_new);
|
||||
if (res != LZW_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Handle the new code */
|
||||
if (code_new == clear_code) {
|
||||
/* Got Clear code */
|
||||
return lzw__clear_codes(ctx, stack_pos_out);
|
||||
|
||||
} else if (code_new == ctx->eoi_code) {
|
||||
/* Got End of Information code */
|
||||
return LZW_EOI_CODE;
|
||||
|
||||
} else if (code_new > current_entry) {
|
||||
/* Code is invalid */
|
||||
return LZW_BAD_CODE;
|
||||
|
||||
} else if (code_new < current_entry) {
|
||||
/* Code is in table */
|
||||
code_out = code_new;
|
||||
last_value = table[code_new].first_value;
|
||||
} else {
|
||||
/* Code not in table */
|
||||
*stack_pos++ = ctx->previous_code_first;
|
||||
code_out = ctx->previous_code;
|
||||
last_value = ctx->previous_code_first;
|
||||
}
|
||||
|
||||
/* Add to the dictionary, only if there's space */
|
||||
if (current_entry < (1 << LZW_CODE_MAX)) {
|
||||
struct lzw_dictionary_entry *entry = table + current_entry;
|
||||
entry->last_value = last_value;
|
||||
entry->first_value = ctx->previous_code_first;
|
||||
entry->previous_entry = ctx->previous_code;
|
||||
ctx->current_entry++;
|
||||
}
|
||||
|
||||
/* Ensure code size is increased, if needed. */
|
||||
if (current_entry == ctx->current_code_size_max) {
|
||||
if (ctx->current_code_size < LZW_CODE_MAX) {
|
||||
ctx->current_code_size++;
|
||||
ctx->current_code_size_max =
|
||||
(1 << ctx->current_code_size) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Store details of this code as "previous code" to the context. */
|
||||
ctx->previous_code_first = table[code_new].first_value;
|
||||
ctx->previous_code = code_new;
|
||||
|
||||
/* Put rest of data for this code on output stack.
|
||||
* Note, in the case of "code not in table", the last entry of the
|
||||
* current code has already been placed on the stack above. */
|
||||
while (code_out > clear_code) {
|
||||
struct lzw_dictionary_entry *entry = table + code_out;
|
||||
*stack_pos++ = entry->last_value;
|
||||
code_out = entry->previous_entry;
|
||||
}
|
||||
*stack_pos++ = table[code_out].last_value;
|
||||
|
||||
*stack_pos_out = stack_pos;
|
||||
return LZW_OK;
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
/*
|
||||
* This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
|
||||
* Licensed under the MIT License,
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Copyright 2017 Michael Drake <michael.drake@codethink.co.uk>
|
||||
*/
|
||||
|
||||
#ifndef LZW_H_
|
||||
#define LZW_H_
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief LZW decompression (interface)
|
||||
*
|
||||
* Decoder for GIF LZW data.
|
||||
*/
|
||||
|
||||
|
||||
/** Maximum LZW code size in bits */
|
||||
#define LZW_CODE_MAX 12
|
||||
|
||||
|
||||
/* Declare lzw internal context structure */
|
||||
struct lzw_ctx;
|
||||
|
||||
|
||||
/** LZW decoding response codes */
|
||||
typedef enum lzw_result {
|
||||
LZW_OK, /**< Success */
|
||||
LZW_OK_EOD, /**< Success; reached zero-length sub-block */
|
||||
LZW_NO_MEM, /**< Error: Out of memory */
|
||||
LZW_NO_DATA, /**< Error: Out of data */
|
||||
LZW_EOI_CODE, /**< Error: End of Information code */
|
||||
LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
|
||||
LZW_BAD_CODE, /**< Error: Bad LZW code */
|
||||
} lzw_result;
|
||||
|
||||
|
||||
/**
|
||||
* Create an LZW decompression context.
|
||||
*
|
||||
* \param[out] ctx Returns an LZW decompression context. Caller owned,
|
||||
* free with lzw_context_destroy().
|
||||
* \return LZW_OK on success, or appropriate error code otherwise.
|
||||
*/
|
||||
lzw_result lzw_context_create(
|
||||
struct lzw_ctx **ctx);
|
||||
|
||||
/**
|
||||
* Destroy an LZW decompression context.
|
||||
*
|
||||
* \param[in] ctx The LZW decompression context to destroy.
|
||||
*/
|
||||
void lzw_context_destroy(
|
||||
struct lzw_ctx *ctx);
|
||||
|
||||
/**
|
||||
* Initialise an LZW decompression context for decoding.
|
||||
*
|
||||
* Caller owns neither `stack_base_out` or `stack_pos_out`.
|
||||
*
|
||||
* \param[in] ctx The LZW decompression context to initialise.
|
||||
* \param[in] compressed_data The compressed data.
|
||||
* \param[in] compressed_data_len Byte length of compressed data.
|
||||
* \param[in] compressed_data_pos Start position in data. Must be position
|
||||
* of a size byte at sub-block start.
|
||||
* \param[in] code_size The initial LZW code size to use.
|
||||
* \param[out] stack_base_out Returns base of decompressed data stack.
|
||||
* \param[out] stack_pos_out Returns current stack position.
|
||||
* There are `stack_pos_out - stack_base_out`
|
||||
* current stack entries.
|
||||
* \return LZW_OK on success, or appropriate error code otherwise.
|
||||
*/
|
||||
lzw_result lzw_decode_init(
|
||||
struct lzw_ctx *ctx,
|
||||
const uint8_t *compressed_data,
|
||||
uint32_t compressed_data_len,
|
||||
uint32_t compressed_data_pos,
|
||||
uint8_t code_size,
|
||||
const uint8_t ** const stack_base_out,
|
||||
const uint8_t ** const stack_pos_out);
|
||||
|
||||
/**
|
||||
* Fill the LZW stack with decompressed data
|
||||
*
|
||||
* Ensure anything on the stack is used before calling this, as anything
|
||||
* on the stack before this call will be trampled.
|
||||
*
|
||||
* Caller does not own `stack_pos_out`.
|
||||
*
|
||||
* \param[in] ctx LZW reading context, updated.
|
||||
* \param[out] stack_pos_out Returns current stack position.
|
||||
* Use with `stack_base_out` value from previous
|
||||
* lzw_decode_init() call.
|
||||
* There are `stack_pos_out - stack_base_out`
|
||||
* current stack entries.
|
||||
* \return LZW_OK on success, or appropriate error code otherwise.
|
||||
*/
|
||||
lzw_result lzw_decode(
|
||||
struct lzw_ctx *ctx,
|
||||
const uint8_t ** const stack_pos_out);
|
||||
|
||||
|
||||
#endif
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003 James Bursa <bursa@users.sourceforge.net>
|
||||
* Copyright 2004 John Tytgat <John.Tytgat@aaug.net>
|
||||
*
|
||||
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
||||
* Licenced under the MIT License,
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef _LIBNSGIF_LOG_H_
|
||||
#define _LIBNSGIF_LOG_H_
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define LOG(x) ((void) 0)
|
||||
#else
|
||||
# define LOG(x) do { fprintf(stderr, x), fputc('\n', stderr); } while (0)
|
||||
#endif /* NDEBUG */
|
||||
|
||||
#endif /* _LIBNSGIF_LOG_H_ */
|
@ -1,11 +1,12 @@
|
||||
#include <cstring>
|
||||
#include <psp2/kernel/clib.h>
|
||||
#include <memory>
|
||||
|
||||
// BMP
|
||||
#include "libnsbmp.h"
|
||||
|
||||
// GIF
|
||||
#include "libnsgif.h"
|
||||
#include "gif_lib.h"
|
||||
|
||||
// JPEG
|
||||
#include "turbojpeg.h"
|
||||
@ -82,40 +83,6 @@ namespace BMP {
|
||||
}
|
||||
}
|
||||
|
||||
namespace GIF {
|
||||
static void *bitmap_create(int width, int height) {
|
||||
/* ensure a stupidly large bitmap is not created */
|
||||
if ((static_cast<long long>(width) * static_cast<long long>(height)) > (MAX_IMAGE_BYTES/BYTES_PER_PIXEL))
|
||||
return nullptr;
|
||||
|
||||
return std::calloc(width * height, BYTES_PER_PIXEL);
|
||||
}
|
||||
|
||||
static void bitmap_set_opaque([[maybe_unused]] void *bitmap, [[maybe_unused]] bool opaque) {
|
||||
assert(bitmap);
|
||||
}
|
||||
|
||||
static bool bitmap_test_opaque([[maybe_unused]] void *bitmap) {
|
||||
assert(bitmap);
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned char *bitmap_get_buffer(void *bitmap) {
|
||||
assert(bitmap);
|
||||
return static_cast<unsigned char *>(bitmap);
|
||||
}
|
||||
|
||||
static void bitmap_destroy(void *bitmap) {
|
||||
assert(bitmap);
|
||||
std::free(bitmap);
|
||||
}
|
||||
|
||||
static void bitmap_modified([[maybe_unused]] void *bitmap) {
|
||||
assert(bitmap);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ICO {
|
||||
static void *bitmap_create(int width, int height, [[maybe_unused]] unsigned int state) {
|
||||
return std::calloc(width * height, BYTES_PER_PIXEL);
|
||||
@ -137,7 +104,7 @@ namespace ICO {
|
||||
}
|
||||
|
||||
namespace Textures {
|
||||
static bool LoadImage(unsigned char *data, GLint format, Tex &texture, void (*free_func)(void *)) {
|
||||
static bool Create(unsigned char *data, GLint format, Tex &texture, void (*free_func)(void *)) {
|
||||
// Create a OpenGL texture identifier
|
||||
glGenTextures(1, &texture.id);
|
||||
glBindTexture(GL_TEXTURE_2D, texture.id);
|
||||
@ -158,7 +125,7 @@ namespace Textures {
|
||||
static bool LoadImageOther(unsigned char **data, SceOff &size, Tex &texture) {
|
||||
unsigned char *image = nullptr;
|
||||
image = stbi_load_from_memory(*data, size, &texture.width, &texture.height, nullptr, BYTES_PER_PIXEL);
|
||||
bool ret = Textures::LoadImage(image, GL_RGBA, texture, stbi_image_free);
|
||||
bool ret = Textures::Create(image, GL_RGBA, texture, stbi_image_free);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -199,67 +166,106 @@ namespace Textures {
|
||||
|
||||
texture.width = bmp.width;
|
||||
texture.height = bmp.height;
|
||||
bool ret = Textures::LoadImage(static_cast<unsigned char *>(bmp.bitmap), GL_RGBA, texture, nullptr);
|
||||
bool ret = Textures::Create(static_cast<unsigned char *>(bmp.bitmap), GL_RGBA, texture, nullptr);
|
||||
bmp_finalise(&bmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool LoadImageGIF(unsigned char **data, SceOff size, std::vector<Tex> &textures) {
|
||||
gif_bitmap_callback_vt bitmap_callbacks = {
|
||||
GIF::bitmap_create,
|
||||
GIF::bitmap_destroy,
|
||||
GIF::bitmap_get_buffer,
|
||||
GIF::bitmap_set_opaque,
|
||||
GIF::bitmap_test_opaque,
|
||||
GIF::bitmap_modified
|
||||
};
|
||||
|
||||
static bool LoadImageGIF(const std::string &path, std::vector<Tex> &textures) {
|
||||
bool ret = false;
|
||||
gif_animation gif;
|
||||
gif_result code = GIF_OK;
|
||||
gif_create(&gif, &bitmap_callbacks);
|
||||
|
||||
do {
|
||||
code = gif_initialise(&gif, size, *data);
|
||||
if (code != GIF_OK && code != GIF_WORKING) {
|
||||
Log::Error("gif_initialise failed: %d\n", code);
|
||||
gif_finalise(&gif);
|
||||
return ret;
|
||||
}
|
||||
} while (code != GIF_OK);
|
||||
int error = 0;
|
||||
GifFileType *gif = DGifOpenFileName(path.c_str(), &error);
|
||||
|
||||
if (!gif) {
|
||||
Log::Error("DGifOpenFileName failed: %d\n", error);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (DGifSlurp(gif) != GIF_OK) {
|
||||
Log::Error("DGifSlurp failed: %d\n", gif->Error);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (gif->ImageCount <= 0) {
|
||||
Log::Error("Gif does not contain any images.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool gif_is_animated = gif.frame_count > 1;
|
||||
|
||||
if (gif_is_animated) {
|
||||
textures.resize(gif.frame_count);
|
||||
|
||||
for (unsigned int i = 0; i < gif.frame_count; i++) {
|
||||
code = gif_decode_frame(&gif, i);
|
||||
if (code != GIF_OK) {
|
||||
Log::Error("gif_decode_frame failed: %d\n", code);
|
||||
return false;
|
||||
textures.resize(gif->ImageCount);
|
||||
|
||||
// seiken's example code from:
|
||||
// https://forums.somethingawful.com/showthread.php?threadid=2773485&userid=0&perpage=40&pagenumber=487#post465199820
|
||||
int width = gif->SWidth;
|
||||
int height = gif->SHeight;
|
||||
std::unique_ptr<uint32_t[]> pixels(new uint32_t[width * height]);
|
||||
|
||||
for (int i = 0; i < width * height; ++i)
|
||||
pixels[i] = gif->SBackGroundColor;
|
||||
|
||||
for (int i = 0; i < gif->ImageCount; ++i) {
|
||||
const SavedImage &frame = gif->SavedImages[i];
|
||||
bool transparency = false;
|
||||
unsigned char transparency_byte = 0;
|
||||
|
||||
// Delay time in hundredths of a second.
|
||||
int delay_time = 1;
|
||||
for (int j = 0; j < frame.ExtensionBlockCount; ++j) {
|
||||
const ExtensionBlock &block = frame.ExtensionBlocks[j];
|
||||
|
||||
if (block.Function != GRAPHICS_EXT_FUNC_CODE)
|
||||
continue;
|
||||
|
||||
// Here's the metadata for this frame.
|
||||
char dispose = (block.Bytes[0] >> 2) & 7;
|
||||
transparency = block.Bytes[0] & 1;
|
||||
delay_time = block.Bytes[1] + (block.Bytes[2] << 8);
|
||||
transparency_byte = block.Bytes[3];
|
||||
|
||||
if (dispose == 2) {
|
||||
// Clear the canvas.
|
||||
for (int k = 0; k < width * height; ++k)
|
||||
pixels[k] = gif->SBackGroundColor;
|
||||
}
|
||||
|
||||
textures[i].width = gif.width;
|
||||
textures[i].height = gif.height;
|
||||
textures[i].delay = gif.frames->frame_delay * 10000;
|
||||
ret = Textures::LoadImage(static_cast<unsigned char *>(gif.frame_image), GL_RGBA, textures[i], nullptr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
code = gif_decode_frame(&gif, 0);
|
||||
if (code != GIF_OK) {
|
||||
Log::Error("gif_decode_frame failed: %d\n", code);
|
||||
return false;
|
||||
}
|
||||
|
||||
textures[0].width = gif.width;
|
||||
textures[0].height = gif.height;
|
||||
ret = Textures::LoadImage(static_cast<unsigned char *>(gif.frame_image), GL_RGBA, textures[0], nullptr);
|
||||
// Colour map for this frame.
|
||||
ColorMapObject *map = frame.ImageDesc.ColorMap ? frame.ImageDesc.ColorMap : gif->SColorMap;
|
||||
|
||||
// Region this frame draws to.
|
||||
int fw = frame.ImageDesc.Width;
|
||||
int fh = frame.ImageDesc.Height;
|
||||
int fl = frame.ImageDesc.Left;
|
||||
int ft = frame.ImageDesc.Top;
|
||||
|
||||
for (int y = 0; y < std::min(height, fh); ++y) {
|
||||
for (int x = 0; x < std::min(width, fw); ++x) {
|
||||
unsigned char byte = frame.RasterBits[x + y * fw];
|
||||
|
||||
// Transparent pixel.
|
||||
if (transparency && byte == transparency_byte)
|
||||
continue;
|
||||
|
||||
// Draw to canvas.
|
||||
const GifColorType &c = map->Colors[byte];
|
||||
pixels[fl + x + (ft + y) * width] = c.Red | (c.Green << 8) | (c.Blue << 16) | (0xff << 24);
|
||||
}
|
||||
}
|
||||
|
||||
textures[i].width = width;
|
||||
textures[i].height = height;
|
||||
textures[i].delay = delay_time * 10000;
|
||||
|
||||
// Here's the actual frame, pixels.get() is now a pointer to the 32-bit RGBA
|
||||
// data for this frame you might expect.
|
||||
ret = Textures::Create(reinterpret_cast<unsigned char *>(pixels.get()), GL_RGBA, textures[i], nullptr);
|
||||
}
|
||||
|
||||
if (DGifCloseFile(gif, &error) != GIF_OK) {
|
||||
Log::Error("DGifCloseFile failed: %d\n", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
gif_finalise(&gif);
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool LoadImageICO(unsigned char **data, SceOff &size, Tex &texture) {
|
||||
@ -306,7 +312,7 @@ namespace Textures {
|
||||
|
||||
texture.width = bmp->width;
|
||||
texture.height = bmp->height;
|
||||
bool ret = Textures::LoadImage(static_cast<unsigned char *>(bmp->bitmap), GL_RGBA, texture, nullptr);
|
||||
bool ret = Textures::Create(static_cast<unsigned char *>(bmp->bitmap), GL_RGBA, texture, nullptr);
|
||||
ico_finalise(&ico);
|
||||
return ret;
|
||||
}
|
||||
@ -330,7 +336,7 @@ namespace Textures {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = Textures::LoadImage(buffer, GL_RGB, texture, nullptr);
|
||||
bool ret = Textures::Create(buffer, GL_RGB, texture, nullptr);
|
||||
tjDestroy(jpeg);
|
||||
delete[] buffer;
|
||||
return ret;
|
||||
@ -338,7 +344,7 @@ namespace Textures {
|
||||
|
||||
static bool LoadImagePCX(unsigned char **data, SceOff &size, Tex &texture) {
|
||||
*data = drpcx_load_memory(*data, size, DRPCX_FALSE, &texture.width, &texture.height, nullptr, BYTES_PER_PIXEL);
|
||||
bool ret = Textures::LoadImage(*data, GL_RGBA, texture, nullptr);
|
||||
bool ret = Textures::Create(*data, GL_RGBA, texture, nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -356,7 +362,7 @@ namespace Textures {
|
||||
if (buffer != nullptr && png_image_finish_read(&image, nullptr, buffer, 0, nullptr) != 0) {
|
||||
texture.width = image.width;
|
||||
texture.height = image.height;
|
||||
ret = Textures::LoadImage(buffer, GL_RGBA, texture, nullptr);
|
||||
ret = Textures::Create(buffer, GL_RGBA, texture, nullptr);
|
||||
delete[] buffer;
|
||||
png_image_free(&image);
|
||||
}
|
||||
@ -388,7 +394,7 @@ namespace Textures {
|
||||
unsigned char *image = new unsigned char[texture.width * texture.height * 4];
|
||||
nsvgRasterize(rasterizer, svg, 0, 0, 1, image, texture.width, texture.height, texture.width * 4);
|
||||
|
||||
bool ret = Textures::LoadImage(image, GL_RGBA, texture, nullptr);
|
||||
bool ret = Textures::Create(image, GL_RGBA, texture, nullptr);
|
||||
|
||||
delete[] image;
|
||||
nsvgDelete(svg);
|
||||
@ -409,7 +415,7 @@ namespace Textures {
|
||||
raster = static_cast<uint32 *>(_TIFFmalloc(num_pixels * sizeof(uint32)));
|
||||
if (raster != nullptr) {
|
||||
if (TIFFReadRGBAImageOriented(tif, texture.width, texture.height, raster, ORIENTATION_TOPLEFT))
|
||||
LoadImage(reinterpret_cast<unsigned char *>(raster), GL_RGBA, texture, _TIFFfree);
|
||||
Textures::Create(reinterpret_cast<unsigned char *>(raster), GL_RGBA, texture, _TIFFfree);
|
||||
else
|
||||
Log::Error("TIFFReadRGBAImage failed\n");
|
||||
|
||||
@ -468,14 +474,14 @@ namespace Textures {
|
||||
textures[frame_index].width = info.canvas_width;
|
||||
textures[frame_index].height = info.canvas_height;
|
||||
textures[frame_index].delay = (timestamp - prev_timestamp) * 1000;
|
||||
ret = Textures::LoadImage(frame_rgba, GL_RGBA, textures[frame_index], nullptr);
|
||||
ret = Textures::Create(frame_rgba, GL_RGBA, textures[frame_index], nullptr);
|
||||
++frame_index;
|
||||
prev_timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*data = WebPDecodeRGBA(*data, size, &textures[0].width, &textures[0].height);
|
||||
ret = Textures::LoadImage(*data, GL_RGBA, textures[0], nullptr);
|
||||
ret = Textures::Create(*data, GL_RGBA, textures[0], nullptr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -495,6 +501,8 @@ namespace Textures {
|
||||
// Because TIFF does not load via buffer, but directly from the path.
|
||||
if (ext == ".TIFF")
|
||||
ret = Textures::LoadImageTIFF(path, textures[0]);
|
||||
else if (ext == ".GIF")
|
||||
ret = Textures::LoadImageGIF(path, textures);
|
||||
else {
|
||||
unsigned char *data = nullptr;
|
||||
SceOff size = 0;
|
||||
@ -504,8 +512,6 @@ namespace Textures {
|
||||
ret = Textures::LoadImageBMP(&data, size, textures[0]);
|
||||
else if ((ext == ".PGM") || (ext == ".PPM") || (ext == ".PSD") || (ext == ".TGA"))
|
||||
ret = Textures::LoadImageOther(&data, size, textures[0]);
|
||||
else if (ext == ".GIF")
|
||||
ret = Textures::LoadImageGIF(&data, size, textures);
|
||||
else if (ext == ".ICO")
|
||||
ret = Textures::LoadImageICO(&data, size, textures[0]);
|
||||
else if ((ext == ".JPG") || (ext == ".JPEG"))
|
||||
|
Loading…
x
Reference in New Issue
Block a user