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,16 +1,53 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converter - Color space conversion registry and orchestration system
2
+ #
3
+ # This module provides the central registry and conversion orchestration for transforming colors
4
+ # between different color spaces within the Abachrome library. It manages a registry of converter
5
+ # classes that handle transformations between specific color space pairs (e.g., sRGB to OKLAB,
6
+ # OKLCH to linear RGB) and provides the main conversion interface used throughout the library.
7
+ #
8
+ # Key features:
9
+ # - Global registry for color space converter registration with automatic discovery
10
+ # - Converter lookup and routing between source and target color spaces
11
+ # - Automatic registration of all converter classes found in the Converters namespace
12
+ # - Conversion orchestration that finds appropriate converters based on color model compatibility
13
+ # - Support for multi-step conversions through intermediate color spaces when needed
14
+ # - Integration with the ColorSpace system for proper color space identification
15
+ #
16
+ # The Converter system serves as the backbone for all color transformations in Abachrome,
17
+ # enabling seamless conversion between any registered color spaces while maintaining the
18
+ # precision and accuracy required for color science calculations. Converter classes follow
19
+ # a naming convention (FromSpaceToSpace) for automatic registration and discovery.
2
20
 
3
21
  module Abachrome
4
22
  class Converter
5
23
  class << self
24
+ # Returns the registry hash used to store color space converters.
25
+ #
26
+ # This method lazily initializes and returns the hash used internally to store
27
+ # converter mappings between different color spaces. This registry is a central
28
+ # repository that maps color space pairs to their appropriate conversion functions.
29
+ #
30
+ # @return [Hash] The converter registry hash, mapping color space pairs to converter functions
6
31
  def registry
7
32
  @registry ||= {}
8
33
  end
9
34
 
35
+ # Register a converter class for transforming colors between two specific color spaces.
36
+ #
37
+ # @param from_space [Symbol, String] The source color space identifier
38
+ # @param to_space [Symbol, String] The destination color space identifier
39
+ # @param converter_class [Class] The converter class that implements the transformation
40
+ # @return [Class] The registered converter class
10
41
  def register(from_space, to_space, converter_class)
11
42
  registry[[from_space.to_s, to_space.to_s]] = converter_class
12
43
  end
13
44
 
45
+ # Converts a color from its current color space to the specified target color space.
46
+ #
47
+ # @param color [Abachrome::Color] The color to convert
48
+ # @param to_space_name [String, Symbol] The name of the target color space
49
+ # @return [Abachrome::Color] The color converted to the target color space
50
+ # @raise [RuntimeError] If no converter is found between the source and target color models
14
51
  def convert(color, to_space_name)
15
52
  to_space = ColorSpace.find(to_space_name)
16
53
  return color if color.color_space == to_space
@@ -23,7 +60,19 @@ module Abachrome
23
60
  converter.convert(color)
24
61
  end
25
62
 
26
- # Automatically register all converters in the Converters namespace
63
+ # @api private
64
+ # @since 0.1.0
65
+ # @example
66
+ # converter = Abachrome::Converter.new
67
+ # converter.register_all_converters
68
+ #
69
+ # Automatically registers all converter classes found in the Converters namespace.
70
+ # The method iterates through constants in the Converters module, identifies classes
71
+ # with naming pattern "FromSpaceToSpace" (e.g., LrgbToOklab), extracts the source
72
+ # and destination color spaces from the class name, and registers the converter
73
+ # class for the corresponding color space conversion.
74
+ #
75
+ # @return [void]
27
76
  def register_all_converters
28
77
  Converters.constants.each do |const_name|
29
78
  const = Converters.const_get(const_name)
@@ -42,6 +91,11 @@ module Abachrome
42
91
 
43
92
  private
44
93
 
94
+ # Retrieves a converter function between two color spaces from the registry.
95
+ #
96
+ # @param from_space_name [String, Symbol] The source color space name
97
+ # @param to_space_name [String, Symbol] The target color space name
98
+ # @return [Proc, nil] The conversion function if registered, or nil if no converter exists for the specified color spaces
45
99
  def find_converter(from_space_name, to_space_name)
