]> git.draconx.ca Git - rrace.git/commitdiff
Reduce the amount of redundant drawing in the game area.
authorNick Bowler <nbowler@draconx.ca>
Wed, 9 Mar 2022 05:43:36 +0000 (00:43 -0500)
committerNick Bowler <nbowler@draconx.ca>
Wed, 9 Mar 2022 06:32:44 +0000 (01:32 -0500)
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
src/game.h
src/motif.c
src/motif.h
src/motif_ui.c
src/x11.c
t/.gitignore
t/boardbit.c [new file with mode: 0644]
tests/game.at

index 056695344cf9598f9c68e18bc7d680e5f22c5c8f..cab8d28d6a484167dd233a76517c883e6149c584 100644 (file)
@@ -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)
index fb2fe9767135e8bfe97bab3cf2bfe46ab035a9a3..301dfac022c87681b9be53b88f96bc91dd1b01b0 100644 (file)
@@ -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
index 52afaaecaf9ff2d89ac6be4fbc8103ed9854c214..ef2bd6a53b3e2a9aad2ca7720ec22f02e24dc4a8 100644 (file)
@@ -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);
 }
 
index 5c4d05fdf69d0326f4f0c29df2d71eb1e7546f97..90ca7af4ea6af07a13b549b2c6fb217577853b7f 100644 (file)
@@ -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
index adbb3e501c3909ae162a7588525b26c8fa656459..5661c1e4cad1de1289db468eaca6cbac30e92f0c 100644 (file)
@@ -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");
index 95e0cf057646085d502929124552196118fb9b4d..f572616192d6d6400ad95e4c350902d567d09d56 100644 (file)
--- 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;
        }
 }
index 8221c2990df93191b4c504ae1791203ea1892e93..52f3776bfa460058f37ce75bbefc7de270db2bce 100644 (file)
@@ -1,3 +1,4 @@
+/boardbit
 /boardmove
 /ewmhicon
 /rng-test
diff --git a/t/boardbit.c b/t/boardbit.c
new file mode 100644 (file)
index 0000000..6ecb6c6
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#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);
+}
index bfd98190aec8e465cd85a8cea9bd236425babe4b..b607cf67e39d695de2b0d7ca5e39fc4c4187f5ce 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
+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(