color 1.5.1 → 1.6

Sign up to get free protection for your applications and to get access to all the features.
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