tui-td 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f09dac0e163cc0b1f2e699d66fe25fd656003b28ad17867a32154073cec8a62
4
- data.tar.gz: a46f622e3a7a89c0451e603e89266575eec49b346af851c8bce393230684c759
3
+ metadata.gz: ebeda13c1510dfd65ec0dd5e45a330ed9100c3dd5a0919dfcedb13340e01a280
4
+ data.tar.gz: fe33d4368ae7b516f18b7cc30974124408214dbd116f6e300986b05a967e4835
5
5
  SHA512:
6
- metadata.gz: adb1c08cd89f21f59e270c73d94f8542ada7949069ebb5c6dcad27dc860264df93f09dea8e8c2053e90f909ca9c5405254e0f51d2afbd6dba194e1b3d293e4d3
7
- data.tar.gz: 70597354fa770012221aa26f03a2ce4198f13ebb8ea66f1bacba20f7be740b1487d31c6908de19036889ae3ab0f60fc4a7cf57d993c5b938453f51618ecb9b28
6
+ metadata.gz: f1c7051f157319f46d96f371654d276ada570e2aea06d4871659cf989734f5c3d65c0da163a23d56ebb844d3c3cb3b5087b6fcc60f8dcebfdbec0d35ab317720
7
+ data.tar.gz: 2f74615955a3434efe4d06427099ae18eca2c43bdefe93b6c8b2465ebb2eb28f84024ecbcfcbda39cedec4e5ac0373141d6f6ec0ac31aff6a64f7cf8b9ebb781
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TUITD
4
+ module CairoRenderer
5
+ CELL_W = 8
6
+ CELL_H = 16
7
+ DEFAULT_FONT = "Arial Unicode MS"
8
+ FONT_SIZE = 12.0
9
+ RENDER_SCALE = 3
10
+
11
+ @available = false
12
+ @cache = {}
13
+
14
+ begin
15
+ require "cairo"
16
+ @available = true
17
+ rescue LoadError
18
+ # Cairo not available; render_glyph_onto is a no-op
19
+ end
20
+
21
+ class << self
22
+ def available?
23
+ @available
24
+ end
25
+
26
+ def render_glyph_onto(cpn_image, px, py, char, fg_rgb, bold:, italic:)
27
+ return unless available?
28
+
29
+ alpha = glyph_alpha(char, bold: bold, italic: italic)
30
+ composite(alpha, cpn_image, px, py, fg_rgb)
31
+ rescue StandardError => e
32
+ warn "CairoRenderer: #{e.message}" if $DEBUG
33
+ end
34
+
35
+ private
36
+
37
+ def glyph_alpha(char, bold:, italic:)
38
+ key = [char.ord, bold, italic]
39
+ @cache[key] ||= render_surface(char, bold, italic)
40
+ end
41
+
42
+ def render_surface(char, bold, italic)
43
+ # Render at higher resolution then box-filter downsample for smoother edges
44
+ scale = RENDER_SCALE
45
+ big_w = CELL_W * scale
46
+ big_h = CELL_H * scale
47
+ surface = Cairo::ImageSurface.new(Cairo::Format::ARGB32, big_w, big_h)
48
+ context = Cairo::Context.new(surface)
49
+
50
+ context.antialias = Cairo::ANTIALIAS_NONE
51
+
52
+ fo = Cairo::FontOptions.new
53
+ fo.hint_style = Cairo::HINT_STYLE_FULL
54
+ fo.hint_metrics = Cairo::HINT_METRICS_ON
55
+ context.font_options = fo
56
+
57
+ slant = italic ? Cairo::FONT_SLANT_ITALIC : Cairo::FONT_SLANT_NORMAL
58
+ weight = bold ? Cairo::FONT_WEIGHT_BOLD : Cairo::FONT_WEIGHT_NORMAL
59
+ context.select_font_face(DEFAULT_FONT, slant, weight)
60
+ context.set_font_size(FONT_SIZE * scale)
61
+
62
+ extents = context.text_extents(char)
63
+ x_off = ((big_w - extents.width) / 2.0) - extents.x_bearing
64
+ y_off = ((big_h - extents.height) / 2.0) - extents.y_bearing
65
+
66
+ context.set_source_rgba(1.0, 1.0, 1.0, 1.0)
67
+ context.move_to(x_off, y_off)
68
+ context.show_text(char)
69
+
70
+ data = surface.data
71
+ stride = surface.stride
72
+ scale_sq = scale * scale
73
+
74
+ alpha_grid = Array.new(CELL_H) { Array.new(CELL_W, 0) }
75
+ CELL_H.times do |dy|
76
+ CELL_W.times do |dx|
77
+ sum = 0
78
+ scale.times do |sy|
79
+ row_off = (dy * scale + sy) * stride
80
+ scale.times do |sx|
81
+ sum += data.getbyte(row_off + (dx * scale + sx) * 4 + 3)
82
+ end
83
+ end
84
+ alpha_grid[dy][dx] = sum / scale_sq
85
+ end
86
+ end
87
+
88
+ alpha_grid
89
+ end
90
+
91
+ def composite(alpha_grid, cpn_image, px, py, fg_rgb)
92
+ fr, fg, fb = fg_rgb
93
+
94
+ CELL_H.times do |dy|
95
+ CELL_W.times do |dx|
96
+ a = alpha_grid[dy][dx]
97
+ next if a < 24
98
+
99
+ factor = [a, 255].min / 255.0
100
+ r = (fr * factor).to_i
101
+ g = (fg * factor).to_i
102
+ b = (fb * factor).to_i
103
+ cpn_image[px + dx, py + dy] = ChunkyPNG::Color.rgb(r, g, b)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "chunky_png"
4
4
  require_relative "ansi_utils"
5
+ require_relative "cairo_renderer"
6
+ require_relative "unifont_glyphs"
5
7
 
6
8
  module TUITD
7
9
  class Screenshot
@@ -356,12 +358,32 @@ module TUITD
356
358
  return
357
359
  end
358
360
 
359
- return if char == " " || char_ord < 32 || char_ord > 126
361
+ return if char == " " || char_ord < 32
360
362
 
361
- rows_data = glyph_rows(char)
362
- return unless rows_data
363
+ # ASCII printable (33-126): use Spleen bitmap font (pixel-perfect)
364
+ if char_ord <= 126
365
+ rows_data = glyph_rows(char)
366
+ if rows_data
367
+ draw_glyph(image, px, py, rows_data, fg_rgb, bold: bold, italic: italic)
368
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
369
+ return
370
+ end
371
+ end
372
+
373
+ # Unicode (127+): try Unifont bitmap first (pixel-perfect like Spleen)
374
+ if char_ord > 126
375
+ unifont_rows = UnifontGlyphs.rows(char_ord)
376
+ if unifont_rows
377
+ draw_glyph(image, px, py, unifont_rows, fg_rgb, bold: bold, italic: italic)
378
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
379
+ return
380
+ end
381
+ end
363
382
 
364
- draw_glyph(image, px, py, rows_data, fg_rgb, bold: bold, italic: italic)
383
+ # Fallback: render via Cairo for characters not in Unifont
384
+ if CairoRenderer.available?
385
+ CairoRenderer.render_glyph_onto(image, px, py, char, fg_rgb, bold: bold, italic: italic)
386
+ end
365
387
 
366
388
  draw_underline(image, px, py, CELL_W, fg_rgb) if underline
367
389
  end