mambax7/pedigree

View on GitHub
class/ImageColor.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

namespace XoopsModules\Pedigree;

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Color.php is the implementation of ImageColor.
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
 * the PHP License and are unable to obtain it through the web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category    Image
 * @package     ImageColor
 * @author      Jason Lotito <jason@lehighweb.com>
 * @author      Andrew Morton <drewish@katherinehouse.com>
 * @copyright   2003-2005 The PHP Group
 * @license     http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version     1.1 2006/04/30
 * @link        http://pear.php.net/package/ImageColor
 */

/**
 * ImageColor handles color conversion and mixing.
 *
 * The class is quick, simple to use, and does its job fairly well but it's got
 * some code smells:
 *  - Call setColors() for some functions but not others.
 *  - Different functions expect different color formats. setColors() only
 *    accepts hex while allocateColor() will accept named or hex (provided the
 *    hex ones start with the # character).
 *  - Some conversions go in only one direction, ie HSV->RGB but no RGB->HSV.
 * I'm going to try to straighten out some of this but I'll be hard to do so
 * without breaking backwards compatibility.
 *
 * @category    Image
 * @package     ImageColor
 * @author      Jason Lotito <jason@lehighweb.com>
 * @author      Andrew Morton <drewish@katherinehouse.com>
 * @copyright   2003-2005 The PHP Group
 * @license     http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version     Release: 0.1.2
 * @link        http://pear.php.net/package/ImageColor
 */

/**
 * Class ImageColor
 */
class ImageColor
{
    /**
     * First color that the class handles for ranges and mixes.
     *
     * @var array
     * @access  public
     * @see     setColors()
     */
    public $color1 = [];
    /**
     * Second color that the class handles for ranges and mixes.
     *
     * @var array
     * @access  public
     * @see     setColors()
     */
    public $color2 = [];
    /**
     * Boolean value for determining whether colors outputted should be limited
     * to the web safe pallet or not.
     *
     * @var bool
     * @access  private
     * @see     setWebSafe()
     */
    private $websafeb = false;

    /**
     * Mix two colors together by finding their average. If the colors are not
     * passed as parameters, the class's colors will be mixed instead.
     *
     * @param bool|string $col1 The first color you want to mix
     * @param bool|string $col2 The second color you want to mix
     *
     * @return string The mixed color.
     * @access  public
     * @author  Jason Lotito <jason@lehighweb.com>
     * @uses    setInternalColors() to assign the colors if any are passed to the
     *                class.
     */
    public function mixColors($col1 = false, $col2 = false)
    {
        if ($col1) {
            $this->setInternalColors($col1, $col2);
        }

        // after finding the average, it will be a float. add 0.5 and then
        // cast to an integer to properly round it to an integer.
        $color3[0] = (int)((($this->color1[0] + $this->color2[0]) / 2) + 0.5);
        $color3[1] = (int)((($this->color1[1] + $this->color2[1]) / 2) + 0.5);
        $color3[2] = (int)((($this->color1[2] + $this->color2[2]) / 2) + 0.5);

        if ($this->websafeb) {
            \array_walk($color3, '\_makeWebSafe');
        }

        return $this->rgb2hex($color3);
    }

    /**
     * Determines whether colors the returned by this class will be rounded to
     * the nearest web safe value.
     *
     * @param bool $bool    Indicates if colors should be limited to the
     *                      websafe pallet.
     *
     * @access  public
     * @author  Jason Lotito <jason@lehighweb.com>
     */
    public function setWebSafe($bool = true)
    {
        $this->websafeb = (bool)$bool;
    }

    /**
     * Set the two colors this class uses for mixing and ranges.
     *
     * @param string $col1 The first color in hex format
     * @param string $col2 The second color in hex format
     *
     * @access  public
     * @author  Jason Lotito <jason@lehighweb.com>
     */
    public function setColors($col1, $col2)
    {
        $this->setInternalColors($col1, $col2);
    }

