From 1ddd20748d01570929a88672366dff69ce300a51 Mon Sep 17 00:00:00 2001 From: untir_l <87096069+untir-l@users.noreply.github.com> Date: Sun, 27 Feb 2022 18:53:43 +0530 Subject: Add support for choosing foreground and background colours --- .gitignore | 1 + README.md | 7 +++++-- cli/hitomezashi_cli.c | 44 ++++++++++++++++++++++++++++++++++++------ cli/hitomezashi_cli.h | 4 +++- lib/hitomezashi.c | 20 ++++++++----------- lib/hitomezashi.h | 10 ++++++---- web/hitomezashi_web.c | 18 +++++++++++++++-- web/hitomezashi_web.h | 4 +++- web/hitomezashi_web_shell.html | 4 ++++ 9 files changed, 84 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index db29d29..d22b09a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ *.wasm hitomezashi_cli **/docs +.idea/ diff --git a/README.md b/README.md index 247ddc4..3d75887 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Library, CLI and web app to generate hitomezashi patterns. These are an example of visually complex and structured patterns arising from simple rules. -Here is an example of such a pattern: +Here is an example of such a pattern (different colours are also supported): sample hitomezashi pattern @@ -32,6 +32,8 @@ To build, run `CC=gcc make libhitomezashi.a`. This is in the `cli/` directory. Run `./hitomezashi_cli -h` for usage info. +Colours are supported! + The output of the CLI app is a BMP image as this is supported by default in SDL2; this will change eventually to output PNGs since BMP is an antiquated format. For now, you can convert using FFmpeg or similar. To build, run `CC=gcc make hitomezashi_cli`. @@ -44,6 +46,8 @@ To build, first run `make clean` if you previously built to native code, then (w You will need to serve, from a web server: `hitomezashi_web.html`, `hitomezashi_web.js`, `hitomezashi_web.wasm` in order for it to work; the `file://` protocol may not be sufficient. +The UI is self-explanatory. + ## Technical and copyright information Written in C11 with SDL2. Code style: `make format-code` will run `clang-format` with the correct parameters. @@ -52,4 +56,3 @@ Licensed under GPLv2 (see `LICENSE` file for full text). This project's source c ## Todos/potential future additions - Add support for output to PNG/other formats to the CLI app -- Add support for choosing foreground and background colours diff --git a/cli/hitomezashi_cli.c b/cli/hitomezashi_cli.c index 72d43b7..53a9568 100644 --- a/cli/hitomezashi_cli.c +++ b/cli/hitomezashi_cli.c @@ -2,7 +2,7 @@ #include "hitomezashi_cli.h" -#include "SDL.h" +#include "SDL2/SDL.h" #define OPTPARSE_IMPLEMENTATION #define OPTPARSE_API static #include @@ -25,9 +25,11 @@ int main(int argc, char **argv) { char *y_pattern; int gap; int thickness; + Uint32 fg_colour; + Uint32 bg_colour; hitomezashi_cli_handle_args(&out_file_path, &x_pattern_len, &y_pattern_len, - &x_pattern, &y_pattern, &gap, &thickness, argc, - argv); + &x_pattern, &y_pattern, &gap, &thickness, + &fg_colour, &bg_colour, argc, argv); if (SDL_Init(SDL_INIT_VIDEO) != 0) { SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Failed to initialise SDL: %s", @@ -37,7 +39,8 @@ int main(int argc, char **argv) { struct Hitomezashi_State state; if (hitomezashi_state_init(&state, x_pattern_len, y_pattern_len, x_pattern, - y_pattern, gap, thickness) != 0) { + y_pattern, gap, thickness, fg_colour, + bg_colour) != 0) { return Hitomezashi_Cli_Exit_Code_Err_State_Init; } @@ -64,6 +67,10 @@ void hitomezashi_cli_help(void) { "-y - specify the y pattern, as with -x\n" "-g - specify the gap between lines as an integer\n" "-t - specify the line thickness as an integer\n" + "-f - (optional) specify the foreground colour in RGB as a 32-bit " + "integer, eg. 0x000000" + "-b - (optional) specify the background colour in RGB as a 32-bit " + "integer, eg. 0xFFFFFF" "-h - print this help and exit") < 0) { exit(Hitomezashi_Cli_Exit_Code_Err_Print_Help); } @@ -73,17 +80,20 @@ void hitomezashi_cli_help(void) { void hitomezashi_cli_handle_args(char **out_file_path, int *x_pattern_len, int *y_pattern_len, char **x_pattern, char **y_pattern, int *gap, int *thickness, - int argc, char **argv) { + Uint32 *fg_colour, Uint32 *bg_colour, int argc, + char **argv) { bool out_file_path_specified = false; bool x_pattern_specified = false; bool y_pattern_specified = false; bool gap_specified = false; bool thickness_specified = false; + *fg_colour = 0x000000; + *bg_colour = 0xffffff; struct optparse options; optparse_init(&options, argv); int option; - while ((option = optparse(&options, ":o:x:y:g:t:h")) != -1) { + while ((option = optparse(&options, ":o:x:y:g:t:f:b:h")) != -1) { switch (option) { case 'o':; *out_file_path = options.optarg; @@ -144,6 +154,28 @@ void hitomezashi_cli_handle_args(char **out_file_path, int *x_pattern_len, *thickness = thickness_l; thickness_specified = true; break; + case 'f':; + long fg_colour_l = strtol(options.optarg, NULL, 0); + if (fg_colour_l < 0 || fg_colour_l > INT_MAX) { + SDL_LogCritical( + SDL_LOG_CATEGORY_ERROR, + "Value for foreground colour must be non-negative and less than %d", + INT_MAX); + exit(Hitomezashi_Cli_Exit_Code_Err_Handle_Args); + } + *fg_colour = fg_colour_l; + break; + case 'b':; + long bg_colour_l = strtol(options.optarg, NULL, 0); + if (bg_colour_l < 0 || bg_colour_l > INT_MAX) { + SDL_LogCritical( + SDL_LOG_CATEGORY_ERROR, + "Value for background colour must be non-negative and less than %d", + INT_MAX); + exit(Hitomezashi_Cli_Exit_Code_Err_Handle_Args); + } + *bg_colour = bg_colour_l; + break; case 'h':; hitomezashi_cli_help(); break; diff --git a/cli/hitomezashi_cli.h b/cli/hitomezashi_cli.h index 3c6b9bf..bce0128 100644 --- a/cli/hitomezashi_cli.h +++ b/cli/hitomezashi_cli.h @@ -2,6 +2,7 @@ #ifndef HITOMEZASHI_CLI_H #define HITOMEZASHI_CLI_H +#include "SDL2/SDL.h" #include /** Exit codes for main(). @@ -31,7 +32,8 @@ enum Hitomezashi_Cli_Exit_Code { void hitomezashi_cli_handle_args(char **out_file_path, int *x_pattern_len, int *y_pattern_len, char **x_pattern, char **y_pattern, int *gap, int *thickness, - int argc, char **argv); + Uint32 *fg_colour, Uint32 *bg_colour, int argc, + char **argv); /** Print the help text and exit. * Exits with Hitomezashi_Cli_Exit_Code_Success (0) on success, and * Hitomezashi_Cli_Exit_Code_Print_Help (2) on failure. diff --git a/lib/hitomezashi.c b/lib/hitomezashi.c index c87073f..9275517 100644 --- a/lib/hitomezashi.c +++ b/lib/hitomezashi.c @@ -8,7 +8,8 @@ SDL_Color HITOMEZASHI_BG_COLOUR = {.r = 255, .g = 255, .b = 255}; enum Hitomezashi_State_Init_Result hitomezashi_state_init(struct Hitomezashi_State *state, int x_pattern_len, int y_pattern_len, char *x_pattern, char *y_pattern, - int gap, int line_thickness) { + int gap, int line_thickness, Uint32 fg_colour, + Uint32 bg_colour) { assert(x_pattern_len >= 0); assert(y_pattern_len >= 0); assert(gap >= 0); @@ -22,6 +23,8 @@ hitomezashi_state_init(struct Hitomezashi_State *state, int x_pattern_len, state->line_thickness = line_thickness; state->output_width = x_pattern_len * gap; state->output_height = y_pattern_len * gap; + state->fg_colour = fg_colour; + state->bg_colour = bg_colour; state->surface = SDL_CreateRGBSurface(0, state->output_width, state->output_height, 32, 0, 0, 0, 0); @@ -36,14 +39,7 @@ enum Hitomezashi_Draw_Result hitomezashi_draw(struct Hitomezashi_State *state) { return Hitomezashi_Draw_Result_Err_Lock_Surface; } - Uint32 bg_colour = - SDL_MapRGB(state->surface->format, HITOMEZASHI_BG_COLOUR.r, - HITOMEZASHI_BG_COLOUR.g, HITOMEZASHI_BG_COLOUR.b); - Uint32 fg_colour = - SDL_MapRGB(state->surface->format, HITOMEZASHI_FG_COLOUR.r, - HITOMEZASHI_FG_COLOUR.g, HITOMEZASHI_FG_COLOUR.b); - - SDL_FillRect(state->surface, NULL, bg_colour); + SDL_FillRect(state->surface, NULL, state->bg_colour); SDL_Rect rect; // Draw y pattern (horizontal) lines @@ -54,7 +50,7 @@ enum Hitomezashi_Draw_Result hitomezashi_draw(struct Hitomezashi_State *state) { rect.y = i * state->gap; rect.w = state->gap; rect.h = state->line_thickness; - SDL_FillRect(state->surface, &rect, fg_colour); + SDL_FillRect(state->surface, &rect, state->fg_colour); } } } @@ -66,7 +62,7 @@ enum Hitomezashi_Draw_Result hitomezashi_draw(struct Hitomezashi_State *state) { rect.y = j * state->gap; rect.w = state->line_thickness; rect.h = state->gap; - SDL_FillRect(state->surface, &rect, fg_colour); + SDL_FillRect(state->surface, &rect, state->fg_colour); } } } @@ -78,7 +74,7 @@ enum Hitomezashi_Draw_Result hitomezashi_draw(struct Hitomezashi_State *state) { rect.y = y; rect.w = state->line_thickness; rect.h = state->line_thickness; - SDL_FillRect(state->surface, &rect, fg_colour); + SDL_FillRect(state->surface, &rect, state->fg_colour); } } diff --git a/lib/hitomezashi.h b/lib/hitomezashi.h index 64b17af..51da75b 100644 --- a/lib/hitomezashi.h +++ b/lib/hitomezashi.h @@ -25,6 +25,9 @@ struct Hitomezashi_State { int line_thickness; + Uint32 fg_colour; + Uint32 bg_colour; + /** Width in pixels of the SDL_Surface needed to hold the pattern. This is * calculated by hitomezashi_state_init(). */ int output_width; @@ -32,11 +35,10 @@ struct Hitomezashi_State { * calculated by hitomezashi_state_init(). */ int output_height; - /** SDL_Surface the pattern is drawn to by hitomezashi_draw(). */ + /** Surface the pattern is drawn to by hitomezashi_draw(). */ SDL_Surface *surface; }; -/** Result of hitomezashi_state_init(). */ enum Hitomezashi_State_Init_Result { Hitomezashi_State_Init_Result_Success, /** The function failed as it encountered an error initialising the @@ -44,7 +46,6 @@ enum Hitomezashi_State_Init_Result { Hitomezashi_State_Init_Result_Err_Create_Surface, }; -/** Result of hitomezashi_draw() */ enum Hitomezashi_Draw_Result { Hitomezashi_Draw_Result_Success, /** The function failed as it encountered an error "locking" the surface to @@ -57,7 +58,8 @@ enum Hitomezashi_Draw_Result { enum Hitomezashi_State_Init_Result hitomezashi_state_init(struct Hitomezashi_State *state, int x_pattern_len, int y_pattern_len, char *x_pattern, char *y_pattern, - int gap, int line_thickness); + int gap, int line_thickness, Uint32 fg_colour, + Uint32 bg_colour); /** Draw the hitomezashi pattern to state->surface. */ enum Hitomezashi_Draw_Result hitomezashi_draw(struct Hitomezashi_State *state); diff --git a/web/hitomezashi_web.c b/web/hitomezashi_web.c index 4859c20..539fa96 100644 --- a/web/hitomezashi_web.c +++ b/web/hitomezashi_web.c @@ -32,6 +32,9 @@ int EMSCRIPTEN_KEEPALIVE main(void) { int gap = strtol(strtok(NULL, "\n"), NULL, 0); int line_thickness = strtol(strtok(NULL, "\n"), NULL, 0); + Uint32 fg_colour = strtol(strtok(NULL, "\n"), NULL, 0); + Uint32 bg_colour = strtol(strtok(NULL, "\n"), NULL, 0); + if (SDL_Init(SDL_INIT_VIDEO) != 0) { return Hitomezashi_Web_Result_Err_Sdl_Init; } @@ -42,7 +45,8 @@ int EMSCRIPTEN_KEEPALIVE main(void) { struct Hitomezashi_State state; if (hitomezashi_state_init(&state, x_pattern_len, y_pattern_len, x_pattern, - y_pattern, gap, line_thickness) != 0) { + y_pattern, gap, line_thickness, fg_colour, + bg_colour) != 0) { return Hitomezashi_Web_Result_Err_State_Init; } @@ -81,6 +85,16 @@ EM_JS(char *, hitomezashi_web_get_args, (void), { const y_pattern = search_params.get("y_pattern"); const gap = search_params.get("gap"); const line_thickness = search_params.get("line_thickness"); + let fg_colour = search_params.get("fg_colour"); + let bg_colour = search_params.get("bg_colour"); + if (!fg_colour) { + fg_colour = "#000000"; + } + if (!bg_colour) { + bg_colour = "#FFFFFF"; + } + fg_colour = "0x" + fg_colour.slice(1); + bg_colour = "0x" + bg_colour.slice(1); if (!(x_pattern && y_pattern && gap && line_thickness)) { result_js = "i"; @@ -91,7 +105,7 @@ EM_JS(char *, hitomezashi_web_get_args, (void), { result_js = "i"; } else { result_js = x_pattern + "\n" + y_pattern + "\n" + gap + "\n" + - line_thickness + "\n"; + line_thickness + "\n" + fg_colour + "\n" + bg_colour + "\n"; } } diff --git a/web/hitomezashi_web.h b/web/hitomezashi_web.h index 5d4f523..361a8c2 100644 --- a/web/hitomezashi_web.h +++ b/web/hitomezashi_web.h @@ -2,6 +2,7 @@ #ifndef HITOMEZASHI_WEB_H #define HITOMEZASHI_WEB_H +#include "SDL2/SDL.h" #include "emscripten.h" enum Hitomezashi_Web_Result { @@ -12,7 +13,8 @@ enum Hitomezashi_Web_Result { }; int hitomezashi_web(int x_pattern_len, int y_pattern_len, char *x_pattern, - char *y_pattern, int gap, int line_thickness); + char *y_pattern, int gap, int line_thickness, + Uint32 fg_colour, Uint32 bg_colour); /** Parse the URL query string and return the parameters thereof. * diff --git a/web/hitomezashi_web_shell.html b/web/hitomezashi_web_shell.html index 7b3ad76..d3224dd 100644 --- a/web/hitomezashi_web_shell.html +++ b/web/hitomezashi_web_shell.html @@ -39,6 +39,8 @@

+

+


@@ -63,6 +65,8 @@ document.querySelector("input[name=\"y_pattern\"]").value = params.get("y_pattern"); document.querySelector("input[name=\"gap\"]").value = params.get("gap"); document.querySelector("input[name=\"line_thickness\"]").value = params.get("line_thickness"); + document.querySelector("input[name=\"fg_colour\"]").value = params.get("fg_colour"); + document.querySelector("input[name=\"bg_colour\"]").value = params.get("bg_colour"); {{{ SCRIPT }}} -- cgit v1.2.3-57-g22cb