image_util 0.1.0 → 0.3.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/AGENTS.md +5 -6
- data/CHANGELOG.md +41 -6
- data/README.md +229 -81
- data/Rakefile +5 -0
- data/docs/cli.md +5 -0
- data/docs/samples/background.png +0 -0
- data/docs/samples/bitmap_text.png +0 -0
- data/docs/samples/colors.png +0 -0
- data/docs/samples/constructor.png +0 -0
- data/docs/samples/dither.png +0 -0
- data/docs/samples/draw.png +0 -0
- data/docs/samples/iterator.png +0 -0
- data/docs/samples/paste.png +0 -0
- data/docs/samples/pdither.png +0 -0
- data/docs/samples/pipe.png +0 -0
- data/docs/samples/range.png +0 -0
- data/docs/samples/redimension.png +0 -0
- data/docs/samples/resize.png +0 -0
- data/docs/samples/sixel.png +0 -0
- data/docs/samples/transform.png +0 -0
- data/exe/image_util +7 -0
- data/lib/image_util/benchmarking.rb +25 -0
- data/lib/image_util/bitmap_font/fonts/smfont/charset.txt +1 -0
- data/lib/image_util/bitmap_font/fonts/smfont/font.png +0 -0
- data/lib/image_util/bitmap_font.rb +72 -0
- data/lib/image_util/cli.rb +54 -0
- data/lib/image_util/codec/chunky_png.rb +67 -0
- data/lib/image_util/codec/image_magick.rb +82 -15
- data/lib/image_util/codec/kitty.rb +81 -0
- data/lib/image_util/codec/libpng.rb +2 -10
- data/lib/image_util/codec/libsixel.rb +14 -14
- data/lib/image_util/codec/libturbojpeg.rb +1 -11
- data/lib/image_util/codec/pam.rb +24 -22
- data/lib/image_util/codec/ruby_sixel.rb +12 -13
- data/lib/image_util/codec.rb +5 -1
- data/lib/image_util/color/css_colors.rb +158 -0
- data/lib/image_util/color.rb +67 -14
- data/lib/image_util/extension.rb +24 -0
- data/lib/image_util/filter/_mixin.rb +9 -0
- data/lib/image_util/filter/background.rb +4 -4
- data/lib/image_util/filter/bitmap_text.rb +17 -0
- data/lib/image_util/filter/colors.rb +21 -0
- data/lib/image_util/filter/draw.rb +22 -9
- data/lib/image_util/filter/palette.rb +197 -0
- data/lib/image_util/filter/paste.rb +1 -1
- data/lib/image_util/filter/redimension.rb +83 -0
- data/lib/image_util/filter/resize.rb +1 -1
- data/lib/image_util/filter/transform.rb +48 -0
- data/lib/image_util/filter.rb +5 -1
- data/lib/image_util/generator/bitmap_text.rb +38 -0
- data/lib/image_util/generator/example/rose.png +0 -0
- data/lib/image_util/generator/example.rb +9 -0
- data/lib/image_util/generator.rb +8 -0
- data/lib/image_util/image/buffer.rb +11 -11
- data/lib/image_util/image.rb +54 -26
- data/lib/image_util/magic.rb +8 -6
- data/lib/image_util/statistic/{color.rb → colors.rb} +2 -2
- data/lib/image_util/statistic.rb +1 -1
- data/lib/image_util/terminal.rb +61 -0
- data/lib/image_util/version.rb +1 -1
- data/lib/image_util/view/interpolated.rb +1 -1
- data/lib/image_util.rb +6 -0
- metadata +82 -4
- data/lib/image_util/filter/dither.rb +0 -96
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31687d0660e040e340b6ed26f581f95de0746e33f992acf7dfcdb4fa4a88f4cd
|
4
|
+
data.tar.gz: 4fee3793295fb2207a54caa1ed430bdc43ddc8307f96ef597ba0ed777618b8e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c02064cb968adae5e5d5b5328922e77fd542d6ca4322a9259d27b513339ff44ca71e272f8cf01055d864f3dbc3fef702d3187a745e98af09a95f2dc9907470c4
|
7
|
+
data.tar.gz: 97c9852b31aab0d2b71f19579968d856fd87eed7339c3b2ec9e6bd1dcee3b674862821f6d4a033148db8d45b0a8228d890670d43c2f0ba921dc98d7d5ed774de
|
data/AGENTS.md
CHANGED
@@ -6,11 +6,10 @@
|
|
6
6
|
- Prefer double-quoted strings except in specs and the gemspec.
|
7
7
|
- Use RSpec's `should` syntax instead of `expect`.
|
8
8
|
- For one-line methods, use the `def name = expression` style.
|
9
|
-
|
10
|
-
|
11
|
-
- Image data is stored in `Image::Buffer` backed by `IO::Buffer`.
|
12
|
-
- Use `Filter::Mixin#define_immutable_version` to add non-bang versions of mutating filters.
|
13
|
-
- Views such as `View::Interpolated` and `View::Rounded` are built with `Data.define`.
|
14
|
-
- Pure Ruby algorithms are provided with optional FFI wrappers for libpng, libturbojpeg and libsixel.
|
9
|
+
- After adding new features or modifying existing ones, change documentation accordingly (especially README and especially CHANGELOG).
|
10
|
+
- Don't discuss codec internals or bug fixes in README. Only list supported formats. Document bug fixes in the CHANGELOG, not in README.
|
15
11
|
- Specs target at least 80% coverage as enforced by SimpleCov.
|
16
12
|
- The library aims to remain lightweight and portable.
|
13
|
+
- Remember to always ensure rake tests pass and Rubocop doesn't complain.
|
14
|
+
- If you are an OpenAI Codex, don't upload images! Tell me to use `rebuild-images-in-readme` script.
|
15
|
+
- When adding files into collection directories (like codec/), if something isn't part of a collection (ie. isn't a codec, but is a mixin), ensure to name the file like `_something.rb`. Consult existing directory structure.
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,44 @@
|
|
1
|
-
## [
|
2
|
-
|
1
|
+
## [0.3.0] - 2025-07-25
|
2
|
+
- Rename `dither!` to `palette_reduce!`
|
3
|
+
- Rename `#set_each_pixel_by_location` to `#set_each_pixel_by_location!` since it's mutable
|
4
|
+
- Rename `color_length` to a more appropriate name `channels`
|
5
|
+
- Thor based CLI with a `support` command that lists codec support, default
|
6
|
+
format handlers and detected terminal features
|
7
|
+
- Replace `palette_reduce!` implementation with a much faster one
|
8
|
+
- Terminal detection for graphic protocols
|
9
|
+
- Support Kitty graphics protocol
|
10
|
+
- Transform filter with rotate and flip operations
|
11
|
+
- Fallback PNG codec via chunky_png
|
12
|
+
- ImageMagick codec can now read and write `gif` and `apng` including animations
|
13
|
+
- Redimension filter to change image dimensions
|
14
|
+
- Sixel and Kitty codecs accept 1D images
|
15
|
+
- `Pam.encode` no longer accepts `fill_to`; Sixel codecs pad images using the
|
16
|
+
redimension filter
|
17
|
+
- Add `BitmapFont` with a sample hand crafted font, add `bitmap_text` generator.
|
18
|
+
- `Color#*` can now accept another `Color` to multiply channels
|
19
|
+
- Add `Colors` filter with `color_multiply!` and alias `*`
|
20
|
+
- `bitmap_text` accepts multiline strings and supports colorization
|
21
|
+
- `bitmap_text` supports left, center and right alignment
|
22
|
+
- Add `BitmapText` filter for overlaying text onto images
|
23
|
+
- `bitmap_text` filter now always respects an alpha channel when pasting text
|
24
|
+
- Open ImageMagick codec pipes in binary mode for Windows compatibility
|
25
|
+
- Format inference from file extension in `Image#to_file`
|
26
|
+
- ImageMagick codec now reads PAM frames using the Pam codec
|
27
|
+
- Force 8-bit output when decoding through ImageMagick to avoid 1-bit images on Windows
|
28
|
+
- ImageMagick codec checks available formats before advertising APNG support
|
29
|
+
- Add `Example` generator: `Image.example_rose`
|
30
|
+
- Disable APNG on Windows until we find out something else than ImageMagick to support it
|
31
|
+
|
32
|
+
## [0.2.0] - 2025-07-21
|
33
|
+
- Ruby Sixel encoder now sets pixel aspect ratio metadata to display correctly in Windows Terminal
|
34
|
+
- Support for all CSS color names
|
35
|
+
- Range assignments with images now resize the image before pasting
|
36
|
+
- Circle drawing filter
|
37
|
+
- ImageMagick codec can encode and decode `png`, `jpeg` and `sixel`
|
38
|
+
|
39
|
+
## [0.1.0] - 2025-07-21
|
40
|
+
|
41
|
+
- Initial release
|
3
42
|
- Drop support for Ruby 3.1
|
4
43
|
- Native SIXEL encoder
|
5
44
|
- Background filter
|
@@ -12,7 +51,3 @@
|
|
12
51
|
- Paste and Draw filters for compositing and drawing
|
13
52
|
- Rounded and interpolated pixel views
|
14
53
|
- `Image#view` helper for custom access
|
15
|
-
|
16
|
-
## [0.1.0] - 2025-07-19
|
17
|
-
|
18
|
-
- Initial release
|
data/README.md
CHANGED
@@ -1,144 +1,288 @@
|
|
1
1
|
# ImageUtil
|
2
2
|
|
3
|
-
ImageUtil
|
3
|
+
ImageUtil is a lightweight Ruby library focused on manipulating images directly in memory. Its primary goal is to help scripts visualize data right in the terminal by supporting SIXEL output alongside common image formats. The API is still evolving and should be considered unstable until version 1.0.
|
4
4
|
|
5
|
-
|
5
|
+
## Creating an Image
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
```ruby
|
8
|
+
require 'image_util'
|
9
|
+
|
10
|
+
# 40×40 RGBA image
|
11
|
+
img = ImageUtil::Image.new(40, 40)
|
12
|
+
```
|
13
|
+
|
14
|
+
An optional block receives pixel coordinates and should return something that can be converted to a color. Dimensions of more than two axes are supported.
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
img = ImageUtil::Image.new(128, 128) { |x, y| ImageUtil::Color[x, y, 40] }
|
18
|
+
```
|
19
|
+
|
20
|
+

|
21
|
+
|
22
|
+
## Loading and Saving
|
16
23
|
|
17
|
-
|
24
|
+
Instead of building an image from scratch you can load one with
|
25
|
+
`ImageUtil::Image.from_string` or `ImageUtil::Image.from_file`.
|
26
|
+
Both helpers understand the built in codecs for `png`, `jpeg`, `pam`, `gif`
|
27
|
+
and `apng` formats:
|
18
28
|
|
19
|
-
|
29
|
+
```ruby
|
30
|
+
img = ImageUtil::Image.from_file("logo.png")
|
31
|
+
data = ImageUtil::Image.from_string(File.binread("logo.jpeg"))
|
32
|
+
```
|
33
|
+
|
34
|
+
A `from_file` method also supports passing IO objects:
|
20
35
|
|
21
|
-
```
|
22
|
-
|
36
|
+
```ruby
|
37
|
+
img = ImageUtil::Image.from_file(IO.popen("magick rose: png:"))
|
38
|
+
img.draw_line([0,0], [69,45], :blue)
|
23
39
|
```
|
24
40
|
|
25
|
-
|
41
|
+

