abachrome 0.1.0 → 0.1.2

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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/README.md +10 -0
  4. data/Rakefile +15 -0
  5. data/devenv.lock +88 -17
  6. data/devenv.nix +2 -1
  7. data/devenv.yaml +5 -12
  8. data/lib/abachrome/abc_decimal.rb +201 -7
  9. data/lib/abachrome/color.rb +88 -1
  10. data/lib/abachrome/color_mixins/blend.rb +53 -2
  11. data/lib/abachrome/color_mixins/lighten.rb +49 -2
  12. data/lib/abachrome/color_mixins/to_colorspace.rb +67 -2
  13. data/lib/abachrome/color_mixins/to_lrgb.rb +70 -2
  14. data/lib/abachrome/color_mixins/to_oklab.rb +67 -2
  15. data/lib/abachrome/color_mixins/to_oklch.rb +60 -2
  16. data/lib/abachrome/color_mixins/to_srgb.rb +77 -2
  17. data/lib/abachrome/color_models/hsv.rb +25 -2
  18. data/lib/abachrome/color_models/lms.rb +34 -0
  19. data/lib/abachrome/color_models/oklab.rb +19 -2
  20. data/lib/abachrome/color_models/oklch.rb +42 -2
  21. data/lib/abachrome/color_models/rgb.rb +28 -2
  22. data/lib/abachrome/color_models/xyz.rb +28 -0
  23. data/lib/abachrome/color_space.rb +88 -2
  24. data/lib/abachrome/converter.rb +56 -2
  25. data/lib/abachrome/converters/base.rb +69 -2
  26. data/lib/abachrome/converters/lms_to_lrgb.rb +36 -0
  27. data/lib/abachrome/converters/lms_to_srgb.rb +23 -0
  28. data/lib/abachrome/converters/lms_to_xyz.rb +30 -0
  29. data/lib/abachrome/converters/lrgb_to_lms.rb +0 -0
  30. data/lib/abachrome/converters/lrgb_to_oklab.rb +28 -2
  31. data/lib/abachrome/converters/lrgb_to_srgb.rb +27 -2
  32. data/lib/abachrome/converters/lrgb_to_xyz.rb +29 -0
  33. data/lib/abachrome/converters/oklab_to_lms.rb +41 -0
  34. data/lib/abachrome/converters/oklab_to_lrgb.rb +56 -29
  35. data/lib/abachrome/converters/oklab_to_oklch.rb +31 -2
  36. data/lib/abachrome/converters/oklab_to_srgb.rb +27 -2
  37. data/lib/abachrome/converters/oklch_to_lrgb.rb +66 -6
  38. data/lib/abachrome/converters/oklch_to_oklab.rb +29 -4
  39. data/lib/abachrome/converters/oklch_to_srgb.rb +26 -2
  40. data/lib/abachrome/converters/oklch_to_xyz.rb +66 -0
  41. data/lib/abachrome/converters/srgb_to_lrgb.rb +26 -2
  42. data/lib/abachrome/converters/srgb_to_oklab.rb +28 -2
  43. data/lib/abachrome/converters/srgb_to_oklch.rb +27 -2
  44. data/lib/abachrome/converters/xyz_to_lms.rb +30 -0
  45. data/lib/abachrome/converters/xyz_to_oklab.rb +38 -0
  46. data/lib/abachrome/gamut/base.rb +2 -2
  47. data/lib/abachrome/gamut/p3.rb +2 -2
  48. data/lib/abachrome/gamut/rec2020.rb +2 -2
  49. data/lib/abachrome/gamut/srgb.rb +20 -2
  50. data/lib/abachrome/illuminants/base.rb +2 -2
  51. data/lib/abachrome/illuminants/d50.rb +2 -2
  52. data/lib/abachrome/illuminants/d55.rb +2 -2
  53. data/lib/abachrome/illuminants/d65.rb +2 -2
  54. data/lib/abachrome/illuminants/d75.rb +2 -2
  55. data/lib/abachrome/named/css.rb +149 -158
  56. data/lib/abachrome/outputs/css.rb +2 -2
  57. data/lib/abachrome/palette.rb +112 -2
  58. data/lib/abachrome/palette_mixins/interpolate.rb +20 -2
  59. data/lib/abachrome/palette_mixins/resample.rb +2 -2
  60. data/lib/abachrome/palette_mixins/stretch_luminance.rb +2 -2
  61. data/lib/abachrome/parsers/hex.rb +2 -2
  62. data/lib/abachrome/to_abcd.rb +10 -2
  63. data/lib/abachrome/version.rb +2 -2
  64. data/lib/abachrome.rb +78 -2
  65. data/logo.png +0 -0
  66. data/logo.webp +0 -0
  67. metadata +26 -67
