textures: Add support for bmp/gif decoding

This commit is contained in:
joel16 2021-02-06 23:34:28 -05:00
parent 6d387ecd4e
commit 54bfadd216
17 changed files with 3822 additions and 29 deletions

View File

@ -33,9 +33,9 @@ include $(DEVKITARM)/3ds_rules
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source source/gui
SOURCES := source source/gui libs/libnsbmp/src libs/libnsgif/src
DATA := data
INCLUDES := include
INCLUDES := include libs/libnsbmp/include libs/libnsgif/include
GRAPHICS := res/drawable
ROMFS := romfs
GFXBUILD := $(ROMFS)/res/drawable

View File

@ -25,9 +25,9 @@ typedef struct {
std::vector<bool> checked_copy;
std::string checked_cwd;
int checked_count = 0;
s64 used_storage = 0;
s64 total_storage = 0;
std::vector<C2D_Image> textures;
u64 used_storage = 0;
u64 total_storage = 0;
C2D_Image texture;
} MenuItem;
namespace GUI {

View File

@ -19,7 +19,7 @@ extern C2D_Image file_icons[NUM_ICONS], icon_dir, icon_dir_dark, wifi_icons[4],
namespace Textures {
void Init(void);
void Exit(void);
bool LoadImageFile(const std::string &path, std::vector<C2D_Image> &textures);
bool LoadImageFile(const std::string &path, C2D_Image *texture);
}
#endif

20
libs/libnsbmp/COPYING Normal file
View File

@ -0,0 +1,20 @@
Copyright (C) 2006 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.

View File

@ -0,0 +1,258 @@
/*
* Copyright 2006 Richard Wilson <richard.wilson@netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
*
* This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*/
/**
* \file
* Bitmap file decoding interface.
*/
#ifndef libnsbmp_h_
#define libnsbmp_h_
#if defined (__cplusplus)
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
/* bmp flags */
#define BMP_NEW 0
/** image is opaque (as opposed to having an alpha mask) */
#define BMP_OPAQUE (1 << 0)
/** memory should be wiped */
#define BMP_CLEAR_MEMORY (1 << 1)
/**
* error return values
*/
typedef enum {
BMP_OK = 0,
BMP_INSUFFICIENT_MEMORY = 1,
BMP_INSUFFICIENT_DATA = 2,
BMP_DATA_ERROR = 3
} bmp_result;
/**
* encoding types
*/
typedef enum {
BMP_ENCODING_RGB = 0,
BMP_ENCODING_RLE8 = 1,
BMP_ENCODING_RLE4 = 2,
BMP_ENCODING_BITFIELDS = 3
} bmp_encoding;
/* API for Bitmap callbacks */
typedef void* (*bmp_bitmap_cb_create)(int width, int height, unsigned int state);
typedef void (*bmp_bitmap_cb_destroy)(void *bitmap);
typedef unsigned char* (*bmp_bitmap_cb_get_buffer)(void *bitmap);
typedef size_t (*bmp_bitmap_cb_get_bpp)(void *bitmap);
/**
* The Bitmap callbacks function table
*/
typedef struct bmp_bitmap_callback_vt_s {
/** Callback to allocate bitmap storage. */
bmp_bitmap_cb_create bitmap_create;
/** Called to free bitmap storage. */
bmp_bitmap_cb_destroy bitmap_destroy;
/** Return a pointer to the pixel data in a bitmap. */
bmp_bitmap_cb_get_buffer bitmap_get_buffer;
/** Find the width of a pixel row in bytes. */
bmp_bitmap_cb_get_bpp bitmap_get_bpp;
} bmp_bitmap_callback_vt;
/**
* bitmap image
*/
typedef struct bmp_image {
/** callbacks for bitmap functions */
bmp_bitmap_callback_vt bitmap_callbacks;
/** pointer to BMP data */
uint8_t *bmp_data;
/** width of BMP (valid after _analyse) */
uint32_t width;
/** heigth of BMP (valid after _analyse) */
uint32_t height;
/** whether the image has been decoded */
bool decoded;
/** decoded image */
void *bitmap;
/* Internal members are listed below */
/** total number of bytes of BMP data available */
uint32_t buffer_size;
/** pixel encoding type */
bmp_encoding encoding;
/** offset of bitmap data */
uint32_t bitmap_offset;
/** bits per pixel */
uint16_t bpp;
/** number of colours */
uint32_t colours;
/** colour table */
uint32_t *colour_table;
/** whether to use bmp's limited transparency */
bool limited_trans;
/** colour to display for "transparent" pixels when using limited
* transparency
*/
uint32_t trans_colour;
/** scanlines are top to bottom */
bool reversed;
/** image is part of an ICO, mask follows */
bool ico;
/** true if the bitmap does not contain an alpha channel */
bool opaque;
/** four bitwise mask */
uint32_t mask[4];
/** four bitwise shifts */
int32_t shift[4];
/** colour representing "transparency" in the bitmap */
uint32_t transparent_index;
} bmp_image;
typedef struct ico_image {
bmp_image bmp;
struct ico_image *next;
} ico_image;
/**
* icon image collection
*/
typedef struct ico_collection {
/** callbacks for bitmap functions */
bmp_bitmap_callback_vt bitmap_callbacks;
/** width of largest BMP */
uint16_t width;
/** heigth of largest BMP */
uint16_t height;
/* Internal members are listed below */
/** pointer to ICO data */
uint8_t *ico_data;
/** total number of bytes of ICO data available */
uint32_t buffer_size;
/** root of linked list of images */
ico_image *first;
} ico_collection;
/**
* Initialises bitmap ready for analysing the bitmap.
*
* \param bmp The Bitmap to initialise
* \param callbacks The callbacks the library will call on operations.
* \return BMP_OK on success or appropriate error code.
*/
bmp_result bmp_create(bmp_image *bmp, bmp_bitmap_callback_vt *callbacks);
/**
* Initialises icon ready for analysing the icon
*
* \param bmp The Bitmap to initialise
* \param callbacks The callbacks the library will call on operations.
* \return BMP_OK on success or appropriate error code.
*/
bmp_result ico_collection_create(ico_collection *ico,
bmp_bitmap_callback_vt *callbacks);
/**
* Analyse a BMP prior to decoding.
*
* This will scan the data provided and perform checks to ensure the data is a
* valid BMP and prepare the bitmap image structure ready for decode.
*
* This function must be called and resturn BMP_OK before bmp_decode() as it
* prepares the bmp internal state for the decode process.
*
* \param bmp the BMP image to analyse.
* \param size The size of data in cdata.
* \param data The bitmap source data.
* \return BMP_OK on success or error code on faliure.
*/
bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data);
/**
* Analyse an ICO prior to decoding.
*
* This function will scan the data provided and perform checks to ensure the
* data is a valid ICO.
*
* This function must be called before ico_find().
*
* \param ico the ICO image to analyse
* \param size The size of data in cdata.
* \param data The bitmap source data.
* \return BMP_OK on success
*/
bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data);
/**
* Decode a BMP
*
* This function decodes the BMP data such that bmp->bitmap is a valid
* image. The state of bmp->decoded is set to TRUE on exit such that it
* can easily be identified which BMPs are in a fully decoded state.
*
* \param bmp the BMP image to decode
* \return BMP_OK on success
*/
bmp_result bmp_decode(bmp_image *bmp);
/**
* Decode a BMP using "limited transparency"
*
* Bitmaps do not have native transparency support. However, there is a
* "trick" that is used in some instances in which the first pixel of the
* bitmap becomes the "transparency index". The decoding application can
* replace this index with whatever background colour it chooses to
* create the illusion of transparency.
*
* When to use transparency is at the discretion of the decoding
* application.
*
* \param bmp the BMP image to decode
* \param colour the colour to use as "transparent"
* \return BMP_OK on success
*/
bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t transparent_colour);
/**
* Finds the closest BMP within an ICO collection
*
* This function finds the BMP with dimensions as close to a specified set
* as possible from the images in the collection.
*
* \param ico the ICO collection to examine
* \param width the preferred width (0 to use ICO header width)
* \param height the preferred height (0 to use ICO header height)
*/
bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height);
/**
* Finalise a BMP prior to destruction.
*
* \param bmp the BMP image to finalise.
*/
void bmp_finalise(bmp_image *bmp);
/**
* Finalise an ICO prior to destruction.
*
* \param ico the ICO image to finalise,
*/
void ico_finalise(ico_collection *ico);
#if defined (__cplusplus)
}
#endif
#endif