    /**
     * Get the range of colors between the class's two colors, given a degree.
     *
     * @param int $degrees     How large a 'step' we should take between the
     *                         colors.
     *
     * @return array Returns an array of hex strings, one element for each
     *               color.
     * @access  public
     * @author  Jason Lotito <jason@lehighweb.com>
     * @todo    Allow for degrees for individual parts of the colors.
     */
    public function getRange($degrees = 2)
    {
        if (0 == $degrees) {
            $degrees = 1;
        }

        // The degrees give us how much we should advance each color at each
        // phase of the loop.  This way, the advance is equal throughout all
        // the colors.

        $red_steps   = ($this->color2[0] - $this->color1[0]) / $degrees;
        $green_steps = ($this->color2[1] - $this->color1[1]) / $degrees;
        $blue_steps  = ($this->color2[2] - $this->color1[2]) / $degrees;

        $allcolors = [];

        /**
         * The loop stops once any color has gone beyond the end color.
         */

        // Loop through all the degrees between the colors
        for ($x = 0; $x < $degrees; ++$x) {
            $col[0] = $red_steps * $x;
            $col[1] = $green_steps * $x;
            $col[2] = $blue_steps * $x;

            // Loop through each R, G, and B
            for ($i = 0; $i < 3; ++$i) {
                $partcolor = $this->color1[$i] + $col[$i];
                // If the color is less than 256
                if ($partcolor < 256) {
                    // Makes sure the colors is not less than 0
                    if ($partcolor > -1) {
                        $newcolor[$i] = $partcolor;
                    } else {
                        $newcolor[$i] = 0;
                    }
                    // Color was greater than 255
                } else {
                    $newcolor[$i] = 255;
                }
            }

            if ($this->websafeb) {
                \array_walk($newcolor, '\_makeWebSafe');
            }

            $allcolors[] = $this->rgb2hex($newcolor);
        }

        return $allcolors;
    }

    /**
     * Change the lightness of the class's two colors.
     *
     * @param int $degree     The degree of the change. Positive values
     *                        lighten the color while negative values will darken it.
     *
     * @access  public
     * @author  Jason Lotito <jason@lehighweb.com>
     * @uses    ImageColor::$color1 as an input and return value.
     * @uses    ImageColor::$color2 as an input and return value.
     */
    public function changeLightness($degree = 10)
    {
        $color1 = &$this->color1;
        $color2 = &$this->color2;

        for ($x = 0; $x < 3; ++$x) {
            if (($color1[$x] + $degree) < 256) {
                if (($color1[$x] + $degree) > -1) {
                    $color1[$x] += $degree;
                } else {
                    $color1[$x] = 0;
                }
            } else {
                $color1[$x] = 255;
            }

            if (($color2[$x] + $degree) < 256) {
                if (($color2[$x] + $degree) > -1) {
                    $color2[$x] += $degree;
                } else {
                    $color2[$x] = 0;
                }
            } else {
                $color2[$x] = 255;
            }
        }
    }

    /**
     * Determine if a light or dark text color would be more readable on a
     * background of a given color. This is determined by the G(reen) value of
     * RGB. You can change the dark and the light colors from their default
     * black and white.
     *
     * @param string|array $color The hex color to analyze
     * @param string       $light The light color value to return if we should
     *                            have light text.
     * @param string       $dark  The dark color value to return if we should have
     *                            dark text.
     *
     * @return string The light or dark value which would make the text most
     *                readable.
     * @access  public
     * @static
     * @author  Jason Lotito <jason@lehighweb.com>
     */
    public function getTextColor($color, $light = '#FFFFFF', $dark = '#000000')
    {
        $color = $this->splitColor($color);
        if ($color[1] > \hexdec('66')) {
            return $dark;
        }

        return $light;
    }

    /**
     * Internal method to set the colors.
     *
     * @param string $col1 First color, either a name or hex value
     * @param string $col2 Second color, either a name or hex value
     *
     * @access  private
     * @author  Jason Lotito <jason@lehighweb.com>
     */
    private function setInternalColors($col1, $col2)
    {
        if ($col1) {
            $this->color1 = $this->splitColor($col1);
        }
        if ($col2) {
            $this->color2 = $this->splitColor($col2);
        }
    }

    /**
     * Given a color, properly split it up into a 3 element RGB array.
     *
     * @param string $color The color.
     *
     * @return array A three element RGB array.
     * @access  private
     * @static
     * @author  Jason Lotito <jason@lehighweb.com>
     */
    private function splitColor($color)
    {
        $color = \str_replace('#', '', $color);
        $c[]   = \hexdec(\mb_substr($color, 0, 2));
        $c[]   = \hexdec(\mb_substr($color, 2, 2));
        $c[]   = \hexdec(\mb_substr($color, 4, 2));

        return $c;
    }

    /**
     * This is deprecated. Use rgb2hex() instead.
     *
     * @access     private
     * @param $color
     *
     * @return string
     * @deprecated Function deprecated after 1.0.1
     * @see        rgb2hex().
     *
     */
    private function returnColor($color)
    {
        return $this->rgb2hex($color);
    }

