New upstream release
Jonathan Carter
2 years ago
0 | 2016-08-01 | |
1 | - Realease 0.1a4 | |
2 | - Fixed missing attribute grapefruit_test.py | |
3 | ||
0 | 4 | 2008-06-15 |
1 | 5 | - Released 0.1a3 |
2 | 6 | - Added Gradient |
0 | Metadata-Version: 1.0 | |
1 | Name: grapefruit | |
2 | Version: 0.1a3 | |
3 | Summary: A module to manipulate color information easily. | |
4 | Home-page: http://code.google.com/p/grapefruit/ | |
5 | Author: Xavier Basty | |
6 | Author-email: xbasty@gmail.com | |
7 | License: Apache License 2.0 | |
8 | Download-URL: http://grapefruit.googlecode.com/files/grapefruit-0.1a3.tar.gz | |
9 | Description: ===================== | |
10 | README for GrapeFruit | |
11 | ===================== | |
12 | ||
13 | Installing | |
14 | ============ | |
15 | ||
16 | **From the sources:** | |
17 | ||
18 | Download the latest grapefruit library from: | |
19 | ||
20 | http://code.google.com/p/grapefruit/ | |
21 | ||
22 | ||
23 | Untar the source distribution and run:: | |
24 | ||
25 | $ python setup.py build | |
26 | $ python setup.py install | |
27 | ||
28 | ||
29 | Testing | |
30 | ========= | |
31 | ||
32 | With setuptools installed:: | |
33 | ||
34 | $ python setup.py test | |
35 | ||
36 | Without setuptools installed:: | |
37 | ||
38 | $ python grapefruit_test.py | |
39 | ||
40 | ||
41 | Getting the code | |
42 | ================== | |
43 | ||
44 | View the trunk at: | |
45 | ||
46 | http://grapefruit.googlecode.com/svn/trunk/ | |
47 | ||
48 | Check out the latest development version anonymously with:: | |
49 | ||
50 | $ svn checkout http://grapefruit.googlecode.com/svn/trunk/ GrapeFruit | |
51 | ||
52 | Documentation | |
53 | =============== | |
54 | ||
55 | You can download a compiled version of the documentation at: | |
56 | ||
57 | http://http://code.google.com/p/grapefruit/downloads/list?q=label:Type-Docs | |
58 | ||
59 | The documentation is generated from reStructuredText sources by Sphinx. | |
60 | If you need to build it, go into the doc folder and run:: | |
61 | ||
62 | make <builder> | |
63 | ||
64 | Or, if you're running windows:: | |
65 | ||
66 | makedoc.cmd | |
67 | ||
68 | ||
69 | License | |
70 | ========= | |
71 | ||
72 | :: | |
73 | ||
74 | Copyright (c) 2008, Xavier Basty | |
75 | ||
76 | Licensed under the Apache License, Version 2.0 (the "License"); | |
77 | you may not use this file except in compliance with the License. | |
78 | You may obtain a copy of the License at | |
79 | ||
80 | http://www.apache.org/licenses/LICENSE-2.0 | |
81 | ||
82 | Unless required by applicable law or agreed to in writing, software | |
83 | distributed under the License is distributed on an "AS IS" BASIS, | |
84 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
85 | See the License for the specific language governing permissions and | |
86 | limitations under the License. | |
87 | ||
88 | 2008-06-15 | |
89 | - Released 0.1a3 | |
90 | - Added Gradient | |
91 | ||
92 | 2008-06-01 | |
93 | - Fixed overflow in alpha blending. | |
94 | ||
95 | 2008-05-29 | |
96 | - Added Saturate/Desaturate. | |
97 | - Added MonochromeScheme | |
98 | - Added the mode parameter to the generation methods to choose the | |
99 | color wheel use for the generation (ryb/rgb). | |
100 | ||
101 | 2008-05-28 | |
102 | - Added the RGB<->RYB hue conversion. | |
103 | - Added an angle parameter to the tetrad scheme to control the shape of | |
104 | the rectangle. | |
105 | ||
106 | 2008-05-27 | |
107 | - Released 0.1a2 | |
108 | ||
109 | 2008-05-24 | |
110 | ||
111 | - Fixed the HSL->RGB conversion | |
112 | (the modulo in the hue conversion was 60 instead of 6.0!) | |
113 | Updated the unit tests (which were wrong!) | |
114 | ||
115 | 2008-05-24 | |
116 | ||
117 | Released 0.1a1 | |
118 | - Convert the documentation to Sphinx | |
119 | - Completed the unit tests | |
120 | - Fixed some stupid typos | |
121 | ||
122 | 2008-05-22 | |
123 | ||
124 | - Refactored pretty much everything to more standard "Python coding style". | |
125 | - Replaced the global variables by Color properties. | |
126 | - Moved the module functions to static methods of Color. | |
127 | - Completed the CIE white point dictionary to include all the standard | |
128 | illuminants. | |
129 | - Added doctest for all the functions. | |
130 | - Fixed the conversions factors to get better results (more exact and | |
131 | more symmetric). | |
132 | - Changed the range of the L component from [0~1] to [0~100] (as it should | |
133 | have been). | |
134 | - Added packaging data and setup. | |
135 | - Changed the structure of the unit tests. | |
136 | ||
137 | 2008-05-08 | |
138 | ||
139 | Released 0.1a0 | |
140 | - Initial checkin of grapefruit | |
141 | ||
142 | Keywords: color conversion | |
143 | Platform: UNKNOWN | |
144 | Classifier: Development Status :: 3 - Alpha | |
145 | Classifier: Intended Audience :: Developers | |
146 | Classifier: License :: OSI Approved :: Apache Software License | |
147 | Classifier: Topic :: Software Development :: Libraries :: Python Modules | |
148 | Classifier: Topic :: Multimedia :: Graphics |
0 | ===================== | |
1 | README for GrapeFruit | |
2 | ===================== | |
3 | ||
4 | Installing | |
5 | ============ | |
6 | ||
7 | **From the sources:** | |
8 | ||
9 | Download the latest grapefruit library from: | |
10 | ||
11 | http://code.google.com/p/grapefruit/ | |
12 | ||
13 | ||
14 | Untar the source distribution and run:: | |
15 | ||
16 | $ python setup.py build | |
17 | $ python setup.py install | |
18 | ||
19 | ||
20 | Testing | |
21 | ========= | |
22 | ||
23 | With setuptools installed:: | |
24 | ||
25 | $ python setup.py test | |
26 | ||
27 | Without setuptools installed:: | |
28 | ||
29 | $ python grapefruit_test.py | |
30 | ||
31 | ||
32 | Getting the code | |
33 | ================== | |
34 | ||
35 | View the trunk at: | |
36 | ||
37 | http://grapefruit.googlecode.com/svn/trunk/ | |
38 | ||
39 | Check out the latest development version anonymously with:: | |
40 | ||
41 | $ svn checkout http://grapefruit.googlecode.com/svn/trunk/ GrapeFruit | |
42 | ||
43 | Documentation | |
44 | =============== | |
45 | ||
46 | You can download a compiled version of the documentation at: | |
47 | ||
48 | http://http://code.google.com/p/grapefruit/downloads/list?q=label:Type-Docs | |
49 | ||
50 | The documentation is generated from reStructuredText sources by Sphinx. | |
51 | If you need to build it, go into the doc folder and run:: | |
52 | ||
53 | make <builder> | |
54 | ||
55 | Or, if you're running windows:: | |
56 | ||
57 | makedoc.cmd | |
58 | ||
59 | ||
60 | License | |
61 | ========= | |
62 | ||
63 | :: | |
64 | ||
65 | Copyright (c) 2008, Xavier Basty | |
66 | ||
67 | Licensed under the Apache License, Version 2.0 (the "License"); | |
68 | you may not use this file except in compliance with the License. | |
69 | You may obtain a copy of the License at | |
70 | ||
71 | http://www.apache.org/licenses/LICENSE-2.0 | |
72 | ||
73 | Unless required by applicable law or agreed to in writing, software | |
74 | distributed under the License is distributed on an "AS IS" BASIS, | |
75 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
76 | See the License for the specific language governing permissions and | |
77 | limitations under the License. |
0 | ===================== | |
1 | README for GrapeFruit | |
2 | ===================== | |
3 | ||
4 | GrapeFruit is a pure Python module that let you easily manipulate and convert color information. | |
5 | Its Primary goal is to be *natural* and *flexible*. | |
6 | ||
7 | The following color systems are supported by GrapeFruit: | |
8 | * RGB (sRGB) | |
9 | * HSL | |
10 | * HSV | |
11 | * YIQ | |
12 | * YUV | |
13 | * CIE-XYZ | |
14 | * CIE-LAB (with the illuminant you want) | |
15 | * CMY | |
16 | * CMYK | |
17 | * HTML/CSS color definition (#RRGGBB, #RGB or the X11 color name) | |
18 | * RYB (artistic color wheel | |
19 | Installing | |
20 | ============ | |
21 | ||
22 | **From the sources:** | |
23 | ||
24 | Download the latest grapefruit library from: | |
25 | ||
26 | https://github.com/xav/Grapefruit | |
27 | ||
28 | ||
29 | Untar the source distribution and run:: | |
30 | ||
31 | $ python setup.py build | |
32 | $ python setup.py install | |
33 | ||
34 | ||
35 | Testing | |
36 | ========= | |
37 | ||
38 | With setuptools installed:: | |
39 | ||
40 | $ python setup.py test | |
41 | ||
42 | Without setuptools installed:: | |
43 | ||
44 | $ python grapefruit_test.py | |
45 | ||
46 | ||
47 | Documentation | |
48 | =============== | |
49 | ||
50 | You can download a compiled version of the documentation at: | |
51 | ||
52 | https://github.com/xav/Grapefruit/downloads | |
53 | ||
54 | The documentation is generated from reStructuredText sources by Sphinx. | |
55 | If you need to build it, go into the doc folder and run:: | |
56 | ||
57 | make <builder> | |
58 | ||
59 | Or, if you're running windows:: | |
60 | ||
61 | makedoc.cmd | |
62 | ||
63 | ||
64 | License | |
65 | ========= | |
66 | ||
67 | :: | |
68 | ||
69 | Copyright (c) 2008, Xavier Basty | |
70 | ||
71 | Licensed under the Apache License, Version 2.0 (the "License"); | |
72 | you may not use this file except in compliance with the License. | |
73 | You may obtain a copy of the License at | |
74 | ||
75 | http://www.apache.org/licenses/LICENSE-2.0 | |
76 | ||
77 | Unless required by applicable law or agreed to in writing, software | |
78 | distributed under the License is distributed on an "AS IS" BASIS, | |
79 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
80 | See the License for the specific language governing permissions and | |
81 | limitations under the License. |
0 | grapefruit (0.1~a4+dfsg-1) unstable; urgency=medium | |
1 | ||
2 | * New upstream release | |
3 | * Update debian/watch file | |
4 | * Remove patches that are no longer required | |
5 | ||
6 | -- Jonathan Carter <jcc@debian.org> Fri, 21 Jan 2022 12:19:03 +0200 | |
7 | ||
0 | 8 | grapefruit (0.1~a3+dfsg-10) unstable; urgency=medium |
1 | 9 | |
2 | 10 | [ Ondřej Nový ] |
0 | Document: grapefruit | |
1 | Title: Grapefruit | |
2 | Author: Xavier Basty | |
3 | Abstract: Python module to manipulate color information easily | |
4 | Section: Graphics | |
5 | ||
6 | Format: HTML | |
7 | Index: /usr/share/doc/python3-grapefruit/html/index.html | |
8 | Files: /usr/share/doc/python3-grapefruit/html/*.html |
0 | doc/_build/html |
0 | grapefruit_0.1~a4+dfsg-1_source.buildinfo python optional |
0 | From 2d8a62a62e9871cb4a75d8487cb7818b0efba66d Mon Sep 17 00:00:00 2001 | |
1 | From: Vincent Danjean <vdanjean@debian.org> | |
2 | Date: Wed, 27 Jun 2018 23:12:26 +0200 | |
3 | Subject: Fix syntax for python3 support (keeping python2 compatibility) | |
4 | ||
5 | --- | |
6 | grapefruit.py | 10 +++++++--- | |
7 | grapefruit_test.py | 9 ++++++--- | |
8 | 2 files changed, 13 insertions(+), 6 deletions(-) | |
9 | ||
10 | diff --git a/grapefruit.py b/grapefruit.py | |
11 | index 43d68f1..f6e5738 100755 | |
12 | --- a/grapefruit.py | |
13 | +++ b/grapefruit.py | |
14 | @@ -17,6 +17,10 @@ | |
15 | ||
16 | '''GrapeFruit - Color manipulation in Python''' | |
17 | ||
18 | +# both next line to support python3 | |
19 | +from functools import reduce | |
20 | +from past.builtins import xrange | |
21 | + | |
22 | # $Id: grapefruit.py 30 2008-06-01 20:44:26Z xbasty $ | |
23 | __author__ = 'Xavier Basty <xbasty@gmail.com>' | |
24 | __version__ = '0.1a3' | |
25 | @@ -285,7 +289,7 @@ class Color: | |
26 | ||
27 | ''' | |
28 | if not(isinstance(values, tuple)): | |
29 | - raise TypeError, 'values must be a tuple' | |
30 | + raise TypeError('values must be a tuple') | |
31 | ||
32 | if mode=='rgb': | |
33 | self.__rgb = values | |
34 | @@ -921,7 +925,7 @@ class Color: | |
35 | html = html.strip().lower() | |
36 | if html[0]=='#': | |
37 | html = html[1:] | |
38 | - elif Color.NAMED_COLOR.has_key(html): | |
39 | + elif html in Color.NAMED_COLOR: | |
40 | html = Color.NAMED_COLOR[html][1:] | |
41 | ||
42 | if len(html)==6: | |
43 | @@ -929,7 +933,7 @@ class Color: | |
44 | elif len(html)==3: | |
45 | rgb = ['%c%c' % (v,v) for v in html] | |
46 | else: | |
47 | - raise ValueError, 'input #%s is not in #RRGGBB format' % html | |
48 | + raise ValueError('input #%s is not in #RRGGBB format' % html) | |
49 | ||
50 | return tuple(((int(n, 16) / 255.0) for n in rgb)) | |
51 | ||
52 | diff --git a/grapefruit_test.py b/grapefruit_test.py | |
53 | index 5693c24..0aa0541 100755 | |
54 | --- a/grapefruit_test.py | |
55 | +++ b/grapefruit_test.py | |
56 | @@ -21,6 +21,9 @@ | |
57 | __author__ = 'xbasty@gmail.com' | |
58 | __version__ = '0.1a3' | |
59 | ||
60 | +# next line to support python3 | |
61 | +from past.builtins import xrange | |
62 | + | |
63 | import unittest | |
64 | import grapefruit | |
65 | ||
66 | @@ -32,13 +35,13 @@ class GrapeFruitTestCase(unittest.TestCase): | |
67 | ''' | |
68 | if hasattr(first,'__iter__') and hasattr(second,'__iter__'): | |
69 | if len(first) != len(second): | |
70 | - raise self.failureException, (msg or "%r != %r" % (first, second)) | |
71 | + raise self.failureException(msg or "%r != %r" % (first, second)) | |
72 | ||
73 | for f, s in zip(first, second): | |
74 | if abs(s-f) > diff: | |
75 | - raise self.failureException, (msg or "%r != %r @ %f" % (first, second, diff)) | |
76 | + raise self.failureException(msg or "%r != %r @ %f" % (first, second, diff)) | |
77 | elif abs(second-first) > diff: | |
78 | - raise self.failureException, (msg or "%r != %r @ %f" % (first, second, diff)) | |
79 | + raise self.failureException(msg or "%r != %r @ %f" % (first, second, diff)) | |
80 | assertNear = failUnlessNear | |
81 | ||
82 | class ConversionTest(GrapeFruitTestCase): |
0 | From aee26db9a7961ef6645c04d17876dd10789a5a79 Mon Sep 17 00:00:00 2001 | |
1 | From: Simon Chopin <chopin.simon@gmail.com> | |
2 | Date: Thu, 8 Oct 2015 09:14:38 -0700 | |
3 | Subject: Fix typos in comments and doc. | |
4 | ||
5 | Bug: http://code.google.com/p/grapefruit/issues/detail?id=7 | |
6 | Last-Update: 2013-04-30 | |
7 | Patch-Name: fix-typos | |
8 | --- | |
9 | doc/index.rst | 6 +++--- | |
10 | grapefruit.py | 26 +++++++++++++------------- | |
11 | grapefruit_test.py | 4 ++-- | |
12 | 3 files changed, 18 insertions(+), 18 deletions(-) | |
13 | ||
14 | diff --git a/doc/index.rst b/doc/index.rst | |
15 | index f000ef4..53a845f 100755 | |
16 | --- a/doc/index.rst | |
17 | +++ b/doc/index.rst | |
18 | @@ -18,7 +18,7 @@ The Color class | |
19 | .. class:: Color | |
20 | ||
21 | The grapefruit module contains only the :class:`Color` class, which exposes all | |
22 | -the functionnalities. It can be used to store a color value and manipulate it, | |
23 | +the functionalities. It can be used to store a color value and manipulate it, | |
24 | or convert it to another color system. | |
25 | ||
26 | If you are only interested in converting you colors from one system to another, | |
27 | @@ -42,7 +42,7 @@ of the :class:`Color` class, and all the properties are read-only. | |
28 | ||
29 | Some operations may provide results a bit outside the specified ranges, | |
30 | the results are not capped. | |
31 | - This is due to certain color systems having a widers gamut than others. | |
32 | + This is due to certain color systems having a wider gamut than others. | |
33 | ||
34 | ||
35 | Class content | |
36 | @@ -423,7 +423,7 @@ Generation methods | |
37 | The generation methods let you create a color scheme by using a color as the | |
38 | start point. | |
39 | ||
40 | -All the method, appart from Gradient and MonochromeScheme, have a 'mode' | |
41 | +All the method, apart from Gradient and MonochromeScheme, have a 'mode' | |
42 | parameter that let you choose which color wheel should be used to generate | |
43 | the scheme. | |
44 | ||
45 | diff --git a/grapefruit.py b/grapefruit.py | |
46 | index 4b88742..43d68f1 100755 | |
47 | --- a/grapefruit.py | |
48 | +++ b/grapefruit.py | |
49 | @@ -1111,7 +1111,7 @@ class Color: | |
50 | ||
51 | @staticmethod | |
52 | def NewFromRgb(r, g, b, alpha=1.0, wref=_DEFAULT_WREF): | |
53 | - '''Create a new instance based on the specifed RGB values. | |
54 | + '''Create a new instance based on the specified RGB values. | |
55 | ||
56 | Parameters: | |
57 | :r: | |
58 | @@ -1138,7 +1138,7 @@ class Color: | |
59 | ||
60 | @staticmethod | |
61 | def NewFromHsl(h, s, l, alpha=1.0, wref=_DEFAULT_WREF): | |
62 | - '''Create a new instance based on the specifed HSL values. | |
63 | + '''Create a new instance based on the specified HSL values. | |
64 | ||
65 | Parameters: | |
66 | :h: | |
67 | @@ -1165,7 +1165,7 @@ class Color: | |
68 | ||
69 | @staticmethod | |
70 | def NewFromHsv(h, s, v, alpha=1.0, wref=_DEFAULT_WREF): | |
71 | - '''Create a new instance based on the specifed HSV values. | |
72 | + '''Create a new instance based on the specified HSV values. | |
73 | ||
74 | Parameters: | |
75 | :h: | |
76 | @@ -1193,7 +1193,7 @@ class Color: | |
77 | ||
78 | @staticmethod | |
79 | def NewFromYiq(y, i, q, alpha=1.0, wref=_DEFAULT_WREF): | |
80 | - '''Create a new instance based on the specifed YIQ values. | |
81 | + '''Create a new instance based on the specified YIQ values. | |
82 | ||
83 | Parameters: | |
84 | :y: | |
85 | @@ -1220,7 +1220,7 @@ class Color: | |
86 | ||
87 | @staticmethod | |
88 | def NewFromYuv(y, u, v, alpha=1.0, wref=_DEFAULT_WREF): | |
89 | - '''Create a new instance based on the specifed YUV values. | |
90 | + '''Create a new instance based on the specified YUV values. | |
91 | ||
92 | Parameters: | |
93 | :y: | |
94 | @@ -1247,7 +1247,7 @@ class Color: | |
95 | ||
96 | @staticmethod | |
97 | def NewFromXyz(x, y, z, alpha=1.0, wref=_DEFAULT_WREF): | |
98 | - '''Create a new instance based on the specifed CIE-XYZ values. | |
99 | + '''Create a new instance based on the specified CIE-XYZ values. | |
100 | ||
101 | Parameters: | |
102 | :x: | |
103 | @@ -1274,7 +1274,7 @@ class Color: | |
104 | ||
105 | @staticmethod | |
106 | def NewFromLab(l, a, b, alpha=1.0, wref=_DEFAULT_WREF): | |
107 | - '''Create a new instance based on the specifed CIE-LAB values. | |
108 | + '''Create a new instance based on the specified CIE-LAB values. | |
109 | ||
110 | Parameters: | |
111 | :l: | |
112 | @@ -1305,7 +1305,7 @@ class Color: | |
113 | ||
114 | @staticmethod | |
115 | def NewFromCmy(c, m, y, alpha=1.0, wref=_DEFAULT_WREF): | |
116 | - '''Create a new instance based on the specifed CMY values. | |
117 | + '''Create a new instance based on the specified CMY values. | |
118 | ||
119 | Parameters: | |
120 | :c: | |
121 | @@ -1332,7 +1332,7 @@ class Color: | |
122 | ||
123 | @staticmethod | |
124 | def NewFromCmyk(c, m, y, k, alpha=1.0, wref=_DEFAULT_WREF): | |
125 | - '''Create a new instance based on the specifed CMYK values. | |
126 | + '''Create a new instance based on the specified CMYK values. | |
127 | ||
128 | Parameters: | |
129 | :c: | |
130 | @@ -1361,7 +1361,7 @@ class Color: | |
131 | ||
132 | @staticmethod | |
133 | def NewFromHtml(html, alpha=1.0, wref=_DEFAULT_WREF): | |
134 | - '''Create a new instance based on the specifed HTML color definition. | |
135 | + '''Create a new instance based on the specified HTML color definition. | |
136 | ||
137 | Parameters: | |
138 | :html: | |
139 | @@ -1392,7 +1392,7 @@ class Color: | |
140 | ||
141 | @staticmethod | |
142 | def NewFromPil(pil, alpha=1.0, wref=_DEFAULT_WREF): | |
143 | - '''Create a new instance based on the specifed PIL color. | |
144 | + '''Create a new instance based on the specified PIL color. | |
145 | ||
146 | Parameters: | |
147 | :pil: | |
148 | @@ -1834,11 +1834,11 @@ class Color: | |
149 | Color((h2, s, l), 'hsl', self.__a, self.__wref)) | |
150 | ||
151 | def TetradicScheme(self, angle=30, mode='ryb'): | |
152 | - '''Return three colors froming a tetrad with this one. | |
153 | + '''Return three colors forming a tetrad with this one. | |
154 | ||
155 | Parameters: | |
156 | :angle: | |
157 | - The angle to substract from the adjacent colors hues [-90...90]. | |
158 | + The angle to subtract from the adjacent colors hues [-90...90]. | |
159 | You can use an angle of zero to generate a square tetrad. | |
160 | :mode: | |
161 | Select which color wheel to use for the generation (ryb/rgb). | |
162 | diff --git a/grapefruit_test.py b/grapefruit_test.py | |
163 | index cf3aba0..5693c24 100755 | |
164 | --- a/grapefruit_test.py | |
165 | +++ b/grapefruit_test.py | |
166 | @@ -28,7 +28,7 @@ class GrapeFruitTestCase(unittest.TestCase): | |
167 | def failUnlessNear(self, first, second, diff=9e-5, msg=None): | |
168 | ''' | |
169 | Fail if the difference between the two objects is greater | |
170 | - than a certain amout (default 9e-5). | |
171 | + than a certain amount (default 9e-5). | |
172 | ''' | |
173 | if hasattr(first,'__iter__') and hasattr(second,'__iter__'): | |
174 | if len(first) != len(second): | |
175 | @@ -112,7 +112,7 @@ class ConversionTest(GrapeFruitTestCase): | |
176 | self.assertEqual((0.6, 0.6, 0.6), grapefruit.Color.RgbToGreyscale(1, 0.8, 0)) | |
177 | ||
178 | class NewFromTest(GrapeFruitTestCase): | |
179 | - '''Test the static color instanciation methods.''' | |
180 | + '''Test the static color instantiation methods.''' | |
181 | def testNewFromRgb(self): | |
182 | c = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0) | |
183 | self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) |
0 | fix-typos | |
1 | 0002-Fix-syntax-for-python3-support-keeping-python2-compa.patch | |
2 | 0 | remove-grapefruit-image |
0 | version=3 | |
0 | version=4 | |
1 | 1 | opts=uversionmangle=s/(a|b|rc)/~$1/,dversionmangle=s/\+dfsg//; \ |
2 | https://github.com/xav/Grapefruit/downloads \ | |
3 | /downloads/xav/Grapefruit/grapefruit-(.+?)(?:\.tar)?\.tar\.gz | |
2 | https://github.com/xav/Grapefruit/tags .*/v?(\d\S+)\.tar\.gz | |
3 | #/downloads/xav/Grapefruit/grapefruit-(.+?)(?:\.tar)?\.tar\.gz |
Binary diff not shown
10 | 10 | # All configuration values have a default value; values that are commented out |
11 | 11 | # serve to show the default value. |
12 | 12 | # |
13 | # $Id: conf.py 14 2008-05-24 09:46:30Z xbasty $ | |
13 | # $Id$ | |
14 | 14 | |
15 | 15 | import sys, os |
16 | 16 |
1 | 1 | # -*- coding: utf-8 -*-# |
2 | 2 | |
3 | 3 | # Copyright (c) 2008, Xavier Basty |
4 | # | |
4 | # | |
5 | 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
6 | 6 | # you may not use this file except in compliance with the License. |
7 | 7 | # You may obtain a copy of the License at |
8 | # | |
8 | # | |
9 | 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
10 | # | |
10 | # | |
11 | 11 | # Unless required by applicable law or agreed to in writing, software |
12 | 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
13 | 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | 16 | |
17 | 17 | '''GrapeFruit - Color manipulation in Python''' |
18 | 18 | |
19 | # $Id: grapefruit.py 30 2008-06-01 20:44:26Z xbasty $ | |
19 | from __future__ import division | |
20 | ||
21 | import sys | |
22 | ||
23 | # $Id$ | |
20 | 24 | __author__ = 'Xavier Basty <xbasty@gmail.com>' |
21 | 25 | __version__ = '0.1a3' |
22 | 26 | |
52 | 56 | |
53 | 57 | class Color: |
54 | 58 | '''Hold a color value. |
55 | ||
59 | ||
56 | 60 | Example usage: |
57 | ||
61 | ||
58 | 62 | To create an instance of the grapefruit.Color from RGB values: |
59 | ||
63 | ||
60 | 64 | >>> import grapefruit |
61 | 65 | >>> r, g, b = 1, 0.5, 0 |
62 | 66 | >>> col = grapefruit.Color.NewFromRgb(r, g, b) |
63 | ||
67 | ||
64 | 68 | To get the values of the color in another colorspace: |
65 | ||
69 | ||
66 | 70 | >>> h, s, v = col.hsv |
67 | 71 | >>> l, a, b = col.lab |
68 | ||
72 | ||
69 | 73 | To get the complementary of a color: |
70 | ||
71 | >>> compl = col.ComplementaryColor() | |
72 | >>> print compl.hsl | |
74 | ||
75 | >>> compl = col.ComplementaryColor(mode='rgb') | |
76 | >>> print(compl.hsl) | |
73 | 77 | (210.0, 1.0, 0.5) |
74 | ||
78 | ||
75 | 79 | To directly convert RGB values to their HSL equivalent: |
76 | ||
80 | ||
77 | 81 | >>> h, s, l = Color.RgbToHsl(r, g, b) |
78 | ||
82 | ||
79 | 83 | ''' |
80 | 84 | |
81 | 85 | WHITE_REFERENCE = { |
119 | 123 | 'sup_F10' : (0.99001, 1.00000, 0.83134), |
120 | 124 | 'sup_F11' : (1.03820, 1.00000, 0.65555), |
121 | 125 | 'sup_F12' : (1.11428, 1.00000, 0.40353)} |
122 | ||
126 | ||
123 | 127 | NAMED_COLOR = { |
124 | 128 | 'aliceblue': '#f0f8ff', |
125 | 129 | 'antiquewhite': '#faebd7', |
271 | 275 | |
272 | 276 | def __init__(self, values, mode='rgb', alpha=1.0, wref=_DEFAULT_WREF): |
273 | 277 | '''Instantiate a new grapefruit.Color object. |
274 | ||
278 | ||
275 | 279 | Parameters: |
276 | 280 | :values: |
277 | 281 | The values of this color, in the specified representation. |
284 | 288 | |
285 | 289 | ''' |
286 | 290 | if not(isinstance(values, tuple)): |
287 | raise TypeError, 'values must be a tuple' | |
291 | raise TypeError('values must be a tuple') | |
288 | 292 | |
289 | 293 | if mode=='rgb': |
290 | 294 | self.__rgb = values |
305 | 309 | try: |
306 | 310 | if isinstance(other, Color): |
307 | 311 | return (self.__rgb==other.__rgb) and (self.__a==other.__a) |
308 | ||
309 | 312 | if len(other) != 4: |
310 | 313 | return False |
311 | rgba = self.__rgb + (self.__a,) | |
312 | return reduce(lambda x, y: x and (y[0]==y[1]), zip(rgba, other), True) | |
314 | return list(self.__rgb + (self.__a,)) == list(other) | |
313 | 315 | except TypeError: |
314 | 316 | return False |
315 | 317 | except AttributeError: |
320 | 322 | |
321 | 323 | def __str__(self): |
322 | 324 | '''A string representation of this grapefruit.Color instance. |
323 | ||
325 | ||
324 | 326 | Returns: |
325 | 327 | The RGBA representation of this grapefruit.Color instance. |
326 | ||
328 | ||
327 | 329 | ''' |
328 | 330 | return '(%g, %g, %g, %g)' % (self.__rgb + (self.__a,)) |
329 | 331 | |
330 | def __unicode__(self): | |
331 | '''A unicode string representation of this grapefruit.Color instance. | |
332 | ||
333 | Returns: | |
334 | The RGBA representation of this grapefruit.Color instance. | |
335 | ||
336 | ''' | |
337 | return u'(%g, %g, %g, %g)' % (self.__rgb + (self.__a,)) | |
332 | if sys.version_info[0] < 3: | |
333 | def __unicode__(self): | |
334 | '''A unicode string representation of this grapefruit.Color instance. | |
335 | ||
336 | Returns: | |
337 | The RGBA representation of this grapefruit.Color instance. | |
338 | ||
339 | ''' | |
340 | return unicode('%g, %g, %g, %g)') % (self.__rgb + (self.__a,)) | |
338 | 341 | |
339 | 342 | def __iter__(self): |
340 | 343 | return iter(self.__rgb + (self.__a,)) |
342 | 345 | def __len__(self): |
343 | 346 | return 4 |
344 | 347 | |
348 | def __GetIsLegal(self): | |
349 | return all(0.0 <= v <= 1.0 for v in self) | |
350 | isLegal = property(fget=__GetIsLegal, doc='Boolean indicating whether the color is within the legal gamut.') | |
351 | ||
352 | def __GetNearestLegal(self): | |
353 | def clamp(x, lo, hi): | |
354 | if x < lo: | |
355 | return lo | |
356 | elif x > hi: | |
357 | return hi | |
358 | else: | |
359 | return x | |
360 | return Color.NewFromRgb(*[clamp(v, 0.0, 1.0) for v in self]) | |
361 | nearestLegal = property(fget=__GetNearestLegal, doc='The nearest legal color.') | |
362 | ||
345 | 363 | @staticmethod |
346 | 364 | def RgbToHsl(r, g, b): |
347 | 365 | '''Convert the color from RGB coordinates to HSL. |
348 | ||
366 | ||
349 | 367 | Parameters: |
350 | 368 | :r: |
351 | 369 | The Red component value [0...1] |
353 | 371 | The Green component value [0...1] |
354 | 372 | :b: |
355 | 373 | The Blue component value [0...1] |
356 | ||
374 | ||
357 | 375 | Returns: |
358 | 376 | The color as an (h, s, l) tuple in the range: |
359 | 377 | h[0...360], |
362 | 380 | |
363 | 381 | >>> Color.RgbToHsl(1, 0.5, 0) |
364 | 382 | (30.0, 1.0, 0.5) |
365 | ||
383 | ||
366 | 384 | ''' |
367 | 385 | minVal = min(r, g, b) # min RGB value |
368 | 386 | maxVal = max(r, g, b) # max RGB value |
384 | 402 | h = 2.0 + dr - db |
385 | 403 | else: |
386 | 404 | h = 4.0 + dg - dr |
387 | ||
405 | ||
388 | 406 | h = (h*60.0) % 360.0 |
389 | 407 | return (h, s, l) |
390 | 408 | |
399 | 417 | @staticmethod |
400 | 418 | def HslToRgb(h, s, l): |
401 | 419 | '''Convert the color from HSL coordinates to RGB. |
402 | ||
420 | ||
403 | 421 | Parameters: |
404 | 422 | :h: |
405 | 423 | The Hue component value [0...1] |
407 | 425 | The Saturation component value [0...1] |
408 | 426 | :l: |
409 | 427 | The Lightness component value [0...1] |
410 | ||
428 | ||
411 | 429 | Returns: |
412 | 430 | The color as an (r, g, b) tuple in the range: |
413 | 431 | r[0...1], |
414 | 432 | g[0...1], |
415 | 433 | b[0...1] |
416 | ||
434 | ||
417 | 435 | >>> Color.HslToRgb(30.0, 1.0, 0.5) |
418 | 436 | (1.0, 0.5, 0.0) |
419 | ||
437 | ||
420 | 438 | ''' |
421 | 439 | if s==0: return (l, l, l) # achromatic (gray) |
422 | 440 | |
436 | 454 | @staticmethod |
437 | 455 | def RgbToHsv(r, g, b): |
438 | 456 | '''Convert the color from RGB coordinates to HSV. |
439 | ||
457 | ||
440 | 458 | Parameters: |
441 | 459 | :r: |
442 | 460 | The Red component value [0...1] |
444 | 462 | The Green component value [0...1] |
445 | 463 | :b: |
446 | 464 | The Blue component value [0...1] |
447 | ||
465 | ||
448 | 466 | Returns: |
449 | 467 | The color as an (h, s, v) tuple in the range: |
450 | 468 | h[0...360], |
451 | 469 | s[0...1], |
452 | 470 | v[0...1] |
453 | ||
471 | ||
454 | 472 | >>> Color.RgbToHsv(1, 0.5, 0) |
455 | (30.0, 1, 1) | |
456 | ||
457 | ''' | |
458 | v = max(r, g, b) | |
459 | d = v - min(r, g, b) | |
473 | (30.0, 1.0, 1.0) | |
474 | ||
475 | ''' | |
476 | v = float(max(r, g, b)) | |
477 | d = v - min(r, g, b) | |
460 | 478 | if d==0: return (0.0, 0.0, v) |
461 | 479 | s = d / v |
462 | 480 | |
468 | 486 | h = 2.0 + dr - db # between cyan & yellow |
469 | 487 | else: # b==v |
470 | 488 | h = 4.0 + dg - dr # between magenta & cyan |
471 | ||
489 | ||
472 | 490 | h = (h*60.0) % 360.0 |
473 | 491 | return (h, s, v) |
474 | 492 | |
475 | 493 | @staticmethod |
476 | 494 | def HsvToRgb(h, s, v): |
477 | 495 | '''Convert the color from RGB coordinates to HSV. |
478 | ||
496 | ||
479 | 497 | Parameters: |
480 | 498 | :h: |
481 | 499 | The Hus component value [0...1] |
483 | 501 | The Saturation component value [0...1] |
484 | 502 | :v: |
485 | 503 | The Value component [0...1] |
486 | ||
504 | ||
487 | 505 | Returns: |
488 | 506 | The color as an (r, g, b) tuple in the range: |
489 | 507 | r[0...1], |
490 | 508 | g[0...1], |
491 | 509 | b[0...1] |
492 | ||
510 | ||
493 | 511 | >>> Color.HslToRgb(30.0, 1.0, 0.5) |
494 | 512 | (1.0, 0.5, 0.0) |
495 | ||
513 | ||
496 | 514 | ''' |
497 | 515 | if s==0: return (v, v, v) # achromatic (gray) |
498 | ||
516 | ||
499 | 517 | h /= 60.0 |
500 | 518 | h = h % 6.0 |
501 | 519 | |
502 | 520 | i = int(h) |
503 | 521 | f = h - i |
504 | 522 | if not(i&1): f = 1-f # if i is even |
505 | ||
523 | ||
506 | 524 | m = v * (1.0 - s) |
507 | 525 | n = v * (1.0 - (s * f)) |
508 | ||
526 | ||
509 | 527 | if i==0: return (v, n, m) |
510 | 528 | if i==1: return (n, v, m) |
511 | 529 | if i==2: return (m, v, n) |
516 | 534 | @staticmethod |
517 | 535 | def RgbToYiq(r, g, b): |
518 | 536 | '''Convert the color from RGB to YIQ. |
519 | ||
537 | ||
520 | 538 | Parameters: |
521 | 539 | :r: |
522 | 540 | The Red component value [0...1] |
524 | 542 | The Green component value [0...1] |
525 | 543 | :b: |
526 | 544 | The Blue component value [0...1] |
527 | ||
545 | ||
528 | 546 | Returns: |
529 | 547 | The color as an (y, i, q) tuple in the range: |
530 | 548 | y[0...1], |
531 | 549 | i[0...1], |
532 | 550 | q[0...1] |
533 | ||
551 | ||
534 | 552 | >>> '(%g, %g, %g)' % Color.RgbToYiq(1, 0.5, 0) |
535 | 553 | '(0.592263, 0.458874, -0.0499818)' |
536 | ||
554 | ||
537 | 555 | ''' |
538 | 556 | y = (r * 0.29895808) + (g * 0.58660979) + (b *0.11443213) |
539 | 557 | i = (r * 0.59590296) - (g * 0.27405705) - (b *0.32184591) |
543 | 561 | @staticmethod |
544 | 562 | def YiqToRgb(y, i, q): |
545 | 563 | '''Convert the color from YIQ coordinates to RGB. |
546 | ||
564 | ||
547 | 565 | Parameters: |
548 | 566 | :y: |
549 | 567 | Tte Y component value [0...1] |
551 | 569 | The I component value [0...1] |
552 | 570 | :q: |
553 | 571 | The Q component value [0...1] |
554 | ||
572 | ||
555 | 573 | Returns: |
556 | 574 | The color as an (r, g, b) tuple in the range: |
557 | 575 | r[0...1], |
558 | 576 | g[0...1], |
559 | 577 | b[0...1] |
560 | ||
578 | ||
561 | 579 | >>> '(%g, %g, %g)' % Color.YiqToRgb(0.592263, 0.458874, -0.0499818) |
562 | '(1, 0.5, 5.442e-007)' | |
563 | ||
580 | '(1, 0.5, 5.442e-07)' | |
581 | ||
564 | 582 | ''' |
565 | 583 | r = y + (i * 0.9562) + (q * 0.6210) |
566 | 584 | g = y - (i * 0.2717) - (q * 0.6485) |
570 | 588 | @staticmethod |
571 | 589 | def RgbToYuv(r, g, b): |
572 | 590 | '''Convert the color from RGB coordinates to YUV. |
573 | ||
591 | ||
574 | 592 | Parameters: |
575 | 593 | :r: |
576 | 594 | The Red component value [0...1] |
578 | 596 | The Green component value [0...1] |
579 | 597 | :b: |
580 | 598 | The Blue component value [0...1] |
581 | ||
599 | ||
582 | 600 | Returns: |
583 | 601 | The color as an (y, u, v) tuple in the range: |
584 | 602 | y[0...1], |
585 | 603 | u[-0.436...0.436], |
586 | 604 | v[-0.615...0.615] |
587 | ||
605 | ||
588 | 606 | >>> '(%g, %g, %g)' % Color.RgbToYuv(1, 0.5, 0) |
589 | 607 | '(0.5925, -0.29156, 0.357505)' |
590 | ||
608 | ||
591 | 609 | ''' |
592 | 610 | y = (r * 0.29900) + (g * 0.58700) + (b * 0.11400) |
593 | 611 | u = -(r * 0.14713) - (g * 0.28886) + (b * 0.43600) |
597 | 615 | @staticmethod |
598 | 616 | def YuvToRgb(y, u, v): |
599 | 617 | '''Convert the color from YUV coordinates to RGB. |
600 | ||
618 | ||
601 | 619 | Parameters: |
602 | 620 | :y: |
603 | 621 | The Y component value [0...1] |
605 | 623 | The U component value [-0.436...0.436] |
606 | 624 | :v: |
607 | 625 | The V component value [-0.615...0.615] |
608 | ||
626 | ||
609 | 627 | Returns: |
610 | 628 | The color as an (r, g, b) tuple in the range: |
611 | 629 | r[0...1], |
612 | 630 | g[0...1], |
613 | 631 | b[0...1] |
614 | ||
632 | ||
615 | 633 | >>> '(%g, %g, %g)' % Color.YuvToRgb(0.5925, -0.2916, 0.3575) |
616 | '(0.999989, 0.500015, -6.3276e-005)' | |
617 | ||
634 | '(0.999989, 0.500015, -6.3276e-05)' | |
635 | ||
618 | 636 | ''' |
619 | 637 | r = y + (v * 1.13983) |
620 | 638 | g = y - (u * 0.39465) - (v * 0.58060) |
624 | 642 | @staticmethod |
625 | 643 | def RgbToXyz(r, g, b): |
626 | 644 | '''Convert the color from sRGB to CIE XYZ. |
627 | ||
645 | ||
628 | 646 | The methods assumes that the RGB coordinates are given in the sRGB |
629 | 647 | colorspace (D65). |
630 | ||
648 | ||
631 | 649 | .. note:: |
632 | ||
650 | ||
633 | 651 | Compensation for the sRGB gamma correction is applied before converting. |
634 | ||
652 | ||
635 | 653 | Parameters: |
636 | 654 | :r: |
637 | 655 | The Red component value [0...1] |
639 | 657 | The Green component value [0...1] |
640 | 658 | :b: |
641 | 659 | The Blue component value [0...1] |
642 | ||
660 | ||
643 | 661 | Returns: |
644 | 662 | The color as an (x, y, z) tuple in the range: |
645 | 663 | x[0...1], |
646 | 664 | y[0...1], |
647 | 665 | z[0...1] |
648 | ||
666 | ||
649 | 667 | >>> '(%g, %g, %g)' % Color.RgbToXyz(1, 0.5, 0) |
650 | 668 | '(0.488941, 0.365682, 0.0448137)' |
651 | ||
669 | ||
652 | 670 | ''' |
653 | 671 | r, g, b = [((v <= 0.03928) and [v / 12.92] or [((v+0.055) / 1.055) **2.4])[0] for v in (r, g, b)] |
654 | ||
672 | ||
655 | 673 | x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805) |
656 | 674 | y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722) |
657 | 675 | z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505) |
662 | 680 | '''Convert the color from CIE XYZ coordinates to sRGB. |
663 | 681 | |
664 | 682 | .. note:: |
665 | ||
683 | ||
666 | 684 | Compensation for sRGB gamma correction is applied before converting. |
667 | ||
685 | ||
668 | 686 | Parameters: |
669 | 687 | :x: |
670 | 688 | The X component value [0...1] |
672 | 690 | The Y component value [0...1] |
673 | 691 | :z: |
674 | 692 | The Z component value [0...1] |
675 | ||
693 | ||
676 | 694 | Returns: |
677 | 695 | The color as an (r, g, b) tuple in the range: |
678 | 696 | r[0...1], |
679 | 697 | g[0...1], |
680 | 698 | b[0...1] |
681 | ||
699 | ||
682 | 700 | >>> '(%g, %g, %g)' % Color.XyzToRgb(0.488941, 0.365682, 0.0448137) |
683 | '(1, 0.5, 6.81883e-008)' | |
684 | ||
701 | '(1, 0.5, 6.81883e-08)' | |
702 | ||
685 | 703 | ''' |
686 | 704 | r = (x * 3.2406255) - (y * 1.5372080) - (z * 0.4986286) |
687 | 705 | g = -(x * 0.9689307) + (y * 1.8757561) + (z * 0.0415175) |
691 | 709 | @staticmethod |
692 | 710 | def XyzToLab(x, y, z, wref=_DEFAULT_WREF): |
693 | 711 | '''Convert the color from CIE XYZ to CIE L*a*b*. |
694 | ||
712 | ||
695 | 713 | Parameters: |
696 | 714 | :x: |
697 | 715 | The X component value [0...1] |
701 | 719 | The Z component value [0...1] |
702 | 720 | :wref: |
703 | 721 | The whitepoint reference, default is 2° D65. |
704 | ||
722 | ||
705 | 723 | Returns: |
706 | 724 | The color as an (L, a, b) tuple in the range: |
707 | 725 | L[0...100], |
708 | 726 | a[-1...1], |
709 | 727 | b[-1...1] |
710 | ||
728 | ||
711 | 729 | >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137) |
712 | 730 | '(66.9518, 0.43084, 0.739692)' |
713 | ||
731 | ||
714 | 732 | >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137, Color.WHITE_REFERENCE['std_D50']) |
715 | 733 | '(66.9518, 0.411663, 0.67282)' |
716 | ||
734 | ||
717 | 735 | ''' |
718 | 736 | # White point correction |
719 | 737 | x /= wref[0] |
720 | 738 | y /= wref[1] |
721 | 739 | z /= wref[2] |
722 | ||
740 | ||
723 | 741 | # Nonlinear distortion and linear transformation |
724 | 742 | x, y, z = [((v > 0.008856) and [v**_oneThird] or [(7.787 * v) + _sixteenHundredsixteenth])[0] for v in (x, y, z)] |
725 | ||
743 | ||
726 | 744 | # Vector scaling |
727 | 745 | l = (116 * y) - 16 |
728 | 746 | a = 5.0 * (x - y) |
729 | 747 | b = 2.0 * (y - z) |
730 | ||
748 | ||
731 | 749 | return (l, a, b) |
732 | 750 | |
733 | 751 | @staticmethod |
734 | 752 | def LabToXyz(l, a, b, wref=_DEFAULT_WREF): |
735 | 753 | '''Convert the color from CIE L*a*b* to CIE 1931 XYZ. |
736 | ||
754 | ||
737 | 755 | Parameters: |
738 | 756 | :l: |
739 | 757 | The L component [0...100] |
743 | 761 | The a component [-1...1] |
744 | 762 | :wref: |
745 | 763 | The whitepoint reference, default is 2° D65. |
746 | ||
764 | ||
747 | 765 | Returns: |
748 | 766 | The color as an (x, y, z) tuple in the range: |
749 | 767 | x[0...q], |
750 | 768 | y[0...1], |
751 | 769 | z[0...1] |
752 | ||
770 | ||
753 | 771 | >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.43084, 0.739692) |
754 | 772 | '(0.488941, 0.365682, 0.0448137)' |
755 | ||
773 | ||
756 | 774 | >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.411663, 0.67282, Color.WHITE_REFERENCE['std_D50']) |
757 | 775 | '(0.488941, 0.365682, 0.0448138)' |
758 | ||
776 | ||
759 | 777 | ''' |
760 | 778 | y = (l + 16) / 116 |
761 | 779 | x = (a / 5.0) + y |
765 | 783 | @staticmethod |
766 | 784 | def CmykToCmy(c, m, y, k): |
767 | 785 | '''Convert the color from CMYK coordinates to CMY. |
768 | ||
786 | ||
769 | 787 | Parameters: |
770 | 788 | :c: |
771 | 789 | The Cyan component value [0...1] |
775 | 793 | The Yellow component value [0...1] |
776 | 794 | :k: |
777 | 795 | The Black component value [0...1] |
778 | ||
796 | ||
779 | 797 | Returns: |
780 | 798 | The color as an (c, m, y) tuple in the range: |
781 | 799 | c[0...1], |
782 | 800 | m[0...1], |
783 | 801 | y[0...1] |
784 | ||
802 | ||
785 | 803 | >>> '(%g, %g, %g)' % Color.CmykToCmy(1, 0.32, 0, 0.5) |
786 | 804 | '(1, 0.66, 0.5)' |
787 | ||
805 | ||
788 | 806 | ''' |
789 | 807 | mk = 1-k |
790 | 808 | return ((c*mk + k), (m*mk + k), (y*mk + k)) |
792 | 810 | @staticmethod |
793 | 811 | def CmyToCmyk(c, m, y): |
794 | 812 | '''Convert the color from CMY coordinates to CMYK. |
795 | ||
813 | ||
796 | 814 | Parameters: |
797 | 815 | :c: |
798 | 816 | The Cyan component value [0...1] |
800 | 818 | The Magenta component value [0...1] |
801 | 819 | :y: |
802 | 820 | The Yellow component value [0...1] |
803 | ||
821 | ||
804 | 822 | Returns: |
805 | 823 | The color as an (c, m, y, k) tuple in the range: |
806 | 824 | c[0...1], |
807 | 825 | m[0...1], |
808 | 826 | y[0...1], |
809 | 827 | k[0...1] |
810 | ||
828 | ||
811 | 829 | >>> '(%g, %g, %g, %g)' % Color.CmyToCmyk(1, 0.66, 0.5) |
812 | 830 | '(1, 0.32, 0, 0.5)' |
813 | ||
831 | ||
814 | 832 | ''' |
815 | 833 | k = min(c, m, y) |
816 | 834 | if k==1.0: return (0.0, 0.0, 0.0, 1.0) |
820 | 838 | @staticmethod |
821 | 839 | def RgbToCmy(r, g, b): |
822 | 840 | '''Convert the color from RGB coordinates to CMY. |
823 | ||
841 | ||
824 | 842 | Parameters: |
825 | 843 | :r: |
826 | 844 | The Red component value [0...1] |
828 | 846 | The Green component value [0...1] |
829 | 847 | :b: |
830 | 848 | The Blue component value [0...1] |
831 | ||
849 | ||
832 | 850 | Returns: |
833 | 851 | The color as an (c, m, y) tuple in the range: |
834 | 852 | c[0...1], |
835 | 853 | m[0...1], |
836 | 854 | y[0...1] |
837 | ||
855 | ||
838 | 856 | >>> Color.RgbToCmy(1, 0.5, 0) |
839 | 857 | (0, 0.5, 1) |
840 | ||
858 | ||
841 | 859 | ''' |
842 | 860 | return (1-r, 1-g, 1-b) |
843 | 861 | |
844 | 862 | @staticmethod |
845 | 863 | def CmyToRgb(c, m, y): |
846 | 864 | '''Convert the color from CMY coordinates to RGB. |
847 | ||
865 | ||
848 | 866 | Parameters: |
849 | 867 | :c: |
850 | 868 | The Cyan component value [0...1] |
852 | 870 | The Magenta component value [0...1] |
853 | 871 | :y: |
854 | 872 | The Yellow component value [0...1] |
855 | ||
873 | ||
856 | 874 | Returns: |
857 | 875 | The color as an (r, g, b) tuple in the range: |
858 | 876 | r[0...1], |
859 | 877 | g[0...1], |
860 | 878 | b[0...1] |
861 | ||
879 | ||
862 | 880 | >>> Color.CmyToRgb(0, 0.5, 1) |
863 | 881 | (1, 0.5, 0) |
864 | ||
882 | ||
865 | 883 | ''' |
866 | 884 | return (1-c, 1-m, 1-y) |
867 | 885 | |
868 | 886 | @staticmethod |
869 | def RgbToHtml(r, g, b): | |
870 | '''Convert the color from (r, g, b) to #RRGGBB. | |
871 | ||
887 | def RgbToIntTuple(r, g, b): | |
888 | '''Convert the color from (r, g, b) to an int tuple. | |
889 | ||
872 | 890 | Parameters: |
873 | 891 | :r: |
874 | 892 | The Red component value [0...1] |
876 | 894 | The Green component value [0...1] |
877 | 895 | :b: |
878 | 896 | The Blue component value [0...1] |
879 | ||
880 | Returns: | |
881 | A CSS string representation of this color (#RRGGBB). | |
882 | ||
883 | >>> Color.RgbToHtml(1, 0.5, 0) | |
884 | '#ff8000' | |
885 | ||
886 | ''' | |
887 | return '#%02x%02x%02x' % tuple((min(round(v*255), 255) for v in (r, g, b))) | |
888 | ||
889 | @staticmethod | |
890 | def HtmlToRgb(html): | |
891 | '''Convert the HTML color to (r, g, b). | |
892 | ||
893 | Parameters: | |
894 | :html: | |
895 | the HTML definition of the color (#RRGGBB or #RGB or a color name). | |
896 | ||
897 | ||
898 | Returns: | |
899 | The color as an (r, g, b) tuple in the range: | |
900 | r[0...255], | |
901 | g[0...2551], | |
902 | b[0...2551] | |
903 | ||
904 | >>> Color.RgbToIntTuple(1, 0.5, 0) | |
905 | (255, 128, 0) | |
906 | ||
907 | ''' | |
908 | return tuple(int(round(v*255)) for v in (r, g, b)) | |
909 | ||
910 | @staticmethod | |
911 | def IntTupleToRgb(intTuple): | |
912 | '''Convert a tuple of ints to (r, g, b). | |
913 | ||
914 | Parameters: | |
915 | The color as an (r, g, b) integer tuple in the range: | |
916 | r[0...255], | |
917 | g[0...255], | |
918 | b[0...255] | |
919 | ||
897 | 920 | Returns: |
898 | 921 | The color as an (r, g, b) tuple in the range: |
899 | 922 | r[0...1], |
900 | 923 | g[0...1], |
901 | 924 | b[0...1] |
902 | ||
925 | ||
926 | >>> '(%g, %g, %g)' % Color.IntTupleToRgb((255, 128, 0)) | |
927 | '(1, 0.501961, 0)' | |
928 | ||
929 | ''' | |
930 | return tuple(v / 255 for v in intTuple) | |
931 | ||
932 | @staticmethod | |
933 | def RgbToHtml(r, g, b): | |
934 | '''Convert the color from (r, g, b) to #RRGGBB. | |
935 | ||
936 | Parameters: | |
937 | :r: | |
938 | The Red component value [0...1] | |
939 | :g: | |
940 | The Green component value [0...1] | |
941 | :b: | |
942 | The Blue component value [0...1] | |
943 | ||
944 | Returns: | |
945 | A CSS string representation of this color (#RRGGBB). | |
946 | ||
947 | >>> Color.RgbToHtml(1, 0.5, 0) | |
948 | '#ff8000' | |
949 | ||
950 | ''' | |
951 | return '#%02x%02x%02x' % tuple((min(round(v*255), 255) for v in (r, g, b))) | |
952 | ||
953 | @staticmethod | |
954 | def HtmlToRgb(html): | |
955 | '''Convert the HTML color to (r, g, b). | |
956 | ||
957 | Parameters: | |
958 | :html: | |
959 | the HTML definition of the color (#RRGGBB or #RGB or a color name). | |
960 | ||
961 | Returns: | |
962 | The color as an (r, g, b) tuple in the range: | |
963 | r[0...1], | |
964 | g[0...1], | |
965 | b[0...1] | |
966 | ||
903 | 967 | Throws: |
904 | 968 | :ValueError: |
905 | 969 | If html is neither a known color name or a hexadecimal RGB |
906 | 970 | representation. |
907 | ||
971 | ||
908 | 972 | >>> '(%g, %g, %g)' % Color.HtmlToRgb('#ff8000') |
909 | 973 | '(1, 0.501961, 0)' |
910 | 974 | >>> '(%g, %g, %g)' % Color.HtmlToRgb('ff8000') |
915 | 979 | '(1, 0.4, 0)' |
916 | 980 | >>> '(%g, %g, %g)' % Color.HtmlToRgb('lemonchiffon') |
917 | 981 | '(1, 0.980392, 0.803922)' |
918 | ||
982 | ||
919 | 983 | ''' |
920 | 984 | html = html.strip().lower() |
921 | 985 | if html[0]=='#': |
922 | 986 | html = html[1:] |
923 | elif Color.NAMED_COLOR.has_key(html): | |
987 | elif html in Color.NAMED_COLOR: | |
924 | 988 | html = Color.NAMED_COLOR[html][1:] |
925 | 989 | |
926 | 990 | if len(html)==6: |
928 | 992 | elif len(html)==3: |
929 | 993 | rgb = ['%c%c' % (v,v) for v in html] |
930 | 994 | else: |
931 | raise ValueError, 'input #%s is not in #RRGGBB format' % html | |
932 | ||
995 | raise ValueError('input #%s is not in #RRGGBB format' % html) | |
996 | ||
933 | 997 | return tuple(((int(n, 16) / 255.0) for n in rgb)) |
934 | 998 | |
935 | 999 | @staticmethod |
936 | 1000 | def RgbToPil(r, g, b): |
937 | 1001 | '''Convert the color from RGB to a PIL-compatible integer. |
938 | ||
1002 | ||
939 | 1003 | Parameters: |
940 | 1004 | :r: |
941 | 1005 | The Red component value [0...1] |
943 | 1007 | The Green component value [0...1] |
944 | 1008 | :b: |
945 | 1009 | The Blue component value [0...1] |
946 | ||
1010 | ||
947 | 1011 | Returns: |
948 | 1012 | A PIL compatible integer (0xBBGGRR). |
949 | ||
1013 | ||
950 | 1014 | >>> '0x%06x' % Color.RgbToPil(1, 0.5, 0) |
951 | 1015 | '0x0080ff' |
952 | ||
1016 | ||
953 | 1017 | ''' |
954 | 1018 | r, g, b = [min(int(round(v*255)), 255) for v in (r, g, b)] |
955 | 1019 | return (b << 16) + (g << 8) + r |
957 | 1021 | @staticmethod |
958 | 1022 | def PilToRgb(pil): |
959 | 1023 | '''Convert the color from a PIL-compatible integer to RGB. |
960 | ||
1024 | ||
961 | 1025 | Parameters: |
962 | 1026 | pil: a PIL compatible color representation (0xBBGGRR) |
963 | 1027 | Returns: |
966 | 1030 | r: [0...1] |
967 | 1031 | g: [0...1] |
968 | 1032 | b: [0...1] |
969 | ||
1033 | ||
970 | 1034 | >>> '(%g, %g, %g)' % Color.PilToRgb(0x0080ff) |
971 | 1035 | '(1, 0.501961, 0)' |
972 | ||
1036 | ||
973 | 1037 | ''' |
974 | 1038 | r = 0xff & pil |
975 | 1039 | g = 0xff & (pil >> 8) |
979 | 1043 | @staticmethod |
980 | 1044 | def _WebSafeComponent(c, alt=False): |
981 | 1045 | '''Convert a color component to its web safe equivalent. |
982 | ||
1046 | ||
983 | 1047 | Parameters: |
984 | 1048 | :c: |
985 | 1049 | The component value [0...1] |
986 | 1050 | :alt: |
987 | 1051 | If True, return the alternative value instead of the nearest one. |
988 | ||
1052 | ||
989 | 1053 | Returns: |
990 | 1054 | The web safe equivalent of the component value. |
991 | ||
1055 | ||
992 | 1056 | ''' |
993 | 1057 | # This sucks, but floating point between 0 and 1 is quite fuzzy... |
994 | 1058 | # So we just change the scale a while to make the equality tests |
995 | 1059 | # work, otherwise it gets wrong at some decimal far to the right. |
996 | 1060 | sc = c * 100.0 |
997 | ||
1061 | ||
998 | 1062 | # If the color is already safe, return it straight away |
999 | 1063 | d = sc % 20 |
1000 | 1064 | if d==0: return c |
1001 | ||
1065 | ||
1002 | 1066 | # Get the lower and upper safe values |
1003 | 1067 | l = sc - d |
1004 | 1068 | u = l + 20 |
1005 | ||
1069 | ||
1006 | 1070 | # Return the 'closest' value according to the alt flag |
1007 | 1071 | if alt: |
1008 | 1072 | if (sc-l) >= (u-sc): return l/100.0 |
1014 | 1078 | @staticmethod |
1015 | 1079 | def RgbToWebSafe(r, g, b, alt=False): |
1016 | 1080 | '''Convert the color from RGB to 'web safe' RGB |
1017 | ||
1081 | ||
1018 | 1082 | Parameters: |
1019 | 1083 | :r: |
1020 | 1084 | The Red component value [0...1] |
1025 | 1089 | :alt: |
1026 | 1090 | If True, use the alternative color instead of the nearest one. |
1027 | 1091 | Can be used for dithering. |
1028 | ||
1092 | ||
1029 | 1093 | Returns: |
1030 | 1094 | The color as an (r, g, b) tuple in the range: |
1031 | 1095 | the range: |
1032 | 1096 | r[0...1], |
1033 | 1097 | g[0...1], |
1034 | 1098 | b[0...1] |
1035 | ||
1099 | ||
1036 | 1100 | >>> '(%g, %g, %g)' % Color.RgbToWebSafe(1, 0.55, 0.0) |
1037 | 1101 | '(1, 0.6, 0)' |
1038 | ||
1102 | ||
1039 | 1103 | ''' |
1040 | 1104 | webSafeComponent = Color._WebSafeComponent |
1041 | 1105 | return tuple((webSafeComponent(v, alt) for v in (r, g, b))) |
1051 | 1115 | The Green component value [0...1] |
1052 | 1116 | :b: |
1053 | 1117 | The Blue component value [0...1] |
1054 | ||
1118 | ||
1055 | 1119 | Returns: |
1056 | 1120 | The color as an (r, g, b) tuple in the range: |
1057 | 1121 | the range: |
1058 | 1122 | r[0...1], |
1059 | 1123 | g[0...1], |
1060 | 1124 | b[0...1] |
1061 | ||
1125 | ||
1062 | 1126 | >>> '(%g, %g, %g)' % Color.RgbToGreyscale(1, 0.8, 0) |
1063 | 1127 | '(0.6, 0.6, 0.6)' |
1064 | ||
1128 | ||
1065 | 1129 | ''' |
1066 | 1130 | v = (r + g + b) / 3.0 |
1067 | 1131 | return (v, v, v) |
1069 | 1133 | @staticmethod |
1070 | 1134 | def RgbToRyb(hue): |
1071 | 1135 | '''Maps a hue on the RGB color wheel to Itten's RYB wheel. |
1072 | ||
1136 | ||
1073 | 1137 | Parameters: |
1074 | 1138 | :hue: |
1075 | 1139 | The hue on the RGB color wheel [0...360] |
1076 | ||
1140 | ||
1077 | 1141 | Returns: |
1078 | 1142 | An approximation of the corresponding hue on Itten's RYB wheel. |
1079 | ||
1143 | ||
1080 | 1144 | >>> Color.RgbToRyb(15) |
1081 | 26 | |
1082 | ||
1145 | 26.0 | |
1146 | ||
1083 | 1147 | ''' |
1084 | 1148 | d = hue % 15 |
1085 | 1149 | i = int(hue / 15) |
1086 | 1150 | x0 = _RybWheel[i] |
1087 | 1151 | x1 = _RybWheel[i+1] |
1088 | 1152 | return x0 + (x1-x0) * d / 15 |
1089 | ||
1153 | ||
1090 | 1154 | @staticmethod |
1091 | 1155 | def RybToRgb(hue): |
1092 | 1156 | '''Maps a hue on Itten's RYB color wheel to the standard RGB wheel. |
1093 | ||
1157 | ||
1094 | 1158 | Parameters: |
1095 | 1159 | :hue: |
1096 | 1160 | The hue on Itten's RYB color wheel [0...360] |
1097 | ||
1161 | ||
1098 | 1162 | Returns: |
1099 | 1163 | An approximation of the corresponding hue on the standard RGB wheel. |
1100 | ||
1164 | ||
1101 | 1165 | >>> Color.RybToRgb(15) |
1102 | 8 | |
1103 | ||
1166 | 8.0 | |
1167 | ||
1104 | 1168 | ''' |
1105 | 1169 | d = hue % 15 |
1106 | 1170 | i = int(hue / 15) |
1111 | 1175 | @staticmethod |
1112 | 1176 | def NewFromRgb(r, g, b, alpha=1.0, wref=_DEFAULT_WREF): |
1113 | 1177 | '''Create a new instance based on the specifed RGB values. |
1114 | ||
1178 | ||
1115 | 1179 | Parameters: |
1116 | 1180 | :r: |
1117 | 1181 | The Red component value [0...1] |
1123 | 1187 | The color transparency [0...1], default is opaque |
1124 | 1188 | :wref: |
1125 | 1189 | The whitepoint reference, default is 2° D65. |
1126 | ||
1190 | ||
1127 | 1191 | Returns: |
1128 | 1192 | A grapefruit.Color instance. |
1129 | ||
1193 | ||
1130 | 1194 | >>> Color.NewFromRgb(1.0, 0.5, 0.0) |
1131 | 1195 | (1.0, 0.5, 0.0, 1.0) |
1132 | 1196 | >>> Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) |
1133 | 1197 | (1.0, 0.5, 0.0, 0.5) |
1134 | ||
1198 | ||
1135 | 1199 | ''' |
1136 | 1200 | return Color((r, g, b), 'rgb', alpha, wref) |
1137 | 1201 | |
1138 | 1202 | @staticmethod |
1139 | 1203 | def NewFromHsl(h, s, l, alpha=1.0, wref=_DEFAULT_WREF): |
1140 | 1204 | '''Create a new instance based on the specifed HSL values. |
1141 | ||
1205 | ||
1142 | 1206 | Parameters: |
1143 | 1207 | :h: |
1144 | 1208 | The Hue component value [0...1] |
1150 | 1214 | The color transparency [0...1], default is opaque |
1151 | 1215 | :wref: |
1152 | 1216 | The whitepoint reference, default is 2° D65. |
1153 | ||
1217 | ||
1154 | 1218 | Returns: |
1155 | 1219 | A grapefruit.Color instance. |
1156 | ||
1220 | ||
1157 | 1221 | >>> Color.NewFromHsl(30, 1, 0.5) |
1158 | 1222 | (1.0, 0.5, 0.0, 1.0) |
1159 | 1223 | >>> Color.NewFromHsl(30, 1, 0.5, 0.5) |
1160 | 1224 | (1.0, 0.5, 0.0, 0.5) |
1161 | ||
1225 | ||
1162 | 1226 | ''' |
1163 | 1227 | return Color((h, s, l), 'hsl', alpha, wref) |
1164 | 1228 | |
1165 | 1229 | @staticmethod |
1166 | 1230 | def NewFromHsv(h, s, v, alpha=1.0, wref=_DEFAULT_WREF): |
1167 | 1231 | '''Create a new instance based on the specifed HSV values. |
1168 | ||
1232 | ||
1169 | 1233 | Parameters: |
1170 | 1234 | :h: |
1171 | 1235 | The Hus component value [0...1] |
1177 | 1241 | The color transparency [0...1], default is opaque |
1178 | 1242 | :wref: |
1179 | 1243 | The whitepoint reference, default is 2° D65. |
1180 | ||
1244 | ||
1181 | 1245 | Returns: |
1182 | 1246 | A grapefruit.Color instance. |
1183 | ||
1247 | ||
1184 | 1248 | >>> Color.NewFromHsv(30, 1, 1) |
1185 | 1249 | (1.0, 0.5, 0.0, 1.0) |
1186 | 1250 | >>> Color.NewFromHsv(30, 1, 1, 0.5) |
1187 | 1251 | (1.0, 0.5, 0.0, 0.5) |
1188 | ||
1252 | ||
1189 | 1253 | ''' |
1190 | 1254 | h2, s, l = Color.RgbToHsl(*Color.HsvToRgb(h, s, v)) |
1191 | 1255 | return Color((h, s, l), 'hsl', alpha, wref) |
1193 | 1257 | @staticmethod |
1194 | 1258 | def NewFromYiq(y, i, q, alpha=1.0, wref=_DEFAULT_WREF): |
1195 | 1259 | '''Create a new instance based on the specifed YIQ values. |
1196 | ||
1260 | ||
1197 | 1261 | Parameters: |
1198 | 1262 | :y: |
1199 | 1263 | The Y component value [0...1] |
1205 | 1269 | The color transparency [0...1], default is opaque |
1206 | 1270 | :wref: |
1207 | 1271 | The whitepoint reference, default is 2° D65. |
1208 | ||
1272 | ||
1209 | 1273 | Returns: |
1210 | 1274 | A grapefruit.Color instance. |
1211 | ||
1275 | ||
1212 | 1276 | >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05)) |
1213 | '(0.999902, 0.499955, -6.6905e-005, 1)' | |
1277 | '(0.999902, 0.499955, -6.6905e-05, 1)' | |
1214 | 1278 | >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05, 0.5)) |
1215 | '(0.999902, 0.499955, -6.6905e-005, 0.5)' | |
1279 | '(0.999902, 0.499955, -6.6905e-05, 0.5)' | |
1216 | 1280 | |
1217 | 1281 | ''' |
1218 | 1282 | return Color(Color.YiqToRgb(y, i, q), 'rgb', alpha, wref) |
1220 | 1284 | @staticmethod |
1221 | 1285 | def NewFromYuv(y, u, v, alpha=1.0, wref=_DEFAULT_WREF): |
1222 | 1286 | '''Create a new instance based on the specifed YUV values. |
1223 | ||
1287 | ||
1224 | 1288 | Parameters: |
1225 | 1289 | :y: |
1226 | 1290 | The Y component value [0...1] |
1232 | 1296 | The color transparency [0...1], default is opaque |
1233 | 1297 | :wref: |
1234 | 1298 | The whitepoint reference, default is 2° D65. |
1235 | ||
1299 | ||
1236 | 1300 | Returns: |
1237 | 1301 | A grapefruit.Color instance. |
1238 | ||
1302 | ||
1239 | 1303 | >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575)) |
1240 | '(0.999989, 0.500015, -6.3276e-005, 1)' | |
1304 | '(0.999989, 0.500015, -6.3276e-05, 1)' | |
1241 | 1305 | >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5)) |
1242 | '(0.999989, 0.500015, -6.3276e-005, 0.5)' | |
1306 | '(0.999989, 0.500015, -6.3276e-05, 0.5)' | |
1243 | 1307 | |
1244 | 1308 | ''' |
1245 | 1309 | return Color(Color.YuvToRgb(y, u, v), 'rgb', alpha, wref) |
1247 | 1311 | @staticmethod |
1248 | 1312 | def NewFromXyz(x, y, z, alpha=1.0, wref=_DEFAULT_WREF): |
1249 | 1313 | '''Create a new instance based on the specifed CIE-XYZ values. |
1250 | ||
1314 | ||
1251 | 1315 | Parameters: |
1252 | 1316 | :x: |
1253 | 1317 | The Red component value [0...1] |
1259 | 1323 | The color transparency [0...1], default is opaque |
1260 | 1324 | :wref: |
1261 | 1325 | The whitepoint reference, default is 2° D65. |
1262 | ||
1326 | ||
1263 | 1327 | Returns: |
1264 | 1328 | A grapefruit.Color instance. |
1265 | ||
1329 | ||
1266 | 1330 | >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137)) |
1267 | '(1, 0.5, 6.81883e-008, 1)' | |
1331 | '(1, 0.5, 6.81883e-08, 1)' | |
1268 | 1332 | >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5)) |
1269 | '(1, 0.5, 6.81883e-008, 0.5)' | |
1333 | '(1, 0.5, 6.81883e-08, 0.5)' | |
1270 | 1334 | |
1271 | 1335 | ''' |
1272 | 1336 | return Color(Color.XyzToRgb(x, y, z), 'rgb', alpha, wref) |
1274 | 1338 | @staticmethod |
1275 | 1339 | def NewFromLab(l, a, b, alpha=1.0, wref=_DEFAULT_WREF): |
1276 | 1340 | '''Create a new instance based on the specifed CIE-LAB values. |
1277 | ||
1341 | ||
1278 | 1342 | Parameters: |
1279 | 1343 | :l: |
1280 | 1344 | The L component [0...100] |
1286 | 1350 | The color transparency [0...1], default is opaque |
1287 | 1351 | :wref: |
1288 | 1352 | The whitepoint reference, default is 2° D65. |
1289 | ||
1353 | ||
1290 | 1354 | Returns: |
1291 | 1355 | A grapefruit.Color instance. |
1292 | ||
1356 | ||
1293 | 1357 | >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692)) |
1294 | '(1, 0.5, 1.09491e-008, 1)' | |
1358 | '(1, 0.5, 1.09491e-08, 1)' | |
1295 | 1359 | >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=Color.WHITE_REFERENCE['std_D50'])) |
1296 | 1360 | '(1.01238, 0.492011, -0.14311, 1)' |
1297 | 1361 | >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5)) |
1298 | '(1, 0.5, 1.09491e-008, 0.5)' | |
1362 | '(1, 0.5, 1.09491e-08, 0.5)' | |
1299 | 1363 | >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, Color.WHITE_REFERENCE['std_D50'])) |
1300 | 1364 | '(1.01238, 0.492011, -0.14311, 0.5)' |
1301 | ||
1365 | ||
1302 | 1366 | ''' |
1303 | 1367 | return Color(Color.XyzToRgb(*Color.LabToXyz(l, a, b, wref)), 'rgb', alpha, wref) |
1304 | 1368 | |
1305 | 1369 | @staticmethod |
1306 | 1370 | def NewFromCmy(c, m, y, alpha=1.0, wref=_DEFAULT_WREF): |
1307 | 1371 | '''Create a new instance based on the specifed CMY values. |
1308 | ||
1372 | ||
1309 | 1373 | Parameters: |
1310 | 1374 | :c: |
1311 | 1375 | The Cyan component value [0...1] |
1317 | 1381 | The color transparency [0...1], default is opaque |
1318 | 1382 | :wref: |
1319 | 1383 | The whitepoint reference, default is 2° D65. |
1320 | ||
1384 | ||
1321 | 1385 | Returns: |
1322 | 1386 | A grapefruit.Color instance. |
1323 | ||
1387 | ||
1324 | 1388 | >>> Color.NewFromCmy(0, 0.5, 1) |
1325 | 1389 | (1, 0.5, 0, 1.0) |
1326 | 1390 | >>> Color.NewFromCmy(0, 0.5, 1, 0.5) |
1327 | 1391 | (1, 0.5, 0, 0.5) |
1328 | ||
1392 | ||
1329 | 1393 | ''' |
1330 | 1394 | return Color(Color.CmyToRgb(c, m, y), 'rgb', alpha, wref) |
1331 | 1395 | |
1332 | 1396 | @staticmethod |
1333 | 1397 | def NewFromCmyk(c, m, y, k, alpha=1.0, wref=_DEFAULT_WREF): |
1334 | 1398 | '''Create a new instance based on the specifed CMYK values. |
1335 | ||
1399 | ||
1336 | 1400 | Parameters: |
1337 | 1401 | :c: |
1338 | 1402 | The Cyan component value [0...1] |
1346 | 1410 | The color transparency [0...1], default is opaque |
1347 | 1411 | :wref: |
1348 | 1412 | The whitepoint reference, default is 2° D65. |
1349 | ||
1413 | ||
1350 | 1414 | Returns: |
1351 | 1415 | A grapefruit.Color instance. |
1352 | ||
1416 | ||
1353 | 1417 | >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5)) |
1354 | 1418 | '(0, 0.34, 0.5, 1)' |
1355 | 1419 | >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5)) |
1361 | 1425 | @staticmethod |
1362 | 1426 | def NewFromHtml(html, alpha=1.0, wref=_DEFAULT_WREF): |
1363 | 1427 | '''Create a new instance based on the specifed HTML color definition. |
1364 | ||
1428 | ||
1365 | 1429 | Parameters: |
1366 | 1430 | :html: |
1367 | 1431 | The HTML definition of the color (#RRGGBB or #RGB or a color name). |
1369 | 1433 | The color transparency [0...1], default is opaque. |
1370 | 1434 | :wref: |
1371 | 1435 | The whitepoint reference, default is 2° D65. |
1372 | ||
1436 | ||
1373 | 1437 | Returns: |
1374 | 1438 | A grapefruit.Color instance. |
1375 | ||
1439 | ||
1376 | 1440 | >>> str(Color.NewFromHtml('#ff8000')) |
1377 | 1441 | '(1, 0.501961, 0, 1)' |
1378 | 1442 | >>> str(Color.NewFromHtml('ff8000')) |
1385 | 1449 | '(1, 0.980392, 0.803922, 1)' |
1386 | 1450 | >>> str(Color.NewFromHtml('#ff8000', 0.5)) |
1387 | 1451 | '(1, 0.501961, 0, 0.5)' |
1388 | ||
1452 | ||
1389 | 1453 | ''' |
1390 | 1454 | return Color(Color.HtmlToRgb(html), 'rgb', alpha, wref) |
1391 | 1455 | |
1392 | 1456 | @staticmethod |
1393 | 1457 | def NewFromPil(pil, alpha=1.0, wref=_DEFAULT_WREF): |
1394 | 1458 | '''Create a new instance based on the specifed PIL color. |
1395 | ||
1459 | ||
1396 | 1460 | Parameters: |
1397 | 1461 | :pil: |
1398 | 1462 | A PIL compatible color representation (0xBBGGRR) |
1400 | 1464 | The color transparency [0...1], default is opaque |
1401 | 1465 | :wref: |
1402 | 1466 | The whitepoint reference, default is 2° D65. |
1403 | ||
1467 | ||
1404 | 1468 | Returns: |
1405 | 1469 | A grapefruit.Color instance. |
1406 | ||
1470 | ||
1407 | 1471 | >>> str(Color.NewFromPil(0x0080ff)) |
1408 | 1472 | '(1, 0.501961, 0, 1)' |
1409 | 1473 | >>> str(Color.NewFromPil(0x0080ff, 0.5)) |
1410 | 1474 | '(1, 0.501961, 0, 0.5)' |
1411 | ||
1475 | ||
1412 | 1476 | ''' |
1413 | 1477 | return Color(Color.PilToRgb(pil), 'rgb', alpha, wref) |
1414 | 1478 | |
1415 | 1479 | def __GetAlpha(self): |
1416 | 1480 | return self.__a |
1417 | 1481 | alpha = property(fget=__GetAlpha, doc='The transparency of this color. 0.0 is transparent and 1.0 is fully opaque.') |
1418 | ||
1482 | ||
1419 | 1483 | def __GetWRef(self): |
1420 | 1484 | return self.__wref |
1421 | 1485 | whiteRef = property(fget=__GetWRef, doc='the white reference point of this color.') |
1423 | 1487 | def __GetRGB(self): |
1424 | 1488 | return self.__rgb |
1425 | 1489 | rgb = property(fget=__GetRGB, doc='The RGB values of this Color.') |
1426 | ||
1490 | ||
1427 | 1491 | def __GetHue(self): |
1428 | 1492 | return self.__hsl[0] |
1429 | 1493 | hue = property(fget=__GetHue, doc='The hue of this color.') |
1461 | 1525 | return Color.CmyToCmyk(*Color.RgbToCmy(*self.__rgb)) |
1462 | 1526 | cmyk = property(fget=__GetCMYK, doc='The CMYK values of this Color.') |
1463 | 1527 | |
1528 | def __GetIntTuple(self): | |
1529 | return Color.RgbToIntTuple(*self.__rgb) | |
1530 | intTuple = property(fget=__GetIntTuple, doc='This Color as a tuple of integers in the range [0...255]') | |
1531 | ||
1464 | 1532 | def __GetHTML(self): |
1465 | 1533 | return Color.RgbToHtml(*self.__rgb) |
1466 | 1534 | html = property(fget=__GetHTML, doc='This Color as an HTML color definition.') |
1479 | 1547 | |
1480 | 1548 | def ColorWithAlpha(self, alpha): |
1481 | 1549 | '''Create a new instance based on this one with a new alpha value. |
1482 | ||
1550 | ||
1483 | 1551 | Parameters: |
1484 | 1552 | :alpha: |
1485 | 1553 | The transparency of the new color [0...1]. |
1486 | ||
1554 | ||
1487 | 1555 | Returns: |
1488 | 1556 | A grapefruit.Color instance. |
1489 | 1557 | |
1490 | 1558 | >>> Color.NewFromRgb(1.0, 0.5, 0.0, 1.0).ColorWithAlpha(0.5) |
1491 | 1559 | (1.0, 0.5, 0.0, 0.5) |
1492 | ||
1560 | ||
1493 | 1561 | ''' |
1494 | 1562 | return Color(self.__rgb, 'rgb', alpha, self.__wref) |
1495 | ||
1563 | ||
1496 | 1564 | def ColorWithWhiteRef(self, wref, labAsRef=False): |
1497 | 1565 | '''Create a new instance based on this one with a new white reference. |
1498 | ||
1566 | ||
1499 | 1567 | Parameters: |
1500 | 1568 | :wref: |
1501 | 1569 | The whitepoint reference. |
1502 | 1570 | :labAsRef: |
1503 | 1571 | If True, the L*a*b* values of the current instance are used as reference |
1504 | 1572 | for the new color; otherwise, the RGB values are used as reference. |
1505 | ||
1573 | ||
1506 | 1574 | Returns: |
1507 | 1575 | A grapefruit.Color instance. |
1508 | 1576 | |
1509 | ||
1577 | ||
1510 | 1578 | >>> c = Color.NewFromRgb(1.0, 0.5, 0.0, 1.0, Color.WHITE_REFERENCE['std_D65']) |
1511 | ||
1579 | ||
1512 | 1580 | >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50']) |
1513 | 1581 | >>> c2.rgb |
1514 | 1582 | (1.0, 0.5, 0.0) |
1515 | 1583 | >>> '(%g, %g, %g)' % c2.whiteRef |
1516 | 1584 | '(0.96721, 1, 0.81428)' |
1517 | ||
1585 | ||
1518 | 1586 | >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50'], labAsRef=True) |
1519 | 1587 | >>> '(%g, %g, %g)' % c2.rgb |
1520 | 1588 | '(1.01463, 0.490339, -0.148131)' |
1534 | 1602 | |
1535 | 1603 | def ColorWithHue(self, hue): |
1536 | 1604 | '''Create a new instance based on this one with a new hue. |
1537 | ||
1605 | ||
1538 | 1606 | Parameters: |
1539 | 1607 | :hue: |
1540 | 1608 | The hue of the new color [0...360]. |
1541 | ||
1609 | ||
1542 | 1610 | Returns: |
1543 | 1611 | A grapefruit.Color instance. |
1544 | 1612 | |
1546 | 1614 | (1.0, 1.0, 0.0, 1.0) |
1547 | 1615 | >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60).hsl |
1548 | 1616 | (60, 1, 0.5) |
1549 | ||
1617 | ||
1550 | 1618 | ''' |
1551 | 1619 | h, s, l = self.__hsl |
1552 | 1620 | return Color((hue, s, l), 'hsl', self.__a, self.__wref) |
1553 | 1621 | |
1554 | 1622 | def ColorWithSaturation(self, saturation): |
1555 | 1623 | '''Create a new instance based on this one with a new saturation value. |
1556 | ||
1624 | ||
1557 | 1625 | .. note:: |
1558 | ||
1626 | ||
1559 | 1627 | The saturation is defined for the HSL mode. |
1560 | ||
1628 | ||
1561 | 1629 | Parameters: |
1562 | 1630 | :saturation: |
1563 | 1631 | The saturation of the new color [0...1]. |
1564 | ||
1632 | ||
1565 | 1633 | Returns: |
1566 | 1634 | A grapefruit.Color instance. |
1567 | 1635 | |
1569 | 1637 | (0.75, 0.5, 0.25, 1.0) |
1570 | 1638 | >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5).hsl |
1571 | 1639 | (30, 0.5, 0.5) |
1572 | ||
1640 | ||
1573 | 1641 | ''' |
1574 | 1642 | h, s, l = self.__hsl |
1575 | 1643 | return Color((h, saturation, l), 'hsl', self.__a, self.__wref) |
1576 | 1644 | |
1577 | 1645 | def ColorWithLightness(self, lightness): |
1578 | 1646 | '''Create a new instance based on this one with a new lightness value. |
1579 | ||
1647 | ||
1580 | 1648 | Parameters: |
1581 | 1649 | :lightness: |
1582 | 1650 | The lightness of the new color [0...1]. |
1583 | ||
1651 | ||
1584 | 1652 | Returns: |
1585 | 1653 | A grapefruit.Color instance. |
1586 | 1654 | |
1588 | 1656 | (0.5, 0.25, 0.0, 1.0) |
1589 | 1657 | >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25).hsl |
1590 | 1658 | (30, 1, 0.25) |
1591 | ||
1659 | ||
1592 | 1660 | ''' |
1593 | 1661 | h, s, l = self.__hsl |
1594 | 1662 | return Color((h, s, lightness), 'hsl', self.__a, self.__wref) |
1595 | 1663 | |
1596 | 1664 | def DarkerColor(self, level): |
1597 | 1665 | '''Create a new instance based on this one but darker. |
1598 | ||
1666 | ||
1599 | 1667 | Parameters: |
1600 | 1668 | :level: |
1601 | 1669 | The amount by which the color should be darkened to produce |
1602 | 1670 | the new one [0...1]. |
1603 | ||
1671 | ||
1604 | 1672 | Returns: |
1605 | 1673 | A grapefruit.Color instance. |
1606 | 1674 | |
1608 | 1676 | (0.5, 0.25, 0.0, 1.0) |
1609 | 1677 | >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25).hsl |
1610 | 1678 | (30, 1, 0.25) |
1611 | ||
1679 | ||
1612 | 1680 | ''' |
1613 | 1681 | h, s, l = self.__hsl |
1614 | 1682 | return Color((h, s, max(l - level, 0)), 'hsl', self.__a, self.__wref) |
1615 | 1683 | |
1616 | 1684 | def LighterColor(self, level): |
1617 | 1685 | '''Create a new instance based on this one but lighter. |
1618 | ||
1686 | ||
1619 | 1687 | Parameters: |
1620 | 1688 | :level: |
1621 | 1689 | The amount by which the color should be lightened to produce |
1622 | 1690 | the new one [0...1]. |
1623 | ||
1691 | ||
1624 | 1692 | Returns: |
1625 | 1693 | A grapefruit.Color instance. |
1626 | 1694 | |
1628 | 1696 | (1.0, 0.75, 0.5, 1.0) |
1629 | 1697 | >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25).hsl |
1630 | 1698 | (30, 1, 0.75) |
1631 | ||
1699 | ||
1632 | 1700 | ''' |
1633 | 1701 | h, s, l = self.__hsl |
1634 | 1702 | return Color((h, s, min(l + level, 1)), 'hsl', self.__a, self.__wref) |
1635 | ||
1703 | ||
1636 | 1704 | def Saturate(self, level): |
1637 | 1705 | '''Create a new instance based on this one but more saturated. |
1638 | ||
1706 | ||
1639 | 1707 | Parameters: |
1640 | 1708 | :level: |
1641 | 1709 | The amount by which the color should be saturated to produce |
1642 | 1710 | the new one [0...1]. |
1643 | ||
1711 | ||
1644 | 1712 | Returns: |
1645 | 1713 | A grapefruit.Color instance. |
1646 | 1714 | |
1648 | 1716 | (0.875, 0.5, 0.125, 1.0) |
1649 | 1717 | >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25).hsl |
1650 | 1718 | (30, 0.75, 0.5) |
1651 | ||
1719 | ||
1652 | 1720 | ''' |
1653 | 1721 | h, s, l = self.__hsl |
1654 | 1722 | return Color((h, min(s + level, 1), l), 'hsl', self.__a, self.__wref) |
1655 | 1723 | |
1656 | 1724 | def Desaturate(self, level): |
1657 | 1725 | '''Create a new instance based on this one but less saturated. |
1658 | ||
1726 | ||
1659 | 1727 | Parameters: |
1660 | 1728 | :level: |
1661 | 1729 | The amount by which the color should be desaturated to produce |
1662 | 1730 | the new one [0...1]. |
1663 | ||
1731 | ||
1664 | 1732 | Returns: |
1665 | 1733 | A grapefruit.Color instance. |
1666 | 1734 | |
1668 | 1736 | (0.625, 0.5, 0.375, 1.0) |
1669 | 1737 | >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25).hsl |
1670 | 1738 | (30, 0.25, 0.5) |
1671 | ||
1739 | ||
1672 | 1740 | ''' |
1673 | 1741 | h, s, l = self.__hsl |
1674 | 1742 | return Color((h, max(s - level, 0), l), 'hsl', self.__a, self.__wref) |
1675 | ||
1743 | ||
1676 | 1744 | def WebSafeDither(self): |
1677 | 1745 | '''Return the two websafe colors nearest to this one. |
1678 | ||
1746 | ||
1679 | 1747 | Returns: |
1680 | 1748 | A tuple of two grapefruit.Color instances which are the two |
1681 | 1749 | web safe colors closest this one. |
1686 | 1754 | '(1, 0.4, 0, 1)' |
1687 | 1755 | >>> str(c2) |
1688 | 1756 | '(1, 0.6, 0, 1)' |
1689 | ||
1757 | ||
1690 | 1758 | ''' |
1691 | 1759 | return ( |
1692 | 1760 | Color(Color.RgbToWebSafe(*self.__rgb), 'rgb', self.__a, self.__wref), |
1694 | 1762 | |
1695 | 1763 | def Gradient(self, target, steps=100): |
1696 | 1764 | '''Create a list with the gradient colors between this and the other color. |
1697 | ||
1765 | ||
1698 | 1766 | Parameters: |
1699 | 1767 | :target: |
1700 | 1768 | The grapefruit.Color at the other end of the gradient. |
1701 | 1769 | :steps: |
1702 | 1770 | The number of gradients steps to create. |
1703 | ||
1704 | ||
1771 | ||
1772 | ||
1705 | 1773 | Returns: |
1706 | 1774 | A list of grapefruit.Color instances. |
1707 | 1775 | |
1709 | 1777 | >>> c2 = Color.NewFromRgb(0.0, 1.0, 0.0, alpha=0) |
1710 | 1778 | >>> c1.Gradient(c2, 3) |
1711 | 1779 | [(0.75, 0.25, 0.0, 0.75), (0.5, 0.5, 0.0, 0.5), (0.25, 0.75, 0.0, 0.25)] |
1712 | ||
1780 | ||
1713 | 1781 | ''' |
1714 | 1782 | gradient = [] |
1715 | 1783 | rgba1 = self.__rgb + (self.__a,) |
1716 | 1784 | rgba2 = target.__rgb + (target.__a,) |
1717 | ||
1785 | ||
1718 | 1786 | steps += 1 |
1719 | for n in xrange(1, steps): | |
1787 | for n in range(1, steps): | |
1720 | 1788 | d = 1.0*n/steps |
1721 | 1789 | r = (rgba1[0]*(1-d)) + (rgba2[0]*d) |
1722 | 1790 | g = (rgba1[1]*(1-d)) + (rgba2[1]*d) |
1724 | 1792 | a = (rgba1[3]*(1-d)) + (rgba2[3]*d) |
1725 | 1793 | |
1726 | 1794 | gradient.append(Color((r, g, b), 'rgb', a, self.__wref)) |
1727 | ||
1795 | ||
1728 | 1796 | return gradient |
1729 | 1797 | |
1730 | 1798 | def ComplementaryColor(self, mode='ryb'): |
1731 | 1799 | '''Create a new instance which is the complementary color of this one. |
1732 | ||
1800 | ||
1733 | 1801 | Parameters: |
1734 | 1802 | :mode: |
1735 | 1803 | Select which color wheel to use for the generation (ryb/rgb). |
1736 | ||
1737 | ||
1804 | ||
1805 | ||
1738 | 1806 | Returns: |
1739 | 1807 | A grapefruit.Color instance. |
1740 | 1808 | |
1741 | >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor() | |
1809 | >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor(mode='rgb') | |
1742 | 1810 | (0.0, 0.5, 1.0, 1.0) |
1743 | >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor().hsl | |
1811 | >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor(mode='rgb').hsl | |
1744 | 1812 | (210, 1, 0.5) |
1745 | ||
1813 | ||
1746 | 1814 | ''' |
1747 | 1815 | h, s, l = self.__hsl |
1748 | 1816 | |
1749 | 1817 | if mode == 'ryb': h = Color.RgbToRyb(h) |
1750 | 1818 | h = (h+180)%360 |
1751 | 1819 | if mode == 'ryb': h = Color.RybToRgb(h) |
1752 | ||
1820 | ||
1753 | 1821 | return Color((h, s, l), 'hsl', self.__a, self.__wref) |
1754 | ||
1822 | ||
1755 | 1823 | def MonochromeScheme(self): |
1756 | 1824 | '''Return 4 colors in the same hue with varying saturation/lightness. |
1757 | ||
1825 | ||
1758 | 1826 | Returns: |
1759 | 1827 | A tuple of 4 grapefruit.Color in the same hue as this one, |
1760 | 1828 | with varying saturation/lightness. |
1769 | 1837 | else: return x-min |
1770 | 1838 | |
1771 | 1839 | h, s, l = self.__hsl |
1772 | ||
1840 | ||
1773 | 1841 | s1 = _wrap(s, 0.3, 0.1, 0.3) |
1774 | 1842 | l1 = _wrap(l, 0.5, 0.2, 0.3) |
1775 | ||
1843 | ||
1776 | 1844 | s2 = s |
1777 | 1845 | l2 = _wrap(l, 0.2, 0.2, 0.6) |
1778 | ||
1846 | ||
1779 | 1847 | s3 = s1 |
1780 | 1848 | l3 = max(0.2, l + (1-l)*0.2) |
1781 | ||
1849 | ||
1782 | 1850 | s4 = s |
1783 | 1851 | l4 = _wrap(l, 0.5, 0.2, 0.3) |
1784 | ||
1852 | ||
1785 | 1853 | return ( |
1786 | 1854 | Color((h, s1, l1), 'hsl', self.__a, self.__wref), |
1787 | 1855 | Color((h, s2, l2), 'hsl', self.__a, self.__wref), |
1788 | 1856 | Color((h, s3, l3), 'hsl', self.__a, self.__wref), |
1789 | 1857 | Color((h, s4, l4), 'hsl', self.__a, self.__wref)) |
1790 | ||
1858 | ||
1791 | 1859 | def TriadicScheme(self, angle=120, mode='ryb'): |
1792 | 1860 | '''Return two colors forming a triad or a split complementary with this one. |
1793 | ||
1861 | ||
1794 | 1862 | Parameters: |
1795 | 1863 | :angle: |
1796 | 1864 | The angle between the hues of the created colors. |
1797 | 1865 | The default value makes a triad. |
1798 | 1866 | :mode: |
1799 | 1867 | Select which color wheel to use for the generation (ryb/rgb). |
1800 | ||
1868 | ||
1801 | 1869 | Returns: |
1802 | 1870 | A tuple of two grapefruit.Color forming a color triad with |
1803 | 1871 | this one or a split complementary. |
1804 | 1872 | |
1805 | 1873 | >>> c1 = Color.NewFromHsl(30, 1, 0.5) |
1806 | ||
1807 | >>> c2, c3 = c1.TriadicScheme() | |
1874 | ||
1875 | >>> c2, c3 = c1.TriadicScheme(mode='rgb') | |
1808 | 1876 | >>> c2.hsl |
1809 | 1877 | (150.0, 1, 0.5) |
1810 | 1878 | >>> c3.hsl |
1811 | 1879 | (270.0, 1, 0.5) |
1812 | ||
1813 | >>> c2, c3 = c1.TriadicScheme(40) | |
1880 | ||
1881 | >>> c2, c3 = c1.TriadicScheme(angle=40, mode='rgb') | |
1814 | 1882 | >>> c2.hsl |
1815 | 1883 | (190.0, 1, 0.5) |
1816 | 1884 | >>> c3.hsl |
1817 | 1885 | (230.0, 1, 0.5) |
1818 | ||
1886 | ||
1819 | 1887 | ''' |
1820 | 1888 | h, s, l = self.__hsl |
1821 | 1889 | angle = min(angle, 120) / 2.0 |
1822 | ||
1890 | ||
1823 | 1891 | if mode == 'ryb': h = Color.RgbToRyb(h) |
1824 | 1892 | h += 180 |
1825 | 1893 | h1 = (h - angle) % 360 |
1827 | 1895 | if mode == 'ryb': |
1828 | 1896 | h1 = Color.RybToRgb(h1) |
1829 | 1897 | h2 = Color.RybToRgb(h2) |
1830 | ||
1898 | ||
1831 | 1899 | return ( |
1832 | 1900 | Color((h1, s, l), 'hsl', self.__a, self.__wref), |
1833 | 1901 | Color((h2, s, l), 'hsl', self.__a, self.__wref)) |
1834 | 1902 | |
1835 | 1903 | def TetradicScheme(self, angle=30, mode='ryb'): |
1836 | 1904 | '''Return three colors froming a tetrad with this one. |
1837 | ||
1905 | ||
1838 | 1906 | Parameters: |
1839 | 1907 | :angle: |
1840 | 1908 | The angle to substract from the adjacent colors hues [-90...90]. |
1841 | 1909 | You can use an angle of zero to generate a square tetrad. |
1842 | 1910 | :mode: |
1843 | 1911 | Select which color wheel to use for the generation (ryb/rgb). |
1844 | ||
1912 | ||
1845 | 1913 | Returns: |
1846 | 1914 | A tuple of three grapefruit.Color forming a color tetrad with |
1847 | 1915 | this one. |
1849 | 1917 | >>> col = Color.NewFromHsl(30, 1, 0.5) |
1850 | 1918 | >>> [c.hsl for c in col.TetradicScheme(mode='rgb', angle=30)] |
1851 | 1919 | [(90, 1, 0.5), (210, 1, 0.5), (270, 1, 0.5)] |
1852 | ||
1920 | ||
1853 | 1921 | ''' |
1854 | 1922 | h, s, l = self.__hsl |
1855 | 1923 | |
1869 | 1937 | |
1870 | 1938 | def AnalogousScheme(self, angle=30, mode='ryb'): |
1871 | 1939 | '''Return two colors analogous to this one. |
1872 | ||
1940 | ||
1873 | 1941 | Args: |
1874 | 1942 | :angle: |
1875 | 1943 | The angle between the hues of the created colors and this one. |
1880 | 1948 | A tuple of grapefruit.Colors analogous to this one. |
1881 | 1949 | |
1882 | 1950 | >>> c1 = Color.NewFromHsl(30, 1, 0.5) |
1883 | ||
1884 | >>> c2, c3 = c1.AnalogousScheme() | |
1951 | ||
1952 | >>> c2, c3 = c1.AnalogousScheme(angle=60, mode='rgb') | |
1885 | 1953 | >>> c2.hsl |
1886 | 1954 | (330, 1, 0.5) |
1887 | 1955 | >>> c3.hsl |
1888 | 1956 | (90, 1, 0.5) |
1889 | ||
1890 | >>> c2, c3 = c1.AnalogousScheme(10) | |
1957 | ||
1958 | >>> c2, c3 = c1.AnalogousScheme(angle=10, mode='rgb') | |
1891 | 1959 | >>> c2.hsl |
1892 | 1960 | (20, 1, 0.5) |
1893 | 1961 | >>> c3.hsl |
1894 | 1962 | (40, 1, 0.5) |
1895 | ||
1963 | ||
1896 | 1964 | ''' |
1897 | 1965 | h, s, l = self.__hsl |
1898 | 1966 | |
1903 | 1971 | if mode == 'ryb': |
1904 | 1972 | h1 = Color.RybToRgb(h1) |
1905 | 1973 | h2 = Color.RybToRgb(h2) |
1906 | ||
1974 | ||
1907 | 1975 | return (Color((h1, s, l), 'hsl', self.__a, self.__wref), |
1908 | 1976 | Color((h2, s, l), 'hsl', self.__a, self.__wref)) |
1909 | 1977 | |
1910 | 1978 | def AlphaBlend(self, other): |
1911 | 1979 | '''Alpha-blend this color on the other one. |
1912 | ||
1980 | ||
1913 | 1981 | Args: |
1914 | 1982 | :other: |
1915 | 1983 | The grapefruit.Color to alpha-blend with this one. |
1916 | ||
1984 | ||
1917 | 1985 | Returns: |
1918 | 1986 | A grapefruit.Color instance which is the result of alpha-blending |
1919 | 1987 | this color on the other one. |
1923 | 1991 | >>> c3 = c1.AlphaBlend(c2) |
1924 | 1992 | >>> str(c3) |
1925 | 1993 | '(1, 0.875, 0.75, 0.84)' |
1926 | ||
1994 | ||
1927 | 1995 | ''' |
1928 | 1996 | # get final alpha channel |
1929 | 1997 | fa = self.__a + other.__a - (self.__a * other.__a) |
1937 | 2005 | |
1938 | 2006 | sr, sg, sb = [v * sa for v in self.__rgb] |
1939 | 2007 | dr, dg, db = [v * da for v in other.__rgb] |
1940 | ||
2008 | ||
1941 | 2009 | return Color((sr+dr, sg+dg, sb+db), 'rgb', fa, self.__wref) |
1942 | 2010 | |
1943 | 2011 | def Blend(self, other, percent=0.5): |
1946 | 2014 | Args: |
1947 | 2015 | :other: |
1948 | 2016 | the grapefruit.Color to blend with this one. |
1949 | ||
2017 | ||
1950 | 2018 | Returns: |
1951 | 2019 | A grapefruit.Color instance which is the result of blending |
1952 | 2020 | this color on the other one. |
1956 | 2024 | >>> c3 = c1.Blend(c2) |
1957 | 2025 | >>> str(c3) |
1958 | 2026 | '(1, 0.75, 0.5, 0.4)' |
1959 | ||
2027 | ||
1960 | 2028 | ''' |
1961 | 2029 | dest = 1.0 - percent |
1962 | 2030 | rgb = tuple(((u * percent) + (v * dest) for u, v in zip(self.__rgb, other.__rgb))) |
1970 | 2038 | |
1971 | 2039 | if __name__=='__main__': |
1972 | 2040 | _test() |
2041 | ||
2042 | # vim: ts=2 sts=2 sw=2 et |
0 | #!/usr/bin/python | |
1 | # -*- coding: utf-8 -*-# | |
2 | ||
3 | # Copyright (c) 2008, Xavier Basty | |
4 | # | |
5 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | # you may not use this file except in compliance with the License. | |
7 | # You may obtain a copy of the License at | |
8 | # | |
9 | # http://www.apache.org/licenses/LICENSE-2.0 | |
10 | # | |
11 | # Unless required by applicable law or agreed to in writing, software | |
12 | # distributed under the License is distributed on an "AS IS" BASIS, | |
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | # See the License for the specific language governing permissions and | |
15 | # limitations under the License. | |
16 | ||
17 | '''Unit tests for the grapefruit module.''' | |
18 | ||
19 | # $Id: grapefruit_test.py 24 2008-05-25 16:23:22Z xbasty $ | |
20 | __author__ = 'xbasty@gmail.com' | |
21 | __version__ = '0.1a3' | |
22 | ||
23 | import unittest | |
24 | import grapefruit | |
25 | ||
26 | class GrapeFruitTestCase(unittest.TestCase): | |
27 | def failUnlessNear(self, first, second, diff=9e-5, msg=None): | |
28 | ''' | |
29 | Fail if the difference between the two objects is greater | |
30 | than a certain amout (default 9e-5). | |
31 | ''' | |
32 | if hasattr(first,'__iter__') and hasattr(second,'__iter__'): | |
33 | if len(first) != len(second): | |
34 | raise self.failureException, (msg or "%r != %r" % (first, second)) | |
35 | ||
36 | for f, s in zip(first, second): | |
37 | if abs(s-f) > diff: | |
38 | raise self.failureException, (msg or "%r != %r @ %f" % (first, second, diff)) | |
39 | elif abs(second-first) > diff: | |
40 | raise self.failureException, (msg or "%r != %r @ %f" % (first, second, diff)) | |
41 | assertNear = failUnlessNear | |
42 | ||
43 | class ConversionTest(GrapeFruitTestCase): | |
44 | '''Test the static color conversion methods.''' | |
45 | ||
46 | def testRgbHsl(self): | |
47 | self.assertNear((30.0, 1.0, 0.5), grapefruit.Color.RgbToHsl(1, 0.5, 0)) | |
48 | self.assertNear((20.0, 1.0, 0.625), grapefruit.Color.RgbToHsl(1, 0.5, 0.25)) #ff8040 | |
49 | self.assertNear((40.0, 1.0, 0.375), grapefruit.Color.RgbToHsl(0.75, 0.5, 0)) #bf8000 | |
50 | ||
51 | self.assertNear((1, 0.5, 0), grapefruit.Color.HslToRgb(30.0, 1.0, 0.5)) | |
52 | self.assertNear((1, 0.5, 0.25), grapefruit.Color.HslToRgb(20.0, 1.0, 0.625)) | |
53 | self.assertNear((0.75, 0.5, 0), grapefruit.Color.HslToRgb(40.0, 1.0, 0.375)) | |
54 | ||
55 | def testRgbHsv(self): | |
56 | self.assertEqual((30.0, 1.0, 1.0), grapefruit.Color.RgbToHsv(1, 0.5, 0)) | |
57 | self.assertEqual((1, 0.5, 0), grapefruit.Color.HsvToRgb(30.0, 1.0, 1.0)) | |
58 | ||
59 | def testRgbYiq(self): | |
60 | self.assertNear((0.5923, 0.4589, -0.05), grapefruit.Color.RgbToYiq(1, 0.5, 0)) | |
61 | self.assertNear((1, 0.5, 0), grapefruit.Color.YiqToRgb(0.5923, 0.4589, -0.05)) | |
62 | ||
63 | def testRgbYuv(self): | |
64 | self.assertNear((0.5925, -0.2916, 0.3575), grapefruit.Color.RgbToYuv(1, 0.5, 0)) | |
65 | self.assertNear((1, 0.5, 0), grapefruit.Color.YuvToRgb(0.5925, -0.2916, 0.3575)) | |
66 | ||
67 | def testRgbXyz(self): | |
68 | self.assertNear((0.4890, 0.3657, 0.04485), grapefruit.Color.RgbToXyz(1, 0.5, 0)) | |
69 | self.assertNear((1, 0.5, 0), grapefruit.Color.XyzToRgb(0.488941, 0.365682, 0.0448137)) | |
70 | ||
71 | def testXyzLab(self): | |
72 | self.assertNear((66.9518, 0.4308, 0.7397), grapefruit.Color.XyzToLab(0.488941, 0.365682, 0.0448137)) | |
73 | self.assertNear((0.4890, 0.3657, 0.0449), grapefruit.Color.LabToXyz(66.9518, 0.4308, 0.7397)) | |
74 | self.assertNear((66.9518, 0.4117, 0.6728), grapefruit.Color.XyzToLab(0.488941, 0.365682, 0.0448137, grapefruit.Color.WHITE_REFERENCE["std_D50"])) | |
75 | self.assertNear((0.4890, 0.3657, 0.0449), grapefruit.Color.LabToXyz(66.9518, 0.4117, 0.6728, grapefruit.Color.WHITE_REFERENCE["std_D50"])) | |
76 | ||
77 | def testCmykCmy(self): | |
78 | self.assertNear((1, 0.32, 0, 0.5), grapefruit.Color.CmyToCmyk(1.0, 0.66, 0.5)) | |
79 | self.assertNear((1.0, 0.66, 0.5), grapefruit.Color.CmykToCmy(1, 0.32, 0, 0.5)) | |
80 | ||
81 | def testRgbCmy(self): | |
82 | self.assertEqual((0, 0.5, 1), grapefruit.Color.RgbToCmy(1, 0.5, 0)) | |
83 | self.assertEqual((1, 0.5, 0), grapefruit.Color.CmyToRgb(0, 0.5, 1)) | |
84 | ||
85 | def testRgbHtml(self): | |
86 | self.assertEqual("#ff8000", grapefruit.Color.RgbToHtml(1, 0.5, 0)) | |
87 | self.assertNear((1.0, 0.5020, 0.0), grapefruit.Color.HtmlToRgb("#ff8000")) | |
88 | self.assertNear((1.0, 0.5020, 0.0), grapefruit.Color.HtmlToRgb("ff8000")) | |
89 | self.assertNear((1.0, 0.4, 0.0), grapefruit.Color.HtmlToRgb("#f60")) | |
90 | self.assertNear((1.0, 0.4, 0.0), grapefruit.Color.HtmlToRgb("f60")) | |
91 | self.assertNear((1.000000, 0.980392, 0.803922), grapefruit.Color.HtmlToRgb("lemonchiffon")) | |
92 | ||
93 | def testRgbPil(self): | |
94 | self.assertNear(0x0080ff, grapefruit.Color.RgbToPil(1, 0.5, 0)) | |
95 | self.assertNear((1.0, 0.5020, 0), grapefruit.Color.PilToRgb(0x0080ff)) | |
96 | ||
97 | def testWebSafeComponent(self): | |
98 | self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.2)) | |
99 | self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.25)) | |
100 | self.assertEqual(0.4, grapefruit.Color._WebSafeComponent(0.3)) | |
101 | self.assertEqual(0.4, grapefruit.Color._WebSafeComponent(0.25, True)) | |
102 | self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.2, True)) | |
103 | self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.3, True)) | |
104 | ||
105 | def testRgbToWebSafe(self): | |
106 | self.assertEqual((1.0, 0.6, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.55, 0.0)) | |
107 | self.assertEqual((1.0, 0.4, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.55, 0.0, True)) | |
108 | self.assertEqual((1.0, 0.4, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.5, 0.0, True)) | |
109 | ||
110 | def testRgbToGreyscale(self): | |
111 | self.assertEqual((0.6, 0.6, 0.6), grapefruit.Color.RgbToGreyscale(1, 0.8, 0)) | |
112 | ||
113 | class NewFromTest(GrapeFruitTestCase): | |
114 | '''Test the static color instanciation methods.''' | |
115 | def testNewFromRgb(self): | |
116 | c = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0) | |
117 | self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) | |
118 | c = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) | |
119 | self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) | |
120 | ||
121 | def testNewFromHsl(self): | |
122 | c = grapefruit.Color.NewFromHsl(30, 1, 0.5) | |
123 | self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) | |
124 | c = grapefruit.Color.NewFromHsl(30, 1, 0.5, 0.5) | |
125 | self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) | |
126 | ||
127 | def testNewFromHsv(self): | |
128 | c = grapefruit.Color.NewFromHsv(30, 1, 1) | |
129 | self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) | |
130 | c = grapefruit.Color.NewFromHsv(30, 1, 1, 0.5) | |
131 | self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) | |
132 | ||
133 | def testNewFromYiq(self): | |
134 | c = grapefruit.Color.NewFromYiq(0.5923, 0.4589, -0.0499818) | |
135 | self.assertNear(c, (1, 0.5, 0, 1)) | |
136 | c = grapefruit.Color.NewFromYiq(0.5923, 0.4589,-0.05, 0.5) | |
137 | self.assertNear(c, (1, 0.5, 0, 0.5)) | |
138 | ||
139 | def testNewFromYuv(self): | |
140 | c = grapefruit.Color.NewFromYuv(0.5925, -0.2916, 0.3575) | |
141 | self.assertNear(c, (1, 0.5, 0, 1)) | |
142 | c = grapefruit.Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5) | |
143 | self.assertNear(c, (1, 0.5, 0, 0.5)) | |
144 | ||
145 | def testNewFromXyz(self): | |
146 | c = grapefruit.Color.NewFromXyz(0.488941, 0.365682, 0.0448137) | |
147 | self.assertNear(c, (1, 0.5, 0, 1)) | |
148 | c = grapefruit.Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5) | |
149 | self.assertNear(c, (1, 0.5, 0, 0.5)) | |
150 | ||
151 | def testNewFromLab(self): | |
152 | c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692) | |
153 | self.assertNear(c, (1, 0.5, 0, 1)) | |
154 | c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=grapefruit.Color.WHITE_REFERENCE["std_D50"]) | |
155 | self.assertNear(c, (1.0123754, 0.492012, -0.143110, 1)) | |
156 | c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5) | |
157 | self.assertNear(c, (1, 0.5, 0, 0.5)) | |
158 | c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, grapefruit.Color.WHITE_REFERENCE["std_D50"]) | |
159 | self.assertNear(c, (1.0123754, 0.492012, -0.143110, 0.5)) | |
160 | ||
161 | def testNewFromCmy(self): | |
162 | c = grapefruit.Color.NewFromCmy(0, 0.5, 1) | |
163 | self.assertEqual(c, (1, 0.5, 0, 1.0)) | |
164 | c = grapefruit.Color.NewFromCmy(0, 0.5, 1, 0.5) | |
165 | self.assertEqual(c, (1, 0.5, 0, 0.5)) | |
166 | ||
167 | def testNewFromCmyk(self): | |
168 | c = grapefruit.Color.NewFromCmyk(1, 0.32, 0, 0.5) | |
169 | self.assertNear(c, (0, 0.34, 0.5, 1)) | |
170 | c = grapefruit.Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5) | |
171 | self.assertNear(c, (0, 0.34, 0.5, 0.5)) | |
172 | ||
173 | def testNewFromHtml(self): | |
174 | c = grapefruit.Color.NewFromHtml("#ff8000") | |
175 | self.assertNear(c, (1, 0.5020, 0, 1)) | |
176 | c = grapefruit.Color.NewFromHtml("ff8000") | |
177 | self.assertNear(c, (1, 0.5020, 0, 1)) | |
178 | c = grapefruit.Color.NewFromHtml("#f60") | |
179 | self.assertNear(c, (1, 0.4, 0, 1)) | |
180 | c = grapefruit.Color.NewFromHtml("f60") | |
181 | self.assertNear(c, (1, 0.4, 0, 1)) | |
182 | c = grapefruit.Color.NewFromHtml("lemonchiffon") | |
183 | self.assertNear(c, (1, 0.9804, 0.8039, 1)) | |
184 | c = grapefruit.Color.NewFromHtml("#ff8000", 0.5) | |
185 | self.assertNear(c, (1, 0.5020, 0, 0.5)) | |
186 | ||
187 | def testNewFromPil(self): | |
188 | c = grapefruit.Color.NewFromPil(0x0080ff) | |
189 | self.assertNear(c, (1, 0.5020, 0, 1)) | |
190 | c = grapefruit.Color.NewFromPil(0x0080ff, 0.5) | |
191 | self.assertNear(c, (1, 0.5020, 0, 0.5)) | |
192 | ||
193 | ||
194 | class ColorTest(GrapeFruitTestCase): | |
195 | def setUp(self): | |
196 | self.rgbCol = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0) | |
197 | self.hslCol = grapefruit.Color.NewFromHsl(30, 1, 0.5) | |
198 | self.hslCol2 = grapefruit.Color.NewFromHsl(30, 0.5, 0.5) | |
199 | ||
200 | def testInit(self): | |
201 | self.assertEqual(grapefruit.Color((1.0, 0.5, 0.0)), (1.0, 0.5, 0.0, 1.0)) | |
202 | self.assertEqual(grapefruit.Color((1.0, 0.5, 0.0), mode='rgb'), (1.0, 0.5, 0.0, 1.0)) | |
203 | self.assertEqual(grapefruit.Color((30, 1, 0.5), mode='hsl'), (1.0, 0.5, 0.0, 1.0)) | |
204 | ||
205 | self.assertRaises(ValueError, grapefruit.Color, (30, 1, 0.5), 'hsv') | |
206 | ||
207 | def testEq(self): | |
208 | self.assertEqual(self.rgbCol, self.hslCol) | |
209 | self.assertEqual(self.rgbCol, (1.0, 0.5, 0.0, 1.0)) | |
210 | self.assertEqual(self.rgbCol, [1.0, 0.5, 0.0, 1.0]) | |
211 | self.assertEqual([1.0, 0.5, 0.0, 1.0], self.rgbCol) | |
212 | self.assertNotEqual(self.rgbCol, '(1.0, 0.5, 0.0, 1.0)') | |
213 | ||
214 | def testRepr(self): | |
215 | self.assertEqual(repr(self.rgbCol), '(1.0, 0.5, 0.0, 1.0)') | |
216 | self.assertEqual(repr(self.hslCol), '(1.0, 0.5, 0.0, 1.0)') | |
217 | ||
218 | def testStr(self): | |
219 | self.assertEqual(str(self.rgbCol), '(1, 0.5, 0, 1)') | |
220 | self.assertEqual(str(self.hslCol), '(1, 0.5, 0, 1)') | |
221 | ||
222 | def testIter(self): | |
223 | self.assertEqual([1, 0.5, 0, 1], list(iter(self.rgbCol))) | |
224 | ||
225 | def testProperties(self): | |
226 | self.assertEqual(self.rgbCol.alpha, 1.0) | |
227 | self.assertEqual(self.rgbCol.whiteRef, grapefruit.Color.WHITE_REFERENCE['std_D65']) | |
228 | self.assertEqual(self.rgbCol.rgb, (1, 0.5, 0)) | |
229 | self.assertEqual(self.hslCol.hue, 30) | |
230 | self.assertEqual(self.rgbCol.hsl, (30, 1, 0.5)) | |
231 | self.assertEqual(self.rgbCol.hsv, (30, 1, 1)) | |
232 | self.assertNear(self.rgbCol.yiq, (0.5923, 0.4589, -0.05)) | |
233 | self.assertNear(self.rgbCol.yuv, (0.5925, -0.2916, 0.3575)) | |
234 | self.assertNear(self.rgbCol.xyz, (0.4890, 0.3657, 0.04485)) | |
235 | self.assertNear(self.rgbCol.lab, (66.9518, 0.4308, 0.7397)) | |
236 | self.assertEqual(self.rgbCol.cmy, (0, 0.5, 1)) | |
237 | self.assertEqual(self.rgbCol.cmyk, (0, 0.5, 1, 0)) | |
238 | self.assertEqual(self.rgbCol.html, '#ff8000') | |
239 | self.assertEqual(self.rgbCol.pil, 0x0080ff) | |
240 | self.assertEqual(self.rgbCol.webSafe, (1, 0.6, 0)) | |
241 | self.assertEqual(self.rgbCol.greyscale, (0.5, 0.5, 0.5)) | |
242 | ||
243 | c = grapefruit.Color.NewFromRgb(1, 0.5, 0, wref=grapefruit.Color.WHITE_REFERENCE['std_D50']) | |
244 | self.assertNear(c.lab, (66.9518, 0.4117, 0.6728)) | |
245 | ||
246 | def testColorWitgAlpha(self): | |
247 | self.assertEqual(self.rgbCol.ColorWithAlpha(0.5), (1, 0.5, 0, 0.5)) | |
248 | ||
249 | def testColorWithWhiteRef(self): | |
250 | self.assertEqual(self.hslCol.ColorWithWhiteRef((0.1, 0.2, 0.3)).whiteRef, (0.1, 0.2, 0.3)) | |
251 | ||
252 | def testColorWithHue(self): | |
253 | self.assertEqual(self.hslCol.ColorWithHue(60), (1.0, 1.0, 0.0, 1.0)) | |
254 | self.assertEqual(self.hslCol.ColorWithHue(60).hsl, (60, 1, 0.5)) | |
255 | ||
256 | def testColorWithSaturation(self): | |
257 | self.assertEqual(self.hslCol.ColorWithSaturation(0.5), (0.75, 0.5, 0.25, 1.0)) | |
258 | self.assertEqual(self.hslCol.ColorWithSaturation(0.5).hsl, (30, 0.5, 0.5)) | |
259 | ||
260 | def testColorWithLightness(self): | |
261 | self.assertEqual(self.hslCol.ColorWithLightness(1), (1.0, 1.0, 1.0, 1.0)) | |
262 | self.assertEqual(self.hslCol.ColorWithLightness(1).hsl, (30, 1.0, 1.0)) | |
263 | ||
264 | def testDarkerColor(self): | |
265 | self.assertNear(self.hslCol.DarkerColor(0.2), (0.6, 0.3, 0.0, 1.0)) | |
266 | self.assertNear(self.hslCol.DarkerColor(0.2).hsl, (30, 1, 0.3)) | |
267 | ||
268 | def testLighterColor(self): | |
269 | self.assertNear(self.hslCol.LighterColor(0.2), (1.0, 0.7, 0.4, 1.0)) | |
270 | self.assertNear(self.hslCol.LighterColor(0.2).hsl, (30, 1, 0.7)) | |
271 | ||
272 | def testSaturate(self): | |
273 | self.assertNear(self.hslCol2.Saturate(0.25), (0.875, 0.5, 0.125, 1.0)) | |
274 | self.assertNear(self.hslCol2.Saturate(0.25).hsl, (30, 0.75, 0.5)) | |
275 | ||
276 | def testDesaturate(self): | |
277 | self.assertNear(self.hslCol2.Desaturate(0.25), (0.625, 0.5, 0.375, 1.0)) | |
278 | self.assertNear(self.hslCol2.Desaturate(0.25).hsl, (30, 0.25, 0.5)) | |
279 | ||
280 | def testWebSafeDither(self): | |
281 | dithered = ( | |
282 | (1.0, 0.6, 0.0, 1.0), | |
283 | (1.0, 0.4, 0.0, 1.0)) | |
284 | self.assertEqual(self.rgbCol.WebSafeDither(), dithered) | |
285 | ||
286 | def testGradient(self): | |
287 | gradient = [ | |
288 | (0.75, 0.25, 0.0, 1.0), | |
289 | (0.5, 0.5, 0.0, 1.0), | |
290 | (0.25, 0.75, 0.0, 1.0)] | |
291 | c1 = grapefruit.Color.NewFromRgb(1.0, 0.0, 0.0) | |
292 | c2 = grapefruit.Color.NewFromRgb(0.0, 1.0, 0.0) | |
293 | self.assertEqual(gradient, c1.Gradient(c2, 3)) | |
294 | ||
295 | def testComplementaryColor(self): | |
296 | self.assertEqual(self.hslCol.ComplementaryColor(mode='rgb').hsl, (210, 1, 0.5)) | |
297 | ||
298 | def testMonochromeScheme(self): | |
299 | monochrome = ( | |
300 | (0.94, 0.8, 0.66, 1.0), # hsl(30, 0.7, 0.8) | |
301 | (0.6, 0.3, 0.0, 1.0), # hsl(30, 1, 0.3) | |
302 | (0.88, 0.6, 0.32, 1.0), # hsl(30, 0.7, 0.6) | |
303 | (1.0, 0.8, 0.6, 1.0)) # hsl(30, 1, 0.8) | |
304 | scheme = self.rgbCol.MonochromeScheme() | |
305 | for i in xrange(len(monochrome)): | |
306 | self.assertNear(scheme[i], monochrome[i]) | |
307 | ||
308 | def testTriadicScheme(self): | |
309 | triad = ( | |
310 | (0.0, 1.0, 0.5, 1.0), | |
311 | (0.5, 0.0, 1.0, 1.0)) | |
312 | self.assertEqual(self.rgbCol.TriadicScheme(mode='rgb'), triad) | |
313 | ||
314 | def testTetradicScheme(self): | |
315 | tetrad = ( | |
316 | (0.5, 1.0, 0.0, 1.0), | |
317 | (0.0, 0.5, 1.0, 1.0), | |
318 | (0.5, 0.0, 1.0, 1.0)) | |
319 | self.assertEqual(self.rgbCol.TetradicScheme(mode='rgb'), tetrad) | |
320 | ||
321 | def testAnalogousScheme(self): | |
322 | scheme = ( | |
323 | (1.0, 0.0, 0.0, 1.0), | |
324 | (1.0, 1.0, 0.0, 1.0)) | |
325 | self.assertEqual(self.rgbCol.AnalogousScheme(mode='rgb'), scheme) | |
326 | ||
327 | def testAlphaBlend(self): | |
328 | c1 = grapefruit.Color.NewFromRgb(1, 0.5, 0, alpha = 0.2) | |
329 | c2 = grapefruit.Color.NewFromRgb(1, 1, 1, alpha = 0.8) | |
330 | self.assertNear(c1.AlphaBlend(c2), (1, 0.875, 0.75, 0.84)) | |
331 | ||
332 | def testBlend(self): | |
333 | c1 = grapefruit.Color.NewFromRgb(1, 0.5, 0, alpha = 0.2) | |
334 | c2 = grapefruit.Color.NewFromRgb(1, 1, 1, alpha = 0.6) | |
335 | self.assertEqual(c1.Blend(c2), (1, 0.75, 0.5, 0.4)) | |
336 | ||
337 | ||
338 | if __name__ == '__main__': | |
339 | unittest.main() | |
340 | pass | |
0 | #!/usr/bin/python | |
1 | # -*- coding: utf-8 -*-# | |
2 | ||
3 | # Copyright (c) 2008, Xavier Basty | |
4 | # | |
5 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | # you may not use this file except in compliance with the License. | |
7 | # You may obtain a copy of the License at | |
8 | # | |
9 | # http://www.apache.org/licenses/LICENSE-2.0 | |
10 | # | |
11 | # Unless required by applicable law or agreed to in writing, software | |
12 | # distributed under the License is distributed on an "AS IS" BASIS, | |
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | # See the License for the specific language governing permissions and | |
15 | # limitations under the License. | |
16 | ||
17 | '''Unit tests for the grapefruit module.''' | |
18 | ||
19 | # $Id$ | |
20 | __author__ = 'xbasty@gmail.com' | |
21 | __version__ = '0.1a3' | |
22 | ||
23 | import unittest | |
24 | import doctest | |
25 | import grapefruit | |
26 | ||
27 | # Also run doctests. | |
28 | def load_tests(loader, tests, ignore): | |
29 | tests.addTests(doctest.DocTestSuite(grapefruit)) | |
30 | return tests | |
31 | ||
32 | ||
33 | class GrapeFruitTestCase(unittest.TestCase): | |
34 | def failUnlessNear(self, first, second, diff=9e-5, msg=None): | |
35 | ''' | |
36 | Fail if the difference between the two objects is greater | |
37 | than a certain amout (default 9e-5). | |
38 | ''' | |
39 | if hasattr(first,'__iter__') and hasattr(second,'__iter__'): | |
40 | if len(first) != len(second): | |
41 | raise self.failureException(msg or "%r != %r" % (first, second)) | |
42 | ||
43 | for f, s in zip(first, second): | |
44 | if abs(s-f) > diff: | |
45 | raise self.failureException(msg or "%r != %r @ %f" % (first, second, diff)) | |
46 | elif abs(second-first) > diff: | |
47 | raise self.failureException(msg or "%r != %r @ %f" % (first, second, diff)) | |
48 | assertNear = failUnlessNear | |
49 | ||
50 | class ConversionTest(GrapeFruitTestCase): | |
51 | '''Test the static color conversion methods.''' | |
52 | ||
53 | def testRgbHsl(self): | |
54 | self.assertNear((30.0, 1.0, 0.5), grapefruit.Color.RgbToHsl(1, 0.5, 0)) | |
55 | self.assertNear((20.0, 1.0, 0.625), grapefruit.Color.RgbToHsl(1, 0.5, 0.25)) #ff8040 | |
56 | self.assertNear((40.0, 1.0, 0.375), grapefruit.Color.RgbToHsl(0.75, 0.5, 0)) #bf8000 | |
57 | ||
58 | self.assertNear((1, 0.5, 0), grapefruit.Color.HslToRgb(30.0, 1.0, 0.5)) | |
59 | self.assertNear((1, 0.5, 0.25), grapefruit.Color.HslToRgb(20.0, 1.0, 0.625)) | |
60 | self.assertNear((0.75, 0.5, 0), grapefruit.Color.HslToRgb(40.0, 1.0, 0.375)) | |
61 | ||
62 | def testRgbHsv(self): | |
63 | self.assertEqual((30.0, 1.0, 1.0), grapefruit.Color.RgbToHsv(1, 0.5, 0)) | |
64 | self.assertEqual((1, 0.5, 0), grapefruit.Color.HsvToRgb(30.0, 1.0, 1.0)) | |
65 | ||
66 | def testRgbYiq(self): | |
67 | self.assertNear((0.5923, 0.4589, -0.05), grapefruit.Color.RgbToYiq(1, 0.5, 0)) | |
68 | self.assertNear((1, 0.5, 0), grapefruit.Color.YiqToRgb(0.5923, 0.4589, -0.05)) | |
69 | ||
70 | def testRgbYuv(self): | |
71 | self.assertNear((0.5925, -0.2916, 0.3575), grapefruit.Color.RgbToYuv(1, 0.5, 0)) | |
72 | self.assertNear((1, 0.5, 0), grapefruit.Color.YuvToRgb(0.5925, -0.2916, 0.3575)) | |
73 | ||
74 | def testRgbXyz(self): | |
75 | self.assertNear((0.4890, 0.3657, 0.04485), grapefruit.Color.RgbToXyz(1, 0.5, 0)) | |
76 | self.assertNear((1, 0.5, 0), grapefruit.Color.XyzToRgb(0.488941, 0.365682, 0.0448137)) | |
77 | ||
78 | def testXyzLab(self): | |
79 | self.assertNear((66.9518, 0.4308, 0.7397), grapefruit.Color.XyzToLab(0.488941, 0.365682, 0.0448137)) | |
80 | self.assertNear((0.4890, 0.3657, 0.0449), grapefruit.Color.LabToXyz(66.9518, 0.4308, 0.7397)) | |
81 | self.assertNear((66.9518, 0.4117, 0.6728), grapefruit.Color.XyzToLab(0.488941, 0.365682, 0.0448137, grapefruit.Color.WHITE_REFERENCE["std_D50"])) | |
82 | self.assertNear((0.4890, 0.3657, 0.0449), grapefruit.Color.LabToXyz(66.9518, 0.4117, 0.6728, grapefruit.Color.WHITE_REFERENCE["std_D50"])) | |
83 | ||
84 | def testCmykCmy(self): | |
85 | self.assertNear((1, 0.32, 0, 0.5), grapefruit.Color.CmyToCmyk(1.0, 0.66, 0.5)) | |
86 | self.assertNear((1.0, 0.66, 0.5), grapefruit.Color.CmykToCmy(1, 0.32, 0, 0.5)) | |
87 | ||
88 | def testRgbCmy(self): | |
89 | self.assertEqual((0, 0.5, 1), grapefruit.Color.RgbToCmy(1, 0.5, 0)) | |
90 | self.assertEqual((1, 0.5, 0), grapefruit.Color.CmyToRgb(0, 0.5, 1)) | |
91 | ||
92 | def testRgbHtml(self): | |
93 | self.assertEqual("#ff8000", grapefruit.Color.RgbToHtml(1, 0.5, 0)) | |
94 | self.assertNear((1.0, 0.5020, 0.0), grapefruit.Color.HtmlToRgb("#ff8000")) | |
95 | self.assertNear((1.0, 0.5020, 0.0), grapefruit.Color.HtmlToRgb("ff8000")) | |
96 | self.assertNear((1.0, 0.4, 0.0), grapefruit.Color.HtmlToRgb("#f60")) | |
97 | self.assertNear((1.0, 0.4, 0.0), grapefruit.Color.HtmlToRgb("f60")) | |
98 | self.assertNear((1.000000, 0.980392, 0.803922), grapefruit.Color.HtmlToRgb("lemonchiffon")) | |
99 | ||
100 | def testRgbPil(self): | |
101 | self.assertNear(0x0080ff, grapefruit.Color.RgbToPil(1, 0.5, 0)) | |
102 | self.assertNear((1.0, 0.5020, 0), grapefruit.Color.PilToRgb(0x0080ff)) | |
103 | ||
104 | def testWebSafeComponent(self): | |
105 | self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.2)) | |
106 | self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.25)) | |
107 | self.assertEqual(0.4, grapefruit.Color._WebSafeComponent(0.3)) | |
108 | self.assertEqual(0.4, grapefruit.Color._WebSafeComponent(0.25, True)) | |
109 | self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.2, True)) | |
110 | self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.3, True)) | |
111 | ||
112 | def testRgbToWebSafe(self): | |
113 | self.assertEqual((1.0, 0.6, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.55, 0.0)) | |
114 | self.assertEqual((1.0, 0.4, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.55, 0.0, True)) | |
115 | self.assertEqual((1.0, 0.4, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.5, 0.0, True)) | |
116 | ||
117 | def testRgbToGreyscale(self): | |
118 | self.assertEqual((0.6, 0.6, 0.6), grapefruit.Color.RgbToGreyscale(1, 0.8, 0)) | |
119 | ||
120 | class NewFromTest(GrapeFruitTestCase): | |
121 | '''Test the static color instanciation methods.''' | |
122 | def testNewFromRgb(self): | |
123 | c = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0) | |
124 | self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) | |
125 | c = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) | |
126 | self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) | |
127 | ||
128 | def testNewFromHsl(self): | |
129 | c = grapefruit.Color.NewFromHsl(30, 1, 0.5) | |
130 | self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) | |
131 | c = grapefruit.Color.NewFromHsl(30, 1, 0.5, 0.5) | |
132 | self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) | |
133 | ||
134 | def testNewFromHsv(self): | |
135 | c = grapefruit.Color.NewFromHsv(30, 1, 1) | |
136 | self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) | |
137 | c = grapefruit.Color.NewFromHsv(30, 1, 1, 0.5) | |
138 | self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) | |
139 | ||
140 | def testNewFromYiq(self): | |
141 | c = grapefruit.Color.NewFromYiq(0.5923, 0.4589, -0.0499818) | |
142 | self.assertNear(c, (1, 0.5, 0, 1)) | |
143 | c = grapefruit.Color.NewFromYiq(0.5923, 0.4589,-0.05, 0.5) | |
144 | self.assertNear(c, (1, 0.5, 0, 0.5)) | |
145 | ||
146 | def testNewFromYuv(self): | |
147 | c = grapefruit.Color.NewFromYuv(0.5925, -0.2916, 0.3575) | |
148 | self.assertNear(c, (1, 0.5, 0, 1)) | |
149 | c = grapefruit.Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5) | |
150 | self.assertNear(c, (1, 0.5, 0, 0.5)) | |
151 | ||
152 | def testNewFromXyz(self): | |
153 | c = grapefruit.Color.NewFromXyz(0.488941, 0.365682, 0.0448137) | |
154 | self.assertNear(c, (1, 0.5, 0, 1)) | |
155 | c = grapefruit.Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5) | |
156 | self.assertNear(c, (1, 0.5, 0, 0.5)) | |
157 | ||
158 | def testNewFromLab(self): | |
159 | c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692) | |
160 | self.assertNear(c, (1, 0.5, 0, 1)) | |
161 | c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=grapefruit.Color.WHITE_REFERENCE["std_D50"]) | |
162 | self.assertNear(c, (1.0123754, 0.492012, -0.143110, 1)) | |
163 | c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5) | |
164 | self.assertNear(c, (1, 0.5, 0, 0.5)) | |
165 | c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, grapefruit.Color.WHITE_REFERENCE["std_D50"]) | |
166 | self.assertNear(c, (1.0123754, 0.492012, -0.143110, 0.5)) | |
167 | ||
168 | def testNewFromLabInteger(self): | |
169 | # Allow specifying lightness as an integer. | |
170 | lab = (60, 0.3, 0.3) | |
171 | c = grapefruit.Color.NewFromLab(*lab) | |
172 | self.assertNear(c.lab, lab) | |
173 | self.assertTrue(c.isLegal) | |
174 | ||
175 | def testNewFromCmy(self): | |
176 | c = grapefruit.Color.NewFromCmy(0, 0.5, 1) | |
177 | self.assertEqual(c, (1, 0.5, 0, 1.0)) | |
178 | c = grapefruit.Color.NewFromCmy(0, 0.5, 1, 0.5) | |
179 | self.assertEqual(c, (1, 0.5, 0, 0.5)) | |
180 | ||
181 | def testNewFromCmyk(self): | |
182 | c = grapefruit.Color.NewFromCmyk(1, 0.32, 0, 0.5) | |
183 | self.assertNear(c, (0, 0.34, 0.5, 1)) | |
184 | c = grapefruit.Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5) | |
185 | self.assertNear(c, (0, 0.34, 0.5, 0.5)) | |
186 | ||
187 | def testNewFromHtml(self): | |
188 | c = grapefruit.Color.NewFromHtml("#ff8000") | |
189 | self.assertNear(c, (1, 0.5020, 0, 1)) | |
190 | c = grapefruit.Color.NewFromHtml("ff8000") | |
191 | self.assertNear(c, (1, 0.5020, 0, 1)) | |
192 | c = grapefruit.Color.NewFromHtml("#f60") | |
193 | self.assertNear(c, (1, 0.4, 0, 1)) | |
194 | c = grapefruit.Color.NewFromHtml("f60") | |
195 | self.assertNear(c, (1, 0.4, 0, 1)) | |
196 | c = grapefruit.Color.NewFromHtml("lemonchiffon") | |
197 | self.assertNear(c, (1, 0.9804, 0.8039, 1)) | |
198 | c = grapefruit.Color.NewFromHtml("#ff8000", 0.5) | |
199 | self.assertNear(c, (1, 0.5020, 0, 0.5)) | |
200 | ||
201 | def testNewFromPil(self): | |
202 | c = grapefruit.Color.NewFromPil(0x0080ff) | |
203 | self.assertNear(c, (1, 0.5020, 0, 1)) | |
204 | c = grapefruit.Color.NewFromPil(0x0080ff, 0.5) | |
205 | self.assertNear(c, (1, 0.5020, 0, 0.5)) | |
206 | ||
207 | ||
208 | class ColorTest(GrapeFruitTestCase): | |
209 | def setUp(self): | |
210 | self.rgbCol = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0) | |
211 | self.hslCol = grapefruit.Color.NewFromHsl(30, 1, 0.5) | |
212 | self.hslCol2 = grapefruit.Color.NewFromHsl(30, 0.5, 0.5) | |
213 | ||
214 | def testInit(self): | |
215 | self.assertEqual(grapefruit.Color((1.0, 0.5, 0.0)), (1.0, 0.5, 0.0, 1.0)) | |
216 | self.assertEqual(grapefruit.Color((1.0, 0.5, 0.0), mode='rgb'), (1.0, 0.5, 0.0, 1.0)) | |
217 | self.assertEqual(grapefruit.Color((30, 1, 0.5), mode='hsl'), (1.0, 0.5, 0.0, 1.0)) | |
218 | ||
219 | self.assertRaises(ValueError, grapefruit.Color, (30, 1, 0.5), 'hsv') | |
220 | ||
221 | def testEq(self): | |
222 | self.assertEqual(self.rgbCol, self.hslCol) | |
223 | self.assertEqual(self.rgbCol, (1.0, 0.5, 0.0, 1.0)) | |
224 | self.assertEqual(self.rgbCol, [1.0, 0.5, 0.0, 1.0]) | |
225 | self.assertEqual([1.0, 0.5, 0.0, 1.0], self.rgbCol) | |
226 | self.assertNotEqual(self.rgbCol, '(1.0, 0.5, 0.0, 1.0)') | |
227 | ||
228 | def testRepr(self): | |
229 | self.assertEqual(repr(self.rgbCol), '(1.0, 0.5, 0.0, 1.0)') | |
230 | self.assertEqual(repr(self.hslCol), '(1.0, 0.5, 0.0, 1.0)') | |
231 | ||
232 | def testStr(self): | |
233 | self.assertEqual(str(self.rgbCol), '(1, 0.5, 0, 1)') | |
234 | self.assertEqual(str(self.hslCol), '(1, 0.5, 0, 1)') | |
235 | ||
236 | def testIter(self): | |
237 | self.assertEqual([1, 0.5, 0, 1], list(iter(self.rgbCol))) | |
238 | ||
239 | def testProperties(self): | |
240 | self.assertEqual(self.rgbCol.alpha, 1.0) | |
241 | self.assertEqual(self.rgbCol.whiteRef, grapefruit.Color.WHITE_REFERENCE['std_D65']) | |
242 | self.assertEqual(self.rgbCol.rgb, (1, 0.5, 0)) | |
243 | self.assertEqual(self.hslCol.hue, 30) | |
244 | self.assertEqual(self.rgbCol.hsl, (30, 1, 0.5)) | |
245 | self.assertEqual(self.rgbCol.hsv, (30, 1, 1)) | |
246 | self.assertNear(self.rgbCol.yiq, (0.5923, 0.4589, -0.05)) | |
247 | self.assertNear(self.rgbCol.yuv, (0.5925, -0.2916, 0.3575)) | |
248 | self.assertNear(self.rgbCol.xyz, (0.4890, 0.3657, 0.04485)) | |
249 | self.assertNear(self.rgbCol.lab, (66.9518, 0.4308, 0.7397)) | |
250 | self.assertEqual(self.rgbCol.cmy, (0, 0.5, 1)) | |
251 | self.assertEqual(self.rgbCol.cmyk, (0, 0.5, 1, 0)) | |
252 | self.assertEqual(self.rgbCol.intTuple, (255, 128, 0)) | |
253 | self.assertEqual(self.rgbCol.html, '#ff8000') | |
254 | self.assertEqual(self.rgbCol.pil, 0x0080ff) | |
255 | self.assertEqual(self.rgbCol.webSafe, (1, 0.6, 0)) | |
256 | self.assertEqual(self.rgbCol.greyscale, (0.5, 0.5, 0.5)) | |
257 | ||
258 | c = grapefruit.Color.NewFromRgb(1, 0.5, 0, wref=grapefruit.Color.WHITE_REFERENCE['std_D50']) | |
259 | self.assertNear(c.lab, (66.9518, 0.4117, 0.6728)) | |
260 | ||
261 | def testColorWitgAlpha(self): | |
262 | self.assertEqual(self.rgbCol.ColorWithAlpha(0.5), (1, 0.5, 0, 0.5)) | |
263 | ||
264 | def testColorWithWhiteRef(self): | |
265 | self.assertEqual(self.hslCol.ColorWithWhiteRef((0.1, 0.2, 0.3)).whiteRef, (0.1, 0.2, 0.3)) | |
266 | ||
267 | def testColorWithHue(self): | |
268 | self.assertEqual(self.hslCol.ColorWithHue(60), (1.0, 1.0, 0.0, 1.0)) | |
269 | self.assertEqual(self.hslCol.ColorWithHue(60).hsl, (60, 1, 0.5)) | |
270 | ||
271 | def testColorWithSaturation(self): | |
272 | self.assertEqual(self.hslCol.ColorWithSaturation(0.5), (0.75, 0.5, 0.25, 1.0)) | |
273 | self.assertEqual(self.hslCol.ColorWithSaturation(0.5).hsl, (30, 0.5, 0.5)) | |
274 | ||
275 | def testColorWithLightness(self): | |
276 | self.assertEqual(self.hslCol.ColorWithLightness(1), (1.0, 1.0, 1.0, 1.0)) | |
277 | self.assertEqual(self.hslCol.ColorWithLightness(1).hsl, (30, 1.0, 1.0)) | |
278 | ||
279 | def testDarkerColor(self): | |
280 | self.assertNear(self.hslCol.DarkerColor(0.2), (0.6, 0.3, 0.0, 1.0)) | |
281 | self.assertNear(self.hslCol.DarkerColor(0.2).hsl, (30, 1, 0.3)) | |
282 | ||
283 | def testLighterColor(self): | |
284 | self.assertNear(self.hslCol.LighterColor(0.2), (1.0, 0.7, 0.4, 1.0)) | |
285 | self.assertNear(self.hslCol.LighterColor(0.2).hsl, (30, 1, 0.7)) | |
286 | ||
287 | def testSaturate(self): | |
288 | self.assertNear(self.hslCol2.Saturate(0.25), (0.875, 0.5, 0.125, 1.0)) | |
289 | self.assertNear(self.hslCol2.Saturate(0.25).hsl, (30, 0.75, 0.5)) | |
290 | ||
291 | def testDesaturate(self): | |
292 | self.assertNear(self.hslCol2.Desaturate(0.25), (0.625, 0.5, 0.375, 1.0)) | |
293 | self.assertNear(self.hslCol2.Desaturate(0.25).hsl, (30, 0.25, 0.5)) | |
294 | ||
295 | def testWebSafeDither(self): | |
296 | dithered = ( | |
297 | (1.0, 0.6, 0.0, 1.0), | |
298 | (1.0, 0.4, 0.0, 1.0)) | |
299 | self.assertEqual(self.rgbCol.WebSafeDither(), dithered) | |
300 | ||
301 | def testGradient(self): | |
302 | gradient = [ | |
303 | (0.75, 0.25, 0.0, 1.0), | |
304 | (0.5, 0.5, 0.0, 1.0), | |
305 | (0.25, 0.75, 0.0, 1.0)] | |
306 | c1 = grapefruit.Color.NewFromRgb(1.0, 0.0, 0.0) | |
307 | c2 = grapefruit.Color.NewFromRgb(0.0, 1.0, 0.0) | |
308 | self.assertEqual(gradient, c1.Gradient(c2, 3)) | |
309 | ||
310 | def testComplementaryColor(self): | |
311 | self.assertEqual(self.hslCol.ComplementaryColor(mode='rgb').hsl, (210, 1, 0.5)) | |
312 | ||
313 | def testMonochromeScheme(self): | |
314 | monochrome = ( | |
315 | (0.94, 0.8, 0.66, 1.0), # hsl(30, 0.7, 0.8) | |
316 | (0.6, 0.3, 0.0, 1.0), # hsl(30, 1, 0.3) | |
317 | (0.88, 0.6, 0.32, 1.0), # hsl(30, 0.7, 0.6) | |
318 | (1.0, 0.8, 0.6, 1.0)) # hsl(30, 1, 0.8) | |
319 | scheme = self.rgbCol.MonochromeScheme() | |
320 | for i in range(len(monochrome)): | |
321 | self.assertNear(scheme[i], monochrome[i]) | |
322 | ||
323 | def testTriadicScheme(self): | |
324 | triad = ( | |
325 | (0.0, 1.0, 0.5, 1.0), | |
326 | (0.5, 0.0, 1.0, 1.0)) | |
327 | self.assertEqual(self.rgbCol.TriadicScheme(mode='rgb'), triad) | |
328 | ||
329 | def testTetradicScheme(self): | |
330 | tetrad = ( | |
331 | (0.5, 1.0, 0.0, 1.0), | |
332 | (0.0, 0.5, 1.0, 1.0), | |
333 | (0.5, 0.0, 1.0, 1.0)) | |
334 | self.assertEqual(self.rgbCol.TetradicScheme(mode='rgb'), tetrad) | |
335 | ||
336 | def testAnalogousScheme(self): | |
337 | scheme = ( | |
338 | (1.0, 0.0, 0.0, 1.0), | |
339 | (1.0, 1.0, 0.0, 1.0)) | |
340 | self.assertEqual(self.rgbCol.AnalogousScheme(mode='rgb'), scheme) | |
341 | ||
342 | def testAlphaBlend(self): | |
343 | c1 = grapefruit.Color.NewFromRgb(1, 0.5, 0, alpha = 0.2) | |
344 | c2 = grapefruit.Color.NewFromRgb(1, 1, 1, alpha = 0.8) | |
345 | self.assertNear(c1.AlphaBlend(c2), (1, 0.875, 0.75, 0.84)) | |
346 | ||
347 | def testBlend(self): | |
348 | c1 = grapefruit.Color.NewFromRgb(1, 0.5, 0, alpha = 0.2) | |
349 | c2 = grapefruit.Color.NewFromRgb(1, 1, 1, alpha = 0.6) | |
350 | self.assertEqual(c1.Blend(c2), (1, 0.75, 0.5, 0.4)) | |
351 | ||
352 | def testNearestLegal(self): | |
353 | c = grapefruit.Color.NewFromRgb(1.1, -0.1, 0.5, alpha=1.1) | |
354 | self.assertFalse(c.isLegal) | |
355 | self.assertNear(c.nearestLegal.rgb, (1.0, 0.0, 0.5)) | |
356 | self.assertNear(c.nearestLegal.alpha, 1.0) | |
357 | ||
358 | def suite(): | |
359 | return unittest.TestLoader().loadTestsFromName(__name__) | |
360 | ||
361 | if __name__ == '__main__': | |
362 | unittest.main(defaultTest='suite') | |
363 | pass | |
364 | ||
365 | # vim: ts=2 sts=2 sw=2 et |
16 | 16 | |
17 | 17 | '''GrapeFruit setup and build script.''' |
18 | 18 | |
19 | # $Id: setup.py 24 2008-05-25 16:23:22Z xbasty $ | |
19 | # $Id$ | |
20 | 20 | __author__ = 'Xavier Basty <xbasty@gmail.com>' |
21 | __version__ = '0.1a3' | |
21 | __version__ = '0.1a4' | |
22 | 22 | |
23 | 23 | |
24 | 24 | # The base package metadata to be used by both distutils and setuptools |
55 | 55 | return open(file).read() |
56 | 56 | |
57 | 57 | def BuildLongDescription(): |
58 | return '\n'.join([Read('README'), Read('CHANGES')]) | |
58 | return '\n'.join([Read('README.rst'), Read('CHANGES')]) | |
59 | 59 | |
60 | 60 | def Main(): |
61 | 61 | # Build the long_description from the README and CHANGES files |