Added background image and ability to set it from the image viewer

This commit is contained in:
Mateusz Faderewski 2023-07-28 21:36:51 +02:00
parent 213ef7f367
commit 01ae2cef37
12 changed files with 299 additions and 17 deletions

View File

@ -29,6 +29,7 @@ SRCS = \
libs/miniz/miniz.c \ libs/miniz/miniz.c \
menu/actions.c \ menu/actions.c \
menu/assets.c \ menu/assets.c \
menu/components/background.c \
menu/menu.c \ menu/menu.c \
menu/mp3_player.c \ menu/mp3_player.c \
menu/path.c \ menu/path.c \

View File

@ -0,0 +1,202 @@
#include <fatfs/ff.h>
#include <stdlib.h>
#include "components.h"
#include "utils/fs.h"
#define CACHE_METADATA_MAGIC (0x424B4731)
typedef struct {
uint32_t magic;
uint32_t width;
uint32_t height;
uint32_t size;
} cache_metadata_t;
static void load_from_cache (component_background_t *c) {
if (!c->cache_location) {
return;
}
FIL fil;
UINT bytes_read;
if (f_open(&fil, strip_sd_prefix(c->cache_location), FA_READ) != FR_OK) {
return;
}
cache_metadata_t cache_metadata;
if ((f_read(&fil, &cache_metadata, sizeof(cache_metadata), &bytes_read) != FR_OK) || (bytes_read != sizeof(cache_metadata))) {
f_close(&fil);
return;
}
uint32_t display_width = display_get_width();
uint32_t display_height = display_get_height();
if (cache_metadata.magic != CACHE_METADATA_MAGIC || cache_metadata.width > display_width || cache_metadata.height > display_height) {
f_close(&fil);
return;
}
c->image = calloc(1, sizeof(surface_t));
*c->image = surface_alloc(FMT_RGBA16, cache_metadata.width, cache_metadata.height);
if (cache_metadata.size != (c->image->height * c->image->stride)) {
surface_free(c->image);
free(c->image);
c->image = NULL;
f_close(&fil);
return;
}
if ((f_read(&fil, c->image->buffer, cache_metadata.size, &bytes_read) != FR_OK) || (bytes_read != cache_metadata.size)) {
surface_free(c->image);
free(c->image);
c->image = NULL;
}
f_close(&fil);
}
static void save_to_cache (component_background_t *c) {
if (!c->cache_location || !c->image) {
return;
}
FIL fil;
UINT bytes_written;
if (f_open(&fil, strip_sd_prefix(c->cache_location), FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) {
return;
}
cache_metadata_t cache_metadata = {
.magic = CACHE_METADATA_MAGIC,
.width = c->image->width,
.height = c->image->height,
.size = (c->image->height * c->image->stride),
};
f_write(&fil, &cache_metadata, sizeof(cache_metadata), &bytes_written);
f_write(&fil, c->image->buffer, cache_metadata.size, &bytes_written);
f_close(&fil);
}
static void prepare_background (component_background_t *c) {
if (!c->image || c->image->width == 0 || c->image->height == 0) {
return;
}
uint32_t display_width = display_get_width();
uint32_t display_height = display_get_height();
int16_t display_center_x = (display_width / 2);
int16_t display_center_y = (display_height / 2);
int16_t image_center_x = (c->image->width / 2);
int16_t image_center_y = (c->image->height / 2);
// Darken the image
rdpq_attach(c->image, NULL);
rdpq_mode_push();
rdpq_set_mode_standard();
rdpq_set_prim_color(RGBA32(0x00, 0x00, 0x00, 0xA0));
rdpq_mode_combiner(RDPQ_COMBINER_FLAT);
rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY);
rdpq_fill_rectangle(0, 0, c->image->width, c->image->height);
rdpq_mode_pop();
rdpq_detach();
// Prepare display list
rspq_block_begin();
rdpq_mode_push();
if ((c->image->width != display_width) || (c->image->height != display_height)) {
rdpq_set_mode_fill(RGBA32(0x00, 0x00, 0x00, 0xFF));
}
if (c->image->width != display_width) {
rdpq_fill_rectangle(
0,
display_center_y - image_center_y,
display_center_x - image_center_x,
display_center_y + image_center_y
);
rdpq_fill_rectangle(
display_center_x + image_center_x - (c->image->width % 2),
display_center_y - image_center_y,
display_width,
display_center_y + image_center_y
);
}
if (c->image->height != display_height) {
rdpq_fill_rectangle(
0,
0,
display_width,
display_center_y - image_center_y
);
rdpq_fill_rectangle(
0,
display_center_y + image_center_y - (c->image->height % 2),
display_width,
display_height
);
}
rdpq_set_mode_copy(false);
rdpq_tex_blit(c->image, display_center_x - image_center_x, display_center_y - image_center_y, NULL);
rdpq_mode_pop();
c->image_display_list = rspq_block_end();
}
static void display_list_free (void *arg) {
rspq_block_free((rspq_block_t *) (arg));
}
component_background_t *component_background_create (char *cache_location) {
component_background_t *c = calloc(1, sizeof(component_background_t));
c->cache_location = cache_location;
load_from_cache(c);
prepare_background(c);
return c;
}
void component_background_replace_image (component_background_t *c, surface_t *image) {
if (!c) {
return;
}
if (c->image) {
surface_free(c->image);
free(c->image);
c->image = NULL;
}
if (c->image_display_list) {
rdpq_call_deferred(display_list_free, c->image_display_list);
c->image_display_list = NULL;
}
c->image = image;
save_to_cache(c);
prepare_background(c);
}
void component_background_draw (component_background_t *c) {
if (!c || !c->image_display_list) {
rdpq_clear(RGBA32(0x00, 0x00, 0x00, 0xFF));
return;
}
rspq_block_run(c->image_display_list);
}