@@ -1,14 +1,74 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "oklab_to_lrgb"
4
- require_relative "oklch_to_oklab"
1
+ # Abachrome::Converters::OklchToLrgb - OKLCH to Linear RGB color space converter
2
+ #
3
+ # This converter transforms colors from the OKLCH color space to the linear RGB (LRGB) color space.
4
+ # The conversion is performed by first transforming OKLCH's cylindrical coordinates (Lightness, Chroma, Hue)
5
+ # into OKLAB's rectangular coordinates (L, a, b).
6
+ # Then, these OKLAB coordinates are converted to LRGB. This second part involves transforming
7
+ # OKLAB to an intermediate non-linear cone response space (L'M'S'), then to a linear
8
+ # cone response space (LMS), and finally from LMS to LRGB using appropriate matrices.
9
+ # All these steps are combined into a single direct conversion method.
10
+ #
11
+ # Key features:
12
+ # - Direct conversion from OKLCH to LRGB.
13
+ # - Combines cylindrical to rectangular conversion (OKLCH to OKLAB)
14
+ # with the OKLAB to LRGB transformation pipeline (OKLAB -> L'M'S' -> LMS -> LRGB).
15
+ # - Uses AbcDecimal arithmetic for precise color science calculations.
16
+ # - Maintains alpha channel transparency values during conversion.
17
+ # - Validates input color space to ensure proper OKLCH source data.
5
18
 
6
19
  module Abachrome
7
20
  module Converters
8
21
  class OklchToLrgb < Abachrome::Converters::Base
9
22
  def self.convert(oklch_color)
10
- oklab_color = OklchToOklab.convert(oklch_color)
11
- OklabToLrgb.convert(oklab_color)
23
+ raise_unless oklch_color, :oklch
24
+
25
+ l_oklch, c_oklch, h_oklch = oklch_color.coordinates.map { |_| AbcDecimal(_) }
26
+ alpha = oklch_color.alpha
27
+
28
+ # Step 1: OKLCH to OKLAB
29
+ # l_oklab is the same as l_oklch
30
+ l_oklab = l_oklch
31
+
32
+ # Convert hue from degrees to radians
33
+ # h_oklch is AbcDecimal, Math::PI is Float. AD(Math::PI) makes it AbcDecimal.
34
+ # Division by AD("180") ensures AbcDecimal arithmetic.
35
+ h_rad = (h_oklch * AD(Math::PI)) / AD("180")
36
+
37
+ # Calculate a_oklab and b_oklab
38
+ # Math.cos/sin take a float; .value of AbcDecimal is BigDecimal.
39
+ # AD(Math.cos/sin(big_decimal_value)) wraps the result back to AbcDecimal.
40
+ a_oklab = c_oklch * AD(Math.cos(h_rad.value))
41
+ b_oklab = c_oklch * AD(Math.sin(h_rad.value))
42
+
43
+ # Step 2: OKLAB to L'M'S' (cone responses, non-linear)
44
+ # Constants from the inverse of M2 matrix (OKLAB to L'M'S')
45
+ # l_oklab, a_oklab, b_oklab are already AbcDecimal.
46
+ l_prime = l_oklab + (AD("0.39633779217376785678") * a_oklab) + (AD("0.21580375806075880339") * b_oklab)
47
+ m_prime = l_oklab - (AD("0.1055613423236563494") * a_oklab) - (AD("0.063854174771705903402") * b_oklab)
48
+ s_prime = l_oklab - (AD("0.089484182094965759684") * a_oklab) - (AD("1.2914855378640917399") * b_oklab)
49
+
50
+ # Step 3: L'M'S' to LMS (cubing to linearize cone responses)
51
+ l_lms = l_prime**3
52
+ m_lms = m_prime**3
53
+ s_lms = s_prime**3
54
+
55
+ # Step 4: LMS to LRGB
56
+ # Using matrix M_lrgb_from_lms (OKLAB specific)
57
+ r_lrgb = (l_lms * AD("4.07674166134799")) + (m_lms * AD("-3.307711590408193")) + (s_lms * AD("0.230969928729428"))
58
+ g_lrgb = (l_lms * AD("-1.2684380040921763")) + (m_lms * AD("2.6097574006633715")) + (s_lms * AD("-0.3413193963102197"))
59
+ b_lrgb = (l_lms * AD("-0.004196086541837188"))+ (m_lms * AD("-0.7034186144594493")) + (s_lms * AD("1.7076147009309444"))
60
+
61
+ # Clamp LRGB values to be non-negative.
62
+ # LRGB values can be outside [0,1] but should be >= 0.
63
+ # Further clamping to [0,1] typically occurs when converting to display-referred spaces like sRGB.
64
+ zero_ad = AD("0")
65
+ output_coords = [
66
+ [r_lrgb, zero_ad].max,
67
+ [g_lrgb, zero_ad].max,
68
+ [b_lrgb, zero_ad].max
69
+ ]
70
+
71
+ Color.new(ColorSpace.find(:lrgb), output_coords, alpha)
12
72
  end
