Imported Upstream version 0.1~a3+dfsg
SVN-Git Migration
8 years ago
0 | 2008-06-15 | |
1 | - Released 0.1a3 | |
2 | - Added Gradient | |
3 | ||
4 | 2008-06-01 | |
5 | - Fixed overflow in alpha blending. | |
6 | ||
7 | 2008-05-29 | |
8 | - Added Saturate/Desaturate. | |
9 | - Added MonochromeScheme | |
10 | - Added the mode parameter to the generation methods to choose the | |
11 | color wheel use for the generation (ryb/rgb). | |
12 | ||
13 | 2008-05-28 | |
14 | - Added the RGB<->RYB hue conversion. | |
15 | - Added an angle parameter to the tetrad scheme to control the shape of | |
16 | the rectangle. | |
17 | ||
18 | 2008-05-27 | |
19 | - Released 0.1a2 | |
20 | ||
21 | 2008-05-24 | |
22 | ||
23 | - Fixed the HSL->RGB conversion | |
24 | (the modulo in the hue conversion was 60 instead of 6.0!) | |
25 | Updated the unit tests (which were wrong!) | |
26 | ||
27 | 2008-05-24 | |
28 | ||
29 | Released 0.1a1 | |
30 | - Convert the documentation to Sphinx | |
31 | - Completed the unit tests | |
32 | - Fixed some stupid typos | |
33 | ||
34 | 2008-05-22 | |
35 | ||
36 | - Refactored pretty much everything to more standard "Python coding style". | |
37 | - Replaced the global variables by Color properties. | |
38 | - Moved the module functions to static methods of Color. | |
39 | - Completed the CIE white point dictionary to include all the standard | |
40 | illuminants. | |
41 | - Added doctest for all the functions. | |
42 | - Fixed the conversions factors to get better results (more exact and | |
43 | more symmetric). | |
44 | - Changed the range of the L component from [0~1] to [0~100] (as it should | |
45 | have been). | |
46 | - Added packaging data and setup. | |
47 | - Changed the structure of the unit tests. | |
48 | ||
49 | 2008-05-08 | |
50 | ||
51 | Released 0.1a0 | |
52 | - Initial checkin of grapefruit |
0 | Copyright (c) 2008, Xavier Basty | |
1 | ||
2 | Licensed under the Apache License, Version 2.0 (the "License"); | |
3 | you may not use this file except in compliance with the License. | |
4 | You may obtain a copy of the License at | |
5 | ||
6 | http://www.apache.org/licenses/LICENSE-2.0 | |
7 | ||
8 | Unless required by applicable law or agreed to in writing, software | |
9 | distributed under the License is distributed on an "AS IS" BASIS, | |
10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | See the License for the specific language governing permissions and | |
12 | limitations under the License.⏎ |
0 | Apache License | |
1 | Version 2.0, January 2004 | |
2 | http://www.apache.org/licenses/ | |
3 | ||
4 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
5 | ||
6 | 1. Definitions. | |
7 | ||
8 | "License" shall mean the terms and conditions for use, reproduction, | |
9 | and distribution as defined by Sections 1 through 9 of this document. | |
10 | ||
11 | "Licensor" shall mean the copyright owner or entity authorized by | |
12 | the copyright owner that is granting the License. | |
13 | ||
14 | "Legal Entity" shall mean the union of the acting entity and all | |
15 | other entities that control, are controlled by, or are under common | |
16 | control with that entity. For the purposes of this definition, | |
17 | "control" means (i) the power, direct or indirect, to cause the | |
18 | direction or management of such entity, whether by contract or | |
19 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
20 | outstanding shares, or (iii) beneficial ownership of such entity. | |
21 | ||
22 | "You" (or "Your") shall mean an individual or Legal Entity | |
23 | exercising permissions granted by this License. | |
24 | ||
25 | "Source" form shall mean the preferred form for making modifications, | |
26 | including but not limited to software source code, documentation | |
27 | source, and configuration files. | |
28 | ||
29 | "Object" form shall mean any form resulting from mechanical | |
30 | transformation or translation of a Source form, including but | |
31 | not limited to compiled object code, generated documentation, | |
32 | and conversions to other media types. | |
33 | ||
34 | "Work" shall mean the work of authorship, whether in Source or | |
35 | Object form, made available under the License, as indicated by a | |
36 | copyright notice that is included in or attached to the work | |
37 | (an example is provided in the Appendix below). | |
38 | ||
39 | "Derivative Works" shall mean any work, whether in Source or Object | |
40 | form, that is based on (or derived from) the Work and for which the | |
41 | editorial revisions, annotations, elaborations, or other modifications | |
42 | represent, as a whole, an original work of authorship. For the purposes | |
43 | of this License, Derivative Works shall not include works that remain | |
44 | separable from, or merely link (or bind by name) to the interfaces of, | |
45 | the Work and Derivative Works thereof. | |
46 | ||
47 | "Contribution" shall mean any work of authorship, including | |
48 | the original version of the Work and any modifications or additions | |
49 | to that Work or Derivative Works thereof, that is intentionally | |
50 | submitted to Licensor for inclusion in the Work by the copyright owner | |
51 | or by an individual or Legal Entity authorized to submit on behalf of | |
52 | the copyright owner. For the purposes of this definition, "submitted" | |
53 | means any form of electronic, verbal, or written communication sent | |
54 | to the Licensor or its representatives, including but not limited to | |
55 | communication on electronic mailing lists, source code control systems, | |
56 | and issue tracking systems that are managed by, or on behalf of, the | |
57 | Licensor for the purpose of discussing and improving the Work, but | |
58 | excluding communication that is conspicuously marked or otherwise | |
59 | designated in writing by the copyright owner as "Not a Contribution." | |
60 | ||
61 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
62 | on behalf of whom a Contribution has been received by Licensor and | |
63 | subsequently incorporated within the Work. | |
64 | ||
65 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
66 | this License, each Contributor hereby grants to You a perpetual, | |
67 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
68 | copyright license to reproduce, prepare Derivative Works of, | |
69 | publicly display, publicly perform, sublicense, and distribute the | |
70 | Work and such Derivative Works in Source or Object form. | |
71 | ||
72 | 3. Grant of Patent License. Subject to the terms and conditions of | |
73 | this License, each Contributor hereby grants to You a perpetual, | |
74 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
75 | (except as stated in this section) patent license to make, have made, | |
76 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
77 | where such license applies only to those patent claims licensable | |
78 | by such Contributor that are necessarily infringed by their | |
79 | Contribution(s) alone or by combination of their Contribution(s) | |
80 | with the Work to which such Contribution(s) was submitted. If You | |
81 | institute patent litigation against any entity (including a | |
82 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
83 | or a Contribution incorporated within the Work constitutes direct | |
84 | or contributory patent infringement, then any patent licenses | |
85 | granted to You under this License for that Work shall terminate | |
86 | as of the date such litigation is filed. | |
87 | ||
88 | 4. Redistribution. You may reproduce and distribute copies of the | |
89 | Work or Derivative Works thereof in any medium, with or without | |
90 | modifications, and in Source or Object form, provided that You | |
91 | meet the following conditions: | |
92 | ||
93 | (a) You must give any other recipients of the Work or | |
94 | Derivative Works a copy of this License; and | |
95 | ||
96 | (b) You must cause any modified files to carry prominent notices | |
97 | stating that You changed the files; and | |
98 | ||
99 | (c) You must retain, in the Source form of any Derivative Works | |
100 | that You distribute, all copyright, patent, trademark, and | |
101 | attribution notices from the Source form of the Work, | |
102 | excluding those notices that do not pertain to any part of | |
103 | the Derivative Works; and | |
104 | ||
105 | (d) If the Work includes a "NOTICE" text file as part of its | |
106 | distribution, then any Derivative Works that You distribute must | |
107 | include a readable copy of the attribution notices contained | |
108 | within such NOTICE file, excluding those notices that do not | |
109 | pertain to any part of the Derivative Works, in at least one | |
110 | of the following places: within a NOTICE text file distributed | |
111 | as part of the Derivative Works; within the Source form or | |
112 | documentation, if provided along with the Derivative Works; or, | |
113 | within a display generated by the Derivative Works, if and | |
114 | wherever such third-party notices normally appear. The contents | |
115 | of the NOTICE file are for informational purposes only and | |
116 | do not modify the License. You may add Your own attribution | |
117 | notices within Derivative Works that You distribute, alongside | |
118 | or as an addendum to the NOTICE text from the Work, provided | |
119 | that such additional attribution notices cannot be construed | |
120 | as modifying the License. | |
121 | ||
122 | You may add Your own copyright statement to Your modifications and | |
123 | may provide additional or different license terms and conditions | |
124 | for use, reproduction, or distribution of Your modifications, or | |
125 | for any such Derivative Works as a whole, provided Your use, | |
126 | reproduction, and distribution of the Work otherwise complies with | |
127 | the conditions stated in this License. | |
128 | ||
129 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
130 | any Contribution intentionally submitted for inclusion in the Work | |
131 | by You to the Licensor shall be under the terms and conditions of | |
132 | this License, without any additional terms or conditions. | |
133 | Notwithstanding the above, nothing herein shall supersede or modify | |
134 | the terms of any separate license agreement you may have executed | |
135 | with Licensor regarding such Contributions. | |
136 | ||
137 | 6. Trademarks. This License does not grant permission to use the trade | |
138 | names, trademarks, service marks, or product names of the Licensor, | |
139 | except as required for reasonable and customary use in describing the | |
140 | origin of the Work and reproducing the content of the NOTICE file. | |
141 | ||
142 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
143 | agreed to in writing, Licensor provides the Work (and each | |
144 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
145 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
146 | implied, including, without limitation, any warranties or conditions | |
147 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
148 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
149 | appropriateness of using or redistributing the Work and assume any | |
150 | risks associated with Your exercise of permissions under this License. | |
151 | ||
152 | 8. Limitation of Liability. In no event and under no legal theory, | |
153 | whether in tort (including negligence), contract, or otherwise, | |
154 | unless required by applicable law (such as deliberate and grossly | |
155 | negligent acts) or agreed to in writing, shall any Contributor be | |
156 | liable to You for damages, including any direct, indirect, special, | |
157 | incidental, or consequential damages of any character arising as a | |
158 | result of this License or out of the use or inability to use the | |
159 | Work (including but not limited to damages for loss of goodwill, | |
160 | work stoppage, computer failure or malfunction, or any and all | |
161 | other commercial damages or losses), even if such Contributor | |
162 | has been advised of the possibility of such damages. | |
163 | ||
164 | 9. Accepting Warranty or Additional Liability. While redistributing | |
165 | the Work or Derivative Works thereof, You may choose to offer, | |
166 | and charge a fee for, acceptance of support, warranty, indemnity, | |
167 | or other liability obligations and/or rights consistent with this | |
168 | License. However, in accepting such obligations, You may act only | |
169 | on Your own behalf and on Your sole responsibility, not on behalf | |
170 | of any other Contributor, and only if You agree to indemnify, | |
171 | defend, and hold each Contributor harmless for any liability | |
172 | incurred by, or claims asserted against, such Contributor by reason | |
173 | of your accepting any such warranty or additional liability. | |
174 | ||
175 | END OF TERMS AND CONDITIONS | |
176 | ||
177 | APPENDIX: How to apply the Apache License to your work. | |
178 | ||
179 | To apply the Apache License to your work, attach the following | |
180 | boilerplate notice, with the fields enclosed by brackets "[]" | |
181 | replaced with your own identifying information. (Don't include | |
182 | the brackets!) The text should be enclosed in the appropriate | |
183 | comment syntax for the file format. We also recommend that a | |
184 | file or class name and description of purpose be included on the | |
185 | same "printed page" as the copyright notice for easier | |
186 | identification within third-party archives. | |
187 | ||
188 | Copyright [yyyy] [name of copyright owner] | |
189 | ||
190 | Licensed under the Apache License, Version 2.0 (the "License"); | |
191 | you may not use this file except in compliance with the License. | |
192 | You may obtain a copy of the License at | |
193 | ||
194 | http://www.apache.org/licenses/LICENSE-2.0 | |
195 | ||
196 | Unless required by applicable law or agreed to in writing, software | |
197 | distributed under the License is distributed on an "AS IS" BASIS, | |
198 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
199 | See the License for the specific language governing permissions and | |
200 | limitations under the License.⏎ |
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 | GrapeFruit's TODO list | |
1 | ---------------------- | |
2 | ||
3 | Patches and bug reports are welcome, just please keep the style consistent | |
4 | with the original source. | |
5 | ||
6 | * grapefruit_test isn't much more that a copy of the doctests right now. | |
7 | Complete it with real unit tests that better define the module behavior. | |
8 | ||
9 | * Add sRGB <-> AdobeRGB conversion. |
0 | # Makefile for Sphinx documentation | |
1 | # | |
2 | ||
3 | # You can set these variables from the command line. | |
4 | SPHINXOPTS = | |
5 | SPHINXBUILD = sphinx-build | |
6 | PAPER = | |
7 | ||
8 | # Internal variables. | |
9 | PAPEROPT_a4 = -D latex_paper_size=a4 | |
10 | PAPEROPT_letter = -D latex_paper_size=letter | |
11 | ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . | |
12 | ||
13 | .PHONY: help clean html web pickle htmlhelp latex changes linkcheck | |
14 | ||
15 | help: | |
16 | @echo "Please use \`make <target>' where <target> is one of" | |
17 | @echo " html to make standalone HTML files" | |
18 | @echo " pickle to make pickle files (usable by e.g. sphinx-web)" | |
19 | @echo " htmlhelp to make HTML files and a HTML help project" | |
20 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" | |
21 | @echo " changes to make an overview over all changed/added/deprecated items" | |
22 | @echo " linkcheck to check all external links for integrity" | |
23 | ||
24 | clean: | |
25 | -rm -rf _build/* | |
26 | ||
27 | html: | |
28 | mkdir -p _build/html _build/doctrees | |
29 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html | |
30 | @echo | |
31 | @echo "Build finished. The HTML pages are in _build/html." | |
32 | ||
33 | pickle: | |
34 | mkdir -p _build/pickle _build/doctrees | |
35 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle | |
36 | @echo | |
37 | @echo "Build finished; now you can process the pickle files or run" | |
38 | @echo " sphinx-web _build/pickle" | |
39 | @echo "to start the sphinx-web server." | |
40 | ||
41 | web: pickle | |
42 | ||
43 | htmlhelp: | |
44 | mkdir -p _build/htmlhelp _build/doctrees | |
45 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp | |
46 | @echo | |
47 | @echo "Build finished; now you can run HTML Help Workshop with the" \ | |
48 | ".hhp project file in _build/htmlhelp." | |
49 | ||
50 | latex: | |
51 | mkdir -p _build/latex _build/doctrees | |
52 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex | |
53 | @echo | |
54 | @echo "Build finished; the LaTeX files are in _build/latex." | |
55 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ | |
56 | "run these through (pdf)latex." | |
57 | ||
58 | changes: | |
59 | mkdir -p _build/changes _build/doctrees | |
60 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes | |
61 | @echo | |
62 | @echo "The overview file is in _build/changes." | |
63 | ||
64 | linkcheck: | |
65 | mkdir -p _build/linkcheck _build/doctrees | |
66 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck | |
67 | @echo | |
68 | @echo "Link check complete; look for any errors in the above output " \ | |
69 | "or in _build/linkcheck/output.txt." |
0 | # -*- coding: utf-8 -*- | |
1 | # | |
2 | # GrapeFruit documentation build configuration file, created by | |
3 | # sphinx-quickstart on Sat May 24 00:09:48 2008. | |
4 | # | |
5 | # This file is execfile()d with the current directory set to its containing dir. | |
6 | # | |
7 | # The contents of this file are pickled, so don't put values in the namespace | |
8 | # that aren't pickleable (module imports are okay, they're removed automatically). | |
9 | # | |
10 | # All configuration values have a default value; values that are commented out | |
11 | # serve to show the default value. | |
12 | # | |
13 | # $Id: conf.py 14 2008-05-24 09:46:30Z xbasty $ | |
14 | ||
15 | import sys, os | |
16 | ||
17 | # If your extensions are in another directory, add it here. If the directory | |
18 | # is relative to the documentation root, use os.path.abspath to make it | |
19 | # absolute, like shown here. | |
20 | #sys.path.append(os.path.abspath('some/directory')) | |
21 | ||
22 | # General configuration | |
23 | # --------------------- | |
24 | ||
25 | # Add any Sphinx extension module names here, as strings. They can be extensions | |
26 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. | |
27 | extensions = ['sphinx.ext.autodoc'] | |
28 | ||
29 | # Add any paths that contain templates here, relative to this directory. | |
30 | templates_path = ['_templates'] | |
31 | ||
32 | # The suffix of source filenames. | |
33 | source_suffix = '.rst' | |
34 | ||
35 | # The master toctree document. | |
36 | master_doc = 'index' | |
37 | ||
38 | # General substitutions. | |
39 | project = 'GrapeFruit' | |
40 | copyright = '2008, Xavier Basty <xbasty@gmail.com>' | |
41 | ||
42 | # The default replacements for |version| and |release|, also used in various | |
43 | # other places throughout the built documents. | |
44 | # | |
45 | # The short X.Y version. | |
46 | version = '0.1' | |
47 | # The full version, including alpha/beta/rc tags. | |
48 | release = '0.1a3' | |
49 | ||
50 | # There are two options for replacing |today|: either, you set today to some | |
51 | # non-false value, then it is used: | |
52 | #today = '' | |
53 | # Else, today_fmt is used as the format for a strftime call. | |
54 | today_fmt = '%B %d, %Y' | |
55 | ||
56 | # List of documents that shouldn't be included in the build. | |
57 | #unused_docs = [] | |
58 | ||
59 | # List of directories, relative to source directories, that shouldn't be searched | |
60 | # for source files. | |
61 | #exclude_dirs = [] | |
62 | ||
63 | # If true, '()' will be appended to :func: etc. cross-reference text. | |
64 | #add_function_parentheses = True | |
65 | ||
66 | # If true, the current module name will be prepended to all description | |
67 | # unit titles (such as .. function::). | |
68 | #add_module_names = True | |
69 | ||
70 | # If true, sectionauthor and moduleauthor directives will be shown in the | |
71 | # output. They are ignored by default. | |
72 | #show_authors = False | |
73 | ||
74 | # The name of the Pygments (syntax highlighting) style to use. | |
75 | pygments_style = 'sphinx' | |
76 | ||
77 | ||
78 | # Options for HTML output | |
79 | # ----------------------- | |
80 | ||
81 | # The style sheet to use for HTML and HTML Help pages. A file of that name | |
82 | # must exist either in Sphinx' static/ path, or in one of the custom paths | |
83 | # given in html_static_path. | |
84 | html_style = 'default.css' | |
85 | ||
86 | # The name for this set of Sphinx documents. If None, it defaults to | |
87 | # "<project> v<release> documentation". | |
88 | #html_title = None | |
89 | ||
90 | # The name of an image file (within the static path) to place at the top of | |
91 | # the sidebar. | |
92 | #html_logo = None | |
93 | ||
94 | # Add any paths that contain custom static files (such as style sheets) here, | |
95 | # relative to this directory. They are copied after the builtin static files, | |
96 | # so a file named "default.css" will overwrite the builtin "default.css". | |
97 | html_static_path = ['_static'] | |
98 | ||
99 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, | |
100 | # using the given strftime format. | |
101 | html_last_updated_fmt = '%b %d, %Y' | |
102 | ||
103 | # If true, SmartyPants will be used to convert quotes and dashes to | |
104 | # typographically correct entities. | |
105 | #html_use_smartypants = True | |
106 | ||
107 | # Custom sidebar templates, maps document names to template names. | |
108 | #html_sidebars = {} | |
109 | ||
110 | # Additional templates that should be rendered to pages, maps page names to | |
111 | # template names. | |
112 | #html_additional_pages = {} | |
113 | ||
114 | # If false, no module index is generated. | |
115 | #html_use_modindex = True | |
116 | ||
117 | # If true, the reST sources are included in the HTML build as _sources/<name>. | |
118 | #html_copy_source = True | |
119 | ||
120 | # If true, an OpenSearch description file will be output, and all pages will | |
121 | # contain a <link> tag referring to it. The value of this option must be the | |
122 | # base URL from which the finished HTML is served. | |
123 | #html_use_opensearch = '' | |
124 | ||
125 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). | |
126 | #html_file_suffix = '' | |
127 | ||
128 | # Output file base name for HTML help builder. | |
129 | htmlhelp_basename = 'GrapeFruitdoc' | |
130 | ||
131 | ||
132 | # Options for LaTeX output | |
133 | # ------------------------ | |
134 | ||
135 | # The paper size ('letter' or 'a4'). | |
136 | #latex_paper_size = 'letter' | |
137 | ||
138 | # The font size ('10pt', '11pt' or '12pt'). | |
139 | #latex_font_size = '10pt' | |
140 | ||
141 | # Grouping the document tree into LaTeX files. List of tuples | |
142 | # (source start file, target name, title, author, document class [howto/manual]). | |
143 | latex_documents = [ | |
144 | ('index', 'GrapeFruit.tex', 'GrapeFruit Documentation', 'Xavier Basty <xbasty@gmail.com>', 'manual'), | |
145 | ] | |
146 | ||
147 | # The name of an image file (relative to this directory) to place at the top of | |
148 | # the title page. | |
149 | #latex_logo = None | |
150 | ||
151 | # For "manual" documents, if this is true, then toplevel headings are parts, | |
152 | # not chapters. | |
153 | #latex_use_parts = False | |
154 | ||
155 | # Additional stuff for the LaTeX preamble. | |
156 | #latex_preamble = '' | |
157 | ||
158 | # Documents to append as an appendix to all manuals. | |
159 | #latex_appendices = [] | |
160 | ||
161 | # If false, no module index is generated. | |
162 | #latex_use_modindex = True |
0 | .. _grapefruit-index: | |
1 | ||
2 | .. image:: _static/GrapeFruit.png | |
3 | ||
4 | Welcome! This is the documentation for GrapeFruit |release|, | |
5 | last updated |today|. | |
6 | ||
7 | See the :ref:`genindex` for a list of the topics. | |
8 | ||
9 | ||
10 | .. module:: grapefruit | |
11 | .. moduleauthor:: Xavier Basty <xbasty@gmail.com> | |
12 | ||
13 | ========================== | |
14 | The Color class | |
15 | ========================== | |
16 | ||
17 | .. class:: Color | |
18 | ||
19 | The grapefruit module contains only the :class:`Color` class, which exposes all | |
20 | the functionnalities. It can be used to store a color value and manipulate it, | |
21 | or convert it to another color system. | |
22 | ||
23 | If you are only interested in converting you colors from one system to another, | |
24 | you can store them using regular tuples instead of :class:`Color` instances. | |
25 | You can then use the class static methods to perform the conversions. | |
26 | ||
27 | :class:`Color` stores both the RGB and HSL representation of the color. | |
28 | This makes possible to keep the hue intact when the color is a pure white | |
29 | due to its lightness. | |
30 | However, certain operations work only with the RGB values, and might then | |
31 | lose the hue. | |
32 | ||
33 | All the operations assume that you provide values in the specified ranges, | |
34 | no checks are made whatsoever. If you provide a value outside of the | |
35 | specified ranges, you'll get some strange results... | |
36 | ||
37 | The class instances are immutable, all the methods return a new instance | |
38 | of the :class:`Color` class, and all the properties are read-only. | |
39 | ||
40 | .. note:: | |
41 | ||
42 | Some operations may provide results a bit outside the specified ranges, | |
43 | the results are not capped. | |
44 | This is due to certain color systems having a widers gamut than others. | |
45 | ||
46 | ||
47 | Class content | |
48 | --------------- | |
49 | ||
50 | - :ref:`class-constants` | |
51 | ||
52 | - :const:`Color.WHITE_REFERENCE` | |
53 | - :const:`Color.NAMED_COLOR` | |
54 | ||
55 | - :ref:`conversion-functions` | |
56 | ||
57 | - :meth:`Color.RgbToHsl` | |
58 | - :meth:`Color.HslToRgb` | |
59 | - :meth:`Color.RgbToHsv` | |
60 | - :meth:`Color.HsvToRgb` | |
61 | - :meth:`Color.RgbToYiq` | |
62 | - :meth:`Color.YiqToRgb` | |
63 | - :meth:`Color.RgbToYuv` | |
64 | - :meth:`Color.YuvToRgb` | |
65 | - :meth:`Color.RgbToXyz` | |
66 | - :meth:`Color.XyzToRgb` | |
67 | - :meth:`Color.XyzToLab` | |
68 | - :meth:`Color.LabToXyz` | |
69 | - :meth:`Color.CmykToCmy` | |
70 | - :meth:`Color.CmyToCmyk` | |
71 | - :meth:`Color.RgbToCmy` | |
72 | - :meth:`Color.CmyToRgb` | |
73 | - :meth:`Color.RgbToHtml` | |
74 | - :meth:`Color.HtmlToRgb` | |
75 | - :meth:`Color.RgbToPil` | |
76 | - :meth:`Color.PilToRgb` | |
77 | - :meth:`Color.RgbToWebSafe` | |
78 | - :meth:`Color.RgbToGreyscale` | |
79 | - :meth:`Color.RgbToRyb` | |
80 | - :meth:`Color.RybToRgb` | |
81 | ||
82 | - :ref:`instantiation-functions` | |
83 | ||
84 | - :meth:`Color.NewFromRgb` | |
85 | - :meth:`Color.NewFromHsl` | |
86 | - :meth:`Color.NewFromHsv` | |
87 | - :meth:`Color.NewFromYiq` | |
88 | - :meth:`Color.NewFromYuv` | |
89 | - :meth:`Color.NewFromXyz` | |
90 | - :meth:`Color.NewFromLab` | |
91 | - :meth:`Color.NewFromCmy` | |
92 | - :meth:`Color.NewFromCmyk` | |
93 | - :meth:`Color.NewFromHtml` | |
94 | - :meth:`Color.NewFromPil` | |
95 | ||
96 | - :ref:`properties` | |
97 | ||
98 | - :attr:`Color.alpha` | |
99 | - :attr:`Color.whiteRef` | |
100 | - :attr:`Color.rgb` | |
101 | - :attr:`Color.hue` | |
102 | - :attr:`Color.hsl` | |
103 | - :attr:`Color.hsv` | |
104 | - :attr:`Color.yiq` | |
105 | - :attr:`Color.yuv` | |
106 | - :attr:`Color.xyz` | |
107 | - :attr:`Color.lab` | |
108 | - :attr:`Color.cmy` | |
109 | - :attr:`Color.cmyk` | |
110 | - :attr:`Color.html` | |
111 | - :attr:`Color.pil` | |
112 | - :attr:`Color.webSafe` | |
113 | - :attr:`Color.greyscale` | |
114 | ||
115 | - :ref:`manipulation-methods` | |
116 | ||
117 | - :meth:`Color.ColorWithAlpha` | |
118 | - :meth:`Color.ColorWithWhiteRef` | |
119 | - :meth:`Color.ColorWithHue` | |
120 | - :meth:`Color.ColorWithSaturation` | |
121 | - :meth:`Color.ColorWithLightness` | |
122 | - :meth:`Color.DarkerColor` | |
123 | - :meth:`Color.LighterColor` | |
124 | - :meth:`Color.Saturate` | |
125 | - :meth:`Color.Desaturate` | |
126 | - :meth:`Color.WebSafeDither` | |
127 | ||
128 | - :ref:`generation-methods` | |
129 | ||
130 | - :meth:`Color.Gradient` | |
131 | - :meth:`Color.ComplementaryColor` | |
132 | - :meth:`Color.TriadicScheme` | |
133 | - :meth:`Color.TetradicScheme` | |
134 | - :meth:`Color.AnalogousScheme` | |
135 | ||
136 | - :ref:`blending-methods` | |
137 | ||
138 | - :meth:`Color.AlphaBlend` | |
139 | - :meth:`Color.Blend` | |
140 | ||
141 | ||
142 | Example usage | |
143 | --------------- | |
144 | ||
145 | To create an instance of the grapefruit.Color from RGB values: | |
146 | ||
147 | >>> import grapefruit | |
148 | >>> r, g, b = 1, 0.5, 0 | |
149 | >>> col = grapefruit.Color.NewFromRgb(r, g, b) | |
150 | ||
151 | To get the values of the color in another colorspace: | |
152 | ||
153 | >>> h, s, v = col.hsv | |
154 | >>> l, a, b = col.lab | |
155 | ||
156 | To get the complementary of a color: | |
157 | ||
158 | >>> compl = col.ComplementaryColor() | |
159 | >>> print compl.hsl | |
160 | (210.0, 1.0, 0.5) | |
161 | ||
162 | To directly convert RGB values to their HSL equivalent: | |
163 | ||
164 | >>> h, s, l = Color.RgbToHsl(r, g, b) | |
165 | ||
166 | ||
167 | ||
168 | .. _class-constants: | |
169 | ||
170 | Class Constants | |
171 | ----------------- | |
172 | ||
173 | .. data:: Color.WHITE_REFERENCE | |
174 | ||
175 | The reference white points of the CIE standards illuminants, calculated from | |
176 | the chromaticity coordinates found at: | |
177 | http://en.wikipedia.org/wiki/Standard_illuminant | |
178 | ||
179 | A dictionary mapping the name of the CIE standard illuminants to their reference | |
180 | white points. The white points are required for the XYZ <-> L*a*b conversions. | |
181 | ||
182 | The key names are build using the following pattern: ``<observer>_<illuminant>`` | |
183 | ||
184 | The possible values for ``<observer>`` are: | |
185 | ||
186 | ====== =================================== | |
187 | Value Observer | |
188 | ====== =================================== | |
189 | std CIE 1931 2° Standard Observer | |
190 | sup CIE 1964 10° Supplementary Observer | |
191 | ====== =================================== | |
192 | ||
193 | The possible values for ``<illuminant>`` are the name of the standard illuminants: | |
194 | ||
195 | ====== ======== ================================================== | |
196 | Value CCT Illuminant | |
197 | ====== ======== ================================================== | |
198 | A 2856 K Incandescent tungsten | |
199 | B 4874 K Direct sunlight at noon (obsolete) | |
200 | C 6774 K North sky daylight (obsolete) | |
201 | D50 5003 K ICC Profile PCS. Horizon light. | |
202 | D55 5503 K Compromise between incandescent and daylight | |
203 | D65 6504 K Noon daylight (TV & sRGB colorspace) | |
204 | D75 7504 K North sky day light | |
205 | E ~5455 K Equal energy radiator (not a black body) | |
206 | F1 6430 K Daylight Fluorescent | |
207 | F2 4230 K Cool White Fluorescent | |
208 | F3 3450 K White Fluorescent | |
209 | F4 2940 K Warm White Fluorescent | |
210 | F5 6350 K Daylight Fluorescent | |
211 | F6 4150 K Lite White Fluorescent | |
212 | F7 6500 K Broadband fluorescent, D65 simulator | |
213 | F8 5000 K Broadband fluorescent, D50 simulator | |
214 | F9 4150 K Broadband fluorescent, Cool White Deluxe | |
215 | F10 5000 K Narrowband fluorescent, Philips TL85, Ultralume 50 | |
216 | F11 4000 K Narrowband fluorescent, Philips TL84, Ultralume 40 | |
217 | F12 3000 K Narrowband fluorescent, Philips TL83, Ultralume 30 | |
218 | ====== ======== ================================================== | |
219 | ||
220 | .. data:: Color.NAMED_COLOR | |
221 | ||
222 | The names and RGB values of the X11 colors supported by popular browsers, with | |
223 | the gray/grey spelling issues, fixed so that both work (e.g light*grey* and | |
224 | light*gray*). | |
225 | ||
226 | Note: For *Gray*, *Green*, *Maroon* and *Purple*, the HTML/CSS values are used | |
227 | instead of the X11 ones | |
228 | (see `X11/CSS clashes <http://en.wikipedia.org/wiki/X11_color_names#Color_names_that_clash_between_X11_and_HTML.2FCSS>`_) | |
229 | ||
230 | Reference: `CSS3 Color module <http://www.w3.org/TR/css3-iccprof#x11-color>`_ | |
231 | ||
232 | ||
233 | .. _conversion-functions: | |
234 | ||
235 | Conversion functions | |
236 | -------------------- | |
237 | ||
238 | The conversion functions are static methods of the :class:`Color` class that | |
239 | let you convert a color stored as the list of its components rather than | |
240 | as a :class:`Color` instance. | |
241 | ||
242 | .. automethod:: Color.RgbToHsl | |
243 | ||
244 | .. automethod:: Color.HslToRgb | |
245 | ||
246 | .. automethod:: Color.RgbToHsv | |
247 | ||
248 | .. automethod:: Color.HsvToRgb | |
249 | ||
250 | .. automethod:: Color.RgbToYiq | |
251 | ||
252 | .. automethod:: Color.YiqToRgb | |
253 | ||
254 | .. automethod:: Color.RgbToYuv | |
255 | ||
256 | .. automethod:: Color.YuvToRgb | |
257 | ||
258 | .. automethod:: Color.RgbToXyz | |
259 | ||
260 | .. automethod:: Color.XyzToRgb | |
261 | ||
262 | .. automethod:: Color.XyzToLab | |
263 | ||
264 | .. automethod:: Color.LabToXyz | |
265 | ||
266 | .. automethod:: Color.CmykToCmy | |
267 | ||
268 | .. automethod:: Color.CmyToCmyk | |
269 | ||
270 | .. automethod:: Color.RgbToCmy | |
271 | ||
272 | .. automethod:: Color.CmyToRgb | |
273 | ||
274 | .. automethod:: Color.RgbToHtml | |
275 | ||
276 | .. automethod:: Color.HtmlToRgb | |
277 | ||
278 | .. automethod:: Color.RgbToPil | |
279 | ||
280 | .. automethod:: Color.PilToRgb | |
281 | ||
282 | .. automethod:: Color.RgbToWebSafe | |
283 | ||
284 | .. automethod:: Color.RgbToGreyscale | |
285 | ||
286 | .. automethod:: Color.RgbToRyb | |
287 | ||
288 | .. automethod:: Color.RybToRgb | |
289 | ||
290 | ||
291 | ||
292 | .. _instantiation-functions: | |
293 | ||
294 | Instantiation functions | |
295 | ----------------------- | |
296 | ||
297 | The instantiation functions let you create a new instance of the :class:`Color` | |
298 | class from the color components using the color system of your choice. | |
299 | ||
300 | .. automethod:: Color.NewFromRgb | |
301 | ||
302 | .. automethod:: Color.NewFromHsl | |
303 | ||
304 | .. automethod:: Color.NewFromHsv | |
305 | ||
306 | .. automethod:: Color.NewFromYiq | |
307 | ||
308 | .. automethod:: Color.NewFromYuv | |
309 | ||
310 | .. automethod:: Color.NewFromXyz | |
311 | ||
312 | .. automethod:: Color.NewFromLab | |
313 | ||
314 | .. automethod:: Color.NewFromCmy | |
315 | ||
316 | .. automethod:: Color.NewFromCmyk | |
317 | ||
318 | .. automethod:: Color.NewFromHtml | |
319 | ||
320 | .. automethod:: Color.NewFromPil | |
321 | ||
322 | ||
323 | ||
324 | .. _properties: | |
325 | ||
326 | Properties | |
327 | ---------- | |
328 | ||
329 | The properties get the value of the instance in the specified color model. | |
330 | ||
331 | The properties returning calculated values unless marked otherwise. | |
332 | ||
333 | .. note:: | |
334 | ||
335 | All the properties are read-only. You need to make a copy of the instance | |
336 | to modify the color value. | |
337 | ||
338 | .. autoattribute:: Color.alpha | |
339 | ||
340 | *This value is not calculated, the stored value is returned directly.* | |
341 | ||
342 | .. autoattribute:: Color.whiteRef | |
343 | ||
344 | *This value is not calculated, the stored value is returned directly.* | |
345 | ||
346 | .. autoattribute:: Color.rgb | |
347 | ||
348 | *This value is not calculated, the stored value is returned directly.* | |
349 | ||
350 | .. autoattribute:: Color.hue | |
351 | ||
352 | *This value is not calculated, the stored value is returned directly.* | |
353 | ||
354 | .. autoattribute:: Color.hsl | |
355 | ||
356 | *This value is not calculated, the stored value is returned directly.* | |
357 | ||
358 | .. autoattribute:: Color.hsv | |
359 | ||
360 | .. autoattribute:: Color.yiq | |
361 | ||
362 | .. autoattribute:: Color.yuv | |
363 | ||
364 | .. autoattribute:: Color.xyz | |
365 | ||
366 | .. autoattribute:: Color.lab | |
367 | ||
368 | .. autoattribute:: Color.cmy | |
369 | ||
370 | .. autoattribute:: Color.cmyk | |
371 | ||
372 | .. autoattribute:: Color.html | |
373 | ||
374 | .. autoattribute:: Color.pil | |
375 | ||
376 | .. autoattribute:: Color.webSafe | |
377 | ||
378 | .. attribute:: Color.greyscale | |
379 | ||
380 | ||
381 | ||
382 | .. _manipulation-methods: | |
383 | ||
384 | Manipulation methods | |
385 | -------------------- | |
386 | ||
387 | The manipulations methods let you create a new color by changing an existing | |
388 | color properties. | |
389 | ||
390 | .. note:: | |
391 | ||
392 | The methods **do not** modify the current Color instance. They create a | |
393 | new instance or a tuple of new instances with the specified modifications. | |
394 | ||
395 | .. automethod:: Color.ColorWithAlpha | |
396 | ||
397 | .. automethod:: Color.ColorWithWhiteRef | |
398 | ||
399 | .. automethod:: Color.ColorWithHue | |
400 | ||
401 | .. automethod:: Color.ColorWithSaturation | |
402 | ||
403 | .. automethod:: Color.ColorWithLightness | |
404 | ||
405 | .. automethod:: Color.DarkerColor | |
406 | ||
407 | .. automethod:: Color.LighterColor | |
408 | ||
409 | .. automethod:: Color.Saturate | |
410 | ||
411 | .. automethod:: Color.Desaturate | |
412 | ||
413 | .. automethod:: Color.WebSafeDither | |
414 | ||
415 | ||
416 | ||
417 | .. _generation-methods: | |
418 | ||
419 | Generation methods | |
420 | ------------------ | |
421 | ||
422 | The generation methods let you create a color scheme by using a color as the | |
423 | start point. | |
424 | ||
425 | All the method, appart from Gradient and MonochromeScheme, have a 'mode' | |
426 | parameter that let you choose which color wheel should be used to generate | |
427 | the scheme. | |
428 | ||
429 | The following modes are available: | |
430 | :ryb: | |
431 | The `RYB <http://en.wikipedia.org/wiki/RYB_color_model>`_ color wheel, | |
432 | or *artistic color wheel*. While scientifically incorrect, it generally | |
433 | produces better schemes than RGB. | |
434 | :rgb: | |
435 | The standard RGB color wheel. | |
436 | ||
437 | .. automethod:: Color.Gradient | |
438 | ||
439 | .. automethod:: Color.ComplementaryColor | |
440 | ||
441 | .. automethod:: Color.MonochromeScheme | |
442 | ||
443 | .. automethod:: Color.TriadicScheme | |
444 | ||
445 | .. automethod:: Color.TetradicScheme | |
446 | ||
447 | .. automethod:: Color.AnalogousScheme | |
448 | ||
449 | ||
450 | ||
451 | .. _blending-methods: | |
452 | ||
453 | Blending methods | |
454 | ---------------- | |
455 | ||
456 | .. automethod:: Color.AlphaBlend | |
457 | ||
458 | .. automethod:: Color.Blend |
0 | @echo off | |
1 | set builder=%1 | |
2 | if [%builder%]==[] set builder=html | |
3 | set target=_build\%builder% | |
4 | if not exist %target%\* md %target% | |
5 | sphinx-build -b %builder% . _build\%builder%\ | |
6 | set target= | |
7 | set builder= |
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 | CHANGES | |
1 | COPYING | |
2 | LICENSE | |
3 | README | |
4 | TODO | |
5 | grapefruit.py | |
6 | grapefruit_test.py | |
7 | setup.py | |
8 | doc/Makefile | |
9 | doc/conf.py | |
10 | doc/index.rst | |
11 | doc/makedoc.cmd | |
12 | doc/_static/GrapeFruit.png | |
13 | grapefruit.egg-info/PKG-INFO | |
14 | grapefruit.egg-info/SOURCES.txt | |
15 | grapefruit.egg-info/dependency_links.txt | |
16 | grapefruit.egg-info/requires.txt | |
17 | grapefruit.egg-info/top_level.txt | |
18 | grapefruit.egg-info/zip-safe⏎ |
0 | setuptools⏎ |
0 | grapefruit |
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 | '''GrapeFruit - Color manipulation in Python''' | |
18 | ||
19 | # $Id: grapefruit.py 30 2008-06-01 20:44:26Z xbasty $ | |
20 | __author__ = 'Xavier Basty <xbasty@gmail.com>' | |
21 | __version__ = '0.1a3' | |
22 | ||
23 | ||
24 | # The default white reference, use 2° Standard Observer, D65 (daylight) | |
25 | _DEFAULT_WREF = (0.95043, 1.00000, 1.08890) | |
26 | ||
27 | _oneThird = 1.0 / 3 | |
28 | _srgbGammaCorrInv = 0.03928 / 12.92 | |
29 | _sixteenHundredsixteenth = 16.0 / 116 | |
30 | ||
31 | _RybWheel = ( | |
32 | 0, 26, 52, | |
33 | 83, 120, 130, | |
34 | 141, 151, 162, | |
35 | 177, 190, 204, | |
36 | 218, 232, 246, | |
37 | 261, 275, 288, | |
38 | 303, 317, 330, | |
39 | 338, 345, 352, | |
40 | 360) | |
41 | ||
42 | _RgbWheel = ( | |
43 | 0, 8, 17, | |
44 | 26, 34, 41, | |
45 | 48, 54, 60, | |
46 | 81, 103, 123, | |
47 | 138, 155, 171, | |
48 | 187, 204, 219, | |
49 | 234, 251, 267, | |
50 | 282, 298, 329, | |
51 | 360) | |
52 | ||
53 | class Color: | |
54 | '''Hold a color value. | |
55 | ||
56 | Example usage: | |
57 | ||
58 | To create an instance of the grapefruit.Color from RGB values: | |
59 | ||
60 | >>> import grapefruit | |
61 | >>> r, g, b = 1, 0.5, 0 | |
62 | >>> col = grapefruit.Color.NewFromRgb(r, g, b) | |
63 | ||
64 | To get the values of the color in another colorspace: | |
65 | ||
66 | >>> h, s, v = col.hsv | |
67 | >>> l, a, b = col.lab | |
68 | ||
69 | To get the complementary of a color: | |
70 | ||
71 | >>> compl = col.ComplementaryColor() | |
72 | >>> print compl.hsl | |
73 | (210.0, 1.0, 0.5) | |
74 | ||
75 | To directly convert RGB values to their HSL equivalent: | |
76 | ||
77 | >>> h, s, l = Color.RgbToHsl(r, g, b) | |
78 | ||
79 | ''' | |
80 | ||
81 | WHITE_REFERENCE = { | |
82 | 'std_A' : (1.09847, 1.00000, 0.35582), | |
83 | 'std_B' : (0.99093, 1.00000, 0.85313), | |
84 | 'std_C' : (0.98071, 1.00000, 1.18225), | |
85 | 'std_D50' : (0.96421, 1.00000, 0.82519), | |
86 | 'std_D55' : (0.95680, 1.00000, 0.92148), | |
87 | 'std_D65' : (0.95043, 1.00000, 1.08890), | |
88 | 'std_D75' : (0.94972, 1.00000, 1.22639), | |
89 | 'std_E' : (1.00000, 1.00000, 1.00000), | |
90 | 'std_F1' : (0.92834, 1.00000, 1.03665), | |
91 | 'std_F2' : (0.99145, 1.00000, 0.67316), | |
92 | 'std_F3' : (1.03753, 1.00000, 0.49861), | |
93 | 'std_F4' : (1.09147, 1.00000, 0.38813), | |
94 | 'std_F5' : (0.90872, 1.00000, 0.98723), | |
95 | 'std_F6' : (0.97309, 1.00000, 0.60191), | |
96 | 'std_F7' : (0.95017, 1.00000, 1.08630), | |
97 | 'std_F8' : (0.96413, 1.00000, 0.82333), | |
98 | 'std_F9' : (1.00365, 1.00000, 0.67868), | |
99 | 'std_F10' : (0.96174, 1.00000, 0.81712), | |
100 | 'std_F11' : (1.00899, 1.00000, 0.64262), | |
101 | 'std_F12' : (1.08046, 1.00000, 0.39228), | |
102 | 'sup_A' : (1.11142, 1.00000, 0.35200), | |
103 | 'sup_B' : (0.99178, 1.00000, 0.84349), | |
104 | 'sup_C' : (0.97286, 1.00000, 1.16145), | |
105 | 'sup_D50' : (0.96721, 1.00000, 0.81428), | |
106 | 'sup_D55' : (0.95797, 1.00000, 0.90925), | |
107 | 'sup_D65' : (0.94810, 1.00000, 1.07305), | |
108 | 'sup_D75' : (0.94417, 1.00000, 1.20643), | |
109 | 'sup_E' : (1.00000, 1.00000, 1.00000), | |
110 | 'sup_F1' : (0.94791, 1.00000, 1.03191), | |
111 | 'sup_F2' : (1.03245, 1.00000, 0.68990), | |
112 | 'sup_F3' : (1.08968, 1.00000, 0.51965), | |
113 | 'sup_F4' : (1.14961, 1.00000, 0.40963), | |
114 | 'sup_F5' : (0.93369, 1.00000, 0.98636), | |
115 | 'sup_F6' : (1.02148, 1.00000, 0.62074), | |
116 | 'sup_F7' : (0.95780, 1.00000, 1.07618), | |
117 | 'sup_F8' : (0.97115, 1.00000, 0.81135), | |
118 | 'sup_F9' : (1.02116, 1.00000, 0.67826), | |
119 | 'sup_F10' : (0.99001, 1.00000, 0.83134), | |
120 | 'sup_F11' : (1.03820, 1.00000, 0.65555), | |
121 | 'sup_F12' : (1.11428, 1.00000, 0.40353)} | |
122 | ||
123 | NAMED_COLOR = { | |
124 | 'aliceblue': '#f0f8ff', | |
125 | 'antiquewhite': '#faebd7', | |
126 | 'aqua': '#00ffff', | |
127 | 'aquamarine': '#7fffd4', | |
128 | 'azure': '#f0ffff', | |
129 | 'beige': '#f5f5dc', | |
130 | 'bisque': '#ffe4c4', | |
131 | 'black': '#000000', | |
132 | 'blanchedalmond': '#ffebcd', | |
133 | 'blue': '#0000ff', | |
134 | 'blueviolet': '#8a2be2', | |
135 | 'brown': '#a52a2a', | |
136 | 'burlywood': '#deb887', | |
137 | 'cadetblue': '#5f9ea0', | |
138 | 'chartreuse': '#7fff00', | |
139 | 'chocolate': '#d2691e', | |
140 | 'coral': '#ff7f50', | |
141 | 'cornflowerblue': '#6495ed', | |
142 | 'cornsilk': '#fff8dc', | |
143 | 'crimson': '#dc143c', | |
144 | 'cyan': '#00ffff', | |
145 | 'darkblue': '#00008b', | |
146 | 'darkcyan': '#008b8b', | |
147 | 'darkgoldenrod': '#b8860b', | |
148 | 'darkgray': '#a9a9a9', | |
149 | 'darkgrey': '#a9a9a9', | |
150 | 'darkgreen': '#006400', | |
151 | 'darkkhaki': '#bdb76b', | |
152 | 'darkmagenta': '#8b008b', | |
153 | 'darkolivegreen': '#556b2f', | |
154 | 'darkorange': '#ff8c00', | |
155 | 'darkorchid': '#9932cc', | |
156 | 'darkred': '#8b0000', | |
157 | 'darksalmon': '#e9967a', | |
158 | 'darkseagreen': '#8fbc8f', | |
159 | 'darkslateblue': '#483d8b', | |
160 | 'darkslategray': '#2f4f4f', | |
161 | 'darkslategrey': '#2f4f4f', | |
162 | 'darkturquoise': '#00ced1', | |
163 | 'darkviolet': '#9400d3', | |
164 | 'deeppink': '#ff1493', | |
165 | 'deepskyblue': '#00bfff', | |
166 | 'dimgray': '#696969', | |
167 | 'dimgrey': '#696969', | |
168 | 'dodgerblue': '#1e90ff', | |
169 | 'firebrick': '#b22222', | |
170 | 'floralwhite': '#fffaf0', | |
171 | 'forestgreen': '#228b22', | |
172 | 'fuchsia': '#ff00ff', | |
173 | 'gainsboro': '#dcdcdc', | |
174 | 'ghostwhite': '#f8f8ff', | |
175 | 'gold': '#ffd700', | |
176 | 'goldenrod': '#daa520', | |
177 | 'gray': '#808080', | |
178 | 'grey': '#808080', | |
179 | 'green': '#008000', | |
180 | 'greenyellow': '#adff2f', | |
181 | 'honeydew': '#f0fff0', | |
182 | 'hotpink': '#ff69b4', | |
183 | 'indianred': '#cd5c5c', | |
184 | 'indigo': '#4b0082', | |
185 | 'ivory': '#fffff0', | |
186 | 'khaki': '#f0e68c', | |
187 | 'lavender': '#e6e6fa', | |
188 | 'lavenderblush': '#fff0f5', | |
189 | 'lawngreen': '#7cfc00', | |
190 | 'lemonchiffon': '#fffacd', | |
191 | 'lightblue': '#add8e6', | |
192 | 'lightcoral': '#f08080', | |
193 | 'lightcyan': '#e0ffff', | |
194 | 'lightgoldenrodyellow': '#fafad2', | |
195 | 'lightgreen': '#90ee90', | |
196 | 'lightgray': '#d3d3d3', | |
197 | 'lightgrey': '#d3d3d3', | |
198 | 'lightpink': '#ffb6c1', | |
199 | 'lightsalmon': '#ffa07a', | |
200 | 'lightseagreen': '#20b2aa', | |
201 | 'lightskyblue': '#87cefa', | |
202 | 'lightslategray': '#778899', | |
203 | 'lightslategrey': '#778899', | |
204 | 'lightsteelblue': '#b0c4de', | |
205 | 'lightyellow': '#ffffe0', | |
206 | 'lime': '#00ff00', | |
207 | 'limegreen': '#32cd32', | |
208 | 'linen': '#faf0e6', | |
209 | 'magenta': '#ff00ff', | |
210 | 'maroon': '#800000', | |
211 | 'mediumaquamarine': '#66cdaa', | |
212 | 'mediumblue': '#0000cd', | |
213 | 'mediumorchid': '#ba55d3', | |
214 | 'mediumpurple': '#9370db', | |
215 | 'mediumseagreen': '#3cb371', | |
216 | 'mediumslateblue': '#7b68ee', | |
217 | 'mediumspringgreen': '#00fa9a', | |
218 | 'mediumturquoise': '#48d1cc', | |
219 | 'mediumvioletred': '#c71585', | |
220 | 'midnightblue': '#191970', | |
221 | 'mintcream': '#f5fffa', | |
222 | 'mistyrose': '#ffe4e1', | |
223 | 'moccasin': '#ffe4b5', | |
224 | 'navajowhite': '#ffdead', | |
225 | 'navy': '#000080', | |
226 | 'oldlace': '#fdf5e6', | |
227 | 'olive': '#808000', | |
228 | 'olivedrab': '#6b8e23', | |
229 | 'orange': '#ffa500', | |
230 | 'orangered': '#ff4500', | |
231 | 'orchid': '#da70d6', | |
232 | 'palegoldenrod': '#eee8aa', | |
233 | 'palegreen': '#98fb98', | |
234 | 'paleturquoise': '#afeeee', | |
235 | 'palevioletred': '#db7093', | |
236 | 'papayawhip': '#ffefd5', | |
237 | 'peachpuff': '#ffdab9', | |
238 | 'peru': '#cd853f', | |
239 | 'pink': '#ffc0cb', | |
240 | 'plum': '#dda0dd', | |
241 | 'powderblue': '#b0e0e6', | |
242 | 'purple': '#800080', | |
243 | 'red': '#ff0000', | |
244 | 'rosybrown': '#bc8f8f', | |
245 | 'royalblue': '#4169e1', | |
246 | 'saddlebrown': '#8b4513', | |
247 | 'salmon': '#fa8072', | |
248 | 'sandybrown': '#f4a460', | |
249 | 'seagreen': '#2e8b57', | |
250 | 'seashell': '#fff5ee', | |
251 | 'sienna': '#a0522d', | |
252 | 'silver': '#c0c0c0', | |
253 | 'skyblue': '#87ceeb', | |
254 | 'slateblue': '#6a5acd', | |
255 | 'slategray': '#708090', | |
256 | 'slategrey': '#708090', | |
257 | 'snow': '#fffafa', | |
258 | 'springgreen': '#00ff7f', | |
259 | 'steelblue': '#4682b4', | |
260 | 'tan': '#d2b48c', | |
261 | 'teal': '#008080', | |
262 | 'thistle': '#d8bfd8', | |
263 | 'tomato': '#ff6347', | |
264 | 'turquoise': '#40e0d0', | |
265 | 'violet': '#ee82ee', | |
266 | 'wheat': '#f5deb3', | |
267 | 'white': '#ffffff', | |
268 | 'whitesmoke': '#f5f5f5', | |
269 | 'yellow': '#ffff00', | |
270 | 'yellowgreen': '#9acd32'} | |
271 | ||
272 | def __init__(self, values, mode='rgb', alpha=1.0, wref=_DEFAULT_WREF): | |
273 | '''Instantiate a new grapefruit.Color object. | |
274 | ||
275 | Parameters: | |
276 | :values: | |
277 | The values of this color, in the specified representation. | |
278 | :mode: | |
279 | The representation mode used for values. | |
280 | :alpha: | |
281 | the alpha value (transparency) of this color. | |
282 | :wref: | |
283 | The whitepoint reference, default is 2° D65. | |
284 | ||
285 | ''' | |
286 | if not(isinstance(values, tuple)): | |
287 | raise TypeError, 'values must be a tuple' | |
288 | ||
289 | if mode=='rgb': | |
290 | self.__rgb = values | |
291 | self.__hsl = Color.RgbToHsl(*values) | |
292 | elif mode=='hsl': | |
293 | self.__hsl = values | |
294 | self.__rgb = Color.HslToRgb(*values) | |
295 | else: | |
296 | raise ValueError('Invalid color mode: ' + mode) | |
297 | ||
298 | self.__a = alpha | |
299 | self.__wref = wref | |
300 | ||
301 | def __ne__(self, other): | |
302 | return not self.__eq__(other) | |
303 | ||
304 | def __eq__(self, other): | |
305 | try: | |
306 | if isinstance(other, Color): | |
307 | return (self.__rgb==other.__rgb) and (self.__a==other.__a) | |
308 | ||
309 | if len(other) != 4: | |
310 | return False | |
311 | rgba = self.__rgb + (self.__a,) | |
312 | return reduce(lambda x, y: x and (y[0]==y[1]), zip(rgba, other), True) | |
313 | except TypeError: | |
314 | return False | |
315 | except AttributeError: | |
316 | return False | |
317 | ||
318 | def __repr__(self): | |
319 | return str(self.__rgb + (self.__a,)) | |
320 | ||
321 | def __str__(self): | |
322 | '''A string representation of this grapefruit.Color instance. | |
323 | ||
324 | Returns: | |
325 | The RGBA representation of this grapefruit.Color instance. | |
326 | ||
327 | ''' | |
328 | return '(%g, %g, %g, %g)' % (self.__rgb + (self.__a,)) | |
329 | ||
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,)) | |
338 | ||
339 | def __iter__(self): | |
340 | return iter(self.__rgb + (self.__a,)) | |
341 | ||
342 | def __len__(self): | |
343 | return 4 | |
344 | ||
345 | @staticmethod | |
346 | def RgbToHsl(r, g, b): | |
347 | '''Convert the color from RGB coordinates to HSL. | |
348 | ||
349 | Parameters: | |
350 | :r: | |
351 | The Red component value [0...1] | |
352 | :g: | |
353 | The Green component value [0...1] | |
354 | :b: | |
355 | The Blue component value [0...1] | |
356 | ||
357 | Returns: | |
358 | The color as an (h, s, l) tuple in the range: | |
359 | h[0...360], | |
360 | s[0...1], | |
361 | l[0...1] | |
362 | ||
363 | >>> Color.RgbToHsl(1, 0.5, 0) | |
364 | (30.0, 1.0, 0.5) | |
365 | ||
366 | ''' | |
367 | minVal = min(r, g, b) # min RGB value | |
368 | maxVal = max(r, g, b) # max RGB value | |
369 | ||
370 | l = (maxVal + minVal) / 2.0 | |
371 | if minVal==maxVal: | |
372 | return (0.0, 0.0, l) # achromatic (gray) | |
373 | ||
374 | d = maxVal - minVal # delta RGB value | |
375 | ||
376 | if l < 0.5: s = d / (maxVal + minVal) | |
377 | else: s = d / (2.0 - maxVal - minVal) | |
378 | ||
379 | dr, dg, db = [(maxVal-val) / d for val in (r, g, b)] | |
380 | ||
381 | if r==maxVal: | |
382 | h = db - dg | |
383 | elif g==maxVal: | |
384 | h = 2.0 + dr - db | |
385 | else: | |
386 | h = 4.0 + dg - dr | |
387 | ||
388 | h = (h*60.0) % 360.0 | |
389 | return (h, s, l) | |
390 | ||
391 | @staticmethod | |
392 | def _HueToRgb(n1, n2, h): | |
393 | h %= 6.0 | |
394 | if h < 1.0: return n1 + ((n2-n1) * h) | |
395 | if h < 3.0: return n2 | |
396 | if h < 4.0: return n1 + ((n2-n1) * (4.0 - h)) | |
397 | return n1 | |
398 | ||
399 | @staticmethod | |
400 | def HslToRgb(h, s, l): | |
401 | '''Convert the color from HSL coordinates to RGB. | |
402 | ||
403 | Parameters: | |
404 | :h: | |
405 | The Hue component value [0...1] | |
406 | :s: | |
407 | The Saturation component value [0...1] | |
408 | :l: | |
409 | The Lightness component value [0...1] | |
410 | ||
411 | Returns: | |
412 | The color as an (r, g, b) tuple in the range: | |
413 | r[0...1], | |
414 | g[0...1], | |
415 | b[0...1] | |
416 | ||
417 | >>> Color.HslToRgb(30.0, 1.0, 0.5) | |
418 | (1.0, 0.5, 0.0) | |
419 | ||
420 | ''' | |
421 | if s==0: return (l, l, l) # achromatic (gray) | |
422 | ||
423 | if l<0.5: n2 = l * (1.0 + s) | |
424 | else: n2 = l+s - (l*s) | |
425 | ||
426 | n1 = (2.0 * l) - n2 | |
427 | ||
428 | h /= 60.0 | |
429 | hueToRgb = Color._HueToRgb | |
430 | r = hueToRgb(n1, n2, h + 2) | |
431 | g = hueToRgb(n1, n2, h) | |
432 | b = hueToRgb(n1, n2, h - 2) | |
433 | ||
434 | return (r, g, b) | |
435 | ||
436 | @staticmethod | |
437 | def RgbToHsv(r, g, b): | |
438 | '''Convert the color from RGB coordinates to HSV. | |
439 | ||
440 | Parameters: | |
441 | :r: | |
442 | The Red component value [0...1] | |
443 | :g: | |
444 | The Green component value [0...1] | |
445 | :b: | |
446 | The Blue component value [0...1] | |
447 | ||
448 | Returns: | |
449 | The color as an (h, s, v) tuple in the range: | |
450 | h[0...360], | |
451 | s[0...1], | |
452 | v[0...1] | |
453 | ||
454 | >>> 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) | |
460 | if d==0: return (0.0, 0.0, v) | |
461 | s = d / v | |
462 | ||
463 | dr, dg, db = [(v - val) / d for val in (r, g, b)] | |
464 | ||
465 | if r==v: | |
466 | h = db - dg # between yellow & magenta | |
467 | elif g==v: | |
468 | h = 2.0 + dr - db # between cyan & yellow | |
469 | else: # b==v | |
470 | h = 4.0 + dg - dr # between magenta & cyan | |
471 | ||
472 | h = (h*60.0) % 360.0 | |
473 | return (h, s, v) | |
474 | ||
475 | @staticmethod | |
476 | def HsvToRgb(h, s, v): | |
477 | '''Convert the color from RGB coordinates to HSV. | |
478 | ||
479 | Parameters: | |
480 | :h: | |
481 | The Hus component value [0...1] | |
482 | :s: | |
483 | The Saturation component value [0...1] | |
484 | :v: | |
485 | The Value component [0...1] | |
486 | ||
487 | Returns: | |
488 | The color as an (r, g, b) tuple in the range: | |
489 | r[0...1], | |
490 | g[0...1], | |
491 | b[0...1] | |
492 | ||
493 | >>> Color.HslToRgb(30.0, 1.0, 0.5) | |
494 | (1.0, 0.5, 0.0) | |
495 | ||
496 | ''' | |
497 | if s==0: return (v, v, v) # achromatic (gray) | |
498 | ||
499 | h /= 60.0 | |
500 | h = h % 6.0 | |
501 | ||
502 | i = int(h) | |
503 | f = h - i | |
504 | if not(i&1): f = 1-f # if i is even | |
505 | ||
506 | m = v * (1.0 - s) | |
507 | n = v * (1.0 - (s * f)) | |
508 | ||
509 | if i==0: return (v, n, m) | |
510 | if i==1: return (n, v, m) | |
511 | if i==2: return (m, v, n) | |
512 | if i==3: return (m, n, v) | |
513 | if i==4: return (n, m, v) | |
514 | return (v, m, n) | |
515 | ||
516 | @staticmethod | |
517 | def RgbToYiq(r, g, b): | |
518 | '''Convert the color from RGB to YIQ. | |
519 | ||
520 | Parameters: | |
521 | :r: | |
522 | The Red component value [0...1] | |
523 | :g: | |
524 | The Green component value [0...1] | |
525 | :b: | |
526 | The Blue component value [0...1] | |
527 | ||
528 | Returns: | |
529 | The color as an (y, i, q) tuple in the range: | |
530 | y[0...1], | |
531 | i[0...1], | |
532 | q[0...1] | |
533 | ||
534 | >>> '(%g, %g, %g)' % Color.RgbToYiq(1, 0.5, 0) | |
535 | '(0.592263, 0.458874, -0.0499818)' | |
536 | ||
537 | ''' | |
538 | y = (r * 0.29895808) + (g * 0.58660979) + (b *0.11443213) | |
539 | i = (r * 0.59590296) - (g * 0.27405705) - (b *0.32184591) | |
540 | q = (r * 0.21133576) - (g * 0.52263517) + (b *0.31129940) | |
541 | return (y, i, q) | |
542 | ||
543 | @staticmethod | |
544 | def YiqToRgb(y, i, q): | |
545 | '''Convert the color from YIQ coordinates to RGB. | |
546 | ||
547 | Parameters: | |
548 | :y: | |
549 | Tte Y component value [0...1] | |
550 | :i: | |
551 | The I component value [0...1] | |
552 | :q: | |
553 | The Q component value [0...1] | |
554 | ||
555 | Returns: | |
556 | The color as an (r, g, b) tuple in the range: | |
557 | r[0...1], | |
558 | g[0...1], | |
559 | b[0...1] | |
560 | ||
561 | >>> '(%g, %g, %g)' % Color.YiqToRgb(0.592263, 0.458874, -0.0499818) | |
562 | '(1, 0.5, 5.442e-007)' | |
563 | ||
564 | ''' | |
565 | r = y + (i * 0.9562) + (q * 0.6210) | |
566 | g = y - (i * 0.2717) - (q * 0.6485) | |
567 | b = y - (i * 1.1053) + (q * 1.7020) | |
568 | return (r, g, b) | |
569 | ||
570 | @staticmethod | |
571 | def RgbToYuv(r, g, b): | |
572 | '''Convert the color from RGB coordinates to YUV. | |
573 | ||
574 | Parameters: | |
575 | :r: | |
576 | The Red component value [0...1] | |
577 | :g: | |
578 | The Green component value [0...1] | |
579 | :b: | |
580 | The Blue component value [0...1] | |
581 | ||
582 | Returns: | |
583 | The color as an (y, u, v) tuple in the range: | |
584 | y[0...1], | |
585 | u[-0.436...0.436], | |
586 | v[-0.615...0.615] | |
587 | ||
588 | >>> '(%g, %g, %g)' % Color.RgbToYuv(1, 0.5, 0) | |
589 | '(0.5925, -0.29156, 0.357505)' | |
590 | ||
591 | ''' | |
592 | y = (r * 0.29900) + (g * 0.58700) + (b * 0.11400) | |
593 | u = -(r * 0.14713) - (g * 0.28886) + (b * 0.43600) | |
594 | v = (r * 0.61500) - (g * 0.51499) - (b * 0.10001) | |
595 | return (y, u, v) | |
596 | ||
597 | @staticmethod | |
598 | def YuvToRgb(y, u, v): | |
599 | '''Convert the color from YUV coordinates to RGB. | |
600 | ||
601 | Parameters: | |
602 | :y: | |
603 | The Y component value [0...1] | |
604 | :u: | |
605 | The U component value [-0.436...0.436] | |
606 | :v: | |
607 | The V component value [-0.615...0.615] | |
608 | ||
609 | Returns: | |
610 | The color as an (r, g, b) tuple in the range: | |
611 | r[0...1], | |
612 | g[0...1], | |
613 | b[0...1] | |
614 | ||
615 | >>> '(%g, %g, %g)' % Color.YuvToRgb(0.5925, -0.2916, 0.3575) | |
616 | '(0.999989, 0.500015, -6.3276e-005)' | |
617 | ||
618 | ''' | |
619 | r = y + (v * 1.13983) | |
620 | g = y - (u * 0.39465) - (v * 0.58060) | |
621 | b = y + (u * 2.03211) | |
622 | return (r, g, b) | |
623 | ||
624 | @staticmethod | |
625 | def RgbToXyz(r, g, b): | |
626 | '''Convert the color from sRGB to CIE XYZ. | |
627 | ||
628 | The methods assumes that the RGB coordinates are given in the sRGB | |
629 | colorspace (D65). | |
630 | ||
631 | .. note:: | |
632 | ||
633 | Compensation for the sRGB gamma correction is applied before converting. | |
634 | ||
635 | Parameters: | |
636 | :r: | |
637 | The Red component value [0...1] | |
638 | :g: | |
639 | The Green component value [0...1] | |
640 | :b: | |
641 | The Blue component value [0...1] | |
642 | ||
643 | Returns: | |
644 | The color as an (x, y, z) tuple in the range: | |
645 | x[0...1], | |
646 | y[0...1], | |
647 | z[0...1] | |
648 | ||
649 | >>> '(%g, %g, %g)' % Color.RgbToXyz(1, 0.5, 0) | |
650 | '(0.488941, 0.365682, 0.0448137)' | |
651 | ||
652 | ''' | |
653 | 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 | ||
655 | x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805) | |
656 | y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722) | |
657 | z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505) | |
658 | return (x, y, z) | |
659 | ||
660 | @staticmethod | |
661 | def XyzToRgb(x, y, z): | |
662 | '''Convert the color from CIE XYZ coordinates to sRGB. | |
663 | ||
664 | .. note:: | |
665 | ||
666 | Compensation for sRGB gamma correction is applied before converting. | |
667 | ||
668 | Parameters: | |
669 | :x: | |
670 | The X component value [0...1] | |
671 | :y: | |
672 | The Y component value [0...1] | |
673 | :z: | |
674 | The Z component value [0...1] | |
675 | ||
676 | Returns: | |
677 | The color as an (r, g, b) tuple in the range: | |
678 | r[0...1], | |
679 | g[0...1], | |
680 | b[0...1] | |
681 | ||
682 | >>> '(%g, %g, %g)' % Color.XyzToRgb(0.488941, 0.365682, 0.0448137) | |
683 | '(1, 0.5, 6.81883e-008)' | |
684 | ||
685 | ''' | |
686 | r = (x * 3.2406255) - (y * 1.5372080) - (z * 0.4986286) | |
687 | g = -(x * 0.9689307) + (y * 1.8757561) + (z * 0.0415175) | |
688 | b = (x * 0.0557101) - (y * 0.2040211) + (z * 1.0569959) | |
689 | return tuple((((v <= _srgbGammaCorrInv) and [v * 12.92] or [(1.055 * (v ** (1/2.4))) - 0.055])[0] for v in (r, g, b))) | |
690 | ||
691 | @staticmethod | |
692 | def XyzToLab(x, y, z, wref=_DEFAULT_WREF): | |
693 | '''Convert the color from CIE XYZ to CIE L*a*b*. | |
694 | ||
695 | Parameters: | |
696 | :x: | |
697 | The X component value [0...1] | |
698 | :y: | |
699 | The Y component value [0...1] | |
700 | :z: | |
701 | The Z component value [0...1] | |
702 | :wref: | |
703 | The whitepoint reference, default is 2° D65. | |
704 | ||
705 | Returns: | |
706 | The color as an (L, a, b) tuple in the range: | |
707 | L[0...100], | |
708 | a[-1...1], | |
709 | b[-1...1] | |
710 | ||
711 | >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137) | |
712 | '(66.9518, 0.43084, 0.739692)' | |
713 | ||
714 | >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137, Color.WHITE_REFERENCE['std_D50']) | |
715 | '(66.9518, 0.411663, 0.67282)' | |
716 | ||
717 | ''' | |
718 | # White point correction | |
719 | x /= wref[0] | |
720 | y /= wref[1] | |
721 | z /= wref[2] | |
722 | ||
723 | # Nonlinear distortion and linear transformation | |
724 | x, y, z = [((v > 0.008856) and [v**_oneThird] or [(7.787 * v) + _sixteenHundredsixteenth])[0] for v in (x, y, z)] | |
725 | ||
726 | # Vector scaling | |
727 | l = (116 * y) - 16 | |
728 | a = 5.0 * (x - y) | |
729 | b = 2.0 * (y - z) | |
730 | ||
731 | return (l, a, b) | |
732 | ||
733 | @staticmethod | |
734 | def LabToXyz(l, a, b, wref=_DEFAULT_WREF): | |
735 | '''Convert the color from CIE L*a*b* to CIE 1931 XYZ. | |
736 | ||
737 | Parameters: | |
738 | :l: | |
739 | The L component [0...100] | |
740 | :a: | |
741 | The a component [-1...1] | |
742 | :b: | |
743 | The a component [-1...1] | |
744 | :wref: | |
745 | The whitepoint reference, default is 2° D65. | |
746 | ||
747 | Returns: | |
748 | The color as an (x, y, z) tuple in the range: | |
749 | x[0...q], | |
750 | y[0...1], | |
751 | z[0...1] | |
752 | ||
753 | >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.43084, 0.739692) | |
754 | '(0.488941, 0.365682, 0.0448137)' | |
755 | ||
756 | >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.411663, 0.67282, Color.WHITE_REFERENCE['std_D50']) | |
757 | '(0.488941, 0.365682, 0.0448138)' | |
758 | ||
759 | ''' | |
760 | y = (l + 16) / 116 | |
761 | x = (a / 5.0) + y | |
762 | z = y - (b / 2.0) | |
763 | return tuple((((v > 0.206893) and [v**3] or [(v - _sixteenHundredsixteenth) / 7.787])[0] * w for v, w in zip((x, y, z), wref))) | |
764 | ||
765 | @staticmethod | |
766 | def CmykToCmy(c, m, y, k): | |
767 | '''Convert the color from CMYK coordinates to CMY. | |
768 | ||
769 | Parameters: | |
770 | :c: | |
771 | The Cyan component value [0...1] | |
772 | :m: | |
773 | The Magenta component value [0...1] | |
774 | :y: | |
775 | The Yellow component value [0...1] | |
776 | :k: | |
777 | The Black component value [0...1] | |
778 | ||
779 | Returns: | |
780 | The color as an (c, m, y) tuple in the range: | |
781 | c[0...1], | |
782 | m[0...1], | |
783 | y[0...1] | |
784 | ||
785 | >>> '(%g, %g, %g)' % Color.CmykToCmy(1, 0.32, 0, 0.5) | |
786 | '(1, 0.66, 0.5)' | |
787 | ||
788 | ''' | |
789 | mk = 1-k | |
790 | return ((c*mk + k), (m*mk + k), (y*mk + k)) | |
791 | ||
792 | @staticmethod | |
793 | def CmyToCmyk(c, m, y): | |
794 | '''Convert the color from CMY coordinates to CMYK. | |
795 | ||
796 | Parameters: | |
797 | :c: | |
798 | The Cyan component value [0...1] | |
799 | :m: | |
800 | The Magenta component value [0...1] | |
801 | :y: | |
802 | The Yellow component value [0...1] | |
803 | ||
804 | Returns: | |
805 | The color as an (c, m, y, k) tuple in the range: | |
806 | c[0...1], | |
807 | m[0...1], | |
808 | y[0...1], | |
809 | k[0...1] | |
810 | ||
811 | >>> '(%g, %g, %g, %g)' % Color.CmyToCmyk(1, 0.66, 0.5) | |
812 | '(1, 0.32, 0, 0.5)' | |
813 | ||
814 | ''' | |
815 | k = min(c, m, y) | |
816 | if k==1.0: return (0.0, 0.0, 0.0, 1.0) | |
817 | mk = 1-k | |
818 | return ((c-k) / mk, (m-k) / mk, (y-k) / mk, k) | |
819 | ||
820 | @staticmethod | |
821 | def RgbToCmy(r, g, b): | |
822 | '''Convert the color from RGB coordinates to CMY. | |
823 | ||
824 | Parameters: | |
825 | :r: | |
826 | The Red component value [0...1] | |
827 | :g: | |
828 | The Green component value [0...1] | |
829 | :b: | |
830 | The Blue component value [0...1] | |
831 | ||
832 | Returns: | |
833 | The color as an (c, m, y) tuple in the range: | |
834 | c[0...1], | |
835 | m[0...1], | |
836 | y[0...1] | |
837 | ||
838 | >>> Color.RgbToCmy(1, 0.5, 0) | |
839 | (0, 0.5, 1) | |
840 | ||
841 | ''' | |
842 | return (1-r, 1-g, 1-b) | |
843 | ||
844 | @staticmethod | |
845 | def CmyToRgb(c, m, y): | |
846 | '''Convert the color from CMY coordinates to RGB. | |
847 | ||
848 | Parameters: | |
849 | :c: | |
850 | The Cyan component value [0...1] | |
851 | :m: | |
852 | The Magenta component value [0...1] | |
853 | :y: | |
854 | The Yellow component value [0...1] | |
855 | ||
856 | Returns: | |
857 | The color as an (r, g, b) tuple in the range: | |
858 | r[0...1], | |
859 | g[0...1], | |
860 | b[0...1] | |
861 | ||
862 | >>> Color.CmyToRgb(0, 0.5, 1) | |
863 | (1, 0.5, 0) | |
864 | ||
865 | ''' | |
866 | return (1-c, 1-m, 1-y) | |
867 | ||
868 | @staticmethod | |
869 | def RgbToHtml(r, g, b): | |
870 | '''Convert the color from (r, g, b) to #RRGGBB. | |
871 | ||
872 | Parameters: | |
873 | :r: | |
874 | The Red component value [0...1] | |
875 | :g: | |
876 | The Green component value [0...1] | |
877 | :b: | |
878 | 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 | Returns: | |
898 | The color as an (r, g, b) tuple in the range: | |
899 | r[0...1], | |
900 | g[0...1], | |
901 | b[0...1] | |
902 | ||
903 | Throws: | |
904 | :ValueError: | |
905 | If html is neither a known color name or a hexadecimal RGB | |
906 | representation. | |
907 | ||
908 | >>> '(%g, %g, %g)' % Color.HtmlToRgb('#ff8000') | |
909 | '(1, 0.501961, 0)' | |
910 | >>> '(%g, %g, %g)' % Color.HtmlToRgb('ff8000') | |
911 | '(1, 0.501961, 0)' | |
912 | >>> '(%g, %g, %g)' % Color.HtmlToRgb('#f60') | |
913 | '(1, 0.4, 0)' | |
914 | >>> '(%g, %g, %g)' % Color.HtmlToRgb('f60') | |
915 | '(1, 0.4, 0)' | |
916 | >>> '(%g, %g, %g)' % Color.HtmlToRgb('lemonchiffon') | |
917 | '(1, 0.980392, 0.803922)' | |
918 | ||
919 | ''' | |
920 | html = html.strip().lower() | |
921 | if html[0]=='#': | |
922 | html = html[1:] | |
923 | elif Color.NAMED_COLOR.has_key(html): | |
924 | html = Color.NAMED_COLOR[html][1:] | |
925 | ||
926 | if len(html)==6: | |
927 | rgb = html[:2], html[2:4], html[4:] | |
928 | elif len(html)==3: | |
929 | rgb = ['%c%c' % (v,v) for v in html] | |
930 | else: | |
931 | raise ValueError, 'input #%s is not in #RRGGBB format' % html | |
932 | ||
933 | return tuple(((int(n, 16) / 255.0) for n in rgb)) | |
934 | ||
935 | @staticmethod | |
936 | def RgbToPil(r, g, b): | |
937 | '''Convert the color from RGB to a PIL-compatible integer. | |
938 | ||
939 | Parameters: | |
940 | :r: | |
941 | The Red component value [0...1] | |
942 | :g: | |
943 | The Green component value [0...1] | |
944 | :b: | |
945 | The Blue component value [0...1] | |
946 | ||
947 | Returns: | |
948 | A PIL compatible integer (0xBBGGRR). | |
949 | ||
950 | >>> '0x%06x' % Color.RgbToPil(1, 0.5, 0) | |
951 | '0x0080ff' | |
952 | ||
953 | ''' | |
954 | r, g, b = [min(int(round(v*255)), 255) for v in (r, g, b)] | |
955 | return (b << 16) + (g << 8) + r | |
956 | ||
957 | @staticmethod | |
958 | def PilToRgb(pil): | |
959 | '''Convert the color from a PIL-compatible integer to RGB. | |
960 | ||
961 | Parameters: | |
962 | pil: a PIL compatible color representation (0xBBGGRR) | |
963 | Returns: | |
964 | The color as an (r, g, b) tuple in the range: | |
965 | the range: | |
966 | r: [0...1] | |
967 | g: [0...1] | |
968 | b: [0...1] | |
969 | ||
970 | >>> '(%g, %g, %g)' % Color.PilToRgb(0x0080ff) | |
971 | '(1, 0.501961, 0)' | |
972 | ||
973 | ''' | |
974 | r = 0xff & pil | |
975 | g = 0xff & (pil >> 8) | |
976 | b = 0xff & (pil >> 16) | |
977 | return tuple((v / 255.0 for v in (r, g, b))) | |
978 | ||
979 | @staticmethod | |
980 | def _WebSafeComponent(c, alt=False): | |
981 | '''Convert a color component to its web safe equivalent. | |
982 | ||
983 | Parameters: | |
984 | :c: | |
985 | The component value [0...1] | |
986 | :alt: | |
987 | If True, return the alternative value instead of the nearest one. | |
988 | ||
989 | Returns: | |
990 | The web safe equivalent of the component value. | |
991 | ||
992 | ''' | |
993 | # This sucks, but floating point between 0 and 1 is quite fuzzy... | |
994 | # So we just change the scale a while to make the equality tests | |
995 | # work, otherwise it gets wrong at some decimal far to the right. | |
996 | sc = c * 100.0 | |
997 | ||
998 | # If the color is already safe, return it straight away | |
999 | d = sc % 20 | |
1000 | if d==0: return c | |
1001 | ||
1002 | # Get the lower and upper safe values | |
1003 | l = sc - d | |
1004 | u = l + 20 | |
1005 | ||
1006 | # Return the 'closest' value according to the alt flag | |
1007 | if alt: | |
1008 | if (sc-l) >= (u-sc): return l/100.0 | |
1009 | else: return u/100.0 | |
1010 | else: | |
1011 | if (sc-l) >= (u-sc): return u/100.0 | |
1012 | else: return l/100.0 | |
1013 | ||
1014 | @staticmethod | |
1015 | def RgbToWebSafe(r, g, b, alt=False): | |
1016 | '''Convert the color from RGB to 'web safe' RGB | |
1017 | ||
1018 | Parameters: | |
1019 | :r: | |
1020 | The Red component value [0...1] | |
1021 | :g: | |
1022 | The Green component value [0...1] | |
1023 | :b: | |
1024 | The Blue component value [0...1] | |
1025 | :alt: | |
1026 | If True, use the alternative color instead of the nearest one. | |
1027 | Can be used for dithering. | |
1028 | ||
1029 | Returns: | |
1030 | The color as an (r, g, b) tuple in the range: | |
1031 | the range: | |
1032 | r[0...1], | |
1033 | g[0...1], | |
1034 | b[0...1] | |
1035 | ||
1036 | >>> '(%g, %g, %g)' % Color.RgbToWebSafe(1, 0.55, 0.0) | |
1037 | '(1, 0.6, 0)' | |
1038 | ||
1039 | ''' | |
1040 | webSafeComponent = Color._WebSafeComponent | |
1041 | return tuple((webSafeComponent(v, alt) for v in (r, g, b))) | |
1042 | ||
1043 | @staticmethod | |
1044 | def RgbToGreyscale(r, g, b): | |
1045 | '''Convert the color from RGB to its greyscale equivalent | |
1046 | ||
1047 | Parameters: | |
1048 | :r: | |
1049 | The Red component value [0...1] | |
1050 | :g: | |
1051 | The Green component value [0...1] | |
1052 | :b: | |
1053 | The Blue component value [0...1] | |
1054 | ||
1055 | Returns: | |
1056 | The color as an (r, g, b) tuple in the range: | |
1057 | the range: | |
1058 | r[0...1], | |
1059 | g[0...1], | |
1060 | b[0...1] | |
1061 | ||
1062 | >>> '(%g, %g, %g)' % Color.RgbToGreyscale(1, 0.8, 0) | |
1063 | '(0.6, 0.6, 0.6)' | |
1064 | ||
1065 | ''' | |
1066 | v = (r + g + b) / 3.0 | |
1067 | return (v, v, v) | |
1068 | ||
1069 | @staticmethod | |
1070 | def RgbToRyb(hue): | |
1071 | '''Maps a hue on the RGB color wheel to Itten's RYB wheel. | |
1072 | ||
1073 | Parameters: | |
1074 | :hue: | |
1075 | The hue on the RGB color wheel [0...360] | |
1076 | ||
1077 | Returns: | |
1078 | An approximation of the corresponding hue on Itten's RYB wheel. | |
1079 | ||
1080 | >>> Color.RgbToRyb(15) | |
1081 | 26 | |
1082 | ||
1083 | ''' | |
1084 | d = hue % 15 | |
1085 | i = int(hue / 15) | |
1086 | x0 = _RybWheel[i] | |
1087 | x1 = _RybWheel[i+1] | |
1088 | return x0 + (x1-x0) * d / 15 | |
1089 | ||
1090 | @staticmethod | |
1091 | def RybToRgb(hue): | |
1092 | '''Maps a hue on Itten's RYB color wheel to the standard RGB wheel. | |
1093 | ||
1094 | Parameters: | |
1095 | :hue: | |
1096 | The hue on Itten's RYB color wheel [0...360] | |
1097 | ||
1098 | Returns: | |
1099 | An approximation of the corresponding hue on the standard RGB wheel. | |
1100 | ||
1101 | >>> Color.RybToRgb(15) | |
1102 | 8 | |
1103 | ||
1104 | ''' | |
1105 | d = hue % 15 | |
1106 | i = int(hue / 15) | |
1107 | x0 = _RgbWheel[i] | |
1108 | x1 = _RgbWheel[i+1] | |
1109 | return x0 + (x1-x0) * d / 15 | |
1110 | ||
1111 | @staticmethod | |
1112 | def NewFromRgb(r, g, b, alpha=1.0, wref=_DEFAULT_WREF): | |
1113 | '''Create a new instance based on the specifed RGB values. | |
1114 | ||
1115 | Parameters: | |
1116 | :r: | |
1117 | The Red component value [0...1] | |
1118 | :g: | |
1119 | The Green component value [0...1] | |
1120 | :b: | |
1121 | The Blue component value [0...1] | |
1122 | :alpha: | |
1123 | The color transparency [0...1], default is opaque | |
1124 | :wref: | |
1125 | The whitepoint reference, default is 2° D65. | |
1126 | ||
1127 | Returns: | |
1128 | A grapefruit.Color instance. | |
1129 | ||
1130 | >>> Color.NewFromRgb(1.0, 0.5, 0.0) | |
1131 | (1.0, 0.5, 0.0, 1.0) | |
1132 | >>> Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) | |
1133 | (1.0, 0.5, 0.0, 0.5) | |
1134 | ||
1135 | ''' | |
1136 | return Color((r, g, b), 'rgb', alpha, wref) | |
1137 | ||
1138 | @staticmethod | |
1139 | def NewFromHsl(h, s, l, alpha=1.0, wref=_DEFAULT_WREF): | |
1140 | '''Create a new instance based on the specifed HSL values. | |
1141 | ||
1142 | Parameters: | |
1143 | :h: | |
1144 | The Hue component value [0...1] | |
1145 | :s: | |
1146 | The Saturation component value [0...1] | |
1147 | :l: | |
1148 | The Lightness component value [0...1] | |
1149 | :alpha: | |
1150 | The color transparency [0...1], default is opaque | |
1151 | :wref: | |
1152 | The whitepoint reference, default is 2° D65. | |
1153 | ||
1154 | Returns: | |
1155 | A grapefruit.Color instance. | |
1156 | ||
1157 | >>> Color.NewFromHsl(30, 1, 0.5) | |
1158 | (1.0, 0.5, 0.0, 1.0) | |
1159 | >>> Color.NewFromHsl(30, 1, 0.5, 0.5) | |
1160 | (1.0, 0.5, 0.0, 0.5) | |
1161 | ||
1162 | ''' | |
1163 | return Color((h, s, l), 'hsl', alpha, wref) | |
1164 | ||
1165 | @staticmethod | |
1166 | def NewFromHsv(h, s, v, alpha=1.0, wref=_DEFAULT_WREF): | |
1167 | '''Create a new instance based on the specifed HSV values. | |
1168 | ||
1169 | Parameters: | |
1170 | :h: | |
1171 | The Hus component value [0...1] | |
1172 | :s: | |
1173 | The Saturation component value [0...1] | |
1174 | :v: | |
1175 | The Value component [0...1] | |
1176 | :alpha: | |
1177 | The color transparency [0...1], default is opaque | |
1178 | :wref: | |
1179 | The whitepoint reference, default is 2° D65. | |
1180 | ||
1181 | Returns: | |
1182 | A grapefruit.Color instance. | |
1183 | ||
1184 | >>> Color.NewFromHsv(30, 1, 1) | |
1185 | (1.0, 0.5, 0.0, 1.0) | |
1186 | >>> Color.NewFromHsv(30, 1, 1, 0.5) | |
1187 | (1.0, 0.5, 0.0, 0.5) | |
1188 | ||
1189 | ''' | |
1190 | h2, s, l = Color.RgbToHsl(*Color.HsvToRgb(h, s, v)) | |
1191 | return Color((h, s, l), 'hsl', alpha, wref) | |
1192 | ||
1193 | @staticmethod | |
1194 | def NewFromYiq(y, i, q, alpha=1.0, wref=_DEFAULT_WREF): | |
1195 | '''Create a new instance based on the specifed YIQ values. | |
1196 | ||
1197 | Parameters: | |
1198 | :y: | |
1199 | The Y component value [0...1] | |
1200 | :i: | |
1201 | The I component value [0...1] | |
1202 | :q: | |
1203 | The Q component value [0...1] | |
1204 | :alpha: | |
1205 | The color transparency [0...1], default is opaque | |
1206 | :wref: | |
1207 | The whitepoint reference, default is 2° D65. | |
1208 | ||
1209 | Returns: | |
1210 | A grapefruit.Color instance. | |
1211 | ||
1212 | >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05)) | |
1213 | '(0.999902, 0.499955, -6.6905e-005, 1)' | |
1214 | >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05, 0.5)) | |
1215 | '(0.999902, 0.499955, -6.6905e-005, 0.5)' | |
1216 | ||
1217 | ''' | |
1218 | return Color(Color.YiqToRgb(y, i, q), 'rgb', alpha, wref) | |
1219 | ||
1220 | @staticmethod | |
1221 | def NewFromYuv(y, u, v, alpha=1.0, wref=_DEFAULT_WREF): | |
1222 | '''Create a new instance based on the specifed YUV values. | |
1223 | ||
1224 | Parameters: | |
1225 | :y: | |
1226 | The Y component value [0...1] | |
1227 | :u: | |
1228 | The U component value [-0.436...0.436] | |
1229 | :v: | |
1230 | The V component value [-0.615...0.615] | |
1231 | :alpha: | |
1232 | The color transparency [0...1], default is opaque | |
1233 | :wref: | |
1234 | The whitepoint reference, default is 2° D65. | |
1235 | ||
1236 | Returns: | |
1237 | A grapefruit.Color instance. | |
1238 | ||
1239 | >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575)) | |
1240 | '(0.999989, 0.500015, -6.3276e-005, 1)' | |
1241 | >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5)) | |
1242 | '(0.999989, 0.500015, -6.3276e-005, 0.5)' | |
1243 | ||
1244 | ''' | |
1245 | return Color(Color.YuvToRgb(y, u, v), 'rgb', alpha, wref) | |
1246 | ||
1247 | @staticmethod | |
1248 | def NewFromXyz(x, y, z, alpha=1.0, wref=_DEFAULT_WREF): | |
1249 | '''Create a new instance based on the specifed CIE-XYZ values. | |
1250 | ||
1251 | Parameters: | |
1252 | :x: | |
1253 | The Red component value [0...1] | |
1254 | :y: | |
1255 | The Green component value [0...1] | |
1256 | :z: | |
1257 | The Blue component value [0...1] | |
1258 | :alpha: | |
1259 | The color transparency [0...1], default is opaque | |
1260 | :wref: | |
1261 | The whitepoint reference, default is 2° D65. | |
1262 | ||
1263 | Returns: | |
1264 | A grapefruit.Color instance. | |
1265 | ||
1266 | >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137)) | |
1267 | '(1, 0.5, 6.81883e-008, 1)' | |
1268 | >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5)) | |
1269 | '(1, 0.5, 6.81883e-008, 0.5)' | |
1270 | ||
1271 | ''' | |
1272 | return Color(Color.XyzToRgb(x, y, z), 'rgb', alpha, wref) | |
1273 | ||
1274 | @staticmethod | |
1275 | def NewFromLab(l, a, b, alpha=1.0, wref=_DEFAULT_WREF): | |
1276 | '''Create a new instance based on the specifed CIE-LAB values. | |
1277 | ||
1278 | Parameters: | |
1279 | :l: | |
1280 | The L component [0...100] | |
1281 | :a: | |
1282 | The a component [-1...1] | |
1283 | :b: | |
1284 | The a component [-1...1] | |
1285 | :alpha: | |
1286 | The color transparency [0...1], default is opaque | |
1287 | :wref: | |
1288 | The whitepoint reference, default is 2° D65. | |
1289 | ||
1290 | Returns: | |
1291 | A grapefruit.Color instance. | |
1292 | ||
1293 | >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692)) | |
1294 | '(1, 0.5, 1.09491e-008, 1)' | |
1295 | >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=Color.WHITE_REFERENCE['std_D50'])) | |
1296 | '(1.01238, 0.492011, -0.14311, 1)' | |
1297 | >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5)) | |
1298 | '(1, 0.5, 1.09491e-008, 0.5)' | |
1299 | >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, Color.WHITE_REFERENCE['std_D50'])) | |
1300 | '(1.01238, 0.492011, -0.14311, 0.5)' | |
1301 | ||
1302 | ''' | |
1303 | return Color(Color.XyzToRgb(*Color.LabToXyz(l, a, b, wref)), 'rgb', alpha, wref) | |
1304 | ||
1305 | @staticmethod | |
1306 | def NewFromCmy(c, m, y, alpha=1.0, wref=_DEFAULT_WREF): | |
1307 | '''Create a new instance based on the specifed CMY values. | |
1308 | ||
1309 | Parameters: | |
1310 | :c: | |
1311 | The Cyan component value [0...1] | |
1312 | :m: | |
1313 | The Magenta component value [0...1] | |
1314 | :y: | |
1315 | The Yellow component value [0...1] | |
1316 | :alpha: | |
1317 | The color transparency [0...1], default is opaque | |
1318 | :wref: | |
1319 | The whitepoint reference, default is 2° D65. | |
1320 | ||
1321 | Returns: | |
1322 | A grapefruit.Color instance. | |
1323 | ||
1324 | >>> Color.NewFromCmy(0, 0.5, 1) | |
1325 | (1, 0.5, 0, 1.0) | |
1326 | >>> Color.NewFromCmy(0, 0.5, 1, 0.5) | |
1327 | (1, 0.5, 0, 0.5) | |
1328 | ||
1329 | ''' | |
1330 | return Color(Color.CmyToRgb(c, m, y), 'rgb', alpha, wref) | |
1331 | ||
1332 | @staticmethod | |
1333 | def NewFromCmyk(c, m, y, k, alpha=1.0, wref=_DEFAULT_WREF): | |
1334 | '''Create a new instance based on the specifed CMYK values. | |
1335 | ||
1336 | Parameters: | |
1337 | :c: | |
1338 | The Cyan component value [0...1] | |
1339 | :m: | |
1340 | The Magenta component value [0...1] | |
1341 | :y: | |
1342 | The Yellow component value [0...1] | |
1343 | :k: | |
1344 | The Black component value [0...1] | |
1345 | :alpha: | |
1346 | The color transparency [0...1], default is opaque | |
1347 | :wref: | |
1348 | The whitepoint reference, default is 2° D65. | |
1349 | ||
1350 | Returns: | |
1351 | A grapefruit.Color instance. | |
1352 | ||
1353 | >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5)) | |
1354 | '(0, 0.34, 0.5, 1)' | |
1355 | >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5)) | |
1356 | '(0, 0.34, 0.5, 0.5)' | |
1357 | ||
1358 | ''' | |
1359 | return Color(Color.CmyToRgb(*Color.CmykToCmy(c, m, y, k)), 'rgb', alpha, wref) | |
1360 | ||
1361 | @staticmethod | |
1362 | def NewFromHtml(html, alpha=1.0, wref=_DEFAULT_WREF): | |
1363 | '''Create a new instance based on the specifed HTML color definition. | |
1364 | ||
1365 | Parameters: | |
1366 | :html: | |
1367 | The HTML definition of the color (#RRGGBB or #RGB or a color name). | |
1368 | :alpha: | |
1369 | The color transparency [0...1], default is opaque. | |
1370 | :wref: | |
1371 | The whitepoint reference, default is 2° D65. | |
1372 | ||
1373 | Returns: | |
1374 | A grapefruit.Color instance. | |
1375 | ||
1376 | >>> str(Color.NewFromHtml('#ff8000')) | |
1377 | '(1, 0.501961, 0, 1)' | |
1378 | >>> str(Color.NewFromHtml('ff8000')) | |
1379 | '(1, 0.501961, 0, 1)' | |
1380 | >>> str(Color.NewFromHtml('#f60')) | |
1381 | '(1, 0.4, 0, 1)' | |
1382 | >>> str(Color.NewFromHtml('f60')) | |
1383 | '(1, 0.4, 0, 1)' | |
1384 | >>> str(Color.NewFromHtml('lemonchiffon')) | |
1385 | '(1, 0.980392, 0.803922, 1)' | |
1386 | >>> str(Color.NewFromHtml('#ff8000', 0.5)) | |
1387 | '(1, 0.501961, 0, 0.5)' | |
1388 | ||
1389 | ''' | |
1390 | return Color(Color.HtmlToRgb(html), 'rgb', alpha, wref) | |
1391 | ||
1392 | @staticmethod | |
1393 | def NewFromPil(pil, alpha=1.0, wref=_DEFAULT_WREF): | |
1394 | '''Create a new instance based on the specifed PIL color. | |
1395 | ||
1396 | Parameters: | |
1397 | :pil: | |
1398 | A PIL compatible color representation (0xBBGGRR) | |
1399 | :alpha: | |
1400 | The color transparency [0...1], default is opaque | |
1401 | :wref: | |
1402 | The whitepoint reference, default is 2° D65. | |
1403 | ||
1404 | Returns: | |
1405 | A grapefruit.Color instance. | |
1406 | ||
1407 | >>> str(Color.NewFromPil(0x0080ff)) | |
1408 | '(1, 0.501961, 0, 1)' | |
1409 | >>> str(Color.NewFromPil(0x0080ff, 0.5)) | |
1410 | '(1, 0.501961, 0, 0.5)' | |
1411 | ||
1412 | ''' | |
1413 | return Color(Color.PilToRgb(pil), 'rgb', alpha, wref) | |
1414 | ||
1415 | def __GetAlpha(self): | |
1416 | return self.__a | |
1417 | alpha = property(fget=__GetAlpha, doc='The transparency of this color. 0.0 is transparent and 1.0 is fully opaque.') | |
1418 | ||
1419 | def __GetWRef(self): | |
1420 | return self.__wref | |
1421 | whiteRef = property(fget=__GetWRef, doc='the white reference point of this color.') | |
1422 | ||
1423 | def __GetRGB(self): | |
1424 | return self.__rgb | |
1425 | rgb = property(fget=__GetRGB, doc='The RGB values of this Color.') | |
1426 | ||
1427 | def __GetHue(self): | |
1428 | return self.__hsl[0] | |
1429 | hue = property(fget=__GetHue, doc='The hue of this color.') | |
1430 | ||
1431 | def __GetHSL(self): | |
1432 | return self.__hsl | |
1433 | hsl = property(fget=__GetHSL, doc='The HSL values of this Color.') | |
1434 | ||
1435 | def __GetHSV(self): | |
1436 | h, s, v = Color.RgbToHsv(*self.__rgb) | |
1437 | return (self.__hsl[0], s, v) | |
1438 | hsv = property(fget=__GetHSV, doc='The HSV values of this Color.') | |
1439 | ||
1440 | def __GetYIQ(self): | |
1441 | return Color.RgbToYiq(*self.__rgb) | |
1442 | yiq = property(fget=__GetYIQ, doc='The YIQ values of this Color.') | |
1443 | ||
1444 | def __GetYUV(self): | |
1445 | return Color.RgbToYuv(*self.__rgb) | |
1446 | yuv = property(fget=__GetYUV, doc='The YUV values of this Color.') | |
1447 | ||
1448 | def __GetXYZ(self): | |
1449 | return Color.RgbToXyz(*self.__rgb) | |
1450 | xyz = property(fget=__GetXYZ, doc='The CIE-XYZ values of this Color.') | |
1451 | ||
1452 | def __GetLAB(self): | |
1453 | return Color.XyzToLab(wref=self.__wref, *Color.RgbToXyz(*self.__rgb)) | |
1454 | lab = property(fget=__GetLAB, doc='The CIE-LAB values of this Color.') | |
1455 | ||
1456 | def __GetCMY(self): | |
1457 | return Color.RgbToCmy(*self.__rgb) | |
1458 | cmy = property(fget=__GetCMY, doc='The CMY values of this Color.') | |
1459 | ||
1460 | def __GetCMYK(self): | |
1461 | return Color.CmyToCmyk(*Color.RgbToCmy(*self.__rgb)) | |
1462 | cmyk = property(fget=__GetCMYK, doc='The CMYK values of this Color.') | |
1463 | ||
1464 | def __GetHTML(self): | |
1465 | return Color.RgbToHtml(*self.__rgb) | |
1466 | html = property(fget=__GetHTML, doc='This Color as an HTML color definition.') | |
1467 | ||
1468 | def __GetPIL(self): | |
1469 | return Color.RgbToPil(*self.__rgb) | |
1470 | pil = property(fget=__GetPIL, doc='This Color as a PIL compatible value.') | |
1471 | ||
1472 | def __GetwebSafe(self): | |
1473 | return Color.RgbToWebSafe(*self.__rgb) | |
1474 | webSafe = property(fget=__GetwebSafe, doc='The web safe color nearest to this one (RGB).') | |
1475 | ||
1476 | def __GetGreyscale(self): | |
1477 | return Color.RgbToGreyscale(*self.rgb) | |
1478 | greyscale = property(fget=__GetGreyscale, doc='The greyscale equivalent to this color (RGB).') | |
1479 | ||
1480 | def ColorWithAlpha(self, alpha): | |
1481 | '''Create a new instance based on this one with a new alpha value. | |
1482 | ||
1483 | Parameters: | |
1484 | :alpha: | |
1485 | The transparency of the new color [0...1]. | |
1486 | ||
1487 | Returns: | |
1488 | A grapefruit.Color instance. | |
1489 | ||
1490 | >>> Color.NewFromRgb(1.0, 0.5, 0.0, 1.0).ColorWithAlpha(0.5) | |
1491 | (1.0, 0.5, 0.0, 0.5) | |
1492 | ||
1493 | ''' | |
1494 | return Color(self.__rgb, 'rgb', alpha, self.__wref) | |
1495 | ||
1496 | def ColorWithWhiteRef(self, wref, labAsRef=False): | |
1497 | '''Create a new instance based on this one with a new white reference. | |
1498 | ||
1499 | Parameters: | |
1500 | :wref: | |
1501 | The whitepoint reference. | |
1502 | :labAsRef: | |
1503 | If True, the L*a*b* values of the current instance are used as reference | |
1504 | for the new color; otherwise, the RGB values are used as reference. | |
1505 | ||
1506 | Returns: | |
1507 | A grapefruit.Color instance. | |
1508 | ||
1509 | ||
1510 | >>> c = Color.NewFromRgb(1.0, 0.5, 0.0, 1.0, Color.WHITE_REFERENCE['std_D65']) | |
1511 | ||
1512 | >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50']) | |
1513 | >>> c2.rgb | |
1514 | (1.0, 0.5, 0.0) | |
1515 | >>> '(%g, %g, %g)' % c2.whiteRef | |
1516 | '(0.96721, 1, 0.81428)' | |
1517 | ||
1518 | >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50'], labAsRef=True) | |
1519 | >>> '(%g, %g, %g)' % c2.rgb | |
1520 | '(1.01463, 0.490339, -0.148131)' | |
1521 | >>> '(%g, %g, %g)' % c2.whiteRef | |
1522 | '(0.96721, 1, 0.81428)' | |
1523 | >>> '(%g, %g, %g)' % c.lab | |
1524 | '(66.9518, 0.43084, 0.739692)' | |
1525 | >>> '(%g, %g, %g)' % c2.lab | |
1526 | '(66.9518, 0.43084, 0.739693)' | |
1527 | ||
1528 | ''' | |
1529 | if labAsRef: | |
1530 | l, a, b = self.__GetLAB() | |
1531 | return Color.NewFromLab(l, a, b, self.__a, wref) | |
1532 | else: | |
1533 | return Color(self.__rgb, 'rgb', self.__a, wref) | |
1534 | ||
1535 | def ColorWithHue(self, hue): | |
1536 | '''Create a new instance based on this one with a new hue. | |
1537 | ||
1538 | Parameters: | |
1539 | :hue: | |
1540 | The hue of the new color [0...360]. | |
1541 | ||
1542 | Returns: | |
1543 | A grapefruit.Color instance. | |
1544 | ||
1545 | >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60) | |
1546 | (1.0, 1.0, 0.0, 1.0) | |
1547 | >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60).hsl | |
1548 | (60, 1, 0.5) | |
1549 | ||
1550 | ''' | |
1551 | h, s, l = self.__hsl | |
1552 | return Color((hue, s, l), 'hsl', self.__a, self.__wref) | |
1553 | ||
1554 | def ColorWithSaturation(self, saturation): | |
1555 | '''Create a new instance based on this one with a new saturation value. | |
1556 | ||
1557 | .. note:: | |
1558 | ||
1559 | The saturation is defined for the HSL mode. | |
1560 | ||
1561 | Parameters: | |
1562 | :saturation: | |
1563 | The saturation of the new color [0...1]. | |
1564 | ||
1565 | Returns: | |
1566 | A grapefruit.Color instance. | |
1567 | ||
1568 | >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5) | |
1569 | (0.75, 0.5, 0.25, 1.0) | |
1570 | >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5).hsl | |
1571 | (30, 0.5, 0.5) | |
1572 | ||
1573 | ''' | |
1574 | h, s, l = self.__hsl | |
1575 | return Color((h, saturation, l), 'hsl', self.__a, self.__wref) | |
1576 | ||
1577 | def ColorWithLightness(self, lightness): | |
1578 | '''Create a new instance based on this one with a new lightness value. | |
1579 | ||
1580 | Parameters: | |
1581 | :lightness: | |
1582 | The lightness of the new color [0...1]. | |
1583 | ||
1584 | Returns: | |
1585 | A grapefruit.Color instance. | |
1586 | ||
1587 | >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25) | |
1588 | (0.5, 0.25, 0.0, 1.0) | |
1589 | >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25).hsl | |
1590 | (30, 1, 0.25) | |
1591 | ||
1592 | ''' | |
1593 | h, s, l = self.__hsl | |
1594 | return Color((h, s, lightness), 'hsl', self.__a, self.__wref) | |
1595 | ||
1596 | def DarkerColor(self, level): | |
1597 | '''Create a new instance based on this one but darker. | |
1598 | ||
1599 | Parameters: | |
1600 | :level: | |
1601 | The amount by which the color should be darkened to produce | |
1602 | the new one [0...1]. | |
1603 | ||
1604 | Returns: | |
1605 | A grapefruit.Color instance. | |
1606 | ||
1607 | >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25) | |
1608 | (0.5, 0.25, 0.0, 1.0) | |
1609 | >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25).hsl | |
1610 | (30, 1, 0.25) | |
1611 | ||
1612 | ''' | |
1613 | h, s, l = self.__hsl | |
1614 | return Color((h, s, max(l - level, 0)), 'hsl', self.__a, self.__wref) | |
1615 | ||
1616 | def LighterColor(self, level): | |
1617 | '''Create a new instance based on this one but lighter. | |
1618 | ||
1619 | Parameters: | |
1620 | :level: | |
1621 | The amount by which the color should be lightened to produce | |
1622 | the new one [0...1]. | |
1623 | ||
1624 | Returns: | |
1625 | A grapefruit.Color instance. | |
1626 | ||
1627 | >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25) | |
1628 | (1.0, 0.75, 0.5, 1.0) | |
1629 | >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25).hsl | |
1630 | (30, 1, 0.75) | |
1631 | ||
1632 | ''' | |
1633 | h, s, l = self.__hsl | |
1634 | return Color((h, s, min(l + level, 1)), 'hsl', self.__a, self.__wref) | |
1635 | ||
1636 | def Saturate(self, level): | |
1637 | '''Create a new instance based on this one but more saturated. | |
1638 | ||
1639 | Parameters: | |
1640 | :level: | |
1641 | The amount by which the color should be saturated to produce | |
1642 | the new one [0...1]. | |
1643 | ||
1644 | Returns: | |
1645 | A grapefruit.Color instance. | |
1646 | ||
1647 | >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25) | |
1648 | (0.875, 0.5, 0.125, 1.0) | |
1649 | >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25).hsl | |
1650 | (30, 0.75, 0.5) | |
1651 | ||
1652 | ''' | |
1653 | h, s, l = self.__hsl | |
1654 | return Color((h, min(s + level, 1), l), 'hsl', self.__a, self.__wref) | |
1655 | ||
1656 | def Desaturate(self, level): | |
1657 | '''Create a new instance based on this one but less saturated. | |
1658 | ||
1659 | Parameters: | |
1660 | :level: | |
1661 | The amount by which the color should be desaturated to produce | |
1662 | the new one [0...1]. | |
1663 | ||
1664 | Returns: | |
1665 | A grapefruit.Color instance. | |
1666 | ||
1667 | >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25) | |
1668 | (0.625, 0.5, 0.375, 1.0) | |
1669 | >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25).hsl | |
1670 | (30, 0.25, 0.5) | |
1671 | ||
1672 | ''' | |
1673 | h, s, l = self.__hsl | |
1674 | return Color((h, max(s - level, 0), l), 'hsl', self.__a, self.__wref) | |
1675 | ||
1676 | def WebSafeDither(self): | |
1677 | '''Return the two websafe colors nearest to this one. | |
1678 | ||
1679 | Returns: | |
1680 | A tuple of two grapefruit.Color instances which are the two | |
1681 | web safe colors closest this one. | |
1682 | ||
1683 | >>> c = Color.NewFromRgb(1.0, 0.45, 0.0) | |
1684 | >>> c1, c2 = c.WebSafeDither() | |
1685 | >>> str(c1) | |
1686 | '(1, 0.4, 0, 1)' | |
1687 | >>> str(c2) | |
1688 | '(1, 0.6, 0, 1)' | |
1689 | ||
1690 | ''' | |
1691 | return ( | |
1692 | Color(Color.RgbToWebSafe(*self.__rgb), 'rgb', self.__a, self.__wref), | |
1693 | Color(Color.RgbToWebSafe(alt=True, *self.__rgb), 'rgb', self.__a, self.__wref)) | |
1694 | ||
1695 | def Gradient(self, target, steps=100): | |
1696 | '''Create a list with the gradient colors between this and the other color. | |
1697 | ||
1698 | Parameters: | |
1699 | :target: | |
1700 | The grapefruit.Color at the other end of the gradient. | |
1701 | :steps: | |
1702 | The number of gradients steps to create. | |
1703 | ||
1704 | ||
1705 | Returns: | |
1706 | A list of grapefruit.Color instances. | |
1707 | ||
1708 | >>> c1 = Color.NewFromRgb(1.0, 0.0, 0.0, alpha=1) | |
1709 | >>> c2 = Color.NewFromRgb(0.0, 1.0, 0.0, alpha=0) | |
1710 | >>> c1.Gradient(c2, 3) | |
1711 | [(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 | ||
1713 | ''' | |
1714 | gradient = [] | |
1715 | rgba1 = self.__rgb + (self.__a,) | |
1716 | rgba2 = target.__rgb + (target.__a,) | |
1717 | ||
1718 | steps += 1 | |
1719 | for n in xrange(1, steps): | |
1720 | d = 1.0*n/steps | |
1721 | r = (rgba1[0]*(1-d)) + (rgba2[0]*d) | |
1722 | g = (rgba1[1]*(1-d)) + (rgba2[1]*d) | |
1723 | b = (rgba1[2]*(1-d)) + (rgba2[2]*d) | |
1724 | a = (rgba1[3]*(1-d)) + (rgba2[3]*d) | |
1725 | ||
1726 | gradient.append(Color((r, g, b), 'rgb', a, self.__wref)) | |
1727 | ||
1728 | return gradient | |
1729 | ||
1730 | def ComplementaryColor(self, mode='ryb'): | |
1731 | '''Create a new instance which is the complementary color of this one. | |
1732 | ||
1733 | Parameters: | |
1734 | :mode: | |
1735 | Select which color wheel to use for the generation (ryb/rgb). | |
1736 | ||
1737 | ||
1738 | Returns: | |
1739 | A grapefruit.Color instance. | |
1740 | ||
1741 | >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor() | |
1742 | (0.0, 0.5, 1.0, 1.0) | |
1743 | >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor().hsl | |
1744 | (210, 1, 0.5) | |
1745 | ||
1746 | ''' | |
1747 | h, s, l = self.__hsl | |
1748 | ||
1749 | if mode == 'ryb': h = Color.RgbToRyb(h) | |
1750 | h = (h+180)%360 | |
1751 | if mode == 'ryb': h = Color.RybToRgb(h) | |
1752 | ||
1753 | return Color((h, s, l), 'hsl', self.__a, self.__wref) | |
1754 | ||
1755 | def MonochromeScheme(self): | |
1756 | '''Return 4 colors in the same hue with varying saturation/lightness. | |
1757 | ||
1758 | Returns: | |
1759 | A tuple of 4 grapefruit.Color in the same hue as this one, | |
1760 | with varying saturation/lightness. | |
1761 | ||
1762 | >>> c = Color.NewFromHsl(30, 0.5, 0.5) | |
1763 | >>> ['(%g, %g, %g)' % clr.hsl for clr in c.MonochromeScheme()] | |
1764 | ['(30, 0.2, 0.8)', '(30, 0.5, 0.3)', '(30, 0.2, 0.6)', '(30, 0.5, 0.8)'] | |
1765 | ||
1766 | ''' | |
1767 | def _wrap(x, min, thres, plus): | |
1768 | if (x-min) < thres: return x + plus | |
1769 | else: return x-min | |
1770 | ||
1771 | h, s, l = self.__hsl | |
1772 | ||
1773 | s1 = _wrap(s, 0.3, 0.1, 0.3) | |
1774 | l1 = _wrap(l, 0.5, 0.2, 0.3) | |
1775 | ||
1776 | s2 = s | |
1777 | l2 = _wrap(l, 0.2, 0.2, 0.6) | |
1778 | ||
1779 | s3 = s1 | |
1780 | l3 = max(0.2, l + (1-l)*0.2) | |
1781 | ||
1782 | s4 = s | |
1783 | l4 = _wrap(l, 0.5, 0.2, 0.3) | |
1784 | ||
1785 | return ( | |
1786 | Color((h, s1, l1), 'hsl', self.__a, self.__wref), | |
1787 | Color((h, s2, l2), 'hsl', self.__a, self.__wref), | |
1788 | Color((h, s3, l3), 'hsl', self.__a, self.__wref), | |
1789 | Color((h, s4, l4), 'hsl', self.__a, self.__wref)) | |
1790 | ||
1791 | def TriadicScheme(self, angle=120, mode='ryb'): | |
1792 | '''Return two colors forming a triad or a split complementary with this one. | |
1793 | ||
1794 | Parameters: | |
1795 | :angle: | |
1796 | The angle between the hues of the created colors. | |
1797 | The default value makes a triad. | |
1798 | :mode: | |
1799 | Select which color wheel to use for the generation (ryb/rgb). | |
1800 | ||
1801 | Returns: | |
1802 | A tuple of two grapefruit.Color forming a color triad with | |
1803 | this one or a split complementary. | |
1804 | ||
1805 | >>> c1 = Color.NewFromHsl(30, 1, 0.5) | |
1806 | ||
1807 | >>> c2, c3 = c1.TriadicScheme() | |
1808 | >>> c2.hsl | |
1809 | (150.0, 1, 0.5) | |
1810 | >>> c3.hsl | |
1811 | (270.0, 1, 0.5) | |
1812 | ||
1813 | >>> c2, c3 = c1.TriadicScheme(40) | |
1814 | >>> c2.hsl | |
1815 | (190.0, 1, 0.5) | |
1816 | >>> c3.hsl | |
1817 | (230.0, 1, 0.5) | |
1818 | ||
1819 | ''' | |
1820 | h, s, l = self.__hsl | |
1821 | angle = min(angle, 120) / 2.0 | |
1822 | ||
1823 | if mode == 'ryb': h = Color.RgbToRyb(h) | |
1824 | h += 180 | |
1825 | h1 = (h - angle) % 360 | |
1826 | h2 = (h + angle) % 360 | |
1827 | if mode == 'ryb': | |
1828 | h1 = Color.RybToRgb(h1) | |
1829 | h2 = Color.RybToRgb(h2) | |
1830 | ||
1831 | return ( | |
1832 | Color((h1, s, l), 'hsl', self.__a, self.__wref), | |
1833 | Color((h2, s, l), 'hsl', self.__a, self.__wref)) | |
1834 | ||
1835 | def TetradicScheme(self, angle=30, mode='ryb'): | |
1836 | '''Return three colors froming a tetrad with this one. | |
1837 | ||
1838 | Parameters: | |
1839 | :angle: | |
1840 | The angle to substract from the adjacent colors hues [-90...90]. | |
1841 | You can use an angle of zero to generate a square tetrad. | |
1842 | :mode: | |
1843 | Select which color wheel to use for the generation (ryb/rgb). | |
1844 | ||
1845 | Returns: | |
1846 | A tuple of three grapefruit.Color forming a color tetrad with | |
1847 | this one. | |
1848 | ||
1849 | >>> col = Color.NewFromHsl(30, 1, 0.5) | |
1850 | >>> [c.hsl for c in col.TetradicScheme(mode='rgb', angle=30)] | |
1851 | [(90, 1, 0.5), (210, 1, 0.5), (270, 1, 0.5)] | |
1852 | ||
1853 | ''' | |
1854 | h, s, l = self.__hsl | |
1855 | ||
1856 | if mode == 'ryb': h = Color.RgbToRyb(h) | |
1857 | h1 = (h + 90 - angle) % 360 | |
1858 | h2 = (h + 180) % 360 | |
1859 | h3 = (h + 270 - angle) % 360 | |
1860 | if mode == 'ryb': | |
1861 | h1 = Color.RybToRgb(h1) | |
1862 | h2 = Color.RybToRgb(h2) | |
1863 | h3 = Color.RybToRgb(h3) | |
1864 | ||
1865 | return ( | |
1866 | Color((h1, s, l), 'hsl', self.__a, self.__wref), | |
1867 | Color((h2, s, l), 'hsl', self.__a, self.__wref), | |
1868 | Color((h3, s, l), 'hsl', self.__a, self.__wref)) | |
1869 | ||
1870 | def AnalogousScheme(self, angle=30, mode='ryb'): | |
1871 | '''Return two colors analogous to this one. | |
1872 | ||
1873 | Args: | |
1874 | :angle: | |
1875 | The angle between the hues of the created colors and this one. | |
1876 | :mode: | |
1877 | Select which color wheel to use for the generation (ryb/rgb). | |
1878 | ||
1879 | Returns: | |
1880 | A tuple of grapefruit.Colors analogous to this one. | |
1881 | ||
1882 | >>> c1 = Color.NewFromHsl(30, 1, 0.5) | |
1883 | ||
1884 | >>> c2, c3 = c1.AnalogousScheme() | |
1885 | >>> c2.hsl | |
1886 | (330, 1, 0.5) | |
1887 | >>> c3.hsl | |
1888 | (90, 1, 0.5) | |
1889 | ||
1890 | >>> c2, c3 = c1.AnalogousScheme(10) | |
1891 | >>> c2.hsl | |
1892 | (20, 1, 0.5) | |
1893 | >>> c3.hsl | |
1894 | (40, 1, 0.5) | |
1895 | ||
1896 | ''' | |
1897 | h, s, l = self.__hsl | |
1898 | ||
1899 | if mode == 'ryb': h = Color.RgbToRyb(h) | |
1900 | h += 360 | |
1901 | h1 = (h - angle) % 360 | |
1902 | h2 = (h + angle) % 360 | |
1903 | if mode == 'ryb': | |
1904 | h1 = Color.RybToRgb(h1) | |
1905 | h2 = Color.RybToRgb(h2) | |
1906 | ||
1907 | return (Color((h1, s, l), 'hsl', self.__a, self.__wref), | |
1908 | Color((h2, s, l), 'hsl', self.__a, self.__wref)) | |
1909 | ||
1910 | def AlphaBlend(self, other): | |
1911 | '''Alpha-blend this color on the other one. | |
1912 | ||
1913 | Args: | |
1914 | :other: | |
1915 | The grapefruit.Color to alpha-blend with this one. | |
1916 | ||
1917 | Returns: | |
1918 | A grapefruit.Color instance which is the result of alpha-blending | |
1919 | this color on the other one. | |
1920 | ||
1921 | >>> c1 = Color.NewFromRgb(1, 0.5, 0, 0.2) | |
1922 | >>> c2 = Color.NewFromRgb(1, 1, 1, 0.8) | |
1923 | >>> c3 = c1.AlphaBlend(c2) | |
1924 | >>> str(c3) | |
1925 | '(1, 0.875, 0.75, 0.84)' | |
1926 | ||
1927 | ''' | |
1928 | # get final alpha channel | |
1929 | fa = self.__a + other.__a - (self.__a * other.__a) | |
1930 | ||
1931 | # get percentage of source alpha compared to final alpha | |
1932 | if fa==0: sa = 0 | |
1933 | else: sa = min(1.0, self.__a/other.__a) | |
1934 | ||
1935 | # destination percentage is just the additive inverse | |
1936 | da = 1.0 - sa | |
1937 | ||
1938 | sr, sg, sb = [v * sa for v in self.__rgb] | |
1939 | dr, dg, db = [v * da for v in other.__rgb] | |
1940 | ||
1941 | return Color((sr+dr, sg+dg, sb+db), 'rgb', fa, self.__wref) | |
1942 | ||
1943 | def Blend(self, other, percent=0.5): | |
1944 | '''Blend this color with the other one. | |
1945 | ||
1946 | Args: | |
1947 | :other: | |
1948 | the grapefruit.Color to blend with this one. | |
1949 | ||
1950 | Returns: | |
1951 | A grapefruit.Color instance which is the result of blending | |
1952 | this color on the other one. | |
1953 | ||
1954 | >>> c1 = Color.NewFromRgb(1, 0.5, 0, 0.2) | |
1955 | >>> c2 = Color.NewFromRgb(1, 1, 1, 0.6) | |
1956 | >>> c3 = c1.Blend(c2) | |
1957 | >>> str(c3) | |
1958 | '(1, 0.75, 0.5, 0.4)' | |
1959 | ||
1960 | ''' | |
1961 | dest = 1.0 - percent | |
1962 | rgb = tuple(((u * percent) + (v * dest) for u, v in zip(self.__rgb, other.__rgb))) | |
1963 | a = (self.__a * percent) + (other.__a * dest) | |
1964 | return Color(rgb, 'rgb', a, self.__wref) | |
1965 | ||
1966 | def _test(): | |
1967 | import doctest | |
1968 | reload(doctest) | |
1969 | doctest.testmod() | |
1970 | ||
1971 | if __name__=='__main__': | |
1972 | _test() |
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 | '''GrapeFruit setup and build script.''' | |
18 | ||
19 | # $Id: setup.py 24 2008-05-25 16:23:22Z xbasty $ | |
20 | __author__ = 'Xavier Basty <xbasty@gmail.com>' | |
21 | __version__ = '0.1a3' | |
22 | ||
23 | ||
24 | # The base package metadata to be used by both distutils and setuptools | |
25 | METADATA = dict( | |
26 | name = "grapefruit", | |
27 | version = __version__, | |
28 | py_modules = ['grapefruit'], | |
29 | author = 'Xavier Basty', | |
30 | author_email = 'xbasty@gmail.com', | |
31 | description = 'A module to manipulate color information easily.', | |
32 | license = 'Apache License 2.0', | |
33 | url = 'http://code.google.com/p/grapefruit/', | |
34 | download_url = 'http://grapefruit.googlecode.com/files/grapefruit-%s.tar.gz' % \ | |
35 | __version__, | |
36 | keywords ='color conversion', | |
37 | ) | |
38 | ||
39 | # Extra package metadata to be used only if setuptools is installed | |
40 | SETUPTOOLS_METADATA = dict( | |
41 | install_requires = ['setuptools'], | |
42 | include_package_data = True, | |
43 | zip_safe = True, | |
44 | classifiers = [ | |
45 | 'Development Status :: 3 - Alpha', | |
46 | 'Intended Audience :: Developers', | |
47 | 'License :: OSI Approved :: Apache Software License', | |
48 | 'Topic :: Software Development :: Libraries :: Python Modules', | |
49 | 'Topic :: Multimedia :: Graphics', | |
50 | ], | |
51 | test_suite = 'grapefruit_test.suite', | |
52 | ) | |
53 | ||
54 | def Read(file): | |
55 | return open(file).read() | |
56 | ||
57 | def BuildLongDescription(): | |
58 | return '\n'.join([Read('README'), Read('CHANGES')]) | |
59 | ||
60 | def Main(): | |
61 | # Build the long_description from the README and CHANGES files | |
62 | METADATA['long_description'] = BuildLongDescription() | |
63 | ||
64 | # Use setuptools if available, otherwise fallback and use distutils | |
65 | try: | |
66 | import setuptools | |
67 | METADATA.update(SETUPTOOLS_METADATA) | |
68 | setuptools.setup(**METADATA) | |
69 | except ImportError: | |
70 | import distutils.core | |
71 | distutils.core.setup(**METADATA) | |
72 | ||
73 | ||
74 | if __name__ == '__main__': | |
75 | Main() |