46
100
  registry[[from_space_name.to_s, to_space_name.to_s]]
47
101
  end
@@ -1,39 +1,100 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::Base - Abstract base class for color space converters
2
+ #
3
+ # This class provides the foundation for implementing color space conversion functionality
4
+ # within the Abachrome library. It defines the interface that all converter classes must
5
+ # implement and provides common validation and utility methods for color transformations.
6
+ #
7
+ # Key features:
8
+ # - Abstract conversion interface requiring subclasses to implement #convert method
9
+ # - Color model validation to ensure proper conversion compatibility
10
+ # - Converter registration and lookup system for managing conversion mappings
11
+ # - Source and target color space compatibility checking
12
+ # - Base functionality for building specific converter implementations
13
+ #
14
+ # All converter classes in the Abachrome system inherit from this base class and implement
15
+ # the specific mathematical transformations needed to convert colors between different
16
+ # color spaces such as sRGB, OKLAB, OKLCH, and linear RGB. The class follows a naming
17
+ # convention pattern (FromSpaceToSpace) for automatic registration and discovery.
2
18
 
3
19
  module Abachrome
4
20
  module Converters
5
21
  class Base
6
22
  attr_reader :from_space, :to_space
7
23
 
24
+ # Initialize a new converter between two color spaces.
25
+ #
26
+ # @param from_space [Abachrome::ColorSpace] The source color space to convert from
27
+ # @param to_space [Abachrome::ColorSpace] The target color space to convert to
28
+ # @return [Abachrome::Converters::Base] A new converter instance
8
29
  def initialize(from_space, to_space)
9
30
  @from_space = from_space
10
31
  @to_space = to_space
11
32
  end
12
33
 
34
+ # Converts a color from one color space to another.
35
+ #
36
+ # @abstract This is an abstract method that must be implemented by subclasses.
37
+ # @param color [Abachrome::Color] The color to convert
38
+ # @return [Abachrome::Color] The converted color
39
+ # @raise [NotImplementedError] If the subclass doesn't implement this method
13
40
  def convert(color)
14
41
  raise NotImplementedError, "Subclasses must implement #convert"
15
42
  end
16
43
 
44
+ # Validates that a color uses the expected color model.
45
+ #
46
+ # @param color [Abachrome::Color] The color object to check
47
+ # @param model [Symbol, String] The expected color model
48
+ # @raise [RuntimeError] If the color's model doesn't match the expected model
49
+ # @return [nil] If the color's model matches the expected model
17
50
  def self.raise_unless(color, model)
18
51
  return if color.color_space.color_model == model
19
52
 
20
53
  raise "#{color} is #{color.color_space.color_model}), expecting #{model}"
21
54
  end
22
55
 
56
+ # Determines if the converter can handle the given color.
57
+ #
58
+ # This method checks if the color's current color space matches
59
+ # the converter's source color space.
60
+ #
61
+ # @param color [Abachrome::Color] The color to check
62
+ # @return [Boolean] true if the converter can convert from the color's current color space,
63
+ # false otherwise
23
64
  def can_convert?(color)
24
65
  color.color_space == from_space
25
66
  end
26
67
 
68
+ # Register a converter class for transforming colors between two specific color spaces.
69
+ #
70
+ # @param from_space_id [Symbol] The identifier of the source color space
71
+ # @param to_space_id [Symbol] The identifier of the destination color space
72
+ # @param converter_class [Class] The converter class that handles the transformation
73
+ # @return [void]
27
74
  def self.register(from_space_id, to_space_id, converter_class)
28
75
  @converters ||= {}
29
76
  @converters[[from_space_id, to_space_id]] = converter_class
30
77
  end
31
78
 