13
73
  end
14
74
  end
@@ -1,16 +1,41 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::OklchToOklab - OKLCH to OKLAB color space converter
2
+ #
3
+ # This converter transforms colors from the OKLCH color space to the OKLAB color space
4
+ # using cylindrical to rectangular coordinate conversion. The transformation converts the
5
+ # cylindrical coordinates (L, C, h) to rectangular coordinates (L, a, b) where lightness
6
+ # remains unchanged, and the a and b components are calculated from chroma and hue using
7
+ # trigonometric functions (cosine and sine respectively).
8
+ #
9
+ # Key features:
10
+ # - Converts OKLCH cylindrical coordinates to OKLAB rectangular coordinates
11
+ # - Preserves lightness component unchanged during conversion
12
+ # - Calculates a component as chroma × cos(hue) for green-red axis positioning
13
+ # - Calculates b component as chroma × sin(hue) for blue-yellow axis positioning
14
+ # - Converts hue angle from degrees to radians for trigonometric calculations
15
+ # - Maintains alpha channel transparency values during conversion
16
+ # - Uses AbcDecimal arithmetic for precise color science calculations
17
+ # - Validates input color space to ensure proper OKLCH source data
18
+ #
19
+ # The OKLAB color space provides the foundation for further conversions to other color
20
+ # spaces and serves as an intermediate step in the color transformation pipeline when
21
+ # working with OKLCH color manipulations that need to be converted to display-ready formats.
2
22
 
3
23
  module Abachrome
4
24
  module Converters
5
25
  class OklchToOklab < Abachrome::Converters::Base
26
+ # Converts a color from OKLCH color space to OKLAB color space.
27
+ #
28
+ # @param oklch_color [Abachrome::Color] The color in OKLCH format to convert
29
+ # @return [Abachrome::Color] The converted color in OKLAB format
30
+ # @raise [StandardError] If the provided color is not in OKLCH color space
6
31
  def self.convert(oklch_color)
7
32
  raise_unless oklch_color, :oklch
8
33
 
9
34
  l, c, h = oklch_color.coordinates.map { |_| AbcDecimal(_) }
10
35
 
11
- h_rad = h * Rational(Math::PI, 180)
12
- a = c * Math.cos(h_rad.value)
13
- b = c * Math.sin(h_rad.value)
36
+ h_rad = (h * Math::PI)/ AD(180)
37
+ a = c * AD(Math.cos(h_rad.value))
38
+ b = c * AD(Math.sin(h_rad.value))
14
39
 
