color 0.1.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,223 @@
1
+ #--
2
+ # Color
3
+ # Colour management with Ruby
4
+ # http://rubyforge.org/projects/color
5
+ # Version 1.4.0
6
+ #
7
+ # Licensed under a MIT-style licence. See Licence.txt in the main
8
+ # distribution for full licensing information.
9
+ #
10
+ # Copyright (c) 2005 - 2007 Austin Ziegler and Matt Lyon
11
+ #
12
+ # $Id: test_all.rb 55 2007-02-03 23:29:34Z austin $
13
+ #++
14
+
15
+ # An HSL colour object. Internally, the hue (#h), saturation (#s), and
16
+ # luminosity/lightness (#l) values are dealt with as fractional values in
17
+ # the range 0..1.
18
+ class Color::HSL
19
+ class << self
20
+ # Creates an HSL colour object from fractional values 0..1.
21
+ def from_fraction(h = 0.0, s = 0.0, l = 0.0)
22
+ colour = Color::HSL.new
23
+ colour.h = h
24
+ colour.s = s
25
+ colour.l = l
26
+ colour
27
+ end
28
+ end
29
+
30
+ # Compares the other colour to this one. The other colour will be
31
+ # converted to HSL before comparison, so the comparison between a HSL
32
+ # colour and a non-HSL colour will be approximate and based on the other
33
+ # colour's #to_hsl conversion. If there is no #to_hsl conversion, this
34
+ # will raise an exception. This will report that two HSL values are
35
+ # equivalent if all component values are within Color::COLOR_TOLERANCE of
36
+ # each other.
37
+ def ==(other)
38
+ other = other.to_hsl
39
+ other.kind_of?(Color::HSL) and
40
+ ((@h - other.h).abs <= Color::COLOR_TOLERANCE) and
41
+ ((@s - other.s).abs <= Color::COLOR_TOLERANCE) and
42
+ ((@l - other.l).abs <= Color::COLOR_TOLERANCE)
43
+ end
44
+
45
+ # Creates an HSL colour object from the standard values of degrees and
46
+ # percentages (e.g., 145 deg, 30%, 50%).
47
+ def initialize(h = 0, s = 0, l = 0)
48
+ @h = h / 360.0
49
+ @s = s / 100.0
50
+ @l = l / 100.0
51
+ end
52
+
53
+ # Present the colour as an HTML/CSS colour string.
54
+ def html
55
+ to_rgb.html
56
+ end
57
+
58
+ # Present the colour as an RGB HTML/CSS colour string (e.g., "rgb(0%, 50%,
59
+ # 100%)"). Note that this will perform a #to_rgb operation using the
60
+ # default conversion formula.
61
+ def css_rgb
62
+ to_rgb.css_rgb
63
+ end
64
+
65
+ # Present the colour as an RGBA (with alpha) HTML/CSS colour string (e.g.,
66
+ # "rgb(0%, 50%, 100%, 1)"). Note that this will perform a #to_rgb
67
+ # operation using the default conversion formula.
68
+ def css_rgba
69
+ to_rgb.css_rgba
70
+ end
71
+
72
+ # Present the colour as an HSL HTML/CSS colour string (e.g., "hsl(180,
73
+ # 25%, 35%)").
74
+ def css_hsl
75
+ "hsl(%3.2f, %3.2f%%, %3.2f%%)" % [ hue, saturation, luminosity ]
76
+ end
77
+
78
+ # Present the colour as an HSLA (with alpha) HTML/CSS colour string (e.g.,
79
+ # "hsla(180, 25%, 35%, 1)").
80
+ def css_hsla
81
+ "hsla(%3.2f, %3.2f%%, %3.2f%%, %3.2f)" % [ hue, saturation, luminosity, 1 ]
82
+ end
83
+
84
+ # Converting to HSL as adapted from Foley and Van-Dam from
85
+ # http://www.bobpowell.net/RGBHSB.htm.
86
+ #
87
+ # NOTE:
88
+ # * If the colour's luminosity is near zero, the colour is always black.
89
+ # * If the colour's luminosity is near one, the colour is always white.
90
+ # * If the colour's saturation is near zero, the colour is always a shade
91
+ # of grey and is based only on the luminosity of the colour.
92
+ #
93
+ def to_rgb(ignored = nil)
94
+ return Color::RGB.new if Color.near_zero_or_less?(@l)
95
+ return Color::RGB.new(0xff, 0xff, 0xff) if Color.near_one_or_more?(@l)
96
+ return Color::RGB.from_fraction(@l, @l, @l) if Color.near_zero?(@s)
97
+
98
+ # Is the value less than 0.5?
99
+ if Color.near_zero_or_less?(@l - 0.5)
100
+ tmp2 = @l * (1.0 + @s.to_f)
101
+ else
102
+ tmp2 = @l + @s - (@l * @s.to_f)
103
+ end
104
+ tmp1 = 2.0 * @l - tmp2
105
+
106
+ tmp3 = [ @h + (1.0 / 3.0), @h, @h - (1.0 / 3.0) ]
107
+
108
+ rgb = tmp3.map { |hue|
109
+ hue += 1.0 if Color.near_zero_or_less?(hue)
110
+ hue -= 1.0 if Color.near_one_or_more?(hue)
111
+
112
+ if Color.near_zero_or_less?((6.0 * hue) - 1.0)
113
+ tmp1 + ((tmp2 - tmp1) * hue * 6.0)
114
+ elsif Color.near_zero_or_less?((2.0 * hue) - 1.0)
115
+ tmp2
116
+ elsif Color.near_zero_or_less?((3.0 * hue) - 2.0)
117
+ tmp1 + (tmp2 - tmp1) * ((2 / 3.0) - hue) * 6.0
118
+ else
119
+ tmp1
120
+ end
121
+ }
122
+
123
+ Color::RGB.from_fraction(*rgb)
124
+ end
125
+
126
+ # Converts to RGB then YIQ.
127
+ def to_yiq
128
+ to_rgb.to_yiq
129
+ end
130
+
131
+ # Converts to RGB then CMYK.
132
+ def to_cmyk
133
+ to_rgb.to_cmyk
134
+ end
135
+
136
+ # Returns the luminosity (#l) of the colour.
137
+ def brightness
138
+ @l
139
+ end
140
+ def to_greyscale
141
+ Color::GrayScale.from_fraction(@l)
142
+ end
143
+ alias to_grayscale to_greyscale
144
+
145
+ # Returns the hue of the colour in degrees.
146
+ def hue
147
+ @h * 360.0
148
+ end
149
+ # Returns the hue of the colour in the range 0.0 .. 1.0.
150
+ def h
151
+ @h
152
+ end
153
+ # Sets the hue of the colour in degrees. Colour is perceived as a wheel,
154
+ # so values should be set properly even with negative degree values.
155
+ def hue=(hh)
156
+ hh = hh / 360.0
157
+
158
+ hh += 1.0 if hh < 0.0
159
+ hh -= 1.0 if hh > 1.0
160
+
161
+ @h = Color.normalize(hh)
162
+ end
163
+ # Sets the hue of the colour in the range 0.0 .. 1.0.
164
+ def h=(hh)
165
+ @h = Color.normalize(hh)
166
+ end
167
+ # Returns the percentage of saturation of the colour.
168
+ def saturation
169
+ @s * 100.0
170
+ end
171
+ # Returns the saturation of the colour in the range 0.0 .. 1.0.
172
+ def s
173
+ @s
174
+ end
175
+ # Sets the percentage of saturation of the colour.
176
+ def saturation=(ss)
177
+ @s = Color.normalize(ss / 100.0)
178
+ end
179
+ # Sets the saturation of the colour in the ragne 0.0 .. 1.0.
180
+ def s=(ss)
181
+ @s = Color.normalize(ss)
182
+ end
183
+
184
+ # Returns the percentage of luminosity of the colour.
185
+ def luminosity
186
+ @l * 100.0
187
+ end
188
+ alias lightness luminosity
189
+ # Returns the luminosity of the colour in the range 0.0 .. 1.0.
190
+ def l
191
+ @l
192
+ end
193
+ # Sets the percentage of luminosity of the colour.
194
+ def luminosity=(ll)
195
+ @l = Color.normalize(ll / 100.0)
196
+ end
197
+ alias lightness= luminosity= ;
198
+ # Sets the luminosity of the colour in the ragne 0.0 .. 1.0.
199
+ def l=(ll)
200
+ @l = Color.normalize(ll)
201
+ end
202
+
203
+ def to_hsl
204
+ self
205
+ end
206
+
207
+ def inspect
208
+ "HSL [%.2f deg, %.2f%%, %.2f%%]" % [ hue, saturation, luminosity ]
209
+ end
210
+
211
+ # Mix the mask colour (which will be converted to an HSL colour) with the
212
+ # current colour at the stated mix percentage as a decimal value.
213
+ #
214
+ # NOTE:: This differs from Color::RGB#mix_with.
215
+ def mix_with(color, mix_percent = 0.5)
216
+ color = color.to_hsl
217
+ _h = ((color.h - self.h) * mix_percent) + self.h
218
+ _s = ((color.s - self.s) * mix_percent) + self.s
219
+ _l = ((color.l - self.l) * mix_percent) + self.l
220
+
221
+ self.class.from_fraction(_h, _s, _l)
222
+ end
223
+ end
@@ -0,0 +1,18 @@
1
+ #--
2
+ # Color
3
+ # Colour management with Ruby
4
+ # http://rubyforge.org/projects/color
5
+ # Version 1.4.0
6
+ #
7
+ # Licensed under a MIT-style licence. See Licence.txt in the main
8
+ # distribution for full licensing information.
9
+ #
10
+ # Copyright (c) 2005 - 2007 Austin Ziegler and Matt Lyon
11
+ #
12
+ # $Id: test_all.rb 55 2007-02-03 23:29:34Z austin $
13
+ #++
14
+
15
+ require 'color'
16
+
17
+ module Color::Palette
18
+ end
@@ -0,0 +1,274 @@
1
+ #--
2
+ # Color
3
+ # Colour management with Ruby
4
+ # http://rubyforge.org/projects/color
5
+ # Version 1.4.0
6
+ #
7
+ # Licensed under a MIT-style licence. See Licence.txt in the main
8
+ # distribution for full licensing information.
9
+ #
10
+ # Copyright (c) 2005 - 2007 Austin Ziegler and Matt Lyon
11
+ #
12
+ # $Id: test_all.rb 55 2007-02-03 23:29:34Z austin $
13
+ #++
14
+
15
+ require 'color/palette'
16
+
17
+ # A class that can read an Adobe Color palette file (used for Photoshop
18
+ # swatches) and provide a Hash-like interface to the contents. Not all
19
+ # colour formats in ACO files are supported. Based largely off the
20
+ # information found by Larry Tesler[http://www.nomodes.com/aco.html].
21
+ #
22
+ # Not all Adobe Color files have named colours; all named entries are
23
+ # returned as an array.
24
+ #
25
+ # pal = Color::Palette::AdobeColor.from_file(my_aco_palette)
26
+ # pal[0] => Color::RGB<...>
27
+ # pal["white"] => [ Color::RGB<...> ]
28
+ # pal["unknown"] => [ Color::RGB<...>, Color::RGB<...>, ... ]
29
+ #
30
+ # AdobeColor palettes are always indexable by insertion order (an integer
31
+ # key).
32
+ #
33
+ # Version 2 palettes use UTF-16 colour names.
34
+ class Color::Palette::AdobeColor
35
+ include Enumerable
36
+
37
+ class << self
38
+ # Create an AdobeColor palette object from the named file.
39
+ def from_file(filename)
40
+ File.open(filename, "rb") { |io| Color::Palette::AdobeColor.from_io(io) }
41
+ end
42
+
43
+ # Create an AdobeColor palette object from the provided IO.
44
+ def from_io(io)
45
+ Color::Palette::AdobeColor.new(io.read)
46
+ end
47
+ end
48
+
49
+ # Returns statistics about the nature of the colours loaded.
50
+ attr_reader :statistics
51
+ # Contains the "lost" colours in the palette. These colours could not be
52
+ # properly loaded (e.g., L*a*b* is not supported by Color, so it is
53
+ # "lost") or are not understood by the algorithms.
54
+ attr_reader :lost
55
+
56
+ # Use this to convert the unsigned word to the signed word, if necessary.
57
+ UwToSw = proc { |n| (n >= (2 ** 16)) ? n - (2 ** 32) : n } #:nodoc:
58
+
59
+ # Create a new AdobeColor palette from the palette file as a string.
60
+ def initialize(palette)
61
+ @colors = []
62
+ @names = {}
63
+ @statistics = Hash.new(0)
64
+ @lost = []
65
+ @order = []
66
+ @version = nil
67
+
68
+ class << palette
69
+ def readwords(count = 1)
70
+ @offset ||= 0
71
+ raise IndexError if @offset >= self.size
72
+ val = self[@offset, count * 2]
73
+ raise IndexError if val.nil? or val.size < (count * 2)
74
+ val = val.unpack("n" * count)
75
+ @offset += count * 2
76
+ val
77
+ end
78
+
79
+ def readutf16(count = 1)
80
+ @offset ||= 0
81
+ raise IndexError if @offset >= self.size
82
+ val = self[@offset, count * 2]
83
+ raise IndexError if val.nil? or val.size < (count * 2)
84
+ @offset += count * 2
85
+ val
86
+ end
87
+ end
88
+
89
+ @version, count = palette.readwords 2
90
+
91
+ raise "Unknown AdobeColor palette version #@version." unless @version.between?(1, 2)
92
+
93
+ count.times do
94
+ space, w, x, y, z = palette.readwords 5
95
+ name = nil
96
+ if @version == 2
97
+ raise IndexError unless palette.readwords == [ 0 ]
98
+ len = palette.readwords
99
+ name = palette.readutf16(len[0] - 1)
100
+ raise IndexError unless palette.readwords == [ 0 ]
101
+ end
102
+
103
+ color = case space
104
+ when 0 then # RGB
105
+ @statistics[:rgb] += 1
106
+
107
+ Color::RGB.new(w / 256, x / 256, y / 256)
108
+ when 1 then # HS[BV] -- Convert to RGB
109
+ @statistics[:hsb] += 1
110
+
111
+ h = w / 65535.0
112
+ s = x / 65535.0
113
+ v = y / 65535.0
114
+
115
+ if defined?(Color::HSB)
116
+ Color::HSB.from_fraction(h, s, v)
117
+ else
118
+ @statistics[:converted] += 1
119
+ if Color.near_zero_or_less?(s)
120
+ Color::RGB.from_fraction(v, v, v)
121
+ else
122
+ if Color.near_one_or_more?(h)
123
+ vh = 0
124
+ else
125
+ vh = h * 6.0
126
+ end
127
+
128
+ vi = vh.floor
129
+ v1 = v.to_f * (1 - s.to_f)
130
+ v2 = v.to_f * (1 - s.to_f * (vh - vi))
131
+ v3 = v.to_f * (1 - s.to_f * (1 - (vh - vi)))
132
+
133
+ case vi
134
+ when 0 then Color::RGB.from_fraction(v, v3, v1)
135
+ when 1 then Color::RGB.from_fraction(v2, v, v1)
136
+ when 2 then Color::RGB.from_fraction(v1, v, v3)
137
+ when 3 then Color::RGB.from_fraction(v1, v2, v)
138
+ when 4 then Color::RGB.from_fraction(v3, v1, v)
139
+ else Color::RGB.from_fraction(v, v1, v2)
140
+ end
141
+ end
142
+ end
143
+ when 2 then # CMYK
144
+ @statistics[:cmyk] += 1
145
+ Color::CMYK.from_percent(100 - (w / 655.35),
146
+ 100 - (x / 655.35),
147
+ 100 - (y / 655.35),
148
+ 100 - (z / 655.35))
149
+ when 7 then # L*a*b*
150
+ @statistics[:lab] += 1
151
+
152
+ l = [w, 10000].min / 100.0
153
+ a = [[-12800, UwToSw[x]].max, 12700].min / 100.0
154
+ b = [[-12800, UwToSw[x]].max, 12700].min / 100.0
155
+
156
+ if defined? Color::Lab
157
+ Color::Lab.new(l, a, b)
158
+ else
159
+ [ space, w, x, y, z ]
160
+ end
161
+ when 8 then # Grayscale
162
+ @statistics[:gray] += 1
163
+
164
+ g = [w, 10000].min / 100.0
165
+ Color::GrayScale.new(g)
166
+ when 9 then # Wide CMYK
167
+ @statistics[:wcmyk] += 1
168
+
169
+ c = [w, 10000].min / 100.0
170
+ m = [x, 10000].min / 100.0
171
+ y = [y, 10000].min / 100.0
172
+ k = [z, 10000].min / 100.0
173
+ Color::CMYK.from_percent(c, m, y, k)
174
+ else
175
+ @statistics[space] += 1
176
+ [ space, w, x, y, z ]
177
+ end
178
+
179
+ @order << [ color, name ]
180
+
181
+ if color.kind_of? Array
182
+ @lost << color
183
+ else
184
+ @colors << color
185
+
186
+ if name
187
+ @names[name] ||= []
188
+ @names[name] << color
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ # Provides the colour or colours at the provided selectors.
195
+ def values_at(*selectors)
196
+ @colors.values_at(*selectors)
197
+ end
198
+
199
+ # If a Numeric +key+ is provided, the single colour value at that position
200
+ # will be returned. If a String +key+ is provided, the colour set (an
201
+ # array) for that colour name will be returned.
202
+ def [](key)
203
+ if key.kind_of?(Numeric)
204
+ @colors[key]
205
+ else
206
+ @names[key]
207
+ end
208
+ end
209
+
210
+ # Loops through each colour.
211
+ def each
212
+ @colors.each { |el| yield el }
213
+ end
214
+
215
+ # Loops through each named colour set.
216
+ def each_name #:yields color_name, color_set:#
217
+ @names.each { |color_name, color_set| yield color_name, color_set }
218
+ end
219
+
220
+ def size
221
+ @colors.size
222
+ end
223
+
224
+ attr_reader :version
225
+
226
+ def to_aco(version = @version) #:nodoc:
227
+ res = ""
228
+
229
+ res << [ version, @order.size ].pack("nn")
230
+
231
+ @order.each do |cnpair|
232
+ color, name = *cnpair
233
+
234
+ # Note: HSB and CMYK formats are lost by the conversions performed on
235
+ # import. They are turned into RGB and WCMYK, respectively.
236
+
237
+ cstr = case color
238
+ when Array
239
+ color
240
+ when Color::RGB
241
+ r = [(color.red * 256).round, 65535].min
242
+ g = [(color.green * 256).round, 65535].min
243
+ b = [(color.blue * 256).round, 65535].min
244
+ [ 0, r, g, b, 0 ]
245
+ when Color::GrayScale
246
+ g = [(color.gray * 100).round, 10000].min
247
+ [ 8, g, 0, 0, 0 ]
248
+ when Color::CMYK
249
+ c = [(color.cyan * 100).round, 10000].min
250
+ m = [(color.magenta * 100).round, 10000].min
251
+ y = [(color.yellow * 100).round, 10000].min
252
+ k = [(color.black * 100).round, 10000].min
253
+ [ 9, c, m, y, k ]
254
+ end
255
+ cstr = cstr.pack("nnnnn")
256
+
257
+ nstr = ""
258
+
259
+ if version == 2
260
+ if (name.size / 2 * 2) == name.size # only where s[0] == byte!
261
+ nstr << [ 0, (name.size / 2) + 1 ].pack("nn")
262
+ nstr << name
263
+ nstr << [ 0 ].pack("n")
264
+ else
265
+ nstr << [ 0, 1, 0 ].pack("nnn")
266
+ end
267
+ end
268
+
269
+ res << cstr << nstr
270
+ end
271
+
272
+ res
273
+ end
274
+ end