abachrome-float 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +3 -0
  3. data/.rubocop.yml +10 -0
  4. data/CHANGELOG.md +21 -0
  5. data/CLA.md +45 -0
  6. data/CODE-OF-CONDUCT.md +9 -0
  7. data/LICENSE +19 -0
  8. data/README.md +315 -0
  9. data/Rakefile +15 -0
  10. data/SECURITY.md +94 -0
  11. data/abachrome-float.gemspec +36 -0
  12. data/demos/ncurses/plasma.rb +124 -0
  13. data/devenv.lock +171 -0
  14. data/devenv.nix +52 -0
  15. data/devenv.yaml +8 -0
  16. data/lib/abachrome/color.rb +197 -0
  17. data/lib/abachrome/color_mixins/blend.rb +100 -0
  18. data/lib/abachrome/color_mixins/lighten.rb +90 -0
  19. data/lib/abachrome/color_mixins/spectral_mix.rb +70 -0
  20. data/lib/abachrome/color_mixins/to_colorspace.rb +107 -0
  21. data/lib/abachrome/color_mixins/to_grayscale.rb +87 -0
  22. data/lib/abachrome/color_mixins/to_lrgb.rb +121 -0
  23. data/lib/abachrome/color_mixins/to_oklab.rb +117 -0
  24. data/lib/abachrome/color_mixins/to_oklch.rb +110 -0
  25. data/lib/abachrome/color_mixins/to_srgb.rb +142 -0
  26. data/lib/abachrome/color_models/cmyk.rb +159 -0
  27. data/lib/abachrome/color_models/hsv.rb +49 -0
  28. data/lib/abachrome/color_models/lms.rb +38 -0
  29. data/lib/abachrome/color_models/oklab.rb +37 -0
  30. data/lib/abachrome/color_models/oklch.rb +91 -0
  31. data/lib/abachrome/color_models/rgb.rb +58 -0
  32. data/lib/abachrome/color_models/xyz.rb +31 -0
  33. data/lib/abachrome/color_models/yiq.rb +37 -0
  34. data/lib/abachrome/color_space.rb +199 -0
  35. data/lib/abachrome/converter.rb +117 -0
  36. data/lib/abachrome/converters/base.rb +128 -0
  37. data/lib/abachrome/converters/cmyk_to_srgb.rb +42 -0
  38. data/lib/abachrome/converters/lms_to_lrgb.rb +40 -0
  39. data/lib/abachrome/converters/lms_to_srgb.rb +27 -0
  40. data/lib/abachrome/converters/lms_to_xyz.rb +34 -0
  41. data/lib/abachrome/converters/lrgb_to_lms.rb +3 -0
  42. data/lib/abachrome/converters/lrgb_to_oklab.rb +57 -0
  43. data/lib/abachrome/converters/lrgb_to_srgb.rb +59 -0
  44. data/lib/abachrome/converters/lrgb_to_xyz.rb +33 -0
  45. data/lib/abachrome/converters/oklab_to_lms.rb +44 -0
  46. data/lib/abachrome/converters/oklab_to_lrgb.rb +71 -0
  47. data/lib/abachrome/converters/oklab_to_oklch.rb +56 -0
  48. data/lib/abachrome/converters/oklab_to_srgb.rb +46 -0
  49. data/lib/abachrome/converters/oklch_to_lrgb.rb +79 -0
  50. data/lib/abachrome/converters/oklch_to_oklab.rb +52 -0
  51. data/lib/abachrome/converters/oklch_to_srgb.rb +46 -0
  52. data/lib/abachrome/converters/oklch_to_xyz.rb +70 -0
  53. data/lib/abachrome/converters/srgb_to_cmyk.rb +64 -0
  54. data/lib/abachrome/converters/srgb_to_lrgb.rb +55 -0
  55. data/lib/abachrome/converters/srgb_to_oklab.rb +45 -0
  56. data/lib/abachrome/converters/srgb_to_oklch.rb +47 -0
  57. data/lib/abachrome/converters/srgb_to_yiq.rb +49 -0
  58. data/lib/abachrome/converters/xyz_to_lms.rb +34 -0
  59. data/lib/abachrome/converters/xyz_to_oklab.rb +42 -0
  60. data/lib/abachrome/converters/yiq_to_srgb.rb +47 -0
  61. data/lib/abachrome/gamut/base.rb +74 -0
  62. data/lib/abachrome/gamut/p3.rb +27 -0
  63. data/lib/abachrome/gamut/rec2020.rb +25 -0
  64. data/lib/abachrome/gamut/srgb.rb +49 -0
  65. data/lib/abachrome/illuminants/base.rb +35 -0
  66. data/lib/abachrome/illuminants/d50.rb +33 -0
  67. data/lib/abachrome/illuminants/d55.rb +29 -0
  68. data/lib/abachrome/illuminants/d65.rb +37 -0
  69. data/lib/abachrome/illuminants/d75.rb +29 -0
  70. data/lib/abachrome/named/css.rb +157 -0
  71. data/lib/abachrome/named/tailwind.rb +301 -0
  72. data/lib/abachrome/outputs/css.rb +119 -0
  73. data/lib/abachrome/palette.rb +244 -0
  74. data/lib/abachrome/palette_mixins/interpolate.rb +53 -0
  75. data/lib/abachrome/palette_mixins/resample.rb +61 -0
  76. data/lib/abachrome/palette_mixins/stretch_luminance.rb +72 -0
  77. data/lib/abachrome/parsers/css.rb +452 -0
  78. data/lib/abachrome/parsers/hex.rb +52 -0
  79. data/lib/abachrome/parsers/tailwind.rb +45 -0
  80. data/lib/abachrome/spectral.rb +276 -0
  81. data/lib/abachrome/to_abcd.rb +23 -0
  82. data/lib/abachrome/version.rb +7 -0
  83. data/lib/abachrome.rb +242 -0
  84. data/logo.png +0 -0
  85. data/logo.webp +0 -0
  86. data/security/assesments/2025-10-12-SECURITY_ASSESSMENT.md +53 -0
  87. data/security/vex.json +21 -0
  88. metadata +146 -0
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Illuminants
5
+ class D65 < Base
6
+ def self.x
7
+ 95.047
8
+ end
9
+
10
+ def self.y
11
+ 100.000
12
+ end
13
+
14
+ def self.z
15
+ 108.883
16
+ end
17
+
18
+ def self.white_point
19
+ [x, y, z]
20
+ end
21
+
22
+ def self.temperature
23
+ 6504
24
+ end
25
+
26
+ def self.name
27
+ "D65"
28
+ end
29
+
30
+ def self.description
31
+ "CIE Standard Illuminant D65 - represents average daylight with a correlated color temperature of 6504K"
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Illuminants
5
+ class D75 < Base
6
+ def x
7
+ 0.299
8
+ end
9
+
10
+ def y
11
+ 0.315
12
+ end
13
+
14
+ def z
15
+ 0.386
16
+ end
17
+
18
+ def temperature
19
+ 7500
20
+ end
21
+
22
+ def description
23
+ "North sky daylight / CIE standard illuminant D75"
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Named
5
+ module CSS
6
+ def self.aliceblue = [240, 248, 255]
7
+ def self.antiquewhite = [250, 235, 215]
8
+ def self.aqua = [0, 255, 255]
9
+ def self.aquamarine = [127, 255, 212]
10
+ def self.azure = [240, 255, 255]
11
+ def self.beige = [245, 245, 220]
12
+ def self.bisque = [255, 228, 196]
13
+ def self.black = [0, 0, 0]
14
+ def self.blanchedalmond = [255, 235, 205]
15
+ def self.blue = [0, 0, 255]
16
+ def self.blueviolet = [138, 43, 226]
17
+ def self.brown = [165, 42, 42]
18
+ def self.burlywood = [222, 184, 135]
19
+ def self.cadetblue = [95, 158, 160]
20
+ def self.chartreuse = [127, 255, 0]
21
+ def self.chocolate = [210, 105, 30]
22
+ def self.coral = [255, 127, 80]
23
+ def self.cornflowerblue = [100, 149, 237]
24
+ def self.cornsilk = [255, 248, 220]
25
+ def self.crimson = [220, 20, 60]
26
+ def self.cyan = [0, 255, 255]
27
+ def self.darkblue = [0, 0, 139]
28
+ def self.darkcyan = [0, 139, 139]
29
+ def self.darkgoldenrod = [184, 134, 11]
30
+ def self.darkgray = [169, 169, 169]
31
+ def self.darkgreen = [0, 100, 0]
32
+ def self.darkgrey = [169, 169, 169]
33
+ def self.darkkhaki = [189, 183, 107]
34
+ def self.darkmagenta = [139, 0, 139]
35
+ def self.darkolivegreen = [85, 107, 47]
36
+ def self.darkorange = [255, 140, 0]
37
+ def self.darkorchid = [153, 50, 204]
38
+ def self.darkred = [139, 0, 0]
39
+ def self.darksalmon = [233, 150, 122]
40
+ def self.darkseagreen = [143, 188, 143]
41
+ def self.darkslateblue = [72, 61, 139]
42
+ def self.darkslategray = [47, 79, 79]
43
+ def self.darkslategrey = [47, 79, 79]
44
+ def self.darkturquoise = [0, 206, 209]
45
+ def self.darkviolet = [148, 0, 211]
46
+ def self.deeppink = [255, 20, 147]
47
+ def self.deepskyblue = [0, 191, 255]
48
+ def self.dimgray = [105, 105, 105]
49
+ def self.dimgrey = [105, 105, 105]
50
+ def self.dodgerblue = [30, 144, 255]
51
+ def self.firebrick = [178, 34, 34]
52
+ def self.floralwhite = [255, 250, 240]
53
+ def self.forestgreen = [34, 139, 34]
54
+ def self.fuchsia = [255, 0, 255]
55
+ def self.gainsboro = [220, 220, 220]
56
+ def self.ghostwhite = [248, 248, 255]
57
+ def self.gold = [255, 215, 0]
58
+ def self.goldenrod = [218, 165, 32]
59
+ def self.gray = [128, 128, 128]
60
+ def self.green = [0, 128, 0]
61
+ def self.greenyellow = [173, 255, 47]
62
+ def self.grey = [128, 128, 128]
63
+ def self.honeydew = [240, 255, 240]
64
+ def self.hotpink = [255, 105, 180]
65
+ def self.indianred = [205, 92, 92]
66
+ def self.indigo = [75, 0, 130]
67
+ def self.ivory = [255, 255, 240]
68
+ def self.khaki = [240, 230, 140]
69
+ def self.lavender = [230, 230, 250]
70
+ def self.lavenderblush = [255, 240, 245]
71
+ def self.lawngreen = [124, 252, 0]
72
+ def self.lemonchiffon = [255, 250, 205]
73
+ def self.lightblue = [173, 216, 230]
74
+ def self.lightcoral = [240, 128, 128]
75
+ def self.lightcyan = [224, 255, 255]
76
+ def self.lightgoldenrodyellow = [250, 250, 210]
77
+ def self.lightgray = [211, 211, 211]
78
+ def self.lightgreen = [144, 238, 144]
79
+ def self.lightgrey = [211, 211, 211]
80
+ def self.lightpink = [255, 182, 193]
81
+ def self.lightsalmon = [255, 160, 122]
82
+ def self.lightseagreen = [32, 178, 170]
83
+ def self.lightskyblue = [135, 206, 250]
84
+ def self.lightslategray = [119, 136, 153]
85
+ def self.lightslategrey = [119, 136, 153]
86
+ def self.lightsteelblue = [176, 196, 222]
87
+ def self.lightyellow = [255, 255, 224]
88
+ def self.lime = [0, 255, 0]
89
+ def self.limegreen = [50, 205, 50]
90
+ def self.linen = [250, 240, 230]
91
+ def self.magenta = [255, 0, 255]
92
+ def self.maroon = [128, 0, 0]
93
+ def self.mediumaquamarine = [102, 205, 170]
94
+ def self.mediumblue = [0, 0, 205]
95
+ def self.mediumorchid = [186, 85, 211]
96
+ def self.mediumpurple = [147, 112, 219]
97
+ def self.mediumseagreen = [60, 179, 113]
98
+ def self.mediumslateblue = [123, 104, 238]
99
+ def self.mediumspringgreen = [0, 250, 154]
100
+ def self.mediumturquoise = [72, 209, 204]
101
+ def self.mediumvioletred = [199, 21, 133]
102
+ def self.midnightblue = [25, 25, 112]
103
+ def self.mintcream = [245, 255, 250]
104
+ def self.mistyrose = [255, 228, 225]
105
+ def self.moccasin = [255, 228, 181]
106
+ def self.navajowhite = [255, 222, 173]
107
+ def self.navy = [0, 0, 128]
108
+ def self.oldlace = [253, 245, 230]
109
+ def self.olive = [128, 128, 0]
110
+ def self.olivedrab = [107, 142, 35]
111
+ def self.orange = [255, 165, 0]
112
+ def self.orangered = [255, 69, 0]
113
+ def self.orchid = [218, 112, 214]
114
+ def self.palegoldenrod = [238, 232, 170]
115
+ def self.palegreen = [152, 251, 152]
116
+ def self.paleturquoise = [175, 238, 238]
117
+ def self.palevioletred = [219, 112, 147]
118
+ def self.papayawhip = [255, 239, 213]
119
+ def self.peachpuff = [255, 218, 185]
120
+ def self.peru = [205, 133, 63]
121
+ def self.pink = [255, 192, 203]
122
+ def self.plum = [221, 160, 221]
123
+ def self.powderblue = [176, 224, 230]
124
+ def self.purple = [128, 0, 128]
125
+ def self.red = [255, 0, 0]
126
+ def self.rosybrown = [188, 143, 143]
127
+ def self.royalblue = [65, 105, 225]
128
+ def self.saddlebrown = [139, 69, 19]
129
+ def self.salmon = [250, 128, 114]
130
+ def self.sandybrown = [244, 164, 96]
131
+ def self.seagreen = [46, 139, 87]
132
+ def self.seashell = [255, 245, 238]
133
+ def self.sienna = [160, 82, 45]
134
+ def self.silver = [192, 192, 192]
135
+ def self.skyblue = [135, 206, 235]
136
+ def self.slateblue = [106, 90, 205]
137
+ def self.slategray = [112, 128, 144]
138
+ def self.slategrey = [112, 128, 144]
139
+ def self.snow = [255, 250, 250]
140
+ def self.springgreen = [0, 255, 127]
141
+ def self.steelblue = [70, 130, 180]
142
+ def self.tan = [210, 180, 140]
143
+ def self.teal = [0, 128, 128]
144
+ def self.thistle = [216, 191, 216]
145
+ def self.tomato = [255, 99, 71]
146
+ def self.turquoise = [64, 224, 208]
147
+ def self.violet = [238, 130, 238]
148
+ def self.wheat = [245, 222, 179]
149
+ def self.white = [255, 255, 255]
150
+ def self.whitesmoke = [245, 245, 245]
151
+ def self.yellow = [255, 255, 0]
152
+ def self.yellowgreen = [154, 205, 50]
153
+ end
154
+ end
155
+ end
156
+
157
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
@@ -0,0 +1,301 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Named
5
+ module Tailwind
6
+ # Tailwind CSS v3 color palette
7
+ # Each color has shades from 50 to 950
8
+
9
+ COLORS = {
10
+ "slate" => {
11
+ "50" => [248, 250, 252],
12
+ "100" => [241, 245, 249],
13
+ "200" => [226, 232, 240],
14
+ "300" => [203, 213, 225],
15
+ "400" => [148, 163, 184],
16
+ "500" => [100, 116, 139],
17
+ "600" => [71, 85, 105],
18
+ "700" => [51, 65, 85],
19
+ "800" => [30, 41, 59],
20
+ "900" => [15, 23, 42],
21
+ "950" => [2, 6, 23]
22
+ },
23
+ "gray" => {
24
+ "50" => [249, 250, 251],
25
+ "100" => [243, 244, 246],
26
+ "200" => [229, 231, 235],
27
+ "300" => [209, 213, 219],
28
+ "400" => [156, 163, 175],
29
+ "500" => [107, 114, 128],
30
+ "600" => [75, 85, 99],
31
+ "700" => [55, 65, 81],
32
+ "800" => [31, 41, 55],
33
+ "900" => [17, 24, 39],
34
+ "950" => [3, 7, 18]
35
+ },
36
+ "zinc" => {
37
+ "50" => [250, 250, 250],
38
+ "100" => [244, 244, 245],
39
+ "200" => [228, 228, 231],
40
+ "300" => [212, 212, 216],
41
+ "400" => [161, 161, 170],
42
+ "500" => [113, 113, 122],
43
+ "600" => [82, 82, 91],
44
+ "700" => [63, 63, 70],
45
+ "800" => [39, 39, 42],
46
+ "900" => [24, 24, 27],
47
+ "950" => [9, 9, 11]
48
+ },
49
+ "neutral" => {
50
+ "50" => [250, 250, 250],
51
+ "100" => [245, 245, 245],
52
+ "200" => [229, 229, 229],
53
+ "300" => [212, 212, 212],
54
+ "400" => [163, 163, 163],
55
+ "500" => [115, 115, 115],
56
+ "600" => [82, 82, 82],
57
+ "700" => [64, 64, 64],
58
+ "800" => [38, 38, 38],
59
+ "900" => [23, 23, 23],
60
+ "950" => [10, 10, 10]
61
+ },
62
+ "stone" => {
63
+ "50" => [250, 250, 249],
64
+ "100" => [245, 245, 244],
65
+ "200" => [231, 229, 228],
66
+ "300" => [214, 211, 209],
67
+ "400" => [168, 162, 158],
68
+ "500" => [120, 113, 108],
69
+ "600" => [87, 83, 78],
70
+ "700" => [68, 64, 60],
71
+ "800" => [41, 37, 36],
72
+ "900" => [28, 25, 23],
73
+ "950" => [12, 10, 9]
74
+ },
75
+ "red" => {
76
+ "50" => [254, 242, 242],
77
+ "100" => [254, 226, 226],
78
+ "200" => [254, 202, 202],
79
+ "300" => [252, 165, 165],
80
+ "400" => [248, 113, 113],
81
+ "500" => [239, 68, 68],
82
+ "600" => [220, 38, 38],
83
+ "700" => [185, 28, 28],
84
+ "800" => [153, 27, 27],
85
+ "900" => [127, 29, 29],
86
+ "950" => [69, 10, 10]
87
+ },
88
+ "orange" => {
89
+ "50" => [255, 247, 237],
90
+ "100" => [255, 237, 213],
91
+ "200" => [254, 215, 170],
92
+ "300" => [253, 186, 116],
93
+ "400" => [251, 146, 60],
94
+ "500" => [249, 115, 22],
95
+ "600" => [234, 88, 12],
96
+ "700" => [194, 65, 12],
97
+ "800" => [154, 52, 18],
98
+ "900" => [124, 45, 18],
99
+ "950" => [67, 20, 7]
100
+ },
101
+ "amber" => {
102
+ "50" => [255, 251, 235],
103
+ "100" => [254, 243, 199],
104
+ "200" => [253, 230, 138],
105
+ "300" => [252, 211, 77],
106
+ "400" => [251, 191, 36],
107
+ "500" => [245, 158, 11],
108
+ "600" => [217, 119, 6],
109
+ "700" => [180, 83, 9],
110
+ "800" => [146, 64, 14],
111
+ "900" => [120, 53, 15],
112
+ "950" => [69, 26, 3]
113
+ },
114
+ "yellow" => {
115
+ "50" => [254, 252, 232],
116
+ "100" => [254, 249, 195],
117
+ "200" => [254, 240, 138],
118
+ "300" => [253, 224, 71],
119
+ "400" => [250, 204, 21],
120
+ "500" => [234, 179, 8],
121
+ "600" => [202, 138, 4],
122
+ "700" => [161, 98, 7],
123
+ "800" => [133, 77, 14],
124
+ "900" => [113, 63, 18],
125
+ "950" => [66, 32, 6]
126
+ },
127
+ "lime" => {
128
+ "50" => [247, 254, 231],
129
+ "100" => [236, 252, 203],
130
+ "200" => [217, 249, 157],
131
+ "300" => [190, 242, 100],
132
+ "400" => [163, 230, 53],
133
+ "500" => [132, 204, 22],
134
+ "600" => [101, 163, 13],
135
+ "700" => [77, 124, 15],
136
+ "800" => [63, 98, 18],
137
+ "900" => [54, 83, 20],
138
+ "950" => [26, 46, 5]
139
+ },
140
+ "green" => {
141
+ "50" => [240, 253, 244],
142
+ "100" => [220, 252, 231],
143
+ "200" => [187, 247, 208],
144
+ "300" => [134, 239, 172],
145
+ "400" => [74, 222, 128],
146
+ "500" => [34, 197, 94],
147
+ "600" => [22, 163, 74],
148
+ "700" => [21, 128, 61],
149
+ "800" => [22, 101, 52],
150
+ "900" => [20, 83, 45],
151
+ "950" => [5, 46, 22]
152
+ },
153
+ "emerald" => {
154
+ "50" => [236, 253, 245],
155
+ "100" => [209, 250, 229],
156
+ "200" => [167, 243, 208],
157
+ "300" => [110, 231, 183],
158
+ "400" => [52, 211, 153],
159
+ "500" => [16, 185, 129],
160
+ "600" => [5, 150, 105],
161
+ "700" => [4, 120, 87],
162
+ "800" => [6, 95, 70],
163
+ "900" => [6, 78, 59],
164
+ "950" => [2, 44, 34]
165
+ },
166
+ "teal" => {
167
+ "50" => [240, 253, 250],
168
+ "100" => [204, 251, 241],
169
+ "200" => [153, 246, 228],
170
+ "300" => [94, 234, 212],
171
+ "400" => [45, 212, 191],
172
+ "500" => [20, 184, 166],
173
+ "600" => [13, 148, 136],
174
+ "700" => [15, 118, 110],
175
+ "800" => [17, 94, 89],
176
+ "900" => [19, 78, 74],
177
+ "950" => [4, 47, 46]
178
+ },
179
+ "cyan" => {
180
+ "50" => [236, 254, 255],
181
+ "100" => [207, 250, 254],
182
+ "200" => [165, 243, 252],
183
+ "300" => [103, 232, 249],
184
+ "400" => [34, 211, 238],
185
+ "500" => [6, 182, 212],
186
+ "600" => [8, 145, 178],
187
+ "700" => [14, 116, 144],
188
+ "800" => [21, 94, 117],
189
+ "900" => [22, 78, 99],
190
+ "950" => [8, 51, 68]
191
+ },
192
+ "sky" => {
193
+ "50" => [240, 249, 255],
194
+ "100" => [224, 242, 254],
195
+ "200" => [186, 230, 253],
196
+ "300" => [125, 211, 252],
197
+ "400" => [56, 189, 248],
198
+ "500" => [14, 165, 233],
199
+ "600" => [2, 132, 199],
200
+ "700" => [3, 105, 161],
201
+ "800" => [7, 89, 133],
202
+ "900" => [12, 74, 110],
203
+ "950" => [8, 47, 73]
204
+ },
205
+ "blue" => {
206
+ "50" => [239, 246, 255],
207
+ "100" => [219, 234, 254],
208
+ "200" => [191, 219, 254],
209
+ "300" => [147, 197, 253],
210
+ "400" => [96, 165, 250],
211
+ "500" => [59, 130, 246],
212
+ "600" => [37, 99, 235],
213
+ "700" => [29, 78, 216],
214
+ "800" => [30, 64, 175],
215
+ "900" => [30, 58, 138],
216
+ "950" => [23, 37, 84]
217
+ },
218
+ "indigo" => {
219
+ "50" => [238, 242, 255],
220
+ "100" => [224, 231, 255],
221
+ "200" => [199, 210, 254],
222
+ "300" => [165, 180, 252],
223
+ "400" => [129, 140, 248],
224
+ "500" => [99, 102, 241],
225
+ "600" => [79, 70, 229],
226
+ "700" => [67, 56, 202],
227
+ "800" => [55, 48, 163],
228
+ "900" => [49, 46, 129],
229
+ "950" => [30, 27, 75]
230
+ },
231
+ "violet" => {
232
+ "50" => [245, 243, 255],
233
+ "100" => [237, 233, 254],
234
+ "200" => [221, 214, 254],
235
+ "300" => [196, 181, 253],
236
+ "400" => [167, 139, 250],
237
+ "500" => [139, 92, 246],
238
+ "600" => [124, 58, 237],
239
+ "700" => [109, 40, 217],
240
+ "800" => [91, 33, 182],
241
+ "900" => [76, 29, 149],
242
+ "950" => [46, 16, 101]
243
+ },
244
+ "purple" => {
245
+ "50" => [250, 245, 255],
246
+ "100" => [243, 232, 255],
247
+ "200" => [233, 213, 255],
248
+ "300" => [216, 180, 254],
249
+ "400" => [192, 132, 252],
250
+ "500" => [168, 85, 247],
251
+ "600" => [147, 51, 234],
252
+ "700" => [126, 34, 206],
253
+ "800" => [107, 33, 168],
254
+ "900" => [88, 28, 135],
255
+ "950" => [59, 7, 100]
256
+ },
257
+ "fuchsia" => {
258
+ "50" => [253, 244, 255],
259
+ "100" => [250, 232, 255],
260
+ "200" => [245, 208, 254],
261
+ "300" => [240, 171, 252],
262
+ "400" => [232, 121, 249],
263
+ "500" => [217, 70, 239],
264
+ "600" => [192, 38, 211],
265
+ "700" => [162, 28, 175],
266
+ "800" => [134, 25, 143],
267
+ "900" => [112, 26, 117],
268
+ "950" => [74, 4, 78]
269
+ },
270
+ "pink" => {
271
+ "50" => [253, 242, 248],
272
+ "100" => [252, 231, 243],
273
+ "200" => [251, 207, 232],
274
+ "300" => [249, 168, 212],
275
+ "400" => [244, 114, 182],
276
+ "500" => [236, 72, 153],
277
+ "600" => [219, 39, 119],
278
+ "700" => [190, 24, 93],
279
+ "800" => [157, 23, 77],
280
+ "900" => [131, 24, 67],
281
+ "950" => [80, 7, 36]
282
+ },
283
+ "rose" => {
284
+ "50" => [255, 241, 242],
285
+ "100" => [255, 228, 230],
286
+ "200" => [254, 205, 211],
287
+ "300" => [253, 164, 175],
288
+ "400" => [251, 113, 133],
289
+ "500" => [244, 63, 94],
290
+ "600" => [225, 29, 72],
291
+ "700" => [190, 18, 60],
292
+ "800" => [159, 18, 57],
293
+ "900" => [136, 19, 55],
294
+ "950" => [76, 5, 25]
295
+ }
296
+ }.freeze
297
+ end
298
+ end
299
+ end
300
+
301
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../color"
4
+ require_relative "../color_space"
5
+
6
+ module Abachrome
7
+ module Outputs
8
+ class CSS
9
+ def self.format(color, gamut: nil, companding: nil)
10
+ rgb_color = color.to_rgb
11
+ r, g, b = rgb_color.coordinates
12
+ a = rgb_color.alpha
13
+
14
+ # Apply gamut mapping if provided
15
+ r, g, b = gamut.map([r, g, b]) if gamut
16
+
17
+ # Apply companding if provided
18
+ if companding
19
+ r = companding.call(r)
20
+ g = companding.call(g)
21
+ b = companding.call(b)
22
+ end
23
+
24
+ # Convert to 8-bit values
25
+ r = (r * 255).round
26
+ g = (g * 255).round
27
+ b = (b * 255).round
28
+
29
+ # Format based on alpha value
30
+ return Kernel.format("rgba(%d, %d, %d, %.3f)", r, g, b, a) unless a == AbcDecimal.new("1.0")
31
+ return Kernel.format("#%02x%02x%02x", r, g, b) unless r == g && g == b
32
+
33
+ # Use shortened hex format for grayscale
34
+ hex = Kernel.format("%02x", r)
35
+ "##{hex}#{hex}#{hex}"
36
+ end
37
+
38
+ def self.format_hex(color, gamut: nil, companding: nil)
39
+ rgb_color = color.to_rgb
40
+ r, g, b = rgb_color.coordinates
41
+ a = rgb_color.alpha
42
+
43
+ # Apply gamut mapping if provided
44
+ r, g, b = gamut.map([r, g, b]) if gamut
45
+
46
+ # Apply companding if provided
47
+ if companding
48
+ r = companding.call(r)
49
+ g = companding.call(g)
50
+ b = companding.call(b)
51
+ end
52
+
53
+ r = (r * 255).round
54
+ g = (g * 255).round
55
+ b = (b * 255).round
56
+
57
+ if a == AbcDecimal.new("1.0")
58
+ Kernel.format("#%02x%02x%02x", r, g, b)
59
+ else
60
+ a = (a * 255).round
61
+ Kernel.format("#%02x%02x%02x%02x", r, g, b, a)
62
+ end
63
+ end
64
+
65
+ def self.format_rgb(color, gamut: nil, companding: nil)
66
+ rgb_color = color.to_rgb
67
+ r, g, b = rgb_color.coordinates
68
+ a = rgb_color.alpha
69
+
70
+ # Apply gamut mapping if provided
71
+ r, g, b = gamut.map([r, g, b]) if gamut
72
+
73
+ # Apply companding if provided
74
+ if companding
75
+ r = companding.call(r)
76
+ g = companding.call(g)
77
+ b = companding.call(b)
78
+ end
79
+
80
+ r = (r * 255).round
81
+ g = (g * 255).round
82
+ b = (b * 255).round
83
+
84
+ if a == AbcDecimal.new("1.0")
85
+ Kernel.format("rgb(%d, %d, %d)", r, g, b)
86
+ else
87
+ Kernel.format("rgba(%d, %d, %d, %.3f)", r, g, b, a)
88
+ end
89
+ end
90
+
91
+ def self.format_oklab(color, gamut: nil, companding: nil, precision: 3)
92
+ oklab_color = color.to_oklab
93
+ l, a, b = oklab_color.coordinates
94
+ alpha = oklab_color.alpha
95
+
96
+ # Apply gamut mapping if provided
97
+ l, a, b = gamut.map([l, a, b]) if gamut
98
+
99
+ # Apply companding if provided
100
+ if companding
101
+ l = companding.call(l)
102
+ a = companding.call(a)
103
+ b = companding.call(b)
104
+ end
105
+
106
+ # Format with appropriate precision
107
+ format_string = "%.#{precision}f %.#{precision}f %.#{precision}f"
108
+
109
+ if alpha == AbcDecimal.new("1.0")
110
+ Kernel.format("oklab(#{format_string})", l, a, b)
111
+ else
112
+ Kernel.format("oklab(#{format_string} / %.#{precision}f)", l, a, b, alpha)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.