-
Notifications
You must be signed in to change notification settings - Fork 3
/
Color2.php
400 lines (382 loc) · 14.1 KB
/
Color2.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Color2.php is the root of the Image_Colro2 package.
*
* PHP version 5
*
* @category Image
* @package Image_Color2
* @author andrew morton <drewish@katherinehouse.com>
* @copyright 2005 Andrew Morton
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version SVN: $Id$
* @link http://pear.php.net/package/Image_Color2
*/
/**
* As a PEAR package, all exceptions are either PEAR_Exception or derived from
* it.
*/
require_once 'PEAR/Exception.php';
/**
* This class requires the Hex color model because it's the standard/simplest
* way to represent an RGB values as a string.
*/
require_once 'Image/Color2/Model/Hex.php';
/**
* The class requires the Named color model because it's cool to be able to
* do things like $color = Image_Color2('teal') and have it work.
*/
require_once 'Image/Color2/Model/Named.php';
/**
* Image_Color2 is a PHP5 package to convert between RGB and various other
* color models.
*
* Here it is in action:
* <code>
* <?php
* require_once 'Image/Color2.php';
*
* \/\/ load a named string and view it as a hex string
* $red = new Image_Color2('red');
* print $red->getHex() . "\n"; # '#ff0000'
*
* \/\/ load a hex string and view it as an RGB array
* $blue = new Image_Color2('#0000ff');
* var_dump($blue->getRgb()); # array(0, 0, 255, 'type' => 'rgb')
*
* \/\/ find the average of red and blue (i.e. mix them)...
* $avg = Image_Color2::average($red, $blue);
* \/\/ ...then convert it to named color
* print $avg->convertTo('named')->getString() . "\n"; # 'purple'
*
* \/\/ convert blue from RGB to HSV...
* $hsv = $blue->convertTo('hsv');
* \/\/ ...then display it as an HSV string and array
* print $hsv->getString() . "\n"; # '240 100% 100%'
* print_r($hsv->getArray()); # array(240, 100, 100, 'type' => 'hsv')
* ?>
* </code>
*
* When converting from one color model to another, say HSL to HSV, an
* intermediate RGB value is used. Each color model has a distinct gamut, or
* range of expressible color values, and the use of an intermediate value
* makes the conversions very imprecise. As a result, the output of these
* conversions should be viewed as an approximation, unfit for any application
* where color matching is important.
*
* A large portion of the code for this package was derived from the work of
* Jason Lotito and the other contributors to Image_Color.
*
* @category Image
* @package Image_Color2
* @author andrew morton <drewish@katherinehouse.com>
* @copyright 2005 Andrew Mortin
* @license http://opensource.org/licenses/lgpl-license.php
* GNU Lesser General Public License, Version 2.1
* @version Release: 0.1.2
* @link http://pear.php.net/package/Image_Color2
* @see Image_Color
* @todo Figure out a clean way to support alpha channels. This class
* will preserve them but as soon as you call a color model for
* conversion they'll be discarded. I think this class will need
* to maintain a separate variable for the alpha channel it really
* seems independent of the color model. 50% red would be the same
* no mater how you represent it.
*/
class Image_Color2
{
/**
* RGB value of the color. After it's assigned by the constructor it should
* never be null.
* @var array
*/
protected $rgb;
/**
* Color model used to read a non-RGB color. This is assigned by the
* constructor. If the source color is RGB no color model is needed so this
* will be null.
* @var Image_Color2_Model
*/
protected $model = null;
/**
* Construct a color from a string, array, or an instance of
* Image_Color2_Model.
* <code>
* \/\/ from a named string
* $red = new Image_Color2('red');
* print $red->getHex(); \/\/ '#ff0000'
*
* \/\/ from a hex string
* $blue = new Image_Color2('#0000ff');
* print $blue->getHex(); \/\/ '#0000ff'
*
* \/\/ from an array
* $black = new Image_Color2(array(0,0,0));
* print $black->getHex(); \/\/ '#000000'
* </code>
*
* @param array|string|Image_Color2_Model $src specifying a color.
* Non-RGB arrays should include the type element to specify a
* color model. Strings will be interpreted as hex if they
* begin with a #, otherwise they'll be treated as named colors.
*
* @throws PEAR_Exception if the color cannot be loaded.
* @uses createModelReflectionMethod() If the color is non-RGB the
* function is used to construct an Image_Color2_Model for
* conversion.
*/
public function __construct($src)
{
if (is_array($src)) {
// check if a type parameter was offered up.
$type = (isset($src['type'])) ? $src['type'] : '';
// type needs to be a proper case to match the class name.
$type = ucwords($type);
if (!$type || $type == 'Rgb') {
$src['type'] = 'rgb';
$this->model = null;
$this->rgb = $src;
} else {
$method = self::createModelReflectionMethod($type, 'fromArray');
$this->model = $method->invoke(null, $src);
}
} else if (is_string($src)) {
if ('#' == substr($src, 0, 1)) {
$this->model = Image_Color2_Model_Hex::fromString($src);
} else {
$this->model = Image_Color2_Model_Named::fromString($src);
}
} else if ($src instanceof Image_Color2_Model) {
$this->model = $src;
}
// at this point we either have a model, an rgb value, or a problem.
if (!is_null($this->model)) {
$this->rgb = $this->model->getRgb();
}
if (is_null($this->rgb)) {
throw new PEAR_Exception('Invalid color definition.');
}
}
/**
* Return a ReflectionMethod of a Image_Color2_Model implementation found in
* the Image/Color2/Model directory.
*
* @param string $type Name of a ColorModel implementation (i.e. for
* Image_Color2_Model_Hsv this would be 'hsv').
* @param string $methodName Name of a static factory method on the
* ColorModel interface ('fromArray', 'fromString', or 'fromRgb').
*
* @return ReflectionMethod
* @throws PEAR_Exception if the class cannot be loaded, or it does not
* implement the Image_Color2_Model interface.
* @uses Image_Color2_Model As the interface for color conversion.
* @internal
*/
protected static function createModelReflectionMethod($type, $methodName)
{
$type = ucfirst($type);
$classpath = 'Image/Color2/Model/' . $type . '.php';
if (!include_once $classpath) {
throw new PEAR_Exception(
"File '{$classpath}' for $type was not found.");
}
$classname = 'Image_Color2_Model_' . $type;
if (!class_exists($classname)) {
throw new PEAR_Exception(
"Class '{$classname}' for $type was not found.");
}
$reflect = new ReflectionClass($classname);
if (!$reflect->implementsInterface('Image_Color2_Model')) {
throw new PEAR_Exception(
"Class '{$classname}' doesn't implement Image_Color2_Model.");
}
return $reflect->getMethod($methodName);
}
/**
* Return the average of the RGB value of two Image_Color2 objects. If
* both objects have an alpha channel it will be averaged too.
* <code>
* $red = new Image_Color2('red');
* $blue = new Image_Color2('blue');
* $color = Image_Color2::average($red, $blue);
* print $color->convertTo('named')->getString(); \/\/ 'purple'
* </code>
*
* @param Image_Color2 $left Left color
* @param Image_Color2 $right Right color
*
* @return Image_Color2
*/
public static function average(Image_Color2 $left, Image_Color2 $right)
{
$lrgb = $left->getRgb();
$rrgb = $right->getRgb();
// remove the type element so we can properly compare lengths
unset($lrgb['type']);
unset($rrgb['type']);
// the color may be RGB or RGBA, either way, they need to be the same
// length.
$size = min(count($lrgb), count($rrgb));
// find the average of each pair of elements
$avg = array();
for ($i = 0; $i < $size; $i++) {
$avg[] = (integer) ((($lrgb[$i] + $rrgb[$i] ) / 2) + 0.5);
}
return new Image_Color2($avg);
}
/**
* Return a copy of this color converted to another color model.
* <code>
* $blue = new Image_Color2('#0000ff');
* $hsv = $blue->convertTo('hsv');
* print $hsv->getString(); \/\/ '240 100% 100%'
* </code>
*
* @param string $type Name of a color model. If this variable is foo then a
* class named Image_Color2_Model_Foo is required.
*
* @return Image_Color2
* @throws PEAR_Exception if the desired color model cannot be found or it
* cannot convert the color.
* @uses createModelReflectionMethod() The function is used to
* construct an Image_Color2_Model that is passed back to the
* constructor.
*/
public function convertTo($type)
{
$method = self::createModelReflectionMethod($type, 'fromRgb');
$model = $method->invoke(null, $this->rgb);
if (is_null($model)) {
throw new PEAR_Exception(
"The '{$type}' color model couldn't convert the color.");
} else {
return new Image_Color2($model);
}
}
/**
* Return the color as a PEAR style RGB array.
*
* The optional 'type' => 'rgb' element should always be included.
*
* <code>
* $color = new Image_Color2(array(0,128,255));
* print_r($color->getRgb()); \/\/ array(0, 128, 255, 'type' => 'rgb')
*
* \/\/ While PHP barfs if you write:
* print $color->getRgb()[0]; \/\/ NOT VALID
* \/\/ You can pass in an optional index parameter for the same effect:
* print $color->getRgb(2); \/\/ 255
* print $color->getRgb('type'); \/\/ 'rgb'
* </code>
*
* @param mixed $index An optional index value to select an element of
* the array. A null value returns the entire array.
*
* @return mixed An array if no index parameter is provided. Otherwise,
* provided the index is valid, a member of the array.
* @uses $rgb Returns a copy of the array.
*/
public function getRgb($index = null)
{
// return an element if requested
if (isset($index)) {
return $this->rgb[$index];
} else {
return $this->rgb;
}
}
/**
* Return the color in a color model dependant, array format. If the color
* was specified as an RGB array this will return the same results as
* getRgb(). Otherwise, the results depend on the underlying color model.
*
* <code>
* $color = new Image_Color2(array(0,128,255));
* print_r($color->getArray()); \/\/ array(0, 128, 255, 'type' => 'rgb')
*
* \/\/ While PHP barfs if you write:
* print $color->getArray()[0]; \/\/ NOT VALID
* \/\/ You can pass in an optional index parameter for the same effect:
* print $color->getArray(2); \/\/ 255
* print $color->getArray('type'); \/\/ 'rgb'
* </code>
*
* @param mixed $index An optional index value to select an element of
* the array. A null value returns the entire array.
*
* @return mixed An array if no index is provided. Otherwise, provided the
* index is valid, a member of the array.
* @uses $rgb If the color was specified as RGB.
* @uses $model If the color wasn't specified as RGB.
* @uses Image_Color2_Model::getArray() For the color conversion if it
* wasn't originally RGB.
*/
public function getArray($index = null)
{
// get the array
if (is_null($this->model)) {
$ret = $this->rgb;
} else {
$ret = $this->model->getArray();
}
// return an element if requested
if (isset($index)) {
return $ret[$index];
} else {
return $ret;
}
}
/**
* Return a hex string representation of the color.
*
* <code>
* $color = new Image_Color2(array(171, 205, 239));
* print $color->getHex(); \/\/ '#abcdef'
* </code>
*
* To obtain a websafe color, convert it using the
* {@link Image_Color2_Model_WebsafeHex websafe hex color model}:
* <code>
* $color = new Image_Color2('#abcdef');
* print $color->convertTo('WebsafeHex')->getString(); \/\/ '#99ccff'
* </code>
*
* @return string Hex RGB string in the form #abcdef.
* @uses Image_Color2_Model_Hex::getString()
*/
public function getHex()
{
return Image_Color2_Model_Hex::fromRgb($this->rgb)->getString();
}
/**
* Return the color as a string. If the color was specified as an RGB array
* this is exactly the same as calling getHex(). Otherwise, the results
* depend on the underlying color model.
*
* <code>
* $red = new Image_Color2(array(255,0,0));
* print $red->getString(); \/\/ '#ff0000'
*
* $orange = new Image_Color2('orange');
* print $orange->getString(); \/\/ 'orange'
*
* $hsl = $orange->convertTo('hsl');
* print $hsl->getString(); \/\/ '38 100% 50%'
* </code>
*
* @return string
* @uses Image_Color2_Model::getString() If the color wasn't originally
* RGB.
* @uses getHex() If the color was originally specified as RGB.
*/
public function getString()
{
if (is_null($this->model)) {
return $this->getHex();
} else {
return $this->model->getString();
}
}
}
?>