Move all Cie XYZ to dedicated class as this is not used in direct frontend convert

Clean up old Colors class with calling new class calls

Test all and set phpstan deprecated messages

Add all missing convert functions for oklab/cielab/oklch/cielch calls

Prepare for test run creation
This commit is contained in:
Clemens Schwaighofer
2024-11-12 18:53:18 +09:00
parent 921b9cb3d9
commit 00821bd5ea
12 changed files with 1446 additions and 348 deletions

View File

@@ -31,6 +31,36 @@ $log = new CoreLibs\Logging\Logging([
]);
$color_class = 'CoreLibs\Convert\Colors';
/**
* print out a color block with info
*
* @param string $color
* @param string $text
* @param string $text_add
* @return string
*/
function display(string $color, string $text, string $text_add): string
{
$css = 'margin:5px;padding:50px;'
. 'width:10%;'
. 'text-align:center;'
. 'color:white;text-shadow: 0 0 5px black;font-weight:bold;';
$template = <<<HTML
<div style="background-color:{COLOR};{CSS}">
{TEXT}
</div>
HTML;
return str_replace(
["{COLOR}", "{TEXT}", "{CSS}"],
[
$color,
$text . ($text_add ? '<br>' . $text_add : ''),
$css
],
$template
);
}
$PAGE_NAME = 'TEST CLASS: CONVERT COLORS';
print "<!DOCTYPE html>";
print "<html><head><title>" . $PAGE_NAME . "</title></head>";
@@ -133,14 +163,20 @@ $oklab = Color::rgbToOkLab(Coordinates\RGB::__constructFromArray([
0
]));
print "OkLab: " . DgS::printAr($oklab) . "<br>";
print display($oklab->toCssString(), $oklab->toCssString(), 'Oklab');
$rgb = Color::okLabToRgb($oklab);
print "OkLab -> RGB: " . DgS::printAr($rgb) . "<br>";
print display($rgb->toCssString(), $rgb->toCssString(), 'OkLab to RGB');
$rgb = Coordinates\RGB::__constructFromArray([250, 100, 10])->toLinear();
print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
$rgb = Coordinates\RGB::__constructFromArray([0, 0, 0])->toLinear();
print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
$cie_lab = Color::okLabToLab($oklab);
print "CieLab: " . DgS::printAr($cie_lab) . "<br>";
print display($cie_lab->toCssString(), $cie_lab->toCssString(), 'OkLab to Cie Lab');
print "</body></html>";

View File

@@ -0,0 +1,344 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/11/12
* DESCRIPTION:
* CIE XYZ color space conversion
* This for various interims work
* none public
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color;
use CoreLibs\Convert\Math;
use CoreLibs\Convert\Color\Coordinates\RGB;
use CoreLibs\Convert\Color\Coordinates\Lab;
use CoreLibs\Convert\Color\Coordinates\XYZ;
class CieXyz
{
// MARK: public wrapper functions
/**
* Convert from RGB to OkLab
* via xyz D65
*
* @param RGB $rgb
* @return Lab
*/
public static function rgbViaXyzD65ToOkLab(RGB $rgb): Lab
{
return self::xyzD65ToOkLab(
self::linRgbToXyzD65($rgb)
);
}
/**
* Convet from OkLab to RGB
* via xyz D65
*
* @param Lab $lab
* @return RGB
*/
public static function okLabViaXyzD65ToRgb(Lab $lab): RGB
{
return self::xyzD65ToLinRgb(
self::okLabToXyzD65($lab)
)->fromLinear();
}
/**
* Convert RGB to CIE Lab
* via xyz D65 to xyz D50
*
* @param RGB $rgb
* @return Lab
*/
public static function rgbViaXyzD65ViaXyzD50ToLab(RGB $rgb): Lab
{
return self::xyzD50ToLab(
self::xyzD65ToXyzD50(
self::linRgbToXyzD65($rgb)
)
);
}
/**
* Convert CIE Lab to RGB
* via xyz D50 to xyz D65
*
* @param Lab $lab
* @return RGB
*/
public static function labViaXyzD50ViaXyzD65ToRgb(Lab $lab): RGB
{
return self::xyzD65ToLinRgb(
self::xyzD50ToXyxD65(
self::labToXyzD50($lab)
)
)->fromLinear();
}
/**
* Undocumented function
*
* @param Lab $lab
* @return Lab
*/
public static function okLabViaXyzD65ViaXyzD50ToLab(Lab $lab): Lab
{
return self::xyzD50ToLab(
self::xyzD65ToXyzD50(
self::okLabToXyzD65($lab)
)
);
}
/**
* Undocumented function
*
* @param Lab $lab
* @return Lab
*/
public static function labViaXyzD50ViaXyzD65ToOkLab(Lab $lab): Lab
{
return self::xyzD65ToOkLab(
self::xyzD50ToXyxD65(
self::labToXyzD50($lab)
)
);
}
// MARK: xyzD65 <-> xyzD50
/**
* xyzD65 to xyzD50 whitepoint
*
* @param XYZ $xyz
* @return XYZ
*/
private static function xyzD65ToXyzD50(XYZ $xyz): XYZ
{
return XYZ::__constructFromArray(Math::multiplyMatrices(
a: [
[1.0479298208405488, 0.022946793341019088, -0.05019222954313557],
[0.029627815688159344, 0.990434484573249, -0.01707382502938514],
[-0.009243058152591178, 0.015055144896577895, 0.7518742899580008],
],
b: $xyz->returnAsArray(),
), whitepoint: 'D50');
}
/**
* xyzD50 to xyzD65 whitepoint
*
* @param XYZ $xyz
* @return XYZ
*/
private static function xyzD50ToXyxD65(XYZ $xyz): XYZ
{
return XYZ::__constructFromArray(Math::multiplyMatrices(
a: [
[0.9554734527042182, -0.023098536874261423, 0.0632593086610217],
[-0.028369706963208136, 1.0099954580058226, 0.021041398966943008],
[0.012314001688319899, -0.020507696433477912, 1.3303659366080753],
],
b: $xyz->returnAsArray()
), whitepoint: 'D65');
}
// MARK: xyzD50 <-> Lab
/**
* Convert xyzD50 to Lab (Cie)
*
* @param XYZ $xyz
* @return Lab
*/
private static function xyzD50ToLab(XYZ $xyz): Lab
{
$_xyz = $xyz->returnAsArray();
$d50 = [
0.3457 / 0.3585,
1.00000,
(1.0 - 0.3457 - 0.3585) / 0.3585,
];
$a = 216 / 24389;
$b = 24389 / 27;
$_xyz = array_map(
fn ($k, $v) => $v / $d50[$k],
array_keys($_xyz),
array_values($_xyz),
);
$f = array_map(
fn ($v) => (($v > $a) ?
pow($v, 1 / 3) :
(($b * $v + 16) / 116)
),
$_xyz,
);
return Lab::__constructFromArray([
(116 * $f[1]) - 16,
500 * ($f[0] - $f[1]),
200 * ($f[1] - $f[2]),
], colorspace: 'CIELab');
}
/**
* Convert Lab (Cie) to xyz D50
*
* @param Lab $lab
* @return XYZ
*/
private static function labToXyzD50(Lab $lab): XYZ
{
$_lab = $lab->returnAsArray();
$a = 24389 / 27;
$b = 216 / 24389;
$f = [];
$f[1] = ($_lab[0] + 16) / 116;
$f[0] = $_lab[1] / 500 + $f[1];
$f[2] = $f[1] - $_lab[2] / 200;
$xyz = [
// x
pow($f[0], 3) > $b ?
pow($f[0], 3) :
(116 * $f[0] - 16) / $a,
// y
$_lab[0] > $a * $b ?
pow(($_lab[0] + 16) / 116, 3) :
$_lab[0] / $a,
// z
pow($f[2], 3) > $b ?
pow($f[2], 3) :
(116 * $f[2] - 16) / $a,
];
$d50 = [
0.3457 / 0.3585,
1.00000,
(1.0 - 0.3457 - 0.3585) / 0.3585,
];
return XYZ::__constructFromArray(
array_map(
fn ($k, $v) => $v * $d50[$k],
array_keys($xyz),
array_values($xyz),
),
whitepoint: 'D50'
);
}
// MARK: xyzD65 <-> (linear)RGB
/**
* convert linear RGB to xyz D65
* if rgb is not flagged linear, it will be auto converted
*
* @param RGB $rgb
* @return XYZ
*/
private static function linRgbToXyzD65(RGB $rgb): XYZ
{
// if not linear, convert to linear
if (!$rgb->linear) {
$rgb->toLinear();
}
return XYZ::__constructFromArray(Math::multiplyMatrices(
[
[0.41239079926595934, 0.357584339383878, 0.1804807884018343],
[0.21263900587151027, 0.715168678767756, 0.07219231536073371],
[0.01933081871559182, 0.11919477979462598, 0.9505321522496607],
],
$rgb->returnAsArray()
), whitepoint: 'D65');
}
/**
* Convert xyz D65 to linear RGB
*
* @param XYZ $xyz
* @return RGB
*/
private static function xyzD65ToLinRgb(XYZ $xyz): RGB
{
// xyz D65 to linrgb
return RGB::__constructFromArray(Math::multiplyMatrices(
a : [
[ 3.2409699419045226, -1.537383177570094, -0.4986107602930034 ],
[ -0.9692436362808796, 1.8759675015077202, 0.04155505740717559 ],
[ 0.05563007969699366, -0.20397695888897652, 1.0569715142428786 ],
],
b : $xyz->returnAsArray()
), linear: true);
}
// MARK: xyzD65 <-> OkLab
/**
* xyz D65 to OkLab
*
* @param XYZ $xyz
* @return Lab
*/
private static function xyzD65ToOkLab(XYZ $xyz): Lab
{
return Lab::__constructFromArray(Math::multiplyMatrices(
[
[0.2104542553, 0.7936177850, -0.0040720468],
[1.9779984951, -2.4285922050, 0.4505937099],
[0.0259040371, 0.7827717662, -0.8086757660],
],
array_map(
callback: fn ($v) => pow($v, 1 / 3),
array: Math::multiplyMatrices(
a: [
[0.8190224432164319, 0.3619062562801221, -0.12887378261216414],
[0.0329836671980271, 0.9292868468965546, 0.03614466816999844],
[0.048177199566046255, 0.26423952494422764, 0.6335478258136937],
],
b: $xyz->returnAsArray(),
),
)
), colorspace: 'OkLab');
}
/**
* xyz D65 to OkLab
*
* @param Lab $lab
* @return XYZ
*/
private static function okLabToXyzD65(Lab $lab): XYZ
{
return XYZ::__constructFromArray(Math::multiplyMatrices(
a: [
[1.2268798733741557, -0.5578149965554813, 0.28139105017721583],
[-0.04057576262431372, 1.1122868293970594, -0.07171106666151701],
[-0.07637294974672142, -0.4214933239627914, 1.5869240244272418],
],
b: array_map(
callback: fn ($v) => is_numeric($v) ? $v ** 3 : 0,
array: Math::multiplyMatrices(
a: [
[0.99999999845051981432, 0.39633779217376785678, 0.21580375806075880339],
[1.0000000088817607767, -0.1055613423236563494, -0.063854174771705903402],
[1.0000000546724109177, -0.089484182094965759684, -1.2914855378640917399],
],
// Divide $lightness by 100 to convert from CSS OkLab
b: $lab->returnAsArray(),
),
),
), whitepoint: 'D65');
}
}
// __END__