    /**
     * Convert an RGB array to a hex string.
     *
     * @param array $color 3 element RGB array.
     *
     * @return string Hex color string.
     * @access  public
     * @static
     * @author  Jason Lotito <jason@lehighweb.com>
     * @see     hex2rgb()
     */
    public function rgb2hex($color)
    {
        return \sprintf('%02X%02X%02X', $color[0], $color[1], $color[2]);
    }

    /**
     * Convert a hex color string into an RGB array. An extra fourth element
     * will be returned with the original hex value.
     *
     * @param string $hex Hex color string.
     *
     * @return array RGB color array with an extra 'hex' element containing
     *               the original hex string.
     * @access  public
     * @static
     * @author  Jason Lotito <jason@lehighweb.com>
     * @see     rgb2hex()
     */
    public function hex2rgb($hex)
    {
        $return        = $this->splitColor($hex);
        $return['hex'] = $hex;

        return $return;
    }

    /**
     * Convert an HSV (Hue, Saturation, Brightness) value to RGB.
     *
     * @param int $h Hue
     * @param int $s Saturation
     * @param int $v Brightness
     *
     * @return array RGB array.
     * @access  public
     * @static
     * @author  Jason Lotito <jason@lehighweb.com>
     * @uses    hsv2hex() to convert the HSV value to Hex.
     * @uses    hex2rgb() to convert the Hex value to RGB.
     */
    public function hsv2rgb($h, $s, $v)
    {
        return $this->hex2rgb($this->hsv2hex($h, $s, $v));
    }

    /**
     * Convert HSV (Hue, Saturation, Brightness) to a hex color string.
     *
     * Originally written by Jurgen Schwietering. Integrated into the class by
     * Jason Lotito.
     *
     * @param int $h Hue
     * @param int $s Saturation
     * @param int $v Brightness
     *
     * @return string The hex string.
     * @access  public
     * @static
     * @author  Jurgen Schwietering <jurgen@schwietering.com>
     * @uses    rgb2hex() to convert the return value to a hex string.
     */
    public function hsv2hex($h, $s, $v)
    {
        $s /= 256.0;
        $v /= 256.0;
        if (0.0 == $s) {
            $r = $g = $b = $v;

            return '';
        }
        $h = $h / 256.0 * 6.0;
        $i = \floor($h);
        $f = $h - $i;

        $v *= 256.0;
        $p = (int)($v * (1.0 - $s));
        $q = (int)($v * (1.0 - $s * $f));
        $t = (int)($v * (1.0 - $s * (1.0 - $f)));
        switch ($i) {
            case 0:
                $r = $v;
                $g = $t;
                $b = $p;
                break;
            case 1:
                $r = $q;
                $g = $v;
                $b = $p;
                break;
            case 2:
                $r = $p;
                $g = $v;
                $b = $t;
                break;
            case 3:
                $r = $p;
                $g = $q;
                $b = $v;
                break;
            case 4:
                $r = $t;
                $g = $p;
                $b = $v;
                break;
            default:
                $r = $v;
                $g = $p;
                $b = $q;
                break;
        }

        return $this->rgb2hex([$r, $g, $b]);
    }

    /**
     * Allocates a color in the given image.
     *
     * User defined color specifications get translated into an array of RGB
     * values.
     *
     * @param resource     $img   Image handle
     * @param string|array $color Name or hex string or an RGB array.
     *
     * @return bool Image color handle.
     * @access  public
     * @static
     * @uses    imagefilledarc() to allocate the color.
     * @uses    color2RGB() to parse the color into RGB values.
     */
    public function allocateColor($img, $color)
    {
        $color = $this->color2RGB($color);

        return \imagefilledarc($img, $color[0], $color[1], $color[2]);
    }

    /**
     * Convert a named or hex color string to an RGB array. If the color begins
     * with the # character it will be treated as a hex value. Everything else
     * will be treated as a named color. If the named color is not known, black
     * will be returned.
     *
     * @param string $color
     *
     * @return array RGB color
     * @access  public
     * @static
     * @author  Laurent Laville <pear@laurent-laville.org>
     * @uses    hex2rgb() to convert colors begining with the # character.
     * @uses    namedColor2RGB() to convert everything not starting with a #.
     */
    public function color2RGB($color)
    {
        $c = [];

        if ('#' === $color[0]) {
            $c = $this->hex2rgb($color);
        } else {
            $c = $this->namedColor2RGB($color);
        }

        return $c;
    }

