shithub: puzzles

Download patch

ref: 76da6ec140cbbdac6136469ce50aab40e218f398
parent: 3e7a6adce5887db04d26438da64fdc4de308f4da
author: Ben Harris <bjh21@bjh21.me.uk>
date: Sat Jul 29 12:06:19 EDT 2023

js: keep colour strings in JavaScript rather than in C

The drawing routines in JavaScript used to take pointers to a C string
containing a CSS colour name.  That meant that JavaScript had to create
a new JavaScript string on ever call to a drawing function, which seemed
ugly.

So now we instead pass colour numbers all the way down into JavaScript
and keep an array of JavaScript strings there that can be re-used.  The
conversion from RGB triples to strings is still done in C, though.

This doesn't seem to have fixed either of the bugs I hoped it would, but
it does measurably improve drawing performance so I think it's worth
doing.

--- a/emcc.c
+++ b/emcc.c
@@ -54,7 +54,7 @@
 extern int js_get_selected_preset(void);
 extern void js_select_preset(int n);
 extern void js_default_colour(float *output);
-extern void js_set_background_colour(const char *bg);
+extern void js_set_colour(int colour_number, const char *colour_string);
 extern void js_get_date_64(unsigned *p);
 extern void js_update_permalinks(const char *desc, const char *seed);
 extern void js_enable_undo_redo(bool undo, bool redo);
@@ -64,21 +64,18 @@
 extern void js_canvas_start_draw(void);
 extern void js_canvas_draw_update(int x, int y, int w, int h);
 extern void js_canvas_end_draw(void);
-extern void js_canvas_draw_rect(int x, int y, int w, int h,
-                                const char *colour);
+extern void js_canvas_draw_rect(int x, int y, int w, int h, int colour);
 extern void js_canvas_clip_rect(int x, int y, int w, int h);
 extern void js_canvas_unclip(void);
 extern void js_canvas_draw_line(float x1, float y1, float x2, float y2,
-                                int width, const char *colour);
+                                int width, int colour);
 extern void js_canvas_draw_poly(const int *points, int npoints,
-                                const char *fillcolour,
-                                const char *outlinecolour);
+                                int fillcolour, int outlinecolour);
 extern void js_canvas_draw_circle(int x, int y, int r,
-                                  const char *fillcolour,
-                                  const char *outlinecolour);
+                                  int fillcolour, int outlinecolour);
 extern int js_canvas_find_font_midpoint(int height, bool monospaced);
 extern void js_canvas_draw_text(int x, int y, int halign,
-                                const char *colptr, int height,
+                                int colour, int height,
                                 bool monospaced, const char *text);
 extern int js_canvas_new_blitter(int w, int h);
 extern void js_canvas_free_blitter(int id);
@@ -185,12 +182,6 @@
 }
 
 /*
- * HTMLish names for the colours allocated by the puzzle.
- */
-static char **colour_strings;
-static int ncolours;
-
-/*
  * The global midend object.
  */
 static midend *me;
@@ -497,19 +488,19 @@
     else
         halign = 0;
 