15
40
  Color.new(
16
41
  ColorSpace.find(:oklab),
@@ -1,4 +1,22 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::OklchToSrgb - OKLCH to sRGB color space converter
2
+ #
3
+ # This converter transforms colors from the OKLCH color space to the standard RGB (sRGB) color space
4
+ # through a two-step conversion process. The transformation first converts OKLCH cylindrical
5
+ # coordinates to OKLAB rectangular coordinates, then applies the standard OKLAB to sRGB
6
+ # transformation pipeline to produce the final gamma-corrected sRGB values.
7
+ #
8
+ # Key features:
9
+ # - Two-stage conversion pipeline: OKLCH → OKLAB → sRGB
10
+ # - Leverages existing OklchToOklab and OklabToSrgb converters for modular transformation
11
+ # - Converts cylindrical coordinates (lightness, chroma, hue) to display-ready RGB values
12
+ # - Maintains alpha channel transparency values during conversion
13
+ # - Applies proper gamma correction for display on standard monitors and web applications
14
+ # - Uses AbcDecimal arithmetic for precise color science calculations
15
+ # - Validates input color space to ensure proper OKLCH source data
16
+ #
17
+ # The sRGB color space is the standard RGB color space for web content and most consumer
18
+ # displays, providing gamma-corrected values that properly represent colors on typical
19
+ # display devices while maintaining compatibility with web standards and digital media formats.
2
20
 
3
21
  require_relative "oklch_to_oklab"
4
22
  require_relative "oklab_to_srgb"
@@ -6,6 +24,12 @@ require_relative "oklab_to_srgb"
6
24
  module Abachrome
7
25
  module Converters
8
26
  class OklchToSrgb < Abachrome::Converters::Base
27
+ # Converts a color from OKLCH color space to sRGB color space.
28
+ # This is done by first converting from OKLCH to OKLAB,
29
+ # then from OKLAB to sRGB.
30
+ #
31
+ # @param oklch_color [Abachrome::Color] Color in OKLCH color space
32
+ # @return [Abachrome::Color] The converted color in sRGB color space
9
33
  def self.convert(oklch_color)
10
34
  # Convert OKLCh to OKLab first
11
35
  oklab_color = OklchToOklab.convert(oklch_color)
@@ -15,4 +39,4 @@ module Abachrome
15
39
  end
16
40
  end
17
41
  end
18
- end
42
+ end
@@ -0,0 +1,66 @@
1
+ module Abachrome
2
+ module Converters
3
+ class OklchToXyz < Abachrome::Converters::Base
4
+ def self.convert(oklch_color)
5
+ raise_unless oklch_color, :oklch
6
+
7
+ l_oklch, c_oklch, h_oklch = oklch_color.coordinates.map { |coord| AbcDecimal(coord) }
8
+ alpha = oklch_color.alpha
9
+
10
+ # Step 1: OKLCH to OKLAB
11
+ # (l_oklab, a_oklab, b_oklab)
12
+ l_oklab = l_oklch
13
+ # h_rad = (h_oklch * Math::PI) / AD(180) # h_oklch is AbcDecimal, Math::PI is Float. Coercion happens.
14
+ # More explicit for Math::PI:
15
+ h_rad = (h_oklch * AD(Math::PI.to_s)) / AD(180)
16
+
17
+ # Standard Math.cos/sin expect float. h_rad is AbcDecimal.
18
+ # .to_f is needed for conversion from AbcDecimal/BigDecimal to Float.
19
+ cos_h_rad = AD(Math.cos(h_rad.to_f))
20
+ sin_h_rad = AD(Math.sin(h_rad.to_f))
21
+
22
+ a_oklab = c_oklch * cos_h_rad
23
+ b_oklab = c_oklch * sin_h_rad
24
+
25
+ # Step 2: OKLAB to L'M'S' (cone responses, non-linear)
26
+ # (l_prime, m_prime, s_prime)
27
+ # These are the M_lms_prime_from_oklab matrix operations.
28
+ # The AbcDecimal() wrapper on the whole sum (as in OklabToLms.rb) is not strictly necessary
29
+ # if l_oklab, a_oklab, b_oklab are already AbcDecimal, as AbcDecimal ops return AbcDecimal.
30
+ l_prime = l_oklab + (AD("0.39633779217376785678") * a_oklab) + (AD("0.21580375806075880339") * b_oklab)
31
+ m_prime = l_oklab - (a_oklab * AD("-0.1055613423236563494")) + (b_oklab * AD("-0.063854174771705903402"))
32
+ s_prime = l_oklab - (a_oklab * AD("-0.089484182094965759684")) + (b_oklab * AD("-1.2914855378640917399"))
33
+
34
+ # Step 3: L'M'S' to LMS (cubing)
35
+ # (l_lms, m_lms, s_lms)
36
+ l_lms = l_prime**3
37
+ m_lms = m_prime**3
38
+ s_lms = s_prime**3
39
+
40
+ # Step 4: LMS to LRGB
41
+ # (r_lrgb, g_lrgb, b_lrgb)
42
+ # Using matrix M_lrgb_from_lms (OKLAB specific)
43
+ r_lrgb = (l_lms * AD("4.07674166134799")) + (m_lms * AD("-3.307711590408193")) + (s_lms * AD("0.230969928729428"))
44
+ g_lrgb = (l_lms * AD("-1.2684380040921763")) + (m_lms * AD("2.6097574006633715")) + (s_lms * AD("-0.3413193963102197"))
45
+ b_lrgb = (l_lms * AD("-0.004196086541837188"))+ (m_lms * AD("-0.7034186144594493")) + (s_lms * AD("1.7076147009309444"))
46
+
47
+ # Clamp LRGB values to be non-negative (as done in LmsToLrgb.rb)
48
+ # Using the pattern [AbcDecimal, Integer].max which relies on AbcDecimal's <=> coercion.
49
+ # AD(0) is AbcDecimal zero.
50
+ zero_ad = AD(0)
51
+ r_lrgb_clamped = [r_lrgb, zero_ad].max
52
+ g_lrgb_clamped = [g_lrgb, zero_ad].max
53
+ b_lrgb_clamped = [b_lrgb, zero_ad].max
54
+
55
+ # Step 5: LRGB to XYZ
56
+ # (x_xyz, y_xyz, z_xyz)
57
+ # Using matrix M_xyz_from_lrgb (sRGB D65)
58
+ x_xyz = (r_lrgb_clamped * AD("0.4124564")) + (g_lrgb_clamped * AD("0.3575761")) + (b_lrgb_clamped * AD("0.1804375"))
59
+ y_xyz = (r_lrgb_clamped * AD("0.2126729")) + (g_lrgb_clamped * AD("0.7151522")) + (b_lrgb_clamped * AD("0.0721750"))
60
+ z_xyz = (r_lrgb_clamped * AD("0.0193339")) + (g_lrgb_clamped * AD("0.1191920")) + (b_lrgb_clamped * AD("0.9503041"))
61
+
62
+ Color.new(ColorSpace.find(:xyz), [x_xyz, y_xyz, z_xyz], alpha)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,8 +1,26 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::SrgbToLrgb - sRGB to Linear RGB color space converter
2
+ #
3
+ # This converter transforms colors from the standard RGB (sRGB) color space to the linear RGB (LRGB) color space by removing gamma correction. The conversion process applies the inverse sRGB transfer function which uses different formulas for small and large values to convert from the gamma-corrected sRGB representation to linear light intensity values.
4
+ #
5
+ # Key features:
6
+ # - Implements the standard sRGB to linear RGB conversion algorithm with precise threshold handling
7
+ # - Converts gamma-corrected sRGB values to linear RGB values for accurate color calculations
8
+ # - Applies different transformation functions based on value magnitude (linear vs power function)
9
+ # - Maintains alpha channel transparency values during conversion
10
+ # - Uses AbcDecimal arithmetic for precise color science calculations
11
+ # - Validates input color space to ensure proper sRGB source data
12
+ #
13
+ # The linear RGB color space provides a linear relationship between stored numeric values and actual light intensity, making it essential for accurate color calculations and serving as an intermediate color space for many color transformations, particularly when converting between different color models.
2
14
 
3
15
  module Abachrome
4
16
  module Converters
5
17
  class SrgbToLrgb
18
+ # Converts a color from sRGB color space to linear RGB color space.
19
+ # This method performs gamma correction by linearizing each sRGB coordinate.
20
+ #
21
+ # @param srgb_color [Abachrome::Color] A color object in the sRGB color space
22
+ # @return [Abachrome::Color] A new color object in the linear RGB (LRGB) color space
23
+ # with the same alpha value as the input color
6
24
  def self.convert(srgb_color)
7
25
  r, g, b = srgb_color.coordinates.map { |c| to_linear(AbcDecimal(c)) }
8
26
 
@@ -13,6 +31,12 @@ module Abachrome
13
31
  )