View File

@@ -9,38 +9,79 @@
* We convert between color cooradinates and color spaces
* as seen in the list below
*
* | | RGB | Oklab | Cie
* | | | HSB | | | | | | |
* | | RGB | HSV | HSL | HWB | OkLab | OkLch | CieLab | CieLch |
* -------+-----+-----+-----+-----+-------+-------+--------+--------+
* RGB | - | o | o | o | o | o | | |
* HSB/HB | o | - | o | o | | | | |
* HSL | o | o | - | o | o | o | | |
* HWB | o | o | o | - | | | | |
* OkLab | o | | o | | - | | | |
* OkLch | o | | o | | | - | | |
* CieLab | | | | | | | - | |
* CieLch | | | | | | | | - |
* | | RGB | Oklab | CieLab |
* | | | HSB | | | | | | |
* | | RGB | HSV | HSL | HWB | OkLab | OkLch | CieLab | CieLch |
* --------+-----+-----+-----+-----+-------+-------+--------+--------+
* RGB | - | o | o | o | o | o | o | o |
* HSB/HSV | o | - | o | o | o | o | o | o |
* HSL | o | o | - | o | o | o | o | o |
* HWB | o | o | o | - | o | o | o | o |
* OkLab | o | o | o | o | - | o | o | o |
* OkLch | o | o | o | o | o | - | o | o |
* CieLab | o | o | o | o | o | o | - | o |
* CieLch | o | o | o | o | o | o | o | - |
*
* All color coordinates are classes
* The data can then be converted to a CSS string
*
* CieXyz Class
* Not theat xyz (CIEXYZ) does not have its own conversion as it is not used in web
* applications
* Also XYZ has two different coordinate systems for the D50 an D65 whitepoint
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color;
use CoreLibs\Convert\Math;
use CoreLibs\Convert\Color\Coordinates\RGB;
use CoreLibs\Convert\Color\Coordinates\HSL;
use CoreLibs\Convert\Color\Coordinates\HSB;
use CoreLibs\Convert\Color\Coordinates\HWB;
use CoreLibs\Convert\Color\Coordinates\LCH;
use CoreLibs\Convert\Color\Coordinates\Lab;
use CoreLibs\Convert\Color\Coordinates\XYZD65;
class Color
{
// MARK: general lab/lch
/**
* general Lab to LCH convert
*
* @param Lab $lab
* @return array{0:float,1:float,2:float} LCH values as array
*/
private static function __labToLch(Lab $lab): array
{
// cieLab to cieLch
$a = $lab->a;
$b = $lab->b;
$hue = atan2($b, $a) * 180 / pi();
return [
$lab->L,
sqrt($a ** 2 + $b ** 2),
$hue >= 0 ? $hue : $hue + 360,
];
}
/**
* general LCH to Lab convert
*
* @param LCH $lch
* @return array{0:float,1:float,2:float} Lab values as array
*/
private static function __lchToLab(LCH $lch): array
{
return [
$lch->L,
$lch->C * cos($lch->H * pi() / 180), // a
$lch->C * sin($lch->H * pi() / 180), // b
];
}
// MARK: RGB <-> HSL
/**
@@ -267,6 +308,40 @@ class Color
]);
}
// MARK: RGB <-> HWB
/**
* Convert RGB to HWB
* via rgb -> hsl -> hsb -> hwb
*
* @param RGB $rgb
* @return HWB
*/
public static function rgbToHwb(RGB $rgb): HWB
{
return self::hsbToHwb(
self::hslToHsb(
self::rgbToHsl($rgb)
)
);
}
/**
* Convert HWB to RGB
* via hwb -> hsb -> hsl -> rgb
*
* @param HWB $hwb
* @return RGB
*/
public static function hwbToRgb(HWB $hwb): RGB
{
return self::hslToRgb(
self::hsbToHsl(
self::hwbToHsb($hwb)
)
);
}
// MARK: HSL <-> HSB
/**
@@ -318,6 +393,38 @@ class Color
]);
}
// MARK: HSL <-> HWB
/**
* Convert HSL to HWB
* via hsl -> hsb -> hwb
*
* @param HSL $hsl
* @return HWB
*/
public static function hslToHwb(HSL $hsl): HWB
{
return self::hsbToHwb(
self::hslToHsb(
$hsl
)
);
}
/**
* Convert HWB to HSL
* via hwb -> hsb -> hsl
*
* @param HWB $hwb
* @return HSL
*/
public static function hwbToHsl(HWB $hwb): HSL
{
return self::hsbToHsl(
self::hwbToHsb($hwb)
);
}
// MARK: HSB <-> HWB
/**
@@ -366,70 +473,31 @@ class Color
]);
}
// MARK: RGB <-> HWB
// MARK: LAB <-> LCH
// toLch
/**
* Convert RGB to HWB
* via rgb -> hsl -> hsb -> hwb
* CIE Lab to LCH
*
* @param RGB $rgb
* @return HWB
* @param Lab $lab
* @return LCH
*/
public static function rgbToHwb(RGB $rgb): HWB
public static function labToLch(Lab $lab): LCH
{
return self::hsbToHwb(
self::hslToHsb(
self::rgbToHsl($rgb)
)
);
// cieLab to cieLch
return LCH::__constructFromArray(self::__labToLch($lab), colorspace: 'CIELab');
}
/**
* Convert HWB to RGB
* via hwb -> hsb -> hsl -> rgb
* Convert CIE LCH to Lab
*
* @param HWB $hwb
* @return RGB
* @param LCH $lch
* @return Lab
*/
public static function hwbToRgb(HWB $hwb): RGB
public static function lchToLab(LCH $lch): Lab
{
return self::hslToRgb(
self::hsbToHsl(
self::hwbToHsb($hwb)
)
);
}
// MARK: HSL <-> HWB
/**
* Convert HSL to HWB
* via hsl -> hsb -> hwb
*
* @param HSL $hsl
* @return HWB
*/
public static function hslToHwb(HSL $hsl): HWB
{
return self::hsbToHwb(
self::hslToHsb(
$hsl
)
);
}
/**
* Convert HWB to HSL
* via hwb -> hsb -> hsl
*
* @param HWB $hwb
* @return HSL
*/
public static function hwbToHsl(HWB $hwb): HSL
{
return self::hsbToHsl(
self::hwbToHsb($hwb)
);
return Lab::__constructFromArray(self::__lchToLab($lch), colorspace: 'CIELab');
}
// MARK: OkLch <-> OkLab
@@ -443,16 +511,7 @@ class Color
public static function okLabToOkLch(Lab $lab): LCH
{
// okLab\toOkLch
$a = $lab->a;
$b = $lab->b;
$hue = atan2($b, $a) * 180 / pi();
return LCH::__constructFromArray([
$lab->L,
sqrt($a ** 2 + $b ** 2),
$hue >= 0 ? $hue : $hue + 360,
]);
return LCH::__constructFromArray(self::__labToLch($lab), colorspace: 'OkLab');
}
/**
@@ -465,114 +524,7 @@ class Color
{
// oklch/toOkLab
// oklch to oklab
return Lab::__constructFromArray([
$lch->L,
$lch->C * cos($lch->H * pi() / 180), // a
$lch->C * sin($lch->H * pi() / 180), // b
], 'Oklab');
}
// MARK: xyzD65 <-> linearRGB
/**
* convert linear RGB to xyz D65
* if rgb is not flagged linear, it will be auto converted
*
* @param RGB $rgb
* @return XYZD65
*/
public static function linRgbToXyzD65(RGB $rgb): XYZD65
{
// if not linear, convert to linear
if (!$rgb->linear) {
$rgb->toLinear();
}
return XYZD65::__constructFromArray(Math::multiplyMatrices(
[
[0.41239079926595934, 0.357584339383878, 0.1804807884018343],
[0.21263900587151027, 0.715168678767756, 0.07219231536073371],
[0.01933081871559182, 0.11919477979462598, 0.9505321522496607],
],
$rgb->returnAsArray()
));
}
/**
* Convert xyz D65 to linear RGB
*
* @param XYZD65 $xyzD65
* @return RGB
*/
public static function xyzD65ToLinRgb(XYZD65 $xyzD65): RGB
{
// xyz D65 to linrgb
return RGB::__constructFromArray(Math::multiplyMatrices(
a : [
[ 3.2409699419045226, -1.537383177570094, -0.4986107602930034 ],
[ -0.9692436362808796, 1.8759675015077202, 0.04155505740717559 ],
[ 0.05563007969699366, -0.20397695888897652, 1.0569715142428786 ],
],
b : $xyzD65->returnAsArray()
), linear: true);
}
// MARK: xyzD65 <-> OkLab
/**
* xyz D65 to OkLab
*
* @param XYZD65 $xyzD65
* @return Lab
*/
public static function xyzD65ToOkLab(XYZD65 $xyzD65): Lab
{
return Lab::__constructFromArray(Math::multiplyMatrices(
[
[0.2104542553, 0.7936177850, -0.0040720468],
[1.9779984951, -2.4285922050, 0.4505937099],
[0.0259040371, 0.7827717662, -0.8086757660],
],
array_map(
callback: fn ($v) => pow($v, 1 / 3),
array: Math::multiplyMatrices(
a: [
[0.8190224432164319, 0.3619062562801221, -0.12887378261216414],
[0.0329836671980271, 0.9292868468965546, 0.03614466816999844],
[0.048177199566046255, 0.26423952494422764, 0.6335478258136937],
],
b: $xyzD65->returnAsArray(),
),
)
), 'Oklab');
}
/**
* xyz D65 to OkLab
*
* @param Lab $lab
* @return XYZD65
*/
public static function okLabToXyzD65(Lab $lab): XYZD65
{
return XYZD65::__constructFromArray(Math::multiplyMatrices(
a: [
[1.2268798733741557, -0.5578149965554813, 0.28139105017721583],
[-0.04057576262431372, 1.1122868293970594, -0.07171106666151701],
[-0.07637294974672142, -0.4214933239627914, 1.5869240244272418],
],
b: array_map(
callback: fn ($v) => is_numeric($v) ? $v ** 3 : 0,
array: Math::multiplyMatrices(
a: [
[0.99999999845051981432, 0.39633779217376785678, 0.21580375806075880339],
[1.0000000088817607767, -0.1055613423236563494, -0.063854174771705903402],
[1.0000000546724109177, -0.089484182094965759684, -1.2914855378640917399],
],
// Divide $lightness by 100 to convert from CSS OkLab
b: $lab->returnAsArray(),
),
),
));
return Lab::__constructFromArray(self::__lchToLab($lch), colorspace: 'OkLab');
}
// MARK: rgb <-> oklab
@@ -585,9 +537,7 @@ class Color
*/
public static function rgbToOkLab(RGB $rgb): Lab
{
return self::xyzD65ToOkLab(
self::linRgbToXyzD65($rgb)
);
return CieXyz::rgbViaXyzD65ToOkLab($rgb);
}
/**
@@ -598,9 +548,7 @@ class Color
*/
public static function okLabToRgb(Lab $lab): RGB
{
return self::xyzD65ToLinRgb(
self::okLabToXyzD65($lab)
)->fromLinear();
return CieXyz::okLabViaXyzD65ToRgb($lab);
}
// MARK: rgb <-> oklch
@@ -753,7 +701,7 @@ class Color
* @param HWB $hwb
* @return Lab
*/
public function hwbToOkLab(HWB $hwb): Lab
public static function hwbToOkLab(HWB $hwb): Lab
{
return self::rgbToOkLab(
self::hwbToRgb($hwb)
@@ -766,7 +714,7 @@ class Color
* @param Lab $lab
* @return HWB
*/
public function okLabToHwb(Lab $lab): HWB
public static function okLabToHwb(Lab $lab): HWB
{
return self::rgbToHwb(
self::okLabToRgb($lab)
@@ -781,7 +729,7 @@ class Color
* @param HWB $hwb
* @return LCH
*/
public function hwbToOkLch(HWB $hwb): LCH
public static function hwbToOkLch(HWB $hwb): LCH
{
return self::rgbToOkLch(
self::hwbToRgb($hwb)
@@ -794,10 +742,367 @@ class Color
* @param LCH $lch
* @return HWB
*/
public function okLchToHwb(LCH $lch): HWB
public static function okLchToHwb(LCH $lch): HWB
{
return self::rgbToHwb(
self::okLchToRgb($lch)
);
}
// MARK: RGB <-> Lab (Cie)
/**
* RGB to Lab
* via RGB -> linRgb -> xyz D65 -> xyz D50 -> Lab
*
* @param RGB $rgb
* @return Lab
*/
public static function rgbToLab(RGB $rgb): Lab
{
return CieXyz::rgbViaXyzD65ViaXyzD50ToLab($rgb);
/* return CieXyz::xyzD50ToLab(
CieXyz::xyzD65ToXyzD50(
CieXyz::linRgbToXyzD65($rgb)
)
); */
}
/**
* Lab to RGB
* via Lab -> xyz D50 -> xyz D65 -> lin RGB -> RGB
*
* @param Lab $lab
* @return RGB
*/
public static function labToRgb(Lab $lab): RGB
{
return CieXyz::labViaXyzD50ViaXyzD65ToRgb($lab);
/* return CieXyz::xyzD65ToLinRgb(
CieXyz::xyzD50ToXyxD65(
CieXyz::labToXyzD50($lab)
)
)->fromLinear(); */
}
// MARK: RGB <-> Lch (Cie)
/**
* Convert RGB to LCH (Cie)
* via RGB to Lab
*
* @param RGB $rgb
* @return LCH
*/
public static function rgbToLch(RGB $rgb): LCH
{
// return self::rgbToL
return self::labToLch(
self::rgbToLab($rgb)
);
}
/**
* Convert LCH (Cie) to RGB
* via Lab to RGB
*
* @param LCH $lch
* @return RGB
*/
public static function lchToRgb(LCH $lch): RGB
{
return self::labToRgb(
self::lchToLab($lch)
);
}
// MARK: HSL <-> Lab (CIE)
/**
* HSL to Lab (CIE)
*
* @param HSL $hsl
* @return Lab
*/
public static function hslToLab(HSL $hsl): Lab
{
return self::rgbToLab(
self::hslToRgb($hsl)
);
}
/**
* Lab (CIE) to HSL
*
* @param Lab $lab
* @return HSL
*/
public static function labToHsl(Lab $lab): HSL
{
return self::rgbToHsl(
self::labToRgb($lab)
);
}
// MARK: HSL <-> Lch (CIE)
/**
* Undocumented function
*
* @param HSL $hsl
* @return LCH
*/
public static function hslToLch(HSL $hsl): LCH
{
return self::rgbToLch(
self::hslToRgb($hsl)
);
}
/**
* Undocumented function
*
* @param LCH $lch
* @return HSL
*/
public static function lchToHsl(LCH $lch): HSL
{
return self::rgbToHsl(
self::lchToRgb($lch)
);
}
// MARK: HSB <-> Lab (CIE)
/**
* Undocumented function
*
* @param HSB $hsb
* @return Lab
*/
public static function hsbToLab(HSB $hsb): Lab
{
return self::rgbToLab(
self::hsbToRgb($hsb)
);
}
/**
* Undocumented function
*
* @param Lab $lab
* @return HSB
*/
public static function labToHsb(Lab $lab): HSB
{
return self::rgbToHsb(
self::labToRgb($lab)
);
}
// MARK: HSB <-> Lch (CIE)
/**
* Undocumented function
*
* @param HSB $hsb
* @return LCH
*/
public static function hsbToLch(HSB $hsb): LCH
{
return self::rgbToLch(
self::hsbToRgb($hsb)
);
}
/**
* Undocumented function
*
* @param LCH $lch
* @return HSB
*/
public static function lchToHsb(LCH $lch): HSB
{
return self::rgbToHsb(
self::lchToRgb($lch)
);
}
// MARK: HWB <-> Lab (CIE)
/**
* Undocumented function
*
* @param HWB $hwb
* @return Lab
*/
public static function hwbToLab(HWB $hwb): Lab
{
return self::rgbToLab(
self::hwbToRgb($hwb)
);
}
/**
* Undocumented function
*
* @param Lab $lab
* @return HWB
*/
public static function labToHwb(Lab $lab): HWB
{
return self::rgbToHwb(
self::labToRgb($lab)
);
}
// MARK: HWB <-> Lch (CIE)
/**
* Undocumented function
*
* @param HWB $hwb
* @return Lch
*/
public static function hwbToLch(HWB $hwb): Lch
{
return self::rgbToLch(
self::hwbToRgb($hwb)
);
}
/**
* Undocumented function
*
* @param LCH $lch
* @return HWB
*/
public static function lchToHweb(LCH $lch): HWB
{
return self::rgbToHwb(
self::lchToRgb($lch)
);
}
// MARK: Lab (Cie) <-> OkLab
/**
* okLab to Lab (Cie)
*
* @param Lab $lab
* @return Lab
*/
public static function okLabToLab(Lab $lab): Lab
{
return CieXyz::okLabViaXyzD65ViaXyzD50ToLab($lab);
/* return CieXyz::xyzD50ToLab(
CieXyz::xyzD65ToXyzD50(
CieXyz::okLabToXyzD65($lab)
)
); */
}
/**
* Lab (Cie) to okLab
*
* @param Lab $lab
* @return Lab
*/
public static function labToOkLab(Lab $lab): Lab
{
return CieXyz::labViaXyzD50ViaXyzD65ToOkLab($lab);
/* return CieXyz::xyzD65ToOkLab(
CieXyz::xyzD50ToXyxD65(
CieXyz::labToXyzD50($lab)
)
); */
}
// MARK: Lab (Cie) <-> Oklch
/**
* OkLch to Lab (CIE)
*
* @param LCH $lch
* @return Lab
*/
public static function okLchToLab(LCH $lch): Lab
{
return self::okLabToLab(
self::okLchToOkLab($lch)
);
}
/**
* Lab (CIE) to OkLch
*
* @param Lab $lab
* @return LCH
*/
public static function labToOkLch(Lab $lab): LCH
{
return self::okLabToOkLch(
self::labToOkLab($lab)
);
}
// MARK: Lch (Cie) <-> OkLch
/**
* okLch to Lch (Cie)
* via okLabToLab
*
* @param LCH $lch
* @return LCH
*/
public static function okLchToLch(LCH $lch): LCH
{
return self::labToLch(
self::okLabToLab(
self::okLchToOkLab($lch)
)
);
}
/**
* Lch (Cie) to OkLch
* via labToOkLab
*
* @param LCH $lch
* @return LCH
*/
public static function lchToOkLch(LCH $lch): LCH
{
return self::labToOkLch(
self::lchToLab($lch)
);
}
// MARK: Lch (Cie) to OkLab
/**
* OkLab to Lch (CIE)
*
* @param LAB $lab
* @return LCH
*/
public static function okLabToLch(LAB $lab): LCH
{
return self::labToLch(
self::okLabToLab($lab)
);
}
/**
* Lch (CIE) to OkLab
*
* @param LCH $lch
* @return LAB
*/
public static function lchToOkLab(LCH $lch): LAB
{
return self::labToOkLab(
self::lchToLab($lch)
);
}
}

View File

@@ -13,6 +13,9 @@ namespace CoreLibs\Convert\Color\Coordinates;
class HSB
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['sRGB'];
/** @var float hue */
private float $H = 0.0;
/** @var float saturation */
@@ -20,6 +23,9 @@ class HSB
/** @var float brightness / value */
private float $B = 0.0;
/** @var string color space: either ok or cie */
private string $colorspace = '';
/**
* HSB (HSV) color coordinates
* Hue/Saturation/Brightness or Value
@@ -28,29 +34,17 @@ class HSB
{
}
/**
* set with each value as parameters
*
* @param float $H Hue
* @param float $S Saturation
* @param float $B Brightness
* @return self
*/
public static function __constructFromSet(float $H, float $S, float $B): self
{
return (new HSB())->setAsArray([$H, $S, $B]);
}
/**
* set from array
* where 0: Hue, 1: Saturation, 2: Brightness
*
* @param array{0:float,1:float,2:float} $hsb
* @param array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=sRGB]
* @return self
*/
public static function __constructFromArray(array $hsb): self
public static function __constructFromArray(array $colors, string $colorspace = 'sRGB'): self
{
return (new HSB())->setAsArray($hsb);
return (new HSB())->setColorspace($colorspace)->setFromArray($colors);
}
/**
@@ -113,6 +107,21 @@ class HSB
return $this->$name;
}
/**
* set the colorspace
*
* @param string $colorspace
* @return self
*/
private function setColorspace(string $colorspace): self
{
if (!in_array($colorspace, $this::COLORSPACES)) {
throw new \InvalidArgumentException('Not allowed colorspace', 0);
}
$this->colorspace = $colorspace;
return $this;
}
/**
* Returns the color as array
* where 0: Hue, 1: Saturation, 2: Brightness
@@ -128,14 +137,14 @@ class HSB
* set color as array
* where 0: Hue, 1: Saturation, 2: Brightness
*
* @param array{0:float,1:float,2:float} $hsb
* @param array{0:float,1:float,2:float} $colors
* @return self
*/
public function setAsArray(array $hsb): self
public function setFromArray(array $colors): self
{
$this->__set('H', $hsb[0]);
$this->__set('S', $hsb[1]);
$this->__set('B', $hsb[2]);
$this->__set('H', $colors[0]);
$this->__set('S', $colors[1]);
$this->__set('B', $colors[2]);
return $this;
}

View File

@@ -11,14 +11,23 @@ declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
use CoreLibs\Convert\Color\Stringify;
class HSL
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['sRGB'];
/** @var float hue */
private float $H = 0.0;
/** @var float saturation */
private float $S = 0.0;
/** @var float lightness (luminance) */
private float $L = 0.0;
/** @var string color space: either ok or cie */
private string $colorspace = '';
/**
* Color Coordinate HSL
* Hue/Saturation/Lightness
@@ -27,29 +36,17 @@ class HSL
{
}
/**
* set with each value as parameters
*
* @param float $H Hue
* @param float $S Saturation
* @param float $L Lightness
* @return self
*/
public static function __constructFromSet(float $H, float $S, float $L): self
{
return (new HSL())->setAsArray([$H, $S, $L]);
}
/**
* set from array
* where 0: Hue, 1: Saturation, 2: Lightness
*
* @param array{0:float,1:float,2:float} $hsl
* @param array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=sRGB]
* @return self
*/
public static function __constructFromArray(array $hsl): self
public static function __constructFromArray(array $colors, string $colorspace = 'sRGB'): self
{
return (new HSL())->setAsArray($hsl);
return (new HSL())->setColorspace($colorspace)->setFromArray($colors);
}
/**
@@ -110,6 +107,21 @@ class HSL
return $this->$name;
}
/**
* set the colorspace
*
* @param string $colorspace
* @return self
*/
private function setColorspace(string $colorspace): self
{
if (!in_array($colorspace, $this::COLORSPACES)) {
throw new \InvalidArgumentException('Not allowed colorspace', 0);
}
$this->colorspace = $colorspace;
return $this;
}
/**
* Returns the color as array
* where 0: Hue, 1: Saturation, 2: Lightness
@@ -125,16 +137,35 @@ class HSL
* set color as array
* where 0: Hue, 1: Saturation, 2: Lightness
*
* @param array{0:float,1:float,2:float} $hsl
* @param array{0:float,1:float,2:float} $colors
* @return self
*/
public function setAsArray(array $hsl): self
public function setFromArray(array $colors): self
{
$this->__set('H', $hsl[0]);
$this->__set('S', $hsl[1]);
$this->__set('L', $hsl[2]);
$this->__set('H', $colors[0]);
$this->__set('S', $colors[1]);
$this->__set('L', $colors[2]);
return $this;
}
/**
* convert to css string with optional opacityt
*
* @param float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string
{
$string = 'hsl('
. $this->H
. ' '
. $this->S
. ' '
. $this->L
. Stringify::setOpacity($opacity)
. ')';
return $string;
}
}
// __END__

View File

@@ -11,14 +11,23 @@ declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
use CoreLibs\Convert\Color\Stringify;
class HWB
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['sRGB'];
/** @var float Hue */
private float $H = 0.0;
/** @var float Whiteness */
private float $W = 0.0;
/** @var float Blackness */
private float $B = 0.0;
/** @var string color space: either ok or cie */
private string $colorspace = '';
/**
* Color Coordinate: HWB
* Hue/Whiteness/Blackness
@@ -27,29 +36,17 @@ class HWB
{
}
/**
* set with each value as parameters
*
* @param float $H Hue
* @param float $W Whiteness
* @param float $B Blackness
* @return self
*/
public static function __constructFromSet(float $H, float $W, float $B): self
{
return (new HWB())->setAsArray([$H, $W, $B]);
}
/**
* set from array
* where 0: Hue, 1: Whiteness, 2: Blackness
*
* @param array{0:float,1:float,2:float} $hwb
* @param array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=sRGB]
* @return self
*/
public static function __constructFromArray(array $hwb): self
public static function __constructFromArray(array $colors, string $colorspace = 'sRGB'): self
{
return (new HWB())->setAsArray($hwb);
return (new HWB())->setColorspace($colorspace)->setFromArray($colors);
}
/**
@@ -110,6 +107,21 @@ class HWB
return $this->$name;
}
/**
* set the colorspace
*
* @param string $colorspace
* @return self
*/
private function setColorspace(string $colorspace): self
{
if (!in_array($colorspace, $this::COLORSPACES)) {
throw new \InvalidArgumentException('Not allowed colorspace', 0);
}
$this->colorspace = $colorspace;
return $this;
}
/**
* Returns the color as array
* where 0: Hue, 1: Whiteness, 2: Blackness
@@ -125,16 +137,35 @@ class HWB
* set color as array
* where 0: Hue, 1: Whiteness, 2: Blackness
*
* @param array{0:float,1:float,2:float} $hwb
* @param array{0:float,1:float,2:float} $colors
* @return self
*/
public function setAsArray(array $hwb): self
public function setFromArray(array $colors): self
{
$this->__set('H', $hwb[0]);
$this->__set('W', $hwb[1]);
$this->__set('B', $hwb[2]);
$this->__set('H', $colors[0]);
$this->__set('W', $colors[1]);
$this->__set('B', $colors[2]);
return $this;
}
/**
* convert to css string with optional opacityt
*
* @param float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string
{
$string = 'hwb('
. $this->H
. ' '
. $this->W
. ' '
. $this->B
. Stringify::setOpacity($opacity)
. ')';
return $string;
}
}
// __END__

View File

@@ -12,8 +12,13 @@ declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
use CoreLibs\Convert\Color\Stringify;
class LCH
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['OkLab', 'CIELab'];
/** @var float Lightness/Luminance
* CIE: 0 to 100
* OKlch: 0.0 to 1.0
@@ -42,29 +47,17 @@ class LCH
{
}
/**
* set with each value as parameters
*
* @param float $L
* @param float $c
* @param float $h
* @return self
*/
public static function __constructFromSet(float $L, float $c, float $h): self
{
return (new LCH())->setAsArray([$L, $c, $h]);
}
/**
* set from array
* where 0: Lightness, 1: Chroma, 2: Hue
*
* @param array{0:float,1:float,2:float} $lch
* @param array{0:float,1:float,2:float} $colors
* @param string $colorspace
* @return self
*/
public static function __constructFromArray(array $lch): self
public static function __constructFromArray(array $colors, string $colorspace): self
{
return (new LCH())->setAsArray($lch);
return (new LCH())->setColorspace($colorspace)->setFromArray($colors);
}
/**
@@ -139,6 +132,21 @@ class LCH
return $this->$name;
}
/**
* set the colorspace
*
* @param string $colorspace
* @return self
*/
private function setColorspace(string $colorspace): self
{
if (!in_array($colorspace, $this::COLORSPACES)) {
throw new \InvalidArgumentException('Not allowed colorspace', 0);
}
$this->colorspace = $colorspace;
return $this;
}
/**
* Returns the color as array
* where 0: Lightness, 1: Chroma, 2: Hue
@@ -154,16 +162,45 @@ class LCH
* set color as array
* where 0: Lightness, 1: Chroma, 2: Hue
*
* @param array{0:float,1:float,2:float} $lch
* @param array{0:float,1:float,2:float} $colors
* @return self
*/
public function setAsArray(array $lch): self
public function setFromArray(array $colors): self
{
$this->__set('L', $lch[0]);
$this->__set('C', $lch[1]);
$this->__set('H', $lch[2]);
$this->__set('L', $colors[0]);
$this->__set('C', $colors[1]);
$this->__set('H', $colors[2]);
return $this;
}
/**
* Convert into css string with optional opacity
*
* @param null|float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string
{
$string = '';
switch ($this->colorspace) {
case 'CIELab':
$string = 'lch';
break;
case 'OkLab':
$string = 'oklch';
break;
}
$string .= '('
. $this->L
. ' '
. $this->c
. ' '
. $this->h
. Stringify::setOpacity($opacity)
. ');';
return $string;
}
}
// __END__

View File

@@ -12,10 +12,12 @@ declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
use CoreLibs\Convert\Color\Stringify;
class Lab
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['Oklab', 'cie'];
private const COLORSPACES = ['OkLab', 'CIELab'];
/** @var float lightness/luminance
* CIE: 0f to 100f
@@ -47,20 +49,6 @@ class Lab
{
}
/**
* set with each value as parameters
*
* @param float $L
* @param float $a
* @param float $b
* @param string $colorspace
* @return self
*/
public static function __constructFromSet(float $L, float $a, float $b, string $colorspace): self
{
return (new Lab())->setColorspace($colorspace)->setAsArray([$L, $a, $b]);
}
/**
* set from array
* where 0: Lightness, 1: a, 2: b
@@ -69,9 +57,9 @@ class Lab
* @param string $colorspace
* @return self
*/
public static function __constructFromArray(array $lab, string $colorspace): self
public static function __constructFromArray(array $colors, string $colorspace): self
{
return (new Lab())->setColorspace($colorspace)->setAsArray($lab);
return (new Lab())->setColorspace($colorspace)->setFromArray($colors);
}
/**
@@ -162,16 +150,45 @@ class Lab
* set color as array
* where 0: Lightness, 1: a, 2: b
*
* @param array{0:float,1:float,2:float} $lab
* @param array{0:float,1:float,2:float} $colors
* @return self
*/
public function setAsArray(array $lab): self
public function setFromArray(array $colors): self
{
$this->__set('L', $lab[0]);
$this->__set('a', $lab[1]);
$this->__set('b', $lab[2]);
$this->__set('L', $colors[0]);
$this->__set('a', $colors[1]);
$this->__set('b', $colors[2]);
return $this;
}
/**
* Convert into css string with optional opacity
*
* @param null|float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string
{
$string = '';
switch ($this->colorspace) {
case 'CIELab':
$string = 'lab';
break;
case 'OkLab':
$string = 'oklab';
break;
}
$string .= '('
. $this->L
. ' '
. $this->a
. ' '
. $this->b
. Stringify::setOpacity($opacity)
. ');';
return $string;
}
}
// __END__

View File

@@ -11,8 +11,13 @@ declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
use CoreLibs\Convert\Color\Stringify;
class RGB
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['sRGB'];
/** @var float red 0 to 255 or 0.0f to 1.0f for linear RGB */
private float $R = 0.0;
/** @var float green 0 to 255 or 0.0f to 1.0f for linear RGB */
@@ -20,6 +25,9 @@ class RGB
/** @var float blue 0 to 255 or 0.0f to 1.0f for linear RGB */
private float $B = 0.0;
/** @var string color space: either ok or cie */
private string $colorspace = '';
/** @var bool set if this is linear */
private bool $linear = false;
@@ -30,31 +38,34 @@ class RGB
{
}
/**
* set with each value as parameters
*
* @param float $R Red
* @param float $G Green
* @param float $B Blue
* @param bool $linear [default=false]
* @return self
*/
public static function __constructFromSet(float $R, float $G, float $B, bool $linear = false): self
{
return (new RGB())->flagLinear($linear)->setAsArray([$R, $G, $B]);
}
/**
* set from array
* where 0: Red, 1: Green, 2: Blue
*
* @param array{0:float,1:float,2:float} $rgb
* @param array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=sRGB]
* @param bool $linear [default=false]
* @return self
*/
public static function __constructFromArray(array $rgb, bool $linear = false): self
public static function __constructFromArray(array $colors, string $colorspace = 'sRGB', bool $linear = false): self
{
return (new RGB())->flagLinear($linear)->setAsArray($rgb);
return (new RGB())->setColorspace($colorspace)->flagLinear($linear)->setFromArray($colors);
}
/**
* Undocumented function
*
* @param string $hex_string
* @param string $colorspace
* @param bool $linear
* @return self
*/
public static function __constructFromHexString(
string $hex_string,
string $colorspace = 'sRGB',
bool $linear = false
): self {
return (new RGB())->setColorspace($colorspace)->flagLinear($linear)->setFromHex($hex_string);
}
/**
@@ -99,6 +110,21 @@ class RGB
return $this->$name;
}
/**
* set the colorspace
*
* @param string $colorspace
* @return self
*/
private function setColorspace(string $colorspace): self
{
if (!in_array($colorspace, $this::COLORSPACES)) {
throw new \InvalidArgumentException('Not allowed colorspace', 0);
}
$this->colorspace = $colorspace;
return $this;
}
/**
* Returns the color as array
* where 0: Red, 1: Green, 2: Blue
@@ -114,17 +140,76 @@ class RGB
* set color as array
* where 0: Red, 1: Green, 2: Blue
*
* @param array{0:float,1:float,2:float} $rgb
* @param array{0:float,1:float,2:float} $colors
* @return self
*/
public function setAsArray(array $rgb): self
public function setFromArray(array $colors): self
{
$this->__set('R', $rgb[0]);
$this->__set('G', $rgb[1]);
$this->__set('B', $rgb[2]);
$this->__set('R', $colors[0]);
$this->__set('G', $colors[1]);
$this->__set('B', $colors[2]);
return $this;
}
/**
* Return current set RGB as hex string. with or without # prefix
*
* @param bool $hex_prefix
* @return string
*/
public function returnAsHex(bool $hex_prefix = true): string
{
// prefix
$hex_color = '';
if ($hex_prefix === true) {
$hex_color = '#';
}
// convert if in linear
if ($this->linear) {
$this->fromLinear();
}
foreach ($this->returnAsArray() as $color) {
$hex_color .= str_pad(dechex((int)$color), 2, '0', STR_PAD_LEFT);
}
return $hex_color;
}
/**
* set colors RGB from hex string
*
* @param string $hex_string
* @return self
*/
public function setFromHex(string $hex_string): self
{
$hex_string = preg_replace("/[^0-9A-Fa-f]/", '', $hex_string); // Gets a proper hex string
if (!is_string($hex_string)) {
throw new \InvalidArgumentException('hex_string argument cannot be empty', 1);
}
$rgbArray = [];
if (strlen($hex_string) == 6) {
// If a proper hex code, convert using bitwise operation.
// No overhead... faster
$colorVal = hexdec($hex_string);
$rgbArray = [
0xFF & ($colorVal >> 0x10),
0xFF & ($colorVal >> 0x8),
0xFF & $colorVal
];
} elseif (strlen($hex_string) == 3) {
// If shorthand notation, need some string manipulations
$rgbArray = [
hexdec(str_repeat(substr($hex_string, 0, 1), 2)),
hexdec(str_repeat(substr($hex_string, 1, 1), 2)),
hexdec(str_repeat(substr($hex_string, 2, 1), 2))
];
} else {
// Invalid hex color code
throw new \UnexpectedValueException('Invalid hex_string: ' . $hex_string, 2);
}
return $this->setFromArray($rgbArray);
}
/**
* set as linear
* can be used as chain call on create if input is linear RGB
@@ -153,7 +238,7 @@ class RGB
*/
public function toLinear(): self
{
$this->flagLinear(true)->setAsArray(array_map(
$this->flagLinear(true)->setFromArray(array_map(
callback: function (int|float $v) {
$v = (float)($v / 255);
$abs = abs($v);
@@ -177,7 +262,7 @@ class RGB
*/
public function fromLinear(): self
{
$this->flagLinear(false)->setAsArray(array_map(
$this->flagLinear(false)->setFromArray(array_map(
callback: function (int|float $v) {
$abs = abs($v);
$sign = ($v < 0) ? -1 : 1;
@@ -197,29 +282,31 @@ class RGB
/**
* convert to css string with optional opacity
* Note: if this is a linea RGB, this data will not be correct
* Note: if this is a linear RGB, the data will converted during this operation and the converted back
*
* @param float|string|null $opacity
* @return string
*/
public function toCssString(null|float|string $opacity = null): string
{
// set opacity, either a string or float
if (is_string($opacity)) {
$opacity = ' / ' . $opacity;
} elseif ($opacity !== null) {
$opacity = ' / ' . $opacity;
} else {
$opacity = '';
// if we are in linear mode, convert to normal mode temporary
$was_linear = false;
if ($this->linear) {
$this->fromLinear();
$was_linear = true;
}
return 'rgb('
$string = 'rgb('
. (int)round($this->R, 0)
. ' '
. (int)round($this->G, 0)
. ' '
. (int)round($this->B, 0)
. $opacity
. Stringify::setOpacity($opacity)
. ')';
if ($was_linear) {
$this->toLinear();
}
return $string;
}
}

View File

@@ -0,0 +1,162 @@
<?php
/**
* AUTHOR: Clemens Schwaighofer
* CREATED: 2024/11/11
* DESCRIPTION:
* Color Coordinate: XYZ (Cie) (colorspace CIEXYZ)
* Note, this is only for the D50 & D65 whitepoint conversion
* https://en.wikipedia.org/wiki/CIE_1931_color_space#Construction_of_the_CIE_XYZ_color_space_from_the_Wright%E2%80%93Guild_data
* https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D
* https://en.wikipedia.org/wiki/Standard_illuminant#D65_values
*/
declare(strict_types=1);
namespace CoreLibs\Convert\Color\Coordinates;
class XYZ
{
/** @var array<string> allowed colorspaces */
private const COLORSPACES = ['CIEXYZ'];
/** @var array<string> allowed whitepoints
* D50: ICC profile PCS (horizon light) <-> CieLab
* D65: RGB color space (noon) <-> linear RGB
*/
private const ILLUMINANT = ['D50', 'D65'];
/** @var float X coordinate */
private float $X = 0.0;
/** @var float Y coordinate (Luminance) */
private float $Y = 0.0;
/** @var float Z coordinate (blue) */
private float $Z = 0.0;
/** @var string color space: either ok or cie */
private string $colorspace = '';
private string $whitepoint = '';
/**
* Color Coordinate Lch
* for oklch
*/
public function __construct()
{
}
/**
* set from array
* where 0: X, 1: Y, 2: Z
*
* @param array{0:float,1:float,2:float} $colors
* @param string $colorspace [default=CIEXYZ]
* @param string $whitepoint [default=''] only D65 or D50 allowed
* @return self
*/
public static function __constructFromArray(
array $colors,
string $colorspace = 'CIEXYZ',
string $whitepoint = ''
): self {
return (new XYZ())
->setColorspace($colorspace)
->setWhitepoint($whitepoint)
->setFromArray($colors);
}
/**
* set color
*
* @param string $name
* @param float $value
* @return void
*/
public function __set(string $name, float $value): void
{
if (!property_exists($this, $name)) {
throw new \ErrorException('Creation of dynamic property is not allowed', 0);
}
// if ($value < 0 || $value > 255) {
// throw new \LengthException('Argument value ' . $value . ' for color ' . $name
// . ' is not in the range of 0 to 255', 1);
// }
$this->$name = $value;
}
/**
* get color
*
* @param string $name
* @return float
*/
public function __get(string $name): float
{
if (!property_exists($this, $name)) {
throw new \ErrorException('Creation of dynamic property is not allowed', 0);
}
return $this->$name;
}
/**
* set the colorspace
*
* @param string $colorspace
* @return self
*/
private function setColorspace(string $colorspace): self
{
if (!in_array($colorspace, $this::COLORSPACES)) {
throw new \InvalidArgumentException('Not allowed colorspace', 0);
}
$this->colorspace = $colorspace;
return $this;
}
/**
* set the whitepoint flag
*
* @param string $whitepoint
* @return self
*/
private function setWhitepoint(string $whitepoint): self
{
if (empty($whitepoint)) {
$this->whitepoint = '';
return $this;
}
if (!in_array($whitepoint, $this::ILLUMINANT)) {
throw new \InvalidArgumentException('Not allowed whitepoint', 0);
}
$this->whitepoint = $whitepoint;
return $this;
}
/**
* Returns the color as array
* where 0: X, 1: Y, 2: Z
*
* @return array{0:float,1:float,2:float}
*/
public function returnAsArray(): array
{
return [$this->X, $this->Y, $this->Z];
}
/**
* set color as array
* where 0: X, 1: Y, 2: Z
*
* @param array{0:float,1:float,2:float} $colors
* @return self
*/
public function setFromArray(array $colors): self
{
$this->__set('X', $colors[0]);
$this->__set('Y', $colors[1]);
$this->__set('Z', $colors[2]);
return $this;
}
}
// __END__

View File

@@ -20,7 +20,26 @@ use CoreLibs\Convert\Color\Coordinates\LCH;
class Stringify
{
/**
* Undocumented function
* Build the opactiy sub string part and return it
*
* @param null|float|string|null $opacity
* @return string
*/
public static function setOpacity(null|float|string $opacity = null): string
{
// set opacity, either a string or float
if (is_string($opacity)) {
$opacity = ' / ' . $opacity;
} elseif ($opacity !== null) {
$opacity = ' / ' . $opacity;
} else {
$opacity = '';
}
return $opacity;
}
/**
* return the CSS string including optional opacity
*
* @param RGB|Lab|LCH|HSL|HWB $data
* @param null|float|string $opacity

View File

@@ -17,6 +17,9 @@ declare(strict_types=1);
namespace CoreLibs\Convert;
use CoreLibs\Convert\Color\Color;
use CoreLibs\Convert\Color\Coordinates;
class Colors
{
/**
@@ -37,7 +40,8 @@ class Colors
int $blue,
bool $hex_prefix = true
): string {
$hex_color = '';
return Coordinates\RGB::__constructFromArray([$red, $green, $blue])->returnAsHex($hex_prefix);
/* $hex_color = '';
if ($hex_prefix === true) {
$hex_color = '#';
}
@@ -50,7 +54,7 @@ class Colors
// pad left with 0
$hex_color .= str_pad(dechex($$color), 2, '0', STR_PAD_LEFT);
}
return $hex_color;
return $hex_color; */
}
/**
@@ -69,7 +73,7 @@ class Colors
bool $return_as_string = false,
string $seperator = ','
): string|array {
$hex_string = preg_replace("/[^0-9A-Fa-f]/", '', $hex_string); // Gets a proper hex string
/* $hex_string = preg_replace("/[^0-9A-Fa-f]/", '', $hex_string); // Gets a proper hex string
if (!is_string($hex_string)) {
throw new \InvalidArgumentException('hex_string argument cannot be empty', 1);
}
@@ -89,7 +93,8 @@ class Colors
} else {
// Invalid hex color code
throw new \UnexpectedValueException('Invalid hex_string: ' . $hex_string, 2);
}
} */
$rgbArray = Coordinates\RGB::__constructFromHexString($hex_string)->returnAsArray();
// returns the rgb string or the associative array
return $return_as_string ? implode($seperator, $rgbArray) : $rgbArray;
}
@@ -108,7 +113,10 @@ class Colors
*/
public static function rgb2hsb(int $red, int $green, int $blue): array
{
// check that rgb is from 0 to 255
return Color::rgbToHsb(
Coordinates\RGB::__constructFromArray([$red, $green, $blue])
)->returnAsArray();
/* // check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $color) {
if ($$color < 0 || $$color > 255) {
throw new \LengthException('Argument value ' . $$color . ' for color ' . $color
@@ -143,7 +151,7 @@ class Colors
(int)round($HUE), // Hue
(int)round(($DELTA / $MAX) * 100), // Saturation
(int)round($MAX * 100) // Value/Brightness
];
]; */
}
/**
@@ -159,7 +167,11 @@ class Colors
*/
public static function hsb2rgb(float $H, float $S, float $V): array
{
// check that H is 0 to 359, 360 = 0
return Color::hsbToRgb(
Coordinates\HSB::__constructFromArray([$H, $S, $V])
)->returnAsArray();
/* // check that H is 0 to 359, 360 = 0
// and S and V are 0 to 1
if ($H == 360) {
$H = 0;
@@ -229,7 +241,7 @@ class Colors
(int)round($red * 255),
(int)round($green * 255),
(int)round($blue * 255)
];
]; */
}
/**
@@ -245,7 +257,11 @@ class Colors
*/
public static function rgb2hsl(int $red, int $green, int $blue): array
{
// check that rgb is from 0 to 255
return Color::rgbToHsl(
Coordinates\RGB::__constructFromArray([$red, $green, $blue])
)->returnAsArray();
/* // check that rgb is from 0 to 255
foreach (['red', 'green', 'blue'] as $color) {
if ($$color < 0 || $$color > 255) {
throw new \LengthException('Argument value ' . $$color . ' for color ' . $color
@@ -285,7 +301,7 @@ class Colors
round($sat * 100, 1),
round($lum * 100, 1)
];
}
} */
}
/**
@@ -300,7 +316,11 @@ class Colors
*/
public static function hsl2rgb(float $hue, float $sat, float $lum): array
{
if ($hue == 360) {
return Color::hslToRgb(
Coordinates\HSL::__constructFromArray([$hue, $sat, $lum])
)->returnAsArray();
/* if ($hue == 360) {
$hue = 0;
}
if ($hue < 0 || $hue > 359) {
@@ -347,7 +367,7 @@ class Colors
(int)round(255 * $hueue($hue)),
(int)round(255 * $hueue($hue - (1 / 3)))
];
}
} */
}
}