    /**
     * Convert a named color to an RGB array. If the color is unknown black
     * is returned.
     *
     * @param string $color Case insensitive color name.
     *
     * @return array RGB color array. If the color was unknown, the result
     *               will be black.
     * @access  public
     * @static
     * @author  Sebastian Bergmann <sb@sebastian-bergmann.de>
     */
    public function namedColor2RGB($color)
    {
        static $colornames;

        if (!isset($colornames)) {
            $colornames = [
                'aliceblue'            => [240, 248, 255],
                'antiquewhite'         => [250, 235, 215],
                'aqua'                 => [0, 255, 255],
                'aquamarine'           => [127, 255, 212],
                'azure'                => [240, 255, 255],
                'beige'                => [245, 245, 220],
                'bisque'               => [255, 228, 196],
                'black'                => [0, 0, 0],
                'blanchedalmond'       => [255, 235, 205],
                'blue'                 => [0, 0, 255],
                'blueviolet'           => [138, 43, 226],
                'brown'                => [165, 42, 42],
                'burlywood'            => [222, 184, 135],
                'cadetblue'            => [95, 158, 160],
                'chartreuse'           => [127, 255, 0],
                'chocolate'            => [210, 105, 30],
                'coral'                => [255, 127, 80],
                'cornflowerblue'       => [100, 149, 237],
                'cornsilk'             => [255, 248, 220],
                'crimson'              => [220, 20, 60],
                'cyan'                 => [0, 255, 255],
                'darkblue'             => [0, 0, 13],
                'darkcyan'             => [0, 139, 139],
                'darkgoldenrod'        => [184, 134, 11],
                'darkgray'             => [169, 169, 169],
                'darkgreen'            => [0, 100, 0],
                'darkkhaki'            => [189, 183, 107],
                'darkmagenta'          => [139, 0, 139],
                'darkolivegreen'       => [85, 107, 47],
                'darkorange'           => [255, 140, 0],
                'darkorchid'           => [153, 50, 204],
                'darkred'              => [139, 0, 0],
                'darksalmon'           => [233, 150, 122],
                'darkseagreen'         => [143, 188, 143],
                'darkslateblue'        => [72, 61, 139],
                'darkslategray'        => [47, 79, 79],
                'darkturquoise'        => [0, 206, 209],
                'darkviolet'           => [148, 0, 211],
                'deeppink'             => [255, 20, 147],
                'deepskyblue'          => [0, 191, 255],
                'dimgray'              => [105, 105, 105],
                'dodgerblue'           => [30, 144, 255],
                'firebrick'            => [178, 34, 34],
                'floralwhite'          => [255, 250, 240],
                'forestgreen'          => [34, 139, 34],
                'fuchsia'              => [255, 0, 255],
                'gainsboro'            => [220, 220, 220],
                'ghostwhite'           => [248, 248, 255],
                'gold'                 => [255, 215, 0],
                'goldenrod'            => [218, 165, 32],
                'gray'                 => [128, 128, 128],
                'green'                => [0, 128, 0],
                'greenyellow'          => [173, 255, 47],
                'honeydew'             => [240, 255, 240],
                'hotpink'              => [255, 105, 180],
                'indianred'            => [205, 92, 92],
                'indigo'               => [75, 0, 130],
                'ivory'                => [255, 255, 240],
                'khaki'                => [240, 230, 140],
                'lavender'             => [230, 230, 250],
                'lavenderblush'        => [255, 240, 245],
                'lawngreen'            => [124, 252, 0],
                'lemonchiffon'         => [255, 250, 205],
                'lightblue'            => [173, 216, 230],
                'lightcoral'           => [240, 128, 128],
                'lightcyan'            => [224, 255, 255],
                'lightgoldenrodyellow' => [250, 250, 210],
                'lightgreen'           => [144, 238, 144],
                'lightgrey'            => [211, 211, 211],
                'lightpink'            => [255, 182, 193],
                'lightsalmon'          => [255, 160, 122],
                'lightseagreen'        => [32, 178, 170],
                'lightskyblue'         => [135, 206, 250],
                'lightslategray'       => [119, 136, 153],
                'lightsteelblue'       => [176, 196, 222],
                'lightyellow'          => [255, 255, 224],
                'lime'                 => [0, 255, 0],
                'limegreen'            => [50, 205, 50],
                'linen'                => [250, 240, 230],
                'magenta'              => [255, 0, 255],
                'maroon'               => [128, 0, 0],
                'mediumaquamarine'     => [102, 205, 170],
                'mediumblue'           => [0, 0, 205],
                'mediumorchid'         => [186, 85, 211],
                'mediumpurple'         => [147, 112, 219],
                'mediumseagreen'       => [60, 179, 113],
                'mediumslateblue'      => [123, 104, 238],
                'mediumspringgreen'    => [0, 250, 154],
                'mediumturquoise'      => [72, 209, 204],
                'mediumvioletred'      => [199, 21, 133],
                'midnightblue'         => [25, 25, 112],
                'mintcream'            => [245, 255, 250],
                'mistyrose'            => [255, 228, 225],
                'moccasin'             => [255, 228, 181],
                'navajowhite'          => [255, 222, 173],
                'navy'                 => [0, 0, 128],
                'oldlace'              => [253, 245, 230],
                'olive'                => [128, 128, 0],
                'olivedrab'            => [107, 142, 35],
                'orange'               => [255, 165, 0],
                'orangered'            => [255, 69, 0],
                'orchid'               => [218, 112, 214],
                'palegoldenrod'        => [238, 232, 170],
                'palegreen'            => [152, 251, 152],
                'paleturquoise'        => [175, 238, 238],
                'palevioletred'        => [219, 112, 147],
                'papayawhip'           => [255, 239, 213],
                'peachpuff'            => [255, 218, 185],
                'peru'                 => [205, 133, 63],
                'pink'                 => [255, 192, 203],
                'plum'                 => [221, 160, 221],
                'powderblue'           => [176, 224, 230],
                'purple'               => [128, 0, 128],
                'red'                  => [255, 0, 0],
                'rosybrown'            => [188, 143, 143],
                'royalblue'            => [65, 105, 225],
                'saddlebrown'          => [139, 69, 19],
                'salmon'               => [250, 128, 114],
                'sandybrown'           => [244, 164, 96],
                'seagreen'             => [46, 139, 87],
                'seashell'             => [255, 245, 238],
                'sienna'               => [160, 82, 45],
                'silver'               => [192, 192, 192],
                'skyblue'              => [135, 206, 235],
                'slateblue'            => [106, 90, 205],
                'slategray'            => [112, 128, 144],
                'snow'                 => [255, 250, 250],
                'springgreen'          => [0, 255, 127],
                'steelblue'            => [70, 130, 180],
                'tan'                  => [210, 180, 140],
                'teal'                 => [0, 128, 128],
                'thistle'              => [216, 191, 216],
                'tomato'               => [255, 99, 71],
                'turquoise'            => [64, 224, 208],
                'violet'               => [238, 130, 238],
                'wheat'                => [245, 222, 179],
                'white'                => [255, 255, 255],
                'whitesmoke'           => [245, 245, 245],
                'yellow'               => [255, 255, 0],
                'yellowgreen'          => [154, 205, 50],
            ];
        }

        $color = \mb_strtolower($color);

        if (isset($colornames[$color])) {
            return $colornames[$color];
        }

        return [0, 0, 0];
    }