14
32
  end
15
33
 
34
+ # Converts a sRGB component to its linear RGB equivalent.
35
+ # This conversion applies the appropriate gamma correction to transform an sRGB value
36
+ # into a linear RGB value.
37
+ #
38
+ # @param v [AbcDecimal, Numeric] The sRGB component value to convert (typically in range 0-1)
39
+ # @return [AbcDecimal] The corresponding linear RGB component value
16
40
  def self.to_linear(v)
17
41
  v_abs = v.abs
18
42
  v_sign = v.negative? ? -1 : 1
@@ -24,4 +48,4 @@ module Abachrome
24
48
  end
25
49
  end
26
50
  end
27
- end
51
+ end
@@ -1,8 +1,34 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::SrgbToOklab - sRGB to OKLAB color space converter
2
+ #
3
+ # This converter transforms colors from the standard RGB (sRGB) color space to the OKLAB color space
4
+ # through a two-step conversion process. The transformation first converts sRGB gamma-corrected
5
+ # values to linear RGB as an intermediate step, then applies the standard OKLAB transformation
6
+ # matrices to produce the final perceptually uniform OKLAB coordinates.
7
+ #
8
+ # Key features:
9
+ # - Two-stage conversion pipeline: sRGB → Linear RGB → OKLAB
10
+ # - Leverages existing SrgbToLrgb and LrgbToOklab converters for modular transformation
11
+ # - Removes gamma correction and applies perceptual uniformity transformations
12
+ # - Maintains alpha channel transparency values during conversion
13
+ # - Uses AbcDecimal arithmetic for precise color science calculations
14
+ # - Validates input color space to ensure proper sRGB source data
15
+ #
16
+ # The OKLAB color space provides better perceptual uniformity compared to traditional RGB spaces,
17
+ # making it ideal for color manipulation operations like blending, lightness adjustments, and
18
+ # gamut mapping where human visual perception accuracy is important. This converter enables
19
+ # seamless transformation from display-ready sRGB values to the scientifically accurate OKLAB
20
+ # representation for advanced color processing workflows.
2
21
 
