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
data/lib/image_util/image.rb
CHANGED
@@ -3,16 +3,15 @@
|
|
3
3
|
module ImageUtil
|
4
4
|
class Image
|
5
5
|
autoload :Buffer, "image_util/image/buffer"
|
6
|
-
autoload :PixelView, "image_util/image/pixel_view"
|
7
6
|
|
8
7
|
Util.irb_fixup
|
9
8
|
|
10
9
|
ALL = nil..nil
|
11
10
|
|
12
|
-
def initialize(*dimensions, color_bits: 8,
|
13
|
-
@buf = Buffer.new(dimensions, color_bits,
|
11
|
+
def initialize(*dimensions, color_bits: 8, channels: 4, &block)
|
12
|
+
@buf = Buffer.new(dimensions, color_bits, channels)
|
14
13
|
|
15
|
-
set_each_pixel_by_location(&block) if block_given?
|
14
|
+
set_each_pixel_by_location!(&block) if block_given?
|
16
15
|
end
|
17
16
|
|
18
17
|
def initialize_from_buffer(buffer)
|
@@ -66,10 +65,10 @@ module ImageUtil
|
|
66
65
|
def height = dimensions[1]
|
67
66
|
def length = dimensions[2]
|
68
67
|
def color_bits = @buf.color_bits
|
69
|
-
def
|
68
|
+
def channels = @buf.channels
|
70
69
|
def pixel_bytes = @buf.pixel_bytes
|
71
70
|
|
72
|
-
def location_expand(location)
|
71
|
+
def location_expand(location = full_image_location)
|
73
72
|
counts = []
|
74
73
|
|
75
74
|
location = location.reverse.map.with_index do |i,idx|
|
@@ -109,10 +108,10 @@ module ImageUtil
|
|
109
108
|
new_dimensions, locations = location_expand(location)
|
110
109
|
new_image = Image.new(*new_dimensions,
|
111
110
|
color_bits: color_bits,
|
112
|
-
|
111
|
+
channels: channels)
|
113
112
|
|
114
113
|
locations.each_with_index do |i, idx|
|
115
|
-
new_image.buffer.set_index(idx *
|
114
|
+
new_image.buffer.set_index(idx * pixel_bytes, @buf.get(i))
|
116
115
|
end
|
117
116
|
|
118
117
|
new_image
|
@@ -169,28 +168,39 @@ module ImageUtil
|
|
169
168
|
end
|
170
169
|
|
171
170
|
include Enumerable
|
172
|
-
include Filter::
|
171
|
+
include Filter::Palette
|
173
172
|
include Filter::Background
|
174
173
|
include Filter::Paste
|
175
174
|
include Filter::Draw
|
176
175
|
include Filter::Resize
|
177
|
-
include
|
176
|
+
include Filter::Transform
|
177
|
+
include Filter::Redimension
|
178
|
+
include Filter::Colors
|
179
|
+
include Filter::BitmapText
|
180
|
+
include Statistic::Colors
|
181
|
+
extend Generator::BitmapText
|
182
|
+
extend Generator::Example
|
178
183
|
|
179
184
|
def length = dimensions.last
|
180
185
|
|
181
|
-
def to_pam
|
182
|
-
Codec.encode(:pam, self
|
186
|
+
def to_pam
|
187
|
+
Codec.encode(:pam, self)
|
183
188
|
end
|
184
189
|
|
185
190
|
def to_string(format, codec: nil, **kwargs)
|
186
191
|
Codec.encode(format, self, codec: codec, **kwargs)
|
187
192
|
end
|
188
193
|
|
189
|
-
def to_file(path_or_io, format, codec: nil, **kwargs)
|
194
|
+
def to_file(path_or_io, format = nil, codec: nil, **kwargs)
|
190
195
|
if path_or_io.respond_to?(:write)
|
196
|
+
raise ArgumentError, "format required" unless format
|
197
|
+
|
191
198
|
path_or_io.binmode if path_or_io.respond_to?(:binmode)
|
192
199
|
Codec.encode_io(format, self, path_or_io, codec: codec, **kwargs)
|
193
200
|
else
|
201
|
+
format ||= Extension.detect(path_or_io)
|
202
|
+
raise ArgumentError, "could not detect format" unless format
|
203
|
+
|
194
204
|
File.open(path_or_io, "wb") do |io|
|
195
205
|
Codec.encode_io(format, self, io, codec: codec, **kwargs)
|
196
206
|
end
|
@@ -201,12 +211,14 @@ module ImageUtil
|
|
201
211
|
Codec.encode(:sixel, self)
|
202
212
|
end
|
203
213
|
|
204
|
-
alias inspect to_sixel
|
205
|
-
|
206
214
|
def pretty_print(p)
|
207
|
-
|
208
|
-
|
209
|
-
|
215
|
+
if (image = Terminal.output_image($stdin, $stdout, self))
|
216
|
+
p.flush
|
217
|
+
p.output << image
|
218
|
+
p.text("", 0)
|
219
|
+
else
|
220
|
+
super
|
221
|
+
end
|
210
222
|
end
|
211
223
|
|
212
224
|
def pixel_count(locations) = location_expand(locations).first.reduce(:*)
|
@@ -223,12 +235,26 @@ module ImageUtil
|
|
223
235
|
end
|
224
236
|
end
|
225
237
|
|
226
|
-
def set_each_pixel_by_location(locations = full_image_location)
|
227
|
-
return enum_for(:set_each_pixel_by_location) { pixel_count(locations) } unless block_given?
|
238
|
+
def set_each_pixel_by_location!(locations = full_image_location)
|
239
|
+
return enum_for(:set_each_pixel_by_location!) { pixel_count(locations) } unless block_given?
|
228
240
|
|
229
|
-
|
230
|
-
|
231
|
-
|
241
|
+
if locations == full_image_location
|
242
|
+
# Optimized path
|
243
|
+
pixels, locations = location_expand
|
244
|
+
|
245
|
+
iter = 0
|
246
|
+
pixels = pixels.reduce(:*)
|
247
|
+
|
248
|
+
while iter < pixels
|
249
|
+
value = yield locations[iter]
|
250
|
+
buffer.set_index(iter * pixel_bytes, value) if value
|
251
|
+
iter += 1
|
252
|
+
end
|
253
|
+
else
|
254
|
+
each_pixel_location(locations) do |location|
|
255
|
+
value = yield location
|
256
|
+
self[*location] = value if value
|
257
|
+
end
|
232
258
|
end
|
233
259
|
end
|
234
260
|
|
data/lib/image_util/magic.rb
CHANGED
@@ -7,7 +7,8 @@ module ImageUtil
|
|
7
7
|
MAGIC_NUMBERS = {
|
8
8
|
pam: "P7\n".b,
|
9
9
|
png: "\x89PNG\r\n\x1a\n".b,
|
10
|
-
jpeg: "\xFF\xD8".b
|
10
|
+
jpeg: "\xFF\xD8".b,
|
11
|
+
gif: "GIF8".b
|
11
12
|
}.freeze
|
12
13
|
|
13
14
|
BYTES_NEEDED = MAGIC_NUMBERS.values.map(&:bytesize).max
|
@@ -19,16 +20,18 @@ module ImageUtil
|
|
19
20
|
def detect(data)
|
20
21
|
return nil unless data
|
21
22
|
|
23
|
+
if data.start_with?(MAGIC_NUMBERS[:png]) && data.byteslice(0, 256).include?("acTL")
|
24
|
+
return :apng
|
25
|
+
end
|
26
|
+
|
22
27
|
MAGIC_NUMBERS.each do |fmt, magic|
|
23
28
|
return fmt if data.start_with?(magic)
|
24
|
-
crlf_magic = magic.gsub("\n", "\r\n")
|
25
|
-
return fmt if crlf_magic != magic && data.start_with?(crlf_magic)
|
26
29
|
end
|
30
|
+
|
27
31
|
nil
|
28
32
|
end
|
29
33
|
|
30
|
-
def detect_io(io)
|
31
|
-
|
34
|
+
def detect_io(io)
|
32
35
|
pos = io.pos
|
33
36
|
data = io.read(BYTES_NEEDED)
|
34
37
|
io.seek(pos)
|
@@ -39,7 +42,6 @@ module ImageUtil
|
|
39
42
|
rest = io.read
|
40
43
|
new_io = StringIO.new((data || "") + (rest || ""))
|
41
44
|
[fmt, new_io]
|
42
|
-
|
43
45
|
end
|
44
46
|
end
|
45
47
|
end
|
data/lib/image_util/statistic.rb
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "io/console"
|
4
|
+
|
5
|
+
module ImageUtil
|
6
|
+
module Terminal
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def detect_support(termin = $stdin, termout = $stdout)
|
10
|
+
return [] if !termin.tty? || !termout.tty?
|
11
|
+
|
12
|
+
supported = termout.instance_variable_get(:@imageutil_support_cache)
|
13
|
+
return supported if supported
|
14
|
+
|
15
|
+
supported = [:tty]
|
16
|
+
|
17
|
+
# Send kitty query
|
18
|
+
query_terminal(termin, termout, "\e_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\e\\\e[c".b) do |resp|
|
19
|
+
resp.start_with?("\e_G".b) && resp.include?("OK".b)
|
20
|
+
end and supported << :kitty
|
21
|
+
|
22
|
+
# Send sixel query
|
23
|
+
query_terminal(termin, termout, "\e[0c".b) do |resp|
|
24
|
+
resp.include?(";4".b)
|
25
|
+
end and supported << :sixel
|
26
|
+
|
27
|
+
termout.instance_variable_set(:@imageutil_support_cache, supported)
|
28
|
+
end
|
29
|
+
|
30
|
+
def query_terminal(termin, termout, query, timeout = 0.2)
|
31
|
+
resp = ""
|
32
|
+
termin.raw do
|
33
|
+
termout.write query
|
34
|
+
termout.flush
|
35
|
+
t0 = Time.now
|
36
|
+
loop do
|
37
|
+
begin
|
38
|
+
resp += termin.read_nonblock(512)
|
39
|
+
break if resp.start_with?("\e".b)
|
40
|
+
rescue IO::WaitReadable
|
41
|
+
IO.select([termin], nil, nil, timeout)
|
42
|
+
end
|
43
|
+
break if Time.now - t0 > timeout
|
44
|
+
end
|
45
|
+
end
|
46
|
+
yield resp
|
47
|
+
rescue EOFError, Errno::EBADF
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def output_image(termin, termout, image)
|
52
|
+
support = detect_support(termin, termout)
|
53
|
+
|
54
|
+
if support.include? :kitty
|
55
|
+
image.to_string(:kitty)
|
56
|
+
elsif support.include? :sixel
|
57
|
+
image.to_string(:sixel)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/image_util/version.rb
CHANGED
@@ -24,7 +24,7 @@ module ImageUtil
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def [](*location)
|
27
|
-
accum = Array.new(image.
|
27
|
+
accum = Array.new(image.channels, 0.0)
|
28
28
|
generate_subpixel_hash(location).each do |loc, weight|
|
29
29
|
image[*loc].each_with_index do |val, idx|
|
30
30
|
accum[idx] += val * weight
|
data/lib/image_util.rb
CHANGED
@@ -6,12 +6,18 @@ module ImageUtil
|
|
6
6
|
class Error < StandardError; end
|
7
7
|
# Your code goes here...
|
8
8
|
|
9
|
+
autoload :BitmapFont, "image_util/bitmap_font"
|
9
10
|
autoload :Color, "image_util/color"
|
10
11
|
autoload :Image, "image_util/image"
|
11
12
|
autoload :Util, "image_util/util"
|
12
13
|
autoload :Codec, "image_util/codec"
|
13
14
|
autoload :Magic, "image_util/magic"
|
15
|
+
autoload :Extension, "image_util/extension"
|
14
16
|
autoload :Filter, "image_util/filter"
|
17
|
+
autoload :Generator, "image_util/generator"
|
15
18
|
autoload :Statistic, "image_util/statistic"
|
19
|
+
autoload :Terminal, "image_util/terminal"
|
16
20
|
autoload :View, "image_util/view"
|
21
|
+
autoload :CLI, "image_util/cli"
|
22
|
+
autoload :Benchmarking, "image_util/benchmarking"
|
17
23
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: image_util
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hmdne
|
@@ -9,6 +9,20 @@ bindir: exe
|
|
9
9
|
cert_chain: []
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: base64
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
12
26
|
- !ruby/object:Gem::Dependency
|
13
27
|
name: ffi
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -23,11 +37,40 @@ dependencies:
|
|
23
37
|
- - "~>"
|
24
38
|
- !ruby/object:Gem::Version
|
25
39
|
version: '1.16'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: io-console
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.5'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.5'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: thor
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.2'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.2'
|
26
68
|
description: Lightweight Color and Image classes for manipulating pixels. Provides
|
27
69
|
SIXEL output plus FFI bindings for libpng, libturbojpeg and libsixel.
|
28
70
|
email:
|
29
71
|
- 54514036+hmdne@users.noreply.github.com
|
30
|
-
executables:
|
72
|
+
executables:
|
73
|
+
- image_util
|
31
74
|
extensions: []
|
32
75
|
extra_rdoc_files: []
|
33
76
|
files:
|
@@ -38,16 +81,34 @@ files:
|
|
38
81
|
- LICENSE.txt
|
39
82
|
- README.md
|
40
83
|
- Rakefile
|
84
|
+
- docs/cli.md
|
41
85
|
- docs/samples/background.png
|
86
|
+
- docs/samples/bitmap_text.png
|
87
|
+
- docs/samples/colors.png
|
88
|
+
- docs/samples/constructor.png
|
42
89
|
- docs/samples/dither.png
|
43
90
|
- docs/samples/draw.png
|
91
|
+
- docs/samples/iterator.png
|
44
92
|
- docs/samples/paste.png
|
93
|
+
- docs/samples/pdither.png
|
94
|
+
- docs/samples/pipe.png
|
95
|
+
- docs/samples/range.png
|
96
|
+
- docs/samples/redimension.png
|
45
97
|
- docs/samples/resize.png
|
46
98
|
- docs/samples/sixel.png
|
99
|
+
- docs/samples/transform.png
|
100
|
+
- exe/image_util
|
47
101
|
- lib/image_util.rb
|
102
|
+
- lib/image_util/benchmarking.rb
|
103
|
+
- lib/image_util/bitmap_font.rb
|
104
|
+
- lib/image_util/bitmap_font/fonts/smfont/charset.txt
|
105
|
+
- lib/image_util/bitmap_font/fonts/smfont/font.png
|
106
|
+
- lib/image_util/cli.rb
|
48
107
|
- lib/image_util/codec.rb
|
49
108
|
- lib/image_util/codec/_guard.rb
|
109
|
+
- lib/image_util/codec/chunky_png.rb
|
50
110
|
- lib/image_util/codec/image_magick.rb
|
111
|
+
- lib/image_util/codec/kitty.rb
|
51
112
|
- lib/image_util/codec/libpng.rb
|
52
113
|
- lib/image_util/codec/libsixel.rb
|
53
114
|
- lib/image_util/codec/libturbojpeg.rb
|
@@ -55,18 +116,28 @@ files:
|
|
55
116
|
- lib/image_util/codec/ruby_sixel.rb
|
56
117
|
- lib/image_util/color.rb
|
57
118
|
- lib/image_util/color/css_colors.rb
|
119
|
+
- lib/image_util/extension.rb
|
58
120
|
- lib/image_util/filter.rb
|
59
121
|
- lib/image_util/filter/_mixin.rb
|
60
122
|
- lib/image_util/filter/background.rb
|
61
|
-
- lib/image_util/filter/
|
123
|
+
- lib/image_util/filter/bitmap_text.rb
|
124
|
+
- lib/image_util/filter/colors.rb
|
62
125
|
- lib/image_util/filter/draw.rb
|
126
|
+
- lib/image_util/filter/palette.rb
|
63
127
|
- lib/image_util/filter/paste.rb
|
128
|
+
- lib/image_util/filter/redimension.rb
|
64
129
|
- lib/image_util/filter/resize.rb
|
130
|
+
- lib/image_util/filter/transform.rb
|
131
|
+
- lib/image_util/generator.rb
|
132
|
+
- lib/image_util/generator/bitmap_text.rb
|
133
|
+
- lib/image_util/generator/example.rb
|
134
|
+
- lib/image_util/generator/example/rose.png
|
65
135
|
- lib/image_util/image.rb
|
66
136
|
- lib/image_util/image/buffer.rb
|
67
137
|
- lib/image_util/magic.rb
|
68
138
|
- lib/image_util/statistic.rb
|
69
|
-
- lib/image_util/statistic/
|
139
|
+
- lib/image_util/statistic/colors.rb
|
140
|
+
- lib/image_util/terminal.rb
|
70
141
|
- lib/image_util/util.rb
|
71
142
|
- lib/image_util/version.rb
|
72
143
|
- lib/image_util/view.rb
|
@@ -1,96 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ImageUtil
|
4
|
-
module Filter
|
5
|
-
module Dither
|
6
|
-
extend ImageUtil::Filter::Mixin
|
7
|
-
|
8
|
-
private
|
9
|
-
|
10
|
-
def dither_distance_sq(c1, c2)
|
11
|
-
len = [c1.length, c2.length].max
|
12
|
-
|
13
|
-
case len
|
14
|
-
when 1
|
15
|
-
d = (c1[0] || 255) - (c2[0] || 255)
|
16
|
-
d * d
|
17
|
-
when 2
|
18
|
-
d0 = (c1[0] || 255) - (c2[0] || 255)
|
19
|
-
d1 = (c1[1] || 255) - (c2[1] || 255)
|
20
|
-
d0 * d0 + d1 * d1
|
21
|
-
when 3
|
22
|
-
d0 = (c1[0] || 255) - (c2[0] || 255)
|
23
|
-
d1 = (c1[1] || 255) - (c2[1] || 255)
|
24
|
-
d2 = (c1[2] || 255) - (c2[2] || 255)
|
25
|
-
d0 * d0 + d1 * d1 + d2 * d2
|
26
|
-
when 4
|
27
|
-
d0 = (c1[0] || 255) - (c2[0] || 255)
|
28
|
-
d1 = (c1[1] || 255) - (c2[1] || 255)
|
29
|
-
d2 = (c1[2] || 255) - (c2[2] || 255)
|
30
|
-
d3 = (c1[3] || 255) - (c2[3] || 255)
|
31
|
-
d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3
|
32
|
-
else
|
33
|
-
sum = 0
|
34
|
-
len.times do |i|
|
35
|
-
d = (c1[i] || 255) - (c2[i] || 255)
|
36
|
-
sum += d * d
|
37
|
-
end
|
38
|
-
sum
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
public
|
43
|
-
|
44
|
-
def dither!(count)
|
45
|
-
palette = histogram.sort_by { |_, v| -v - rand }.first(count).map(&:first)
|
46
|
-
|
47
|
-
cache = {}
|
48
|
-
|
49
|
-
nearest = lambda do |color|
|
50
|
-
key = (color[0] || 255) |
|
51
|
-
((color[1] || 255) << 8) |
|
52
|
-
((color[2] || 255) << 16) |
|
53
|
-
((color[3] || 255) << 24)
|
54
|
-
cache[key] ||= begin
|
55
|
-
best = palette.first
|
56
|
-
best_dist = dither_distance_sq(color, best)
|
57
|
-
idx = 1
|
58
|
-
while idx < palette.length
|
59
|
-
c = palette[idx]
|
60
|
-
dist = dither_distance_sq(color, c)
|
61
|
-
if dist < best_dist
|
62
|
-
best = c
|
63
|
-
best_dist = dist
|
64
|
-
end
|
65
|
-
idx += 1
|
66
|
-
end
|
67
|
-
best
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
if dimensions.length == 2
|
72
|
-
w = width
|
73
|
-
h = height
|
74
|
-
buf = buffer
|
75
|
-
idx = 0
|
76
|
-
step = buf.pixel_bytes
|
77
|
-
h.times do
|
78
|
-
w.times do
|
79
|
-
color = buf.get_index(idx)
|
80
|
-
buf.set_index(idx, nearest.call(color))
|
81
|
-
idx += step
|
82
|
-
end
|
83
|
-
end
|
84
|
-
else
|
85
|
-
set_each_pixel_by_location do |loc|
|
86
|
-
color = self[*loc]
|
87
|
-
nearest.call(color)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
self
|
91
|
-
end
|
92
|
-
|
93
|
-
define_immutable_version :dither
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|