1388
libs/libnsbmp/src/libnsbmp.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
/*
* 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 _LIBNSBMP_LOG_H_
#define _LIBNSBMP_LOG_H_
#ifdef NDEBUG
# define LOG(x) ((void) 0)
#else
# ifdef __GNUC__
# define LOG(x) do { printf x, fputc('\n', stdout)); } while (0)
# elif defined(__CC_NORCROFT)
# define LOG(x) do { printf x, fputc('\n', stdout)); } while (0)
# else
# define LOG(x) do { printf x, fputc('\n', stdout)); } while (0)
# endif
#endif
#endif

20
libs/libnsgif/COPYING Normal file
View File

@ -0,0 +1,20 @@
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.

View File

@ -0,0 +1,200 @@
/*
* 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;
/** previous frame for GIF_FRAME_RESTORE */
void *prev_frame;
/** previous frame index */
int prev_index;
/** previous frame width */
unsigned prev_width;
/** previous frame height */
unsigned prev_height;
} 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

View File

@ -0,0 +1,4 @@
# Sources
DIR_SOURCES := libnsgif.c lzw.c
include $(NSBUILD)/Makefile.subdir

1228
libs/libnsgif/src/libnsgif.c Normal file

File diff suppressed because it is too large Load Diff

377
libs/libnsgif/src/lzw.c Normal file
View File