79
+ # Find a converter for converting between color spaces.
80
+ #
81
+ # @param from_space_id [Symbol, String] The identifier of the source color space
82
+ # @param to_space_id [Symbol, String] The identifier of the destination color space
83
+ # @return [Converter, nil] The converter instance for the specified color spaces, or nil if no converter is found
32
84
  def self.find_converter(from_space_id, to_space_id)
33
85
  @converters ||= {}
34
86
  @converters[[from_space_id, to_space_id]]
35
87
  end
36
88
 
89
+ # Converts a color from its current color space to a target color space.
90
+ #
91
+ # This method finds the appropriate converter class for the given source and
92
+ # target color spaces and performs the conversion.
93
+ #
94
+ # @param color [Abachrome::Color] The color to convert
95
+ # @param to_space [Abachrome::ColorSpace] The target color space to convert to
96
+ # @return [Abachrome::Color] The converted color in the target color space
97
+ # @raise [ConversionError] If no converter is found for the given color spaces
37
98
  def self.convert(color, to_space)
38
99
  converter_class = find_converter(color.color_space.id, to_space.id)
39
100
  unless converter_class
@@ -47,6 +108,12 @@ module Abachrome
47
108
 
48
109
  private
49
110
 
111
+ # Validates if a color can be converted from its current color space.
112
+ # Raises an ArgumentError if the color's space doesn't match the expected source space.
113
+ #
114
+ # @param color [Abachrome::Color] The color object to validate
115
+ # @raise [ArgumentError] If the color cannot be converted from its current color space
116
+ # @return [nil] Returns nil if the color is valid for conversion
50
117
  def validate_color!(color)
51
118
  return if can_convert?(color)
52
119
 
@@ -54,4 +121,4 @@ module Abachrome
54
121
  end
55
122
  end
56
123
  end