View File

@ -0,0 +1,32 @@
/**
* @file components.h
* @brief Menu Components
* @ingroup menu
*/
#ifndef COMPONENTS_H__
#define COMPONENTS_H__
#include <libdragon.h>
/**
* @addtogroup
* @{ menu_components
*/
typedef struct {
char *cache_location;
surface_t *image;
rspq_block_t *image_display_list;
} component_background_t;
component_background_t *component_background_create (char *cache_location);
void component_background_replace_image (component_background_t *c, surface_t *image);
void component_background_draw (component_background_t *c);
/** @} */ /* menu_components */
#endif

View File

@ -9,6 +9,7 @@
#include "boot/boot.h" #include "boot/boot.h"
#include "components/components.h"
#include "flashcart/flashcart.h" #include "flashcart/flashcart.h"
#include "path.h" #include "path.h"
#include "settings.h" #include "settings.h"
@ -82,6 +83,10 @@ typedef struct {
int entries; int entries;
int selected; int selected;
} browser; } browser;
struct {
component_background_t *background;
} components;
} menu_t; } menu_t;

View File

@ -4,6 +4,7 @@
#include <fatfs/ff.h> #include <fatfs/ff.h>
#include <libdragon.h> #include <libdragon.h>
#include "../components/components.h"
#include "fragments/fragments.h" #include "fragments/fragments.h"
#include "utils/fs.h" #include "utils/fs.h"
#include "views.h" #include "views.h"
@ -252,7 +253,6 @@ static void draw (menu_t *menu, surface_t *d) {
const int text_other_actions_x = text_x + 450; const int text_other_actions_x = text_x + 450;
const int highlight_offset = 2; const int highlight_offset = 2;
const color_t bg_color = RGBA32(0x00, 0x00, 0x00, 0xFF);
const color_t highlight_color = RGBA32(0x3F, 0x3F, 0x3F, 0xFF); const color_t highlight_color = RGBA32(0x3F, 0x3F, 0x3F, 0xFF);
const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF);
const color_t directory_color = RGBA32(0xFF, 0xFF, 0x70, 0xFF); const color_t directory_color = RGBA32(0xFF, 0xFF, 0x70, 0xFF);
@ -270,7 +270,9 @@ static void draw (menu_t *menu, surface_t *d) {
} }
rdpq_attach(d, NULL); rdpq_attach(d, NULL);
rdpq_clear(bg_color);
// Background
component_background_draw(menu->components.background);
// Layout // Layout
fragment_borders(d); fragment_borders(d);

