color 1.5.1 → 1.6

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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YjQwMTIxMzBkMGIzMmI2YmJjOWMwODE3ZDU5ZTlhYTFlNGM2YmQ0Zg==
4
+ NzUzZTAyZmI4OGRlNmJlZWJiZDdhZDk5NmFhNDFiNGU4ZjNmMTQxNA==
5
5
  data.tar.gz: !binary |-
6
- M2VjNGUwNmFmOWRhMzQzZjA3OWZhMTM2MDgxYzcxMzc4NjYzOTZlZQ==
6
+ YzJhMDQ1ZjdkYjgzNWM3YzFhNWYzYzAwMTkwOTViZjUzZDhhOGRmMQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MmYzNTFkZWZjYWM1ZTAxNjJhYzZkMTk0NGE2OTU5MTBiOGM1NWY4Mzg3NDA1
10
- MmVjMjEzOTYzYzVkMGNkOGRmNDY0MTM0YmI0ZDc3YTYxMDAxMzE1OGZhNTNh
11
- NmU4Y2Q5ZTY2ZjA1NDMwZThmYTA1ZDNjNTkzZTMzOGEwMzJlYmE=
9
+ NmQxOWEyNjJiZTJiNWQ1YzUxNGQzMzQ3MDhiYmI4MGRlN2Y1ZDg1OGIyNGY2
10
+ NGRmYzA1YTI5YmExZTYzNTAzMDFkOTIxNTYyYTliZGJlN2IwYzU2OWZiYjMz
11
+ YWMyNWViNWE5M2RiZGFmYzk1NDk2MzExZDk0NmI4YzVjNTg3NGM=
12
12
  data.tar.gz: !binary |-
13
- NWIxY2I2NTVjN2RjZDQwMTc5NjQwZDRhODM5MmMwODJiMDQwMGNmM2M4Yzk3
14
- ZDUxMzJhYjE1ODA3ZGY3NTZmMzJkODg0MmNjZTI2NzA5NjcxZjcxNTYzMzk2
15
- YWQyZGQ3Y2RmY2RkOTc0NzkzMTYxODI1Mzg4MzA5MDA3YjEyYjQ=
13
+ ODkwMWY4NWNjMTRlMWJiOWY0ZjM0NzE0NjY2MDIwNWU0N2E5NjQxMmM1YTJi
14
+ MDFiZjYzMzY0MzBhOTZhYjk5MTVkOGRiYmIwNTdjNDQ3MWU2NmE5MTZkMjhh
15
+ MWYzMDk5YzY3NzA1OGQzZjg0OGNiYWRmZWJiOTNkOWI0YmM0NzM=
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -0,0 +1,5 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ Autotest.add_hook :initialize do |at|
4
+ at.testlib = ".minitest"
5
+ end
@@ -0,0 +1,2 @@
1
+ gem "minitest"
2
+ require "minitest/autorun"
@@ -0,0 +1,35 @@
1
+ ---
2
+ language: ruby
3
+ rvm:
4
+ - 2.1.0
5
+ - 2.0.0
6
+ - 1.9.3
7
+ - jruby-19mode
8
+ - rbx-2
9
+ - 1.8.7
10
+ - jruby-18mode
11
+ - ree
12
+ - ruby-head
13
+ - jruby-head
14
+ matrix:
15
+ allow_failures:
16
+ - rvm: rbx-2
17
+ gemfile:
18
+ - Gemfile
19
+ before_script:
20
+ - |
21
+ case "${TRAVIS_RUBY_VERSION}" in
22
+ rbx*)
23
+ gem install psych
24
+ ;;
25
+ esac
26
+ - rake travis:before -t
27
+ script: rake travis
28
+ after_script:
29
+ - rake travis:after -t
30
+ notifications:
31
+ email:
32
+ recipients:
33
+ - austin@rubyforge.org
34
+ on_success: change
35
+ on_failure: always
@@ -55,3 +55,6 @@ Here's the most direct way to get your work merged into the project:
55
55
 
56
56
  * Austin Ziegler created color-tools.
57
57
  * Matt Lyons created color.
58
+ * Dave Heitzman (contrast comparison)
59
+ * Thomas Sawyer
60
+ * Aaron Hill (CIE94 colour matching)
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # -*- ruby -*-
2
+
3
+ # NOTE: This file is present to keep Travis CI happy. Edits to it will not
4
+ # be accepted.
5
+
6
+ source "https://rubygems.org/"
7
+ gemspec
8
+
9
+ # vim: syntax=ruby
@@ -1,3 +1,21 @@
1
+ == 1.6 / 2014-05-19
2
+
3
+ * Major enhancements:
4
+ * Aaron Hill (@armahillo) implemented the CIE Delta E 94 method by which an
5
+ RGB colour can be asked for the closest matching colour from a list of
6
+ provided colours. Fixes #5.
7
+ * To implement #closest_match and #delta_e94, conversion methods for sRGB to
8
+ XYZ and XYZ to L*a*b* space were implemented. These should be considered
9
+ experimental.
10
+
11
+ * Tooling fixes:
12
+ * Ensured that the gem manifest was up-to-date. Fixes #4 reported by @boutil.
13
+ Thanks!
14
+ * Fixed problems with Travis builds. Note that Ruby 1.9.2 is no longer
15
+ tested. Rubinius remains in a “failure-tolerated” mode.
16
+ * Color 1.6 is, barring security patches, the last release of Color that will
17
+ support Ruby 1.8.
18
+
1
19
  == 1.5.1 / 2014-01-28
2
20
 
3
21
  * color 1.5 was a yanked release.
@@ -2,7 +2,7 @@
2
2
 
3
3
  This software is available under an MIT-style licence.
4
4
 
5
- * Copyright 2005–2013 Austin Ziegler, Matt Lyon, and other contributors
5
+ * Copyright 2005–2014 Austin Ziegler, Matt Lyon, and other contributors
6
6
 
