color_corrector 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e949065e4e833524707fd819b09f952e84805dba
4
+ data.tar.gz: 5db4477bf399c53f7459fd04df3a76f9710a7057
5
+ SHA512:
6
+ metadata.gz: 17221c17cecbca24f7707d81d575d8afdb396b8f41cb87a7ef6ca864aec62a720e996201d8183e376275c21c710f139c096a60aad1ef31586cba613218ffeb22
7
+ data.tar.gz: 8c8edb9e234148281c9100828b7017b777f2cbd61573a24330612373d521063c710e18dd981bd9c202a8a33dc2c880929517e956de2c47dd960244b737b51419
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'color_corrector/color'
4
+ require 'color_corrector/version'
5
+
6
+ # # Color Corrector
7
+ module ColorCorrector
8
+ ONE_SIXTH = 1 / 6.0
9
+ ONE_THIRD = 1 / 3.0
10
+ TWO_THIRDS = 2 / 3.0
11
+
12
+ class << self
13
+ def make_readable(background, foreground, level = 'AA', size = 'large')
14
+ until readable?(background, foreground, level, size)
15
+ background = if light?(foreground)
16
+ darken(background)
17
+ else
18
+ lighten(background)
19
+ end
20
+ end
21
+ background
22
+ end
23
+
24
+ def readable?(background, foreground, level = 'AA', size = 'large')
25
+ ratio = contrast_ratio(background, foreground)
26
+ contrast = case [level, size]
27
+ when %w[AA large]
28
+ 3
29
+ when %w[AAA small]
30
+ 7
31
+ else # %w[AA small], %w[AAA large]
32
+ 4.5
33
+ end
34
+ ratio >= contrast
35
+ end
36
+
37
+ def contrast_ratio(background, foreground)
38
+ background = background.delete('#')
39
+ foreground = foreground.delete('#')
40
+ ratio(background, foreground)
41
+ end
42
+
43
+ def darken(hex_color, amount = 0.01)
44
+ red, green, blue = hex_color.delete('#').scan(/../).map(&:hex)
45
+ hue, saturation, lightness = rgb_to_hsl(red, blue, green)
46
+ lightness -= amount
47
+ rgb = hsl_to_rgb(hue, saturation, lightness)
48
+ format '#%02x%02x%02x', *rgb
49
+ end
50
+
51
+ def lighten(hex_color, amount = 0.01)
52
+ red, green, blue = hex_color.delete('#').scan(/../).map(&:hex)
53
+ hue, saturation, lightness = rgb_to_hsl(red, blue, green)
54
+ lightness += amount
55
+ rgb = hsl_to_rgb(hue, saturation, lightness)
56
+ format '#%02x%02x%02x', *rgb
57
+ end
58
+
59
+ def rgb_to_hsl(red, blue, green)
60
+ components = [red, blue, green].map { |chroma| chroma / 255.0 }
61
+ max = components.max
62
+ min = components.min
63
+ red, blue, green = components
64
+ lightness = (max + min) / 2
65
+
66
+ if max == min
67
+ hue = saturation = 0
68
+ else
69
+ chroma = max - min
70
+ saturation =
71
+ lightness > 0.5 ? (chroma / (2 - max - min)) : (chroma / (max + min))
72
+ hue = case max
73
+ when red
74
+ (green - blue) / chroma + (green < blue ? 6 : 0)
75
+ when green
76
+ (blue - red) / chroma + 2
77
+ when blue
78
+ (red - green) / chroma + 4
79
+ end
80
+ hue /= 6
81
+ end
82
+ [hue, saturation, lightness]
83
+ end
84
+
85
+ # Variables papa, quebec and tango were originally designated by single
86
+ # letters but have been changed to match the NATO photonetic alphabet
87
+ def hsl_to_rgb(hue, saturation, lightness)
88
+ if saturation.zero?
89
+ red = green = blue = lightness
90
+ else
91
+ quebec = if lightness < 0.5
92
+ lightness * (1 + saturation)
93
+ else
94
+ lightness + saturation - lightness * saturation
95
+ end
96
+ papa = 2 * lightness - quebec
97
+ red, green, blue = [ONE_THIRD, 0, -ONE_THIRD].map do |offset|
98
+ hue_to_rgb papa, quebec, hue + offset
99
+ end
100
+ end
101
+ [red, green, blue].map { |component| (component * 255).round }
102
+ end
103
+
104
+ # :reek:FeatureEnvy
105
+ def hue_to_rgb(papa, quebec, tango)
106
+ tango = bound_hue_01(tango)
107
+ if tango < ONE_SIXTH
108
+ papa + (quebec - papa) * 6 * tango
109
+ elsif tango < 0.5 # (1 / 2.0)
110
+ quebec
111
+ elsif tango < TWO_THIRDS
112
+ papa + (quebec - papa) * (TWO_THIRDS - tango) * 6
113
+ else
114
+ papa
115
+ end
116
+ end
117
+
118
+ def bound_hue_01(hue)
119
+ if hue.negative?
120
+ hue + 1
121
+ elsif hue > 1
122
+ hue - 1
123
+ else
124
+ hue
125
+ end
126
+ end
127
+
128
+ # :reek:UncommunicativeVariableName and :reek:UncommunicativeParameterName
129
+ def ratio(rgb1, rgb2)
130
+ c1, c2 = [rgb1, rgb2].map { |rgb| Color.new(rgb) }
131
+ l1, l2 = [c1, c2].map(&:relative_luminance).sort
132
+ (l2 + 0.05) / (l1 + 0.05)
133
+ end
134
+
135
+ def relative_luminance(red, green, blue)
136
+ (0.2126 * f(red)) + (0.7152 * f(green)) + (0.0722 * f(blue))
137
+ end
138
+
139
+ # :reek:UncommunicativeVariableName and :reek:UncommunicativeMethodName
140
+ def f(component)
141
+ c = component / 255.0
142
+ c <= 0.03928 ? c : ((c + 0.055) / 1.055)**2.4
143
+ end
144
+
145
+ def light?(hex)
146
+ red, green, blue = hex.delete('#').scan(/../).map(&:hex)
147
+ luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue
148
+ luma >= 128
149
+ end
150
+
151
+ def dark?(hex)
152
+ red, green, blue = hex.delete('#').scan(/../).map(&:hex)
153
+ luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue
154
+ luma < 128
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Color Class
4
+ class Color
5
+ attr_reader :r, :g, :b
6
+
7
+ def initialize(*args)
8
+ input = args.size == 1 ? args[0] : args
9
+ @r, @g, @b = Color.rgb_from(input)
10
+ end
11
+
12
+ def relative_luminance
13
+ ColorCorrector.relative_luminance(r, g, b)
14
+ end
15
+
16
+ def ratio(color)
17
+ ColorCorrector.ratio(self, color)
18
+ end
19
+
20
+ def to_rgb
21
+ [r, g, b]
22
+ end
23
+
24
+ class << self
25
+ def rgb_from(input)
26
+ case input
27
+ when String # hex code
28
+ raise 'Invalid hex code' unless input =~ /\A([0-9a-f]{3}){1,2}\z/i
29
+ rgb = if input.size == 3
30
+ input.split('').map { |h| h * 2 }
31
+ else
32
+ input.scan(/.{2}/)
33
+ end
34
+ rgb.map { |h| h.hex.to_i }
35
+ when Array # array of numbers
36
+ raise 'Invalid RGB array' unless input.size == 3
37
+ input.map(&:to_f)
38
+ when Hash
39
+ rgb = input.values_at(:r, :g, :b)
40
+ raise 'Invalid RGB hash' unless rgb.size == 3
41
+ rgb.map(&:to_f)
42
+ else
43
+ methods = [:r,:g, :b]
44
+ if methods.select { |m| input.respond_to?(m) }.size == methods.size
45
+ methods.map { |m| input.send(m) }
46
+ else
47
+ raise 'Invalid input'
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ColorCorrecter
4
+ # gem version
5
+ VERSION = '0.0.1'
6
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: color_corrector
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Lukas Barry
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-08-30 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: lukasbbarry@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/color_corrector.rb
20
+ - lib/color_corrector/color.rb
21
+ - lib/color_corrector/version.rb
22
+ homepage: https://github.com/LukasBarry/color_corrector
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 2.5.2
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: Color Corrector allows you to revise your colors to be WCAG compliant
46
+ test_files: []