[util] Add --draw / --paint flags to raster, vector, gpu

Move the force_draw / force_paint flags from gpu-output.hh
into the shared view-options.hh so all rendering utils
expose --draw and --paint with the same semantics: --paint
wins over --draw, otherwise auto-detect via the font's
color tables.

raster and vector previously only ran the paint pipeline
when the font had a color table; they now also honor
--paint (renders mono fonts via paint synthesis) and
--draw (forces the outline path even on color fonts).

Vector's per-glyph try-paint-then-fall-back-to-draw is
replaced with a single mode decision up front.  Each
glyph then goes through hb_vector_{paint,draw}_glyph
(non-_or_fail), which handles the synthesis fallback
internally — color glyphs render in either mode (paint
synthesizes for non-color fonts; draw renders nothing for
color-only glyphs without an outline, which matches user
intent for --draw).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
diff --git a/util/gpu-output.hh b/util/gpu-output.hh
index 2e3cdb8..8576739 100644
--- a/util/gpu-output.hh
+++ b/util/gpu-output.hh
@@ -56,8 +56,6 @@
   hb_bool_t use_d3d11 = false;
   hb_bool_t demo = false;
   hb_bool_t bench = false;
-  hb_bool_t force_draw  = false;  /* --draw   : use monochrome draw path */
-  hb_bool_t force_paint = false;  /* --paint  : use paint path */
   hb_bool_t show_extents = false; /* --show-extents */
   /* Initial state for interactive toggles; each corresponds to
    * a keyboard shortcut in demo_view_t. */
@@ -92,8 +90,8 @@
       {"bench",		0, G_OPTION_FLAG_NO_ARG,
 				G_OPTION_ARG_CALLBACK,	(gpointer) &parse_bench,"Demo in fullscreen benchmark mode",	nullptr},
       {"type",		'T', 0, G_OPTION_ARG_STRING,	&this->type_text,	"Type these keystrokes on start",	"keys"},
-      {"draw",		0, 0, G_OPTION_ARG_NONE,	&this->force_draw,	"Force monochrome draw path",		nullptr},
-      {"paint",		0, 0, G_OPTION_ARG_NONE,	&this->force_paint,	"Force color paint path",		nullptr},
+      {"draw",		0, 0, G_OPTION_ARG_NONE,	&this->view.force_draw,	"Force monochrome draw path",		nullptr},
+      {"paint",		0, 0, G_OPTION_ARG_NONE,	&this->view.force_paint,	"Force color paint path",		nullptr},
       {"output-file",	'o', 0, G_OPTION_ARG_STRING,	&this->output_file,	"Render one frame to PPM file (\"-\" for stdout) and exit","filename"},
       {"show-extents",	0, 0, G_OPTION_ARG_NONE,	&this->show_extents,	"Draw a frame around each glyph's ink extents",	nullptr},
       /* Interactive-toggle defaults.  Each mirrors a keybinding
@@ -153,8 +151,8 @@
      * otherwise auto: fonts without color paint get the draw path
      * (smaller shader, better perf).  A mismatched pair of flags
      * is a user error; prefer paint. */
-    draw_only = force_paint ? false
-	      : force_draw  ? true
+    draw_only = view.force_paint ? false
+	      : view.force_draw  ? true
 	      : !hb_ot_color_has_paint (face);
 
     if (use_metal)
diff --git a/util/raster-output.hh b/util/raster-output.hh
index 4d4eae8..7ada1ad 100644
--- a/util/raster-output.hh
+++ b/util/raster-output.hh
@@ -81,12 +81,18 @@
     subpixel_bits = font_opts->subpixel_bits;
 
     hb_face_t *face = hb_font_get_face (font);
-    has_color = hb_ot_color_has_paint (face) ||
-		hb_ot_color_has_layers (face) ||
+    bool font_has_color = hb_ot_color_has_paint (face) ||
+			  hb_ot_color_has_layers (face) ||
 #ifndef HB_NO_SVG
-		hb_ot_color_has_svg (face) ||
+			  hb_ot_color_has_svg (face) ||
 #endif