|
42
|
+
|
43
|
+
The same formats can be written back using `to_string` or `to_file`.
|
44
|
+
When saving to a file path, `to_file` can infer the format from the file extension.
|
26
45
|
|
27
46
|
```ruby
|
28
|
-
|
47
|
+
img.to_file("out.png")
|
48
|
+
binary = img.to_string(:jpeg)
|
29
49
|
```
|
30
50
|
|
31
|
-
|
51
|
+
## Image Information
|
32
52
|
|
33
|
-
|
53
|
+
After loading or creating an image, you can access information about it, like
|
54
|
+
dimensions or color bits.
|
34
55
|
|
35
|
-
```
|
36
|
-
|
37
|
-
|
38
|
-
|
56
|
+
```ruby
|
57
|
+
img.dimensions # => [20,30]
|
58
|
+
img.width # => 20
|
59
|
+
img.height # => 30
|
60
|
+
img.color_bits # => 8 (means every channel has 8 bits of color)
|
61
|
+
img.channels # => 3 (RGB)
|
39
62
|
```
|
40
63
|
|
41
|
-
##
|
64
|
+
## Terminal Output
|
65
|
+
|
66
|
+
Images can be previewed in compatible terminals:
|
42
67
|
|
43
68
|
```ruby
|
44
|
-
|
69
|
+
puts ImageUtil::Terminal.output_image($stdin, $stdout, img)
|
70
|
+
```
|
71
|
+
|
72
|
+
In `irb` or `pry` the `inspect` method shows the image automatically, so you can
|
73
|
+
just evaluate the object:
|
45
74
|
|
46
|
-
|
47
|
-
|
75
|
+
```ruby
|
76
|
+
img
|
77
|
+
```
|
78
|
+
|
79
|
+
The library checks if the Kitty graphics protocol is available and falls back to SIXEL otherwise. Kitty graphics protocol is supported by Kitty, Konsole
|
80
|
+
and a couple others. SIXEL, most notably, works in Windows Terminal, Konsole (KDE), iTerm2 (macOS), XTerm (launch with: `xterm -ti vt340`). Here's how SIXEL
|
81
|
+
looks in Konsole:
|
82
|
+
|
83
|
+

|
84
|
+
|
85
|
+
This library supports generating Sixel with either `libsixel`, `ImageMagick` or using a pure-Ruby Sixel generator. For best performance, try to install one of
|
86
|
+
the earlier system packages. Both Kitty and SIXEL outputs also accept one-dimensional images, treating them as height `1`.
|
87
|
+
|
88
|
+
|
89
|
+
## Color Values
|
90
|
+
|
91
|
+
`ImageUtil::Color.from` (also known as `ImageUtil::Color.[]`) accepts several inputs:
|
92
|
+
|
93
|
+
- Another `Color` instance
|
94
|
+
- Arrays of numeric components (`[r, g, b]` or `[r, g, b, a]`)
|
95
|
+
- Numbers (used for all RGB channels)
|
96
|
+
- Symbols or strings containing CSS color names (`:rebeccapurple`, 'papayawhip')
|
97
|
+
- Hex strings like `'#abc'`, `'#aabbcc'` or `'#rrggbbaa'`
|
98
|
+
|
99
|
+
When numeric components are given, integers are first clamped to the `0..255`
|
100
|
+
range. Float values are treated as fractions of 255, so `0.5` becomes `127.5`
|
101
|
+
and `1.0` becomes `255`. After scaling, values are again clamped to this range.
|
102
|
+
If the alpha channel is omitted it defaults to `255`.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
ImageUtil::Color[0.5] # => #808080
|
106
|
+
ImageUtil::Color[:red] # => #ff0000
|
107
|
+
ImageUtil::Color["#fc0"] # => #ffcc00
|
108
|
+
```
|
109
|
+
|
110
|
+
Note that whenever the library expects a color, it may be given in any form accepted by this function.
|
111
|
+
|
112
|
+
## Pixel Access
|
113
|
+
|
114
|
+
Pixels can be accessed with integer coordinates or ranges. When ranges are used
|
115
|
+
a new `Image` containing that region is returned and can be modified separately.
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
img[0, 0] = '#ff0000'
|
119
|
+
patch = img[0..1, 0..1]
|
120
|
+
```
|
48
121
|
|
49
|
-
|
50
|
-
i[0, 0] = ImageUtil::Color[255, 0, 0]
|
122
|
+
For instance, you can extract a region, edit it and paste it back:
|
51
123
|
|
52
|
-
|
53
|
-
|
124
|
+
```ruby
|
125
|
+
img = ImageUtil::Image.new(128, 128) { [0, 0, 0] }
|
126
|
+
corner = img[0..32, 0..32]
|
127
|
+
corner.all = :green
|
128
|
+
img[0..32, 0..32] = corner
|
129
|
+
img[2, 2] = :yellow
|
130
|
+
# img.to_file("pixel_patch.png", :png)
|
131
|
+
img
|
54
132
|
```
|
55
133
|
|
56
|
-
|
134
|
+

|
135
|
+
|
136
|
+
Assigning an image to a range automatically resizes it to fit before pasting.
|
137
|
+
|
138
|
+
On the other hand, if you assign a color to a range, it will fill all referenced
|
139
|
+
pixels with that color (draw a rectangle).
|
140
|
+
|
141
|
+
Iteration helpers operate on arbitrary ranges and share the same syntax used
|
142
|
+
when indexing images. `each_pixel` yields color objects, while
|
143
|
+
`each_pixel_location` yields coordinate arrays. `set_each_pixel_by_location!`
|
144
|
+
assigns the value returned by the block to every location (unless `nil` is returned).
|
57
145
|
|
58
146
|
```ruby
|
59
|
-
#
|
60
|
-
|
147
|
+
# create an all-black image
|
148
|
+
img = ImageUtil::Image.new(128, 128) { :black }
|
61
149
|
|
62
|
-
#
|
63
|
-
|
64
|
-
|
150
|
+
# fill a checkerboard pattern
|
151
|
+
img.set_each_pixel_by_location! do |x, y|
|
152
|
+
:red if (x + y).odd?
|
65
153
|
end
|
66
154
|
|
67
|
-
#
|
68
|
-
|
69
|
-
source = ImageUtil::Image.new(2, 2) { ImageUtil::Color[255, 0, 0, 128] }
|
70
|
-
target.paste!(source, 3, 3, respect_alpha: true)
|
155
|
+
# count how many red pixels were set
|
156
|
+
black = img.each_pixel.count { |c| c == :red }
|
71
157
|
|
72
|
-
#
|
73
|
-
|
158
|
+
# display img in terminal
|
159
|
+
img
|
74
160
|
```
|
75
161
|
|
76
|
-
|
77
|
-
|
78
|
-
|
162
|
+

|
163
|
+
|
164
|
+
Note that instead of manually calling `set_each_pixel_by_location`, you can just pass a block to `ImageUtil::Image.new`.
|
79
165
|
|
80
|
-
|
166
|
+
## Filters
|
167
|
+
|
168
|
+
`ImageUtil::Image` ships with a few convenience filters. Each bang method
|
169
|
+
modifies the image in place while the non-bang version returns a copy.
|
170
|
+
|
171
|
+
### Background
|
172
|
+
|
173
|
+
Flatten an RGBA image on a solid color.
|
81
174
|
|
82
175
|
```ruby
|
83
|
-
#
|
84
|
-
img = ImageUtil::Image.
|
176
|
+
# create a transparent image gradient containing shades of red only
|
177
|
+
img = ImageUtil::Image.new(128, 128) { |x, y| [255, 0, 0, x + y] }
|
178
|
+
|
179
|
+
# put it on a blue background
|
180
|
+
img.background([0, 0, 255])
|
181
|
+
```
|
85
182
|
|
86
|
-
|
87
|
-
img.to_file("out.jpg", :jpeg)
|
183
|
+

|
88
184
|
|
89
|
-
|
90
|
-
|
185
|
+
### Paste
|
186
|
+
|
187
|
+
Place one image over another. When `respect_alpha` is true, the pasted pixels are
|
188
|
+
blended with the base image.
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
base = ImageUtil::Image.new(128, 128) { |x, y| [x, y, 50] }
|
192
|
+
overlay = ImageUtil::Image.new(64, 64) { |x, y| [255, 0, 0, (x + y) * 2] }
|
193
|
+
base.paste!(overlay, 32, 32, respect_alpha: true)
|
91
194
|
```
|
92
195
|
|
93
|
-
|
196
|
+

