shithub: puzzles

Download patch

ref: 58511aa009c672cc2ade783d537a5830806fd02c
parent: 76da6ec140cbbdac6136469ce50aab40e218f398
author: Ben Harris <bjh21@bjh21.me.uk>
date: Sun Jul 30 07:30:19 EDT 2023

Same Game: more efficient tile_redraw

I've rewritten tile_redraw to reduce the number of calls to
draw_rect().  Before, it would generally make five calls to
draw_rect() when drawing a tile.  Now it makes at most three, and
usually two.  That's one draw_rect() for each colour that appears in
the tile, which is as good as it can get.  This reduces the time to
draw a large puzzle by about 35% on Firefox 102.

This is of significance to me because CanvasRenderingContext2D on my
test KaiOS device seems to have a limit on the number of fill() and
fillRect() calls that it will tolerate in a short time.  This means
that if you issue more than 1024 fillRect() calls in rapid succession,
the later ones are simply ignored.

Same Game's largest preset called draw_rect() so much that it hit this
limit.  That meant that the right-hand side of the grid didn't get
properly drawn when starting a new game.  Now that it is less
profligate with draw_rect() it fits comfortably within the limit and I
get to see the entire grid.

--- a/samegame.c
+++ b/samegame.c
@@ -1471,6 +1471,7 @@
                         int tile, int bgcolour)
 {
     int outer = bgcolour, inner = outer, col = tile & TILE_COLMASK;
+    int tile_w, tile_h, outer_w, outer_h;
 
     if (col) {
 	if (tile & TILE_IMPOSSIBLE) {
@@ -1483,19 +1484,25 @@
 	    outer = inner = col;
 	}
     }
-    draw_rect(dr, COORD(x), COORD(y), TILE_INNER, TILE_INNER, outer);
-    draw_rect(dr, COORD(x)+TILE_INNER/4, COORD(y)+TILE_INNER/4,
-	      TILE_INNER/2, TILE_INNER/2, inner);
-
-    if (dright)
-	draw_rect(dr, COORD(x)+TILE_INNER, COORD(y), TILE_GAP, TILE_INNER,
-		  (tile & TILE_JOINRIGHT) ? outer : bgcolour);
-    if (dbelow)
-	draw_rect(dr, COORD(x), COORD(y)+TILE_INNER, TILE_INNER, TILE_GAP,
-		  (tile & TILE_JOINDOWN) ? outer : bgcolour);
-    if (dright && dbelow)
-	draw_rect(dr, COORD(x)+TILE_INNER, COORD(y)+TILE_INNER, TILE_GAP, TILE_GAP,
-		  (tile & TILE_JOINDIAG) ? outer : bgcolour);
+    tile_w = dright ? TILE_SIZE : TILE_INNER;
+    tile_h = dbelow ? TILE_SIZE : TILE_INNER;
+    outer_w = (tile & TILE_JOINRIGHT) ? tile_w : TILE_INNER;
+    outer_h = (tile & TILE_JOINDOWN)  ? tile_h : TILE_INNER;
+    /* Draw the background if any of it will be visible. */
+    if (outer_w != tile_w || outer_h != tile_h || outer == bgcolour)
+        draw_rect(dr, COORD(x), COORD(y), tile_w, tile_h, bgcolour);
+    /* Draw the piece. */
+    if (outer != bgcolour)
+        draw_rect(dr, COORD(x), COORD(y), outer_w, outer_h, outer);
+    if (inner != outer)
+        draw_rect(dr, COORD(x)+TILE_INNER/4, COORD(y)+TILE_INNER/4,
+                  TILE_INNER/2, TILE_INNER/2, inner);
+    /* Reset bottom-right corner if necessary. */
+    if ((tile & (TILE_JOINRIGHT | TILE_JOINDOWN | TILE_JOINDIAG)) ==
+        (TILE_JOINRIGHT | TILE_JOINDOWN) && outer != bgcolour &&
+        TILE_GAP != 0)
+	draw_rect(dr, COORD(x)+TILE_INNER, COORD(y)+TILE_INNER,
+                  TILE_GAP, TILE_GAP, bgcolour);
 
     if (tile & TILE_HASSEL) {
 	int sx = COORD(x)+2, sy = COORD(y)+2, ssz = TILE_INNER-5;