textures: Remove libnsgif and use gif_lib for loading gifs

This commit is contained in:
Joel16 2022-08-01 14:48:39 -04:00
parent 0117ae3ac6
commit 05eb58cf48
13 changed files with 196 additions and 2012 deletions

View File

@ -39,9 +39,9 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := libs/imgui libs/imgui/misc/freetype libs/libnsbmp libs/libnsgif source source/imgui_nx source/popups source/tabs
SOURCES := libs/imgui libs/imgui/misc/freetype libs/libnsbmp source source/imgui_nx source/popups source/tabs
DATA := data
INCLUDES := include include/imgui_nx libs/imgui libs/imgui/misc/freetype libs/libnsbmp libs/libnsgif libs/
INCLUDES := include include/imgui_nx libs/imgui libs/imgui/misc/freetype libs/libnsbmp libs/
ROMFS := res
# Output folders for autogenerated files in romfs
@ -75,7 +75,7 @@ CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := `curl-config --libs` `freetype-config --libs` -lturbojpeg -ljpeg -lpng -lwebp -ljansson \
LIBS := `curl-config --libs` `freetype-config --libs` -lgif -lturbojpeg -ljpeg -lpng -lwebp -ljansson \
-lglad -lEGL -lglapi -ldrm_nouveau -lnx -lm -lz
#---------------------------------------------------------------------------------

View File

@ -21,4 +21,3 @@ NX Shell is a multi-purpose file manager for the Nintendo Switch that aims towar
- **Preetisketch** for the banner.
- **Dear ImGui developers and contributors** for the GUI.
- **devkitPro maintainers and contributors** for libnx, devkitA64, and many other packages used by this project.
- **mtheall/averne** for imgui::deko3d and imgui::nx used by this project.

View File