57
- end
124
+ end
@@ -0,0 +1,36 @@
1
+ module Abachrome
2
+ module Converters
3
+ class LmsToLrgb < Abachrome::Converters::Base
4
+ # Converts a color from LMS color space to linear RGB color space.
5
+ #
6
+ # This method implements the final part of the OKLAB to linear RGB transformation,
7
+ # converting LMS (Long, Medium, Short) coordinates to linear RGB coordinates
8
+ # using the standard transformation matrix. The LMS color space represents
9
+ # the response of the three types of cone cells in the human eye.
10
+ #
11
+ # @param lms_color [Abachrome::Color] The color in LMS color space
12
+ # @raise [ArgumentError] If the input color is not in LMS color space
13
+ # @return [Abachrome::Color] The resulting color in linear RGB color space with
14
+ # the same alpha as the input color
15
+ def self.convert(lms_color)
16
+ raise_unless lms_color, :lms
17
+
18
+ l, m, s = lms_color.coordinates.map { |_| AbcDecimal(_) }
19
+
20
+ r = (l * AD("4.07674166134799")) +
21
+ (m * AD("-3.307711590408193")) +
22
+ (s * AD("0.230969928729428"))
23
+ g = (l * AD("-1.2684380040921763")) +
24
+ (m * AD("2.6097574006633715")) +
25
+ (s * AD("-0.3413193963102197"))
26
+ b = (l * AD("-0.004196086541837188")) +
27
+ (m * AD("-0.7034186144594493")) +
28
+ (s * AD("1.7076147009309444"))
29
+
30
+ output_coords = [r, g, b].map { |it| [it, 0].max }
31
+
32
+ Color.new(ColorSpace.find(:lrgb), output_coords, lms_color.alpha)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,23 @@
1
+ module Abachrome
2
+ module Converters
3
+ class LmsToSrgb < Abachrome::Converters::Base
4
+ # Converts a color from LMS color space to sRGB color space.
5
+ #
6
+ # This method implements a two-step conversion process:
7
+ # 1. First converts from LMS to linear RGB using the standard transformation matrix
8
+ # 2. Then converts from linear RGB to sRGB by applying gamma correction
9
+ #
10
+ # @param lms_color [Abachrome::Color] The color in LMS color space
11
+ # @raise [ArgumentError] If the input color is not in LMS color space
12
+ # @return [Abachrome::Color] The resulting color in sRGB color space with
13
+ # the same alpha as the input color
14
+ def self.convert(lms_color)
15
+ # First convert LMS to linear RGB
16
+ lrgb_color = LmsToLrgb.convert(lms_color)
17
+
18
+ # Then convert linear RGB to sRGB
19
+ LrgbToSrgb.convert(lrgb_color)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ module Abachrome
2
+ module Converters
3
+ class LmsToXyz < Abachrome::Converters::Base
4
+ # Converts a color from LMS color space to XYZ color space.
5
+ #
6
+ # This method implements the LMS to XYZ 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 lms_color [Abachrome::Color] The color in LMS color space
13
+ # @raise [ArgumentError] If the input color is not in LMS color space
14
+ # @return [Abachrome::Color] The resulting color in XYZ color space with
15
+ # the same alpha as the input color
16
+ def self.convert(lms_color)
17
+ raise_unless lms_color, :lms
18
+
19
+ l, m, s = lms_color.coordinates.map { |_| AbcDecimal(_) }
20
+
21
+ # LMS to XYZ transformation matrix
22
+ x = (l * AD("1.86006661")) - (m * AD("1.12948190")) + (s * AD("0.21989740"))
23
+ y = (l * AD("0.36122292")) + (m * AD("0.63881308")) - (s * AD("0.00000000"))
24
+ z = (l * AD("0.00000000")) - (m * AD("0.00000000")) + (s * AD("1.08906362"))
25
+
26
+ Color.new(ColorSpace.find(:xyz), [x, y, z], lms_color.alpha)
27
+ end
28
+ end
29
+ end
30
+ end
File without changes
@@ -1,8 +1,34 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::LrgbToOklab - Linear RGB to OKLAB color space converter
2
+ #
3
+ # This converter transforms colors from the linear RGB (LRGB) color space to the OKLAB color space
4
+ # using the standard OKLAB transformation matrices. The conversion process applies a series of
5
+ # matrix transformations and non-linear operations to accurately map linear RGB coordinates to
6
+ # the perceptually uniform OKLAB color space.
7
+ #
8
+ # Key features:
9
+ # - Implements the official OKLAB transformation algorithm with high-precision matrices
10
+ # - Converts linear RGB values through intermediate LMS color space representation
11
+ # - Applies cube root transformation for perceptual uniformity in the OKLAB space
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 linear RGB 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.
2
19
 
3
20
  module Abachrome
4
21
  module Converters
5
22
  class LrgbToOklab < Abachrome::Converters::Base
23
+ # Converts a color from linear RGB (LRGB) color space to OKLAB color space.
24
+ #
25
+ # This conversion applies a matrix transformation to the linear RGB values,
26
+ # followed by a non-linear transformation, then another matrix transformation
27
+ # to produce OKLAB coordinates.
28
+ #
29
+ # @param rgb_color [Abachrome::Color] A color in linear RGB (LRGB) color space
30
+ # @raise [ArgumentError] If the provided color is not in LRGB color space
31
+ # @return [Abachrome::Color] The converted color in OKLAB color space with the same alpha value as the input
6
32
  def self.convert(rgb_color)
7
33
  raise_unless rgb_color, :lrgb
8
34
 
@@ -24,4 +50,4 @@ module Abachrome
24
50
  end
25
51
  end
26
52
  end
27
- end
53
+ end
@@ -1,8 +1,25 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::LrgbToSrgb - Linear RGB to sRGB color space converter
2
+ #
3
+ # This converter transforms colors from the linear RGB (LRGB) color space to the standard RGB (sRGB) color space by applying gamma correction. The conversion process applies the sRGB transfer function which uses different formulas for small and large values to match the non-linear response characteristics of typical display devices.
4
+ #
5
+ # Key features:
6
+ # - Implements the standard sRGB gamma correction algorithm with precise threshold handling
7
+ # - Converts linear RGB values to gamma-corrected sRGB values for proper display representation
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 linear RGB source data
12
+ #
13
+ # The sRGB color space is the standard RGB color space for web content and most consumer displays, providing gamma correction that better matches human visual perception and display device characteristics compared to linear RGB values.
2
14
 