@ -0,0 +1,377 @@
/*
* 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;
}

105
libs/libnsgif/src/lzw.h Normal file
View File

@ -0,0 +1,105 @@
/*
* 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

View File

@ -0,0 +1,21 @@
/*
* 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_ */

View File

@ -107,7 +107,7 @@ namespace GUI {
switch(file_type) {
case FileTypeImage:
if (Textures::LoadImageFile(path, item->textures))
if (Textures::LoadImageFile(path, &item->texture))
item->state = MENU_STATE_IMAGEVIEWER;
break;

View File

@ -7,16 +7,13 @@
namespace GUI {
void DisplayImageViewer(MenuItem *item) {
C2D_TargetClear(top_screen, BLACK_BG);
C2D::Image(item->textures[0], ((400 - item->textures[0].subtex->width) / 2), ((240 - item->textures[0].subtex->height) / 2));
C2D::Image(item->texture, ((400 - item->texture.subtex->width) / 2), ((240 - item->texture.subtex->height) / 2));
}
void ControlImageViewer(MenuItem *item, u32 *kDown) {
if (*kDown & KEY_B) {
for (u32 i = 0; i < item->textures.size(); i++) {
delete[] item->textures[i].tex;
delete[] item->textures[i].subtex;
}
delete[] item->texture.tex;
delete[] item->texture.subtex;
item->textures.clear();
item->state = MENU_STATE_FILEBROWSER;
}

View File

@ -1,16 +1,25 @@
#include <cassert>
#include <cstring>
// PNG
#include <png.h>
// BMP
#include <libnsbmp.h>
// GIF
#include <libnsgif.h>
// JPEG
#include <turbojpeg.h>
// PNG
#include <png.h>
#include "fs.h"
#include "log.h"
#include "sprites.h"
#include "textures.h"
#define MAX_IMAGE_BYTES (48 * 1024 * 1024)
C2D_Image file_icons[NUM_ICONS], icon_dir, icon_dir_dark, wifi_icons[4], \
battery_icons[6], battery_icons_charging[6], icon_check, icon_uncheck, icon_check_dark, icon_uncheck_dark, \
icon_radio_off, icon_radio_on, icon_radio_dark_off, icon_radio_dark_on, icon_toggle_on, icon_toggle_dark_on, \
@ -20,6 +29,66 @@ C2D_Image file_icons[NUM_ICONS], icon_dir, icon_dir_dark, wifi_icons[4], \
icon_sd, icon_sd_dark, icon_sd_overlay, icon_secure, icon_secure_dark, icon_secure_overlay, icon_search, \
icon_nav_drawer, icon_actions, icon_back;
static const u32 BYTES_PER_PIXEL = 4;
namespace BMP {
static void *bitmap_create(int width, int height, [[maybe_unused]] unsigned int state) {
/* ensure a stupidly large (>50Megs or so) 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 unsigned char *bitmap_get_buffer(void *bitmap) {
assert(bitmap);
return static_cast<unsigned char *>(bitmap);
}
static size_t bitmap_get_bpp([[maybe_unused]] void *bitmap) {
return BYTES_PER_PIXEL;
}
static void bitmap_destroy(void *bitmap) {
assert(bitmap);
std::free(bitmap);
}
}
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 Textures {
typedef enum ImageType {
ImageTypeBMP,
@ -31,7 +100,6 @@ namespace Textures {
} ImageType;
static C2D_SpriteSheet spritesheet;
static const u32 BYTES_PER_PIXEL = 4;
static const u32 TRANSPARENT_COLOR = 0xFFFFFFFF;
static Result ReadFile(const std::string &path, u8 **buffer, u64 *size) {
@ -137,8 +205,92 @@ namespace Textures {
return false;
}
static bool LoadImageBMP(u8 **data, u64 *size, C2D_Image *texture) {
bmp_bitmap_callback_vt bitmap_callbacks = {
BMP::bitmap_create,
BMP::bitmap_destroy,
BMP::bitmap_get_buffer,
BMP::bitmap_get_bpp
};
bmp_result code = BMP_OK;
bmp_image bmp;
bmp_create(&bmp, &bitmap_callbacks);
code = bmp_analyse(&bmp, *size, *data);
if (code != BMP_OK) {
bmp_finalise(&bmp);
return false;
}
code = bmp_decode(&bmp);
if (code != BMP_OK) {
if ((code != BMP_INSUFFICIENT_DATA) && (code != BMP_DATA_ERROR)) {
bmp_finalise(&bmp);
return false;
}
/* skip if the decoded image would be ridiculously large */
if ((bmp.width * bmp.height) > 200000) {
bmp_finalise(&bmp);
return false;
}
}
bool LoadImagePNG(u8 **data, u64 *size, C2D_Image *texture) {
bool ret = Textures::C3DTexToC2DImage(texture, static_cast<u32>(bmp.width), static_cast<u32>(bmp.height), static_cast<u8 *>(bmp.bitmap));
bmp_finalise(&bmp);
return ret;
}
static bool LoadImageGIF(u8 **data, u64 *size, C2D_Image *texture) {
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
};
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);
code = gif_decode_frame(&gif, 0);
if (code != GIF_OK) {
Log::Error("gif_decode_frame failed: %d\n", code);
return false;
}
ret = Textures::C3DTexToC2DImage(texture, static_cast<u32>(gif.width), static_cast<u32>(gif.height), static_cast<u8 *>(gif.frame_image));
gif_finalise(&gif);
return ret;
}
static bool LoadImageJPEG(u8 **data, u64 *size, C2D_Image *texture) {
tjhandle jpeg = tjInitDecompress();
int width = 0, height = 0, jpegsubsamp = 0;
tjDecompressHeader2(jpeg, *data, *size, &width, &height, &jpegsubsamp);
u8 *buffer = new u8[width * height * BYTES_PER_PIXEL];
tjDecompress2(jpeg, *data, *size, buffer, width, 0, height, TJPF_RGBA, TJFLAG_FASTDCT);
bool ret = Textures::C3DTexToC2DImage(texture, static_cast<u32>(width), static_cast<u32>(height), buffer);
tjDestroy(jpeg);
delete[] buffer;
return ret;
}
static bool LoadImagePNG(u8 **data, u64 *size, C2D_Image *texture) {
bool ret = false;
png_image image;
std::memset(&image, 0, (sizeof image));
@ -165,18 +317,6 @@ namespace Textures {
return ret;
}
static bool LoadImageJPEG(u8 **data, u64 *size, C2D_Image *texture) {
tjhandle jpeg = tjInitDecompress();
int width = 0, height = 0, jpegsubsamp = 0;
tjDecompressHeader2(jpeg, *data, *size, &width, &height, &jpegsubsamp);
u8 *buffer = new u8[width * height * BYTES_PER_PIXEL];
tjDecompress2(jpeg, *data, *size, buffer, width, 0, height, TJPF_RGBA, TJFLAG_FASTDCT);
bool ret = Textures::C3DTexToC2DImage(texture, static_cast<u32>(width), static_cast<u32>(height), buffer);
tjDestroy(jpeg);
delete[] buffer;
return ret;
}
ImageType GetImageType(const std::string &filename) {
std::string ext = FS::GetFileExt(filename);
@ -209,6 +349,14 @@ namespace Textures {
ImageType type = GetImageType(path);
switch(type) {
case ImageTypeBMP:
ret = Textures::LoadImageBMP(&data, &size, &textures[0]);
break;
case ImageTypeGIF:
ret = Textures::LoadImageGIF(&data, &size, &textures[0]);
break;
case ImageTypeJPEG:
ret = Textures::LoadImageJPEG(&data, &size, &textures[0]);
break;