From b39d19918b63b80d1f3b068d158fe43415b9d1b6 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Wed, 9 Mar 2022 00:43:36 -0500 Subject: [PATCH] Reduce the amount of redundant drawing in the game area. We only need to redraw tiles that have changed after a move, or (in the case of expose events) that intersect the exposed region. --- Makefile.am | 6 ++++- src/game.h | 40 ++++++++++++++++++++++++++++++++ src/motif.c | 2 +- src/motif.h | 2 +- src/motif_ui.c | 56 ++++++++++++++++++++++++++++++++++++-------- src/x11.c | 14 ++++++----- t/.gitignore | 1 + t/boardbit.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/game.at | 34 +++++++++++++++++++++++++++ 9 files changed, 199 insertions(+), 19 deletions(-) create mode 100644 t/boardbit.c diff --git a/Makefile.am b/Makefile.am index 0566953..cab8d28 100644 --- a/Makefile.am +++ b/Makefile.am @@ -67,13 +67,17 @@ $(GUIFILES:.dat=.h): $(DX_BASEDIR)/scripts/gen-tree.awk DISTCLEANFILES += $(GUIFILES:.dat=.h) EXTRA_DIST += $(DX_BASEDIR)/scripts/gen-tree.awk $(GUIFILES) -check_PROGRAMS = t/boardmove t/ewmhicon t/rng-test +check_PROGRAMS = t/boardmove t/boardbit t/ewmhicon t/rng-test EXTRA_DIST += t/xos256ss.c t_boardmove_SOURCES = t/boardmove.c src/game.c t_boardmove_LDADD = libgnu.a $(t_boardmove_OBJECTS): $(gnulib_headers) +t_boardbit_SOURCES = t/boardbit.c +t_boardbit_LDADD = libgnu.a +$(t_boardbit_OBJECTS): $(gnulib_headers) + t_ewmhicon_SOURCES = t/ewmhicon.c src/ewmhicon.c common/src/help.c t_ewmhicon_LDADD = libgnu.a $(MOTIF_LIBS) $(t_ewmhicon_OBJECTS): $(gnulib_headers) diff --git a/src/game.h b/src/game.h index fb2fe97..301dfac 100644 --- a/src/game.h +++ b/src/game.h @@ -111,6 +111,46 @@ static inline uint_fast32_t board_mask_v(int x, int y0, int y1) return (col << 5*y1) & (col >> 5*(4-y0)); } +/* + * Return the board bitmap setting locations on or above row y. + */ +static inline uint_fast32_t board_above(int y) +{ + uint_fast32_t val = board_row(y); + + return val | (val-1); +} + +/* + * Return the board bitmap setting locations on or below row y. + */ +static inline uint_fast32_t board_below(int y) +{ + uint_fast32_t val = board_row(y); + + return val | (~val + 1); +} + +/* + * Return the board bitmap setting locations on or left of column x. + */ +static inline uint_fast32_t board_left(int x) +{ + uint_fast32_t val = board_column(x); + + return val | (val - 0x108421); +} + +/* + * Return the board bitmap setting locations on or right of column x. + */ +static inline uint_fast32_t board_right(int x) +{ + uint_fast32_t val = board_column(x); + + return ~val + 0x108421; +} + /* * Move the bits in the game bitmaps according to a move at position (x, y), * and update the location of the empty position which, if the move was valid diff --git a/src/motif.c b/src/motif.c index 52afaae..ef2bd6a 100644 --- a/src/motif.c +++ b/src/motif.c @@ -142,7 +142,7 @@ static void proc_new_game(Widget w, XEvent *e, String *argv, Cardinal *argc) game_reset(&state.board); x11_redraw_goal(&state); - x11_redraw_game(&state); + x11_redraw_game(&state, -1); x11_redraw_icon(&state, shell); } diff --git a/src/motif.h b/src/motif.h index 5c4d05f..90ca7af 100644 --- a/src/motif.h +++ b/src/motif.h @@ -40,6 +40,6 @@ void ui_initialize(struct app_state *state, Widget shell); void x11_initialize(struct app_state *state, Screen *screen); void x11_redraw_icon(struct app_state *state, Widget shell); void x11_redraw_goal(struct app_state *state); -void x11_redraw_game(struct app_state *state); +void x11_redraw_game(struct app_state *state, uint_fast32_t mask); #endif diff --git a/src/motif_ui.c b/src/motif_ui.c index adbb3e5..5661c1e 100644 --- a/src/motif_ui.c +++ b/src/motif_ui.c @@ -206,7 +206,7 @@ construct_menu(const struct ui_menuitem *root, Widget parent, unsigned i) static void game_resize(Widget w, void *data, void *cb_data) { if (XtIsRealized(w)) - x11_redraw_game(data); + x11_redraw_game(data, -1); } static void goal_resize(Widget w, void *data, void *cb_data) @@ -215,6 +215,29 @@ static void goal_resize(Widget w, void *data, void *cb_data) x11_redraw_goal(data); } +static void do_input_move(struct app_state *state, int x, int y) +{ + uint_least32_t *gp = state->board.game, prev[4]; + + memcpy(prev, gp, sizeof prev); + if (game_do_move(&state->board, x, y) == 0) { + uint_least32_t mask; + + if (game_check_goal(&state->board)) { + printf("You win!\n"); + game_finish(&state->board); + } + + /* Figure out which tiles changed */ + prev[0] ^= gp[0]; + prev[1] ^= gp[1]; + prev[2] ^= gp[2]; + mask = prev[0] | prev[1] | prev[2]; + + x11_redraw_game(state, mask); + } +} + static void game_input(Widget w, void *data, void *cb_data) { XmDrawingAreaCallbackStruct *cbs = cb_data; @@ -229,20 +252,33 @@ static void game_input(Widget w, void *data, void *cb_data) XmNheight, &height, (char *)NULL); x = click->x / (width/5); - y = click->y / (width/5); + y = click->y / (height/5); } if (x > 4 || y > 4) return; - if (game_do_move(&state->board, x, y) == 0) { - if (game_check_goal(&state->board)) { - printf("You win!\n"); - game_finish(&state->board); - } + do_input_move(state, x, y); +} - x11_redraw_game(state); - } +static void game_expose(Widget w, void *data, void *cb_data) +{ + XmDrawingAreaCallbackStruct *cbs = cb_data; + XExposeEvent *e = &cbs->event->xexpose; + uint_fast32_t tile_mask; + Dimension width, height; + + XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, (char *)NULL); + if (!(width /= 5) || !(height /= 5)) + return; + + /* Figure out which tiles have been uncovered */ + tile_mask = board_right(e->x / width); + tile_mask &= board_below(e->y / height); + tile_mask &= board_above((e->y + e->height - 1) / height); + tile_mask &= board_left((e->x + e->width - 1) / width); + + x11_redraw_game(data, tile_mask); } void ui_initialize(struct app_state *state, Widget shell) @@ -261,7 +297,7 @@ void ui_initialize(struct app_state *state, Widget shell) state->game = XtNameToWidget(shell, "*gameCanvas"); XtAddCallback(state->game, XmNresizeCallback, game_resize, state); - XtAddCallback(state->game, XmNexposeCallback, game_resize, state); + XtAddCallback(state->game, XmNexposeCallback, game_expose, state); XtAddCallback(state->game, XmNinputCallback, game_input, state); state->goal = XtNameToWidget(shell, "*goalCanvas"); diff --git a/src/x11.c b/src/x11.c index 95e0cf0..f572616 100644 --- a/src/x11.c +++ b/src/x11.c @@ -201,10 +201,11 @@ void x11_redraw_icon(struct app_state *state, Widget shell) } } -void x11_redraw_game(struct app_state *state) +void x11_redraw_game(struct app_state *state, uint_fast32_t mask) { Display *display = XtDisplay(state->goal); Window game = XtWindow(state->game); + uint_least32_t *gp = state->board.game; Dimension w, h; int i; @@ -212,10 +213,11 @@ void x11_redraw_game(struct app_state *state) w /= 5; h /= 5; for (i = 0; i < 25; i++) { - uint_least32_t *gp = state->board.game; - - redraw_tile(state, display, game, - gp[0], gp[1], gp[2], - i%5, i/5, w, h); + if (mask & 1) { + redraw_tile(state, display, game, + gp[0], gp[1], gp[2], + i%5, i/5, w, h); + } + mask >>= 1; } } diff --git a/t/.gitignore b/t/.gitignore index 8221c29..52f3776 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -1,3 +1,4 @@ +/boardbit /boardmove /ewmhicon /rng-test diff --git a/t/boardbit.c b/t/boardbit.c new file mode 100644 index 0000000..6ecb6c6 --- /dev/null +++ b/t/boardbit.c @@ -0,0 +1,63 @@ +/* + * Test board mask calculation functions. + * Copyright © 2022 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "game.h" + +enum { display_width = 12 }; + +static void show_patterns(const char *name, uint_fast32_t func(int)) +{ + unsigned long vals[5]; + int i, row, labelw = display_width; + + for (i = 0; i < 5; i++) { + printf("%*s", display_width-labelw, ""); + labelw = printf("%2s(%d)", name, i); + vals[i] = func(i); + } + putchar('\n'); + + for (row = 0; row < 5; row++) { + putchar(' '); + for (i = 0; i < 25; i++) { + unsigned long val; + + val = vals[i/5]; + vals[i/5] >>= 1; + + if (i && i % 5 == 0) + printf("%*s", display_width-5, ""); + putchar(val & 1 ? '@' : '.'); + if (i == 24) + putchar('\n'); + } + } +} + +int main(void) +{ + show_patterns("left", board_left); + putchar('\n'); + show_patterns("right", board_right); + putchar('\n'); + show_patterns("above", board_above); + putchar('\n'); + show_patterns("below", board_below); +} diff --git a/tests/game.at b/tests/game.at index bfd9819..b607cf6 100644 --- a/tests/game.at +++ b/tests/game.at @@ -13,6 +13,40 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +AT_SETUP([board bitmap functions]) + +AT_CHECK([boardbit], [0], +[[left(0) left(1) left(2) left(3) left(4) + @.... @@... @@@.. @@@@. @@@@@ + @.... @@... @@@.. @@@@. @@@@@ + @.... @@... @@@.. @@@@. @@@@@ + @.... @@... @@@.. @@@@. @@@@@ + @.... @@... @@@.. @@@@. @@@@@ + +right(0) right(1) right(2) right(3) right(4) + @@@@@ .@@@@ ..@@@ ...@@ ....@ + @@@@@ .@@@@ ..@@@ ...@@ ....@ + @@@@@ .@@@@ ..@@@ ...@@ ....@ + @@@@@ .@@@@ ..@@@ ...@@ ....@ + @@@@@ .@@@@ ..@@@ ...@@ ....@ + +above(0) above(1) above(2) above(3) above(4) + @@@@@ @@@@@ @@@@@ @@@@@ @@@@@ + ..... @@@@@ @@@@@ @@@@@ @@@@@ + ..... ..... @@@@@ @@@@@ @@@@@ + ..... ..... ..... @@@@@ @@@@@ + ..... ..... ..... ..... @@@@@ + +below(0) below(1) below(2) below(3) below(4) + @@@@@ ..... ..... ..... ..... + @@@@@ @@@@@ ..... ..... ..... + @@@@@ @@@@@ @@@@@ ..... ..... + @@@@@ @@@@@ @@@@@ @@@@@ ..... + @@@@@ @@@@@ @@@@@ @@@@@ @@@@@ +]]) + +AT_CLEANUP + AT_SETUP([game_do_move zigzag]) AT_CHECK([boardmove m4_do( -- 2.43.2