-    js_canvas_draw_text(x, y, halign, colour_strings[colour],
+    js_canvas_draw_text(x, y, halign, colour,
                         fontsize, fonttype == FONT_FIXED, text);
 }
 
 static void js_draw_rect(void *handle, int x, int y, int w, int h, int colour)
 {
-    js_canvas_draw_rect(x, y, w, h, colour_strings[colour]);
+    js_canvas_draw_rect(x, y, w, h, colour);
 }
 
 static void js_draw_line(void *handle, int x1, int y1, int x2, int y2,
                          int colour)
 {
-    js_canvas_draw_line(x1, y1, x2, y2, 1, colour_strings[colour]);
+    js_canvas_draw_line(x1, y1, x2, y2, 1, colour);
 }
 
 static void js_draw_thick_line(void *handle, float thickness,
@@ -516,23 +507,19 @@
                                float x1, float y1, float x2, float y2,
                                int colour)
 {
-    js_canvas_draw_line(x1, y1, x2, y2, thickness, colour_strings[colour]);
+    js_canvas_draw_line(x1, y1, x2, y2, thickness, colour);
 }
 
 static void js_draw_poly(void *handle, const int *coords, int npoints,
                          int fillcolour, int outlinecolour)
 {
-    js_canvas_draw_poly(coords, npoints,
-                        fillcolour >= 0 ? colour_strings[fillcolour] : NULL,
-                        colour_strings[outlinecolour]);
+    js_canvas_draw_poly(coords, npoints, fillcolour, outlinecolour);
 }
 
 static void js_draw_circle(void *handle, int cx, int cy, int radius,
                            int fillcolour, int outlinecolour)
 {
-    js_canvas_draw_circle(cx, cy, radius,
-                          fillcolour >= 0 ? colour_strings[fillcolour] : NULL,
-                          colour_strings[outlinecolour]);
+    js_canvas_draw_circle(cx, cy, radius, fillcolour, outlinecolour);
 }
 
 struct blitter {
@@ -1035,7 +1022,7 @@
 {
     const char *param_err;
     float *colours;
-    int i;
+    int i, ncolours;
 
     /*
      * Initialise JavaScript event handlers.
@@ -1118,7 +1105,6 @@
      * hex ID strings.
      */
     colours = midend_colours(me, &ncolours);
-    colour_strings = snewn(ncolours, char *);
     for (i = 0; i < ncolours; i++) {
         char col[40];
         sprintf(col, "#%02x%02x%02x",
@@ -1125,10 +1111,8 @@
                 (unsigned)(0.5F + 255 * colours[i*3+0]),
                 (unsigned)(0.5F + 255 * colours[i*3+1]),
                 (unsigned)(0.5F + 255 * colours[i*3+2]));
-        colour_strings[i] = dupstr(col);
+        js_set_colour(i, col);
     }
-    /* Put the background colour in a CSS variable. */
-    js_set_background_colour(colour_strings[0]);
 
     /*
      * Request notification when the game ids change (e.g. if the user
--- a/emcclib.js
+++ b/emcclib.js
@@ -181,14 +181,15 @@
     },
 
     /*
-     * void js_set_background_colour(const char *bg);
+     * void js_set_colour(int colour_number, char const *colour_string);
      *
-     * Record the puzzle background colour in a CSS variable so
-     * the style sheet can use it if it wants.
+     * Record a colour string used by the puzzle.
      */
-    js_set_background_colour: function(bgptr) {
-        document.documentElement.style.setProperty("--puzzle-background",
-                                                   UTF8ToString(bgptr));
+    js_set_colour: function(colour_number, colour_string) {
+        colours[colour_number] = UTF8ToString(colour_string);
+        if (colour_number == 0)
+            document.documentElement.style.setProperty("--puzzle-background",
+                                                       colours[colour_number]);
     },
 
     /*
@@ -332,13 +333,12 @@
     },
 
     /*
-     * void js_canvas_draw_rect(int x, int y, int w, int h,
-     *                          const char *colour);
+     * void js_canvas_draw_rect(int x, int y, int w, int h, int colour);
      * 
      * Draw a rectangle.
      */
-    js_canvas_draw_rect: function(x, y, w, h, colptr) {
-        ctx.fillStyle = UTF8ToString(colptr);
+    js_canvas_draw_rect: function(x, y, w, h, colour) {
+        ctx.fillStyle = colours[colour];
         ctx.fillRect(x, y, w, h);
     },
 
@@ -365,7 +365,7 @@
 
     /*
      * void js_canvas_draw_line(float x1, float y1, float x2, float y2,
-     *                          int width, const char *colour);
+     *                          int width, int colour);
      * 
      * Draw a line. We must adjust the coordinates by 0.5 because
      * Javascript's canvas coordinates appear to be pixel corners,
@@ -375,7 +375,7 @@
      * Postscriptish drawing frameworks).
      */
     js_canvas_draw_line: function(x1, y1, x2, y2, width, colour) {
-        colour = UTF8ToString(colour);
+        colour = colours[colour];
 
         ctx.beginPath();
         ctx.moveTo(x1 + 0.5, y1 + 0.5);
@@ -392,8 +392,7 @@
 
     /*
      * void js_canvas_draw_poly(int *points, int npoints,
-     *                          const char *fillcolour,
-     *                          const char *outlinecolour);
+     *                          int fillcolour, int outlinecolour);
      * 
      * Draw a polygon.
      */
@@ -405,21 +404,20 @@
             ctx.lineTo(getValue(pointptr+8*i  , 'i32') + 0.5,
                        getValue(pointptr+8*i+4, 'i32') + 0.5);
         ctx.closePath();
-        if (fill != 0) {
-            ctx.fillStyle = UTF8ToString(fill);
+        if (fill >= 0) {
+            ctx.fillStyle = colours[fill];
             ctx.fill();
         }
         ctx.lineWidth = '1';
         ctx.lineCap = 'round';
         ctx.lineJoin = 'round';
-        ctx.strokeStyle = UTF8ToString(outline);
+        ctx.strokeStyle = colours[outline];
         ctx.stroke();
     },
 
     /*
      * void js_canvas_draw_circle(int x, int y, int r,
-     *                            const char *fillcolour,
-     *                            const char *outlinecolour);
+     *                            int fillcolour, int outlinecolour);
      * 
      * Draw a circle.
      */
@@ -426,14 +424,14 @@
     js_canvas_draw_circle: function(x, y, r, fill, outline) {
         ctx.beginPath();
         ctx.arc(x + 0.5, y + 0.5, r, 0, 2*Math.PI);
-        if (fill != 0) {
-            ctx.fillStyle = UTF8ToString(fill);
+        if (fill >= 0) {
+            ctx.fillStyle = colours[fill];
             ctx.fill();
         }
         ctx.lineWidth = '1';
         ctx.lineCap = 'round';
         ctx.lineJoin = 'round';
-        ctx.strokeStyle = UTF8ToString(outline);
+        ctx.strokeStyle = colours[outline];
         ctx.stroke();
     },
 
@@ -518,10 +516,10 @@
      * alignment is handled here, since we can get the canvas draw
      * function to do it for us with almost no extra effort.
      */
-    js_canvas_draw_text: function(x, y, halign, colptr, fontsize, monospaced,
+    js_canvas_draw_text: function(x, y, halign, colour, fontsize, monospaced,
                                   text) {
         canvas_set_font(ctx, fontsize, monospaced);
-        ctx.fillStyle = UTF8ToString(colptr);
+        ctx.fillStyle = colours[colour];
         ctx.textAlign = (halign == 0 ? 'left' :
                          halign == 1 ? 'center' : 'right');
         ctx.textBaseline = 'alphabetic';
--- a/emccpre.js
+++ b/emccpre.js
@@ -41,6 +41,10 @@
 // by js_canvas_end_draw.
 var update_xmin, update_xmax, update_ymin, update_ymax;
 
+// Colour strings to use when drawing, to save converting them from C
+// every time.
+var colours = [];
+
 // Module object for Emscripten. We fill in these parameters to ensure
 // that when main() returns nothing will get cleaned up so we remain
 // able to call the puzzle's various callbacks.