color_contrast_calc 0.3.0 → 0.4.0

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,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 624186d8778091959fe8526cba79cbe56708c16d
4
- data.tar.gz: 2961fad47dfc7afa4b4893dd3d7b0995a97e5601
3
+ metadata.gz: 513b56ff5dca5ea3a6a7343a4cc5111a19c976e2
4
+ data.tar.gz: 9958f22f8d1474b93a3573ce728ffe6e41abe6d7
5
5
  SHA512:
6
- metadata.gz: a28e5639b5db61cb0a722a1802b6969d1bb2c20d60783efd9242094f76e984987bd16330d37ac2f36f480c25b81b2679617035159dde4f74798c101a7b7e0de8
7
- data.tar.gz: 5f95b64878aba232ed3923849de80776c33229a899fb76014ed397fa3918f89ab869cc690546cbe49ee68f8f970944f4e315ef23beb29fdfae0391f26b1efdcc
6
+ metadata.gz: ebf3189b2a716538c29fe3c3730e47f79d9a51fba4d53989269fe20f9a56e0225ee38e105936aa2c05844b3fb4925243feeefa9c670733975f82b46eabb15c2d
7
+ data.tar.gz: 5c6b613c861aaa290e31d1c1c710c6d7775beb742f43c896f25e7c10bf1b890cc7833ebe7f37b8b4c5727bc1ce68eb4ad9c8916f8bc853fc8f47d405ac2d34ec
data/README.ja.md CHANGED
@@ -174,7 +174,7 @@ The contrast ratio between #ffff00 and #9d6600 is 4.5121
174
174
  ### 例3: ある色のグレースケール
175
175
 
176
176
  ある色のグレースケールを得るために`ColorContrastCalc::Color` には
177
- `new_grayscale_color`というインスタンスメソッドがあります。
177
+ `with_grayscale`というインスタンスメソッドがあります。
178
178
 
179
179
  例えば次のコードを`grayscale.rb`として保存し:
180
180
 
@@ -185,8 +185,8 @@ yellow = ColorContrastCalc.color_from('yellow')
185
185
  orange = ColorContrastCalc.color_from('orange')
186
186
 
187
187
  report = 'The grayscale of %s is %s.'
188
- puts(format(report, yellow.hex, yellow.new_grayscale_color))
189
- puts(format(report, orange.hex, orange.new_grayscale_color))
188
+ puts(format(report, yellow.hex, yellow.with_grayscale))
189
+ puts(format(report, orange.hex, orange.with_grayscale))
190
190
  ```
191
191
 
192
192
  以下のように実行します:
@@ -197,9 +197,20 @@ The grayscale of #ffff00 is #ededed.
197
197
  The grayscale of #ffa500 is #acacac.
198
198
  ```
199
199
 
200
- また`new_grayscale_color`以外に、以下のインスタンスメッソドが
200
+ また`with_grayscale`以外に、以下のインスタンスメッソドが
201
201
  `ColorContrastCalc::Color`では利用できます。:
202
202
 
203
+ * `with_brightness`
204
+ * `with_contrast`
205
+ * `with_hue_rotate`
206
+ * `with_invert`
207
+ * `with_saturate`
208
+
209
+ #### 非推奨のメソッド
210
+
211
+ 以下のメソッドは非推奨であることにご注意下さい。
212
+
213
+ * `new_grayscale_color`
203
214
  * `new_brightness_color`
204
215
  * `new_contrast_color`
205
216
  * `new_hue_rotate_color`
data/README.md CHANGED
@@ -174,7 +174,7 @@ The contrast ratio between #ffff00 and #9d6600 is 4.5121
174
174
  ### Example 3: Grayscale of given colors
175
175
 
176
176
  For getting grayscale, `ColorContrastCalc::Color` has an instance method
177
- `new_grayscale_color`.
177
+ `with_grayscale`.
178
178
  For example, save the following code as `grayscale.rb`:
179
179
 
