image_util 0.2.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 +3 -0
- data/CHANGELOG.md +31 -0
- data/README.md +125 -24
- 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/iterator.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/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 +76 -18
- 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 +11 -12
- data/lib/image_util/codec.rb +5 -1
- data/lib/image_util/color/css_colors.rb +3 -1
- data/lib/image_util/color.rb +62 -9
- 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 +2 -11
- 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 +49 -23
- 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 +75 -4
- data/lib/image_util/filter/dither.rb +0 -96
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImageUtil
|
4
|
+
module Filter
|
5
|
+
module BitmapText
|
6
|
+
extend ImageUtil::Filter::Mixin
|
7
|
+
|
8
|
+
def bitmap_text!(text, *location, **kwargs)
|
9
|
+
loc = location.dup
|
10
|
+
loc += [0] * (dimensions.length - loc.length)
|
11
|
+
paste!(Image.bitmap_text(text, **kwargs), *loc, respect_alpha: true)
|
12
|
+
end
|
13
|
+
|
14
|
+
define_immutable_version :bitmap_text
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImageUtil
|
4
|
+
module Filter
|
5
|
+
module Colors
|
6
|
+
extend ImageUtil::Filter::Mixin
|
7
|
+
|
8
|
+
def color_multiply!(color)
|
9
|
+
col = Color.from(color)
|
10
|
+
set_each_pixel_by_location! do |loc|
|
11
|
+
self[*loc] * col
|
12
|
+
end
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
define_immutable_version :color_multiply
|
17
|
+
|
18
|
+
alias * color_multiply
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -16,12 +16,12 @@ module ImageUtil
|
|
16
16
|
)
|
17
17
|
fp = self.view(view)
|
18
18
|
|
19
|
-
axis = axis_to_number(axis)
|
19
|
+
axis = Filter::Mixin.axis_to_number(axis)
|
20
20
|
draw_axis ||= case axis
|
21
21
|
when 0 then 1
|
22
22
|
when 1 then 0
|
23
23
|
end
|
24
|
-
draw_axis = axis_to_number(draw_axis)
|
24
|
+
draw_axis = Filter::Mixin.axis_to_number(draw_axis)
|
25
25
|
|
26
26
|
limit ||= (0..)
|
27
27
|
limit = Range.new(limit.begin, dimensions[axis]-1, false) if limit.end == nil
|
@@ -85,15 +85,6 @@ module ImageUtil
|
|
85
85
|
end
|
86
86
|
|
87
87
|
define_immutable_version :draw_function, :draw_line, :draw_circle
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def axis_to_number(axis)
|
92
|
-
axis = 0 if axis == :x
|
93
|
-
axis = 1 if axis == :y
|
94
|
-
axis = 2 if axis == :z
|
95
|
-
axis
|
96
|
-
end
|
97
88
|
end
|
98
89
|
end
|
99
90
|
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImageUtil
|
4
|
+
module Filter
|
5
|
+
module Palette
|
6
|
+
extend ImageUtil::Filter::Mixin
|
7
|
+
|
8
|
+
# A more descriptive name for this structure would be
|
9
|
+
# N-tree, where N = 2**color_components. Let's pretend
|
10
|
+
# we are dealing with 3-color component trees though,
|
11
|
+
# so Octree it is.
|
12
|
+
class ColorOctree < Array
|
13
|
+
def dig(nexthop, *rest)
|
14
|
+
self[nexthop] ||= ColorOctree.new(length)
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def number_bits(number)
|
19
|
+
array = Array.new(8)
|
20
|
+
i = 0
|
21
|
+
number = number.to_i
|
22
|
+
while i < 8
|
23
|
+
array[7 - i] = ((number >> i) & 1)
|
24
|
+
i += 1
|
25
|
+
end
|
26
|
+
array
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_key(component_bits)
|
30
|
+
number = 0
|
31
|
+
component_bits.reverse_each do |i|
|
32
|
+
number <<= 1
|
33
|
+
number |= i
|
34
|
+
end
|
35
|
+
number
|
36
|
+
end
|
37
|
+
|
38
|
+
def key_array(color)
|
39
|
+
color.map do |component|
|
40
|
+
number_bits(component)
|
41
|
+
end.transpose.map do |i|
|
42
|
+
generate_key(i)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# rubocop:disable Style/Semicolon
|
47
|
+
|
48
|
+
# Optimized path for (r,g,b) colors.
|
49
|
+
def key_array3(color)
|
50
|
+
i = 0
|
51
|
+
r = color[0].to_i; g = color[1].to_i; b = color[2].to_i
|
52
|
+
array = Array.new(8)
|
53
|
+
while i < 8
|
54
|
+
array[7 - i] = (r & 1) | (g & 1) << 1 | (b & 1) << 2
|
55
|
+
r >>= 1; g >>= 1; b >>= 1
|
56
|
+
i += 1
|
57
|
+
end
|
58
|
+
array
|
59
|
+
end
|
60
|
+
|
61
|
+
# Optimized path for (r,g,b,a) colors.
|
62
|
+
def key_array4(color)
|
63
|
+
i = 0
|
64
|
+
r = color[0].to_i; g = color[1].to_i; b = color[2].to_i; a = color[3].to_i
|
65
|
+
array = Array.new(8)
|
66
|
+
while i < 8
|
67
|
+
array[7 - i] = (r & 1) | (g & 1) << 1 | (b & 1) << 2 | (a & 1) << 3
|
68
|
+
r >>= 1; g >>= 1; b >>= 1; a >>= 1
|
69
|
+
i += 1
|
70
|
+
end
|
71
|
+
array
|
72
|
+
end
|
73
|
+
|
74
|
+
# rubocop:enable Style/Semicolon
|
75
|
+
|
76
|
+
def build_from(colors)
|
77
|
+
colors.each do |color|
|
78
|
+
color_path = case color.length
|
79
|
+
when 3
|
80
|
+
key_array3(color)
|
81
|
+
when 4
|
82
|
+
key_array4(color)
|
83
|
+
else
|
84
|
+
key_array(color)
|
85
|
+
end
|
86
|
+
|
87
|
+
dig(*color_path[0..-2])[color_path[-1]] = color
|
88
|
+
end
|
89
|
+
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
# rubocop:disable Style/StringConcatenation
|
94
|
+
def inspect
|
95
|
+
"#<Octree:[" +
|
96
|
+
filter_map.with_index do |i,idx|
|
97
|
+
"0b#{idx.to_s(2)} => #{i.inspect}," if i
|
98
|
+
end.join(", ") +
|
99
|
+
"]>"
|
100
|
+
end
|
101
|
+
# rubocop:enable Style/StringConcatenation
|
102
|
+
|
103
|
+
def available_with_index
|
104
|
+
each_with_index.select(&:first)
|
105
|
+
end
|
106
|
+
|
107
|
+
def pretty_print(pp)
|
108
|
+
pp.group(1, "#<Octree:", ">") do
|
109
|
+
pp.breakable ""
|
110
|
+
pp.group(1, "[", "]") do
|
111
|
+
pp.seplist(available_with_index) do |i,idx|
|
112
|
+
pp.text "0b#{idx.to_s(2)}"
|
113
|
+
pp.text " => "
|
114
|
+
pp.pp i
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def empty?
|
121
|
+
compact.empty?
|
122
|
+
end
|
123
|
+
|
124
|
+
def take(n)
|
125
|
+
taken = []
|
126
|
+
length.times do |idx|
|
127
|
+
next unless (i = self[idx])
|
128
|
+
|
129
|
+
taken << i
|
130
|
+
self[idx] = nil
|
131
|
+
|
132
|
+
if taken.length >= n - 1 && !empty?
|
133
|
+
taken << self
|
134
|
+
break
|
135
|
+
elsif taken.length >= n
|
136
|
+
break
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
raise if taken.length > n
|
141
|
+
|
142
|
+
taken
|
143
|
+
end
|
144
|
+
|
145
|
+
def colors
|
146
|
+
select { |i| i.is_a? Color } +
|
147
|
+
select { |i| i.is_a? ColorOctree }.map { |i| i.colors }.flatten(1)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def palette_reduce!(count)
|
152
|
+
colors = unique_colors
|
153
|
+
|
154
|
+
return self if colors.length <= count
|
155
|
+
|
156
|
+
octree = ColorOctree.new
|
157
|
+
octree.build_from(colors)
|
158
|
+
|
159
|
+
queue = [octree]
|
160
|
+
while queue.length < count
|
161
|
+
elem = queue.shift
|
162
|
+
case elem
|
163
|
+
when Color
|
164
|
+
queue << elem
|
165
|
+
when ColorOctree
|
166
|
+
needed = count - queue.length
|
167
|
+
got = elem.take(needed)
|
168
|
+
queue += got
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
equiv = {}
|
173
|
+
|
174
|
+
queue.each do |i|
|
175
|
+
case i
|
176
|
+
when Color
|
177
|
+
equiv[i] = i
|
178
|
+
when ColorOctree
|
179
|
+
colors = i.colors
|
180
|
+
picked = colors[colors.length / 2]
|
181
|
+
colors.each do |color|
|
182
|
+
equiv[color] = picked
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
set_each_pixel_by_location! do |loc|
|
188
|
+
equiv[self[*loc]]
|
189
|
+
end
|
190
|
+
|
191
|
+
self
|
192
|
+
end
|
193
|
+
|
194
|
+
define_immutable_version :palette_reduce
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImageUtil
|
4
|
+
module Filter
|
5
|
+
module Redimension
|
6
|
+
extend ImageUtil::Filter::Mixin
|
7
|
+
|
8
|
+
def redimension!(*new_dimensions)
|
9
|
+
if fast_redimension?(new_dimensions)
|
10
|
+
begin
|
11
|
+
resize_buffer!(new_dimensions)
|
12
|
+
return self
|
13
|
+
rescue StandardError
|
14
|
+
# fall back to generic implementation
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
out = Image.new(*new_dimensions, color_bits: color_bits, channels: channels)
|
19
|
+
|
20
|
+
copy_counts = new_dimensions.map.with_index do |dim, idx|
|
21
|
+
[dim, dimensions[idx] || 1].min
|
22
|
+
end
|
23
|
+
|
24
|
+
ranges = copy_counts[1..] || []
|
25
|
+
each_coordinates(ranges) do |coords|
|
26
|
+
src_buf = row_buffer(coords)
|
27
|
+
out.buffer.copy_1d(src_buf, 0, *coords)
|
28
|
+
end
|
29
|
+
|
30
|
+
initialize_from_buffer(out.buffer)
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
define_immutable_version :redimension
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def fast_redimension?(new_dimensions)
|
39
|
+
io = buffer.io_buffer
|
40
|
+
return false unless io.respond_to?(:resize)
|
41
|
+
return false if io.external? || io.locked?
|
42
|
+
|
43
|
+
dims = dimensions
|
44
|
+
|
45
|
+
min_len = [dims.length, new_dimensions.length].min
|
46
|
+
idx = 0
|
47
|
+
idx += 1 while idx < min_len && dims[idx] == new_dimensions[idx]
|
48
|
+
|
49
|
+
return false if idx == dims.length && idx == new_dimensions.length
|
50
|
+
return false unless (dims[(idx + 1)..] || []).all? { |d| d == 1 }
|
51
|
+
return false unless (dims[new_dimensions.length..] || []).all? { |d| d == 1 }
|
52
|
+
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def resize_buffer!(new_dimensions)
|
57
|
+
new_size = new_dimensions.reduce(1, :*)
|
58
|
+
new_size *= channels
|
59
|
+
new_size *= color_bits / 8
|
60
|
+
buffer.io_buffer.resize(new_size)
|
61
|
+
initialize_from_buffer(Image::Buffer.new(new_dimensions, color_bits, channels, buffer.io_buffer))
|
62
|
+
end
|
63
|
+
|
64
|
+
def each_coordinates(ranges, prefix = [], &block)
|
65
|
+
if ranges.empty?
|
66
|
+
yield prefix
|
67
|
+
else
|
68
|
+
ranges.first.times do |i|
|
69
|
+
each_coordinates(ranges[1..], prefix + [i], &block)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def row_buffer(coords)
|
75
|
+
buf = buffer
|
76
|
+
coords_src = coords[0, dimensions.length - 1]
|
77
|
+
coords_src += [0] * (dimensions.length - 1 - coords_src.length)
|
78
|
+
coords_src.reverse_each { |c| buf = buf.last_dimension(c) }
|
79
|
+
buf
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -10,7 +10,7 @@ module ImageUtil
|
|
10
10
|
new_dim == 1 ? 0.0 : (old_dim - 1).to_f / (new_dim - 1)
|
11
11
|
end
|
12
12
|
|
13
|
-
Image.new(*new_dimensions, color_bits: color_bits,
|
13
|
+
Image.new(*new_dimensions, color_bits: color_bits, channels: channels) do |loc|
|
14
14
|
src_loc = loc.zip(factors).map { |coord, factor| coord * factor }
|
15
15
|
src[*src_loc]
|
16
16
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImageUtil
|
4
|
+
module Filter
|
5
|
+
module Transform
|
6
|
+
extend ImageUtil::Filter::Mixin
|
7
|
+
|
8
|
+
def flip!(axis = :x)
|
9
|
+
axis = Filter::Mixin.axis_to_number(axis)
|
10
|
+
dims = dimensions
|
11
|
+
out = Image.new(*dims, color_bits: color_bits, channels: channels)
|
12
|
+
each_pixel_location do |loc|
|
13
|
+
new_loc = loc.dup
|
14
|
+
new_loc[axis] = dims[axis] - 1 - loc[axis]
|
15
|
+
out[*new_loc] = self[*loc]
|
16
|
+
end
|
17
|
+
initialize_from_buffer(out.buffer)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def rotate!(angle, axes: %i[x y])
|
22
|
+
axes = axes.map { |a| Filter::Mixin.axis_to_number(a) }
|
23
|
+
turns = (angle.to_f / 90).round % 4
|
24
|
+
turns += 4 if turns.negative?
|
25
|
+
turns.times { rotate90_once!(*axes) }
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
define_immutable_version :flip, :rotate
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def rotate90_once!(axis1 = 0, axis2 = 1)
|
34
|
+
dims = dimensions
|
35
|
+
new_dims = dims.dup
|
36
|
+
new_dims[axis1], new_dims[axis2] = dims[axis2], dims[axis1] # rubocop:disable Style/ParallelAssignment
|
37
|
+
out = Image.new(*new_dims, color_bits: color_bits, channels: channels)
|
38
|
+
each_pixel_location do |loc|
|
39
|
+
new_loc = loc.dup
|
40
|
+
new_loc[axis1] = dims[axis2] - 1 - loc[axis2]
|
41
|
+
new_loc[axis2] = loc[axis1]
|
42
|
+
out[*new_loc] = self[*loc]
|
43
|
+
end
|
44
|
+
initialize_from_buffer(out.buffer)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/image_util/filter.rb
CHANGED
@@ -2,11 +2,15 @@
|
|
2
2
|
|
3
3
|
module ImageUtil
|
4
4
|
module Filter
|
5
|
-
autoload :
|
5
|
+
autoload :Palette, "image_util/filter/palette"
|
6
6
|
autoload :Background, "image_util/filter/background"
|
7
7
|
autoload :Paste, "image_util/filter/paste"
|
8
8
|
autoload :Draw, "image_util/filter/draw"
|
9
9
|
autoload :Resize, "image_util/filter/resize"
|
10
|
+
autoload :Transform, "image_util/filter/transform"
|
11
|
+
autoload :Redimension, "image_util/filter/redimension"
|
12
|
+
autoload :Colors, "image_util/filter/colors"
|
13
|
+
autoload :BitmapText, "image_util/filter/bitmap_text"
|
10
14
|
|
11
15
|
autoload :Mixin, "image_util/filter/_mixin"
|
12
16
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImageUtil
|
4
|
+
module Generator
|
5
|
+
module BitmapText
|
6
|
+
def bitmap_text(text, font: BitmapFont.default_font, color: nil, align: :left)
|
7
|
+
fnt = BitmapFont.cached_load(font)
|
8
|
+
lines = text.split("\n")
|
9
|
+
|
10
|
+
rendered = lines.map { |line| fnt.render_line_of_text(line) }
|
11
|
+
|
12
|
+
width = rendered.map(&:width).max || 0
|
13
|
+
height = rendered.map(&:height).first.to_i * rendered.length
|
14
|
+
height += rendered.length - 1 if rendered.length > 1
|
15
|
+
|
16
|
+
out = Image.new(width, height)
|
17
|
+
y = 0
|
18
|
+
rendered.each do |img|
|
19
|
+
x = case align
|
20
|
+
when :left
|
21
|
+
0
|
22
|
+
when :center
|
23
|
+
(width - img.width) / 2
|
24
|
+
when :right
|
25
|
+
width - img.width
|
26
|
+
else
|
27
|
+
raise ArgumentError, "invalid alignment #{align.inspect}"
|
28
|
+
end
|
29
|
+
out.paste!(img, x, y)
|
30
|
+
y += img.height + 1
|
31
|
+
end
|
32
|
+
|
33
|
+
out *= color if color
|
34
|
+
out
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
Binary file
|
@@ -6,7 +6,7 @@ Warning[:experimental] = false
|
|
6
6
|
module ImageUtil
|
7
7
|
class Image
|
8
8
|
class Buffer
|
9
|
-
def initialize(dimensions, color_bits,
|
9
|
+
def initialize(dimensions, color_bits, channels, buffer = nil)
|
10
10
|
@color_type = case color_bits
|
11
11
|
when 8
|
12
12
|
:U8
|
@@ -21,13 +21,15 @@ module ImageUtil
|
|
21
21
|
@dimensions = dimensions.freeze
|
22
22
|
@color_bits = color_bits
|
23
23
|
@color_bytes = color_bits / 8
|
24
|
-
@
|
24
|
+
@channels = channels
|
25
25
|
|
26
26
|
@buffer_size = @dimensions.reduce(&:*)
|
27
|
-
@buffer_size *= @
|
27
|
+
@buffer_size *= @channels
|
28
28
|
@buffer_size *= @color_bytes
|
29
29
|
|
30
|
-
@
|
30
|
+
@pixel_bytes = @channels * @color_bytes
|
31
|
+
|
32
|
+
@io_buffer_types = ([@color_type]*@channels).freeze
|
31
33
|
|
32
34
|
@buffer = buffer || IO::Buffer.new(@buffer_size)
|
33
35
|
|
@@ -36,7 +38,7 @@ module ImageUtil
|
|
36
38
|
freeze
|
37
39
|
end
|
38
40
|
|
39
|
-
attr_reader :dimensions, :color_bits, :color_bytes, :
|
41
|
+
attr_reader :dimensions, :color_bits, :color_bytes, :channels, :pixel_bytes
|
40
42
|
|
41
43
|
def offset_of(*location)
|
42
44
|
location.length == @dimensions.length or raise ArgumentError, "wrong number of dimensions"
|
@@ -50,8 +52,6 @@ module ImageUtil
|
|
50
52
|
offset * pixel_bytes
|
51
53
|
end
|
52
54
|
|
53
|
-
def pixel_bytes = @color_length * @color_bytes
|
54
|
-
|
55
55
|
def initialize_copy(_other)
|
56
56
|
@buffer = @buffer.dup
|
57
57
|
end
|
@@ -62,7 +62,7 @@ module ImageUtil
|
|
62
62
|
|
63
63
|
def get_index(index)
|
64
64
|
value = @buffer.get_values(@io_buffer_types, index)
|
65
|
-
Color.from_buffer(value, @color_bits)
|
65
|
+
Color.from_buffer(value, @color_bits).freeze
|
66
66
|
end
|
67
67
|
|
68
68
|
def set(location, value)
|
@@ -70,7 +70,7 @@ module ImageUtil
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def set_index(index, value)
|
73
|
-
value = Color.
|
73
|
+
value = Color.from_any_to_buffer(value, @color_bits, @channels)
|
74
74
|
@buffer.set_values(@io_buffer_types, index, value)
|
75
75
|
end
|
76
76
|
|
@@ -82,7 +82,7 @@ module ImageUtil
|
|
82
82
|
Buffer.new(
|
83
83
|
dimensions_without_last,
|
84
84
|
@color_bits,
|
85
|
-
@
|
85
|
+
@channels,
|
86
86
|
@buffer.slice(o0, o1 - o0)
|
87
87
|
)
|
88
88
|
end
|
@@ -126,7 +126,7 @@ module ImageUtil
|
|
126
126
|
}.freeze
|
127
127
|
|
128
128
|
OPT_GET_INDEX = {
|
129
|
-
8 => ->(index) { Color.new(*@buffer.get_values(@io_buffer_types, index)) }
|
129
|
+
8 => ->(index) { Color.new(*@buffer.get_values(@io_buffer_types, index)).freeze }
|
130
130
|
}.freeze
|
131
131
|
end
|
132
132
|
end
|