|
197
|
+
|
198
|
+
### Draw
|
199
|
+
|
200
|
+
Draw simple shapes directly on the image.
|
94
201
|
|
95
202
|
```ruby
|
96
|
-
|
97
|
-
|
203
|
+
img = ImageUtil::Image.new(128, 128) { [0, 0, 0] }
|
204
|
+
img.draw_line!([0, 0], [127, 127], :red)
|
205
|
+
img.draw_line!([0, 127], [127, 0], :lime)
|
206
|
+
img.draw_circle!([64, 64], 30, :blue)
|
207
|
+
```
|
208
|
+
|
209
|
+

|
210
|
+
|
211
|
+
### Resize
|
98
212
|
|
99
|
-
|
100
|
-
result = base.paste(other, 10, 10)
|
213
|
+
Scale an image to new dimensions.
|
101
214
|
|
102
|
-
|
103
|
-
|
215
|
+
```ruby
|
216
|
+
img = ImageUtil::Image.new(128, 128) { |x, y| [x, y, 30] }
|
217
|
+
img[20, 20] = img.resize(64, 64)
|
218
|
+
img
|
104
219
|
```
|
105
220
|
|
106
|
-
|
221
|
+

|
222
|
+
|
223
|
+
### Palette
|
224
|
+
|
225
|
+
Reduce the image to a limited palette.
|
107
226
|
|
108
227
|
```ruby
|
109
|
-
|
110
|
-
|
111
|
-
|
228
|
+
img = ImageUtil::Image.new(128, 128) { |x, y| [x * 2, y * 2, 200] }
|
229
|
+
img.palette_reduce(64)
|
230
|
+
```
|
231
|
+
|
232
|
+

