An open API service indexing awesome lists of open source software.

https://github.com/atomicptr/color

Couleur is a modern PHP 8.1+ color library, intended to be compatible with CSS Color Module Level 4.
https://github.com/atomicptr/color

Last synced: 3 months ago
JSON representation

Couleur is a modern PHP 8.1+ color library, intended to be compatible with CSS Color Module Level 4.

Awesome Lists containing this project

README

          

# 🎨 Color: A modern PHP 8.3+ color library

[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/atomicptr/color/blob/main/LICENSE)
[![PHP Version](https://img.shields.io/packagist/php-v/atomicptr/color?style=flat)](https://packagist.org/packages/atomicptr/color)
[![Last commit on main branch](https://img.shields.io/github/last-commit/atomicptr/color)](https://github.com/atomicptr/color)
[![Latest Version on Packagist](https://img.shields.io/packagist/v/atomicptr/color)](https://packagist.org/packages/atomicptr/color)
[![Total Packagist downloads](https://img.shields.io/packagist/dt/atomicptr/color)](https://packagist.org/packages/atomicptr/color)

**Color** is a modern **PHP 8.3+ color library**, intended to be compatible with **[CSS Color Module Level 4](https://drafts.csswg.org/css-color-4)**, and inspired by **[Color.js](https://github.com/LeaVerou/color.js)** from [Lea Verou](https://github.com/LeaVerou) and [Chris Lilley](https://github.com/svgeesus).

This is a hard fork of [matthieumastadenis/couleur](https://github.com/matthieumastadenis/couleur)

The main goal of this package is to allow **color conversions** between multiple, old and new [🌈 Color Spaces](#-color-spaces), like the famous **LCH** which provides [many advantages for design purpose](https://lea.verou.me/2020/04/lch-colors-in-css-what-why-and-how/).

**Color** is made to be usable with an **[OOP](https://en.wikipedia.org/wiki/Object-oriented_programming)** approach as well as with a **[FP](https://en.wikipedia.org/wiki/Functional_programming)** approach:

- If you prefer **OOP**, you can use [🏭 Immutable Objects and the `ColorFactory`](#-immutable-objects-and-the-colorfactory) ;
- If you prefer **FP**, you can directly use the multiple [🧰 Pure Functions](#-pure-functions) ;

## ⚙️ Installation

Use the following command to add **Color** to your project with [Composer](https://getcomposer.org/):

```bash
composer require atomicptr/color
```

Don't forget to include the [autoloader](https://getcomposer.org/doc/01-basic-usage.md#autoloading) provided by Composer:

```php
toRgb();

// Stringify:
echo $rgb1; // Prints 'rgb(100% 0% 0%)'
echo $rgb1->stringify(); // Prints 'rgb(100% 0% 0%)'
echo $rgb1->stringify(legacy : false, alpha : true); // Prints 'rgb(100% 0% 0% / 100%)'
echo $rgb1->stringify(legacy : true); // Prints 'rgb(255,0,0)'
echo $rgb1->stringify(legacy : true, alpha : true); // Prints 'rgba(255,0,0,1)'

// Create a variant color:
$rgb2 = $rgb1->change('-35', '+20', 60);
echo $rgb2->stringify(legacy : true); // Prints 'rgb(220,20,60)';

// Convert to CSS:
$css2 = $rgb2->toCss();
echo $css2; // Prints 'crimson'

// Convert to Lch:
$lch = $css2->toLch();
echo $lch->stringify(alpha : true); // Prints 'lch(47.878646049% 79.619059282 26.464486652deg / 100%)'

// Convert to P3:
$p3 = $lch->toP3();
echo $p3; // Prints 'color(display-p3 0.791710722 0.191507424 0.257366748)'

```

[↑ Back to Top](#-color-a-modern-php-83-color-library)

## 📚 Usage

### 🏭 Immutable Objects and the `ColorFactory`

#### Direct instanciation

**Color** provides one **immutable class** for each supported [🌈 Color Space](#-color-spaces). You can of course instantiate these classes manually:

```php
**Note** :
> You may have noticed from the previous example that it implies passing *correctly formatted* values to each constructor.
>
> For example, the `Rgb` class expects to receive opacity expressed in the same magnitude than red, green and blue values, meaning *as a number between 0 and 255*. Same thing for the `HexRgb` class which expects only *hexadecimal strings* for each of the four parameters it takes (opacity included).
>
> Because of this, you may prefer to avoid instanciating these classes yourself. A simpler solution is to [use the ColorFactory](#using-the-colorfactory) like in the following examples. It will automatically handle *values conversion* for you.

[↑ Back to Top](#-color-a-modern-php-83-color-library)

#### Using the `ColorFactory`

The best and simplest way to create color objects is by using the `ColorFactory` **abstract class** which provides a specific **static method** for each supported [🌈 Color Space](#-color-spaces):

```php
coordinates()); // [ 0, 100, 50, 255 ]

// With the $from parameter, we can ensure that the input value will be treated like we want
// The following line creates a new colors\Rgb instance from an HSL input:
$rgb2 = ColorFactory::newRgb([ 0, 100, 50 ], 'hsl');
\var_dump($rgb2->coordinates()); // [ 255, 0, 0, 255 ]

// Same result, but with usage of the ColorSpace enum:
$rgb3 = ColorFactory::newRgb([ 0, 100, 50 ], ColorSpace::Hsl);
\var_dump($rgb3->coordinates()); // [ 255, 0, 0, 255 ]

```

You can alternatively use the `new()` **static method**, which adds a `$to` parameter just after the input value. If this parameter is not specified, the *targetted color space* will automatically be determined according to the format of the value:

```php
toCss();

// Converting to a new colors\XyzD50 instance:
$xyzD50 = $css->toXyzD50();

// Converting to a new colors\OkLch instance (using the to() method):
$okLch = $xyzD50->to(ColorSpace::OkLch);

```

Note that **any color** can be converted to CSS with the `toCss()` method. It will automatically pick the *closest* CSS color:

```php
toCss();

// Prints 'red':
echo $css;

```

All color objects are directly **stringable**. They also provide a `stringify()` method which offers more possibilities:

```php
stringify(null, true);

// Prints 'rgba(255,0,0,1)' (using $legacy and $alpha parameters):
echo $rgb->stringify(true, true);

$lch = ColorFactory::newLch([ 54.2905429, 106.837191, 40.8576688 ], ColorSpace::Lch);

// Prints 'lch(54.29% 106.84 40.86deg)' Using the $precision parameter:
echo $lch->stringify(precision : 2);

```

All color objects also have a `coordinates()` method which returns an array:

```php
coordinates();

```

You can also directly access **readonly properties** from each color object:

```php
red;

$hsl = ColorFactory::newHsl([ 0, 100, 50 ]);

// Prints 50:
echo $hsl->lightness;

```

All color objects have a `change()` method which always return a *new object* corresponding to a *variant* of the current color.

```php
change(hue : 180, opacity : 80);
echo $hsl2; // hsl(180deg 100% 50% / 80%)

// Add, subtract, multiply, divide coordinates:
$hsl3 = $hsl2->change('+20', '-10', '*1.5', '/2');
echo $hsl3; // hsl(200deg 90% 75% / 40%)

// Reduce coordinates by modulus:
$hsl4 = $hsl3->change(opacity : '%6');
echo $hsl4; // hsl(200deg 90% 75% / 4%)

// Calculate the percentage of coordinates:
$hsl5 = $hsl4->change('50%');
echo $hsl5; // hsl(100deg 90% 75% / 4%)

// Add, subtract, multiply, divide coordinates by a percentage:
$hsl6 = $hsl5->change('+50%', '-50%', '/10%', '*200%');
echo $hsl6; // hsl(150deg 45% 10% / 32%)

// Reduce coordinates by a percentage modulus:
$hsl7 = $hsl6->change(saturation : '%20%');
echo $hsl7; // hsl(150deg 0% 10% / 32%)

```

> **Note**:
> The `change()` method of the `HexRgb` class behave differently depending on the operation you ask for :
>
> - For replacing a coordinate you have to provide an **hexadecimal value** ;
> - For additions and substractions you have to provide an **hexadecimal value** ;
> - For all other operactions you have to provide a **decimal value** ;
>
> Please observe the detailed demonstration in the next example:

```php
change('80', 'AA', 'BB', 'AA');
echo $hex2; // #80AABBAA

// When adding or subtracting coordinates, provide hexadecimal numbers:
$hex3 = $hex2->change('+8', '-11');
echo $hex3; // #89BA (88 99 BB AA)

// When multiplying, dividing or reducing coordinates by modulo, provide decimal numbers:
$hex4 = $hex3->change(null, '*1.5', '/2', '%3');
echo $hex4; // #88E65E02 (88 dechex(153*1.5) dechex(187/2) dechex(170%3))

// When using percentages, provide decimal numbers:
$hex5 = $hex4->change('20%');
echo $hex5; // #1BE65E02 (dechex(136*20/100) E6 5E 02)

// When using percentages with addition, substraction, multiplication or division, provide decimal numbers:
$hex6 = $hex5->change('+50%', '-20%', '/2%', '*200%');
echo $hex6; // #29B83208 (dechex(27+(27*50/100)) dechex(230-(230*20/100)) dechex(94/(84*2/100)) dechex(2*(2*200/100)))

// When using percentages with modulo, provide decimal numbers:
$hex7 = $hex6->change(green : '%4%');
echo $hex7; // #29023208 (29 dechex(184%(184*4/100)) 32 08)

```

> **Note**:
> The `change()` method of the `Css` class also behave differently: it only accepts a stringable color name or an instances of [the CssColor Enum](#the-csscolor-enum), which replace the color without variations.
>
> Please observe the next example:

```php
change(CssColor::purple);
echo $css2; // purple

$css2 = $css1->change(CssColor::purple);
echo $css2; // purple

$css3 = $css2->change('hotpink');
echo $css3; // hotpink

// Throws an UnsupportedCssColor Exception:
$css4 = $css3->change('invalid');

```

[↑ Back to Top](#-color-a-modern-php-83-color-library)

### 🧰 Pure Functions

Objects in **Color** are all based on a collection of **pure functions** under the hood. These functions can be used directly if you don't want to use objects.

> **Note**:
> Choosing this *functional programming approach* is better in terms of performance, but can be a bit more tedious because you have to manipulate arrays of values instead of objects.

There are three main types of functions provided by **Color** : dedicated [Color Space Functions](#color-space-functions), dedicated [Conversion Functions](#conversion-functions), and [Generic Functions](#generic-functions):

#### Color Space Functions

Each supported [🌈 Color Space](#-color-spaces) has its own dedicated functions, accessible under the namespace `Atomicptr\Color\Utils\[space]`. Those are the same for each color space: `clean()`, `from()`, `stringify()` and `verify()`.

`clean()` functions are made to transform an input value in a correctly formated set of values, according to the corresponding color space. They all return an array, except for `css\clean()` which directly returns an instance of the [the `CssColor` Enum](#the-csscolor-enum):

```php

**Note** :
> This function does not *clean* values inside of the array. For a typical usage, you may want to pass its result into the corresponding `clean()` function (see the [Color Space Functions](#color-space-functions) section for more details).

```php
value;

// Always returns 9, which is the default value for the COULEUR_PRECISION constant:
Constant::PRECISION->value;

// Returns the value of the COULEUR_LEGACY constant if defined, or 0 by default:
Constant::LEGACY->value();

// Returns the value of the COULEUR_PRECISION constant if defined, or 9 by default:
Constant::PRECISION->value();

// Returns the value of the COULEUR_LEGACY constant if defined, or 1 as fallback:
Constant::LEGACY->value(1);

// Returns the value of the COULEUR_PRECISION constant if defined, or 3 as fallback:
Constant::PRECISION->value(3);

// Returns the value of the COULEUR_LEGACY constant if defined, or define it with 1 as value then returns 1:
Constant::LEGACY->value(1, true);

// Returns the value of the COULEUR_PRECISION constant if defined, or define it with 3 as value then returns 3:
Constant::PRECISION->value(3, true);

```

[↑ Back to Top](#-color-a-modern-php-83-color-library)

#### The `ColorSpace` Enum

This enum is the simplest way to access all **color spaces** supported by **Color**:

```php
aliases();

// Returns ColorSpace::Rgb:
ColorSpace::fromAlias('rgb');
ColorSpace::fromAlias('srgb');
ColorSpace::fromAlias('RGBA');

// Returns ColorSpace::Lab:
ColorSpace::fromAlias('lab');
ColorSpace::fromAlias('cielab');
ColorSpace::fromAlias('CIE-LAB');

```

You can easily access [dedicated functions](#color-space-functions) from each `ColorSpace` with the `cleanCallback()`, `fromCallback()`, `stringifyCallback()` and `verifyCallback()` methods:

```php
cleanCallback();

// Returns [ 255, 127.5, 0, 255 ]:
ColorSpace::Rgb->cleanCallback()('rgb(100%,50%,0)');

// Returns [ 'FF', '00', '00', 'FF' ]:
ColorSpace::HexRgb->cleanCallback()('#f00');

// Returns 'Atomicptr\Color\Utils\XyzD50\from':
ColorSpace::XyzD50->fromCallback();

// Returns [ 255, 0, 0, 255 ]:
ColorSpace::Rgb->fromCallback()('hsl(0deg,100%,50%)');

// Returns CssColor::red:
ColorSpace::Css->fromCallback()('#f00');

// Returns 'Atomicptr\Color\Utils\XyzD50\stringify':
ColorSpace::XyzD50->stringifyCallback();

// Returns 'rgb(100% 0% 0% / 50%):
ColorSpace::Rgb->stringifyCallback()(255, 0 , 0, 127.5);

// Returns '#F00':
ColorSpace::Css->stringifyCallback()(CssColor::red);

// Returns 'Atomicptr\Color\Utils\CSS\verify':
ColorSpace::Css->verifyCallback();

// Returns true:
ColorSpace::Rgb->verifyCallback()('rgb(100%,50%,0)');

// Returns false:
ColorSpace::HexRgb->verifyCallback()('#ff');

```

[↑ Back to Top](#-color-a-modern-php-83-color-library)

#### The `CssColor` Enum

This enum helps managing `CSS` **named colors**. With its multiple methods, you can easily know if a `CssColor` exists and get the corresponding `RGB` or `HexRGB` coordinates from it:

```php
toRgbCoordinates();

// Returns [ 'FF', '00', '00' ]:
CssColor::red->toHexRgbCoordinates();

```

The `fromCss()` method allows you to get a specific `CssColor` by its name. If no supported color matches the `$name` parameter, a `UnsupportedCssColor` Exception will be thrown by default, unless you provide a `$fallback` or you set the `$throw` parameter to **false**.

```php
toHexRgbString();

// Returns '#f00' (using the $uppercase parameter):
CssColor::red->toHexRgbString(uppercase : false);

// Returns 'F00' (using the $sharp parameter):
CssColor::red->toHexRgbString(sharp : false);

// Returns '#F00F' (using the $alpha parameter):
CssColor::red->toHexRgbString(true);

// Returns '#FF0000' (using the $short parameter):
CssColor::red->toHexRgbString(short : false);

// Returns '#FF0000FF' (using $alpha and $short parameters):
CssColor::red->toHexRgbString(true, false);

// Returns '#ff0000ff' (using $alpha, $short and $uppercase parameters):
CssColor::red->toHexRgbString(true, false, false);

// Returns 'ff0000ff' (using $alpha, $short, $uppercase and $sharp parameters):
CssColor::red->toHexRgbString(true, false, false, false);

// Returns 'rgb(100% 0% 0%)':
CssColor::red->toRgbString();

// Returns 'rgb(100% 0% 0% / 100%)' (using the $alpha parameter):
CssColor::red->toRgbString(alpha : true);

// Returns 'rgb(255,0,0)' (using the $legacy parameter):
CssColor::red->toRgbString(true);

// Returns 'rgba(255,0,0,1)' (using the $legacy and $alpha parameters):
CssColor::red->toRgbString(true, true);

```

You can also get a new `Color` **object** from any `CssColor` by using the `toCss()`, `toHexRgb()` or `toRgb()` method:

```php
toCss();

// Returns a new colors\HexRgb instance:
CssColor::red->toHexRgb();

// Returns a new colors\Rgb instance:
CssColor::red->toRgb();

```

[↑ Back to Top](#-color-a-modern-php-83-color-library)

## 🌈 Color Spaces

**Color** currently supports the following **color spaces** and formats:

### CSS

In **Color**, the `Css` color space refers to the **named colors** according to the [CSS specification](https://drafts.csswg.org/css-color-4/#named-colors). Because they are a predefined and standardized list of exact colors, they all can be accessed easily with [the CssColor Enum](#the-csscolor-enum).

> **Note** :
> `Css` colors **cannot** have an opacity value. If you want to apply transparency to a `Css` color, you first **have to** convert it into another color space. In the same way, if you convert a color with transparency into its `Css` equivalent, it will **lose** the transparency.

- **ColorSpace enum case** : `ColorSpace::Css` ;
- **Color class** : `Atomicptr\Color\Colors\Css` ;
- **Dedicated functions namespace** : `Atomicptr\Color\Utils\CSS` ;
- **Accepted aliases** : `css`, `html`, `web` ;

### Hexadecimal RGB

- **ColorSpace** case: `ColorSpace::HexRgb` ;
- **Color class** : `Atomicptr\Color\Colors\HexRgb` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\HexRGB` ;
- **Accepted aliases** : `hex`, `hexrgb`, `hex-rgb`, `hex_rgb`, `hexadecimal` ;
- **Coordinates** : `red`, `green`, `blue` ;

### HSL

- **ColorSpace** case: `ColorSpace::Hsl` ;
- **Color class** : `Atomicptr\Color\Colors\Hsl` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\HSL` ;
- **Accepted aliases** : `hsl`, `hsla` ;
- **Coordinates** : `hue`, `saturation`, `lightness` ;

### HSV

- **ColorSpace** case: `ColorSpace::Hsv` ;
- **Color class** : `Atomicptr\Color\Colors\Hsv` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\HSV` ;
- **Accepted aliases** : `hsv`, `hsb` ;
- **Coordinates** : `hue`, `saturation`, `value` ;

### HWB

- **ColorSpace** case: `ColorSpace::Hwb` ;
- **Color class** : `Atomicptr\Color\Colors\Hwb` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\HWB` ;
- **Accepted aliases** : `hwb` ;
- **Coordinates** : `hue`, `whiteness`, `blackness` ;

### Lab

- **ColorSpace** case: `ColorSpace::Lab` ;
- **Color class** : `Atomicptr\Color\Colors\Lab` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\Lab` ;
- **Accepted aliases** : `lab`, `cielab`, `cie-lab`, `cie_lab` ;
- **Coordinates** : `lightness`, `b`, `a` ;

### Lch

- **ColorSpace** case: `ColorSpace::Lch` ;
- **Color class** : `Atomicptr\Color\Colors\Lch` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\Lch` ;
- **Accepted aliases** : `lch`, `cielch`, `cie-lch`, `cie_lch` ;
- **Coordinates** : `lightness`, `chroma`, `hue` ;

### Linear RGB

- **ColorSpace** case: `ColorSpace::LinRgb` ;
- **Color class** : `Atomicptr\Color\Colors\LinRgb` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\LinRGB` ;
- **Accepted aliases** : `srgb-linear`, `linrgb`, `linsrgb`, `lin-rgb`, `lin_rgb`, `lin-srgb`, `lin_srgb` ;
- **Coordinates** : `red`, `green`, `blue` ;

### Linear P3

- **ColorSpace** case: `ColorSpace::LinP3` ;
- **Color class** : `Atomicptr\Color\Colors\LinP3` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\LinP3` ;
- **Accepted aliases** : `p3-linear`, `p3_linear`, `linp3`, `lin-p3`, `lin_p3` ;
- **Coordinates** : `red`, `green`, `blue` ;

### Linear ProPhoto

- **ColorSpace** case: `ColorSpace::LinProPhoto` ;
- **Color class** : `Atomicptr\Color\Colors\LinProPhoto` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\LinProPhoto` ;
- **Accepted aliases** : `prophoto-linear`, `prophoto_linear`, `linprophoto`, `lin-prophoto`, `lin_prophoto` ;
- **Coordinates** : `red`, `green`, `blue` ;

### OkLab

- **ColorSpace** case: `ColorSpace::OkLab` ;
- **Color class** : `Atomicptr\Color\Colors\OkLab` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\OkLab` ;
- **Accepted aliases** : `oklab`, `ok-lab`, `ok_lab` ;
- **Coordinates** : `lightness`, `a`, `b` ;

### OkLch

- **ColorSpace** case: `ColorSpace::OkLch` ;
- **Color class** : `Atomicptr\Color\Colors\OkLch` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\OkLch` ;
- **Accepted aliases** : `oklch`, `ok-lch`, `ok_lch` ;
- **Coordinates** : `lightness`, `chroma`, `hue` ;

### P3

- **ColorSpace** case: `ColorSpace::LinP3` ;
- **Color class** : `Atomicptr\Color\Colors\LinP3` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\LinP3` ;
- **Accepted aliases** : `display-p3`, `display_p3`, `p3` ;
- **Coordinates** : `red`, `green`, `blue` ;

### ProPhoto

- **ColorSpace** case: `ColorSpace::ProPhoto` ;
- **Color class** : `Atomicptr\Color\Colors\ProPhoto` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\ProPhoto` ;
- **Accepted aliases** : `prophoto`, `prophoto-rgb`, `prophoto_rgb` ;
- **Coordinates** : `red`, `green`, `blue` ;

### RGB

- **ColorSpace** case: `ColorSpace::Rgb` ;
- **Color class** : `Atomicptr\Color\Colors\Rgb` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\RGB` ;
- **Accepted aliases** : `rgb`, `rgba`, `srgb`, `s-rgb`, `s_rgb` ;
- **Coordinates** : `red`, `green`, `blue` ;

### XYZ-D50

- **ColorSpace** case: `ColorSpace::XyzD50` ;
- **Color class** : `Atomicptr\Color\Colors\XyzD50` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\XyzD50` ;
- **Accepted aliases** : `xyz-d50`, `xyz_d50`, `xyzd50` ;
- **Coordinates** : `x`, `y`, `z` ;

### XYZ-D65

- **ColorSpace** case: `ColorSpace::XyzD65` ;
- **Color class** : `Atomicptr\Color\Colors\XyzD65` ;
- **Dedicated functions** namespace : `Atomicptr\Color\Utils\XyzD65` ;
- **Accepted aliases** : `xyz-d65`, `xyz_d65`, `xyzd65`, `xyz` ;
- **Coordinates** : `x`, `y`, `z` ;

[↑ Back to Top](#-color-a-modern-php-83-color-library)

## 📜 License

**Color** is publicly shared under the **MIT License**. You can freely use it in any project. For more information, please read the included [License File](https://github.com/atomicptr/color/blob/main/LICENSE).

[↑ Back to Top](#-color-a-modern-php-83-color-library)

## ❤️ Thanks

A huge thanks to [Lea Verou](https://github.com/LeaVerou) and [Chris Lilley](https://github.com/svgeesus) for their incredible work and their precise articles on the subject. With a special thanks to Chris for the time and the answers he gave me during the implementation of this library.

Also since this is a fork, a huge thanks to [Matthieu Masta Denis](https://github.com/matthieumastadenis) for developing the original project.

[↑ Back to Top](#-color-a-modern-php-83-color-library)