    /**
     * Convert an RGB percentage string into an RGB array.
     *
     * @param string|array $color Percentage color string like "50%,20%,100%".
     *
     * @return array RGB color array.
     * @access  public
     * @static
     */
    public function percentageColor2RGB($color)
    {
        // remove spaces
        $color = \str_replace(' ', '', $color);
        // remove the percent signs
        $color = \str_replace('%', '', $color);
        // split the string by commas
        $color = \explode(',', $color);

        $ret = [];
        foreach ($color as $k => $v) {
            // range checks
            if ($v <= 0) {
                $ret[$k] = 0;
            } elseif ($v <= 100) {
                // add 0.5 then cast to an integer to round the value.
                $ret[$k] = (int)((2.55 * $v) + 0.5);
            } else {
                $ret[$k] = 255;
            }
        }

        return $ret;
    }
}

// For Array Walk
// {{{
/**
 * Function for array_walk() to round colors to the closest web safe value.
 *
 * @param int $color One channel of an RGB color.
 *
 * @return int The websafe equivalent of the color channel.
 * @author  Jason Lotito <jason@lehighweb.com>
 * @author  Andrew Morton <drewish@katherinehouse.com>
 * @access  private
 * @static
 */
function _makeWebSafe(&$color)
{
    if ($color < 0x1a) {
        $color = 0x00;
    } elseif ($color < 0x4d) {
        $color = 0x33;
    } elseif ($color < 0x80) {
        $color = 0x66;
    } elseif ($color < 0xB3) {
        $color = 0x99;
    } elseif ($color < 0xE6) {
        $color = 0xCC;
    } else {
        $color = 0xFF;
    }

    return $color;
}
// }}}