|
233
|
+
|
234
|
+
### Transform
|
235
|
+
|
236
|
+
Rotate or flip an image.
|
112
237
|
|
113
|
-
|
114
|
-
|
115
|
-
|
238
|
+
```ruby
|
239
|
+
img = ImageUtil::Image.new(128, 128) { |x, y| [x, y, 0] }
|
240
|
+
img.flip!(:x)
|
241
|
+
img.rotate!(90)
|
242
|
+
# img.rotate!(90, axes: [:x, :z])
|
116
243
|
```
|
117
244
|
|
118
|
-
|
245
|
+

|
246
|
+
|
247
|
+
### Colors
|
119
248
|
|
120
|
-
|
121
|
-
common formats such as PNG, JPEG and SIXEL. The library ships with pure Ruby
|
122
|
-
encoders and FFI wrappers around `libpng`, `libturbojpeg` and `libsixel` when
|
123
|
-
available.
|
249
|
+
Multiply all pixels by a color.
|
124
250
|
|
125
251
|
```ruby
|
126
|
-
|
127
|
-
|
252
|
+
img = ImageUtil::Image.new(128, 128) { [255, 255, 255, 128] }
|
253
|
+
img * :red
|
254
|
+
```
|
128
255
|
|
129
|
-
|
130
|
-
|
131
|
-
|
256
|
+

