abachrome 0.1.5 → 0.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 +4 -4
- data/abachrome.gemspec +1 -0
- data/devenv.nix +1 -1
- data/lib/abachrome/abc_decimal.rb +38 -35
- data/lib/abachrome/color.rb +37 -10
- data/lib/abachrome/color_mixins/blend.rb +7 -5
- data/lib/abachrome/color_mixins/lighten.rb +8 -6
- data/lib/abachrome/color_mixins/spectral_mix.rb +70 -0
- data/lib/abachrome/color_mixins/to_colorspace.rb +10 -8
- data/lib/abachrome/color_mixins/to_grayscale.rb +87 -0
- data/lib/abachrome/color_mixins/to_lrgb.rb +14 -12
- data/lib/abachrome/color_mixins/to_oklab.rb +16 -14
- data/lib/abachrome/color_mixins/to_oklch.rb +12 -10
- data/lib/abachrome/color_mixins/to_srgb.rb +17 -15
- data/lib/abachrome/color_models/cmyk.rb +159 -0
- data/lib/abachrome/color_models/hsv.rb +5 -3
- data/lib/abachrome/color_models/lms.rb +3 -1
- data/lib/abachrome/color_models/oklab.rb +3 -1
- data/lib/abachrome/color_models/oklch.rb +6 -4
- data/lib/abachrome/color_models/rgb.rb +4 -2
- data/lib/abachrome/color_models/xyz.rb +3 -1
- data/lib/abachrome/color_models/yiq.rb +37 -0
- data/lib/abachrome/color_space.rb +28 -14
- data/lib/abachrome/converter.rb +10 -8
- data/lib/abachrome/converters/base.rb +13 -11
- data/lib/abachrome/converters/cmyk_to_srgb.rb +42 -0
- data/lib/abachrome/converters/lms_to_lrgb.rb +5 -3
- data/lib/abachrome/converters/lms_to_srgb.rb +6 -4
- data/lib/abachrome/converters/lms_to_xyz.rb +5 -3
- data/lib/abachrome/converters/lrgb_to_lms.rb +3 -1
- data/lib/abachrome/converters/lrgb_to_oklab.rb +5 -3
- data/lib/abachrome/converters/lrgb_to_srgb.rb +6 -4
- data/lib/abachrome/converters/lrgb_to_xyz.rb +5 -3
- data/lib/abachrome/converters/oklab_to_lms.rb +9 -7
- data/lib/abachrome/converters/oklab_to_lrgb.rb +7 -7
- data/lib/abachrome/converters/oklab_to_oklch.rb +4 -2
- data/lib/abachrome/converters/oklab_to_srgb.rb +4 -2
- data/lib/abachrome/converters/oklch_to_lrgb.rb +5 -3
- data/lib/abachrome/converters/oklch_to_oklab.rb +5 -3
- data/lib/abachrome/converters/oklch_to_srgb.rb +6 -4
- data/lib/abachrome/converters/oklch_to_xyz.rb +6 -4
- data/lib/abachrome/converters/srgb_to_cmyk.rb +64 -0
- data/lib/abachrome/converters/srgb_to_lrgb.rb +5 -3
- data/lib/abachrome/converters/srgb_to_oklab.rb +4 -2
- data/lib/abachrome/converters/srgb_to_oklch.rb +5 -3
- data/lib/abachrome/converters/srgb_to_yiq.rb +49 -0
- data/lib/abachrome/converters/xyz_to_lms.rb +5 -3
- data/lib/abachrome/converters/xyz_to_oklab.rb +5 -3
- data/lib/abachrome/converters/yiq_to_srgb.rb +47 -0
- data/lib/abachrome/gamut/base.rb +3 -3
- data/lib/abachrome/gamut/p3.rb +3 -3
- data/lib/abachrome/gamut/rec2020.rb +2 -2
- data/lib/abachrome/gamut/srgb.rb +4 -2
- data/lib/abachrome/illuminants/base.rb +2 -2
- data/lib/abachrome/illuminants/d50.rb +2 -2
- data/lib/abachrome/illuminants/d55.rb +2 -2
- data/lib/abachrome/illuminants/d65.rb +2 -2
- data/lib/abachrome/illuminants/d75.rb +2 -2
- data/lib/abachrome/named/css.rb +149 -149
- data/lib/abachrome/named/tailwind.rb +265 -265
- data/lib/abachrome/outputs/css.rb +2 -2
- data/lib/abachrome/palette.rb +26 -25
- data/lib/abachrome/palette_mixins/interpolate.rb +3 -1
- data/lib/abachrome/palette_mixins/resample.rb +2 -2
- data/lib/abachrome/palette_mixins/stretch_luminance.rb +2 -2
- data/lib/abachrome/parsers/css.rb +86 -71
- data/lib/abachrome/parsers/hex.rb +2 -2
- data/lib/abachrome/parsers/tailwind.rb +8 -8
- data/lib/abachrome/spectral.rb +277 -0
- data/lib/abachrome/to_abcd.rb +4 -4
- data/lib/abachrome/version.rb +2 -2
- data/lib/abachrome.rb +66 -10
- metadata +29 -3
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Abachrome::ColorMixins::ToGrayscale - Grayscale conversion mixin
|
|
4
|
+
#
|
|
5
|
+
# This mixin provides methods for converting colors to grayscale using various
|
|
6
|
+
# standard luma calculations. It supports both legacy Rec. 601 (SDTV) and modern
|
|
7
|
+
# Rec. 709 (HDTV) coefficients for accurate grayscale conversion based on human
|
|
8
|
+
# eye sensitivity.
|
|
9
|
+
#
|
|
10
|
+
# Key features:
|
|
11
|
+
# - Rec. 601 luma: Y = 0.299R + 0.587G + 0.114B (NTSC/SDTV standard)
|
|
12
|
+
# - Rec. 709 luma: Y = 0.2126R + 0.7152G + 0.0722B (HDTV standard)
|
|
13
|
+
# - Maintains alpha channel during conversion
|
|
14
|
+
# - Uses BigDecimal for precise calculations
|
|
15
|
+
#
|
|
16
|
+
# This is essential for image processing, accessibility checks, and any application
|
|
17
|
+
# that needs perceptually accurate grayscale conversion rather than simple averaging.
|
|
18
|
+
|
|
19
|
+
module Abachrome
|
|
20
|
+
module ColorMixins
|
|
21
|
+
module ToGrayscale
|
|
22
|
+
# Converts the color to grayscale using Rec. 601 luma coefficients (legacy NTSC standard).
|
|
23
|
+
# This is the same calculation used in the YIQ color space's Y component.
|
|
24
|
+
#
|
|
25
|
+
# @return [Abachrome::Color] A grayscale version of the color in sRGB space
|
|
26
|
+
def to_grayscale_601
|
|
27
|
+
rgb_color = to_srgb
|
|
28
|
+
r, g, b = rgb_color.coordinates
|
|
29
|
+
|
|
30
|
+
# Rec. 601 luma: Y = 0.299R + 0.587G + 0.114B
|
|
31
|
+
luma = (AD("0.299") * r) + (AD("0.587") * g) + (AD("0.114") * b)
|
|
32
|
+
|
|
33
|
+
Color.from_rgb(luma, luma, luma, alpha)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Converts the color to grayscale using Rec. 709 luma coefficients (HDTV standard).
|
|
37
|
+
#
|
|
38
|
+
# @return [Abachrome::Color] A grayscale version of the color in sRGB space
|
|
39
|
+
def to_grayscale_709
|
|
40
|
+
rgb_color = to_srgb
|
|
41
|
+
r, g, b = rgb_color.coordinates
|
|
42
|
+
|
|
43
|
+
# Rec. 709 luma: Y = 0.2126R + 0.7152G + 0.0722B
|
|
44
|
+
luma = (AD("0.2126") * r) + (AD("0.7152") * g) + (AD("0.0722") * b)
|
|
45
|
+
|
|
46
|
+
Color.from_rgb(luma, luma, luma, alpha)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Converts the color to grayscale using the default Rec. 601 standard.
|
|
50
|
+
# This is an alias for to_grayscale_601 and matches the legacy behavior
|
|
51
|
+
# expected by most image processing applications.
|
|
52
|
+
#
|
|
53
|
+
# @return [Abachrome::Color] A grayscale version of the color in sRGB space
|
|
54
|
+
def to_grayscale
|
|
55
|
+
to_grayscale_601
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Calculates the relative luminance (luma) value using Rec. 601 coefficients.
|
|
59
|
+
# This returns just the Y component without creating a new color.
|
|
60
|
+
#
|
|
61
|
+
# @return [AbcDecimal] The luma value in range [0, 1]
|
|
62
|
+
def luma_601
|
|
63
|
+
rgb_color = to_srgb
|
|
64
|
+
r, g, b = rgb_color.coordinates
|
|
65
|
+
(AD("0.299") * r) + (AD("0.587") * g) + (AD("0.114") * b)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Calculates the relative luminance (luma) value using Rec. 709 coefficients.
|
|
69
|
+
# This returns just the Y component without creating a new color.
|
|
70
|
+
#
|
|
71
|
+
# @return [AbcDecimal] The luma value in range [0, 1]
|
|
72
|
+
def luma_709
|
|
73
|
+
rgb_color = to_srgb
|
|
74
|
+
r, g, b = rgb_color.coordinates
|
|
75
|
+
(AD("0.2126") * r) + (AD("0.7152") * g) + (AD("0.0722") * b)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Calculates the relative luminance using the default Rec. 601 standard.
|
|
79
|
+
# Alias for luma_601.
|
|
80
|
+
#
|
|
81
|
+
# @return [AbcDecimal] The luma value in range [0, 1]
|
|
82
|
+
def luma
|
|
83
|
+
luma_601
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Abachrome::ColorMixins::ToLrgb - Linear RGB color space conversion functionality
|
|
2
4
|
#
|
|
3
5
|
# This mixin provides methods for converting colors to the linear RGB (LRGB) color space,
|
|
@@ -28,7 +30,7 @@ module Abachrome
|
|
|
28
30
|
# which uses a linear relationship between the stored numeric value and
|
|
29
31
|
# the actual light intensity. If the color is already in the LRGB space,
|
|
30
32
|
# it returns the current object without conversion.
|
|
31
|
-
#
|
|
33
|
+
#
|
|
32
34
|
# @return [Abachrome::Color] A new color object in the LRGB color space,
|
|
33
35
|
# or the original object if already in LRGB space
|
|
34
36
|
def to_lrgb
|
|
@@ -39,10 +41,10 @@ module Abachrome
|
|
|
39
41
|
|
|
40
42
|
# Converts the current color to the linear RGB (LRGB) color space and updates
|
|
41
43
|
# the receiver's state. If the color is already in LRGB space, this is a no-op.
|
|
42
|
-
#
|
|
44
|
+
#
|
|
43
45
|
# Unlike #to_lrgb which returns a new color instance, this method modifies the
|
|
44
46
|
# current object by changing its color space and coordinates to the LRGB equivalent.
|
|
45
|
-
#
|
|
47
|
+
#
|
|
46
48
|
# @return [Abachrome::Color] the receiver itself, now in LRGB color space
|
|
47
49
|
def to_lrgb!
|
|
48
50
|
unless color_space.name == :lrgb
|
|
@@ -54,11 +56,11 @@ module Abachrome
|
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
# Returns the linear red component value of the color.
|
|
57
|
-
#
|
|
59
|
+
#
|
|
58
60
|
# This method accesses the first coordinate from the color in linear RGB space.
|
|
59
61
|
# Linear RGB values differ from standard RGB by using a non-gamma-corrected
|
|
60
62
|
# linear representation of luminance.
|
|
61
|
-
#
|
|
63
|
+
#
|
|
62
64
|
# @return [AbcDecimal] The linear red component value, typically in range [0, 1]
|
|
63
65
|
def lred
|
|
64
66
|
to_lrgb.coordinates[0]
|
|
@@ -68,7 +70,7 @@ module Abachrome
|
|
|
68
70
|
# linear RGB color space first. Linear RGB uses a different scale than standard
|
|
69
71
|
# sRGB, with values representing linear light energy rather than gamma-corrected
|
|
70
72
|
# values.
|
|
71
|
-
#
|
|
73
|
+
#
|
|
72
74
|
# @return [AbcDecimal] The linear green component value from the color's linear
|
|
73
75
|
# RGB representation
|
|
74
76
|
def lgreen
|
|
@@ -76,17 +78,17 @@ module Abachrome
|
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
# Returns the linear blue channel value of this color after conversion to linear RGB color space.
|
|
79
|
-
#
|
|
81
|
+
#
|
|
80
82
|
# This method converts the current color to the linear RGB color space and extracts the blue
|
|
81
83
|
# component (the third coordinate).
|
|
82
|
-
#
|
|
84
|
+
#
|
|
83
85
|
# @return [AbcDecimal] The linear blue component value, typically in the range [0, 1]
|
|
84
86
|
def lblue
|
|
85
87
|
to_lrgb.coordinates[2]
|
|
86
88
|
end
|
|
87
89
|
|
|
88
90
|
# Returns the coordinates of the color in the linear RGB color space.
|
|
89
|
-
#
|
|
91
|
+
#
|
|
90
92
|
# @return [Array<AbcDecimal>] An array of three AbcDecimal values representing
|
|
91
93
|
# the red, green, and blue components in linear RGB color space.
|
|
92
94
|
def lrgb_values
|
|
@@ -96,14 +98,14 @@ module Abachrome
|
|
|
96
98
|
# Returns an array of sRGB values as integers in the 0-255 range.
|
|
97
99
|
# This method converts the color to RGB, scales the values to the 0-255 range,
|
|
98
100
|
# rounds to integers, and ensures they are clamped within the valid range.
|
|
99
|
-
#
|
|
101
|
+
#
|
|
100
102
|
# @return [Array<Integer>] An array of three RGB integer values between 0-255
|
|
101
103
|
def rgb_array
|
|
102
104
|
to_rgb.coordinates.map { |c| (c * 255).round.clamp(0, 255) }
|
|
103
105
|
end
|
|
104
106
|
|
|
105
107
|
# Returns a hexadecimal string representation of the color in RGB format.
|
|
106
|
-
#
|
|
108
|
+
#
|
|
107
109
|
# @return [String] A hexadecimal color code in the format '#RRGGBB' where RR, GG, and BB
|
|
108
110
|
# are two-digit hexadecimal values for the red, green, and blue components respectively.
|
|
109
111
|
# @example
|
|
@@ -116,4 +118,4 @@ module Abachrome
|
|
|
116
118
|
end
|
|
117
119
|
end
|
|
118
120
|
|
|
119
|
-
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
121
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Abachrome::ColorMixins::ToOklab - OKLAB color space conversion functionality
|
|
2
4
|
#
|
|
3
5
|
# This mixin provides methods for converting colors to the OKLAB color space, which is a
|
|
@@ -23,10 +25,10 @@ module Abachrome
|
|
|
23
25
|
module ColorMixins
|
|
24
26
|
module ToOklab
|
|
25
27
|
# Converts the current color to the OKLAB color space.
|
|
26
|
-
#
|
|
28
|
+
#
|
|
27
29
|
# If the color is already in OKLAB, it returns the color unchanged.
|
|
28
30
|
# Otherwise, it uses the Converter to transform the color to OKLAB.
|
|
29
|
-
#
|
|
31
|
+
#
|
|
30
32
|
# @return [Abachrome::Color] A new Color object in the OKLAB color space
|
|
31
33
|
def to_oklab
|
|
32
34
|
return self if color_space.name == :oklab
|
|
@@ -38,11 +40,11 @@ module Abachrome
|
|
|
38
40
|
# This method transforms the current color into OKLAB space,
|
|
39
41
|
# modifying the original object by updating its color space
|
|
40
42
|
# and coordinates if not already in OKLAB.
|
|
41
|
-
#
|
|
43
|
+
#
|
|
42
44
|
# @example
|
|
43
45
|
# color = Abachrome::Color.from_hex("#ff5500")
|
|
44
46
|
# color.to_oklab! # Color now uses OKLAB color space
|
|
45
|
-
#
|
|
47
|
+
#
|
|
46
48
|
# @return [Abachrome::Color] self, with updated color space and coordinates
|
|
47
49
|
def to_oklab!
|
|
48
50
|
unless color_space.name == :oklab
|
|
@@ -56,27 +58,27 @@ module Abachrome
|
|
|
56
58
|
# Returns the lightness component (L) of the color in the OKLAB color space.
|
|
57
59
|
# The lightness value ranges from 0 (black) to 1 (white) and represents
|
|
58
60
|
# the perceived lightness of the color.
|
|
59
|
-
#
|
|
61
|
+
#
|
|
60
62
|
# @return [AbcDecimal] The lightness (L) value from the OKLAB color space
|
|
61
63
|
def lightness
|
|
62
64
|
to_oklab.coordinates[0]
|
|
63
65
|
end
|
|
64
66
|
|
|
65
67
|
# Returns the L (Lightness) component from the OKLAB color space.
|
|
66
|
-
#
|
|
68
|
+
#
|
|
67
69
|
# The L value represents perceptual lightness in the OKLAB color space,
|
|
68
70
|
# typically ranging from 0 (black) to 1 (white).
|
|
69
|
-
#
|
|
71
|
+
#
|
|
70
72
|
# @return [AbcDecimal] The L (Lightness) component from the OKLAB color space
|
|
71
73
|
def l
|
|
72
74
|
to_oklab.coordinates[0]
|
|
73
75
|
end
|
|
74
76
|
|
|
75
77
|
# Returns the 'a' component from the OKLAB color space (green-red axis).
|
|
76
|
-
#
|
|
78
|
+
#
|
|
77
79
|
# The 'a' component in OKLAB represents the position on the green-red axis,
|
|
78
80
|
# with negative values being more green and positive values being more red.
|
|
79
|
-
#
|
|
81
|
+
#
|
|
80
82
|
# @return [AbcDecimal] The 'a' component value from the OKLAB color space.
|
|
81
83
|
# @see #to_oklab For the full conversion to OKLAB color space
|
|
82
84
|
def a
|
|
@@ -84,25 +86,25 @@ module Abachrome
|
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
# Returns the B value of the color in OKLAB color space.
|
|
87
|
-
#
|
|
89
|
+
#
|
|
88
90
|
# This method first converts the color to OKLAB color space if needed,
|
|
89
91
|
# then extracts the B component (blue-yellow axis), which is the third
|
|
90
92
|
# coordinate in the OKLAB model.
|
|
91
|
-
#
|
|
93
|
+
#
|
|
92
94
|
# @return [AbcDecimal] The B component value in OKLAB color space
|
|
93
95
|
def b
|
|
94
96
|
to_oklab.coordinates[2]
|
|
95
97
|
end
|
|
96
98
|
|
|
97
99
|
# Returns the OKLAB color space coordinates for this color.
|
|
98
|
-
#
|
|
100
|
+
#
|
|
99
101
|
# @return [Array] An array of OKLAB coordinates [L, a, b] representing the color in OKLAB color space
|
|
100
102
|
def oklab_values
|
|
101
103
|
to_oklab.coordinates
|
|
102
104
|
end
|
|
103
105
|
|
|
104
106
|
# Returns an array representation of the color's coordinates in the OKLAB color space.
|
|
105
|
-
#
|
|
107
|
+
#
|
|
106
108
|
# @return [Array<AbcDecimal>] An array containing the coordinates of the color
|
|
107
109
|
# in the OKLAB color space in the order [L, a, b]
|
|
108
110
|
def oklab_array
|
|
@@ -112,4 +114,4 @@ module Abachrome
|
|
|
112
114
|
end
|
|
113
115
|
end
|
|
114
116
|
|
|
115
|
-
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
117
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Abachrome::ColorMixins::ToOklch - OKLCH color space conversion functionality
|
|
2
4
|
#
|
|
3
5
|
# This mixin provides methods for converting colors to the OKLCH color space, which is a
|
|
@@ -21,12 +23,12 @@ module Abachrome
|
|
|
21
23
|
module ColorMixins
|
|
22
24
|
module ToOklch
|
|
23
25
|
# Converts the current color to the OKLCH color space.
|
|
24
|
-
#
|
|
26
|
+
#
|
|
25
27
|
# This method transforms the color into the perceptually uniform OKLCH color space.
|
|
26
28
|
# If the color is already in OKLCH, it returns itself unchanged. If the color is in
|
|
27
29
|
# OKLAB, it directly converts from OKLAB to OKLCH. For all other color spaces, it
|
|
28
30
|
# first converts to OKLAB as an intermediate step, then converts to OKLCH.
|
|
29
|
-
#
|
|
31
|
+
#
|
|
30
32
|
# @return [Abachrome::Color] A new Color object in the OKLCH color space
|
|
31
33
|
def to_oklch
|
|
32
34
|
return self if color_space.name == :oklch
|
|
@@ -44,7 +46,7 @@ module Abachrome
|
|
|
44
46
|
# This method transforms the current color to OKLCH color space, modifying
|
|
45
47
|
# the original object instead of creating a new one. If the color is already
|
|
46
48
|
# in OKLCH space, no conversion is performed.
|
|
47
|
-
#
|
|
49
|
+
#
|
|
48
50
|
# @return [Abachrome::Color] self, allowing for method chaining
|
|
49
51
|
def to_oklch!
|
|
50
52
|
unless color_space.name == :oklch
|
|
@@ -58,7 +60,7 @@ module Abachrome
|
|
|
58
60
|
# Returns the lightness component of the color in the OKLCH color space.
|
|
59
61
|
# This method provides direct access to the first coordinate of the OKLCH
|
|
60
62
|
# representation of the color, which represents perceptual lightness.
|
|
61
|
-
#
|
|
63
|
+
#
|
|
62
64
|
# @return [AbcDecimal] the lightness value in the OKLCH color space,
|
|
63
65
|
# typically in the range of 0.0 to 1.0, where 0.0 is black and 1.0 is white
|
|
64
66
|
def lightness
|
|
@@ -67,14 +69,14 @@ module Abachrome
|
|
|
67
69
|
|
|
68
70
|
# Returns the chroma value of the color by converting it to the OKLCH color space.
|
|
69
71
|
# Chroma represents color intensity or saturation in the OKLCH color space.
|
|
70
|
-
#
|
|
72
|
+
#
|
|
71
73
|
# @return [AbcDecimal] The chroma value (second coordinate) from the OKLCH color space
|
|
72
74
|
def chroma
|
|
73
75
|
to_oklch.coordinates[1]
|
|
74
76
|
end
|
|
75
77
|
|
|
76
78
|
# Returns the hue value of the color in the OKLCH color space.
|
|
77
|
-
#
|
|
79
|
+
#
|
|
78
80
|
# @return [AbcDecimal] The hue component of the color in degrees (0-360)
|
|
79
81
|
# from the OKLCH color space representation.
|
|
80
82
|
def hue
|
|
@@ -82,7 +84,7 @@ module Abachrome
|
|
|
82
84
|
end
|
|
83
85
|
|
|
84
86
|
# Returns the OKLCH coordinates of the color.
|
|
85
|
-
#
|
|
87
|
+
#
|
|
86
88
|
# @return [Array<AbcDecimal>] Array of OKLCH coordinates [lightness, chroma, hue] where:
|
|
87
89
|
# - lightness: perceptual lightness component (0-1)
|
|
88
90
|
# - chroma: colorfulness/saturation component
|
|
@@ -92,11 +94,11 @@ module Abachrome
|
|
|
92
94
|
end
|
|
93
95
|
|
|
94
96
|
# Returns the OKLCH coordinates of the color as an array.
|
|
95
|
-
#
|
|
97
|
+
#
|
|
96
98
|
# Converts the current color to OKLCH color space and returns its coordinates
|
|
97
99
|
# as an array. The OKLCH color space represents colors using Lightness,
|
|
98
100
|
# Chroma, and Hue components in a perceptually uniform way.
|
|
99
|
-
#
|
|
101
|
+
#
|
|
100
102
|
# @return [Array<Numeric>] An array containing the OKLCH coordinates [lightness, chroma, hue]
|
|
101
103
|
def oklch_array
|
|
102
104
|
to_oklch.coordinates
|
|
@@ -105,4 +107,4 @@ module Abachrome
|
|
|
105
107
|
end
|
|
106
108
|
end
|
|
107
109
|
|
|
108
|
-
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
110
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Abachrome::ColorMixins::ToSrgb - sRGB color space conversion functionality
|
|
2
4
|
#
|
|
3
5
|
# This mixin provides methods for converting colors to the sRGB color space, which is the
|
|
@@ -23,11 +25,11 @@ module Abachrome
|
|
|
23
25
|
module ColorMixins
|
|
24
26
|
module ToSrgb
|
|
25
27
|
# Converts the current color to the sRGB color space.
|
|
26
|
-
#
|
|
28
|
+
#
|
|
27
29
|
# If the color is already in the sRGB color space, returns the color instance
|
|
28
30
|
# unchanged. Otherwise, performs a color space conversion from the current
|
|
29
31
|
# color space to sRGB.
|
|
30
|
-
#
|
|
32
|
+
#
|
|
31
33
|
# @return [Abachrome::Color] A new Color instance in the sRGB color space,
|
|
32
34
|
# or self if already in sRGB
|
|
33
35
|
def to_srgb
|
|
@@ -37,7 +39,7 @@ module Abachrome
|
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
# Alias for #to_srgb method.
|
|
40
|
-
#
|
|
42
|
+
#
|
|
41
43
|
# @return [Abachrome::Color] The color converted to sRGB color space
|
|
42
44
|
def to_rgb
|
|
43
45
|
# assume they mean srgb
|
|
@@ -59,7 +61,7 @@ module Abachrome
|
|
|
59
61
|
# Converts the current color to sRGB color space in place.
|
|
60
62
|
# This is an alias for {#to_srgb!} as RGB commonly refers to sRGB
|
|
61
63
|
# in web and design contexts.
|
|
62
|
-
#
|
|
64
|
+
#
|
|
63
65
|
# @return [self] Returns self after converting to sRGB
|
|
64
66
|
def to_rgb!
|
|
65
67
|
# assume they mean srgb
|
|
@@ -67,7 +69,7 @@ module Abachrome
|
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
# Returns the red component of the color in the sRGB color space.
|
|
70
|
-
#
|
|
72
|
+
#
|
|
71
73
|
# @return [AbcDecimal] The red component value in the sRGB color space,
|
|
72
74
|
# normalized between 0 and 1.
|
|
73
75
|
def red
|
|
@@ -75,27 +77,27 @@ module Abachrome
|
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
# Returns the green component of the color in sRGB space.
|
|
78
|
-
#
|
|
80
|
+
#
|
|
79
81
|
# This method converts the current color to sRGB color space if needed,
|
|
80
82
|
# then extracts the green component (second coordinate).
|
|
81
|
-
#
|
|
83
|
+
#
|
|
82
84
|
# @return [AbcDecimal] The green component value in the sRGB color space, typically in the range 0-1
|
|
83
85
|
def green
|
|
84
86
|
to_srgb.coordinates[1]
|
|
85
87
|
end
|
|
86
88
|
|
|
87
89
|
# Returns the blue component of the color in sRGB color space.
|
|
88
|
-
#
|
|
90
|
+
#
|
|
89
91
|
# This method converts the current color to sRGB if needed and
|
|
90
92
|
# extracts the third coordinate value (blue).
|
|
91
|
-
#
|
|
93
|
+
#
|
|
92
94
|
# @return [AbcDecimal] The blue component value in sRGB space, typically in range 0-1
|
|
93
95
|
def blue
|
|
94
96
|
to_srgb.coordinates[2]
|
|
95
97
|
end
|
|
96
98
|
|
|
97
99
|
# Returns the RGB color values in the sRGB color space.
|
|
98
|
-
#
|
|
100
|
+
#
|
|
99
101
|
# @return [Array<AbcDecimal>] An array of three AbcDecimal values representing
|
|
100
102
|
# the red, green, and blue color components in the sRGB color space.
|
|
101
103
|
def srgb_values
|
|
@@ -103,18 +105,18 @@ module Abachrome
|
|
|
103
105
|
end
|
|
104
106
|
|
|
105
107
|
# Returns the RGB values of the color as coordinates in the sRGB color space.
|
|
106
|
-
#
|
|
108
|
+
#
|
|
107
109
|
# @return [Array<Abachrome::AbcDecimal>] The RGB coordinates (red, green, blue) in sRGB color space
|
|
108
110
|
def rgb_values
|
|
109
111
|
to_srgb.coordinates
|
|
110
112
|
end
|
|
111
113
|
|
|
112
114
|
# Returns an array of RGB values (0-255) for this color.
|
|
113
|
-
#
|
|
115
|
+
#
|
|
114
116
|
# This method converts the color to sRGB, then scales the component values
|
|
115
117
|
# from the 0-1 range to the 0-255 range commonly used in RGB color codes.
|
|
116
118
|
# Values are rounded to the nearest integer and clamped between 0 and 255.
|
|
117
|
-
#
|
|
119
|
+
#
|
|
118
120
|
# @return [Array<Integer>] An array of three integers representing the [R, G, B]
|
|
119
121
|
# values in the 0-255 range
|
|
120
122
|
def rgb_array
|
|
@@ -123,7 +125,7 @@ module Abachrome
|
|
|
123
125
|
|
|
124
126
|
# Returns a hexadecimal representation of this color in sRGB color space.
|
|
125
127
|
# Converts the color to sRGB, then formats it as a hexadecimal string.
|
|
126
|
-
#
|
|
128
|
+
#
|
|
127
129
|
# @return [String] A string in the format "#RRGGBB" where RR, GG, and BB are
|
|
128
130
|
# the hexadecimal representations of the red, green, and blue components,
|
|
129
131
|
# each ranging from 00 to FF.
|
|
@@ -137,4 +139,4 @@ module Abachrome
|
|
|
137
139
|
end
|
|
138
140
|
end
|
|
139
141
|
|
|
140
|
-
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
142
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Abachrome::ColorModels::CMYK - CMYK color space model utilities
|
|
4
|
+
#
|
|
5
|
+
# This module provides utility methods for the CMYK (Cyan, Magenta, Yellow, Key/Black)
|
|
6
|
+
# color model within the Abachrome color manipulation library. CMYK represents colors
|
|
7
|
+
# using the subtractive color synthesis model used in printing and physical media.
|
|
8
|
+
#
|
|
9
|
+
# Key features:
|
|
10
|
+
# - Subtractive color model (ink on paper, not light)
|
|
11
|
+
# - Four channels: Cyan, Magenta, Yellow, and Key (Black)
|
|
12
|
+
# - Supports Undercolor Removal (UCR) for ink savings
|
|
13
|
+
# - Supports Gray Component Replacement (GCR) for richer blacks
|
|
14
|
+
# - High-precision BigDecimal arithmetic for exact ink percentages
|
|
15
|
+
# - Essential for print media, PDF generation, and pre-press workflows
|
|
16
|
+
#
|
|
17
|
+
# The CMYK model is fundamental for professional printing, where the fourth channel
|
|
18
|
+
# (Black/Key) is added to achieve crisp text and deep shadows that cannot be produced
|
|
19
|
+
# by combining cyan, magenta, and yellow inks alone.
|
|
20
|
+
|
|
21
|
+
require_relative "../abc_decimal"
|
|
22
|
+
|
|
23
|
+
module Abachrome
|
|
24
|
+
module ColorModels
|
|
25
|
+
class CMYK
|
|
26
|
+
include Abachrome::ToAbcd
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
# Normalizes CMYK color component values to the [0,1] range.
|
|
30
|
+
#
|
|
31
|
+
# @param c [Numeric] Cyan component (0-1 or 0-100 for percentages)
|
|
32
|
+
# @param m [Numeric] Magenta component (0-1 or 0-100 for percentages)
|
|
33
|
+
# @param y [Numeric] Yellow component (0-1 or 0-100 for percentages)
|
|
34
|
+
# @param k [Numeric] Key/Black component (0-1 or 0-100 for percentages)
|
|
35
|
+
# @return [Array<AbcDecimal>] Array of four normalized components as AbcDecimal objects
|
|
36
|
+
def normalize(c, m, y, k)
|
|
37
|
+
[c, m, y, k].map do |value|
|
|
38
|
+
case value
|
|
39
|
+
when String
|
|
40
|
+
if value.end_with?("%")
|
|
41
|
+
AbcDecimal(value.chomp("%")) / AbcDecimal(100)
|
|
42
|
+
else
|
|
43
|
+
AbcDecimal(value)
|
|
44
|
+
end
|
|
45
|
+
when Numeric
|
|
46
|
+
if value > 1
|
|
47
|
+
AbcDecimal(value) / AbcDecimal(100)
|
|
48
|
+
else
|
|
49
|
+
AbcDecimal(value)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Converts RGB values to CMYK using the standard naive conversion.
|
|
56
|
+
# This produces high ink coverage and is generally not recommended for production.
|
|
57
|
+
#
|
|
58
|
+
# @param r [Numeric] Red component (0-1)
|
|
59
|
+
# @param g [Numeric] Green component (0-1)
|
|
60
|
+
# @param b [Numeric] Blue component (0-1)
|
|
61
|
+
# @return [Array<AbcDecimal>] Array of [c, m, y, k] values
|
|
62
|
+
def from_rgb_naive(r, g, b)
|
|
63
|
+
r = AbcDecimal(r)
|
|
64
|
+
g = AbcDecimal(g)
|
|
65
|
+
b = AbcDecimal(b)
|
|
66
|
+
|
|
67
|
+
# Simple complementary conversion
|
|
68
|
+
c = AbcDecimal(1) - r
|
|
69
|
+
m = AbcDecimal(1) - g
|
|
70
|
+
y = AbcDecimal(1) - b
|
|
71
|
+
k = AbcDecimal(0)
|
|
72
|
+
|
|
73
|
+
[c, m, y, k]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Converts RGB values to CMYK using Undercolor Removal (UCR).
|
|
77
|
+
# This extracts the gray component to the black (K) channel, reducing ink usage.
|
|
78
|
+
#
|
|
79
|
+
# @param r [Numeric] Red component (0-1)
|
|
80
|
+
# @param g [Numeric] Green component (0-1)
|
|
81
|
+
# @param b [Numeric] Blue component (0-1)
|
|
82
|
+
# @param gcr_amount [Numeric] Gray Component Replacement amount (0-1), default 1.0 for full UCR
|
|
83
|
+
# @return [Array<AbcDecimal>] Array of [c, m, y, k] values
|
|
84
|
+
def from_rgb_ucr(r, g, b, gcr_amount = 1.0)
|
|
85
|
+
r = AbcDecimal(r)
|
|
86
|
+
g = AbcDecimal(g)
|
|
87
|
+
b = AbcDecimal(b)
|
|
88
|
+
gcr = AbcDecimal(gcr_amount)
|
|
89
|
+
|
|
90
|
+
# Calculate complementary CMY values
|
|
91
|
+
c = AbcDecimal(1) - r
|
|
92
|
+
m = AbcDecimal(1) - g
|
|
93
|
+
y = AbcDecimal(1) - b
|
|
94
|
+
|
|
95
|
+
# Find the minimum (gray component)
|
|
96
|
+
k = [c, m, y].min * gcr
|
|
97
|
+
|
|
98
|
+
# Remove the gray component from CMY if k > 0
|
|
99
|
+
if k.positive?
|
|
100
|
+
c -= k
|
|
101
|
+
m -= k
|
|
102
|
+
y -= k
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
[c, m, y, k]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Alias for from_rgb_ucr with full GCR (the standard approach).
|
|
109
|
+
# GCR is essentially the same as UCR but allows control over how much
|
|
110
|
+
# of the gray component to extract.
|
|
111
|
+
#
|
|
112
|
+
# @param r [Numeric] Red component (0-1)
|
|
113
|
+
# @param g [Numeric] Green component (0-1)
|
|
114
|
+
# @param b [Numeric] Blue component (0-1)
|
|
115
|
+
# @param amount [Numeric] Amount of GCR to apply (0-1), default 1.0
|
|
116
|
+
# @return [Array<AbcDecimal>] Array of [c, m, y, k] values
|
|
117
|
+
def from_rgb_gcr(r, g, b, amount = 1.0)
|
|
118
|
+
from_rgb_ucr(r, g, b, amount)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Converts CMYK values back to RGB.
|
|
122
|
+
# This is a straightforward inverse of the naive RGB→CMY conversion
|
|
123
|
+
# plus the addition of the black channel.
|
|
124
|
+
#
|
|
125
|
+
# @param c [Numeric] Cyan component (0-1)
|
|
126
|
+
# @param m [Numeric] Magenta component (0-1)
|
|
127
|
+
# @param y [Numeric] Yellow component (0-1)
|
|
128
|
+
# @param k [Numeric] Key/Black component (0-1)
|
|
129
|
+
# @return [Array<AbcDecimal>] Array of [r, g, b] values
|
|
130
|
+
def to_rgb(c, m, y, k)
|
|
131
|
+
c = AbcDecimal(c)
|
|
132
|
+
m = AbcDecimal(m)
|
|
133
|
+
y = AbcDecimal(y)
|
|
134
|
+
k = AbcDecimal(k)
|
|
135
|
+
|
|
136
|
+
# Standard CMYK to RGB conversion
|
|
137
|
+
r = (AbcDecimal(1) - c) * (AbcDecimal(1) - k)
|
|
138
|
+
g = (AbcDecimal(1) - m) * (AbcDecimal(1) - k)
|
|
139
|
+
b = (AbcDecimal(1) - y) * (AbcDecimal(1) - k)
|
|
140
|
+
|
|
141
|
+
[r, g, b]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Calculates the Total Area Coverage (TAC) for CMYK values.
|
|
145
|
+
# TAC is the sum of all four ink percentages and is critical in print workflows
|
|
146
|
+
# to prevent excessive ink that can cause smearing or paper damage.
|
|
147
|
+
#
|
|
148
|
+
# @param c [Numeric] Cyan component (0-1)
|
|
149
|
+
# @param m [Numeric] Magenta component (0-1)
|
|
150
|
+
# @param y [Numeric] Yellow component (0-1)
|
|
151
|
+
# @param k [Numeric] Key/Black component (0-1)
|
|
152
|
+
# @return [AbcDecimal] Total ink coverage as a decimal (0-4, often expressed as 0-400%)
|
|
153
|
+
def total_area_coverage(c, m, y, k)
|
|
154
|
+
AbcDecimal(c) + AbcDecimal(m) + AbcDecimal(y) + AbcDecimal(k)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Abachrome::ColorModels::HSV - HSV color space model definition
|
|
2
4
|
#
|
|
3
5
|
# This module defines the HSV (Hue, Saturation, Value) color model within the Abachrome
|
|
@@ -30,13 +32,13 @@ module Abachrome
|
|
|
30
32
|
|
|
31
33
|
# Validates whether the coordinates are valid for the HSV color model.
|
|
32
34
|
# Each component (hue, saturation, value) must be in the range [0, 1].
|
|
33
|
-
#
|
|
35
|
+
#
|
|
34
36
|
# @param coordinates [Array<Numeric>] An array of three values representing
|
|
35
37
|
# hue (h), saturation (s), and value (v) in the range [0, 1]
|
|
36
38
|
# @return [Boolean] true if all coordinates are within valid ranges, false otherwise
|
|
37
39
|
def valid_coordinates?(coordinates)
|
|
38
40
|
h, s, v = coordinates
|
|
39
|
-
h
|
|
41
|
+
h.between?(0, 1.0) &&
|
|
40
42
|
s >= 0 && s <= 1.0 &&
|
|
41
43
|
v >= 0 && v <= 1.0
|
|
42
44
|
end
|
|
@@ -44,4 +46,4 @@ module Abachrome
|
|
|
44
46
|
end
|
|
45
47
|
end
|
|
46
48
|
|
|
47
|
-
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
49
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Abachrome::ColorModels::Lms - LMS color space model definition
|
|
2
4
|
#
|
|
3
5
|
# This module defines the LMS color model within the Abachrome color manipulation library.
|
|
@@ -33,4 +35,4 @@ ColorSpace.register(
|
|
|
33
35
|
[]
|
|
34
36
|
)
|
|
35
37
|
|
|
36
|
-
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
38
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Abachrome::ColorModels::Oklab - OKLAB color space model definition
|
|
2
4
|
#
|
|
3
5
|
# This module defines the OKLAB color model within the Abachrome color manipulation library.
|
|
@@ -32,4 +34,4 @@ ColorSpace.register(
|
|
|
32
34
|
["ok-lab"]
|
|
33
35
|
)
|
|
34
36
|
|
|
35
|
-
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
37
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|