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.
- checksums.yaml +4 -4
- data/.envrc +3 -0
- data/README.md +10 -0
- data/Rakefile +15 -0
- data/devenv.lock +88 -17
- data/devenv.nix +2 -1
- data/devenv.yaml +5 -12
- data/lib/abachrome/abc_decimal.rb +201 -7
- data/lib/abachrome/color.rb +88 -1
- data/lib/abachrome/color_mixins/blend.rb +53 -2
- data/lib/abachrome/color_mixins/lighten.rb +49 -2
- data/lib/abachrome/color_mixins/to_colorspace.rb +67 -2
- data/lib/abachrome/color_mixins/to_lrgb.rb +70 -2
- data/lib/abachrome/color_mixins/to_oklab.rb +67 -2
- data/lib/abachrome/color_mixins/to_oklch.rb +60 -2
- data/lib/abachrome/color_mixins/to_srgb.rb +77 -2
- data/lib/abachrome/color_models/hsv.rb +25 -2
- data/lib/abachrome/color_models/lms.rb +34 -0
- data/lib/abachrome/color_models/oklab.rb +19 -2
- data/lib/abachrome/color_models/oklch.rb +42 -2
- data/lib/abachrome/color_models/rgb.rb +28 -2
- data/lib/abachrome/color_models/xyz.rb +28 -0
- data/lib/abachrome/color_space.rb +88 -2
- data/lib/abachrome/converter.rb +56 -2
- data/lib/abachrome/converters/base.rb +69 -2
- data/lib/abachrome/converters/lms_to_lrgb.rb +36 -0
- data/lib/abachrome/converters/lms_to_srgb.rb +23 -0
- data/lib/abachrome/converters/lms_to_xyz.rb +30 -0
- data/lib/abachrome/converters/lrgb_to_lms.rb +0 -0
- data/lib/abachrome/converters/lrgb_to_oklab.rb +28 -2
- data/lib/abachrome/converters/lrgb_to_srgb.rb +27 -2
- data/lib/abachrome/converters/lrgb_to_xyz.rb +29 -0
- data/lib/abachrome/converters/oklab_to_lms.rb +41 -0
- data/lib/abachrome/converters/oklab_to_lrgb.rb +56 -29
- data/lib/abachrome/converters/oklab_to_oklch.rb +31 -2
- data/lib/abachrome/converters/oklab_to_srgb.rb +27 -2
- data/lib/abachrome/converters/oklch_to_lrgb.rb +66 -6
- data/lib/abachrome/converters/oklch_to_oklab.rb +29 -4
- data/lib/abachrome/converters/oklch_to_srgb.rb +26 -2
- data/lib/abachrome/converters/oklch_to_xyz.rb +66 -0
- data/lib/abachrome/converters/srgb_to_lrgb.rb +26 -2
- data/lib/abachrome/converters/srgb_to_oklab.rb +28 -2
- data/lib/abachrome/converters/srgb_to_oklch.rb +27 -2
- data/lib/abachrome/converters/xyz_to_lms.rb +30 -0
- data/lib/abachrome/converters/xyz_to_oklab.rb +38 -0
- data/lib/abachrome/gamut/base.rb +2 -2
- data/lib/abachrome/gamut/p3.rb +2 -2
- data/lib/abachrome/gamut/rec2020.rb +2 -2
- data/lib/abachrome/gamut/srgb.rb +20 -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 -158
- data/lib/abachrome/outputs/css.rb +2 -2
- data/lib/abachrome/palette.rb +112 -2
- data/lib/abachrome/palette_mixins/interpolate.rb +20 -2
- data/lib/abachrome/palette_mixins/resample.rb +2 -2
- data/lib/abachrome/palette_mixins/stretch_luminance.rb +2 -2
- data/lib/abachrome/parsers/hex.rb +2 -2
- data/lib/abachrome/to_abcd.rb +10 -2
- data/lib/abachrome/version.rb +2 -2
- data/lib/abachrome.rb +78 -2
- data/logo.png +0 -0
- data/logo.webp +0 -0
- metadata +26 -67
data/lib/abachrome/converter.rb
CHANGED
@@ -1,16 +1,53 @@
|
|
1
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|