7
7
  Permission is hereby granted, free of charge, to any person obtaining a copy of
8
8
  this software and associated documentation files (the "Software"), to deal in
@@ -1,4 +1,10 @@
1
+ .autotest
2
+ .gemtest
1
3
  .hoerc
4
+ .minitest.rb
5
+ .travis.yml
6
+ Contributing.rdoc
7
+ Gemfile
2
8
  History.rdoc
3
9
  Licence.rdoc
4
10
  Manifest.txt
@@ -13,10 +19,12 @@ lib/color/palette.rb
13
19
  lib/color/palette/adobecolor.rb
14
20
  lib/color/palette/gimp.rb
15
21
  lib/color/palette/monocontrast.rb
16
- lib/color/rgb/colors.rb
17
22
  lib/color/rgb.rb
23
+ lib/color/rgb/colors.rb
24
+ lib/color/rgb/contrast.rb
18
25
  lib/color/rgb/metallic.rb
19
26
  lib/color/yiq.rb
27
+ test/minitest_helper.rb
20
28
  test/test_adobecolor.rb
21
29
  test/test_cmyk.rb
22
30
  test/test_color.rb
@@ -4,8 +4,8 @@ home :: http://color.rubyforge.org
4
4
  code :: https://github.com/halostatue/color
5
5
  bugs :: https://github.com/halostatue/color/issues
6
6
  rdoc :: http://rubydoc.info/github/halostatue/color