3
15
  module Abachrome
4
16
  module Converters
5
17
  class LrgbToSrgb < Abachrome::Converters::Base
18
+ # Converts a color from linear RGB to sRGB color space.
19
+ #
20
+ # @param lrgb_color [Abachrome::Color] The color in linear RGB color space to convert
21
+ # @return [Abachrome::Color] A new Color object in sRGB color space with the converted coordinates
22
+ # @raise [TypeError] If the provided color is not in linear RGB color space
6
23
  def self.convert(lrgb_color)
7
24
  raise_unless lrgb_color, :lrgb
8
25
  r, g, b = lrgb_color.coordinates.map { |c| to_srgb(AbcDecimal(c)) }
@@ -16,6 +33,14 @@ module Abachrome
16
33
  )
17
34
  end
18
35
 
36
+ # Converts a linear RGB value to standard RGB color space (sRGB) value.
37
+ #
38
+ # This method implements the standard linearization function used in the sRGB color space.
39
+ # For small values (≤ 0.0031308), a simple linear transformation is applied.
40
+ # For larger values, a power function with gamma correction is used.
41
+ #
42
+ # @param v [AbcDecimal] The linear RGB value to convert
43
+ # @return [AbcDecimal] The corresponding sRGB value, preserving the sign of the input
19
44
  def self.to_srgb(v)
20
45
  v_abs = v.abs
21
46
  v_sign = v.negative? ? -1 : 1
@@ -27,4 +52,4 @@ module Abachrome
27
52
  end
28
53
  end
29
54
  end
