inker 0.1.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 +7 -0
- data/.gitignore +57 -0
- data/.rspec +3 -0
- data/.travis.yml +25 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +142 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/inker.gemspec +34 -0
- data/lib/data/colors.yml +163 -0
- data/lib/inker.rb +30 -0
- data/lib/inker/color.rb +152 -0
- data/lib/inker/color/serializers.rb +69 -0
- data/lib/inker/color/tools.rb +389 -0
- data/lib/inker/version.rb +4 -0
- data/lib/inker/wrappers/string.rb +13 -0
- metadata +119 -0
data/lib/inker.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "digest"
|
3
|
+
require "inker/version"
|
4
|
+
require "inker/color"
|
5
|
+
require "inker/wrappers/string"
|
6
|
+
|
7
|
+
|
8
|
+
# The main module of the gem, which loads all necessary classes and
|
9
|
+
# provides a helper for {Inker::Color} object generation from a string and
|
10
|
+
# for named colors map.
|
11
|
+
module Inker
|
12
|
+
class << self
|
13
|
+
# Creates a new instance of {Inker::Color}, which could be used for color
|
14
|
+
# manipulation or for collecting color info.
|
15
|
+
#
|
16
|
+
# @param str [String] the string to transform into {Inker::Color}
|
17
|
+
# @return [Inker::Color]
|
18
|
+
def color(str)
|
19
|
+
Color.new(str)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# Returns the map of named colors and their respective HEX representation.
|
24
|
+
#
|
25
|
+
# @return [Hash] a map of named colors and their HEX color
|
26
|
+
def named_colors
|
27
|
+
@named_colors ||= YAML.load_file(File.expand_path('../data/colors.yml', __FILE__))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/inker/color.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
require_relative 'color/tools'
|
2
|
+
require_relative 'color/serializers'
|
3
|
+
|
4
|
+
module Inker
|
5
|
+
# This class is used to represent a color in Ruby as an object. It allows
|
6
|
+
# to create a new instance of {Inker::Color} from a string which represents a color.
|
7
|
+
# It also allows to obtain more info about the color and convert color to a different
|
8
|
+
# format.
|
9
|
+
class Color
|
10
|
+
extend Tools
|
11
|
+
include Serializers
|
12
|
+
|
13
|
+
attr_reader :red, :green, :blue, :alpha
|
14
|
+
|
15
|
+
# Create a new {Inker::Color} object from a color string.
|
16
|
+
#
|
17
|
+
# @param color_str [String] a color string
|
18
|
+
def initialize(color_str)
|
19
|
+
@input = color_str.to_s.downcase.gsub(/\s+/, "")
|
20
|
+
|
21
|
+
Color.parse_color(@input).tap do |color|
|
22
|
+
@red = color[:red]
|
23
|
+
@green = color[:green]
|
24
|
+
@blue = color[:blue]
|
25
|
+
@alpha = color[:alpha]
|
26
|
+
end
|
27
|
+
|
28
|
+
validate_color!
|
29
|
+
end
|
30
|
+
|
31
|
+
def ==(color)
|
32
|
+
self.red == color.red and
|
33
|
+
self.green == color.green and
|
34
|
+
self.blue == color.blue and
|
35
|
+
self.alpha == color.alpha
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# Set the value of red component.
|
40
|
+
#
|
41
|
+
# @param value [Integer] the value of red component [0-255]
|
42
|
+
def red=(value)
|
43
|
+
raise ArgumentError.new("Invalid value: #{value.inspect}") if value.to_i < 0 or value.to_i > 255
|
44
|
+
@red = value
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# Set the value of green component.
|
49
|
+
#
|
50
|
+
# @param value [Integer] the value of green component [0-255]
|
51
|
+
def green=(value)
|
52
|
+
raise ArgumentError.new("Invalid value: #{value.inspect}") if value.to_i < 0 or value.to_i > 255
|
53
|
+
@green = value
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# Set the value of blue component.
|
58
|
+
#
|
59
|
+
# @param value [Integer] the value of blue component [0-255]
|
60
|
+
def blue=(value)
|
61
|
+
raise ArgumentError.new("Invalid value: #{value.inspect}") if value.to_i < 0 or value.to_i > 255
|
62
|
+
@blue = value
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Set the value of alpha component.
|
67
|
+
#
|
68
|
+
# @param value [Float] the value of alpha component [0.0-1.0]
|
69
|
+
def alpha=(value)
|
70
|
+
raise ArgumentError.new("Invalid value: #{value.inspect}") if value.to_f < 0 or value.to_f > 1
|
71
|
+
@alpha = value
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# Calculate the brightness of a color.
|
76
|
+
#
|
77
|
+
# @return [Integer] a value between 0-255 which indicates the brightness of the color
|
78
|
+
def brightness
|
79
|
+
Color.brightness(@red, @green, @blue)
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
# Calculate the lightness of a color.
|
84
|
+
#
|
85
|
+
# @return [Float] a value between 0.0-1.0 which indicates the lightness of the color
|
86
|
+
def lightness
|
87
|
+
Color.lightness(@red, @green, @blue)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
# Calculate the saturation of a color.
|
92
|
+
#
|
93
|
+
# @return [Float] a value between 0.0-1.0 which indicates the saturation of the color
|
94
|
+
def saturation
|
95
|
+
Color.saturation(@red, @green, @blue)
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# Calculate the HUE of a color.
|
100
|
+
#
|
101
|
+
# @return [Integer] a value between 0-360 which indicates the HUE of the color
|
102
|
+
def hue
|
103
|
+
Color.hue(@red, @green, @blue)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns a boolean which indicates if the color is dark.
|
107
|
+
#
|
108
|
+
# @return [Boolean] `true` when color is dark
|
109
|
+
def dark?
|
110
|
+
brightness < 128
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns a boolean which indicates if the color is light.
|
114
|
+
# @return [Boolean] `true` when color is light
|
115
|
+
def light?
|
116
|
+
!dark?
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
# Convert color to string in the specified format.
|
121
|
+
#
|
122
|
+
# @param format [String] indicates the format to which to output the color (default: `hex`)
|
123
|
+
#
|
124
|
+
# @return [String] a string representation of the color
|
125
|
+
def to_s(format = 'hex')
|
126
|
+
case format.to_s.strip.downcase
|
127
|
+
when 'hex6' then self.hex6
|
128
|
+
when 'rgb' then self.rgb
|
129
|
+
when 'rgba' then self.rgba
|
130
|
+
when 'hsl' then self.hsl
|
131
|
+
when 'hsla' then self.hsla
|
132
|
+
else
|
133
|
+
self.hex
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
# Validates the values of the color.
|
140
|
+
def validate_color!
|
141
|
+
invalid = (@red < 0 or @red > 255)
|
142
|
+
invalid ||= (@green < 0 or @green > 255)
|
143
|
+
invalid ||= (@blue < 0 or @blue > 255)
|
144
|
+
invalid ||= (@alpha < 0 or @alpha > 1)
|
145
|
+
|
146
|
+
if invalid
|
147
|
+
raise ArgumentError.new "Invalid color: #{@input.inspect} " \
|
148
|
+
"(R: #{@red.inspect}, G: #{@green.inspect}, B: #{@blue.inspect}, A: #{@alpha.inspect})"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Inker
|
2
|
+
class Color
|
3
|
+
# This module implements the methods which can be used to serialize
|
4
|
+
# a color as string in different formats.
|
5
|
+
module Serializers
|
6
|
+
|
7
|
+
# Convert color to a HEX color string.
|
8
|
+
#
|
9
|
+
# @param force_alpha [Boolean] indicates if alpha channel should be included in HEX color string
|
10
|
+
# when alpha component wasn't specified
|
11
|
+
# @return [String] a HEX color string
|
12
|
+
def hex(force_alpha: false)
|
13
|
+
result = hex6
|
14
|
+
result += (alpha * 255).to_i.to_s(16).rjust(2, "0") if alpha < 1 or force_alpha
|
15
|
+
|
16
|
+
return result
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Convert color to a HEX color string without alpha channel.
|
21
|
+
#
|
22
|
+
# @return [String] a HEX color string without alpha channel
|
23
|
+
def hex6
|
24
|
+
result = "#"
|
25
|
+
result += red.to_s(16).rjust(2, "0")
|
26
|
+
result += green.to_s(16).rjust(2, "0")
|
27
|
+
result += blue.to_s(16).rjust(2, "0")
|
28
|
+
|
29
|
+
return result
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# Convert color to RGB color string.
|
34
|
+
#
|
35
|
+
# @return [String] a RGB color string
|
36
|
+
def rgb
|
37
|
+
return "rgb(#{red}, #{green}, #{blue})"
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Convert color to RGBA color string.
|
42
|
+
#
|
43
|
+
# @param alpha_precision [Integer] indicates the precision of alpha value
|
44
|
+
#
|
45
|
+
# @return [String] a RGBA color string
|
46
|
+
def rgba(alpha_precision: 2)
|
47
|
+
return "rgba(#{red}, #{green}, #{blue}, #{alpha.round(alpha_precision)})"
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
# Convert color to HSL color string.
|
52
|
+
#
|
53
|
+
# @return [String] a HSL color string
|
54
|
+
def hsl
|
55
|
+
return "hsl(#{hue}, #{(saturation * 100).round}%, #{(lightness * 100).round}%)"
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# Convert color to HSL color string.
|
60
|
+
#
|
61
|
+
# @param alpha_precision [Integer] indicates the precision of alpha value
|
62
|
+
#
|
63
|
+
# @return [String] a HSL color string
|
64
|
+
def hsla(alpha_precision: 2)
|
65
|
+
return "hsl(#{hue}, #{(saturation * 100).round}%, #{(lightness * 100).round}%, #{alpha.round(alpha_precision)})"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,389 @@
|
|
1
|
+
module Inker
|
2
|
+
class Color
|
3
|
+
# Tools module implements a set of methods useful for color parsing and
|
4
|
+
# for getting useful info about color.
|
5
|
+
module Tools
|
6
|
+
# Regular expression for HEX colors matching
|
7
|
+
HEX_REGEX = /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/
|
8
|
+
|
9
|
+
# Regular expression for RGB colors matching
|
10
|
+
RGB_REGEX = /^rgb\((\d+,){2}\d+\)$/
|
11
|
+
|
12
|
+
# Regular expression for RGBA colors matching
|
13
|
+
RGBA_REGEX = /^rgba\((\d+,){3}\d+(.\d+)?\)$/
|
14
|
+
|
15
|
+
# Regular expression for HSL colors matching
|
16
|
+
HSL_REGEX = /^hsl\((\d+)(,\d+%|,\d+(.\d+)){2}\)$/
|
17
|
+
|
18
|
+
# Regular expression for HSLA colors matching
|
19
|
+
HSLA_REGEX = /^hsla\((\d+)(,\d+%|,\d+(.\d+)){2},\d+(.\d+)?\)$/
|
20
|
+
|
21
|
+
# Calculate the brightness of a color.
|
22
|
+
#
|
23
|
+
# @param red [Integer] the value of red component [0-255]
|
24
|
+
# @param green [Integer] the value of green component [0-255]
|
25
|
+
# @param blue [Integer] the value of blue component [0-255]
|
26
|
+
#
|
27
|
+
# @return [Integer] a value between 0-255 which indicates the brightness of the color
|
28
|
+
def brightness(red, green, blue)
|
29
|
+
Math.sqrt(0.299 * red**2 + 0.587 * green**2 + 0.114 * blue**2).round
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# Calculate the lightness of a color from RGB components.
|
34
|
+
#
|
35
|
+
# @param red [Integer] the value of red component [0-255]
|
36
|
+
# @param green [Integer] the value of green component [0-255]
|
37
|
+
# @param blue [Integer] the value of blue component [0-255]
|
38
|
+
#
|
39
|
+
# @return [Float] a value in range 0.0-1.0 which indicates the ligthness of the color
|
40
|
+
def lightness(red, green, blue)
|
41
|
+
min, max = [red, green, blue].minmax
|
42
|
+
|
43
|
+
return (min + max) / (2.0 * 255)
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# Calculate the saturation of a color from RGB components.
|
48
|
+
#
|
49
|
+
# @param red [Integer] the value of red component [0-255]
|
50
|
+
# @param green [Integer] the value of green component [0-255]
|
51
|
+
# @param blue [Integer] the value of blue component [0-255]
|
52
|
+
#
|
53
|
+
# @return [Float] a value in range 0.0-1.0 which indicates the saturation of the color
|
54
|
+
def saturation(red, green, blue)
|
55
|
+
# return 0 for black and white colors
|
56
|
+
return 0 if red == green and red == blue and (red == 0 or red == 255)
|
57
|
+
|
58
|
+
lightness = lightness(red, green, blue)
|
59
|
+
min, max = [red / 255.0, green / 255.0, blue / 255.0].minmax
|
60
|
+
|
61
|
+
return lightness < 0.5 ? (max - min) / (max + min) : (max - min) / (2.0 - max - min)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
# Calculate the HUE value of a color from RGB components.
|
66
|
+
#
|
67
|
+
# @param red [Integer] the value of red component [0-255]
|
68
|
+
# @param green [Integer] the value of green component [0-255]
|
69
|
+
# @param blue [Integer] the value of blue component [0-255]
|
70
|
+
#
|
71
|
+
# @return [Integer] a value in range 0-360 which indicates the HUE value of the color
|
72
|
+
def hue(red, green, blue)
|
73
|
+
min, max = [red, green, blue].minmax
|
74
|
+
|
75
|
+
numerator = (max - min).to_f
|
76
|
+
return 0 if numerator == 0
|
77
|
+
|
78
|
+
hue = (red == max) ? (green - blue) / numerator :
|
79
|
+
(green == max) ? 2 + (blue - red) / numerator :
|
80
|
+
4 + (red - green) / numerator
|
81
|
+
hue = hue * 60
|
82
|
+
return (hue < 0 ? hue + 360 : hue).round
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# Get RGB values from a color in HSL format.
|
87
|
+
#
|
88
|
+
# @param hue [Integer] the value of HUE component [0-360]
|
89
|
+
# @param saturation [Float] the saturation of the color [0.0-1.0]
|
90
|
+
# @param lightness [Float] the lightness of the color [0.0-1.0]
|
91
|
+
#
|
92
|
+
# @return [Hash] a `Hash` which contains the values of RGB components
|
93
|
+
def hsl_to_rgb(hue, saturation, lightness)
|
94
|
+
result = nil
|
95
|
+
if saturation == 0
|
96
|
+
# There's no saturation, so it's a gray scale color, which
|
97
|
+
# depends only on brightness
|
98
|
+
brightness = (lightness * 255).round
|
99
|
+
|
100
|
+
# All RGB components are equal to brightness
|
101
|
+
result = {
|
102
|
+
red: brightness,
|
103
|
+
green: brightness,
|
104
|
+
blue: brightness
|
105
|
+
}
|
106
|
+
else
|
107
|
+
q = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation
|
108
|
+
p = 2 * lightness - q
|
109
|
+
norm_hue = hue / 360.0
|
110
|
+
|
111
|
+
result = {
|
112
|
+
red: (hue_to_rgb(p, q, norm_hue + 1.0/3.0) * 255).round,
|
113
|
+
green: (hue_to_rgb(p, q, norm_hue) * 255).round,
|
114
|
+
blue: (hue_to_rgb(p, q, norm_hue - 1.0/3.0) * 255).round
|
115
|
+
}
|
116
|
+
end
|
117
|
+
|
118
|
+
return result
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
# Returns a `Boolean` which indicates if color is in HEX format
|
123
|
+
#
|
124
|
+
# @param color_str [String] a color string
|
125
|
+
#
|
126
|
+
# @return [Boolean] `true` when color is in HEX format
|
127
|
+
def is_hex?(color_str)
|
128
|
+
!!(color_str.to_s.downcase.strip =~ HEX_REGEX)
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
# Returns a `Boolean` which indicates if color is in RGB format
|
133
|
+
#
|
134
|
+
# @param color_str [String] a color string
|
135
|
+
#
|
136
|
+
# @return [Boolean] `true` when color is in RGB format
|
137
|
+
def is_rgb?(color_str)
|
138
|
+
!!(color_str.to_s.downcase.gsub(/\s+/, '') =~ RGB_REGEX)
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
# Returns a `Boolean` which indicates if color is in RGBA format
|
143
|
+
#
|
144
|
+
# @param color_str [String] a color string
|
145
|
+
#
|
146
|
+
# @return [Boolean] `true` when color is in RGBA format
|
147
|
+
def is_rgba?(color_str)
|
148
|
+
!!(color_str.to_s.downcase.gsub(/\s+/, '') =~ RGBA_REGEX)
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
# Returns a `Boolean` which indicates if color is in HSL format
|
153
|
+
#
|
154
|
+
# @param color_str [String] a color string
|
155
|
+
#
|
156
|
+
# @return [Boolean] `true` when color is in HSL format
|
157
|
+
def is_hsl?(color_str)
|
158
|
+
!!(color_str.to_s.downcase.gsub(/\s+/, '') =~ HSL_REGEX)
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
# Returns a `Boolean` which indicates if color is in HSLA format
|
163
|
+
#
|
164
|
+
# @param color_str [String] a color string
|
165
|
+
#
|
166
|
+
# @return [Boolean] `true` when color is in HSLA format
|
167
|
+
def is_hsla?(color_str)
|
168
|
+
!!(color_str.to_s.downcase.gsub(/\s+/, '') =~ HSLA_REGEX)
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
# Generate a random `Inker::Color`.
|
173
|
+
#
|
174
|
+
# @param with_alpha [Boolean] when `true` include alpha channel
|
175
|
+
#
|
176
|
+
# @return [Inker::Color] a random color
|
177
|
+
def random(with_alpha: false)
|
178
|
+
prefix = with_alpha ? "rgba" : "rgb"
|
179
|
+
values = (1..3).map{ (rand * 255).round }
|
180
|
+
values << rand.round(2) if with_alpha
|
181
|
+
|
182
|
+
Inker.color("#{prefix}(#{values.join(",")})")
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
# A helper for `Inker::Color` generation from RGB components.
|
187
|
+
#
|
188
|
+
# @param red [Integer] the value of red component [0-255]
|
189
|
+
# @param green [Integer] the value of green component [0-255]
|
190
|
+
# @param blue [Integer] the value of blue component [0-255]
|
191
|
+
#
|
192
|
+
# @return [Inker::Color] a `Inker::Color` generated from passed RGB values
|
193
|
+
def from_rgb(red, green, blue)
|
194
|
+
Inker.color("rgb(#{red}, #{green}, #{blue})")
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
# A helper for `Inker::Color` generation from RGBA components.
|
199
|
+
#
|
200
|
+
# @param red [Integer] the value of red component [0-255]
|
201
|
+
# @param green [Integer] the value of green component [0-255]
|
202
|
+
# @param blue [Integer] the value of blue component [0-255]
|
203
|
+
# @param alpha [Float] the value of alpha component [0.0-1.1]
|
204
|
+
#
|
205
|
+
# @return [Inker::Color] a `Inker::Color` generated from passed RGBA values
|
206
|
+
def from_rgba(red, green, blue, alpha)
|
207
|
+
Inker.color("rgba(#{red}, #{green}, #{blue}, #{alpha})")
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
# A helper for `Inker::Color` generation from HSL components.
|
212
|
+
#
|
213
|
+
# @param hue [Integer] the value of HUE component [0-360]
|
214
|
+
# @param saturation [Float] the value of saturation component [0.0-1.0]
|
215
|
+
# @param lightness [Float] the value of lightness component [0.0-1.0]
|
216
|
+
#
|
217
|
+
# @return [Inker::Color] a `Inker::Color` generated from passed HSL values
|
218
|
+
def from_hsl(hue, saturation, lightness)
|
219
|
+
Inker.color("hsl(#{hue}, #{saturation}, #{lightness})")
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
# A helper for `Inker::Color` generation from HSLA components.
|
224
|
+
#
|
225
|
+
# @param hue [Integer] the value of HUE component [0-360]
|
226
|
+
# @param saturation [Float] the value of saturation component [0.0-1.0]
|
227
|
+
# @param lightness [Float] the value of lightness component [0.0-1.0]
|
228
|
+
# @param alpha [Float] the value of alpha component [0.0-1.1]
|
229
|
+
#
|
230
|
+
# @return [Inker::Color] a `Inker::Color` generated from passed HSLA values
|
231
|
+
def from_hsla(hue, saturation, lightness, alpha)
|
232
|
+
Inker.color("hsla(#{hue}, #{saturation}, #{lightness}, #{alpha})")
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
# Use MD5 digest of the string to get hex values from specified positions (by default `[0, 29, 14, 30, 28, 31]`)
|
237
|
+
# in order to obtain a color in HEX format which represents the specified string.
|
238
|
+
#
|
239
|
+
# @params custom_string [String] a string from which to generate a color
|
240
|
+
# @params positions [Array] an array of 6 numbers in range 0-31 which indicates the position
|
241
|
+
# of hex value to get in order to obtain a 6 chars hex string, which will be the result color
|
242
|
+
#
|
243
|
+
# @return [Inker::Color] a `Inker::Color` object which represents the color associated to input string
|
244
|
+
def from_custom_string(custom_string, positions: [0, 29, 14, 30, 28, 31])
|
245
|
+
digest = Digest::MD5.hexdigest(custom_string.to_s)
|
246
|
+
Inker.color("##{positions.map{|p| digest[p]}.join}")
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
# Parse a color string an return it's RGBA components as a hash.
|
251
|
+
#
|
252
|
+
# @example
|
253
|
+
# Inker::Color.parse_color("#FF005544") # returns {:red=>255, :green=>0, :blue=>85, :alpha=>0.4}
|
254
|
+
#
|
255
|
+
# @param color_str [String] color string to parse
|
256
|
+
# @return [Hash] a hash which contains RGBA components of parsed color
|
257
|
+
def parse_color(color_str)
|
258
|
+
# Normalize input string by stripping white spaces and converting
|
259
|
+
# string to downcase
|
260
|
+
input = color_str.to_s.strip.downcase
|
261
|
+
|
262
|
+
# By default result is nil
|
263
|
+
result = nil
|
264
|
+
|
265
|
+
# Try to guess the format of color string and parse it by
|
266
|
+
# using the apropriate algorithm
|
267
|
+
if is_hex?(input)
|
268
|
+
# Parse the string as HEX color
|
269
|
+
result = parse_hex(input)
|
270
|
+
elsif is_rgb?(input)
|
271
|
+
# Parse the string as RGB color
|
272
|
+
result = parse_rgb(input)
|
273
|
+
elsif is_rgba?(input)
|
274
|
+
# Parse the string as RGBA color
|
275
|
+
result = parse_rgb(input, is_rgba: true)
|
276
|
+
elsif is_hsl?(input)
|
277
|
+
# Parse the string as HSL color
|
278
|
+
result = parse_hsl(input)
|
279
|
+
elsif is_hsla?(input)
|
280
|
+
# Parse the string as HSLA color
|
281
|
+
result = parse_hsl(input, is_hsla: true)
|
282
|
+
else
|
283
|
+
# Check if color is in "named color" format
|
284
|
+
named_color = Inker.named_colors[input]
|
285
|
+
if named_color
|
286
|
+
# If a named color has been matched, use it's HEX value and
|
287
|
+
# parse it as HEX color
|
288
|
+
result = parse_hex(named_color)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# If we didn't have any match, throw an ArgumentError error
|
293
|
+
raise ArgumentError.new("Unknown color format: #{color_str.to_s.strip.inspect}") if result.nil?
|
294
|
+
|
295
|
+
return result
|
296
|
+
end
|
297
|
+
|
298
|
+
private
|
299
|
+
|
300
|
+
# Parse a color string as HEX color.
|
301
|
+
#
|
302
|
+
# @param color_str [String] input color string
|
303
|
+
#
|
304
|
+
# @return [Hash] a `Hash` which contains RGBA components of parsed color
|
305
|
+
def parse_hex(color_str)
|
306
|
+
# Remove the leading '#' character from input color string
|
307
|
+
input = color_str.gsub(/^#/, '')
|
308
|
+
|
309
|
+
# Convert to HEX6 when color is in HEX3 format
|
310
|
+
input = input.chars.map{|x| x * 2 }.join if input.length == 3
|
311
|
+
|
312
|
+
# Get RGB components
|
313
|
+
result = {
|
314
|
+
red: Integer(input[0..1], 16),
|
315
|
+
green: Integer(input[2..3], 16),
|
316
|
+
blue: Integer(input[4..5], 16),
|
317
|
+
alpha: 1.0
|
318
|
+
}
|
319
|
+
|
320
|
+
# When color is in HEX8 format, get also alpha channel value
|
321
|
+
if input.length == 8
|
322
|
+
result[:alpha] = Integer(input[6..7], 16) / 255.0
|
323
|
+
end
|
324
|
+
|
325
|
+
return result
|
326
|
+
end
|
327
|
+
|
328
|
+
|
329
|
+
# Parse color string as RGB(A) color.
|
330
|
+
#
|
331
|
+
# @param color_str [String] input RGB(A) color string
|
332
|
+
# @param is_rgba [Boolean] indicates if color string is in RGBA format
|
333
|
+
#
|
334
|
+
# @return [Hash] a `Hash` which contains RGBA components of parsed color
|
335
|
+
def parse_rgb(color_str, is_rgba: false)
|
336
|
+
components = color_str.gsub(/(^rgb(a)?\(|\)$)/, "").split(",")
|
337
|
+
|
338
|
+
return {
|
339
|
+
red: components.shift.to_i,
|
340
|
+
green: components.shift.to_i,
|
341
|
+
blue: components.shift.to_i,
|
342
|
+
alpha: (is_rgba ? components.shift.to_f : 1.0)
|
343
|
+
}
|
344
|
+
end
|
345
|
+
|
346
|
+
|
347
|
+
# Parse color string as HSL(A) color.
|
348
|
+
#
|
349
|
+
# @param color_str [String] input HSL(A) color string
|
350
|
+
# @param is_hsla [Boolean] indicates if color string is in HSL(A) format
|
351
|
+
#
|
352
|
+
# @return [Hash] a `Hash` which contains RGBA components of parsed color
|
353
|
+
def parse_hsl(color_str, is_hsla: false)
|
354
|
+
components = color_str.gsub(/(^hsl(a)?\(|\)$)/, "").split(",")
|
355
|
+
|
356
|
+
hue = components.shift.to_i
|
357
|
+
|
358
|
+
saturation = components.shift
|
359
|
+
saturation = saturation.include?("%") ? saturation.to_f / 100 : saturation.to_f
|
360
|
+
|
361
|
+
lightness = components.shift
|
362
|
+
lightness = lightness.include?("%") ? lightness.to_f / 100 : lightness.to_f
|
363
|
+
|
364
|
+
result = hsl_to_rgb(hue, saturation, lightness)
|
365
|
+
result[:alpha] = is_hsla ? components.shift.to_f : 1.0
|
366
|
+
|
367
|
+
return result
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
# A helper function which allows to calculate the RGB component value from HSL color.
|
372
|
+
#
|
373
|
+
# @param p [Float] `2 * lightness -q`
|
374
|
+
# @param q [Float] `lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation`
|
375
|
+
# @param t [Float] `hue + 1/3`, `hue` or `hue - 1/3` according to which component is going to be calculated [r,g,b]
|
376
|
+
#
|
377
|
+
# @return [Float] a value which represents a RGB component in range 0.0-1.0
|
378
|
+
def hue_to_rgb(p, q, t)
|
379
|
+
t += 1 if t < 0
|
380
|
+
t -= 1 if t > 1
|
381
|
+
|
382
|
+
return p + (q - p) * 6 * t if t < 1.0 / 6.0
|
383
|
+
return q if t < 1.0 / 2.0
|
384
|
+
return p + (q - p) * (2/3 - t) * 6 if t < 2.0 / 3.0
|
385
|
+
return p;
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|