SavvyWombat

Color-PHP

Easily convert between and easily manipulate Hex, HSL, RGB and custom colorspaces.

composer require savvywombat\color
GitHub Packagist

Main features

  • support for CSS Color Module Level 3 color specifications
    • #a02917
    • rgb(160, 41, 23)
    • hsl(8, 75%, 36%)
  • support for alpha transparency
    • #a0291780
    • rgb(160, 41, 23, 0.5)
    • hsl(8, 75%, 36%, 0.5)
  • modifiers to change red/green/blue or hue/saturation/lightness and alpha on any color type
  • support for custom colorspaces which can then be converted to other colorspaces
  • support for custom modifiers which can be applied to any colorspace
  • immutable behaviour
  • support for CSS color keywords

Conversion

hsl(208, 40%, 41%)echo (string) Color::fromString('#3f6c93')->toHsl();

Modification and immutability

The Color object is immutable - so conversions and modifications will return a new Color object.

We can set the red channel (or green or blue) to any integer value from 0 to 255.

#333333$color = Color::fromString('#333333');
#643333$color->red(100);

We can add or subtract an absolute value to or from the red channel (or green or blue). However, these channels are limited to values between 0 and 255 inclusive.

#333333$color = Color::fromString('#333333');
#973333$color->red('+100');
#332933$color->green('-10');
#3333ff$color->blue('+300');
#003333$color->red('-100');

Alternatively, we can use relative values to modify any of the color channels:

#333333$color = Color::fromString('#333333');
#333366$color->blue('+100%');
#331a33$color->green('-50%');

Finally, you can set the channel to a value between it's current value and the upper (or lower) limit by passing a fractional argument.

#333399$color->blue('+1/2');
#333326$color->blue('+1/2');

Any modifier can be applied to any colorspace.

For example, you can modify the hue, saturation or lightness of a HEX or RGB color, or you can modify a HSL by changing it's red, green or blue values.

#993333$color = Color::fromString('#993333');
#339933$color->hue('+120');
#339933$color->hue('+480');
#333399$color->hue('-120');
#a62626$color->saturation('+25%');
#732626$color->lightness('-25%');

hsl(0, 50%, 50%)      $color = Color::fromString('hsl(0, 50%, 50%)');
hsl(293, 55.3%, 51.7%)$color->blue('200');

Note that saturation and lightness are limited to any value between 0 and 100%, while hue is cyclic and unlimited (though you should try to remain in the bounds of 0–360°). This means a hue of 390° is the equivalent of 30° and also −330°.

The open-ended value of hue also means that relative and fractional modifications are not possible.

Custom color types

Custom color types must extend the Color abstract class, and also implement the following methods from ColorInterface:

public static function fromString(string $colorSpec): ColorInterface;
public function __toString(): string;

public static function fromRgb(Rgb $rgb): ColorInterface;
public function toRgb(): Rgb;
class Gray extends Color implements ColorInterface
{
    protected $value;

    /**
     * @param  float $value A percentage value, 0 for black and 100 for white.
     * @param  float $alpha The alpha channel, 0.0 for complete transparency and 1.0 for complete opacity.
     */
    public function __construct(float $value, float $alpha = 1.0)
    {
        $this->value = min(max($value, 0), 100); // limit value to be between 0 and 100
        $this->alpha = min(max($alpha, 0), 1.0); // limit alpha to be between 0 and 1.0
    }

    /**
     * Create a new Gray color from the provided color specification.
     *
     * @param  string $colorSpec Accepted formats: gray(50%) or gray(50%, 0.5)
     */
    public static function fromString(string $colorSpec): ColorInterface
    {
    }

    /**
     * Output a string representation of the Gray color - gray(50%) or gray(50%, 0.5) (with an alpha channel)
     *
     * @return  string Either of the following formats, depending on whether an alpha channel has been set: gray(50%) or gray(50%, 0.5)
     */
    public function __toString(): string
    {
    }

    /**
     * Create a new instance of Gray based on the provided Rgb
     *
     * @param  Rgb $rgb
     * @return  Gray Return a new instance of Gray based on the RGB values in the provided object.
     */
    public static function fromRgb(Rgb $rgb): ColorInterface
    {
    }