@ -3,7 +3,7 @@
#include <switch.h>
#include <vector>
#include "imgui.h"
#include "textures.hpp"
enum WINDOW_STATES {
WINDOW_STATE_FILEBROWSER = 0,
@ -37,6 +37,9 @@ typedef struct {
WindowCheckboxData checkbox_data;
s64 used_storage = 0;
s64 total_storage = 0;
std::vector<Tex> textures;
long unsigned int frame_count = 0;
float zoom_factor = 1.0f;
} WindowData;
extern WindowData data;
@ -47,6 +50,9 @@ namespace FileBrowser {
}
namespace Windows {
void SetupWindow(void);
void ExitWindow(void);
void ResetCheckbox(WindowData &data);
void MainWindow(WindowData &data, u64 &key, bool progress);
void ImageViewer(void);
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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_ */

37
source/image.cpp Normal file
View File

@ -0,0 +1,37 @@
#include "config.hpp"
#include "gui.hpp"
#include "imgui.h"
#include "windows.hpp"
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_internal.h"
namespace Windows {
void ImageViewer(void) {
Windows::SetupWindow();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGuiWindowFlags_ filename_flag = !cfg.image_filename? ImGuiWindowFlags_NoTitleBar : ImGuiWindowFlags_None;
if (ImGui::Begin(data.entries[data.selected].name, nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | filename_flag)) {
if (((data.textures[0].width * data.zoom_factor) <= 1280) && ((data.textures[0].height * data.zoom_factor) <= 720))
ImGui::SetCursorPos((ImGui::GetWindowSize() - ImVec2((data.textures[0].width * data.zoom_factor), (data.textures[0].height * data.zoom_factor))) * 0.5f);
if (data.textures.size() > 1) {
svcSleepThread(data.textures[data.frame_count].delay);
ImGui::Image(reinterpret_cast<ImTextureID>(data.textures[data.frame_count].id), (ImVec2((data.textures[data.frame_count].width * data.zoom_factor),
(data.textures[data.frame_count].height * data.zoom_factor))));
data.frame_count++;
// Reset frame counter
if (data.frame_count == data.textures.size() - 1)
data.frame_count = 0;
}
else
ImGui::Image(reinterpret_cast<ImTextureID>(data.textures[0].id), ImVec2((data.textures[0].width * data.zoom_factor), (data.textures[0].height * data.zoom_factor)));
}
Windows::ExitWindow();
ImGui::PopStyleVar();
}
}

View File

@ -173,7 +173,19 @@ namespace Tabs {
sorts_specs->SpecsDirty = true;
}
else {
char path[FS_MAX_PATH + 1];
switch (file_type) {
case FileTypeImage:
if ((std::snprintf(path, FS_MAX_PATH, "%s/%s", cfg.cwd, data.entries[i].name)) > 0) {
Textures::LoadImageFile(path, data.textures);
data.state = WINDOW_STATE_IMAGEVIEWER;
}
break;
default:
break;
}
}
}

View File

@ -1,11 +1,12 @@
#include <cstring>
#include <string>
#include <memory>
// BMP
#include <libnsbmp.h>
#include "libnsbmp.h"
// BMP
#include "libnsgif.h"
// GIF
#include <gif_lib.h>
// JPEG
#include <turbojpeg.h>
@ -21,7 +22,7 @@
#define STBI_ONLY_PNM
#define STBI_ONLY_PSD
#define STBI_ONLY_TGA
#include <stb_image.h>
#include "stb_image.h"
// PNG
#include <png.h>
@ -66,40 +67,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 Textures {
typedef enum ImageType {
ImageTypeBMP,
@ -144,7 +111,7 @@ namespace Textures {
return 0;
}
static bool LoadImage(unsigned char *data, GLint format, Tex &texture) {
static bool Create(unsigned char *data, GLint format, Tex &texture) {
glGenTextures(1, &texture.id);
glBindTexture(GL_TEXTURE_2D, texture.id);
@ -175,7 +142,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);
ret = Textures::Create(buffer, GL_RGBA, texture);
delete[] buffer;
png_image_free(&image);
}
@ -224,67 +191,106 @@ namespace Textures {
texture.width = bmp.width;
texture.height = bmp.height;
bool ret = LoadImage(static_cast<unsigned char *>(bmp.bitmap), GL_RGBA, texture);
bool ret = Create(static_cast<unsigned char *>(bmp.bitmap), GL_RGBA, texture);
bmp_finalise(&bmp);
return ret;
}
static bool LoadImageGIF(unsigned char **data, s64 &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);
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;
}
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);
textures.resize(gif->ImageCount);
bool gif_is_animated = gif.frame_count > 1;
// seiken's example code from:
// https://forums.somethingawful.com/showthread.php?threadid=2773485&userid=0&perpage=40&pagenumber=487#post465199820
textures[0].width = gif->SWidth;
textures[0].height = gif->SHeight;
std::unique_ptr<u32[]> pixels(new u32[textures[0].width * textures[0].height]);
if (gif_is_animated) {
textures.resize(gif.frame_count);
for (int i = 0; i < textures[0].width * textures[0].height; ++i)
pixels[i] = gif->SBackGroundColor;
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;
}
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];
textures[i].width = gif.width;
textures[i].height = gif.height;
textures[i].delay = gif.frames->frame_delay;
ret = Textures::LoadImage(static_cast<unsigned char *>(gif.frame_image), GL_RGBA, textures[i]);
}
}
else {
code = gif_decode_frame(&gif, 0);
if (code != GIF_OK) {
Log::Error("gif_decode_frame failed: %d\n", code);
return false;
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 < textures[0].width * textures[0].height; ++k)
pixels[k] = gif->SBackGroundColor;
}
}
textures[0].width = gif.width;
textures[0].height = gif.height;
ret = Textures::LoadImage(static_cast<unsigned char *>(gif.frame_image), GL_RGBA, textures[0]);
// 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(textures[0].height, fh); ++y) {
for (int x = 0; x < std::min(textures[0].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) * textures[0].width] = c.Red | (c.Green << 8) | (c.Blue << 16) | (0xff << 24);
}
}
textures[i].delay = delay_time * 10000000;
textures[i].width = textures[0].width;
textures[i].height = textures[0].height;
// 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]);
}
gif_finalise(&gif);
return ret;
if (DGifCloseFile(gif, &error) != GIF_OK) {
Log::Error("DGifCloseFile failed: %d\n", error);
return false;
}
return true;
}
static bool LoadImageJPEG(unsigned char **data, s64 &size, Tex &texture) {
@ -293,7 +299,7 @@ namespace Textures {
tjDecompressHeader2(jpeg, *data, size, &texture.width, &texture.height, &jpegsubsamp);
unsigned char *buffer = new unsigned char[texture.width * texture.height * 3];
tjDecompress2(jpeg, *data, size, buffer, texture.width, 0, texture.height, TJPF_RGB, TJFLAG_FASTDCT);
bool ret = LoadImage(buffer, GL_RGB, texture);
bool ret = Create(buffer, GL_RGB, texture);
tjDestroy(jpeg);
delete[] buffer;
return ret;
@ -301,7 +307,7 @@ namespace Textures {
static bool LoadImageOther(unsigned char **data, s64 &size, Tex &texture) {
unsigned char *image = stbi_load_from_memory(*data, size, &texture.width, &texture.height, nullptr, STBI_rgb_alpha);
bool ret = Textures::LoadImage(image, GL_RGBA, texture);
bool ret = Textures::Create(image, GL_RGBA, texture);
return ret;
}
@ -319,7 +325,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);
ret = Textures::Create(buffer, GL_RGBA, texture);
delete[] buffer;
png_image_free(&image);
}
@ -336,7 +342,7 @@ namespace Textures {
static bool LoadImageWEBP(unsigned char **data, s64 &size, Tex &texture) {
*data = WebPDecodeRGBA(*data, size, &texture.width, &texture.height);
bool ret = Textures::LoadImage(*data, GL_RGBA, texture);
bool ret = Textures::Create(*data, GL_RGBA, texture);
return ret;
}
@ -359,45 +365,47 @@ namespace Textures {
bool LoadImageFile(const char path[FS_MAX_PATH], std::vector<Tex> &textures) {
bool ret = false;
unsigned char *data = nullptr;
s64 size = 0;
if (R_FAILED(Textures::ReadFile(path, &data, size))) {
delete[] data;
return ret;
}
// Resize to 1 initially. If the file is a GIF it will be resized accordingly.
textures.resize(1);
ImageType type = GetImageType(path);
switch(type) {
case ImageTypeBMP:
ret = Textures::LoadImageBMP(&data, size, textures[0]);
break;
ImageType type = Textures::GetImageType(path);
case ImageTypeGIF:
ret = Textures::LoadImageGIF(&data, size, textures);
break;
if (type == ImageTypeGIF)
ret = Textures::LoadImageGIF(path, textures);
else {
unsigned char *data = nullptr;
s64 size = 0;
case ImageTypeJPEG:
ret = Textures::LoadImageJPEG(&data, size, textures[0]);
break;
if (R_FAILED(Textures::ReadFile(path, &data, size))) {
delete[] data;
return ret;
}
case ImageTypePNG:
ret = Textures::LoadImagePNG(&data, size, textures[0]);
break;
switch(type) {
case ImageTypeBMP:
ret = Textures::LoadImageBMP(&data, size, textures[0]);
break;
case ImageTypeJPEG:
ret = Textures::LoadImageJPEG(&data, size, textures[0]);
break;
case ImageTypePNG:
ret = Textures::LoadImagePNG(&data, size, textures[0]);
break;
case ImageTypeWEBP:
ret = Textures::LoadImageWEBP(&data, size, textures[0]);
break;
default:
ret = Textures::LoadImageOther(&data, size, textures[0]);
break;
}
case ImageTypeWEBP:
ret = Textures::LoadImageWEBP(&data, size, textures[0]);
break;
default:
ret = Textures::LoadImageOther(&data, size, textures[0]);
break;
delete[] data;
}
delete[] data;
return ret;
}

View File

@ -8,13 +8,13 @@
#include "windows.hpp"
namespace Windows {
static void SetupWindow(void) {
void SetupWindow(void) {
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiCond_Once);
ImGui::SetNextWindowSize(ImVec2(1280.0f, 720.0f), ImGuiCond_Once);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
};
static void ExitWindow(void) {
void ExitWindow(void) {
ImGui::End();
ImGui::PopStyleVar();
};
@ -55,6 +55,10 @@ namespace Windows {
Popups::DeletePopup(data);
break;
case WINDOW_STATE_IMAGEVIEWER:
Windows::ImageViewer();
break;
default:
break;
}
@ -76,6 +80,7 @@ namespace Windows {
if (key & HidNpadButton_B) {
switch(data.state) {
case WINDOW_STATE_OPTIONS:
case WINDOW_STATE_IMAGEVIEWER:
data.state = WINDOW_STATE_FILEBROWSER;
break;