1<?php
2
3/**
4 *    pChart - a PHP class to build charts!
5 *
6 *    http://pchart.sourceforge.net
7 *
8 *    This program is free software: you can redistribute it and/or modify
9 *    it under the terms of the GNU General Public License as published by
10 *    the Free Software Foundation, either version 1,2,3 of the License, or
11 *    (at your option) any later version.
12 *
13 *    This program is distributed in the hope that it will be useful,
14 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *    GNU General Public License for more details.
17 *
18 *    You should have received a copy of the GNU General Public License
19 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22/**
23 * Color is an immutable class, so all mutator methods return a new
24 * Color instance rather than modifying this instance.
25 *
26 * The immutability is in practice undermined by the fact that the RGB
27 * components are public. This is a transitional detail that should
28 * eventually be done away with.
29 */
30class Color {
31    /**
32     * The members r, g and b are still public since they are used
33     * within GDCanvas. Since we don't have any GDCanvas unit tests
34     * yet, we can't safely make these private at the moment.
35     */
36    public $r;
37    public $g;
38    public $b;
39
40    /**
41     * Initializes a new RGB color
42     *
43     * @param int|string $red  either red channel or the whole color in hex
44     * @param int $green
45     * @param int $blue
46     * @throws InvalidArgumentException
47     */
48    public function __construct($red, $green = null, $blue = null) {
49        if(!is_numeric($red)) {
50            // we assume it's hex
51            list($red, $green, $blue) = $this->Hex2RGB($red);
52        }
53        if(is_null($green)) $green = $red;
54        if(is_null($blue)) $blue = $red;
55
56        if($red < 0 || $red > 255) {
57            throw new InvalidArgumentException("Invalid Red component");
58        }
59
60        if($green < 0 || $green > 255) {
61            throw new InvalidArgumentException("Invalid Green component");
62        }
63
64        if($blue < 0 || $blue > 255) {
65            throw new InvalidArgumentException("Invalid Blue component");
66        }
67
68        $this->r = $red;
69        $this->g = $green;
70        $this->b = $blue;
71    }
72
73    /**
74     * Creates a new random color
75     *
76     * @static
77     * @todo make sure it's a visible color
78     * @param mixed $rand optional externally created random value
79     * @return Color
80     */
81    public static function random($rand = null) {
82        if(!$rand) $rand = rand();
83
84        return new Color('#'.substr(md5($rand),0,6));
85    }
86
87    /**
88     * Return the color as a HTML hex color
89     *
90     * @return string
91     */
92    public function getHex() {
93        return sprintf('#%02x%02x%02x', $this->r, $this->g, $this->b);
94    }
95
96    /**
97     * Return RGB values of a hex color
98     *
99     * @param $color
100     * @return array
101     * @throws InvalidArgumentException
102     */
103    private function Hex2RGB($color) {
104        if(substr($color,0,1) == '#') $color = substr($color, 1);
105
106        if(strlen($color) == 6) {
107            list($r, $g, $b) = array(
108                $color[0].$color[1],
109                $color[2].$color[3],
110                $color[4].$color[5]
111            );
112        } elseif(strlen($color) == 3) {
113            list($r, $g, $b) = array(
114                $color[0].$color[0],
115                $color[1].$color[1],
116                $color[2].$color[2]
117            );
118        } else {
119            throw new InvalidArgumentException("Invalid hex color: ".$color);
120        }
121
122        $r = hexdec($r);
123        $g = hexdec($g);
124        $b = hexdec($b);
125
126        return array($r, $g, $b);
127    }
128
129    /**
130     * Return a new color formed by adding the specified increment to
131     * the R, G and B values
132     */
133    public function addRGBIncrement($increment) {
134        $incremented = new Color($this->r, $this->g, $this->b);
135
136        $incremented->r = $this->truncateColorComponentRange($incremented->r + $increment);
137        $incremented->g = $this->truncateColorComponentRange($incremented->g + $increment);
138        $incremented->b = $this->truncateColorComponentRange($incremented->b + $increment);
139
140        return $incremented;
141    }
142
143    /**
144     * Returns a string representation of the color
145     *
146     * @return string
147     */
148    public function __toString() {
149        return sprintf("Color<%d, %d, %d>", $this->r, $this->g, $this->b);
150    }
151
152    /**
153     * Makes sure the input is a valid color range (0-255)
154     *
155     * @param $input
156     * @return int
157     */
158    private function truncateColorComponentRange($input) {
159        if($input > 255) {
160            return 255;
161        } elseif($input < 0) {
162            return 0;
163        } else {
164            return $input;
165        }
166    }
167
168    /**
169     * Get the red channel
170     *
171     * @return int
172     */
173    public function getR() {
174        return $this->r;
175    }
176
177    /**
178     * Get the green channel
179     *
180     * @return int
181     */
182    public function getG() {
183        return $this->g;
184    }
185
186    /**
187     * Get the blue channel
188     *
189     * @return int
190     */
191    public function getB() {
192        return $this->b;
193    }
194}