3
22
  module Abachrome
4
23
  module Converters
5
24
  class SrgbToOklab
25
+ # Converts a color from sRGB color space to Oklab color space.
26
+ # The conversion happens in two steps:
27
+ # 1. sRGB is first converted to linear RGB
28
+ # 2. Linear RGB is then converted to Oklab
29
+ #
30
+ # @param srgb_color [Abachrome::Color] The color in sRGB color space to convert
31
+ # @return [Abachrome::Color] The converted color in Oklab color space
6
32
  def self.convert(srgb_color)
7
33
  # First convert sRGB to linear RGB
8
34
  lrgb_color = SrgbToLrgb.convert(srgb_color)
@@ -12,4 +38,4 @@ module Abachrome
12
38
  end
13
39
  end
14
40
  end
15
- end
41
+ end
@@ -1,4 +1,22 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::SrgbToOklch - sRGB to OKLCH color space converter
2
+ #
3
+ # This converter transforms colors from the standard RGB (sRGB) color space to the OKLCH color space
4
+ # through a two-step conversion process. The transformation first converts sRGB gamma-corrected
5
+ # values to OKLAB rectangular coordinates as an intermediate step, then applies cylindrical coordinate
6
+ # conversion to produce the final OKLCH values with lightness, chroma, and hue components.
7
+ #
8
+ # Key features:
9
+ # - Two-stage conversion pipeline: sRGB → OKLAB → OKLCH
10
+ # - Leverages existing SrgbToOklab and OklabToOklch converters for modular transformation
11
+ # - Converts display-ready RGB values to perceptually uniform cylindrical coordinates
12
+ # - Maintains alpha channel transparency values during conversion
13
+ # - Uses AbcDecimal arithmetic for precise color science calculations
14
+ # - Validates input color space to ensure proper sRGB source data
15
+ #
16
+ # The OKLCH color space provides an intuitive interface for color manipulation through its
17
+ # cylindrical coordinate system, making it ideal for hue adjustments, saturation modifications,
18
+ # and other color operations that benefit from polar coordinates while maintaining perceptual
19
+ # uniformity for natural-looking color transformations.
2
20
 
