Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
3.12% covered (danger)
3.12%
1 / 32
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ColorMix
3.12% covered (danger)
3.12%
1 / 32
33.33% covered (danger)
33.33%
1 / 3
462.03
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toCss
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
380
 trim
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Css\Value;
6
7/**
8 * CSS Color 5 §3 — `color-mix(in <space>, c1 [p1%], c2 [p2%])`.
9 *
10 * Stored as the parsed declaration; the actual mixing math runs
11 * when the 4E color engine ships. Percentages are normalised at
12 * parse time per §3.1:
13 *
14 *   - If neither percentage is supplied, both default to 50%.
15 *   - If one is supplied, the other is 100% − that value.
16 *   - If both sum to > 0% but ≠ 100%, multiply by 100/(p1+p2) to
17 *     normalise and remember the original sum as `alphaMultiplier`
18 *     so the resulting alpha can be scaled.
19 *   - If both sum to 0%, the result is invalid; the parser
20 *     returns null in that case.
21 *
22 * `hueInterpolation` is non-null only when `space` is a polar
23 * space (HSL, HWB, LCH, OKLCH); for other spaces the field is
24 * meaningless and stays null.
25 */
26final readonly class ColorMix extends Value
27{
28    public function __construct(
29        public ColorSpace $space,
30        public Color $color1,
31        public float $percentage1,
32        public Color $color2,
33        public float $percentage2,
34        public float $alphaMultiplier = 1.0,
35        public ?HueInterpolation $hueInterpolation = null,
36    ) {}
37
38    public function toCss(): string
39    {
40        $hueSuffix = $this->hueInterpolation !== null
41            ? ' ' . $this->hueInterpolation->value . ' hue'
42            : '';
43        $space = match ($this->space) {
44            ColorSpace::sRGB => 'srgb',
45            ColorSpace::sRGBLinear => 'srgb-linear',
46            ColorSpace::DisplayP3 => 'display-p3',
47            ColorSpace::DisplayP3Linear => 'display-p3-linear',
48            ColorSpace::A98RGB => 'a98-rgb',
49            ColorSpace::A98RGBLinear => 'a98-rgb-linear',
50            ColorSpace::ProPhotoRGB => 'prophoto-rgb',
51            ColorSpace::ProPhotoRGBLinear => 'prophoto-rgb-linear',
52            ColorSpace::Rec2020 => 'rec2020',
53            ColorSpace::Rec2020Linear => 'rec2020-linear',
54            ColorSpace::Lab => 'lab',
55            ColorSpace::Lch => 'lch',
56            ColorSpace::OKLab => 'oklab',
57            ColorSpace::OKLCH => 'oklch',
58            ColorSpace::XYZ, ColorSpace::XYZD65 => 'xyz-d65',
59            ColorSpace::XYZD50 => 'xyz-d50',
60            ColorSpace::HWB => 'hwb',
61        };
62        return sprintf(
63            'color-mix(in %s%s, %s %s%%, %s %s%%)',
64            $space,
65            $hueSuffix,
66            $this->color1->toCss(),
67            self::trim($this->percentage1),
68            $this->color2->toCss(),
69            self::trim($this->percentage2),
70        );
71    }
72
73    private static function trim(float $v): string
74    {
75        return fmod($v, 1.0) === 0.0 ? (string) (int) $v : (string) $v;
76    }
77}