Passed
Push — master ( de3d61...be839c )
by Alec
13:42 queued 13s
created

HexColorToAnsiCodeConverter   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 101
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 43
c 0
b 0
f 0
dl 0
loc 101
rs 10
wmc 13

7 Methods

Rating   Name   Duplication   Size   Complexity  
A convertHexColorToAnsiColorCode() 0 13 1
A getSaturation() 0 12 2
A degradeHexColorToAnsi4() 0 8 2
A convert() 0 8 1
A convertFromRGB() 0 9 1
A degradeFrom() 0 11 3
A degradeHexColorToAnsi8() 0 10 3
1
<?php
2
3
declare(strict_types=1);
4
5
6
namespace AlecRabbit\Spinner\Extras\Color;
7
8
use AlecRabbit\Spinner\Contract\Option\OptionStyleMode;
9
use AlecRabbit\Spinner\Exception\InvalidArgumentException;
10
use AlecRabbit\Spinner\Extras\Color\A\AColorToAnsiCodeConverter;
11
use AlecRabbit\Spinner\Extras\Contract\IHexColorToAnsiCodeConverter;
12
13
final class HexColorToAnsiCodeConverter extends AColorToAnsiCodeConverter implements IHexColorToAnsiCodeConverter
14
{
15
    public function convert(string $color): string
16
    {
17
        $color = $this->normalize($color);
18
19
        return match ($this->styleMode) {
20
            OptionStyleMode::ANSI4 => $this->convert4($color),
21
            OptionStyleMode::ANSI8 => $this->convert8($color),
22
            default => $this->convertHexColorToAnsiColorCode($color),
23
        };
24
    }
25
26
    /**
27
     * @throws InvalidArgumentException
28
     */
29
    protected function convertHexColorToAnsiColorCode(string $hexColor): string
30
    {
31
        $color = $this->toInt($hexColor);
32
33
        $r = ($color >> 16) & 255;
34
        $g = ($color >> 8) & 255;
35
        $b = $color & 255;
36
37
        return match ($this->styleMode) {
38
            OptionStyleMode::ANSI4 => (string)$this->convertFromRGB($r, $g, $b),
39
            OptionStyleMode::ANSI8 => '8;5;' . (string)$this->convertFromRGB($r, $g, $b),
40
            OptionStyleMode::ANSI24 => sprintf('8;2;%d;%d;%d', $r, $g, $b),
41
            default => throw new InvalidArgumentException('Should not be thrown: Unsupported style mode.'),
42
        };
43
    }
44
45
    /**
46
     * @throws InvalidArgumentException
47
     */
48
    protected function convertFromRGB(int $r, int $g, int $b): int
49
    {
50
        return match ($this->styleMode) {
51
            OptionStyleMode::ANSI4 => $this->degradeHexColorToAnsi4($r, $g, $b),
52
            OptionStyleMode::ANSI8 => $this->degradeHexColorToAnsi8($r, $g, $b),
53
            default => throw new InvalidArgumentException(
54
                sprintf(
55
                    'RGB cannot be converted to "%s".',
56
                    $this->styleMode->name
0 ignored issues
show
Bug introduced by
The property name does not seem to exist on AlecRabbit\Spinner\Contract\Option\OptionStyleMode.
Loading history...
57
                )
58
            )
59
        };
60
    }
61
62
    protected function degradeHexColorToAnsi4(int $r, int $g, int $b): int
63
    {
64
        /** @psalm-suppress TypeDoesNotContainType */
65
        if (round($this->getSaturation($r, $g, $b) / 50) === 0) { // 0 === round(... - it is a hack
66
            return 0;
67
        }
68
69
        return (int)((round($b / 255) << 2) | (round($g / 255) << 1) | round($r / 255));
70
    }
71
72
    protected function getSaturation(int $r, int $g, int $b): int
73
    {
74
        $rf = $r / 255;
75
        $gf = $g / 255;
76
        $bf = $b / 255;
77
        $v = max($rf, $gf, $bf);
78
79
        if (0 === $diff = $v - min($rf, $gf, $bf)) {
80
            return 0;
81
        }
82
83
        return (int)($diff * 100 / $v);
84
    }
85
86
    /**
87
     * Inspired from (MIT license):
88
     *
89
     * @link https://github.com/ajalt/colormath/blob/e464e0da1b014976736cf97250063248fc77b8e7/colormath/src/commonMain/kotlin/com/github/ajalt/colormath/model/Ansi256.kt
90
     */
91
    protected function degradeHexColorToAnsi8(int $r, int $g, int $b): int
92
    {
93
        if ($r === $g && $g === $b) {
94
            return $this->degradeFrom($r);
95
        }
96
97
        return 16 +
98
            (36 * (int)round($r / 255 * 5)) +
99
            (6 * (int)round($g / 255 * 5)) +
100
            (int)round($b / 255 * 5);
101
    }
102
103
    protected function degradeFrom(int $r): int
104
    {
105
        if ($r < 8) {
106
            return 16;
107
        }
108
109
        if ($r > 248) {
110
            return 231;
111
        }
112
113
        return (int)round(($r - 8) / 247 * 24) + 232;
114
    }
115
}
116