30
- end
55
+ end
@@ -0,0 +1,29 @@
1
+ module Abachrome
2
+ module Converters
3
+ class LrgbToXyz < Abachrome::Converters::Base
4
+ # Converts a color from linear RGB color space to XYZ color space.
5
+ #
6
+ # This method implements the linear RGB to XYZ transformation using the standard
7
+ # transformation matrix for the sRGB color space with D65 white point. The XYZ
8
+ # color space is the CIE 1931 color space that forms the basis for most other
9
+ # color space definitions and serves as a device-independent reference.
10
+ #
11
+ # @param lrgb_color [Abachrome::Color] The color in linear RGB color space
12
+ # @raise [ArgumentError] If the input color is not in linear RGB color space
13
+ # @return [Abachrome::Color] The resulting color in XYZ color space with
14
+ # the same alpha as the input color
15
+ def self.convert(lrgb_color)
16
+ raise_unless lrgb_color, :lrgb
17
+
18
+ r, g, b = lrgb_color.coordinates.map { |_| AbcDecimal(_) }
19
+
20
+ # Linear RGB to XYZ transformation matrix (sRGB/D65)
21
+ x = (r * AD("0.4124564")) + (g * AD("0.3575761")) + (b * AD("0.1804375"))
22
+ y = (r * AD("0.2126729")) + (g * AD("0.7151522")) + (b * AD("0.0721750"))
23
+ z = (r * AD("0.0193339")) + (g * AD("0.1191920")) + (b * AD("0.9503041"))
24
+
25
+ Color.new(ColorSpace.find(:xyz), [x, y, z], lrgb_color.alpha)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ module Abachrome
2
+ module Converters
3
+ class OklabToLms < Abachrome::Converters::Base
4
+ # Converts a color from OKLAB color space to LMS color space.
5
+ #
6
+ # This method implements the first part of the OKLAB to linear RGB transformation,
7
+ # converting OKLAB coordinates to the intermediate LMS (Long, Medium, Short) color space
8
+ # which represents the response of the three types of cone cells in the human eye.
9
+ #
10
+ # @param oklab_color [Abachrome::Color] The color in OKLAB color space
11
+ # @raise [ArgumentError] If the input color is not in OKLAB color space
12
+ # @return [Abachrome::Color] The resulting color in LMS color space with
13
+ # the same alpha as the input color
14
+ def self.convert(oklab_color)
15
+ raise_unless oklab_color, :oklab
16
+
17
+ l, a, b = oklab_color.coordinates.map { |_| AbcDecimal(_) }
18
+
19
+ l_ = AbcDecimal((l ) +
20
+ (AD("0.39633779217376785678") * a) +
21
+ (AD("0.21580375806075880339") * b))
22
+
23
+ m_ = AbcDecimal((l) -
24
+ (a * AD("-0.1055613423236563494")) +
25
+ (b * AD("-0.063854174771705903402")))
26
+
27
+ s_ = AbcDecimal((l) -
28
+ (a * AD("-0.089484182094965759684")) +
29
+ (b * AD("-1.2914855378640917399")))
30
+
31
+ # Apply cubic operation to convert from L'M'S' to LMS
32
+ l_lms = AbcDecimal(l_)**3
33
+ m_lms = AbcDecimal(m_)**3
34
+ s_lms = AbcDecimal(s_)**3
35
+
36
+ Color.new(ColorSpace.find(:lms), [l_lms, m_lms, s_lms], oklab_color.alpha)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -1,39 +1,66 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::OklabToLrgb - OKLAB to Linear RGB color space converter
2
+ #
3
+ # This converter transforms colors from the OKLAB color space to the linear RGB (LRGB) color space
4
+ # using the standard OKLAB transformation matrices. The conversion process first transforms
5
+ # OKLAB coordinates to the intermediate LMS (Long, Medium, Short) color space, then applies
6
+ # another matrix transformation to convert LMS coordinates to linear RGB coordinates.
7
+ #
8
+ # Key features:
9
+ # - Implements the official OKLAB inverse transformation algorithm with high-precision matrices
10
+ # - Converts OKLAB coordinates through intermediate LMS color space representation
11
+ # - Applies cubic transformation for perceptual uniformity in the OKLAB space
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 OKLAB source data
15
+ #
16
+ # The linear RGB color space provides a linear relationship between stored numeric values and
17
+ # actual light intensity, making it essential for accurate color calculations and serving as
18
+ # an intermediate color space for many color transformations, particularly when converting
19
+ # between different color models or preparing colors for display on standard monitors.
2
20
 
3
21
  module Abachrome
4
22
  module Converters
5
23
  class OklabToLrgb < Abachrome::Converters::Base
24
+ # Converts a color from OKLAB color space to linear RGB (LRGB) color space.
25
+ #
26
+ # This method performs a two-step conversion:
27
+ # 1. OKLAB to LMS (cone response space)
28
+ # 2. LMS to LRGB (linear RGB)
29
+ #
30
+ # @param oklab_color [Abachrome::Color] The color in OKLAB color space
31
+ # @raise [ArgumentError] If the input color is not in OKLAB color space
32
+ # @return [Abachrome::Color] The resulting color in linear RGB color space with
33
+ # the same alpha as the input color
6
34
  def self.convert(oklab_color)
7
35
  raise_unless oklab_color, :oklab
8
36
 
