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); +}