3
21
  require_relative "srgb_to_oklab"
4
22
  require_relative "oklab_to_oklch"
@@ -6,6 +24,13 @@ require_relative "oklab_to_oklch"
6
24
  module Abachrome
7
25
  module Converters
8
26
  class SrgbToOklch < Abachrome::Converters::Base
27
+ # Converts an sRGB color to OKLCH color space
28
+ #
29
+ # @param srgb_color [Abachrome::Color] The color in sRGB color space to convert
30
+ # @return [Abachrome::Color] The converted color in OKLCH color space
31
+ # @note This is a two-step conversion process: first from sRGB to OKLab, then from OKLab to OKLCH
32
+ # @see SrgbToOklab
33
+ # @see OklabToOklch
9
34
  def self.convert(srgb_color)
10
35
  # First convert sRGB to OKLab
11
36
  oklab_color = SrgbToOklab.convert(srgb_color)
@@ -15,4 +40,4 @@ module Abachrome
15
40
  end
16
41
  end
17
42
  end
18
- end
43
+ end
@@ -0,0 +1,30 @@
1
+ module Abachrome
2
+ module Converters
3
+ class XyzToLms < Abachrome::Converters::Base
4
+ # Converts a color from XYZ color space to LMS color space.
5
+ #
6
+ # This method implements the XYZ to LMS transformation using the standard
7
+ # transformation matrix. The LMS color space represents the response of
8
+ # the three types of cone cells in the human eye (Long, Medium, Short),
9
+ # while XYZ is the CIE 1931 color space that forms the basis for most
10
+ # other color space definitions.
11
+ #
12
+ # @param xyz_color [Abachrome::Color] The color in XYZ color space
13
+ # @raise [ArgumentError] If the input color is not in XYZ color space
14
+ # @return [Abachrome::Color] The resulting color in LMS color space with
15
+ # the same alpha as the input color
16
+ def self.convert(xyz_color)
17
+ raise_unless xyz_color, :xyz
18
+
19
+ x, y, z = xyz_color.coordinates.map { |_| AbcDecimal(_) }
20
+
21
+ # XYZ to LMS transformation matrix
22
+ l = (x * AD("0.8189330101")) + (y * AD("0.3618667424")) - (z * AD("0.1288597137"))
23
+ m = (x * AD("0.0329845436")) + (y * AD("0.9293118715")) + (z * AD("0.0361456387"))
24
+ s = (x * AD("0.0482003018")) + (y * AD("0.2643662691")) + (z * AD("0.6338517070"))
25
+
26
+ Color.new(ColorSpace.find(:lms), [l, m, s], xyz_color.alpha)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ module Abachrome
2
+ module Converters
3
+ class XyzToOklab < Abachrome::Converters::Base
4
+ # Converts a color from XYZ color space to OKLAB color space.
5
+ #
6
+ # This method implements the XYZ to OKLAB transformation by first
7
+ # converting XYZ coordinates to the intermediate LMS (Long, Medium, Short)
8
+ # color space, then applying the LMS to OKLAB transformation matrix.
9
+ #
10
+ # @param xyz_color [Abachrome::Color] The color in XYZ color space
11
+ # @raise [ArgumentError] If the input color is not in XYZ color space
12
+ # @return [Abachrome::Color] The resulting color in OKLAB color space with
13
+ # the same alpha as the input color
14
+ def self.convert(xyz_color)
15
+ raise_unless xyz_color, :xyz
16
+
17
+ x, y, z = xyz_color.coordinates.map { |_| AbcDecimal(_) }
18
+
19
+ # XYZ to LMS transformation matrix
20
+ l = (x * AD("0.8189330101")) + (y * AD("0.3618667424")) - (z * AD("0.1288597137"))
21
+ m = (x * AD("0.0329845436")) + (y * AD("0.9293118715")) + (z * AD("0.0361456387"))
22
+ s = (x * AD("0.0482003018")) + (y * AD("0.2643662691")) + (z * AD("0.6338517070"))
23
+
24
+ # Apply cube root transformation
25
+ l_ = AbcDecimal(l)**Rational(1, 3)
26
+ m_ = AbcDecimal(m)**Rational(1, 3)
27
+ s_ = AbcDecimal(s)**Rational(1, 3)
28
+
29
+ # LMS to OKLAB transformation matrix
30
+ lightness = (AD("0.2104542553") * l_) + (AD("0.793617785") * m_) - (AD("0.0040720468") * s_)
31
+ a = (AD("1.9779984951") * l_) - (AD("2.4285922050") * m_) + (AD("0.4505937099") * s_)
32
+ b = (AD("0.0259040371") * l_) + (AD("0.7827717662") * m_) - (AD("0.8086757660") * s_)
33
+
34
+ Color.new(ColorSpace.find(:oklab), [lightness, a, b], xyz_color.alpha)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ #
2
2
 