180
180
  ```ruby
@@ -184,8 +184,8 @@ yellow = ColorContrastCalc.color_from('yellow')
184
184
  orange = ColorContrastCalc.color_from('orange')
185
185
 
186
186
  report = 'The grayscale of %s is %s.'
187
- puts(format(report, yellow.hex, yellow.new_grayscale_color))
188
- puts(format(report, orange.hex, orange.new_grayscale_color))
187
+ puts(format(report, yellow.hex, yellow.with_grayscale))
188
+ puts(format(report, orange.hex, orange.with_grayscale))
189
189
  ```
190
190
 
191
191
  Then execute the script:
@@ -196,9 +196,20 @@ The grayscale of #ffff00 is #ededed.
196
196
  The grayscale of #ffa500 is #acacac.
197
197
  ```
198
198
 
199
- And other than `new_grayscale_color`, following instance methods
199
+ And other than `with_grayscale`, following instance methods
200
200
  are available for `ColorContrastCalc::Color`:
201
201
 
202
+ * `with_brightness`
203
+ * `with_contrast`
204
+ * `with_hue_rotate`
205
+ * `with_invert`
206
+ * `with_saturate`
207
+
208
+ #### Deprecated instance methods
209
+
210
+ Please note the following methods are deprecated:
211
+
212
+ * `new_grayscale_color`
202
213
  * `new_brightness_color`
203
214
  * `new_contrast_color`
204
215
  * `new_hue_rotate_color`
@@ -6,5 +6,5 @@ yellow = ColorContrastCalc.color_from('yellow')
6
6
  orange = ColorContrastCalc.color_from('orange')
7
7
 
8
8
  report = 'The grayscale of %s is %s.'
9
- puts(format(report, yellow.hex, yellow.new_grayscale_color))
10
- puts(format(report, orange.hex, orange.new_grayscale_color))
9
+ puts(format(report, yellow.hex, yellow.with_grayscale))
10
+ puts(format(report, orange.hex, orange.with_grayscale))
@@ -3,6 +3,7 @@
3
3
  require 'color_contrast_calc/utils'
4
4
  require 'color_contrast_calc/checker'
5
5
  require 'color_contrast_calc/threshold_finder'
6
+ require 'color_contrast_calc/deprecated'
6
7
  require 'json'
7
8
 
8
9
  module ColorContrastCalc
@@ -13,6 +14,7 @@ module ColorContrastCalc
13
14
  # instances of Color class.
14
15
 
15
16
  class Color
17
+ include Deprecated::Color
16
18
  # @private
17
19
  RGB_LIMITS = [0, 255].freeze
18
20
 
@@ -102,7 +104,7 @@ module ColorContrastCalc
102
104
  # code is assigned instead.
103
105
  # @return [Color] New color with adjusted contrast
104
106
 
105
- def new_contrast_color(ratio, name = nil)
107
+ def with_contrast(ratio, name = nil)
106
108
  generate_new_color(Converter::Contrast, ratio, name)
107
109
  end
108
110
 
@@ -115,7 +117,7 @@ module ColorContrastCalc
115
117
  # code is assigned instead.
116
118
  # @return [Color] New color with adjusted brightness
117
119
 
118
- def new_brightness_color(ratio, name = nil)
120
+ def with_brightness(ratio, name = nil)
119
121
  generate_new_color(Converter::Brightness, ratio, name)
120
122
  end
121
123
 
@@ -128,7 +130,7 @@ module ColorContrastCalc
128
130
  # code is assigned instead.
129
131
  # @return [Color] New inverted color
130
132
 
131
- def new_invert_color(ratio = 100, name = nil)
133
+ def with_invert(ratio = 100, name = nil)
132
134
  generate_new_color(Converter::Invert, ratio, name)
133
135
  end
134
136
 
@@ -141,7 +143,7 @@ module ColorContrastCalc
141
143
  # code is assigned instead.
142
144
  # @return [Color] New hue rotation applied color
143
145
 
144
- def new_hue_rotate_color(degree, name = nil)
146
+ def with_hue_rotate(degree, name = nil)
145
147
  generate_new_color(Converter::HueRotate, degree, name)
146
148
  end
147
149
 
@@ -154,7 +156,7 @@ module ColorContrastCalc
154
156
  # code is assigned instead.
155
157
  # @return [Color] New saturated color
156
158
 
157
- def new_saturate_color(ratio, name = nil)
159
+ def with_saturate(ratio, name = nil)
158
160
  generate_new_color(Converter::Saturate, ratio, name)
159
161
  end
160
162
 
@@ -167,7 +169,7 @@ module ColorContrastCalc
167
169
  # code is assigned instead.
168
170
  # @return [Color] New grayscale color
169
171
 
170
- def new_grayscale_color(ratio = 100, name = nil)
172
+ def with_grayscale(ratio = 100, name = nil)
171
173
  generate_new_color(Converter::Grayscale, ratio, name)
172
174
  end
173
175
 
@@ -290,7 +292,7 @@ module ColorContrastCalc
290
292
  # The max contrast in this context means that of colors modified
291
293
  # by the operation defined at
292
294
  # * {https://www.w3.org/TR/filter-effects/#funcdef-contrast}
293
- # @return [Boolean] true if self.new_contrast_color(r) where r is
295
+ # @return [Boolean] true if self.with_contrast(r) where r is
294
296
  # greater than 100 returns the same color as self.
295
297
 
296
298
  def max_contrast?
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ColorContrastCalc
4
+ ##
5
+ # Collection of deprecated methods.
6
+
7
+ module Deprecated
8
+ def self.warn(old_method, new_method)
9
+ STDERR.puts "##{old_method} is deprecated. Use ##{new_method} instead"
10
+ end
11
+
12
+ module Color
13
+ # @deprecated Use {#with_contrast} instead
14
+ def new_contrast_color(ratio, name = nil)
15
+ Deprecated.warn(__method__, :with_contrast)
16
+ with_contrast(ratio, name)
17
+ end
18
+
19
+ # @deprecated Use {#with_brightness} instead
20
+ def new_brightness_color(ratio, name = nil)
21
+ Deprecated.warn(__method__, :with_brightness)
22
+ with_brightness(ratio, name)
23
+ end
24
+
25
+ # @deprecated Use {#with_invert} instead
26
+ def new_invert_color(ratio = 100, name = nil)
27
+ Deprecated.warn(__method__, :with_invert)
28
+ with_invert(ratio, name)
29
+ end
30
+
31
+ # @deprecated Use {#with_hue_rotate} instead
32
+ def new_hue_rotate_color(degree, name = nil)
33
+ Deprecated.warn(__method__, :with_hue_rotate)
34
+ with_hue_rotate(degree, name)
35
+ end
36
+
37
+ # @deprecated Use {#with_saturate} instead
38
+ def new_saturate_color(ratio, name = nil)
39
+ Deprecated.warn(__method__, :with_saturate)
40
+ with_saturate(ratio, name)
41
+ end
42
+
43
+ # @deprecated Use {#with_grayscale} instead
44
+ def new_grayscale_color(ratio = 100, name = nil)
45
+ Deprecated.warn(__method__, :with_grayscale)
46
+ with_grayscale(ratio, name)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -14,7 +14,7 @@ module ColorContrastCalc
14
14
  module Criteria
15
15
  # @private
16
16
 
17
- def self.threshold_criteria(level, fixed_rgb, other_rgb)
17
+ def self.define(level, fixed_rgb, other_rgb)
18
18
  if should_scan_darker_side?(fixed_rgb, other_rgb)
19
19
  return ToDarkerSide.new(level, fixed_rgb)
20
20
  end
@@ -32,16 +32,15 @@ module ColorContrastCalc
32
32
  end
33
33
 
34
34
  class SearchDirection
35
- attr_reader :level, :target_ratio, :fixed_luminance
35
+ attr_reader :target_contrast, :fixed_luminance
36
36
 
37
37
  def initialize(level, fixed_rgb)
38
- @level = level
39
- @target_ratio = Checker.level_to_ratio(level)
38
+ @target_contrast = Checker.level_to_ratio(level)
40
39
  @fixed_luminance = Checker.relative_luminance(fixed_rgb)
41
40
  end
42
41
 
43
42
  def sufficient_contrast?(rgb)
44
- contrast_ratio(rgb) >= @target_ratio
43
+ contrast_ratio(rgb) >= @target_contrast
45
44
  end
46
45
 
47
46
  def contrast_ratio(rgb)
@@ -53,28 +52,28 @@ module ColorContrastCalc
53
52
  class ToDarkerSide < SearchDirection
54
53
  # @private
55
54
 
56
- def round(r)
57
- (r * 10).floor / 10.0
55
+ def round(ratio)
56
+ (ratio * 10).floor / 10.0
58
57
  end
59
58
 
60
59
  # @private
61
60
 
62
61
  def increment_condition(contrast_ratio)
63
- contrast_ratio > @target_ratio
62
+ contrast_ratio > @target_contrast
64
63
  end
65
64
  end
66
65
 
67
66
  class ToBrighterSide < SearchDirection
68
67
  # @private
69
68
 
70
- def round(r)
71
- (r * 10).ceil / 10.0
69
+ def round(ratio)
70
+ (ratio * 10).ceil / 10.0
72
71
  end
73
72
 
74
73
  # @private
75
74
 
76
75
  def increment_condition(contrast_ratio)
77
- @target_ratio > contrast_ratio
76
+ @target_contrast > contrast_ratio
78
77
  end
79
78
  end
80
79
  end
@@ -96,13 +95,55 @@ module ColorContrastCalc
96
95
 
97
96
  # @private
98
97
 
99
- def sufficient_contrast?(fixed_rgb, other_rgb, level)
100
- target_ratio = Checker.level_to_ratio(level)
101
- ratio = Checker.contrast_ratio(fixed_rgb, other_rgb)
102
- ratio >= target_ratio
98
+ def sufficient_contrast?(ref_luminance, rgb, criteria)
99
+ luminance = Checker.relative_luminance(rgb)
100
+ ratio = Checker.luminance_to_contrast_ratio(ref_luminance, luminance)
101
+ ratio >= criteria.target_contrast
103
102
  end
104
103
 
105
104
  private :sufficient_contrast?
105
+
106
+ def rgb_with_better_ratio(color, criteria, last_r, passing_r)
107
+ closest = rgb_with_ratio(color, last_r)
108
+
109
+ if passing_r && !criteria.sufficient_contrast?(closest)
110
+ return rgb_with_ratio(color, passing_r)
111
+ end
112
+
113
+ closest
114
+ end
115
+
116
+ private :rgb_with_better_ratio
117
+
118
+ # @private
119
+
120
+ def find_ratio(other_color, criteria, init_ratio, init_width)
121
+ target_contrast = criteria.target_contrast
122
+ r = init_ratio
123
+ passing_r = nil
124
+
125
+ FinderUtils.binary_search_width(init_width, 0.01) do |d|
126
+ contrast = criteria.contrast_ratio(rgb_with_ratio(other_color, r))
127
+
128
+ passing_r = r if contrast >= target_contrast
129
+ break if contrast == target_contrast
130
+
131
+ r += criteria.increment_condition(contrast) ? d : -d
132
+ end
133
+
134
+ [r, passing_r]
135
+ end
136
+
137
+ private :find_ratio
138
+
139
+ # @private
140
+
141
+ def rgb_with_ratio(rgb, ratio)
142
+ raise(NotImplementedError,
143
+ "Implement ##{__method__} with arguments #{rgb} and #{ratio}")
144
+ end
145
+
146
+ private :rgb_with_ratio
106
147
  end
107
148
 
108
149
  ##
@@ -126,19 +167,27 @@ module ColorContrastCalc
126
167
  # adjusted from that of +other_rgb+
127
168
 
128
169
  def self.find(fixed_rgb, other_rgb, level = Checker::Level::AA)
129
- criteria = Criteria.threshold_criteria(level, fixed_rgb, other_rgb)
170
+ criteria = Criteria.define(level, fixed_rgb, other_rgb)
130
171
  w = calc_upper_ratio_limit(other_rgb) / 2.0
131
172
 
132
173
  upper_rgb = upper_limit_rgb(criteria, other_rgb, w * 2)
133
174
  return upper_rgb if upper_rgb
134
175
 
135
- r, sufficient_r = calc_brightness_ratio(other_rgb, criteria, w)
176
+ last_r, passing_r = find_ratio(other_rgb, criteria, w, w).map do |ratio|
177
+ criteria.round(ratio) if ratio
178
+ end
179
+
180
+ rgb_with_better_ratio(other_rgb, criteria, last_r, passing_r)
181
+ end
136
182
 
137
- generate_satisfying_color(other_rgb, criteria, r, sufficient_r)
183
+ def self.rgb_with_ratio(rgb, ratio)
184
+ Converter::Brightness.calc_rgb(rgb, ratio)
138
185
  end
139
186
 
187
+ private_class_method :rgb_with_ratio
188
+
140
189
  def self.upper_limit_rgb(criteria, other_rgb, max_ratio)
141
- limit_rgb = Converter::Brightness.calc_rgb(other_rgb, max_ratio)
190
+ limit_rgb = rgb_with_ratio(other_rgb, max_ratio)
142
191
  limit_rgb if exceed_upper_limit?(criteria, other_rgb, limit_rgb)
143
192
  end
144
193
 
@@ -152,44 +201,6 @@ module ColorContrastCalc
152
201
 
153
202
  private_class_method :exceed_upper_limit?
154
203
 
155
- def self.calc_brightness_ratio(other_rgb, criteria, w)
156
- target_ratio = criteria.target_ratio
157
- r = w
158
- sufficient_r = nil
159
-
160
- FinderUtils.binary_search_width(w, 0.01) do |d|
161
- contrast_ratio = calc_contrast_ratio(criteria, other_rgb, r)
162
-
163
- sufficient_r = r if contrast_ratio >= target_ratio
164
- break if contrast_ratio == target_ratio
165
-
166
- r += criteria.increment_condition(contrast_ratio) ? d : -d
167
- end
168
-
169
- [r, sufficient_r]
170
- end
171
-
172
- private_class_method :calc_brightness_ratio
173
-
174
- def self.generate_satisfying_color(other_rgb, criteria, r, sufficient_r)
175
- nearest = Converter::Brightness.calc_rgb(other_rgb, criteria.round(r))
176
-
177
- if sufficient_r && !criteria.sufficient_contrast?(nearest)
178
- return Converter::Brightness.calc_rgb(other_rgb,
179
- criteria.round(sufficient_r))
180
- end
181
-
182
- nearest
183
- end
184
-
185
- private_class_method :generate_satisfying_color
186
-
187
- def self.calc_contrast_ratio(criteria, other_rgb, r)
188
- criteria.contrast_ratio(Converter::Brightness.calc_rgb(other_rgb, r))
189
- end
190
-
191
- private_class_method :calc_contrast_ratio
192
-
193
204
  # @private
194
205
 
195
206
  def self.calc_upper_ratio_limit(rgb)
@@ -221,68 +232,49 @@ module ColorContrastCalc
221
232
 
222
233
  def self.find(fixed_rgb, other_rgb, level = Checker::Level::AA)
223
234
  other_hsl = Utils.rgb_to_hsl(other_rgb)
224
- criteria = Criteria.threshold_criteria(level, fixed_rgb, other_rgb)
235
+ criteria = Criteria.define(level, fixed_rgb, other_rgb)
225
236
  max, min = determine_minmax(fixed_rgb, other_rgb, other_hsl[2])
226
237
 
227
- boundary_color = lightness_boundary_color(fixed_rgb, max, min, level)
228
- return boundary_color if boundary_color
238
+ boundary_rgb = lightness_boundary_rgb(fixed_rgb, max, min, criteria)
239
+ return boundary_rgb if boundary_rgb
229
240
 
230
- l, sufficient_l = calc_lightness_ratio(other_hsl, criteria, max, min)
241
+ last_l, passing_l = find_ratio(other_hsl, criteria,
242
+ (max + min) / 2.0, max - min)
231
243
 
232
- generate_satisfying_color(other_hsl, criteria, l, sufficient_l)
244
+ rgb_with_better_ratio(other_hsl, criteria, last_l, passing_l)
233
245
  end
234
246
 
235
- def self.determine_minmax(fixed_rgb, other_rgb, init_l)
236
- scan_darker_side = Criteria.should_scan_darker_side?(fixed_rgb,
237
- other_rgb)
238
- scan_darker_side ? [init_l, 0] : [100, init_l] # [max, min]
239
- end
240
-
241
- private_class_method :determine_minmax
242
-
243
- def self.lightness_boundary_color(rgb, max, min, level)
244
- if min.zero? && !sufficient_contrast?(Rgb::BLACK, rgb, level)
245
- return Rgb::BLACK
247
+ def self.rgb_with_ratio(hsl, ratio)
248
+ if hsl[2] != ratio
249
+ hsl = hsl.dup
250
+ hsl[2] = ratio
246
251
  end
247
252
 
248
- if max == 100 && !sufficient_contrast?(Rgb::WHITE, rgb, level)
249
- return Rgb::WHITE
250
- end
253
+ Utils.hsl_to_rgb(hsl)
251
254
  end
252
255
 
253
- private_class_method :lightness_boundary_color
256
+ private_class_method :rgb_with_ratio
254
257
 
255
- def self.calc_lightness_ratio(other_hsl, criteria, max, min)
256
- h, s, = other_hsl
257
- l = (max + min) / 2.0
258
- sufficient_l = nil
259
-
260
- FinderUtils.binary_search_width(max - min, 0.01) do |d|
261
- contrast_ratio = criteria.contrast_ratio(Utils.hsl_to_rgb([h, s, l]))
262
-
263
- sufficient_l = l if contrast_ratio >= criteria.target_ratio
264
- break if contrast_ratio == criteria.target_ratio
265
-
266
- l += criteria.increment_condition(contrast_ratio) ? d : -d
267
- end
268
-
269
- [l, sufficient_l]
258
+ def self.determine_minmax(fixed_rgb, other_rgb, init_l)
259
+ on_darker_side = Criteria.should_scan_darker_side?(fixed_rgb, other_rgb)
260
+ on_darker_side ? [init_l, 0] : [100, init_l] # [max, min]
270
261
  end
271
262
 
272
- private_class_method :calc_lightness_ratio
273
-
274
- def self.generate_satisfying_color(other_hsl, criteria, l, sufficient_l)
275
- h, s, = other_hsl
276
- nearest = Utils.hsl_to_rgb([h, s, l])
263
+ private_class_method :determine_minmax
277
264
 
278
- if sufficient_l && !criteria.sufficient_contrast?(nearest)
279
- return Utils.hsl_to_rgb([h, s, sufficient_l])
265
+ def self.lightness_boundary_rgb(rgb, max, min, criteria)
266
+ if min.zero? && !sufficient_contrast?(Checker::Luminance::BLACK,
267
+ rgb, criteria)
268
+ return Rgb::BLACK
280
269
  end
281
270
 
282
- nearest
271
+ if max == 100 && !sufficient_contrast?(Checker::Luminance::WHITE,
272
+ rgb, criteria)
273
+ return Rgb::WHITE
274
+ end
283
275
  end
284
276
 
285
- private_class_method :generate_satisfying_color
277
+ private_class_method :lightness_boundary_rgb
286
278
  end
287
279
  end
288
280
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ColorContrastCalc
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: color_contrast_calc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - HASHIMOTO, Naoki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-20 00:00:00.000000000 Z
11
+ date: 2018-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -114,6 +114,7 @@ files:
114
114
  - lib/color_contrast_calc/color.rb
115
115
  - lib/color_contrast_calc/converter.rb
116
116
  - lib/color_contrast_calc/data/color_keywords.json
117
+ - lib/color_contrast_calc/deprecated.rb
117
118
  - lib/color_contrast_calc/shim.rb
118
119
  - lib/color_contrast_calc/sorter.rb
119
120
  - lib/color_contrast_calc/threshold_finder.rb