|
257
|
+
|
258
|
+
### Bitmap Text
|
259
|
+
|
260
|
+
Overlay text using the bundled bitmap font.
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
img = ImageUtil::Image.new(128, 128) { [0, 0, 0] }
|
264
|
+
img.bitmap_text!("Lorem ipsum dolor sit\namet, consectetur adipiscing\nelit.", 2, 2, color: :blue)
|
132
265
|
```
|
133
266
|
|
134
|
-
|
267
|
+

|
268
|
+
|
269
|
+
### Redimension
|
270
|
+
|
271
|
+
Change how many dimensions an image has or adjust their size.
|
135
272
|
|
136
273
|
```ruby
|
137
|
-
|
274
|
+
img = ImageUtil::Image.new(64, 64) { :white }
|
275
|
+
img.redimension!(128, 128) # can also add additional dimensions
|
276
|
+
img.background(:blue)
|
138
277
|
```
|
139
278
|
|
140
|
-
|
141
|
-
|
279
|
+

|
280
|
+
|
281
|
+
## Command Line
|
282
|
+
|
283
|
+
The gem includes a small `image_util` CLI. Run `image_util support` to list
|
284
|
+
available codecs, default format handlers and detected terminal features.
|
285
|
+
See [docs/cli.md](docs/cli.md) for details.
|
142
286
|
|
143
287
|
## Development
|
144
288
|
|
@@ -146,6 +290,10 @@ After checking out the repo, run `bin/setup` to install dependencies. Then run
|
|
146
290
|
`rake spec` to execute the tests. You can also run `bin/console` for an
|
147
291
|
interactive prompt for experimenting with the library.
|
148
292
|
|
293
|
+
### Benchmarking
|
294
|
+
|
295
|
+
Run `bin/benchmark` to execute a small benchmark.
|
296
|
+
|
149
297
|
## Contributing
|
150
298
|
|
151
299
|
Bug reports and pull requests are welcome on GitHub at
|
data/Rakefile
CHANGED
data/docs/cli.md
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/exe/image_util
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "benchmark/ips"
|
4
|
+
|
5
|
+
module ImageUtil
|
6
|
+
module Benchmarking
|
7
|
+
module_function
|
8
|
+
|
9
|
+
# Benchmarks creating a 64×64×64 black image for the given time in seconds.
|
10
|
+
def image_creation(seconds = 5)
|
11
|
+
::Benchmark.ips do |x|
|
12
|
+
x.warmup = 0
|
13
|
+
x.time = seconds
|
14
|
+
x.report("from Symbol") { Image.new(64, 64, 64) { :black } }
|
15
|
+
x.report("from Array (4->4 channels)") { Image.new(64, 64, 64) { |x,y,z| [x,y,z,255] } }
|
16
|
+
x.report("from Array (3->4 channels)") { Image.new(64, 64, 64) { |x,y,z| [x,y,z] } }
|
17
|
+
x.report("from Array (3->3 channels)") { Image.new(64, 64, 64, channels: 3) { |x,y,z| [x,y,z] } }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(seconds = 5)
|
22
|
+
image_creation(seconds)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()-_+=:;'"|\/?.,[]{}<>€
|
Binary file
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImageUtil
|
4
|
+
class BitmapFont
|
5
|
+
def initialize(name)
|
6
|
+
font = Image.from_file("#{__dir__}/bitmap_font/fonts/#{name}/font.png")
|
7
|
+
charset = File.read("#{__dir__}/bitmap_font/fonts/#{name}/charset.txt").chomp.chars
|
8
|
+
|
9
|
+
parse_image(font, charset)
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse_image(font, charset)
|
13
|
+
font.height.times do |n|
|
14
|
+
if font[0,n] == :red
|
15
|
+
@height = n
|
16
|
+
break
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
character_pos = []
|
21
|
+
|
22
|
+
n = 0
|
23
|
+
while n < font.width
|
24
|
+
if font[n, @height] == :blue
|
25
|
+
start = n
|
26
|
+
n += 1 while n < font.width && font[n, @height] == :blue
|
27
|
+
n -= 1
|
28
|
+
finish = n
|
29
|
+
character_pos << (start..finish)
|
30
|
+
end
|
31
|
+
n += 1
|
32
|
+
end
|
33
|
+
|
34
|
+
font = font.dup
|
35
|
+
font.set_each_pixel_by_location! do |x,y|
|
36
|
+
[255, 255, 255, 255 - font[x,y].g]
|
37
|
+
end
|
38
|
+
|
39
|
+
@characters = character_pos.map.with_index do |range,idx|
|
40
|
+
[charset[idx], font[range, ...@height]]
|
41
|
+
end.to_h
|
42
|
+
end
|
43
|
+
|
44
|
+
def render_line_of_text(text)
|
45
|
+
width = 1
|
46
|
+
text.chars.each do |char|
|
47
|
+
width += @characters[char].width + 1
|
48
|
+
end
|
49
|
+
width -= 1
|
50
|
+
|
51
|
+
img = Image.new(width, @height)
|
52
|
+
width = 0
|
53
|
+
text.chars.each do |char|
|
54
|
+
img[width,0] = @characters[char]
|
55
|
+
width += @characters[char].width + 1
|
56
|
+
end
|
57
|
+
|
58
|
+
img
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.fonts
|
62
|
+
Dir["#{__dir__}/bitmap_font/fonts/*"].map { |i| File.basename(i) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.default_font = "smfont"
|
66
|
+
|
67
|
+
def self.cached_load(font)
|
68
|
+
@fonts ||= {}
|
69
|
+
@fonts[font] ||= BitmapFont.new(font)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|