3
3
  module Abachrome
4
4
  module Gamut
@@ -69,4 +69,4 @@ module Abachrome
69
69
  end
70
70
  end
71
71
  end
72
- end
72
+ end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ #
2
2
 
3
3
  module Abachrome
4
4
  module Gamut
@@ -22,4 +22,4 @@ module Abachrome
22
22
 
23
23
  register(P3.new)
24
24
  end
25
- end
25
+ end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ #
2
2
 
3
3
  lib
4
4
  abachrome
@@ -20,4 +20,4 @@ module Abachrome
20
20
 
21
21
  register(Rec2020.new)
22
22
  end
23
- end
23
+ end
@@ -1,4 +1,22 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Gamut::SRGB - sRGB color gamut definition and validation
2
+ #
3
+ # This module defines the sRGB color gamut within the Abachrome color manipulation library.
4
+ # The sRGB gamut represents the range of colors that can be displayed on standard monitors
5
+ # and is the default color space for web content and most consumer displays. It uses the
6
+ # D65 white point and specific primary color coordinates that define the boundaries of
7
+ # reproducible colors in the sRGB color space.
8
+ #
9
+ # Key features:
10
+ # - Defines sRGB primary color coordinates for red, green, and blue
11
+ # - Uses D65 illuminant as the white point reference for proper color reproduction
12
+ # - Provides gamut boundary validation to ensure colors fall within displayable ranges
13
+ # - Implements color containment checking for RGB coordinate validation
14
+ # - Automatically registers with the global gamut registry for system-wide access
15
+ # - Serves as the foundation for sRGB color space conversions and display output
16
+ #
17
+ # The sRGB gamut is essential for ensuring colors are properly represented on standard
18
+ # displays and provides the color boundary definitions needed for gamut mapping operations
19
+ # when converting between different color spaces or preparing colors for web and print output.
2
20
 
3
21
  require_relative "base"
4
22
 
@@ -24,4 +42,4 @@ module Abachrome
24
42
 
25
43
  register(SRGB.new)
26
44
  end
27
- end
45
+ end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ #
2
2
 
3
3
  module Abachrome
4
4
  module Illuminants
@@ -30,4 +30,4 @@ module Abachrome
30
30
  end
31
31
  end
32
32
  end
33
- end
33
+ end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ #
2
2
 
3
3
  module Abachrome
4
4
  module Illuminants
@@ -28,4 +28,4 @@ module Abachrome
28
28
  end
29
29
  end
30
30
  end
31
- end
31
+ end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ #
2
2
 
3
3
  module Abachrome
4
4
  module Illuminants
@@ -24,4 +24,4 @@ module Abachrome
24
24
  end
25
25
  end
26
26
  end
27
- end
27
+ end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ #
2
2
 
3
3
  module Abachrome
4
4
  module Illuminants
@@ -32,4 +32,4 @@ module Abachrome
32
32
  end
33
33
  end
34
34
  end
35
- end
35
+ end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ #
2
2
 
3
3
  module Abachrome
4
4
  module Illuminants
@@ -24,4 +24,4 @@ module Abachrome
24
24
  end
25
25
  end
26
26
  end
27
- end
27
+ end