9
- l, a, b = oklab_color.coordinates.map { |_| AbcDecimal(_) }
10
-
11
- l_ = AbcDecimal((l * AD("0.99999999845051981432")) +
12
- (AD("0.39633779217376785678") * a) +
13
- (AD("0.21580375806075880339") * b))
14
-
15
- m_ = AbcDecimal((l * AD("1.0000000088817607767")) -
16
- (a * AD("0.1055613423236563494")) -
17
- (b * AD("0.063854174771705903402")))
18
- s_ = AbcDecimal((l * AD("1.000000054672410917")) -
19
- (a * AD("0.089484182094965759684")) -
20
- (b * AD("1.2914855378640917399")))
21
-
22
- l = AbcDecimal(l_)**3
23
- m = AbcDecimal(m_)**3
24
- s = AbcDecimal(s_)**3
25
-
26
- r = (l * AD("4.07674166134799")) -
27
- (m * AD("3.307711590408193")) +
28
- (s * AD("0.230969928729428"))
29
- g = (l * AD("-1.2684380040921763")) +
30
- (m * AD("2.6097574006633715")) -
31
- (s * AD("0.3413193963102197"))
32
- b = (l * AD("-0.004196086541837188")) -
33
- (m * AD("0.7034186144594493")) +
34
- (s * AD("1.7076147009309444"))
35
-
36
- output_coords = [r, g, b].map { |it| [it, 0].max }
37
+ l_ok, a_ok, b_ok = oklab_color.coordinates.map { |_| AbcDecimal(_) }
38
+
39
+ # Step 1: OKLAB to L'M'S' (cone responses, non-linear)
40
+ # These are the M_lms_prime_from_oklab matrix operations.
41
+ l_prime = AbcDecimal(l_ok + (AD("0.39633779217376785678") * a_ok) + (AD("0.21580375806075880339") * b_ok))
42
+ m_prime = AbcDecimal(l_ok - (a_ok * AD("0.1055613423236563494")) - (b_ok * AD("0.063854174771705903402"))) # Note: original OklabToLms had + (b * AD("-0.063..."))
43
+ s_prime = AbcDecimal(l_ok - (a_ok * AD("0.089484182094965759684")) - (b_ok * AD("1.2914855378640917399"))) # Note: original OklabToLms had + (b * AD("-1.291..."))
44
+
45
+
46
+ # Step 2: L'M'S' to LMS (cubing)
47
+ l_lms = l_prime**3
48
+ m_lms = m_prime**3
49
+ s_lms = s_prime**3
50
+
51
+ # Step 3: LMS to LRGB
52
+ # Using matrix M_lrgb_from_lms (OKLAB specific)
53
+ r_lrgb = (l_lms * AD("4.07674166134799")) + (m_lms * AD("-3.307711590408193")) + (s_lms * AD("0.230969928729428"))
54
+ g_lrgb = (l_lms * AD("-1.2684380040921763")) + (m_lms * AD("2.6097574006633715")) + (s_lms * AD("-0.3413193963102197"))
55
+ b_lrgb = (l_lms * AD("-0.004196086541837188"))+ (m_lms * AD("-0.7034186144594493")) + (s_lms * AD("1.7076147009309444"))
56
+
57
+ # Clamp LRGB values to be non-negative (as done in LmsToLrgb.rb)
58
+ # It's also common to clamp to [0, 1] range after conversion from a wider gamut space
59
+ # For LRGB, often just ensuring non-negative is done, and further clamping happens
60
+ # when converting to sRGB or other display spaces.
61
+ # Here, we'll ensure non-negative as per LmsToLrgb.
62
+ output_coords = [r_lrgb, g_lrgb, b_lrgb].map { |it| [it, AD(0)].max }
63
+
37
64
 
38
65
  Color.new(ColorSpace.find(:lrgb), output_coords, oklab_color.alpha)
39
66
  end
