ripta-color-tools 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Changelog +55 -0
- data/Install +18 -0
- data/README.rdoc +82 -0
- data/Rakefile +123 -0
- data/lib/color.rb +78 -0
- data/lib/color/cmyk.rb +185 -0
- data/lib/color/css.rb +27 -0
- data/lib/color/grayscale.rb +135 -0
- data/lib/color/hsl.rb +134 -0
- data/lib/color/palette.rb +18 -0
- data/lib/color/palette/gimp.rb +105 -0
- data/lib/color/palette/monocontrast.rb +178 -0
- data/lib/color/rgb-colors.rb +189 -0
- data/lib/color/rgb.rb +315 -0
- data/lib/color/rgb/metallic.rb +28 -0
- data/lib/color/yiq.rb +80 -0
- data/metaconfig +13 -0
- data/pre-setup.rb +55 -0
- data/setup.rb +1366 -0
- 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 +84 -0
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$
|
10
|
+
#++
|
11
|
+
|
12
|
+
# This namespace contains some CSS colour names.
|
13
|
+
module Color::CSS
|
14
|
+
|
15
|
+
# Returns the RGB colour for name or +nil+ if the name is not valid.
|
16
|
+
def self.[](name)
|
17
|
+
@colors[name.to_s.downcase.to_sym]
|
18
|
+
end
|
19
|
+
|
20
|
+
@colors = {}
|
21
|
+
Color::RGB.constants.each do |const|
|
22
|
+
next if const == "PDF_FORMAT_STR"
|
23
|
+
next if const == "Metallic"
|
24
|
+
@colors[const.downcase.to_sym] ||= Color::RGB.const_get(const)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,135 @@
|
|
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$
|
10
|
+
#++
|
11
|
+
|
12
|
+
# A colour object representing shades of grey. Used primarily in PDF
|
13
|
+
# document creation.
|
14
|
+
class Color::GrayScale
|
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.
|
18
|
+
PDF_FORMAT_STR = "%.3f %s"
|
19
|
+
|
20
|
+
# Creates a greyscale colour object from fractional values 0..1.
|
21
|
+
#
|
22
|
+
# Color::GreyScale.from_fraction(0.5)
|
23
|
+
def self.from_fraction(g = 0)
|
24
|
+
color = Color::GrayScale.new
|
25
|
+
color.g = g
|
26
|
+
color
|
27
|
+
end
|
28
|
+
|
29
|
+
# Creates a greyscale colour object from percentages 0..100.
|
30
|
+
#
|
31
|
+
# Color::GrayScale.new(50)
|
32
|
+
def initialize(g = 0)
|
33
|
+
@g = g / 100.0
|
34
|
+
end
|
35
|
+
|
36
|
+
# Compares the other colour to this one. The other colour will be
|
37
|
+
# converted to GreyScale before comparison, so the comparison between a
|
38
|
+
# GreyScale colour and a non-GreyScale colour will be approximate and
|
39
|
+
# based on the other colour's #to_greyscale conversion. If there is no
|
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.
|
43
|
+
def ==(other)
|
44
|
+
other = other.to_grayscale
|
45
|
+
other.kind_of?(Color::GrayScale) and
|
46
|
+
((@g - other.g).abs <= 1e-4)
|
47
|
+
end
|
48
|
+
|
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.
|
51
|
+
def pdf_fill
|
52
|
+
PDF_FORMAT_STR % [ @g, "g" ]
|
53
|
+
end
|
54
|
+
|
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.
|
57
|
+
def pdf_stroke
|
58
|
+
PDF_FORMAT_STR % [ @g, "G" ]
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_255
|
62
|
+
[(@g * 255).round, 255].min
|
63
|
+
end
|
64
|
+
private :to_255
|
65
|
+
|
66
|
+
# Present the colour as an HTML/CSS colour string.
|
67
|
+
def html
|
68
|
+
gs = "%02x" % to_255
|
69
|
+
"##{gs * 3}"
|
70
|
+
end
|
71
|
+
|
72
|
+
# Convert the greyscale colour to CMYK.
|
73
|
+
def to_cmyk
|
74
|
+
k = 1.0 - @g.to_f
|
75
|
+
Color::CMYK.from_fraction(0, 0, 0, k)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Convert the greyscale colour to RGB.
|
79
|
+
def to_rgb(ignored = true)
|
80
|
+
g = to_255
|
81
|
+
Color::RGB.new(g, g, g)
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_grayscale
|
85
|
+
self
|
86
|
+
end
|
87
|
+
alias to_greyscale to_grayscale
|
88
|
+
|
89
|
+
# Lightens the greyscale colour by the stated percent.
|
90
|
+
def lighten_by(percent)
|
91
|
+
g = [@g + (@g * (percent / 100.0)), 1.0].min
|
92
|
+
Color::GrayScale.from_fraction(g)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Darken the RGB hue by the stated percent.
|
96
|
+
def darken_by(percent)
|
97
|
+
g = [@g - (@g * (percent / 100.0)), 0.0].max
|
98
|
+
Color::GrayScale.from_fraction(g)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns the YIQ (NTSC) colour encoding of the greyscale value. This
|
102
|
+
# is an approximation, as the values for I and Q are calculated by
|
103
|
+
# treating the greyscale value as an RGB value. The Y (intensity or
|
104
|
+
# brightness) value is the same as the greyscale value.
|
105
|
+
def to_yiq
|
106
|
+
y = @g
|
107
|
+
i = (@g * 0.596) + (@g * -0.275) + (@g * -0.321)
|
108
|
+
q = (@g * 0.212) + (@g * -0.523) + (@g * 0.311)
|
109
|
+
Color::YIQ.from_fraction(y, i, q)
|
110
|
+
end
|
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
|
+
|
117
|
+
# Returns the brightness value for this greyscale value; this is the
|
118
|
+
# greyscale value.
|
119
|
+
def brightness
|
120
|
+
@g
|
121
|
+
end
|
122
|
+
|
123
|
+
attr_reader :g
|
124
|
+
|
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
|
135
|
+
end
|
data/lib/color/hsl.rb
ADDED
@@ -0,0 +1,134 @@
|
|
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$
|
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
|
+
|
109
|
+
def to_greyscale
|
110
|
+
Color::GrayScale.from_fraction(@l)
|
111
|
+
end
|
112
|
+
|
113
|
+
alias to_grayscale to_greyscale
|
114
|
+
|
115
|
+
attr_reader :h, :s, :l
|
116
|
+
|
117
|
+
def h=(hh) #:nodoc:
|
118
|
+
hh = 1.0 if hh > 1
|
119
|
+
hh = 0.0 if hh < 0
|
120
|
+
@h = hh
|
121
|
+
end
|
122
|
+
|
123
|
+
def s=(ss) #:nodoc:
|
124
|
+
ss = 1.0 if ss > 1
|
125
|
+
ss = 0.0 if ss < 0
|
126
|
+
@s = ss
|
127
|
+
end
|
128
|
+
|
129
|
+
def l=(ll) #:nodoc:
|
130
|
+
ll = 1.0 if ll > 1
|
131
|
+
ll = 0.0 if ll < 0
|
132
|
+
@l = ll
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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$
|
10
|
+
#++
|
11
|
+
module Color
|
12
|
+
module Palette
|
13
|
+
|
14
|
+
autoload :Gimp, "color/palette/gimp"
|
15
|
+
autoload :MonoContrast, "color/palette/monocontrast"
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,105 @@
|
|
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$
|
10
|
+
#++
|
11
|
+
|
12
|
+
# A class that can read a GIMP (GNU Image Manipulation Program) palette
|
13
|
+
# file and provide a Hash-like interface to the contents. GIMP colour
|
14
|
+
# palettes are RGB values only.
|
15
|
+
#
|
16
|
+
# Because two or more entries in a GIMP palette may have the same name,
|
17
|
+
# all named entries are returned as an array.
|
18
|
+
#
|
19
|
+
# pal = Color::Palette::Gimp.from_file(my_gimp_palette)
|
20
|
+
# pal[0] => Color::RGB<...>
|
21
|
+
# pal["white"] => [ Color::RGB<...> ]
|
22
|
+
# pal["unknown"] => [ Color::RGB<...>, Color::RGB<...>, ... ]
|
23
|
+
#
|
24
|
+
# GIMP Palettes are always indexable by insertion order (an integer key).
|
25
|
+
class Color::Palette::Gimp
|
26
|
+
include Enumerable
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# Create a GIMP palette object from the named file.
|
30
|
+
def from_file(filename)
|
31
|
+
File.open(filename, "rb") { |io| Color::Palette::Gimp.from_io(io) }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create a GIMP palette object from the provided IO.
|
35
|
+
def from_io(io)
|
36
|
+
Color::Palette::Gimp.new(io.read)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create a new GIMP palette.
|
41
|
+
def initialize(palette)
|
42
|
+
@colors = []
|
43
|
+
@names = {}
|
44
|
+
@valid = false
|
45
|
+
@name = "(unnamed)"
|
46
|
+
|
47
|
+
index = 0
|
48
|
+
|
49
|
+
palette.split($/).each do |line|
|
50
|
+
line.chomp!
|
51
|
+
line.gsub!(/\s*#.*\Z/, '')
|
52
|
+
|
53
|
+
next if line.empty?
|
54
|
+
|
55
|
+
if line =~ /\AGIMP Palette\Z/
|
56
|
+
@valid = true
|
57
|
+
next
|
58
|
+
end
|
59
|
+
|
60
|
+
info = /(\w+):\s(.*$)/.match(line)
|
61
|
+
if info
|
62
|
+
@name = info.captures[1] if info.captures[0] =~ /name/i
|
63
|
+
next
|
64
|
+
end
|
65
|
+
|
66
|
+
line.gsub!(/^\s+/, '')
|
67
|
+
data = line.split(/\s+/, 4)
|
68
|
+
name = data.pop.strip
|
69
|
+
data.map! { |el| el.to_i }
|
70
|
+
|
71
|
+
color = Color::RGB.new(*data)
|
72
|
+
|
73
|
+
@colors[index] = color
|
74
|
+
@names[name] ||= []
|
75
|
+
@names[name] << color
|
76
|
+
|
77
|
+
index += 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def [](key)
|
82
|
+
if key.kind_of?(Numeric)
|
83
|
+
@colors[key]
|
84
|
+
else
|
85
|
+
@names[key]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Loops through each colour.
|
90
|
+
def each
|
91
|
+
@colors.each { |el| yield el }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Loops through each named colour set.
|
95
|
+
def each_name #:yields color_name, color_set:#
|
96
|
+
@names.each { |color_name, color_set| yield color_name, color_set }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns true if this is believed to be a valid GIMP palette.
|
100
|
+
def valid?
|
101
|
+
@valid
|
102
|
+
end
|
103
|
+
|
104
|
+
attr_reader :name
|
105
|
+
end
|
@@ -0,0 +1,178 @@
|
|
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$
|
10
|
+
#++
|
11
|
+
|
12
|
+
# Generates a monochromatic constrasting colour palette for background and
|
13
|
+
# foreground. What does this mean?
|
14
|
+
#
|
15
|
+
# Monochromatic: A single colour is used to generate the base palette, and
|
16
|
+
# this colour is lightened five times and darkened five times to provide
|
17
|
+
# eleven distinct colours.
|
18
|
+
#
|
19
|
+
# Contrasting: The foreground is also generated as a monochromatic colour
|
20
|
+
# palettte; however, all generated colours are tested to see that they are
|
21
|
+
# appropriately contrasting to ensure maximum readability of the
|
22
|
+
# foreground against the background.
|
23
|
+
class Color::Palette::MonoContrast
|
24
|
+
# Hash of CSS background colour values.
|
25
|
+
#
|
26
|
+
# This is always 11 values:
|
27
|
+
#
|
28
|
+
# 0:: The starting colour.
|
29
|
+
# +1..+5:: Lighter colours.
|
30
|
+
# -1..-5:: Darker colours.
|
31
|
+
attr_reader :background
|
32
|
+
# Hash of CSS foreground colour values.
|
33
|
+
#
|
34
|
+
# This is always 11 values:
|
35
|
+
#
|
36
|
+
# 0:: The starting colour.
|
37
|
+
# +1..+5:: Lighter colours.
|
38
|
+
# -1..-5:: Darker colours.
|
39
|
+
attr_reader :foreground
|
40
|
+
|
41
|
+
DEFAULT_MINIMUM_BRIGHTNESS_DIFF = (125.0 / 255.0)
|
42
|
+
|
43
|
+
attr_reader :minimum_brightness_diff
|
44
|
+
|
45
|
+
# The minimum brightness difference between the background and the
|
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.
|
50
|
+
def minimum_brightness_diff=(bd) #:nodoc:
|
51
|
+
if bd.nil?
|
52
|
+
@minimum_brightness_diff = DEFAULT_MINIMUM_BRIGHTNESS_DIFF
|
53
|
+
elsif bd > 1.0
|
54
|
+
@minimum_brightness_diff = 1.0
|
55
|
+
elsif bd < 0.0
|
56
|
+
@minimum_brightness_diff = 0.0
|
57
|
+
else
|
58
|
+
@minimum_brightness_diff = bd
|
59
|
+
end
|
60
|
+
|
61
|
+
regenerate(@background[0], @foreground[0])
|
62
|
+
end
|
63
|
+
|
64
|
+
DEFAULT_MINIMUM_COLOR_DIFF = (500.0 / 255.0)
|
65
|
+
|
66
|
+
attr_reader :minimum_color_diff
|
67
|
+
|
68
|
+
# The minimum colour difference between the background and the
|
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.
|
72
|
+
def minimum_color_diff=(cd) #:noco:
|
73
|
+
if cd.nil?
|
74
|
+
@minimum_color_diff = DEFAULT_MINIMUM_COLOR_DIFF
|
75
|
+
elsif cd > 3.0
|
76
|
+
@minimum_color_diff = 3.0
|
77
|
+
elsif cd < 0.0
|
78
|
+
@minimum_color_diff = 0.0
|
79
|
+
else
|
80
|
+
@minimum_color_diff = cd
|
81
|
+
end
|
82
|
+
regenerate(@background[0], @foreground[0])
|
83
|
+
end
|
84
|
+
|
85
|
+
# Generate the initial palette.
|
86
|
+
def initialize(background, foreground = nil)
|
87
|
+
@minimum_brightness_diff = DEFAULT_MINIMUM_BRIGHTNESS_DIFF
|
88
|
+
@minimum_color_diff = DEFAULT_MINIMUM_COLOR_DIFF
|
89
|
+
|
90
|
+
regenerate(background, foreground)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Generate the colour palettes.
|
94
|
+
def regenerate(background, foreground = nil)
|
95
|
+
foreground ||= background
|
96
|
+
background = background.to_rgb
|
97
|
+
foreground = foreground.to_rgb
|
98
|
+
|
99
|
+
@background = {}
|
100
|
+
@foreground = {}
|
101
|
+
|
102
|
+
@background[-5] = background.darken_by(10)
|
103
|
+
@background[-4] = background.darken_by(25)
|
104
|
+
@background[-3] = background.darken_by(50)
|
105
|
+
@background[-2] = background.darken_by(75)
|
106
|
+
@background[-1] = background.darken_by(85)
|
107
|
+
@background[ 0] = background
|
108
|
+
@background[+1] = background.lighten_by(85)
|
109
|
+
@background[+2] = background.lighten_by(75)
|
110
|
+
@background[+3] = background.lighten_by(50)
|
111
|
+
@background[+4] = background.lighten_by(25)
|
112
|
+
@background[+5] = background.lighten_by(10)
|
113
|
+
|
114
|
+
@foreground[-5] = calculate_foreground(@background[-5], foreground)
|
115
|
+
@foreground[-4] = calculate_foreground(@background[-4], foreground)
|
116
|
+
@foreground[-3] = calculate_foreground(@background[-3], foreground)
|
117
|
+
@foreground[-2] = calculate_foreground(@background[-2], foreground)
|
118
|
+
@foreground[-1] = calculate_foreground(@background[-1], foreground)
|
119
|
+
@foreground[ 0] = calculate_foreground(@background[ 0], foreground)
|
120
|
+
@foreground[+1] = calculate_foreground(@background[+1], foreground)
|
121
|
+
@foreground[+2] = calculate_foreground(@background[+2], foreground)
|
122
|
+
@foreground[+3] = calculate_foreground(@background[+3], foreground)
|
123
|
+
@foreground[+4] = calculate_foreground(@background[+4], foreground)
|
124
|
+
@foreground[+5] = calculate_foreground(@background[+5], foreground)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Given a background colour and a foreground colour, modifies the
|
128
|
+
# foreground colour so that it will have enough contrast to be seen
|
129
|
+
# against the background colour.
|
130
|
+
#
|
131
|
+
# Uses #mininum_brightness_diff and #minimum_color_diff.
|
132
|
+
def calculate_foreground(background, foreground)
|
133
|
+
nfg = nil
|
134
|
+
# Loop through brighter and darker versions of the foreground color.
|
135
|
+
# The numbers here represent the amount of foreground color to mix
|
136
|
+
# with black and white.
|
137
|
+
[100, 75, 50, 25, 0].each do |percent|
|
138
|
+
dfg = foreground.darken_by(percent)
|
139
|
+
lfg = foreground.lighten_by(percent)
|
140
|
+
|
141
|
+
dbd = brightness_diff(background, dfg)
|
142
|
+
lbd = brightness_diff(background, lfg)
|
143
|
+
|
144
|
+
if lbd > dbd
|
145
|
+
nfg = lfg
|
146
|
+
nbd = lbd
|
147
|
+
else
|
148
|
+
nfg = dfg
|
149
|
+
nbd = dbd
|
150
|
+
end
|
151
|
+
|
152
|
+
ncd = color_diff(background, nfg)
|
153
|
+
|
154
|
+
break if nbd >= @minimum_brightness_diff and ncd >= @minimum_color_diff
|
155
|
+
end
|
156
|
+
nfg
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns the absolute difference between the brightness levels of two
|
160
|
+
# colours. This will be a decimal value between 0 and 1. W3C
|
161
|
+
# accessibility guidelines for colour
|
162
|
+
# contrast[http://www.w3.org/TR/AERT#color-contrast] suggest that this
|
163
|
+
# value be at least approximately 0.49 (125 / 255.0) for proper contrast.
|
164
|
+
def brightness_diff(c1, c2)
|
165
|
+
(c1.brightness - c2.brightness).abs
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns the contrast between to colours, a decimal value between 0 and
|
169
|
+
# 3. W3C accessibility guidelines for colour
|
170
|
+
# contrast[http://www.w3.org/TR/AERT#color-contrast] suggest that this
|
171
|
+
# value be at least approximately 1.96 (500 / 255.0) for proper contrast.
|
172
|
+
def color_diff(c1, c2)
|
173
|
+
r = (c1.r - c2.r).abs
|
174
|
+
g = (c1.g - c2.g).abs
|
175
|
+
b = (c1.b - c2.b).abs
|
176
|
+
r + g + b
|
177
|
+
end
|
178
|
+
end
|