From dd9ca556f953e257899811b513c76ad681b1b95c Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Mon, 30 Jun 2025 15:57:40 -0600 Subject: [PATCH 1/4] font/sprite: add sflc supplement circle pieces --- ...ymbols_for_legacy_computing_supplement.zig | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig b/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig index 9f7e8815d..01258b041 100644 --- a/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig +++ b/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig @@ -192,6 +192,60 @@ pub fn draw1CC21_1CC2F( ); } +/// Twelfth and Quarter circle pieces. +/// 饻鞍 饻氨 饻安 饻俺 饻按 饻暗 饻岸 饻胺 饻案 饻肮 饻昂 饻盎 饻凹 饻敖 饻熬 饻翱 +/// +/// 饻鞍饻氨饻安饻俺 +/// 饻按饻暗饻岸饻胺 +/// 饻案饻肮饻昂饻盎 +/// 饻凹饻敖饻熬饻翱 +/// +/// These are actually ellipses, sized to touch +/// the edge of their enclosing set of cells. +pub fn draw1CC30_1CC3F( + cp: u32, + canvas: *font.sprite.Canvas, + width: u32, + height: u32, + metrics: font.Metrics, +) !void { + switch (cp) { + // 饻鞍 UPPER LEFT TWELFTH CIRCLE + 0x1CC30 => try circlePiece(canvas, width, height, metrics, 0, 0, 2, 2), + // 饻氨 UPPER CENTRE LEFT TWELFTH CIRCLE + 0x1CC31 => try circlePiece(canvas, width, height, metrics, 1, 0, 2, 2), + // 饻安 UPPER CENTRE RIGHT TWELFTH CIRCLE + 0x1CC32 => try circlePiece(canvas, width, height, metrics, 2, 0, 2, 2), + // 饻俺 UPPER RIGHT TWELFTH CIRCLE + 0x1CC33 => try circlePiece(canvas, width, height, metrics, 3, 0, 2, 2), + // 饻按 UPPER MIDDLE LEFT TWELFTH CIRCLE + 0x1CC34 => try circlePiece(canvas, width, height, metrics, 0, 1, 2, 2), + // 饻暗 UPPER LEFT QUARTER CIRCLE + 0x1CC35 => try circlePiece(canvas, width, height, metrics, 0, 0, 1, 1), + // 饻岸 UPPER RIGHT QUARTER CIRCLE + 0x1CC36 => try circlePiece(canvas, width, height, metrics, 1, 0, 1, 1), + // 饻胺 UPPER MIDDLE RIGHT TWELFTH CIRCLE + 0x1CC37 => try circlePiece(canvas, width, height, metrics, 3, 1, 2, 2), + // 饻案 LOWER MIDDLE LEFT TWELFTH CIRCLE + 0x1CC38 => try circlePiece(canvas, width, height, metrics, 0, 2, 2, 2), + // 饻肮 LOWER LEFT QUARTER CIRCLE + 0x1CC39 => try circlePiece(canvas, width, height, metrics, 0, 1, 1, 1), + // 饻昂 LOWER RIGHT QUARTER CIRCLE + 0x1CC3A => try circlePiece(canvas, width, height, metrics, 1, 1, 1, 1), + // 饻盎 LOWER MIDDLE RIGHT TWELFTH CIRCLE + 0x1CC3B => try circlePiece(canvas, width, height, metrics, 3, 2, 2, 2), + // 饻凹 LOWER LEFT TWELFTH CIRCLE + 0x1CC3C => try circlePiece(canvas, width, height, metrics, 0, 3, 2, 2), + // 饻敖 LOWER CENTRE LEFT TWELFTH CIRCLE + 0x1CC3D => try circlePiece(canvas, width, height, metrics, 1, 3, 2, 2), + // 饻熬 LOWER CENTRE RIGHT TWELFTH CIRCLE + 0x1CC3E => try circlePiece(canvas, width, height, metrics, 2, 3, 2, 2), + // 饻翱 LOWER RIGHT TWELFTH CIRCLE + 0x1CC3F => try circlePiece(canvas, width, height, metrics, 3, 3, 2, 2), + else => unreachable, + } +} + /// Separated Block Sextants pub fn draw1CE51_1CE8F( cp: u32, @@ -271,3 +325,93 @@ pub fn draw1CE51_1CE8F( .on, ); } + +fn circlePiece( + canvas: *font.sprite.Canvas, + width: u32, + height: u32, + metrics: font.Metrics, + x: f64, + y: f64, + w: f64, + h: f64, +) !void { + // Radius in pixels of the arc we need to draw. + const wdth: f64 = @as(f64, @floatFromInt(width)) * w; + const hght: f64 = @as(f64, @floatFromInt(height)) * h; + + // Position in pixels (rather than cells) for x/y + const xp: f64 = @as(f64, @floatFromInt(width)) * x; + const yp: f64 = @as(f64, @floatFromInt(height)) * y; + + // Set the clip so we don't include anything outside of the cell. + canvas.clip_left = canvas.padding_x; + canvas.clip_right = canvas.padding_x; + canvas.clip_top = canvas.padding_y; + canvas.clip_bottom = canvas.padding_y; + + // Coefficient for approximating a circular arc. + const c: f64 = (std.math.sqrt2 - 1.0) * 4.0 / 3.0; + const cw = c * wdth; + const ch = c * hght; + + const thick: f64 = @floatFromInt(metrics.box_thickness); + const ht = thick * 0.5; + + var path = canvas.staticPath(2); + + if (xp < wdth) { + if (yp < hght) { + // Upper left arc. + path.moveTo(wdth - xp, ht - yp); + path.curveTo( + wdth - cw - xp, + ht - yp, + ht - xp, + hght - ch - yp, + ht - xp, + hght - yp, + ); + } else { + // Lower left arc. + path.moveTo(ht - xp, hght - yp); + path.curveTo( + ht - xp, + hght + ch - yp, + wdth - cw - xp, + hght * 2 - ht - yp, + wdth - xp, + hght * 2 - ht - yp, + ); + } + } else { + if (yp < hght) { + // Upper right arc. + path.moveTo(wdth - xp, ht - yp); + path.curveTo( + wdth + cw - xp, + ht - yp, + wdth * 2 - ht - xp, + hght - ch - yp, + wdth * 2 - ht - xp, + hght - yp, + ); + } else { + // Lower right arc. + path.moveTo(wdth * 2 - ht - xp, hght - yp); + path.curveTo( + wdth * 2 - ht - xp, + hght + ch - yp, + wdth + cw - xp, + hght * 2 - ht - yp, + wdth - xp, + hght * 2 - ht - yp, + ); + } + } + + try canvas.strokePath(path.wrapped_path, .{ + .line_cap_mode = .butt, + .line_width = @floatFromInt(metrics.box_thickness), + }, .on); +} From 0414e9e2819dfa08bf47a9aca8c3cd8ea5d7eafe Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Mon, 30 Jun 2025 16:19:15 -0600 Subject: [PATCH 2/4] font/sprite: add (some) sflc supplement box drawing chars --- ...ymbols_for_legacy_computing_supplement.zig | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig b/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig index 01258b041..9ae92cc72 100644 --- a/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig +++ b/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig @@ -61,6 +61,8 @@ const xHalfs = common.xHalfs; const yQuads = common.yQuads; const rect = common.rect; +const box = @import("box.zig"); + const font = @import("../../main.zig"); const octant_min = 0x1cd00; @@ -246,6 +248,81 @@ pub fn draw1CC30_1CC3F( } } +/// TODO: These two characters should be easy, but it's not clear how they're +/// meant to align with adjacent cells, what characters they're meant to +/// be used with: +/// - 1CC1F 饻盁 BOX DRAWINGS DOUBLE DIAGONAL UPPER RIGHT TO LOWER LEFT +/// - 1CC20 饻盃 BOX DRAWINGS DOUBLE DIAGONAL UPPER LEFT TO LOWER RIGHT +pub fn draw1CC1B_1CC1E( + cp: u32, + canvas: *font.sprite.Canvas, + width: u32, + height: u32, + metrics: font.Metrics, +) !void { + const w: i32 = @intCast(width); + const h: i32 = @intCast(height); + const t: i32 = @intCast(metrics.box_thickness); + switch (cp) { + // 饻皼 BOX DRAWINGS LIGHT HORIZONTAL AND UPPER RIGHT + 0x1CC1B => { + box.linesChar(metrics, canvas, .{ .left = .light, .right = .light }); + canvas.box(w - t, 0, w, @divFloor(h, 2), .on); + }, + // 饻皽 BOX DRAWINGS LIGHT HORIZONTAL AND LOWER RIGHT + 0x1CC1C => { + box.linesChar(metrics, canvas, .{ .left = .light, .right = .light }); + canvas.box(w - t, @divFloor(h, 2), w, h, .on); + }, + // 饻皾 BOX DRAWINGS LIGHT TOP AND UPPER LEFT + 0x1CC1D => { + canvas.box(0, 0, w, t, .on); + canvas.box(0, 0, t, @divFloor(h, 2), .on); + }, + // 饻盀 BOX DRAWINGS LIGHT BOTTOM AND LOWER LEFT + 0x1CC1E => { + canvas.box(0, h - t, w, h, .on); + canvas.box(0, @divFloor(h, 2), t, h, .on); + }, + else => unreachable, + } +} + +pub fn draw1CE16_1CE19( + cp: u32, + canvas: *font.sprite.Canvas, + width: u32, + height: u32, + metrics: font.Metrics, +) !void { + const w: i32 = @intCast(width); + const h: i32 = @intCast(height); + const t: i32 = @intCast(metrics.box_thickness); + switch (cp) { + // 饻笘 BOX DRAWINGS LIGHT VERTICAL AND TOP RIGHT + 0x1CE16 => { + box.linesChar(metrics, canvas, .{ .up = .light, .down = .light }); + canvas.box(@divFloor(w, 2), 0, w, t, .on); + }, + // 饻笚 BOX DRAWINGS LIGHT VERTICAL AND BOTTOM RIGHT + 0x1CE17 => { + box.linesChar(metrics, canvas, .{ .up = .light, .down = .light }); + canvas.box(@divFloor(w, 2), h - t, w, h, .on); + }, + // 饻笜 BOX DRAWINGS LIGHT VERTICAL AND TOP LEFT + 0x1CE18 => { + box.linesChar(metrics, canvas, .{ .up = .light, .down = .light }); + canvas.box(0, 0, @divFloor(w, 2), t, .on); + }, + // 饻笝 BOX DRAWINGS LIGHT VERTICAL AND BOTTOM LEFT + 0x1CE19 => { + box.linesChar(metrics, canvas, .{ .up = .light, .down = .light }); + canvas.box(0, h - t, @divFloor(w, 2), h, .on); + }, + else => unreachable, + } +} + /// Separated Block Sextants pub fn draw1CE51_1CE8F( cp: u32, From b4d83e6349e34022f73ef550cfccaf8f361a3533 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Mon, 30 Jun 2025 18:46:49 -0600 Subject: [PATCH 3/4] font/sprite: align quadrants better with other glyphs Use `xHalfs` and `yHalfs` so that the dimensions of each quadrant are appropriately aligned with block elements like the one half block, which could be 1px taller than the bottom quadrants before this change. This is in line with what we do for sextants, the fact that on odd-sized cells there's a 1px overlap is considered acceptable there so I assume it's acceptable here too. --- src/font/sprite/draw/block.zig | 14 ++++++++------ src/font/sprite/draw/common.zig | 8 ++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/font/sprite/draw/block.zig b/src/font/sprite/draw/block.zig index 27c6ae516..f7faacea7 100644 --- a/src/font/sprite/draw/block.zig +++ b/src/font/sprite/draw/block.zig @@ -15,6 +15,8 @@ const common = @import("common.zig"); const Shade = common.Shade; const Quads = common.Quads; const Alignment = common.Alignment; +const xHalfs = common.xHalfs; +const yHalfs = common.yHalfs; const rect = common.rect; const font = @import("../../main.zig"); @@ -174,11 +176,11 @@ fn quadrant( canvas: *font.sprite.Canvas, comptime quads: Quads, ) void { - const center_x = metrics.cell_width / 2 + metrics.cell_width % 2; - const center_y = metrics.cell_height / 2 + metrics.cell_height % 2; + const x_halfs = xHalfs(metrics); + const y_halfs = yHalfs(metrics); - if (quads.tl) rect(metrics, canvas, 0, 0, center_x, center_y); - if (quads.tr) rect(metrics, canvas, center_x, 0, metrics.cell_width, center_y); - if (quads.bl) rect(metrics, canvas, 0, center_y, center_x, metrics.cell_height); - if (quads.br) rect(metrics, canvas, center_x, center_y, metrics.cell_width, metrics.cell_height); + if (quads.tl) rect(metrics, canvas, 0, 0, x_halfs[0], y_halfs[0]); + if (quads.tr) rect(metrics, canvas, x_halfs[1], 0, metrics.cell_width, y_halfs[0]); + if (quads.bl) rect(metrics, canvas, 0, y_halfs[1], x_halfs[0], metrics.cell_height); + if (quads.br) rect(metrics, canvas, x_halfs[1], y_halfs[1], metrics.cell_width, metrics.cell_height); } diff --git a/src/font/sprite/draw/common.zig b/src/font/sprite/draw/common.zig index 2f608180e..d10128cdf 100644 --- a/src/font/sprite/draw/common.zig +++ b/src/font/sprite/draw/common.zig @@ -204,6 +204,14 @@ pub fn xHalfs(metrics: font.Metrics) [2]u32 { return .{ half_width, metrics.cell_width - half_width }; } +/// yHalfs[0] should be used as the bottom edge of a top-aligned half. +/// yHalfs[1] should be used as the top edge of a bottom-aligned half. +pub fn yHalfs(metrics: font.Metrics) [2]u32 { + const float_height: f64 = @floatFromInt(metrics.cell_height); + const half_height: u32 = @intFromFloat(@round(0.5 * float_height)); + return .{ half_height, metrics.cell_height - half_height }; +} + /// Use these values as such: /// yThirds[0] bottom edge of the first third. /// yThirds[1] top edge of the second third. From adace942d014072470ba170d036be53f38f153a7 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Tue, 1 Jul 2025 13:20:10 -0600 Subject: [PATCH 4/4] font/sprite: update reference images --- .../testdata/U+1CC00...U+1CCFF-11x21+2.png | Bin 402 -> 1032 bytes .../testdata/U+1CC00...U+1CCFF-12x24+3.png | Bin 534 -> 1295 bytes .../testdata/U+1CC00...U+1CCFF-18x36+4.png | Bin 1025 -> 2193 bytes .../testdata/U+1CC00...U+1CCFF-9x17+1.png | Bin 316 -> 794 bytes .../testdata/U+1CE00...U+1CEFF-11x21+2.png | Bin 562 -> 632 bytes .../testdata/U+1CE00...U+1CEFF-12x24+3.png | Bin 741 -> 819 bytes .../testdata/U+1CE00...U+1CEFF-18x36+4.png | Bin 1388 -> 1492 bytes .../testdata/U+1CE00...U+1CEFF-9x17+1.png | Bin 399 -> 443 bytes .../testdata/U+2500...U+25FF-11x21+2.png | Bin 2220 -> 2225 bytes .../testdata/U+2500...U+25FF-9x17+1.png | Bin 1844 -> 1853 bytes 10 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png b/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png index 581b0bbf0547c2526878bb36c8ecbb8277689956..e04e7726b965fab41da5ed0c4f7e3501709db29f 100644 GIT binary patch literal 1032 zcmeAS@N?(olHy`uVBq!ia0y~yVEDkm!1#fKfq{Wxujk%h3=GWYJzX3_D(1Yoq0M*L zK%&L*{{Ksw+ADUu)~#4AIW6?S|Aiis%pm0;ELJ^l{o~uMlfMbc92aVO9N|K#OMd;^-$%bI|M2D0PiB8XWa(s1dk42l1BL^3`V0&V|Np<4oxOIcfxz*?1{M_s zMote0CLyhdV3Lv3V@1nNo%P=7%hEH|e>dLwufOwT<2Hl$ex7>&-mH*)QCj?e;;W$h zJHAbIa(>veb-jobSNfXTr56G$lHY2#nrZSJ$Zu11ySDTxr;hGsJzs}JjXC?a*f4TA zCu;DtGJ<@-@I!vf;`r^?CWqN?i&x__>8?JTe)GECd^etRCU5K`vS+_45xy+N_5YHh z+Jq@j7k&P?Klq29<=097*~{Z!|9FwAIQ!65r`jk+Pk-;CHS;o|o zUZE@>8^2nLOH*Cr%F7Nprb@$W6gI*+cJ`BX|N&@*e^(Zuc>f{R(xHHik2J5>r`f({6Bfpu41mhkdHo7uLV5x?<$-2_^E64tW15= zHxI>C_d2J(i1N8RX`%P;g$u0L@YJqcDR1`WVa%d6|LzxkGMHPzqps_Bjq`lbwk>lr zPoKZkw8nVP!`F?z4?nF}<&uogPx$nZdwtzyqx0?P0hnw*@sc5b`msu2JtJEK-@~g2X&s{an^L HB{Ts5rQoPc literal 402 zcmeAS@N?(olHy`uVBq!ia0y~yVEDkm!1#fKfq{Wxujk%h3=E8so-U3d6?5JkFl1z4 zU^ukktA8$+y#`1D65b)fz`*eT|C`Gjxf&dJm@oYQ|H^8ItX7NP!S4JZH3yXwB zB({RAgjmyXh81kXVMa~{28Mv}eGzaNYY4FCVXx#8HxoG9UX@$dUx?6#b$m4CWoRu*SO@yasKCHbvC5tS?5vs56So)$v|eWva$^#j(7>Y7!=j>~2xhrBsm$3wqp{%S zl9JPd`|`aNpYt4DYR?~tA@UjAZcgO0Ss>keEdvPxHGK1--v^V?Y7yE;O&{iut|8nq+qo)o$tTf6>Oji?Zt z;k&bGipTYtOx0(<63SHW_piEfxla4`Rl)-eIjv zwq;5w6!2Fc517ScTFbM&=#=n#KGt1mC4<{JAc=xNCbboc;sk0 zQ*XCYm-*wTyJY|B7Hz**>-g$S&G*w9a~9fifB9wWqrN+&PT0fZz{IbL57wu7Y${yv zv)%RcUgND@F59Mke08h()HBgGWx)rB1(whCEqr}gp(s$7Icavs#5k+Rd38q?24!xa z^!~}M-9N9kh%qlp%&sh$TiO=($G~$Z^XV6V{>fE#I4`{<@ze8RiH_R-;`#t8=_7C<;H#J1fbXu9? zR1tRUqL$7}(Oo56`90~a4ofdBiCY=6yu`aQ#4AiI?=|O=;6MH!9wfJ1dM;?&SYjuy z`txV;v_y$EY-nOVBq1L*;ryuC9G(s{5{Knt^`sZ9A@MMyZ8Tk=}Aqf&V9oIF$AvSw;&g&0E2i2M1Bwk vd+6=qMji$Rh64)queD_sY-0gQ!SFnW_a%=Gr&g^8S;OGz>gTe~DWM4fdu|#f literal 534 zcmeAS@N?(olHy`uVBq!ia0y~yU{qjWU~=GKU|?Y2`0`{n0|Vn-PZ!6KiaBo%7&0<2 zFdSO&)jyZhUjw873IC8}U|{(F|IOt*8nL%!aF1x7#RNle{)fh>i`1}%fbKqt*$Or)5=L?d!)p74WtpGc)zLf z=HFGjZ?rkoG9Bd<+0I|Gv5mj+5lfe_hTj4U7-Pq42Y!foHx6<(7zi*N{I`1>Q?uYv z)0vHXn2sO(cW$;yz#Iq5=k4b|o=@j~@3vrvb>p$iad8#qOu9}8bKnL-8SknV++^17 z7XaG@anO%OX0XF=8gdGjoSiHkLOhrGfx^1`chmjD4!!Qbv{~JlMJPi{#AAUj6Q_wN zm&ygPMwVGztU?*x4otqSOq^R>6&ja0HL@`6`^*G(VpQP7#|AvD0vxRZ9Bly{Z2}yP z5K@4nErDZK=j<(INw%kR&E?IjjLkRSjq39EzchL7)q1X0r-cC;BIwkk`Uc76Pya5O zlQGl%^1D>EMU478c_)AW&HjG*!?x@EE+)(OKZt+f9sl*T?Hd01Dtb?6yi&9}yko+D z)x)2I3)af44KsLPUS8LCSnT+MSkHj}zwO^g*nZ1ZOH*4UE}eoukl2am_wRjgjj_krbXdsK2l-zv76 z^_=pr^qz9`s{XuT6?yU5zOQxydp}?PDY9+iJK;uWAC`TmzqU)ThCSb>_M&XrV$%hp zTZG&c$pKMMSSE}Uj_Inew|Heq$g((`BQYtPL~4vwmL_dMM!hv`x#$`y0&Dr+sglON3}av`GW^7~^f>L(UNdo;;rnyTyocl(r@xIr^tr~A#1w`Te3S_!Y|IdQ=D61=ejD_8_hzeW$*u#X$(Gf z{M$Eo)sn;PpPtY9@b>S|S!aDF&;Ie8dmYEYttoM{95P*s&FoW)Up=W0cJebwJ|8c? z+v;Bc=ef>xc5!w;G@eWmd6!)FU~>As z*YIcQf|lz7HP@%iFuwidjQGup4nuB{7y0rfp%2!1wcL63Ug&r4e%-65qt3go{jy`b z|B{FDkn-fV@2bNFJgyJ_zW>$O8~>{1hTaP~D+_rMtC##00^YlJ#dW(MR{g!?AcwtnI3KPr*){&0)OL@;uS9@(7GXul*{EP|UYOY>}#?k2^%Umnkz z;S>B#?MBMGN4dp$JO_GA-xVd#(zC00#Flv`;ls~|AD2~KxOvol`yGv0l56dcEMoGt z`*Wbxaz??a-LE$IPVVO~WIu9bcUacrEML0@b~dhw4Nrn{A58At&@?edC27yk%~L04 zsIN z^sF#!0<{7{#X(JBo zL8uaVm~hK1L=BS$VW<+gYteoDK?Gd|x{nV8qpQI1u|G6S;NHRTG29xslH7-^sNVU& pffj5SiC_a*#oNPh&mcKfhUKs3#kQ#8;Ax;p@^tlcS?83{1OT$=XSM(U literal 1025 zcmeAS@N?(olHy`uVBq!ia0y~yU|hh!z?{Irz`(%Zuk~1hfr0sir;B4q#hf<>3>g_1 z7!ED?>YvN$uK`kkgm*|XFfjc8|K@Nb4+8_k0fqY4+A<5au^_2nU|@K_4N+sr%fP_U zu;Aa?T3$wJCR8;Bj9@ia9JvlTFdV(`dw!I7)9$86KQ4Y?x?2);NPUYQ!~MtK9;|hr zXC7BE^SQBqUclMO(jlLpuwAv;>1awkm!YoXb^iA^C%r2E23u7dIV&_Sb82L{r8L1I zc%cJRuAs+)l>rKk+c;D%glJ4~h-MMW&=T=bc(8vbGuVNL;hxZa$cgTWIZO~W@W7Cs zgAo`O5RV>ao@ v;}~Iczz^(T>7$DiM0%P64Y=G6K44(Ti2ihBxt;q0Q1bC~^>bP0l+XkK4z3?y diff --git a/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png b/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png index ecdb2ce10121405cfa22222a848c0ce9c99f775b..459822e6359f512fa3654227681071c1847c59e0 100644 GIT binary patch delta 684 zcmdnPG>dJ5ay`>SPZ!6KiaBqtUu0u86ll9x`+s_#=FSe^&P>(%H9M^0alJL=LwX2o3G4Zapmuq zZRYdOKTP$T*BR#*@T;il(sXTB$7Sz+Tm1}>d{ZYgnP0W3tG>Bu6^khQbXLX;E?%jeBTUvj1@E^uC~&*^JTmHlA%ZefIx}%=GpDn+oPFPAfOiD?}=&+nH*y_N5MU1Y|9`n|$>g93Jnn7Zrxi^-z*?fq00#|u z)&Kui?!W)|lh4;8uC4mZ1AZ3;D!wSWbN#d;r(FNzq7DraHc>B45w_!8j0TSw85sWm ze{-0TlYxPuVZs0P(vzB);AVjtOgyZUmoX_ae-LJve3D5q_<%nHM2{gaSkJ$;YacR7 qqiOoX0#fr9VeV}~4Gjhc27kua-E|X0xmT|SneFN7=d#Wzp$Py^zD=P3 diff --git a/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png b/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png index ed0e8381614585f5bbb2eac395badc97154b2740..03305c81c24482574aa134051787ecb75293f0ab 100644 GIT binary patch literal 632 zcmeAS@N?(olHy`uVBq!ia0y~yVEDkm!1#fKfq{Wxujk%h3=B;6o-U3d6?5L)Fyv}5 z5OFzp=l`Xs2#$?Wj)^~w?@kh_%)Q10Qq91?kg(@*(fPM2yZ^j@ywAYk$KN$77Bi-@ zuy3C9AR$ah=8RWUbEId1fi@>!n(D!WYrc4J**R$Sayupgjp0!7> zaJ$HtFh1Ra3(=NeGz{*4V+PxIb0Zh4B9F_(|NmcK*K2a>={RgHzHNq5qFzL5`}_BG zTLsi?`sebtM&yRqbrN?a(L2hx6h@W@u`@$nPPh4u@-aK(Br+8YU z{@>e|?zKIfIxUI&P|Nd;n;SpqCO)}PIj8gERJj&T`L6X}cBu0(Ffc4Q`Rmh`ef+BD z+@`6;%;?qe-C@98^D=(wH#45~TSTHE76&htX8^nN(nd}O10Lpsf484wQ*`NVeQ#A- z_u+8Q8t!F|pX7RrPsuj1a{uFD133zYA3TC+J#5Ge3WEjz*7iPRmPQNq+30F~AF^Xt f^A=$avYG;J=a&pCSAR4-2C~=F)z4*}Q$iB}pm_D^ literal 562 zcmeAS@N?(olHy`uVBq!ia0y~yVEDkm!1#fKfq{Wxujk%h3=E7vJzX3_D(1X7V93b8 zz;I~6SN~itdkv5RB)mg{fq~)w|2Kylc^DWN4k*;W)|OeYjRi>s0|P?>7g)_rN3IqF z9@Y!L=UZ)_;j5|Dvf#%@TY;V#llbo*Wd`f`kQ!S0@#FuZAO0EnPcAT-S1GrP?YPLj z`t7_er%rO-*3F%&%*0(2=jSKzW90#mjy-{ob{)xEc~RPO%Yoi|ClI?nj8 z&|>Lv+eDDL?l%*hEbUB3Mj-O*Yw7q4b z^YcQrW16-c!geCdzwA&41;T=p|2}Qm&#!vUZJJulj9wkz9R}PsFXN}~%dOaZDJaJg zZ1JD|5By+9URub@z`((L@b7lFM+di@oO}4A=jGR2#&-~P$aImTWAjmy%ynzj@ z>o6lH0|P_Dg8%D(3vz*C5w3uNA%_K`#E=)P)~ zA}m}?tc^_$jtdk5DnwtfFfjc8|K`RBM1I&=X=TAH07IY1;gq;yy^i02=xzt z{yENJJ1PCx+`2YXa z&GyqkzyJTgJlS6SifilBE23GaS889G7974pclo-2 zXudsIX~tT?02Pzu3>ab^q)uMMbSymOGuc&WTwV<+mV1B2d&Cz{Vrem=1l-IU4NA*UOa%jQS^ z^RNCNQMY@I;RTqP0yP`IHqP0*2ja$?NN#ME+j8{%hkG0OTra+v_pI78FF^Ms?<&=u zj$2jCS4?}tc2$Uhf#JdWxi&}V*!17IHCa03^-11UU!SmDttx8Hvfb&pRqjXUUG{0` zZ_lfr_I;8mpGm=jMrK}_j0X-ME?rw*kv&N|#Ji|D3&~&>1_p*OTZmH*8}fqV?cdtH zZyBV)K?w>@2o{H`VT8r4^rR+KB?o-am0(8y9acyRf!nn9AtRbi2V%g+yhXUC_aQSv a&2)x<)y^kAcO<)m+~w)&=d#Wzp$PyHGBgVS literal 741 zcmeAS@N?(olHy`uVBq!ia0y~yU{qjWU~=GKU|?Y2`0`{n0|V0IEXM``2YXaZvNjP$t{K^uVki`hPrI21DpB5z~hMrEN#72^g#4=c4gVlsoUyw(x+$hh)8G{1SBjt-~iTq;2J;Jl3R{k zOojq37k}Ts#jH^_gY${-J!bR08eL71uKSI%>djX^eZqCMsHio|a;MYQIWY^P{MH5N zviE)XKE1J#jZI1-BVj>flcK9)i(0clTvi4Kh8+h^1)n+f z`NUde6V$_3Oq;A7a=KCZo89I;{=ff4%!}P-c;SEpGczxbiGjeMjjx$^ex0PfrR*mp zP;VaOJZvD~a`E48KSs_q9jyO^-pf8W;OVZN#GE?m&Vr~((jneO%~_nFAl@={`HZR0 zCq`EnHD}rGblf`c&Vnfaynwap<}0o-ufOs8z^6TLf2aS{zj>^YjnAZDK_fG-3`gCq zubTH}Jz=|wWSVDQfG#M){XRir1QD~o582UUZ9T-H2sKz@-ydDg-nR_s=7>YpFoOKj ru;BlC=}AqfiRge2SjpSN*p$p+ImiB3>g_1 z7!ED?>YvN$uK`kkgm*|XFfjc8|K_40*8v3qhJ*k2TWMZa5fDu|D7oR&x}9I)N*e;N zKYsfB`-ks**(_bc8h#6Q%uMFqGSQ)s=_sd&dcYhQFd1KiwT2Ol=%Wnf@vSnzLc z??Yy3Cb&Dnj5$nbYJ4BE6I8?dkQ-f%3?rI3wY-ezYJ4ERz2eB#Vj$vj@b~8isSE{X z#+)C_U^m`2`KfF{`sxaNAe*7sZo7@AZoh}sW9IPOfVGpQLtZ~& zyK1%5ajRb*j63(W4b(3OIU5WFTrU3Gy^W!ho%eB0#e1uE-kL2>Cs;GdRZW<)c^lkx*eLQH>pRkNbM1^IjD5u!O{nu zVvTJ2to|Ep*gz52up1Jkw+y)s8wjvo{C(dlc22ES_dOa-}&p&|a$ zG1s=~{`2(hcCa4Y{Oly_s>-6qta&jDW~qy>z{X{eIr4S??s6m5J#k*u>d6`&j*FdG zH8aFmH8Vn1D6pPgq`=zCCAvg`L3-vbN44(VzyG@%1YYP>XO}tnT6Oak*Fa%@l6TeDCu~=%ikh=*cRFsJcV|JAe_jAs`Y)0>2Km~`W~{5EiGsnE1ISR@TDCLTNo zrOf~T-ypIQW)|4M3Qk=JC4bK{pydDqh{F#va)LAD|Mk+7nn2MCO?V6p41ZW4hQL+) z7UTjIT?iE)gdstVFyywN25MP#z#pmvp7GZ{M9++GpccWEVCMTj=tTpjpL3udYUE*H zU^t*q|5`a_!8R6zV;LA2DxgZBKKi%*T?d+ delta 918 zcmcb@{f29T3ZwBvRUP@mjXVqt3Qr&SX@Y+{2=z#xRG4f#LuE zH-{NH85kHE7W`j-TTnv-MK!~L$jM(>l$aUjuulA|WWR<9tl^3ySF?dc%f;XGcg2eA z;?`)h|MXFAhv6v$o>xpDla%f3x6A#!zvIKEIq8-=owm-2Ss3NFEOszF z*$QAczLR4BTY1Zni^`R4^pUe7F1FUP=M`1k%?-)&s$^_SjE;ceoG(pBkL5X+&I zv2}t%Q<-ANf~!tV983l$uF0zGtStZkpF^lgZjMX=3#TK$AD=~2O5_D@h)X~b$l!AE z_x)YFl{_3&TYl8$-_-1Jz7VRy_rm!kTfah~gT5cw@eB+M*LD=tKm0Pi{y)FDp8bo; zYm=ozUZ3P$_4Ntc)vBWAEF@>HRX1M&mS!|Fsw;o4wwmj>{L-B#FG7Rdk*mc)#QEUw z`?rK&I2q1$5Iv*S{qKWB{a;R%fG>`XET@D#7QAR?;+&!i33ZMidSAo0?)h_MuKhu! z3rZh?Kq32Z=>wK-hr`|nImH^=rZ)-e^fy8rx}%tJ@;4Sm<^pksiGLN{3m{%SY{&~r zk_-N=)qTh*4N9I6)gW>Mx{|XG_|cRUOwMANJ(-75i1E#21x6*kKj0+!7M5lHt$)|i b1UB@-oJPOiH$17gK?Zxe`njxgN@xNAdv0jS diff --git a/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png b/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png index cf8d5afc7e5294fbbe63568eb4fbcfdba549ca52..a822c4f58a76d4becfb6991ca4974fe3c528e2d2 100644 GIT binary patch literal 443 zcmeAS@N?(olHy`uVBq!ia0y~yV7S1*z&L?}fq{YH*Mp4(3=E7-o-U3d6?5KPH{@zC z;BmNk@BgK)ZUv1UB6bh=u01=+K1~;-m?2@_<0AXGdilwF-fv|!i|76|4iWqVVnHU)U|9^AGkc&ZqgZ0Ai`&JxMYuE}k+P^Cta(LmqHVUE+L~gKl zpTB1E&Up33CGRq8EgKiC{}4CXb*W)ZUrL}-z{Zb}3JeSnb~A#_yWz-nR6&6G!teXP zoYGe{pT7wa8y_?!*J&zJJ<<_4S5+D7#bG*TYL5azcdrb2MAmPapvJh9tH-60}A!8nPV1i wV?oG+STh(v=Da=3$O$&*|N7s8T%efYW;8T8pQ(ObbTLSkr>mdKI;Vst00ecP4FCWD literal 399 zcmeAS@N?(olHy`uVBq!ia0y~yV7S1*z&L?}fq{YH*Mp4(3=E86o-U3d6?5JkFl1z4 zU^ukktA8%1zXnJF61I?LU|{(F|IOu#Yz&G5tQY?Oe--&H*Q=-U#1YFircW;|twAW> z&}Fgq&;Q!$inK2eMVfYAs@3J2n@}ZlbfN3Qv=32=3=9t}Q3Fj;p@NJGX(jH7JteLKzPbz&S032S#wx^#N0zBp@>A6Wmv zxxxfaXtOieJYoX-=dd9!0|P_Df`4m$AF@j`Av^(My+K#Q`;Z%5O${T+oVSM=Il<=q fU%yzM3l!Yk{B6~H{e=PoK?Zrc`njxgN@xNAvxkTA diff --git a/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png b/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png index 00b508dcb221d03ea7ebb416569824a41637229b..9ae5b45ff498b398202e44aa19158721e7bd2b29 100644 GIT binary patch delta 184 zcmZ1@xKVI|3S-4a)kN0%)4WWEJPwXti%q;1n`}{cRuK4Hs8n|EY)7ey`FrU<%LFQ7 zKa{r|ec;dZ&-#OZ%Yg%}54aQyG%K0}Br0YecAli-sox#_RwDPY=B;DJB|Gf-*MCc$ zCnHy#o>?bv@FBS%o|y*(_DJlgee!L)h$gGtkIy^>zmGO3ZhiGKEkQ5=3?5i*Q1P6k j;^|ewxLJ^`ok=9@hsdp;hADjv3=9mOu6{1-oD!MiGO`-N_|qZhyV=z*F(V zz6b9)CLf&7@~8a4e2&IO;e(P+4?;dLDH?n*Yj0QaoODlY`z=$eW6>o&-(P&HkrVsv zHt&sGbb98;vllpcVw!JDC4j+$tq*?n$JyRd3YGU?^Xdi<49sDirs6qC#go~VVY3)p cJCn!)p>0}mN-Y~07#J8lUHx3vIVCg!0QNpiM*si- diff --git a/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png b/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png index ea100d7f117b4c4c506f6b174c9cdc11e9a108ee..cf96b7d15bd266af18ffb33076d8e614b44d4018 100644 GIT binary patch delta 688 zcmdnOx0i2%G7~$?M%8MT`m>xZ1_BKir)*oAn>kI*(&vNv#EZ=f_OVxcL>#MG{_%Im zwgYtw<^C;{TNbd*dlRo`opRv|y~o1NEMMbwJSHeOzUPlR9~-AKe=d*JzuUTwspZyn znMDT*W+^x}v2a#}2{6Dxj5#v{!~g$p?l|%uHsEPZ`)DY_y{;rgNr3+^rYZ*zZpo>^6W z?c478&-dT<&7S9QIj`}XXZZYuS>;bTw)}edW3TH%j_tkY{{6Zz#kn~~**#s~y&|`d zcT;$7LEdjJ>F67uTzmyr$S`m9_y3{Msp_my&%)8(T-^M{!EjpycR{e;%E|2|M^AvuzgsJrPqZvo`l)ThCE?nm!aVT!ux20+SFFSnmkvO!)bMoSu z0a7PInlJLJ?`*%|P|My^XK^xi9@{IP#4m?EpB*+ec+a<3N#1XhwJPuZ@0a-xTj-jz WF>SeA#@`wNie^t&KbLh*2~7aI?k zz1vJCR{C7tZvB%MODfzGJ*F4_iIthgcPd`TV}gQX(=u+U_4P4R?5cmfInDTfIj4|{ z$Akr$O$>1GU_Unl!~g$p?l`VJY`}A@kfSv~ga1%-00#)w3ve_CXfy_J%(gnd@$S5` z8Dif5=Wp@qta@R-dZqs}cDN}}#{KWt^}X78^N%Nan^#r57GEl;lC{m@zmZv0VYKc+ z=P=dC1zL@(Zf9|(MV{Tsq-%BK_EZlhk=SV=jjPy-RJmARss1|qx%O4s;_KJ<{jB@9 zyZ-K$XPhQ)#WLoXe3uM+yH2Jt%wA^ybPb(Dxr;aNw-b39q7Z)K;?2{7akGmX--Jrf zvEFy+pje+xU@(i;bBDK=h3&kOP70jZ&alvOhnR%pW!b3mkH$a zw{tJ@9x~u@x!E8nv2?KklX6N+-vvP{C-;{eJ@o-DlPwA#F&I4zQQ3b!AWBy4j7(EV^Zgy1q;19 zuQBRpoH=~5ej>}jJ=AKnyVL5TZBT?QG zbBR#d7sk5T%N4ImyX_5rw0+;5SgA{WEsAqeAI4~%v**1zlhgTy*}`HGSuf{nR};1z z_I#&k@?;;goAsP0dj8JucoMf9_I!5O)ZjheVp+dUo}P#Q|NSC)*nsae^Ed7cg=fuG QZJ