skrift 0.1.0 → 0.4.0
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 +4 -4
- data/Rakefile +5 -0
- data/examples/banner.rb +96 -0
- data/examples/banner_boxdrawing.rb +139 -0
- data/examples/banner_shadow.rb +207 -0
- data/{test.rb → examples/glyph_dump.rb} +2 -2
- data/lib/skrift/font.rb +381 -293
- data/lib/skrift/font_set.rb +58 -0
- data/lib/skrift/glyph_cache.rb +142 -0
- data/lib/skrift/outline.rb +42 -45
- data/lib/skrift/raster.rb +74 -65
- data/lib/skrift/sft.rb +84 -70
- data/lib/skrift/version.rb +1 -1
- data/lib/skrift.rb +45 -11
- metadata +11 -7
- data/skrift.gemspec +0 -39
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9194573a4c3814c4c48eb0f026274a10f43856fc4c480d7d0631967d7b971fcd
|
|
4
|
+
data.tar.gz: 42646ed388052afa1095fdf4ae4fc700630d5e44a5174f576ace7abed88d0fa5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 506d51fdfca628d4ce5ed43cd1d84bbf6b3d828ab12673e94ac68c540155af8f7cf17f23558e603d07bf18b448a2cb86e1f3b6517f2fb5922e4159d35fdc8676
|
|
7
|
+
data.tar.gz: cb719b47e60d4c9335bfb218c66a9900f9833375b20f17687fdb466390af21e76fe435d8ea90470a04d3ef88c57c637816e29d346a8d56227bf2b368c05978bb
|
data/Rakefile
CHANGED
data/examples/banner.rb
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Bypass bundler
|
|
2
|
+
ENV['BUNDLE_GEMFILE'] = nil
|
|
3
|
+
|
|
4
|
+
require 'pp'
|
|
5
|
+
require_relative '../lib/skrift'
|
|
6
|
+
|
|
7
|
+
# Function to render a string as a text banner
|
|
8
|
+
def render_text_banner(text, font_path = "resources/Ubuntu-Regular.ttf", scale = 20)
|
|
9
|
+
# Load the font
|
|
10
|
+
f = Font.load(font_path)
|
|
11
|
+
|
|
12
|
+
# Create a new SFT instance
|
|
13
|
+
sft = SFT.new(f)
|
|
14
|
+
sft.x_scale = scale
|
|
15
|
+
sft.y_scale = scale
|
|
16
|
+
sft.x_offset = 0
|
|
17
|
+
sft.y_offset = 0
|
|
18
|
+
|
|
19
|
+
# Get font metrics
|
|
20
|
+
metrics = sft.lmetrics
|
|
21
|
+
|
|
22
|
+
# Calculate the maximum height needed for any character
|
|
23
|
+
max_height = metrics.ascender - metrics.descender + metrics.line_gap
|
|
24
|
+
|
|
25
|
+
# Array to store each row of pixels for the banner
|
|
26
|
+
banner_rows = []
|
|
27
|
+
|
|
28
|
+
# Initialize the banner with empty rows
|
|
29
|
+
max_height.to_i.times do
|
|
30
|
+
banner_rows << []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Process each character in the text
|
|
34
|
+
text.each_char do |char|
|
|
35
|
+
# Get the glyph ID for this character
|
|
36
|
+
gid = sft.lookup(char.ord)
|
|
37
|
+
|
|
38
|
+
# Skip if glyph not found
|
|
39
|
+
next if gid.nil?
|
|
40
|
+
|
|
41
|
+
# Get metrics for this glyph
|
|
42
|
+
gmetrics = sft.gmetrics(gid)
|
|
43
|
+
|
|
44
|
+
# Create an image for this glyph
|
|
45
|
+
img = Image.new(gmetrics.min_width, gmetrics.min_height)
|
|
46
|
+
|
|
47
|
+
# Render the glyph
|
|
48
|
+
if sft.render(gid, img)
|
|
49
|
+
# Calculate vertical offset based on metrics
|
|
50
|
+
y_offset = metrics.ascender - gmetrics.y_offset
|
|
51
|
+
|
|
52
|
+
# Calculate character width including spacing
|
|
53
|
+
char_width = img.width + 2 # Width plus spacing
|
|
54
|
+
|
|
55
|
+
# First, add empty space for all rows in the banner for this character
|
|
56
|
+
banner_rows.each do |row|
|
|
57
|
+
row.concat([0] * char_width)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Then, overwrite with actual character data where it exists
|
|
61
|
+
img.height.times do |row|
|
|
62
|
+
row_in_banner = row + y_offset.to_i
|
|
63
|
+
|
|
64
|
+
# Skip rows outside the banner bounds
|
|
65
|
+
next if row_in_banner < 0 || row_in_banner >= banner_rows.length
|
|
66
|
+
|
|
67
|
+
# Get pixel values for this row of the character
|
|
68
|
+
row_pixels = img.pixels[row * img.width...(row + 1) * img.width]
|
|
69
|
+
|
|
70
|
+
# Calculate the position to start writing pixels
|
|
71
|
+
start_pos = banner_rows[row_in_banner].length - char_width
|
|
72
|
+
|
|
73
|
+
# Overwrite the empty space with actual character data
|
|
74
|
+
row_pixels.each_with_index do |pixel, i|
|
|
75
|
+
banner_rows[row_in_banner][start_pos + i] = pixel
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Print the banner
|
|
82
|
+
banner_rows.each do |row|
|
|
83
|
+
row.each do |pixel|
|
|
84
|
+
# Use ANSI color codes to display grayscale values
|
|
85
|
+
print "\33[32;48;2;#{pixel};#{pixel};#{pixel}m "
|
|
86
|
+
end
|
|
87
|
+
puts "\33[39;49m"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Get text from command line or use default
|
|
92
|
+
text = ARGV[0] || "Hello!"
|
|
93
|
+
scale = ARGV[1] ? ARGV[1].to_i : 20
|
|
94
|
+
|
|
95
|
+
puts "Rendering: '#{text}' with scale #{scale}"
|
|
96
|
+
render_text_banner(text, "resources/Ubuntu-Regular.ttf", scale)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Bypass bundler
|
|
2
|
+
ENV['BUNDLE_GEMFILE'] = nil
|
|
3
|
+
|
|
4
|
+
require 'pp'
|
|
5
|
+
require_relative '../lib/skrift'
|
|
6
|
+
|
|
7
|
+
# Function to render a string as a text banner
|
|
8
|
+
def render_text_banner(text, font_path = "resources/FiraGO-Regular.ttf", scale = 16, threshold = 30)
|
|
9
|
+
# Load the font
|
|
10
|
+
f = Font.load(font_path)
|
|
11
|
+
|
|
12
|
+
# Create a new SFT instance
|
|
13
|
+
sft = SFT.new(f)
|
|
14
|
+
sft.x_scale = scale
|
|
15
|
+
sft.y_scale = scale
|
|
16
|
+
sft.x_offset = 0
|
|
17
|
+
sft.y_offset = 0
|
|
18
|
+
|
|
19
|
+
# Get font metrics
|
|
20
|
+
metrics = sft.lmetrics
|
|
21
|
+
|
|
22
|
+
# Calculate the maximum height needed for any character
|
|
23
|
+
max_height = metrics.ascender - metrics.descender + metrics.line_gap
|
|
24
|
+
|
|
25
|
+
# Array to store each row of pixels for the banner
|
|
26
|
+
banner_rows = []
|
|
27
|
+
|
|
28
|
+
# Initialize the banner with empty rows
|
|
29
|
+
max_height.to_i.times do
|
|
30
|
+
banner_rows << []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Process each character in the text
|
|
34
|
+
text.each_char do |char|
|
|
35
|
+
# Get the glyph ID for this character
|
|
36
|
+
gid = sft.lookup(char.ord)
|
|
37
|
+
|
|
38
|
+
# Skip if glyph not found
|
|
39
|
+
next if gid.nil?
|
|
40
|
+
|
|
41
|
+
# Get metrics for this glyph
|
|
42
|
+
gmetrics = sft.gmetrics(gid)
|
|
43
|
+
|
|
44
|
+
# Create an image for this glyph
|
|
45
|
+
img = Image.new(gmetrics.min_width, gmetrics.min_height)
|
|
46
|
+
|
|
47
|
+
# Render the glyph
|
|
48
|
+
if sft.render(gid, img)
|
|
49
|
+
# Calculate vertical offset based on metrics
|
|
50
|
+
y_offset = metrics.ascender - gmetrics.y_offset
|
|
51
|
+
|
|
52
|
+
# Calculate character width including spacing
|
|
53
|
+
char_width = img.width + 2 # Width plus spacing
|
|
54
|
+
|
|
55
|
+
# First, add empty space for all rows in the banner for this character
|
|
56
|
+
banner_rows.each do |row|
|
|
57
|
+
row.concat([0] * char_width)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Then, overwrite with actual character data where it exists
|
|
61
|
+
img.height.times do |row|
|
|
62
|
+
row_in_banner = row + y_offset.to_i
|
|
63
|
+
|
|
64
|
+
# Skip rows outside the banner bounds
|
|
65
|
+
next if row_in_banner < 0 || row_in_banner >= banner_rows.length
|
|
66
|
+
|
|
67
|
+
# Get pixel values for this row of the character
|
|
68
|
+
row_pixels = img.pixels[row * img.width...(row + 1) * img.width]
|
|
69
|
+
|
|
70
|
+
# Calculate the position to start writing pixels
|
|
71
|
+
start_pos = banner_rows[row_in_banner].length - char_width
|
|
72
|
+
|
|
73
|
+
# Overwrite the empty space with actual character data
|
|
74
|
+
row_pixels.each_with_index do |pixel, i|
|
|
75
|
+
banner_rows[row_in_banner][start_pos + i] = pixel
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Print the banner
|
|
82
|
+
# Process banner for box drawing characters (look at 2x2 blocks)
|
|
83
|
+
(0...banner_rows.length-1).step(2) do |y|
|
|
84
|
+
row_str = ""
|
|
85
|
+
(0...banner_rows[y].length-1).step(2) do |x|
|
|
86
|
+
# Get the 2x2 grid of pixels (with bounds checking)
|
|
87
|
+
top_left = banner_rows[y][x]
|
|
88
|
+
top_right = x+1 < banner_rows[y].length ? banner_rows[y][x+1] : 0
|
|
89
|
+
bottom_left = y+1 < banner_rows.length ? banner_rows[y+1][x] : 0
|
|
90
|
+
bottom_right = (y+1 < banner_rows.length && x+1 < banner_rows[y+1].length) ? banner_rows[y+1][x+1] : 0
|
|
91
|
+
|
|
92
|
+
# Check if any pixel is above threshold
|
|
93
|
+
if [top_left, top_right, bottom_left, bottom_right].max >= threshold
|
|
94
|
+
# Determine which quadrants are filled (1=filled, 0=empty)
|
|
95
|
+
tl = top_left >= threshold ? 1 : 0
|
|
96
|
+
tr = top_right >= threshold ? 1 : 0
|
|
97
|
+
bl = bottom_left >= threshold ? 1 : 0
|
|
98
|
+
br = bottom_right >= threshold ? 1 : 0
|
|
99
|
+
|
|
100
|
+
# Calculate average color for foreground
|
|
101
|
+
fg_pixel = [top_left, top_right, bottom_left, bottom_right].max
|
|
102
|
+
fg_color = "\e[38;2;#{fg_pixel};#{fg_pixel};#{fg_pixel}m"
|
|
103
|
+
|
|
104
|
+
# Select appropriate Unicode box drawing character based on pattern
|
|
105
|
+
char = case [tl, tr, bl, br]
|
|
106
|
+
when [1, 1, 1, 1] then "\u2588" # Full block
|
|
107
|
+
when [1, 1, 1, 0] then "\u259B" # Quadrant upper left and upper right and lower right
|
|
108
|
+
when [1, 1, 0, 1] then "\u259C" # Quadrant upper left and lower left and lower right
|
|
109
|
+
when [1, 0, 1, 1] then "\u2599" # Quadrant upper left and upper right and lower left
|
|
110
|
+
when [0, 1, 1, 1] then "\u259F" # Quadrant upper right and lower left and lower right
|
|
111
|
+
when [1, 1, 0, 0] then "\u2580" # Upper half block
|
|
112
|
+
when [0, 0, 1, 1] then "\u2584" # Lower half block
|
|
113
|
+
when [1, 0, 1, 0] then "\u258C" # Left half block
|
|
114
|
+
when [0, 1, 0, 1] then "\u2590" # Right half block
|
|
115
|
+
when [1, 0, 0, 0] then "\u2598" # Quadrant upper left
|
|
116
|
+
when [0, 1, 0, 0] then "\u259D" # Quadrant upper right
|
|
117
|
+
when [0, 0, 1, 0] then "\u2596" # Quadrant lower left
|
|
118
|
+
when [0, 0, 0, 1] then "\u2597" # Quadrant lower right
|
|
119
|
+
when [1, 0, 0, 1] then "\u259A" # Quadrant upper left and lower right
|
|
120
|
+
when [0, 1, 1, 0] then "\u259E" # Quadrant upper right and lower left
|
|
121
|
+
else " "
|
|
122
|
+
end
|
|
123
|
+
row_str += "#{fg_color}#{char}"
|
|
124
|
+
else
|
|
125
|
+
# Skip rendering pixels below threshold
|
|
126
|
+
row_str += " "
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
puts row_str + "\e[0m" # Reset all attributes
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Get text from command line or use default
|
|
134
|
+
text = ARGV[0] || "Hello!"
|
|
135
|
+
scale = ARGV[1] ? ARGV[1].to_i : 16
|
|
136
|
+
threshold = ARGV[2] ? ARGV[2].to_i : 30
|
|
137
|
+
|
|
138
|
+
puts "Rendering: '#{text}' with scale #{scale} and threshold #{threshold}"
|
|
139
|
+
render_text_banner(text, "resources/FiraGO-Regular.ttf", scale, threshold)
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
require 'pp'
|
|
2
|
+
require_relative '../lib/skrift'
|
|
3
|
+
|
|
4
|
+
# Helper method to determine the appropriate shadow character
|
|
5
|
+
def determine_shadow_char(banner_rows, x, y, threshold)
|
|
6
|
+
# If current position is above threshold, no shadow needed
|
|
7
|
+
return nil if x < 0 || y < 0
|
|
8
|
+
return nil if y < banner_rows.length && x < banner_rows[y].length && banner_rows[y][x] >= threshold
|
|
9
|
+
|
|
10
|
+
# Check surrounding pixels
|
|
11
|
+
up = (y > 0 && y - 1 < banner_rows.length && x < banner_rows[y - 1].length) ? banner_rows[y - 1][x-1] >= threshold : false
|
|
12
|
+
down = (y + 1 < banner_rows.length && x < banner_rows[y + 1].length) ? banner_rows[y + 1][x-1] >= threshold : false
|
|
13
|
+
left = (x > 0 && y < banner_rows.length && x - 1 < banner_rows[y].length) ? banner_rows[y][x - 2] >= threshold : false
|
|
14
|
+
right = (y < banner_rows.length && x + 1 < banner_rows[y].length) ? banner_rows[y][x] >= threshold : false
|
|
15
|
+
|
|
16
|
+
# For shadow characters, we need to invert our understanding of "up", "down", etc.
|
|
17
|
+
# A shadow character connects TO a character, not FROM it.
|
|
18
|
+
# So we need to swap the directions:
|
|
19
|
+
# - "up" means the shadow is below a character (connects upward)
|
|
20
|
+
# - "down" means the shadow is above a character (connects downward)
|
|
21
|
+
# - "left" means the shadow is to the right of a character (connects leftward)
|
|
22
|
+
# - "right" means the shadow is to the left of a character (connects rightward)
|
|
23
|
+
|
|
24
|
+
# Determine box drawing character based on connections
|
|
25
|
+
if up && down && left && right
|
|
26
|
+
return "┼" # Box drawing: vertical and horizontal
|
|
27
|
+
elsif up && down && left
|
|
28
|
+
return "│" # Box drawing: vertical
|
|
29
|
+
#"Y" #┤" # Box drawing: vertical and left
|
|
30
|
+
elsif up && down && right
|
|
31
|
+
return "├" # Box drawing: vertical and right
|
|
32
|
+
elsif up && left && right
|
|
33
|
+
return "┴" # Box drawing: up and horizontal
|
|
34
|
+
elsif down && left && right
|
|
35
|
+
return "┬" # Box drawing: down and horizontal
|
|
36
|
+
elsif left && right
|
|
37
|
+
return "X" #
|
|
38
|
+
elsif up && down
|
|
39
|
+
return "│" # Box drawing: vertical
|
|
40
|
+
elsif up && right
|
|
41
|
+
return "└" # Box drawing: up and right
|
|
42
|
+
elsif up && left
|
|
43
|
+
return "┌" # Box drawing: down and right
|
|
44
|
+
#"B" #┘" # Box drawing: up and left
|
|
45
|
+
elsif down && right
|
|
46
|
+
return "┌" # Box drawing: down and right
|
|
47
|
+
elsif down && left
|
|
48
|
+
return "┐" # Box drawing: down and left
|
|
49
|
+
elsif up
|
|
50
|
+
return "─" # Box drawing: horizontal
|
|
51
|
+
elsif down
|
|
52
|
+
return "┐" # Box drawing: down and left #"Z"#╷" # Box drawing: down
|
|
53
|
+
elsif left
|
|
54
|
+
return "│" # Box drawing: vertical
|
|
55
|
+
#"A" #╴" # Box drawing: left
|
|
56
|
+
elsif right
|
|
57
|
+
return "╶" # Box drawing: right
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# If we're below a character but not directly adjacent, use a light shade
|
|
61
|
+
if y > 0 && y - 1 < banner_rows.length && x < banner_rows[y - 1].length && banner_rows[y - 1][x] >= threshold
|
|
62
|
+
return "└" # Box drawing: up and right
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
return nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Function to render a string as a text banner
|
|
69
|
+
def render_text_banner(text, font_path = "resources/FiraGO-Regular.ttf", scale = 16, threshold = 30)
|
|
70
|
+
# Load the font
|
|
71
|
+
f = Font.load(font_path)
|
|
72
|
+
|
|
73
|
+
# Create a new SFT instance
|
|
74
|
+
sft = SFT.new(f)
|
|
75
|
+
sft.x_scale = scale
|
|
76
|
+
sft.y_scale = scale
|
|
77
|
+
sft.x_offset = 0
|
|
78
|
+
sft.y_offset = 0
|
|
79
|
+
|
|
80
|
+
# Get font metrics
|
|
81
|
+
metrics = sft.lmetrics
|
|
82
|
+
|
|
83
|
+
# Calculate the maximum height needed for any character
|
|
84
|
+
max_height = metrics.ascender - metrics.descender + metrics.line_gap
|
|
85
|
+
|
|
86
|
+
# Array to store each row of pixels for the banner
|
|
87
|
+
banner_rows = []
|
|
88
|
+
|
|
89
|
+
# Initialize the banner with empty rows
|
|
90
|
+
max_height.to_i.times do
|
|
91
|
+
banner_rows << []
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Process each character in the text
|
|
95
|
+
text.each_char do |char|
|
|
96
|
+
# Get the glyph ID for this character
|
|
97
|
+
gid = sft.lookup(char.ord)
|
|
98
|
+
|
|
99
|
+
# Skip if glyph not found
|
|
100
|
+
next if gid.nil?
|
|
101
|
+
|
|
102
|
+
# Get metrics for this glyph
|
|
103
|
+
gmetrics = sft.gmetrics(gid)
|
|
104
|
+
|
|
105
|
+
# Create an image for this glyph
|
|
106
|
+
img = Image.new(gmetrics.min_width, gmetrics.min_height)
|
|
107
|
+
|
|
108
|
+
# Render the glyph
|
|
109
|
+
if sft.render(gid, img)
|
|
110
|
+
# Calculate vertical offset based on metrics
|
|
111
|
+
y_offset = metrics.ascender - gmetrics.y_offset
|
|
112
|
+
|
|
113
|
+
# Calculate character width including spacing
|
|
114
|
+
char_width = img.width + 2 # Width plus spacing
|
|
115
|
+
|
|
116
|
+
# First, add empty space for all rows in the banner for this character
|
|
117
|
+
banner_rows.each do |row|
|
|
118
|
+
row.concat([0] * char_width)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Then, overwrite with actual character data where it exists
|
|
122
|
+
img.height.times do |row|
|
|
123
|
+
row_in_banner = row + y_offset.to_i
|
|
124
|
+
|
|
125
|
+
# Skip rows outside the banner bounds
|
|
126
|
+
next if row_in_banner < 0 || row_in_banner >= banner_rows.length
|
|
127
|
+
|
|
128
|
+
# Get pixel values for this row of the character
|
|
129
|
+
row_pixels = img.pixels[row * img.width...(row + 1) * img.width]
|
|
130
|
+
|
|
131
|
+
# Calculate the position to start writing pixels
|
|
132
|
+
start_pos = banner_rows[row_in_banner].length - char_width
|
|
133
|
+
|
|
134
|
+
# Overwrite the empty space with actual character data
|
|
135
|
+
row_pixels.each_with_index do |pixel, i|
|
|
136
|
+
banner_rows[row_in_banner][start_pos + i] = pixel
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Process banner for box drawing characters (look at 2x2 blocks)
|
|
143
|
+
(0...banner_rows.length).step(2) do |y|
|
|
144
|
+
row_str = ""
|
|
145
|
+
(0...banner_rows[y].length - 1).step(2) do |x|
|
|
146
|
+
# Get the 2x2 grid of pixels (with bounds checking)
|
|
147
|
+
top_left = banner_rows[y][x]
|
|
148
|
+
top_right = x + 1 < banner_rows[y].length ? banner_rows[y][x + 1] : 0
|
|
149
|
+
bottom_left = y + 1 < banner_rows.length ? banner_rows[y + 1][x] : 0
|
|
150
|
+
bottom_right = (y + 1 < banner_rows.length && x + 1 < banner_rows[y + 1].length) ? banner_rows[y + 1][x + 1] : 0
|
|
151
|
+
|
|
152
|
+
# Check if any pixel is above threshold
|
|
153
|
+
if [top_left, top_right, bottom_left, bottom_right].max >= threshold
|
|
154
|
+
# Determine which quadrants are filled (1=filled, 0=empty)
|
|
155
|
+
tl = top_left >= threshold ? 1 : 0
|
|
156
|
+
tr = top_right >= threshold ? 1 : 0
|
|
157
|
+
bl = bottom_left >= threshold ? 1 : 0
|
|
158
|
+
br = bottom_right >= threshold ? 1 : 0
|
|
159
|
+
|
|
160
|
+
# Calculate average color for foreground
|
|
161
|
+
fg_pixel = [top_left, top_right, bottom_left, bottom_right].max
|
|
162
|
+
fg_color = "\e[38;2;#{fg_pixel};#{fg_pixel/4};#{fg_pixel/4}m"
|
|
163
|
+
|
|
164
|
+
# Select appropriate Unicode box drawing character based on pattern
|
|
165
|
+
char = case [tl, tr, bl, br]
|
|
166
|
+
when [1, 1, 1, 1] then "\u2588" # Full block
|
|
167
|
+
when [1, 1, 1, 0] then "\u259B" # Quadrant upper left and upper right and lower right
|
|
168
|
+
when [1, 1, 0, 1] then "\u259C" # Quadrant upper left and lower left and lower right
|
|
169
|
+
when [1, 0, 1, 1] then "\u2599" # Quadrant upper left and upper right and lower left
|
|
170
|
+
when [0, 1, 1, 1] then "\u259F" # Quadrant upper right and lower left and lower right
|
|
171
|
+
when [1, 1, 0, 0] then "\u2580" # Upper half block
|
|
172
|
+
when [0, 0, 1, 1] then "\u2584" # Lower half block
|
|
173
|
+
when [1, 0, 1, 0] then "\u258C" # Left half block
|
|
174
|
+
when [0, 1, 0, 1] then "\u2590" # Right half block
|
|
175
|
+
when [1, 0, 0, 0] then "\u2598" # Quadrant upper left
|
|
176
|
+
when [0, 1, 0, 0] then "\u259D" # Quadrant upper right
|
|
177
|
+
when [0, 0, 1, 0] then "\u2596" # Quadrant lower left
|
|
178
|
+
when [0, 0, 0, 1] then "\u2597" # Quadrant lower right
|
|
179
|
+
when [1, 0, 0, 1] then "\u259A" # Quadrant upper left and lower right
|
|
180
|
+
when [0, 1, 1, 0] then "\u259E" # Quadrant upper right and lower left
|
|
181
|
+
else " "
|
|
182
|
+
end
|
|
183
|
+
row_str += "#{fg_color}#{char}"
|
|
184
|
+
else
|
|
185
|
+
# Check for shadow character
|
|
186
|
+
shadow_char = determine_shadow_char(banner_rows, x, y, threshold)
|
|
187
|
+
if shadow_char.nil?
|
|
188
|
+
row_str += " "
|
|
189
|
+
else
|
|
190
|
+
# Use a grey color for shadow
|
|
191
|
+
shadow_color = "\e[38;2;64;64;64m"
|
|
192
|
+
row_str += "#{shadow_color}#{shadow_char}"
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
puts row_str + "\e[0m" # Reset all attributes
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Get text from command line or use default
|
|
202
|
+
text = ARGV[0] || "Hello!"
|
|
203
|
+
scale = ARGV[1] ? ARGV[1].to_i : 16
|
|
204
|
+
threshold = ARGV[2] ? ARGV[2].to_i : 30
|
|
205
|
+
|
|
206
|
+
puts "Rendering: '#{text}' with scale #{scale} and threshold #{threshold}"
|
|
207
|
+
render_text_banner(text, "resources/FiraGO-Regular.ttf", scale, threshold)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
require 'pp'
|
|
3
|
-
require_relative '
|
|
3
|
+
require_relative '../lib/skrift'
|
|
4
4
|
|
|
5
5
|
f = Font.load("resources/Ubuntu-Regular.ttf")
|
|
6
6
|
#f = Font.load("resources/FiraGO-Regular_extended_with_NotoSansEgyptianHieroglyphs-Regular.ttf")
|
|
@@ -59,7 +59,7 @@ if sft.render(gid, img)
|
|
|
59
59
|
#img.pixels = r.post_process
|
|
60
60
|
|
|
61
61
|
img.height.times do |row|
|
|
62
|
-
img.pixels[row*img.width .. (row+1)*img.width-1].map do |s|
|
|
62
|
+
img.pixels[row * img.width .. (row + 1) * img.width - 1].map do |s|
|
|
63
63
|
print "\033[32;48;2;#{s};#{s};#{s}m%02x " % s
|
|
64
64
|
end
|
|
65
65
|
puts "\033[39;49m"
|