View File

@ -1,5 +1,6 @@
#include <libdragon.h> #include <libdragon.h>
#include "../components/components.h"
#include "fragments/fragments.h" #include "fragments/fragments.h"
#include "views.h" #include "views.h"
@ -21,11 +22,12 @@ static void draw (menu_t *menu, surface_t *d) {
const int text_x = layout->offset_x + layout->offset_text_x; const int text_x = layout->offset_x + layout->offset_text_x;
int text_y = layout->offset_y + layout->offset_text_y; int text_y = layout->offset_y + layout->offset_text_y;
const color_t bg_color = RGBA32(0x00, 0x00, 0x00, 0xFF);
const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF);
rdpq_attach(d, NULL); rdpq_attach(d, NULL);
rdpq_clear(bg_color);
// Background
component_background_draw(menu->components.background);
// Layout // Layout
fragment_borders(d); fragment_borders(d);

View File

@ -2,6 +2,7 @@
#include <libdragon.h> #include <libdragon.h>
#include <stdlib.h> #include <stdlib.h>
#include "../components/components.h"
#include "../png_decoder.h" #include "../png_decoder.h"
#include "../rom_database.h" #include "../rom_database.h"
#include "fragments/fragments.h" #include "fragments/fragments.h"
@ -282,11 +283,12 @@ static void draw (menu_t *menu, surface_t *d) {
const int text_x = layout->offset_x + layout->offset_text_x; const int text_x = layout->offset_x + layout->offset_text_x;
int text_y = layout->offset_y + layout->offset_text_y; int text_y = layout->offset_y + layout->offset_text_y;
const color_t bg_color = RGBA32(0x00, 0x00, 0x00, 0xFF);
const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF);
rdpq_attach(d, NULL); rdpq_attach(d, NULL);
rdpq_clear(bg_color);
// Background
component_background_draw(menu->components.background);
// Layout // Layout
fragment_borders(d); fragment_borders(d);

View File