7
- code climate :: {<img src="https://codeclimate.com/github/halostatue/color.png" />}[https://codeclimate.com/github/halostatue/color]
8
7
  continuous integration :: {<img src="https://travis-ci.org/halostatue/color.png" />}[https://travis-ci.org/halostatue/color]
8
+ test coverage :: {<img src="https://coveralls.io/repos/halostatue/color/badge.png" alt="Coverage Status" />}[https://coveralls.io/r/halostatue/color]
9
9
 
10
10
  == Description
11
11
 
@@ -15,28 +15,20 @@ named RGB colours (184 with spelling variations) that are commonly supported in
15
15
  HTML, SVG, and X11 applications. A technique for generating monochromatic
16
16
  contrasting palettes is also included.
17
17
 
18
- The capabilities of the Color library are limited to pure mathematical
19
- manipulation of the colours based on colour theory without reference to colour
20
- profiles (such as sRGB or Adobe RGB). For most purposes, when working with the
21
- RGB and HSL colours, this won't matter. However, some colour models (like CIE
22
- L*a*b*) are not supported because Color does not yet support colour profiles,
23
- giving no meaningful way to convert colours in absolute colour spaces (like
24
- L*a*b*, XYZ) to non-absolute colour spaces (like RGB).
18
+ The Color library performs purely mathematical manipulation of the colours
19
+ based on colour theory without reference to colour profiles (such as sRGB or
20
+ Adobe RGB). For most purposes, when working with RGB and HSL colour spaces,
21
+ this won't matter. Absolute colour spaces (like CIE L*a*b* and XYZ) and cannot
22
+ be reliably converted to relative colour spaces (like RGB) without colour
23
+ profiles.
25
24
 
26
- Color version 1.5.1 is mostly a maintenance release, fixing some bugs that may
27
- have been introduced with the previous release on Ruby 1.8.7. New features
28
- include an experimental contrast comparison method for RGB colours (found in
29
- lib/color/rgb/contrast.rb) provided by Dave Heitzman, and methods suggested by
30
- Thomas Sawyer based on the Spectrum library.
25
+ Color version 1.6 primarily adds a colour matching method for RGB and
26
+ experimental CIE L*a*b* and XYZ conversion methods for use with the colour
27
+ matching method.
31
28
 
32
- Barring bugs introduced in this release, this will be the last version of color
33
- that supports Ruby 1.8, so make sure that your gem specification is set
34
- properly, to <tt>~> 1.5</tt> if that matters for your application.
35
-
36
- === Note about color 1.5
37
-
38
- Color 1.5 was released before the documetation was complete and has been
39
- yanked.
29
+ Barring bugs introduced in this release, this is the last version of color that
30
+ supports Ruby 1.8, so make sure that your gem specification is set properly (to
31
+ <tt>~> 1.6</tt>) if that matters for your application.
40
32
 
41
33
  == History
42
34
 
data/Rakefile CHANGED
@@ -8,24 +8,20 @@ Hoe.plugin :email
8
8
  Hoe.plugin :gemspec2
9
9
  Hoe.plugin :git
10
10
  Hoe.plugin :minitest
11
- Hoe.plugin :rubyforge
12
11
  Hoe.plugin :travis
12
+ Hoe.plugin :email unless ENV['CI'] or ENV['TRAVIS']
13
13
 
14
14
  spec = Hoe.spec 'color' do
15
- developer('Austin Ziegler', 'austin@rubyforge.org')
15
+ developer('Austin Ziegler', 'halostatue@gmail.com')
16
16
  developer('Matt Lyon', 'matt@postsomnia.com')
17
17
 
18
- self.need_tar = true
19
-
20
- # self.require_ruby_version '>= 1.9.2'
18
+ license 'MIT'
21
19
 
22
- self.remote_rdoc_dir = '.'
23
- self.rsync_args << ' --exclude=statsvn/'
20
+ self.need_tar = true
24
21
 
25
22
  self.history_file = 'History.rdoc'
26
23
  self.readme_file = 'README.rdoc'
27
24
  self.extra_rdoc_files = FileList["*.rdoc"].to_a
28
- self.licenses = ["MIT"]
29
25
 
30
26
  self.extra_dev_deps << ['hoe-doofus', '~> 1.0']
31
27
  self.extra_dev_deps << ['hoe-gemspec2', '~> 1.1']
@@ -34,16 +30,36 @@ spec = Hoe.spec 'color' do
34
30
  self.extra_dev_deps << ['hoe-travis', '~> 1.2']
35
31
  self.extra_dev_deps << ['minitest', '~> 5.0']
36
32
  self.extra_dev_deps << ['rake', '~> 10.0']
33
+
34
+ if RUBY_VERSION >= '1.9' and (ENV['CI'] or ENV['TRAVIS'])
35
+ self.extra_dev_deps << ['simplecov', '~> 0.7']
36
+ self.extra_dev_deps << ['coveralls', '~> 0.7']
37
+ end
37
38
  end
38
39
 
39
- namespace :test do
40
- desc "Runs test coverage. Only works Ruby 1.9+ and assumes 'simplecov' is installed."
41
- task :coverage do
42
- spec.test_prelude = [
43
- 'require "simplecov"',
44
- 'SimpleCov.start("test_frameworks") { command_name "Minitest" }',
45
- 'gem "minitest"'
46
- ].join('; ')
47
- Rake::Task['test'].execute
40
+ if RUBY_VERSION >= '1.9'
41
+ namespace :test do
42
+ desc "Submit test coverage to Coveralls"
43
+ task :coveralls do
44
+ spec.test_prelude = [
45
+ 'require "psych"',
46
+ 'require "simplecov"',
47
+ 'require "coveralls"',
48
+ 'SimpleCov.formatter = Coveralls::SimpleCov::Formatter',
49
+ ].join('; ')
50
+ Rake::Task['test'].execute
51
+ end
52
+
53
+ desc "Runs test coverage. Only works Ruby 1.9+ and assumes 'simplecov' is installed."
54
+ task :coverage do
55
+ spec.test_prelude = [
56
+ 'require "simplecov"',
57
+ 'SimpleCov.start("test_frameworks") { command_name "Minitest" }',
58
+ 'gem "minitest"'
59
+ ].join('; ')
60
+ Rake::Task['test'].execute
61
+ end
48
62
  end
63
+
64
+ Rake::Task['travis'].prerequisites.replace(%w(test:coveralls))
49
65
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  # = Colour Management with Ruby
5
5
  module Color
6
- COLOR_VERSION = '1.5.1'
6
+ COLOR_VERSION = '1.6'
7
7
 
8
8
  class RGB; end
9
9
  class CMYK; end
@@ -7,102 +7,6 @@ class Color::RGB
7
7
  # PDF::Writer package.
8
8
  PDF_FORMAT_STR = "%.3f %.3f %.3f %s"
9
9
 
10
- class << self
11
- # Creates an RGB colour object from percentages 0..100.
12
- #
13
- # Color::RGB.from_percentage(10, 20 30)
14
- def from_percentage(r = 0, g = 0, b = 0, &block)
15
- new(r, g, b, 100.0, &block)
16
- end
17
-
18
- # Creates an RGB colour object from fractional values 0..1.
19
- #
20
- # Color::RGB.from_fraction(.3, .2, .1)
21
- def from_fraction(r = 0.0, g = 0.0, b = 0.0, &block)
22
- new(r, g, b, 1.0, &block)
23
- end
24
-
25
- # Creates an RGB colour object from a grayscale fractional value 0..1.
26
- def from_grayscale_fraction(l = 0.0, &block)
27
- new(l, l, l, 1.0, &block)
28
- end
29
- alias_method :from_greyscale_fraction, :from_grayscale_fraction
30
-
31
- # Creates an RGB colour object from an HTML colour descriptor (e.g.,
32
- # <tt>"fed"</tt> or <tt>"#cabbed;"</tt>.
33
- #
34
- # Color::RGB.from_html("fed")
35
- # Color::RGB.from_html("#fed")
36
- # Color::RGB.from_html("#cabbed")
37
- # Color::RGB.from_html("cabbed")
38
- def from_html(html_colour, &block)
39
- # When we can move to 1.9+ only, this will be \h
40
- h = html_colour.scan(/[0-9a-f]/i)
41
- case h.size
42
- when 3
43
- new(*h.map { |v| (v * 2).to_i(16) }, &block)
44
- when 6
45
- new(*h.each_slice(2).map { |v| v.join.to_i(16) }, &block)
46
- else
47
- raise ArgumentError, "Not a supported HTML colour type."
48
- end
49
- end
50
-
51
- # Find or create a colour by an HTML hex code. This differs from the
52
- # #from_html method in that if the colour code matches a named colour,
53
- # the existing colour will be returned.
54
- #
55
- # Color::RGB.by_hex('ff0000').name # => 'red'
56
- # Color::RGB.by_hex('ff0001').name # => nil
57
- #
58
- # If a block is provided, the value that is returned by the block will
59
- # be returned instead of the exception caused by an error in providing a
60
- # correct hex format.
61
- def by_hex(hex, &block)
62
- __by_hex.fetch(html_hexify(hex)) { from_html(hex) }
63
- rescue
64
- if block
65
- block.call
66
- else
67
- raise
68
- end
69
- end
70
-
71
- # Return a colour as identified by the colour name.
72
- def by_name(name, &block)
73
- __by_name.fetch(name.to_s.downcase, &block)
74
- end
75
-
76
- # Return a colour as identified by the colour name, or by hex.
77
- def by_css(name_or_hex, &block)
78
- by_name(name_or_hex) { by_hex(name_or_hex, &block) }
79
- end
80
-
81
- # Extract named or hex colours from the provided text.
82
- def extract_colors(text, mode = :both)
83
- text = text.downcase
84
- regex = case mode
85
- when :name
86
- Regexp.union(__by_name.keys)
87
- when :hex
88
- Regexp.union(__by_hex.keys)
89
- when :both
90
- Regexp.union(__by_hex.keys + __by_name.keys)
91
- end
92
-
93
- text.scan(regex).map { |match|
94
- case mode
95
- when :name
96
- by_name(match)
97
- when :hex
98
- by_hex(match)
99
- when :both
100
- by_css(match)
101
- end
102
- }
103
- end
104
- end
105
-
106
10
  # Coerces the other Color object into RGB.
107
11
  def coerce(other)
108
12
  other.to_rgb
@@ -269,6 +173,78 @@ class Color::RGB
269
173
  Color::HSL.from_fraction(hue, sat, lum)
270
174
  end
271
175
 
176
+ # Returns the XYZ colour encoding of the value. Based on the
177
+ # {RGB to XYZ}[http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html]
178
+ # formula presented by Bruce Lindbloom.
179
+ #
180
+ # Currently only the sRGB colour space is supported.
181
+ def to_xyz(color_space = :sRGB)
182
+ unless color_space.to_s.downcase == 'srgb'
183
+ raise ArgumentError, "Unsupported colour space #{color_space}."
184
+ end
185
+
186
+ # Inverse sRGB companding. Linearizes RGB channels with respect to
187
+ # energy.
188
+ r, g, b = [ @r, @g, @b ].map { |v|
189
+ if (v > 0.04045)
190
+ (((v + 0.055) / 1.055) ** 2.4) * 100
191
+ else
192
+ (v / 12.92) * 100
193
+ end
194
+ }
195
+
196
+ # Convert using the RGB/XYZ matrix at:
197
+ # http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html#WSMatrices
198
+ {
199
+ :x => (r * 0.4124564 + g * 0.3575761 + b * 0.1804375),
200
+ :y => (r * 0.2126729 + g * 0.7151522 + b * 0.0721750),
201
+ :z => (r * 0.0193339 + g * 0.1191920 + b * 0.9503041)
202
+ }
203
+ end
204
+
205
+ # Returns the L*a*b* colour encoding of the value via the XYZ colour
206
+ # encoding. Based on the
207
+ # {XYZ to Lab}[http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html]
208
+ # formula presented by Bruce Lindbloom.
209
+ #
210
+ # Currently only the sRGB colour space is supported and defaults to using
211
+ # a D65 reference white.
212
+ def to_lab(color_space = :sRGB, reference_white = [ 95.047, 100.00, 108.883 ])
213
+ xyz = to_xyz
214
+
215
+ # Calculate the ratio of the XYZ values to the reference white.
216
+ # http://www.brucelindbloom.com/index.html?Equations.html
217
+ xr = xyz[:x] / reference_white[0]
218
+ yr = xyz[:y] / reference_white[1]
219
+ zr = xyz[:z] / reference_white[2]
220
+
221
+ # NOTE: This should be using Rational instead of floating point values,
222
+ # otherwise there will be discontinuities.
223
+ # http://www.brucelindbloom.com/LContinuity.html
224
+ epsilon = (216 / 24389.0)
225
+ kappa = (24389 / 27.0)
226
+
227
+ # And now transform
228
+ # http://en.wikipedia.org/wiki/Lab_color_space#Forward_transformation
229
+ # There is a brief explanation there as far as the nature of the calculations,
230
+ # as well as a much nicer looking modeling of the algebra.
231
+ fx, fy, fz = [ xr, yr, zr ].map { |t|
232
+ if (t > (epsilon))
233
+ t ** (1.0 / 3)
234
+ else # t <= epsilon
235
+ ((kappa * t) + 16) / 116.0
236
+ # The 4/29 here is for when t = 0 (black). 4/29 * 116 = 16, and 16 -
237
+ # 16 = 0, which is the correct value for L* with black.
238
+ # ((1.0/3)*((29.0/6)**2) * t) + (4.0/29)
239
+ end
240
+ }
241
+ {
242
+ :L => ((116 * fy) - 16),
243
+ :a => (500 * (fx - fy)),
244
+ :b => (200 * (fy - fz))
245
+ }
246
+ end
247
+
272
248
  # Mix the RGB hue with White so that the RGB hue is the specified
273
249
  # percentage of the resulting colour. Strictly speaking, this isn't a
274
250
  # darken_by operation.
@@ -350,6 +326,117 @@ class Color::RGB
350
326
  hsl.to_rgb
351
327
  end
352
328
 
329
+ # TODO: Identify the base colour profile used for L*a*b* and XYZ
330
+ # conversions.
331
+
332
+ # Calculates and returns the closest match to this colour from a list of
333
+ # provided colours. Returns +nil+ if +color_list+ is empty or if there is
334
+ # no colour within the +threshold_distance+.
335
+ #
336
+ # +threshold_distance+ is used to determine the minimum colour distance
337
+ # permitted. Uses the CIE Delta E 1994 algorithm (CIE94) to find near
338
+ # matches based on perceived visual colour. The default value (1000.0) is
339
+ # an arbitrarily large number. The values <tt>:jnd</tt> and
340
+ # <tt>:just_noticeable</tt> may be passed as the +threshold_distance+ to
341
+ # use the value <tt>2.3</tt>.
342
+ def closest_match(color_list, threshold_distance = 1000.0)
343
+ color_list = [ color_list ].flatten(1)
344
+ return nil if color_list.empty?
345
+
346
+ threshold_distance = case threshold_distance
347
+ when :jnd, :just_noticeable
348
+ 2.3
349
+ else
350
+ threshold_distance.to_f
351
+ end
352
+ lab = to_lab
353
+ closest_distance = threshold_distance
354
+ best_match = nil
355
+
356
+ color_list.each do |c|
357
+ distance = delta_e94(lab, c.to_lab)
358
+ if (distance < closest_distance)
359
+ closest_distance = distance
360
+ best_match = c
361
+ end
362
+ end
363
+ best_match
364
+ end
365
+
366
+ # The Delta E (CIE94) algorithm
367
+ # http://en.wikipedia.org/wiki/Color_difference#CIE94
368
+ #
369
+ # There is a newer version, CIEDE2000, that uses slightly more complicated
370
+ # math, but addresses "the perceptual uniformity issue" left lingering by
371
+ # the CIE94 algorithm. color_1 and color_2 are both L*a*b* hashes,
372
+ # rendered by #to_lab.
373
+ #
374
+ # Since our source is treated as sRGB, we use the "graphic arts" presets
375
+ # for k_L, k_1, and k_2
376
+ #
377
+ # The calculations go through LCH(ab). (?)
378
+ #
379
+ # See also http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE94.html
380
+ #
381
+ # NOTE: This should be moved to Color::Lab.
382
+ def delta_e94(color_1, color_2, weighting_type = :graphic_arts)
383
+ case weighting_type
384
+ when :graphic_arts
385
+ k_1 = 0.045
386
+ k_2 = 0.015
387
+ k_L = 1
388
+ when :textiles
389
+ k_1 = 0.048
390
+ k_2 = 0.014
391
+ k_L = 2
392
+ else
393
+ raise ArgumentError, "Unsupported weighting type #{weighting_type}."
394
+ end
395
+
396
+ # delta_E = Math.sqrt(
397
+ # ((delta_L / (k_L * s_L)) ** 2) +
398
+ # ((delta_C / (k_C * s_C)) ** 2) +
399
+ # ((delta_H / (k_H * s_H)) ** 2)
400
+ # )
401
+ #
402
+ # Under some circumstances in real computers, delta_H could be an
403
+ # imaginary number (it's a square root value), so we're going to treat
404
+ # this as:
405
+ #
406
+ # delta_E = Math.sqrt(
407
+ # ((delta_L / (k_L * s_L)) ** 2) +
408
+ # ((delta_C / (k_C * s_C)) ** 2) +
409
+ # (delta_H2 / ((k_H * s_H) ** 2)))
410
+ # )
411
+ #
412
+ # And not perform the square root when calculating delta_H2.
413
+
414
+ k_C = k_H = 1
415
+
416
+ l_1, a_1, b_1 = color_1.values_at(:L, :a, :b)
417
+ l_2, a_2, b_2 = color_2.values_at(:L, :a, :b)
418
+
419
+ delta_a = a_1 - a_2
420
+ delta_b = b_1 - b_2
421
+
422
+ c_1 = Math.sqrt((a_1 ** 2) + (b_1 ** 2))
423
+ c_2 = Math.sqrt((a_2 ** 2) + (b_2 ** 2))
424
+
425
+ delta_L = color_1[:L] - color_2[:L]
426
+ delta_C = c_1 - c_2
427
+
428
+ delta_H2 = (delta_a ** 2) + (delta_b ** 2) - (delta_C ** 2)
429
+
430
+ s_L = 1
431
+ s_C = 1 + k_1 * c_1
432
+ s_H = 1 + k_2 * c_1
433
+
434
+ composite_L = (delta_L / (k_L * s_L)) ** 2
435
+ composite_C = (delta_C / (k_C * s_C)) ** 2
436
+ composite_H = delta_H2 / ((k_H * s_H) ** 2)
437
+ Math.sqrt(composite_L + composite_C + composite_H)
438
+ end
439
+
353
440
  # Returns the red component of the colour in the normal 0 .. 255 range.
354
441
  def red
355
442
  @r * 255.0
@@ -454,7 +541,7 @@ class Color::RGB
454
541
  # Retrieve the maxmum RGB value from the current colour as a GrayScale
455
542
  # colour
456
543
  def max_rgb_as_grayscale
457
- Color::GrayScale.from_fraction([@r, @g, @b].max)
544
+ Color::GrayScale.from_fraction([@r, @g, @b].max)
458
545
  end
459
546
  alias max_rgb_as_greyscale max_rgb_as_grayscale
460
547
 
@@ -486,6 +573,102 @@ class Color::RGB
486
573
  end
487
574
  end
488
575
 
576
+ class << Color::RGB
577
+ # Creates an RGB colour object from percentages 0..100.
578
+ #
579
+ # Color::RGB.from_percentage(10, 20, 30)
580
+ def from_percentage(r = 0, g = 0, b = 0, &block)
581
+ new(r, g, b, 100.0, &block)
582
+ end
583
+
584
+ # Creates an RGB colour object from fractional values 0..1.
585
+ #
586
+ # Color::RGB.from_fraction(.3, .2, .1)
587
+ def from_fraction(r = 0.0, g = 0.0, b = 0.0, &block)
588
+ new(r, g, b, 1.0, &block)
589
+ end
590
+
591
+ # Creates an RGB colour object from a grayscale fractional value 0..1.
592
+ def from_grayscale_fraction(l = 0.0, &block)
593
+ new(l, l, l, 1.0, &block)
594
+ end
595
+ alias_method :from_greyscale_fraction, :from_grayscale_fraction
596
+
597
+ # Creates an RGB colour object from an HTML colour descriptor (e.g.,
598
+ # <tt>"fed"</tt> or <tt>"#cabbed;"</tt>.
599
+ #
600
+ # Color::RGB.from_html("fed")
601
+ # Color::RGB.from_html("#fed")
602
+ # Color::RGB.from_html("#cabbed")
603
+ # Color::RGB.from_html("cabbed")
604
+ def from_html(html_colour, &block)
605
+ # When we can move to 1.9+ only, this will be \h
606
+ h = html_colour.scan(/[0-9a-f]/i)
607
+ case h.size
608
+ when 3
609
+ new(*h.map { |v| (v * 2).to_i(16) }, &block)
610
+ when 6
611
+ new(*h.each_slice(2).map { |v| v.join.to_i(16) }, &block)
612
+ else
613
+ raise ArgumentError, "Not a supported HTML colour type."
614
+ end
615
+ end
616
+
617
+ # Find or create a colour by an HTML hex code. This differs from the
618
+ # #from_html method in that if the colour code matches a named colour,
619
+ # the existing colour will be returned.
620
+ #
621
+ # Color::RGB.by_hex('ff0000').name # => 'red'
622
+ # Color::RGB.by_hex('ff0001').name # => nil
623
+ #
624
+ # If a block is provided, the value that is returned by the block will
625
+ # be returned instead of the exception caused by an error in providing a
626
+ # correct hex format.
627
+ def by_hex(hex, &block)
628
+ __by_hex.fetch(html_hexify(hex)) { from_html(hex) }
629
+ rescue
630
+ if block
631
+ block.call
632
+ else
633
+ raise
634
+ end
635
+ end
636
+
637
+ # Return a colour as identified by the colour name.
638
+ def by_name(name, &block)
639
+ __by_name.fetch(name.to_s.downcase, &block)
640
+ end
641
+
642
+ # Return a colour as identified by the colour name, or by hex.
643
+ def by_css(name_or_hex, &block)
644
+ by_name(name_or_hex) { by_hex(name_or_hex, &block) }
645
+ end
646
+
647
+ # Extract named or hex colours from the provided text.
648
+ def extract_colors(text, mode = :both)
649
+ text = text.downcase
650
+ regex = case mode
651
+ when :name
652
+ Regexp.union(__by_name.keys)
653
+ when :hex
654
+ Regexp.union(__by_hex.keys)
655
+ when :both
656
+ Regexp.union(__by_hex.keys + __by_name.keys)
657
+ end
658
+
659
+ text.scan(regex).map { |match|
660
+ case mode
661
+ when :name
662
+ by_name(match)
663
+ when :hex
664
+ by_hex(match)
665
+ when :both
666
+ by_css(match)
667
+ end
668
+ }
669
+ end
670
+ end
671
+
489
672
  class << Color::RGB
490
673
  private
491
674
  def __named_color(mod, rgb, *names)
@@ -0,0 +1,57 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ class Color::RGB
4
+ # Outputs how much contrast this color has with another RGB color.
5
+ # Computes the same regardless of which one is considered foreground. If
6
+ # the other color does not have a #to_rgb method, this will throw an
7
+ # exception. Anything over about 0.22 should have a high likelihood of
8
+ # being legible, but the larger the difference, the more contrast.
9
+ # Otherwise, to be safe go with something > 0.3
10
+ def contrast(other)
11
+ other = coerce(other)
12
+
13
+ # The following numbers have been set with some care.
14
+ ((diff_brightness(other) * 0.65) +
15
+ (diff_hue(other) * 0.20) +
16
+ (diff_luminosity(other) * 0.15))
17
+ end
18
+
19
+ private
20
+ # Provides the luminosity difference between two rbg vals
21
+ def diff_luminosity(other)
22
+ other = coerce(other)
23
+ l1 = (0.2126 * (other.r) ** 2.2) +
24
+ (0.7152 * (other.b) ** 2.2) +
25
+ (0.0722 * (other.g) ** 2.2);
26
+
27
+ l2 = (0.2126 * (self.r) ** 2.2) +
28
+ (0.7152 * (self.b) ** 2.2) +
29
+ (0.0722 * (self.g) ** 2.2);
30
+
31
+ ((([l1, l2].max) + 0.05) / ( ([l1, l2].min) + 0.05 ) - 1) / 20.0
32
+ end
33
+
34
+ # Provides the brightness difference.
35
+ def diff_brightness(other)
36
+ other = other.to_rgb
37
+ br1 = (299 * other.r + 587 * other.g + 114 * other.b)
38
+ br2 = (299 * self.r + 587 * self.g + 114 * self.b)
39
+ (br1 - br2).abs / 1000.0;
40
+ end
41
+
42
+ # Provides the euclidean distance between the two color values
43
+ def diff_euclidean(other)
44
+ other = other.to_rgb
45
+ ((((other.r - self.r) ** 2) +
46
+ ((other.g - self.g) ** 2) +
47
+ ((other.b - self.b) ** 2)) ** 0.5) / 1.7320508075688772
48
+ end
49
+
50
+ # Difference in the two colors' hue
51
+ def diff_hue(other)
52
+ other = other.to_rgb
53
+ ((self.r - other.r).abs +
54
+ (self.g - other.g).abs +
55
+ (self.b - other.b).abs) / 3
56
+ end
57
+ end
@@ -0,0 +1,6 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ require 'color'
4
+
5
+ gem 'minitest'
6
+ require 'minitest/autorun'
@@ -277,6 +277,47 @@ module TestColor
277
277
  Color::RGB::Cayenne.to_yiq)
278
278
  end
279
279
 
280
+ def test_to_lab
281
+ # Luminosity can be an absolute.
282
+ assert_in_delta(0.0, Color::RGB::Black.to_lab[:L], Color::COLOR_TOLERANCE)
283
+ assert_in_delta(100.0, Color::RGB::White.to_lab[:L], Color::COLOR_TOLERANCE)
284
+
285
+ # It's not really possible to have absolute
286
+ # numbers here because of how L*a*b* works, but
287
+ # negative/positive comparisons work
288
+ assert(Color::RGB::Green.to_lab[:a] < 0)
289
+ assert(Color::RGB::Magenta.to_lab[:a] > 0)
290
+ assert(Color::RGB::Blue.to_lab[:b] < 0)
291
+ assert(Color::RGB::Yellow.to_lab[:b] > 0)
292
+ end
293
+
294
+ def test_closest_match
295
+ # It should match Blue to Indigo (very simple match)
296
+ match_from = [Color::RGB::Red, Color::RGB::Green, Color::RGB::Blue]
297
+ assert_equal(Color::RGB::Blue, Color::RGB::Indigo.closest_match(match_from))
298
+ # But fails if using the :just_noticeable difference.
299
+ assert_nil(Color::RGB::Indigo.closest_match(match_from, :just_noticeable))
300
+
301
+ # Crimson & Firebrick are visually closer than DarkRed and Firebrick
302
+ # (more precise match)
303
+ match_from += [Color::RGB::DarkRed, Color::RGB::Crimson]
304
+ assert_equal(Color::RGB::Crimson,
305
+ Color::RGB::Firebrick.closest_match(match_from))
306
+ # Specifying a threshold low enough will cause even that match to
307
+ # fail, though.
308
+ assert_nil(Color::RGB::Firebrick.closest_match(match_from, 8.0))
309
+ # If the match_from list is an empty array, it also returns nil
310
+ assert_nil(Color::RGB::Red.closest_match([]))
311
+
312
+ # RGB::Green is 0,128,0, so we'll pick something VERY close to it, visually
313
+ jnd_green = Color::RGB.new(3, 132, 3)
314
+ assert_equal(Color::RGB::Green,
315
+ jnd_green.closest_match(match_from, :jnd))
316
+ # And then something that's just barely out of the tolerance range
317
+ diff_green = Color::RGB.new(9, 142, 9)
318
+ assert_nil(diff_green.closest_match(match_from, :jnd))
319
+ end
320
+
280
321
  def test_add
281
322
  white = Color::RGB::Cyan + Color::RGB::Yellow
282
323
  refute_nil(white)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: color
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: '1.6'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Austin Ziegler
@@ -11,10 +11,10 @@ bindir: bin
11
11
  cert_chain:
12
12
  - !binary |-
13
13
  LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUROakNDQWg2Z0F3SUJB
14
- Z0lCQURBTkJna3Foa2lHOXcwQkFRVUZBREJCTVE4d0RRWURWUVFEREFaaGRY
14
+ Z0lCQVRBTkJna3Foa2lHOXcwQkFRVUZBREJCTVE4d0RRWURWUVFEREFaaGRY
15
15
  TjAKYVc0eEdUQVhCZ29Ka2lhSmsvSXNaQUVaRmdseWRXSjVabTl5WjJVeEV6
16
- QVJCZ29Ka2lhSmsvSXNaQUVaRmdOdgpjbWN3SGhjTk1UTXdNakEwTURNek16
17
- STNXaGNOTVRRd01qQTBNRE16TXpJM1dqQkJNUTh3RFFZRFZRUUREQVpoCmRY
16
+ QVJCZ29Ka2lhSmsvSXNaQUVaRmdOdgpjbWN3SGhjTk1UUXdNakl5TURNME1U
17
+ UXpXaGNOTVRVd01qSXlNRE0wTVRReldqQkJNUTh3RFFZRFZRUUREQVpoCmRY
18
18
  TjBhVzR4R1RBWEJnb0praWFKay9Jc1pBRVpGZ2x5ZFdKNVptOXlaMlV4RXpB
19
19
  UkJnb0praWFKay9Jc1pBRVoKRmdOdmNtY3dnZ0VpTUEwR0NTcUdTSWIzRFFF
20
20
  QkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDMm1QTmY0TDM3R2hLSQpTUENZc3ZZ
@@ -25,19 +25,19 @@ cert_chain:
25
25
  Z3kxR1Z3VAppNnBrRHM4TGhaV1hkT0QrOTIxbDJaMU5aR1phOUtOYkpJZzZ2
26
26
  dGdZS1U5OGpRNXFyOWlZM2lrQkFzcEhyRmFzCks2VVN2R2dBZzhmQ0Q1WWlv
27
27
  dEJFdkNCTVl0ZnFtZnJocGRVMnArZ3ZUZ2VMVzFLYWV2d3FkN25nUW1GVXJG
28
- RzEKZVVKU1VSdjVBZ01CQUFHak9UQTNNQWtHQTFVZEV3UUNNQUF3SFFZRFZS
29
- ME9CQllFRkF0SktNcDZZWU5xbGdSMwo5VGlaTFdxdkxhZ1NNQXNHQTFVZER3
30
- UUVBd0lFc0RBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQXBUUGt2RG04Cjdn
31
- SmxVVDRGZnVtWFB2dHVxUDY3THhVdEdFOHN5dlIwQTRBcyswUC93eWxMSkZV
32
- T3NHVFRkWll0VGhoeENTSkcKKzdLRzJGZkljSDRaejJkOTdhclpHQXpCb2k4
33
- aVBodDIvVXRTbDFmQ2NVSTV2bUphMU1pWFpUMm9xZFc3V3lkcQpyQVpjQlBs
34
- cllZdWl3dEdJMHlxSU9nQmZYU1pDV1dzSnN1eVRLRUxlcDZtQ0xnejBZWlVm
35
- bXZLcjhXL0FiM2F4CkR1THpIOTJMU1JqWkp5anlBVXB3L1ZjMnJNNGdpaVA1
36
- anRCeXJiMVkxZEduUWhIVE1IZjFHZnVjV203TncvVjkKdHdFUFZ3OCswZjg4
37
- SlF1Y3hPVG1URjFOYkxGcGlSd1FVWjF6b1piTmcyZTdtU2hjL2VleG5WTFdL
38
- Rkt4Um9QNgpLUGozV29EK3NwQjhmQT09Ci0tLS0tRU5EIENFUlRJRklDQVRF
28
+ RzEKZVVKU1VSdjVBZ01CQUFHak9UQTNNQWtHQTFVZEV3UUNNQUF3Q3dZRFZS
29
+ MFBCQVFEQWdTd01CMEdBMVVkRGdRVwpCQlFMU1NqS2VtR0RhcFlFZC9VNG1T
30
+ MXFyeTJvRWpBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQU5tMmFnVGRECjlT
31
+ Mk53WE1XMGphbnNJblh0UW1CNDRxay9wc1d1anRHbm4rb1QrYTlLWE81cC9n
32
+ eDJtbXg4aE1GMDJ3VUJ4MUgKazk2SFVJL2pSM0hkaFlDZkc2b0p1RXpnWHJG
33
+ aVNCSncvY09KaU04djNhSHNBd0kzTmVMZUlyUndCWUIza0kzagoxcWZKWGNP
34
+ V3c3YzYzVHJzRFgzN3hqMmU0UDBETkoxY1RyRG15RDJ5VFE1Nzc2TTEzR2I2
35
+ blhqcmVTZXEwdC9uCjYwTmo5MUoxb0hZazZMRmEwZW8vZ3lrVGJMeWFacnNh
36
+ WGxOYjNqN0NqaFV6T3BZT2hpQ1VIM3M5dEtUR1hkLysKTG1aN0J4VE1zRGha
37
+ SHkzay9FVEZoaSs3cElVV2xGbzBpbXJkeUxoZCtKdzNib1ZqM0NtdnloY3dt
38
+ cG9NMEs5bApBT21yVWlFbFVxTE9aQT09Ci0tLS0tRU5EIENFUlRJRklDQVRF
39
39
  LS0tLS0K
40
- date: 2014-01-28 00:00:00.000000000 Z
40
+ date: 2014-05-20 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: minitest
@@ -45,28 +45,14 @@ dependencies:
45
45
  requirements:
46
46
  - - ~>
47
47
  - !ruby/object:Gem::Version
48
- version: '5.2'
48
+ version: '5.3'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - ~>
54
54
  - !ruby/object:Gem::Version
55
- version: '5.2'
56
- - !ruby/object:Gem::Dependency
57
- name: rubyforge
58
- requirement: !ruby/object:Gem::Requirement
59
- requirements:
60
- - - ! '>='
61
- - !ruby/object:Gem::Version
62
- version: 2.0.4
63
- type: :development
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
69
- version: 2.0.4
55
+ version: '5.3'
70
56
  - !ruby/object:Gem::Dependency
71
57
  name: rdoc
72
58
  requirement: !ruby/object:Gem::Requirement
@@ -171,14 +157,14 @@ dependencies:
171
157
  requirements:
172
158
  - - ~>
173
159
  - !ruby/object:Gem::Version
174
- version: '3.8'
160
+ version: '3.12'
175
161
  type: :development
176
162
  prerelease: false
177
163
  version_requirements: !ruby/object:Gem::Requirement
178
164
  requirements:
179
165
  - - ~>
180
166
  - !ruby/object:Gem::Version
181
- version: '3.8'
167
+ version: '3.12'
182
168
  description: ! 'Color is a Ruby library to provide basic RGB, CMYK, HSL, and other
183
169
  colourspace
184
170
 
@@ -191,52 +177,50 @@ description: ! 'Color is a Ruby library to provide basic RGB, CMYK, HSL, and oth
191
177
  contrasting palettes is also included.
192
178
 
193
179
 
194
- The capabilities of the Color library are limited to pure mathematical
195
-
196
- manipulation of the colours based on colour theory without reference to colour
197
-
198
- profiles (such as sRGB or Adobe RGB). For most purposes, when working with the
180
+ The Color library performs purely mathematical manipulation of the colours
199
181
 
200
- RGB and HSL colours, this won''t matter. However, some colour models (like CIE
182
+ based on colour theory without reference to colour profiles (such as sRGB or
201
183
 
202
- L*a*b*) are not supported because Color does not yet support colour profiles,
184
+ Adobe RGB). For most purposes, when working with RGB and HSL colour spaces,
203
185
 
204
- giving no meaningful way to convert colours in absolute colour spaces (like
186
+ this won''t matter. Absolute colour spaces (like CIE L*a*b* and XYZ) and cannot
205
187
 
206
- L*a*b*, XYZ) to non-absolute colour spaces (like RGB).
188
+ be reliably converted to relative colour spaces (like RGB) without colour
207
189
 
190
+ profiles.
208
191
 
209
- Color version 1.5.1 is mostly a maintenance release, fixing some bugs that may
210
192
 
211
- have been introduced with the previous release on Ruby 1.8.7. New features
193
+ Color version 1.6 primarily adds a colour matching method for RGB and
212
194
 
213
- include an experimental contrast comparison method for RGB colours (found in
195
+ experimental CIE L*a*b* and XYZ conversion methods for use with the colour
214
196
 
215
- lib/color/rgb/contrast.rb) provided by Dave Heitzman, and methods suggested by
197
+ matching method.
216
198
 
217
- Thomas Sawyer based on the Spectrum library.
218
199
 
200
+ Barring bugs introduced in this release, this is the last version of color that
219
201
 
220
- Barring bugs introduced in this release, this will be the last version of color
202
+ supports Ruby 1.8, so make sure that your gem specification is set properly (to
221
203
 
222
- that supports Ruby 1.8, so make sure that your gem specification is set
223
-
224
- properly, to <tt>~> 1.5</tt> if that matters for your application.'
204
+ <tt>~> 1.6</tt>) if that matters for your application.'
225
205
  email:
226
- - austin@rubyforge.org
206
+ - halostatue@gmail.com
227
207
  - matt@postsomnia.com
228
208
  executables: []
229
209
  extensions: []
230
210
  extra_rdoc_files:
211
+ - Contributing.rdoc
231
212
  - History.rdoc
232
213
  - Licence.rdoc
233
214
  - Manifest.txt
234
215
  - README.rdoc
235
- - Contributing.rdoc
236
216
  files:
217
+ - .autotest
237
218
  - .gemtest
238
219
  - .hoerc
220
+ - .minitest.rb
221
+ - .travis.yml
239
222
  - Contributing.rdoc
223
+ - Gemfile
240
224
  - History.rdoc
241
225
  - Licence.rdoc
242
226
  - Manifest.txt
@@ -253,8 +237,10 @@ files:
253
237
  - lib/color/palette/monocontrast.rb
254
238
  - lib/color/rgb.rb
255
239
  - lib/color/rgb/colors.rb
240
+ - lib/color/rgb/contrast.rb
256
241
  - lib/color/rgb/metallic.rb
257
242
  - lib/color/yiq.rb
243
+ - test/minitest_helper.rb
258
244
  - test/test_adobecolor.rb
259
245
  - test/test_cmyk.rb
260
246
  - test/test_color.rb
@@ -286,7 +272,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
286
272
  - !ruby/object:Gem::Version
287
273
  version: '0'
288
274
  requirements: []
289
- rubyforge_project: color
275
+ rubyforge_project:
290
276
  rubygems_version: 2.2.1
291
277
  signing_key:
292
278
  specification_version: 4
metadata.gz.sig CHANGED
Binary file