From db1162ead7642efc06b5f67703e9e5fa69fa2f63 Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Fri, 2 Aug 2024 00:17:00 +0800 Subject: [PATCH] Implement a generic image loader The symbols of available image loaders should be hidden. This commit introduces a generic image loader that can select the appropriate image loader implementation by parsing the name of the given image file. Currently, both PNG and JPEG image formats are supported, and we might support (animated) GIFs later. Close #14 --- Makefile | 1 + apps/main.c | 15 +++----- include/twin.h | 10 ++---- include/twin_private.h | 16 +++++++++ src/image-jpeg.c | 2 +- src/image-png.c | 2 +- src/image.c | 81 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 src/image.c diff --git a/Makefile b/Makefile index 8570aa9..b014a51 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ libtwin.a_files-y = \ src/icon.c \ src/pixmap.c \ src/timeout.c \ + src/image.c \ src/api.c libtwin.a_includes-y := \ diff --git a/apps/main.c b/apps/main.c index 970d09e..9c6a411 100644 --- a/apps/main.c +++ b/apps/main.c @@ -25,17 +25,13 @@ #define ASSET_PATH "assets/" -/** +/* * Load the background pixmap from storage. - * @filename: File name of a PNG background. - * - * Return a default pattern if the load of @filename fails. + * Return a default pattern if the load of @path or image loader fails. */ -static twin_pixmap_t *load_background(twin_screen_t *screen, - const char *filename) +static twin_pixmap_t *load_background(twin_screen_t *screen, const char *path) { -#if defined(CONFIG_LOADER_PNG) - twin_pixmap_t *raw_background = twin_png_to_pixmap(filename, TWIN_ARGB32); + twin_pixmap_t *raw_background = twin_pixmap_from_file(path, TWIN_ARGB32); if (!raw_background) /* Fallback to a default pattern */ return twin_make_pattern(); @@ -67,9 +63,6 @@ static twin_pixmap_t *load_background(twin_screen_t *screen, twin_pixmap_destroy(raw_background); return scaled_background; -#else - return twin_make_pattern(); -#endif } static twin_context_t *tx = NULL; diff --git a/include/twin.h b/include/twin.h index 1ccbad4..0cbe42f 100644 --- a/include/twin.h +++ b/include/twin.h @@ -687,16 +687,10 @@ void twin_icon_draw(twin_pixmap_t *pixmap, twin_matrix_t matrix); /* - * image-jpeg.c + * image.c */ -twin_pixmap_t *twin_jpeg_to_pixmap(const char *filepath, twin_format_t fmt); - -/* - * image-png.c - */ - -twin_pixmap_t *twin_png_to_pixmap(const char *filepath, twin_format_t fmt); +twin_pixmap_t *twin_pixmap_from_file(const char *path, twin_format_t fmt); /* * label.c diff --git a/include/twin_private.h b/include/twin_private.h index 0c25ac3..ab46f7a 100644 --- a/include/twin_private.h +++ b/include/twin_private.h @@ -536,4 +536,20 @@ static inline int twin_clz(uint32_t v) } #endif +/* Pattern Matching for C macros. + * https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms + */ + +/* In Visual Studio, __VA_ARGS__ is treated as a separate parameter. */ +#define FIX_VC_BUG(x) x + +/* catenate */ +#define PRIMITIVE_CAT(a, ...) FIX_VC_BUG(a##__VA_ARGS__) + +#define IIF(c) PRIMITIVE_CAT(IIF_, c) +/* run the 2nd parameter */ +#define IIF_0(t, ...) __VA_ARGS__ +/* run the 1st parameter */ +#define IIF_1(t, ...) t + #endif /* _TWIN_PRIVATE_H_ */ diff --git a/src/image-jpeg.c b/src/image-jpeg.c index 8218d9e..5262fd1 100644 --- a/src/image-jpeg.c +++ b/src/image-jpeg.c @@ -41,7 +41,7 @@ static void twin_jpeg_error_exit(j_common_ptr cinfo) longjmp(jerr->jbuf, 1); } -twin_pixmap_t *twin_jpeg_to_pixmap(const char *filepath, twin_format_t fmt) +twin_pixmap_t *_twin_jpeg_to_pixmap(const char *filepath, twin_format_t fmt) { twin_pixmap_t *pix = NULL; diff --git a/src/image-png.c b/src/image-png.c index e624169..96e3f60 100644 --- a/src/image-png.c +++ b/src/image-png.c @@ -42,7 +42,7 @@ static void _convertBGRtoARGB(uint8_t *data, int width, int height) } #endif -twin_pixmap_t *twin_png_to_pixmap(const char *filepath, twin_format_t fmt) +twin_pixmap_t *_twin_png_to_pixmap(const char *filepath, twin_format_t fmt) { uint8_t signature[8]; int rb = 0; diff --git a/src/image.c b/src/image.c new file mode 100644 index 0000000..284974c --- /dev/null +++ b/src/image.c @@ -0,0 +1,81 @@ +#include +#include + +#include "twin_private.h" + +#if !defined(CONFIG_LOADER_PNG) +#define CONFIG_LOADER_PNG 0 +#endif + +#if !defined(CONFIG_LOADER_JPEG) +#define CONFIG_LOADER_JPEG 0 +#endif + +/* Feature test macro */ +#define LOADER_HAS(x) CONFIG_LOADER_##x + +/* clang-format off */ +#define SUPPORTED_FORMATS \ + IIF(LOADER_HAS(PNG))( \ + _(png) \ + ) \ + IIF(LOADER_HAS(JPEG))( \ + _(jpeg) \ + ) +/* clang-format on */ + +typedef enum { + IMAGE_TYPE_unknown, +#define _(x) IMAGE_TYPE_##x, + SUPPORTED_FORMATS +#undef _ +} twin_image_format_t; + +/* FIXME: Check the header of the given images to determine the supported image + * formats instead of parsing the filename without checking its content. + */ +static twin_image_format_t image_type_from_name(const char *path) +{ + twin_image_format_t type = IMAGE_TYPE_unknown; + const char *extname = strrchr(path, '.'); + if (!extname) + return IMAGE_TYPE_unknown; +#if LOADER_HAS(PNG) + else if (!strcasecmp(extname, ".png")) { + type = IMAGE_TYPE_png; + } +#endif +#if LOADER_HAS(JPEG) + else if (!strcasecmp(extname, ".jpg") || !strcasecmp(extname, ".jpeg")) { + type = IMAGE_TYPE_jpeg; + } +#endif + /* otherwise, unsupported format */ + + return type; +} + +#define _(x) \ + twin_pixmap_t *_twin_##x##_to_pixmap(const char *filepath, \ + twin_format_t fmt); +SUPPORTED_FORMATS +#undef _ + +typedef twin_pixmap_t *(*loader_func_t)(const char *, twin_format_t); + +/* clang-format off */ +static loader_func_t image_loaders[] = { + [IMAGE_TYPE_unknown] = NULL, +#define _(x) [IMAGE_TYPE_##x] = _twin_##x##_to_pixmap, + SUPPORTED_FORMATS +#undef _ +}; +/* clang-format on */ + +twin_pixmap_t *twin_pixmap_from_file(const char *path, twin_format_t fmt) +{ + loader_func_t loader = image_loaders[image_type_from_name(path)]; + if (!loader) + return NULL; + return loader(path, fmt); +}