@@ -1,8 +1,37 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::OklabToOklch - OKLAB to OKLCH color space converter
2
+ #
3
+ # This converter transforms colors from the OKLAB color space to the OKLCH color space
4
+ # using cylindrical coordinate conversion. The transformation converts the rectangular
5
+ # coordinates (L, a, b) to cylindrical coordinates (L, C, h) where lightness remains
6
+ # unchanged, chroma is calculated as the Euclidean distance in the a-b plane, and hue
7
+ # is calculated as the angle in the a-b plane expressed in degrees.
8
+ #
9
+ # Key features:
10
+ # - Converts OKLAB rectangular coordinates to OKLCH cylindrical coordinates
11
+ # - Preserves lightness component unchanged during conversion
12
+ # - Calculates chroma as sqrt(a² + b²) for colorfulness representation
13
+ # - Computes hue angle using atan2 function and normalizes to 0-360 degree range
14
+ # - Maintains alpha channel transparency values during conversion
15
+ # - Uses AbcDecimal arithmetic for precise color science calculations
16
+ # - Validates input color space to ensure proper OKLAB source data
17
+ #
18
+ # The OKLCH color space provides an intuitive interface for color manipulation through
19
+ # its cylindrical coordinate system, making it ideal for hue adjustments, saturation
20
+ # modifications, and other color operations that benefit from polar coordinates.
2
21
 
3
22
  module Abachrome
4
23
  module Converters
5
24
  class OklabToOklch < Abachrome::Converters::Base
25
+ # Converts a color from OKLAB color space to OKLCH color space.
26
+ # The method performs a mathematical transformation from the rectangular
27
+ # coordinates (L, a, b) to cylindrical coordinates (L, C, h), where:
28
+ # - L (lightness) remains the same
29
+ # - C (chroma) is calculated as the Euclidean distance from the origin in the a-b plane
30
+ # - h (hue) is calculated as the angle in the a-b plane
31
+ #
32
+ # @param oklab_color [Abachrome::Color] A color in the OKLAB color space
33
+ # @raise [ArgumentError] If the provided color is not in OKLAB color space
34
+ # @return [Abachrome::Color] The equivalent color in OKLCH color space with the same alpha value
6
35
  def self.convert(oklab_color)
7
36
  raise_unless oklab_color, :oklab
8
37
 
@@ -20,4 +49,4 @@ module Abachrome
20
49
  end
21
50
  end
22
51
  end
23
- end
52
+ end
@@ -1,8 +1,33 @@
1
- # frozen_string_literal: true
1
+ # Abachrome::Converters::OklabToSrgb - OKLAB to sRGB color space converter
2
+ #
3
+ # This converter transforms colors from the OKLAB color space to the standard RGB (sRGB) color space
4
+ # through a two-step conversion process. The transformation first converts OKLAB coordinates to
5
+ # linear RGB as an intermediate step, then applies gamma correction to produce the final sRGB
6
+ # values suitable for display on standard monitors and web applications.
7
+ #
8
+ # Key features:
9
+ # - Two-stage conversion pipeline: OKLAB → Linear RGB → sRGB
10
+ # - Leverages existing OklabToLrgb and LrgbToSrgb converters for modular transformation
11
+ # - Maintains alpha channel transparency values during conversion
12
+ # - Applies proper gamma correction for display-ready color values
13
+ # - Uses AbcDecimal arithmetic for precise color science calculations
14
+ # - Validates input color space to ensure proper OKLAB source data
15
+ #
16
+ # The sRGB color space is the standard RGB color space for web content and most consumer
17
+ # displays, providing gamma-corrected values that properly represent colors on typical
18
+ # display devices while maintaining compatibility with web standards and digital media formats.
2
19
 
3
20
  module Abachrome
4
21
  module Converters
5
22
  class OklabToSrgb < Abachrome::Converters::Base
23
+ # Converts a color from the Oklab color space to the sRGB color space.
24
+ # This conversion is performed in two steps:
25
+ # 1. First converts from Oklab to linear RGB
26
+ # 2. Then converts from linear RGB to sRGB
27
+ #
28
+ # @param oklab_color [Color] A color in the Oklab color space
29
+ # @raise [ArgumentError] If the provided color is not in the Oklab color space
30
+ # @return [Color] The converted color in the sRGB color space
6
31
  def self.convert(oklab_color)
7
32
  raise_unless oklab_color, :oklab
8
33
 
@@ -14,4 +39,4 @@ module Abachrome
14
39
  end
15
40
  end
16
41
  end
17
- end
42
+ end