@ -1,27 +1,38 @@
#include <libdragon.h> #include <libdragon.h>
#include <stdlib.h> #include <stdlib.h>
#include "../components/components.h"
#include "../png_decoder.h" #include "../png_decoder.h"
#include "fragments/fragments.h" #include "fragments/fragments.h"
#include "views.h" #include "views.h"
static bool image_loading; static bool image_loading;
static bool image_set_as_background;
static surface_t *image; static surface_t *image;
static void process (menu_t *menu) { static void process (menu_t *menu) {
if (menu->actions.back) { if (menu->actions.back) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
} else if (menu->actions.enter) {
if (image) {
menu->next_mode = MENU_MODE_BROWSER;
image_set_as_background = true;
}
} }
} }
static void draw (menu_t *menu, surface_t *d) { static void draw (menu_t *menu, surface_t *d) {
rdpq_attach_clear(d, NULL);
if (!image) { if (!image) {
rdpq_attach(d, NULL);
component_background_draw(menu->components.background);
fragment_loader(d); fragment_loader(d);
} else { } else {
rdpq_attach_clear(d, NULL);
uint16_t x = (d->width / 2) - (image->width / 2); uint16_t x = (d->width / 2) - (image->width / 2);
uint16_t y = (d->height / 2) - (image->height / 2); uint16_t y = (d->height / 2) - (image->height / 2);
@ -48,14 +59,19 @@ static void deinit (menu_t *menu) {
} }
if (image) { if (image) {
if (image_set_as_background) {
component_background_replace_image(menu->components.background, image);
} else {
surface_free(image); surface_free(image);
free(image); free(image);
} }
} }
}
void view_image_viewer_init (menu_t *menu) { void view_image_viewer_init (menu_t *menu) {
image_loading = false; image_loading = false;
image_set_as_background = false;
image = NULL; image = NULL;
path_t *path = path_clone(menu->browser.directory); path_t *path = path_clone(menu->browser.directory);

View File

@ -1,5 +1,6 @@
#include <libdragon.h> #include <libdragon.h>
#include "../components/components.h"
#include "../rom_database.h" #include "../rom_database.h"
#include "boot/boot.h" #include "boot/boot.h"
#include "flashcart/flashcart.h" #include "flashcart/flashcart.h"
@ -200,11 +201,12 @@ static void draw (menu_t *menu, surface_t *d) {
const int text_x = layout->offset_x + layout->offset_text_x; const int text_x = layout->offset_x + layout->offset_text_x;
int text_y = layout->offset_y + layout->offset_text_y; int text_y = layout->offset_y + layout->offset_text_y;
const color_t bg_color = RGBA32(0x00, 0x00, 0x00, 0xFF);
const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF);
rdpq_attach(d, NULL); rdpq_attach(d, NULL);
rdpq_clear(bg_color);
// Background
component_background_draw(menu->components.background);
if (load_pending) { if (load_pending) {
fragment_loader(d); fragment_loader(d);

View File

@ -1,5 +1,6 @@
#include <libdragon.h> #include <libdragon.h>
#include "../components/components.h"
#include "../mp3_player.h" #include "../mp3_player.h"
#include "fragments/fragments.h" #include "fragments/fragments.h"
#include "views.h" #include "views.h"
@ -75,11 +76,12 @@ static void draw (menu_t *menu, surface_t *d) {
const int text_x = layout->offset_x + layout->offset_text_x; const int text_x = layout->offset_x + layout->offset_text_x;
int text_y = layout->offset_y + layout->offset_text_y; int text_y = layout->offset_y + layout->offset_text_y;
const color_t bg_color = RGBA32(0x00, 0x00, 0x00, 0xFF);
const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF);
rdpq_attach(d, NULL); rdpq_attach(d, NULL);
rdpq_clear(bg_color);
// Background
component_background_draw(menu->components.background);
// Layout // Layout
fragment_borders(d); fragment_borders(d);

View File

@ -1,8 +1,15 @@
#include <libdragon.h> #include <libdragon.h>
#include "../components/components.h"
#include "utils/fs.h"
#include "views.h" #include "views.h"
#define CACHE_DIRECTORY "sd:/menu/cache"
#define BACKGROUND_CACHE "sd:/menu/cache/background.data"
static void process (menu_t *menu) { static void process (menu_t *menu) {
menu->next_mode = MENU_MODE_BROWSER; menu->next_mode = MENU_MODE_BROWSER;
} }
@ -12,12 +19,19 @@ static void draw (menu_t *menu, surface_t *d) {
rdpq_detach_show(); rdpq_detach_show();
} }
static void load (menu_t *menu) {
menu->components.background = component_background_create(BACKGROUND_CACHE);
}
void view_startup_init (menu_t *menu) { void view_startup_init (menu_t *menu) {
// Nothing to initialize (yet) directory_create(CACHE_DIRECTORY);
} }
void view_startup_display (menu_t *menu, surface_t *display) { void view_startup_display (menu_t *menu, surface_t *display) {
process(menu); process(menu);
draw(menu, display); draw(menu, display);
load(menu);
} }

View File

@ -1,6 +1,7 @@
#include <time.h> #include <time.h>
#include <libdragon.h> #include <libdragon.h>
#include "../components/components.h"
#include "fragments/fragments.h" #include "fragments/fragments.h"
#include "views.h" #include "views.h"
@ -35,11 +36,12 @@ static void draw (menu_t *menu, surface_t *d) {
const int text_x = layout->offset_x + layout->offset_text_x; const int text_x = layout->offset_x + layout->offset_text_x;
int text_y = layout->offset_y + layout->offset_text_y; int text_y = layout->offset_y + layout->offset_text_y;
const color_t bg_color = RGBA32(0x00, 0x00, 0x00, 0xFF);
const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF); const color_t text_color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF);
rdpq_attach(d, NULL); rdpq_attach(d, NULL);
rdpq_clear(bg_color);
// Background
component_background_draw(menu->components.background);
// Layout // Layout
fragment_borders(d); fragment_borders(d);