color-tools 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +35 -1
- data/Install +11 -2
- data/README +44 -12
- data/Rakefile +11 -2
- data/lib/color.rb +35 -158
- data/lib/color/cmyk.rb +62 -18
- data/lib/color/css.rb +27 -0
- data/lib/color/grayscale.rb +43 -13
- data/lib/color/hsl.rb +130 -0
- data/lib/color/palette.rb +4 -0
- data/lib/color/palette/gimp.rb +47 -12
- data/lib/color/palette/monocontrast.rb +41 -11
- data/lib/color/rgb-colors.rb +189 -0
- data/lib/color/rgb.rb +163 -27
- data/lib/color/rgb/metallic.rb +28 -0
- data/lib/color/yiq.rb +35 -9
- data/metaconfig +11 -2
- data/pre-setup.rb +12 -3
- data/tests/test_cmyk.rb +116 -0
- data/tests/test_css.rb +28 -0
- data/tests/test_gimp.rb +79 -0
- data/tests/test_grayscale.rb +87 -0
- data/tests/test_hsl.rb +93 -0
- data/tests/test_monocontrast.rb +145 -0
- data/tests/test_rgb.rb +160 -0
- data/tests/test_yiq.rb +81 -0
- data/tests/testall.rb +20 -0
- metadata +23 -8
data/lib/color/css.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#--
|
2
|
+
# Colour management with Ruby.
|
3
|
+
#
|
4
|
+
# Copyright 2005 Austin Ziegler
|
5
|
+
# http://rubyforge.org/ruby-pdf/
|
6
|
+
#
|
7
|
+
# Licensed under a MIT-style licence.
|
8
|
+
#
|
9
|
+
# $Id: css.rb,v 1.1 2005/08/05 23:07:20 austin Exp $
|
10
|
+
#++
|
11
|
+
|
12
|
+
require 'color'
|
13
|
+
|
14
|
+
# This namespace contains some CSS colour names.
|
15
|
+
module Color::CSS
|
16
|
+
# Returns the RGB colour for name or +nil+ if the name is not valid.
|
17
|
+
def self.[](name)
|
18
|
+
@colors[name.to_s.downcase.to_sym]
|
19
|
+
end
|
20
|
+
|
21
|
+
@colors = {}
|
22
|
+
Color::RGB.constants.each do |const|
|
23
|
+
next if const == "PDF_FORMAT_STR"
|
24
|
+
next if const == "Metallic"
|
25
|
+
@colors[const.downcase.to_sym] ||= Color::RGB.const_get(const)
|
26
|
+
end
|
27
|
+
end
|
data/lib/color/grayscale.rb
CHANGED
@@ -3,22 +3,32 @@
|
|
3
3
|
#
|
4
4
|
# Copyright 2005 Austin Ziegler
|
5
5
|
# http://rubyforge.org/ruby-pdf/
|
6
|
+
#
|
7
|
+
# Licensed under a MIT-style licence.
|
8
|
+
#
|
9
|
+
# $Id: grayscale.rb,v 1.3 2005/08/08 02:44:17 austin Exp $
|
6
10
|
#++
|
7
11
|
|
8
12
|
# A colour object representing shades of grey. Used primarily in PDF
|
9
13
|
# document creation.
|
10
14
|
class Color::GrayScale
|
11
|
-
# The format
|
15
|
+
# The format of a DeviceGrey colour for PDF. In color-tools 2.0 this
|
16
|
+
# will be removed from this package and added back as a modification by
|
17
|
+
# the PDF::Writer package.
|
12
18
|
PDF_FORMAT_STR = "%.3f %s"
|
13
19
|
|
14
|
-
# Creates a greyscale colour object from fractional values 0
|
20
|
+
# Creates a greyscale colour object from fractional values 0..1.
|
21
|
+
#
|
22
|
+
# Color::GreyScale.from_fraction(0.5)
|
15
23
|
def self.from_fraction(g = 0)
|
16
24
|
color = Color::GrayScale.new
|
17
25
|
color.g = g
|
18
26
|
color
|
19
27
|
end
|
20
28
|
|
21
|
-
# Creates a greyscale colour object from percentages 0
|
29
|
+
# Creates a greyscale colour object from percentages 0..100.
|
30
|
+
#
|
31
|
+
# Color::GrayScale.new(50)
|
22
32
|
def initialize(g = 0)
|
23
33
|
@g = g / 100.0
|
24
34
|
end
|
@@ -27,18 +37,23 @@ class Color::GrayScale
|
|
27
37
|
# converted to GreyScale before comparison, so the comparison between a
|
28
38
|
# GreyScale colour and a non-GreyScale colour will be approximate and
|
29
39
|
# based on the other colour's #to_greyscale conversion. If there is no
|
30
|
-
# #to_greyscale conversion, this will raise an exception.
|
40
|
+
# #to_greyscale conversion, this will raise an exception. This will
|
41
|
+
# report that two GreyScale values are equivalent if they are within
|
42
|
+
# 1e-4 (0.0001) of each other.
|
31
43
|
def ==(other)
|
32
44
|
other = other.to_grayscale
|
33
|
-
other.kind_of?(Color::GrayScale) and
|
45
|
+
other.kind_of?(Color::GrayScale) and
|
46
|
+
((@g - other.g).abs <= 1e-4)
|
34
47
|
end
|
35
48
|
|
36
|
-
# Present the colour as a fill colour string for PDF.
|
49
|
+
# Present the colour as a DeviceGrey fill colour string for PDF. This
|
50
|
+
# will be removed from the default package in color-tools 2.0.
|
37
51
|
def pdf_fill
|
38
52
|
PDF_FORMAT_STR % [ @g, "g" ]
|
39
53
|
end
|
40
54
|
|
41
|
-
# Present the colour as a stroke colour string for PDF.
|
55
|
+
# Present the colour as a DeviceGrey stroke colour string for PDF. This
|
56
|
+
# will be removed from the default package in color-tools 2.0.
|
42
57
|
def pdf_stroke
|
43
58
|
PDF_FORMAT_STR % [ @g, "G" ]
|
44
59
|
end
|
@@ -50,19 +65,19 @@ class Color::GrayScale
|
|
50
65
|
|
51
66
|
# Present the colour as an HTML/CSS colour string.
|
52
67
|
def html
|
53
|
-
gs = "%02x" %
|
68
|
+
gs = "%02x" % to_255
|
54
69
|
"##{gs * 3}"
|
55
70
|
end
|
56
71
|
|
57
72
|
# Convert the greyscale colour to CMYK.
|
58
73
|
def to_cmyk
|
59
74
|
k = 1.0 - @g.to_f
|
60
|
-
Color::CMYK.
|
75
|
+
Color::CMYK.from_fraction(0, 0, 0, k)
|
61
76
|
end
|
62
77
|
|
63
78
|
# Convert the greyscale colour to RGB.
|
64
79
|
def to_rgb(ignored = true)
|
65
|
-
g =
|
80
|
+
g = to_255
|
66
81
|
Color::RGB.new(g, g, g)
|
67
82
|
end
|
68
83
|
|
@@ -74,12 +89,12 @@ class Color::GrayScale
|
|
74
89
|
# Lightens the greyscale colour by the stated percent.
|
75
90
|
def lighten_by(percent)
|
76
91
|
g = [@g + (@g * (percent / 100.0)), 1.0].min
|
77
|
-
Color::
|
92
|
+
Color::GrayScale.from_fraction(g)
|
78
93
|
end
|
79
94
|
|
80
95
|
# Darken the RGB hue by the stated percent.
|
81
96
|
def darken_by(percent)
|
82
|
-
g = [@g - (@g * (percent / 100.0)),
|
97
|
+
g = [@g - (@g * (percent / 100.0)), 0.0].max
|
83
98
|
Color::GrayScale.from_fraction(g)
|
84
99
|
end
|
85
100
|
|
@@ -94,6 +109,11 @@ class Color::GrayScale
|
|
94
109
|
Color::YIQ.from_fraction(y, i, q)
|
95
110
|
end
|
96
111
|
|
112
|
+
# Returns the HSL colour encoding of the greyscale value.
|
113
|
+
def to_hsl
|
114
|
+
Color::HSL.from_fraction(0, 0, @g)
|
115
|
+
end
|
116
|
+
|
97
117
|
# Returns the brightness value for this greyscale value; this is the
|
98
118
|
# greyscale value.
|
99
119
|
def brightness
|
@@ -101,5 +121,15 @@ class Color::GrayScale
|
|
101
121
|
end
|
102
122
|
|
103
123
|
attr_accessor :g
|
124
|
+
remove_method :g= ;
|
125
|
+
def g=(gg) #:nodoc:
|
126
|
+
gg = 1.0 if gg > 1
|
127
|
+
gg = 0.0 if gg < 0
|
128
|
+
@g = gg
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
module Color
|
133
|
+
# A synonym for Color::GrayScale.
|
134
|
+
GreyScale = GrayScale
|
104
135
|
end
|
105
|
-
Color::GreyScale = Color::GrayScale
|
data/lib/color/hsl.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
#--
|
2
|
+
# Colour management with Ruby.
|
3
|
+
#
|
4
|
+
# Copyright 2005 Austin Ziegler
|
5
|
+
# http://rubyforge.org/ruby-pdf/
|
6
|
+
#
|
7
|
+
# Licensed under a MIT-style licence.
|
8
|
+
#
|
9
|
+
# $Id: hsl.rb,v 1.2 2005/08/08 02:44:17 austin Exp $
|
10
|
+
#++
|
11
|
+
|
12
|
+
# An HSL colour object. Internally, the hue (#h), saturation (#s), and
|
13
|
+
# luminosity (#l) values are dealt with as fractional values in the range
|
14
|
+
# 0..1.
|
15
|
+
class Color::HSL
|
16
|
+
class << self
|
17
|
+
# Creates an HSL colour object from fractional values 0..1.
|
18
|
+
def from_fraction(h = 0.0, s = 0.0, l = 0.0)
|
19
|
+
colour = Color::HSL.new
|
20
|
+
colour.h = h
|
21
|
+
colour.s = s
|
22
|
+
colour.l = l
|
23
|
+
colour
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Compares the other colour to this one. The other colour will be
|
28
|
+
# converted to HSL before comparison, so the comparison between a HSL
|
29
|
+
# colour and a non-HSL colour will be approximate and based on the other
|
30
|
+
# colour's #to_hsl conversion. If there is no #to_hsl conversion, this
|
31
|
+
# will raise an exception. This will report that two HSL values are
|
32
|
+
# equivalent if all component values are within 1e-4 (0.0001) of each
|
33
|
+
# other.
|
34
|
+
def ==(other)
|
35
|
+
other = other.to_hsl
|
36
|
+
other.kind_of?(Color::HSL) and
|
37
|
+
((@h - other.h).abs <= 1e-4) and
|
38
|
+
((@s - other.s).abs <= 1e-4) and
|
39
|
+
((@l - other.l).abs <= 1e-4)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Creates an HSL colour object from the standard values of degrees and
|
43
|
+
# percentages (e.g., 145�, 30%, 50%).
|
44
|
+
def initialize(h = 0, s = 0, l = 0)
|
45
|
+
@h = h / 360.0
|
46
|
+
@s = s / 100.0
|
47
|
+
@l = l / 100.0
|
48
|
+
end
|
49
|
+
|
50
|
+
# Present the colour as an HTML/CSS colour string.
|
51
|
+
def html
|
52
|
+
to_rgb.html
|
53
|
+
end
|
54
|
+
|
55
|
+
# Converting to HSL as adapted from Foley and Van-Dam from
|
56
|
+
# http://www.bobpowell.net/RGBHSB.htm.
|
57
|
+
def to_rgb(ignored = nil)
|
58
|
+
# If luminosity is zero, the colour is always black.
|
59
|
+
return Color::RGB.new if @l == 0
|
60
|
+
# If luminosity is one, the colour is always white.
|
61
|
+
return Color::RGB.new(0xff, 0xff, 0xff) if @l == 1
|
62
|
+
# If saturation is zero, the colour is always a greyscale colour.
|
63
|
+
return Color::RGB.new(@l, @l, @l) if @s <= 1e-5
|
64
|
+
|
65
|
+
if (@l - 0.5) < 1e-5
|
66
|
+
tmp2 = @l * (1.0 + @s.to_f)
|
67
|
+
else
|
68
|
+
tmp2 = @l + @s - (@l * @s.to_f)
|
69
|
+
end
|
70
|
+
tmp1 = 2.0 * @l - tmp2
|
71
|
+
|
72
|
+
t3 = [ @h + 1.0 / 3.0, @h, @h - 1.0 / 3.0 ]
|
73
|
+
t3 = t3.map { |tmp3|
|
74
|
+
tmp3 += 1.0 if tmp3 < 1e-5
|
75
|
+
tmp3 -= 1.0 if (tmp3 - 1.0) > 1e-5
|
76
|
+
tmp3
|
77
|
+
}
|
78
|
+
|
79
|
+
rgb = t3.map do |tmp3|
|
80
|
+
if ((6.0 * tmp3) - 1.0) < 1e-5
|
81
|
+
tmp1 + ((tmp2 - tmp1) * tmp3 * 6.0)
|
82
|
+
elsif ((2.0 * tmp3) - 1.0) < 1e-5
|
83
|
+
tmp2
|
84
|
+
elsif ((3.0 * tmp3) - 2.0) < 1e-5
|
85
|
+
tmp1 + (tmp2 - tmp1) * ((2 / 3.0) - tmp3) * 6.0
|
86
|
+
else
|
87
|
+
tmp1
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
Color::RGB.from_fraction(*rgb)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Converts to RGB then YIQ.
|
95
|
+
def to_yiq
|
96
|
+
to_rgb.to_yiq
|
97
|
+
end
|
98
|
+
|
99
|
+
# Converts to RGB then CMYK.
|
100
|
+
def to_cmyk
|
101
|
+
to_rgb.to_cmyk
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns the luminosity (#l) of the colour.
|
105
|
+
def brightness
|
106
|
+
@l
|
107
|
+
end
|
108
|
+
def to_greyscale
|
109
|
+
Color::GrayScale.from_fraction(@l)
|
110
|
+
end
|
111
|
+
alias to_grayscale to_greyscale
|
112
|
+
|
113
|
+
attr_accessor :h, :s, :l
|
114
|
+
remove_method :h=, :s=, :l= ;
|
115
|
+
def h=(hh) #:nodoc:
|
116
|
+
hh = 1.0 if hh > 1
|
117
|
+
hh = 0.0 if hh < 0
|
118
|
+
@h = hh
|
119
|
+
end
|
120
|
+
def s=(ss) #:nodoc:
|
121
|
+
ss = 1.0 if ss > 1
|
122
|
+
ss = 0.0 if ss < 0
|
123
|
+
@s = ss
|
124
|
+
end
|
125
|
+
def l=(ll) #:nodoc:
|
126
|
+
ll = 1.0 if ll > 1
|
127
|
+
ll = 0.0 if ll < 0
|
128
|
+
@l = ll
|
129
|
+
end
|
130
|
+
end
|
data/lib/color/palette.rb
CHANGED
data/lib/color/palette/gimp.rb
CHANGED
@@ -3,30 +3,50 @@
|
|
3
3
|
#
|
4
4
|
# Copyright 2005 Austin Ziegler
|
5
5
|
# http://rubyforge.org/ruby-pdf/
|
6
|
+
#
|
7
|
+
# Licensed under a MIT-style licence.
|
8
|
+
#
|
9
|
+
# $Id: gimp.rb,v 1.3 2005/08/08 02:44:17 austin Exp $
|
6
10
|
#++
|
7
11
|
|
8
12
|
require 'color/palette'
|
9
13
|
|
14
|
+
# A class that can read a GIMP (GNU Image Manipulation Program) palette
|
15
|
+
# file and provide a Hash-like interface to the contents. GIMP colour
|
16
|
+
# palettes are RGB values only.
|
17
|
+
#
|
18
|
+
# Because two or more entries in a GIMP palette may have the same name,
|
19
|
+
# all named entries are returned as an array.
|
20
|
+
#
|
21
|
+
# pal = Color::Palette::Gimp.from_file(my_gimp_palette)
|
22
|
+
# pal[0] => Color::RGB<...>
|
23
|
+
# pal["white"] => [ Color::RGB<...> ]
|
24
|
+
# pal["unknown"] => [ Color::RGB<...>, Color::RGB<...>, ... ]
|
25
|
+
#
|
26
|
+
# GIMP Palettes are always indexable by insertion order (an integer key).
|
10
27
|
class Color::Palette::Gimp
|
11
28
|
include Enumerable
|
12
29
|
|
13
30
|
class << self
|
31
|
+
# Create a GIMP palette object from the named file.
|
14
32
|
def from_file(filename)
|
15
33
|
File.open(filename, "rb") { |io| Color::Palette::Gimp.from_io(io) }
|
16
34
|
end
|
17
35
|
|
36
|
+
# Create a GIMP palette object from the provided IO.
|
18
37
|
def from_io(io)
|
19
38
|
Color::Palette::Gimp.new(io.read)
|
20
39
|
end
|
21
40
|
end
|
22
41
|
|
42
|
+
# Create a new GIMP palette.
|
23
43
|
def initialize(palette)
|
24
|
-
@colors =
|
44
|
+
@colors = []
|
45
|
+
@names = {}
|
25
46
|
@valid = false
|
26
47
|
@name = "(unnamed)"
|
27
48
|
|
28
49
|
index = 0
|
29
|
-
collision = {}
|
30
50
|
|
31
51
|
palette.split($/).each do |line|
|
32
52
|
line.chomp!
|
@@ -47,26 +67,41 @@ class Color::Palette::Gimp
|
|
47
67
|
|
48
68
|
line.gsub!(/^\s+/, '')
|
49
69
|
data = line.split(/\s+/, 4)
|
50
|
-
name = data.pop
|
70
|
+
name = data.pop.strip
|
51
71
|
data.map! { |el| el.to_i }
|
52
72
|
|
53
|
-
|
73
|
+
color = Color::RGB.new(*data)
|
54
74
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@colors[name] = @colors[index]
|
59
|
-
end
|
60
|
-
end
|
75
|
+
@colors[index] = color
|
76
|
+
@names[name] ||= []
|
77
|
+
@names[name] << color
|
61
78
|
|
62
|
-
|
79
|
+
index += 1
|
80
|
+
end
|
63
81
|
end
|
64
82
|
|
65
83
|
def [](key)
|
66
|
-
|
84
|
+
if key.kind_of?(Numeric)
|
85
|
+
@colors[key]
|
86
|
+
else
|
87
|
+
@names[key]
|
88
|
+
end
|
67
89
|
end
|
68
90
|
|
91
|
+
# Loops through each colour.
|
69
92
|
def each
|
70
93
|
@colors.each { |el| yield el }
|
71
94
|
end
|
95
|
+
|
96
|
+
# Loops through each named colour set.
|
97
|
+
def each_name #:yields color_name, color_set:#
|
98
|
+
@names.each { |color_name, color_set| yield color_name, color_set }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns true if this is believed to be a valid GIMP palette.
|
102
|
+
def valid?
|
103
|
+
@valid
|
104
|
+
end
|
105
|
+
|
106
|
+
attr_reader :name
|
72
107
|
end
|
@@ -3,6 +3,10 @@
|
|
3
3
|
#
|
4
4
|
# Copyright 2005 Austin Ziegler
|
5
5
|
# http://rubyforge.org/ruby-pdf/
|
6
|
+
#
|
7
|
+
# Licensed under a MIT-style licence.
|
8
|
+
#
|
9
|
+
# $Id: monocontrast.rb,v 1.3 2005/08/08 02:44:17 austin Exp $
|
6
10
|
#++
|
7
11
|
|
8
12
|
require 'color/palette'
|
@@ -26,7 +30,7 @@ class Color::Palette::MonoContrast
|
|
26
30
|
# 0:: The starting colour.
|
27
31
|
# +1..+5:: Lighter colours.
|
28
32
|
# -1..-5:: Darker colours.
|
29
|
-
|
33
|
+
attr_reader :background
|
30
34
|
# Hash of CSS foreground colour values.
|
31
35
|
#
|
32
36
|
# This is always 11 values:
|
@@ -34,30 +38,56 @@ class Color::Palette::MonoContrast
|
|
34
38
|
# 0:: The starting colour.
|
35
39
|
# +1..+5:: Lighter colours.
|
36
40
|
# -1..-5:: Darker colours.
|
37
|
-
|
41
|
+
attr_reader :foreground
|
42
|
+
|
43
|
+
DEFAULT_MINIMUM_BRIGHTNESS_DIFF = (125.0 / 255.0)
|
38
44
|
|
39
45
|
# The minimum brightness difference between the background and the
|
40
|
-
# foreground. Setting this value will
|
41
|
-
# the base colours. The default value
|
46
|
+
# foreground, and must be between 0..1. Setting this value will
|
47
|
+
# regenerate the palette based on the base colours. The default value
|
48
|
+
# for this is 125 / 255.0. If this value is set to +nil+, it will be
|
49
|
+
# restored to the default.
|
42
50
|
attr_accessor :minimum_brightness_diff
|
51
|
+
remove_method :minimum_brightness_diff= ;
|
43
52
|
def minimum_brightness_diff=(bd) #:nodoc:
|
44
|
-
|
53
|
+
if bd.nil?
|
54
|
+
@minimum_brightness_diff = DEFAULT_MINIMUM_BRIGHTNESS_DIFF
|
55
|
+
elsif bd > 1.0
|
56
|
+
@minimum_brightness_diff = 1.0
|
57
|
+
elsif bd < 0.0
|
58
|
+
@minimum_brightness_diff = 0.0
|
59
|
+
else
|
60
|
+
@minimum_brightness_diff = bd
|
61
|
+
end
|
62
|
+
|
45
63
|
regenerate(@background[0], @foreground[0])
|
46
64
|
end
|
47
65
|
|
66
|
+
DEFAULT_MINIMUM_COLOR_DIFF = (500.0 / 255.0)
|
67
|
+
|
48
68
|
# The minimum colour difference between the background and the
|
49
|
-
# foreground. Setting this value will
|
50
|
-
# the base colours.
|
69
|
+
# foreground, and must be between 0..3. Setting this value will
|
70
|
+
# regenerate the palette based on the base colours. The default value
|
71
|
+
# for this is 500 / 255.0.
|
51
72
|
attr_accessor :minimum_color_diff
|
73
|
+
remove_method :minimum_color_diff= ;
|
52
74
|
def minimum_color_diff=(cd) #:noco:
|
53
|
-
|
75
|
+
if cd.nil?
|
76
|
+
@minimum_color_diff = DEFAULT_MINIMUM_COLOR_DIFF
|
77
|
+
elsif cd > 3.0
|
78
|
+
@minimum_color_diff = 3.0
|
79
|
+
elsif cd < 0.0
|
80
|
+
@minimum_color_diff = 0.0
|
81
|
+
else
|
82
|
+
@minimum_color_diff = cd
|
83
|
+
end
|
54
84
|
regenerate(@background[0], @foreground[0])
|
55
85
|
end
|
56
86
|
|
57
87
|
# Generate the initial palette.
|
58
88
|
def initialize(background, foreground = nil)
|
59
|
-
@minimum_brightness_diff =
|
60
|
-
@minimum_color_diff =
|
89
|
+
@minimum_brightness_diff = DEFAULT_MINIMUM_BRIGHTNESS_DIFF
|
90
|
+
@minimum_color_diff = DEFAULT_MINIMUM_COLOR_DIFF
|
61
91
|
|
62
92
|
regenerate(background, foreground)
|
63
93
|
end
|
@@ -140,7 +170,7 @@ class Color::Palette::MonoContrast
|
|
140
170
|
# Returns the contrast between to colours, a decimal value between 0 and
|
141
171
|
# 3. W3C accessibility guidelines for colour
|
142
172
|
# contrast[http://www.w3.org/TR/AERT#color-contrast] suggest that this
|
143
|
-
# value be at least approximately 1.96 (500 /
|
173
|
+
# value be at least approximately 1.96 (500 / 255.0) for proper contrast.
|
144
174
|
def color_diff(c1, c2)
|
145
175
|
r = (c1.r - c2.r).abs
|
146
176
|
g = (c1.g - c2.g).abs
|