    /**
     * Create a new instance of an Rgb color based on the Gray value.
     * @param  Rgb $rgb
     */
    public function toRgb(): Rgb
    {
    }
}

Creation - fromString

public static function fromString(string $colorSpec): ColorInterface
{
    $channels = Color::extractChannels($colorSpec, self::class);
    // $channels[1] is the gray value
    // $channels[3] is the optional alpha channel

    if (!isset($channels[3])) {
        $channels[3] = 1.0;
    }

    return new Gray((float) $channels[1], (float) $channels[3]);
}

To be able to create a Gray color from a string specification, you must first register the color object and one or more regex patterns for the color specification.

Color::registerColor('Gray', Gray::class);
Color::registerColorSpec('gray\((\d{1,3}(\.\d{1,2})?)%\)', Gray::class); // gray(50%)
Color::registerColorSpec('gray\((\d{1,3}(\.\d{1,2})?)%,\s*([0-1](\.\d{1,2})?)\)', Gray::class); // gray(50%, 0.5)

$gray = Color::fromString("gray(50%, 0.5)");

Conversion - fromRgb and toRgb

To convert between color types, you only need to implement conversions from and to RGB types. You will then be able to convert between any other registered type.

public static function fromRgb(Rgb $rgb): ColorInterface
{
    $average = ($rgb->red + $rgb->green + $rgb->blue) / 3;
    $gray = $average * 100 / 255; // rgb values are in the range 0-255, gray values are 0-100%

    return new Gray($gray, $rgb->alpha); // copies the original alpha value
}

public function toRgb(): Rgb
{
    $gray = $this->value * 255 / 100; // scale the gray value to 0-255 for ease

    $return new Rgb($gray, $gray, $gray, $this->alpha); // all color channels are equal, and copy the current alpha value
}
Color::registerColor('Gray', Gray::class);
$hex = Color::fromString('#339966');
$gray = $hex->toGray();
echo (string) $gray; // gray(40%);

$hex = Color::fromString('#33996680');
$gray = $hex->toGray();
echo (string) $gray; // gray(40%, 0.5);

Custom color modifiers

Calling a modifier on any color type will convert it to the type that implements the registered method, and then convert the result back to the original type.

For example, the saturation modifier is defined on the Hsl colorspace class. If we wish to increase the saturation of an Rgb color, the library will convert from that value to an intermediary Hsl color, apply the change to the saturation, and then return a new Rgb with the changes applied.

To allow this implicit conversion, custom modifier methods must return an object which extends the abstract Color class. Ideally, it should return a copy of the same type the method is implemented on.

/**
 * Change the gray value of a color
 *
 * @param  $gray The change to the grayness of this color.
 * @return  self This will return a copy of the Gray class.
 */
public function gray($gray): self
{
    $gray = $this->adjustValue($this->gray, $gray, 100);

    $gray = min($gray, max($gray, 0)); // gray is limited to 0 to 100

    return new self($gray, $this->alpha); // return a copy of the Gray object
}

adjustValue is a method on the abstract Color class that handles the different values allowed for adjustment (including relative or fractional changes) and returns the new absolute value for that property.

Support for color keywords

You can create a Hex color using any of the CSS color keywords:

#483d8b$hex = Color::fromString('darkslateblue');

hsl(248, 39%, 39%)$hsl = Color::fromString('darkslateblue')->toHsl;

Built-in modifiers

As mentioned above, all of the built-in modifiers can be applied to any color type.

  • alpha - set or adjust the transparency of the color (limited between 0 and 100%).
  • red - set or adjust the redness of the color (limited between 0 and 255).
  • green - set or adjust the greenness of the color (limited between 0 and 255).
  • blue - set or adjust the blueness of the color (limited between 0 and 255).
  • hue - set or adjust the hue of the color.
  • saturation - set or adjust the saturation of the color (limited between 0 and 100%).
  • lightness - set or adjust the lightness of the color (limited between 0 and 100%).
  • invert - inverts the color by adding 180° to the hue.