-		hb_ot_color_has_png (face);
+			  hb_ot_color_has_png (face);
+    /* --paint wins over --draw; otherwise auto-detect.
+     * hb_raster_paint_glyph synthesizes paint from outlines
+     * for mono fonts, so --paint works even without color. */
+    has_color = force_paint ? true
+	      : force_draw  ? false
+	      : font_has_color;
 
     fg_color = HB_COLOR ((uint8_t) foreground_color.b,
 			 (uint8_t) foreground_color.g,
diff --git a/util/vector-output.hh b/util/vector-output.hh
index 9abb171..10b1013 100644
--- a/util/vector-output.hh
+++ b/util/vector-output.hh
@@ -234,6 +234,21 @@
       foreground_use_palette && foreground_palette && foreground_palette->len;
     unsigned palette_glyph_index = 0;
 
+    /* Pick draw vs paint mode.  --paint wins over --draw;
+     * otherwise auto-detect via the font's color tables.
+     * hb_vector_paint_glyph synthesizes paint from outlines
+     * for mono fonts, so --paint works even without color. */
+    hb_face_t *face = hb_font_get_face (font);
+    bool font_has_color = hb_ot_color_has_paint (face) ||
+			  hb_ot_color_has_layers (face) ||
+#ifndef HB_NO_SVG
+			  hb_ot_color_has_svg (face) ||
+#endif
+			  hb_ot_color_has_png (face);
+    bool use_paint = force_paint ? true
+		   : force_draw  ? false
+		   : font_has_color;
+
     hb_direction_t dir = direction;
     if (dir == HB_DIRECTION_INVALID)
       dir = HB_DIRECTION_LTR;
@@ -256,7 +271,7 @@
         float pen_x = g.x + off_x;
         float pen_y = g.y + off_y;
 
-        if (paint)
+        if (use_paint && paint)
         {
           if (use_foreground_palette)
           {
@@ -267,17 +282,16 @@
           }
 
           hb_vector_paint_set_transform (paint, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f);
-          if (hb_vector_paint_glyph_or_fail (paint, upem_font, g.gid, pen_x, pen_y,
-                                     extents_mode))
-          {
-            had_paint = true;
-            continue;
-          }
+          hb_vector_paint_glyph (paint, upem_font, g.gid, pen_x, pen_y,
+                                 extents_mode);
+          had_paint = true;
         }
-
-        if (hb_vector_draw_glyph_or_fail (draw, upem_font, g.gid, pen_x, pen_y,
-                                  extents_mode))
+        else
+        {
+          hb_vector_draw_glyph (draw, upem_font, g.gid, pen_x, pen_y,
+                                extents_mode);
           had_draw = true;
+        }
       }
     }
 
diff --git a/util/view-options.hh b/util/view-options.hh
index 06dfce7..75d2c16 100644
--- a/util/view-options.hh
+++ b/util/view-options.hh
@@ -242,6 +242,8 @@
   hb_bool_t logical = false;
   hb_bool_t ink = false;
   hb_bool_t show_extents = false;
+  hb_bool_t force_draw = false;   /* --draw  : use mono outline path */
+  hb_bool_t force_paint = false;  /* --paint : use color paint path */
 
   bool parse_custom_palette_entries (GError **error)
   {
@@ -375,6 +377,8 @@
     {"logical",		0, 0, G_OPTION_ARG_NONE,	&this->logical,		"Render to logical box instead of union of logical and ink boxes",	nullptr},
     {"ink",		0, 0, G_OPTION_ARG_NONE,	&this->ink,			"Render to ink box instead of union of logical and ink boxes",	nullptr},
     {"show-extents",	0, 0, G_OPTION_ARG_NONE,	&this->show_extents,		"Draw glyph extents",							nullptr},
+    {"draw",		0, 0, G_OPTION_ARG_NONE,	&this->force_draw,		"Force monochrome draw path (overrides auto-detect)",	nullptr},
+    {"paint",		0, 0, G_OPTION_ARG_NONE,	&this->force_paint,		"Force color paint path (overrides auto-detect)",	nullptr},
     {nullptr}
   };
   parser->add_group (entries,