CHANGES 777 0 0 2673 11025170712 5113 02008-06-15 - Released 0.1a3 - Added Gradient 2008-06-01 - Fixed overflow in alpha blending. 2008-05-29 - Added Saturate/Desaturate. - Added MonochromeScheme - Added the mode parameter to the generation methods to choose the color wheel use for the generation (ryb/rgb). 2008-05-28 - Added the RGB<->RYB hue conversion. - Added an angle parameter to the tetrad scheme to control the shape of the rectangle. 2008-05-27 - Released 0.1a2 2008-05-24 - Fixed the HSL->RGB conversion (the modulo in the hue conversion was 60 instead of 6.0!) Updated the unit tests (which were wrong!) 2008-05-24 Released 0.1a1 - Convert the documentation to Sphinx - Completed the unit tests - Fixed some stupid typos 2008-05-22 - Refactored pretty much everything to more standard "Python coding style". - Replaced the global variables by Color properties. - Moved the module functions to static methods of Color. - Completed the CIE white point dictionary to include all the standard illuminants. - Added doctest for all the functions. - Fixed the conversions factors to get better results (more exact and more symmetric). - Changed the range of the L component from [0~1] to [0~100] (as it should have been). - Added packaging data and setup. - Changed the structure of the unit tests. 2008-05-08 Released 0.1a0 - Initial checkin of grapefruit COPYING 777 0 0 1057 11015362366 5155 0Copyright (c) 2008, Xavier Basty Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.doc/ 777 0 0 0 11025172065 4575 5doc/conf.py 777 0 0 12417 11017713404 6203 0# -*- coding: utf-8 -*- # # GrapeFruit documentation build configuration file, created by # sphinx-quickstart on Sat May 24 00:09:48 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. # # $Id: conf.py 14 2008-05-24 09:46:30Z xbasty $ import sys, os # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. #sys.path.append(os.path.abspath('some/directory')) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'GrapeFruit' copyright = '2008, Xavier Basty ' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = '0.1' # The full version, including alpha/beta/rc tags. release = '0.1a3' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directories, that shouldn't be searched # for source files. #exclude_dirs = [] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'default.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. #html_logo = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'GrapeFruitdoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('index', 'GrapeFruit.tex', 'GrapeFruit Documentation', 'Xavier Basty ', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True doc/index.rst 777 0 0 27246 11025031152 6543 0.. _grapefruit-index: .. image:: _static/GrapeFruit.png Welcome! This is the documentation for GrapeFruit |release|, last updated |today|. See the :ref:`genindex` for a list of the topics. .. module:: grapefruit .. moduleauthor:: Xavier Basty ========================== The Color class ========================== .. class:: Color The grapefruit module contains only the :class:`Color` class, which exposes all the functionnalities. It can be used to store a color value and manipulate it, or convert it to another color system. If you are only interested in converting you colors from one system to another, you can store them using regular tuples instead of :class:`Color` instances. You can then use the class static methods to perform the conversions. :class:`Color` stores both the RGB and HSL representation of the color. This makes possible to keep the hue intact when the color is a pure white due to its lightness. However, certain operations work only with the RGB values, and might then lose the hue. All the operations assume that you provide values in the specified ranges, no checks are made whatsoever. If you provide a value outside of the specified ranges, you'll get some strange results... The class instances are immutable, all the methods return a new instance of the :class:`Color` class, and all the properties are read-only. .. note:: Some operations may provide results a bit outside the specified ranges, the results are not capped. This is due to certain color systems having a widers gamut than others. Class content --------------- - :ref:`class-constants` - :const:`Color.WHITE_REFERENCE` - :const:`Color.NAMED_COLOR` - :ref:`conversion-functions` - :meth:`Color.RgbToHsl` - :meth:`Color.HslToRgb` - :meth:`Color.RgbToHsv` - :meth:`Color.HsvToRgb` - :meth:`Color.RgbToYiq` - :meth:`Color.YiqToRgb` - :meth:`Color.RgbToYuv` - :meth:`Color.YuvToRgb` - :meth:`Color.RgbToXyz` - :meth:`Color.XyzToRgb` - :meth:`Color.XyzToLab` - :meth:`Color.LabToXyz` - :meth:`Color.CmykToCmy` - :meth:`Color.CmyToCmyk` - :meth:`Color.RgbToCmy` - :meth:`Color.CmyToRgb` - :meth:`Color.RgbToHtml` - :meth:`Color.HtmlToRgb` - :meth:`Color.RgbToPil` - :meth:`Color.PilToRgb` - :meth:`Color.RgbToWebSafe` - :meth:`Color.RgbToGreyscale` - :meth:`Color.RgbToRyb` - :meth:`Color.RybToRgb` - :ref:`instantiation-functions` - :meth:`Color.NewFromRgb` - :meth:`Color.NewFromHsl` - :meth:`Color.NewFromHsv` - :meth:`Color.NewFromYiq` - :meth:`Color.NewFromYuv` - :meth:`Color.NewFromXyz` - :meth:`Color.NewFromLab` - :meth:`Color.NewFromCmy` - :meth:`Color.NewFromCmyk` - :meth:`Color.NewFromHtml` - :meth:`Color.NewFromPil` - :ref:`properties` - :attr:`Color.alpha` - :attr:`Color.whiteRef` - :attr:`Color.rgb` - :attr:`Color.hue` - :attr:`Color.hsl` - :attr:`Color.hsv` - :attr:`Color.yiq` - :attr:`Color.yuv` - :attr:`Color.xyz` - :attr:`Color.lab` - :attr:`Color.cmy` - :attr:`Color.cmyk` - :attr:`Color.html` - :attr:`Color.pil` - :attr:`Color.webSafe` - :attr:`Color.greyscale` - :ref:`manipulation-methods` - :meth:`Color.ColorWithAlpha` - :meth:`Color.ColorWithWhiteRef` - :meth:`Color.ColorWithHue` - :meth:`Color.ColorWithSaturation` - :meth:`Color.ColorWithLightness` - :meth:`Color.DarkerColor` - :meth:`Color.LighterColor` - :meth:`Color.Saturate` - :meth:`Color.Desaturate` - :meth:`Color.WebSafeDither` - :ref:`generation-methods` - :meth:`Color.Gradient` - :meth:`Color.ComplementaryColor` - :meth:`Color.TriadicScheme` - :meth:`Color.TetradicScheme` - :meth:`Color.AnalogousScheme` - :ref:`blending-methods` - :meth:`Color.AlphaBlend` - :meth:`Color.Blend` Example usage --------------- To create an instance of the grapefruit.Color from RGB values: >>> import grapefruit >>> r, g, b = 1, 0.5, 0 >>> col = grapefruit.Color.NewFromRgb(r, g, b) To get the values of the color in another colorspace: >>> h, s, v = col.hsv >>> l, a, b = col.lab To get the complementary of a color: >>> compl = col.ComplementaryColor() >>> print compl.hsl (210.0, 1.0, 0.5) To directly convert RGB values to their HSL equivalent: >>> h, s, l = Color.RgbToHsl(r, g, b) .. _class-constants: Class Constants ----------------- .. data:: Color.WHITE_REFERENCE The reference white points of the CIE standards illuminants, calculated from the chromaticity coordinates found at: http://en.wikipedia.org/wiki/Standard_illuminant A dictionary mapping the name of the CIE standard illuminants to their reference white points. The white points are required for the XYZ <-> L*a*b conversions. The key names are build using the following pattern: ``_`` The possible values for ```` are: ====== =================================== Value Observer ====== =================================== std CIE 1931 2° Standard Observer sup CIE 1964 10° Supplementary Observer ====== =================================== The possible values for ```` are the name of the standard illuminants: ====== ======== ================================================== Value CCT Illuminant ====== ======== ================================================== A 2856 K Incandescent tungsten B 4874 K Direct sunlight at noon (obsolete) C 6774 K North sky daylight (obsolete) D50 5003 K ICC Profile PCS. Horizon light. D55 5503 K Compromise between incandescent and daylight D65 6504 K Noon daylight (TV & sRGB colorspace) D75 7504 K North sky day light E ~5455 K Equal energy radiator (not a black body) F1 6430 K Daylight Fluorescent F2 4230 K Cool White Fluorescent F3 3450 K White Fluorescent F4 2940 K Warm White Fluorescent F5 6350 K Daylight Fluorescent F6 4150 K Lite White Fluorescent F7 6500 K Broadband fluorescent, D65 simulator F8 5000 K Broadband fluorescent, D50 simulator F9 4150 K Broadband fluorescent, Cool White Deluxe F10 5000 K Narrowband fluorescent, Philips TL85, Ultralume 50 F11 4000 K Narrowband fluorescent, Philips TL84, Ultralume 40 F12 3000 K Narrowband fluorescent, Philips TL83, Ultralume 30 ====== ======== ================================================== .. data:: Color.NAMED_COLOR The names and RGB values of the X11 colors supported by popular browsers, with the gray/grey spelling issues, fixed so that both work (e.g light*grey* and light*gray*). Note: For *Gray*, *Green*, *Maroon* and *Purple*, the HTML/CSS values are used instead of the X11 ones (see `X11/CSS clashes `_) Reference: `CSS3 Color module `_ .. _conversion-functions: Conversion functions -------------------- The conversion functions are static methods of the :class:`Color` class that let you convert a color stored as the list of its components rather than as a :class:`Color` instance. .. automethod:: Color.RgbToHsl .. automethod:: Color.HslToRgb .. automethod:: Color.RgbToHsv .. automethod:: Color.HsvToRgb .. automethod:: Color.RgbToYiq .. automethod:: Color.YiqToRgb .. automethod:: Color.RgbToYuv .. automethod:: Color.YuvToRgb .. automethod:: Color.RgbToXyz .. automethod:: Color.XyzToRgb .. automethod:: Color.XyzToLab .. automethod:: Color.LabToXyz .. automethod:: Color.CmykToCmy .. automethod:: Color.CmyToCmyk .. automethod:: Color.RgbToCmy .. automethod:: Color.CmyToRgb .. automethod:: Color.RgbToHtml .. automethod:: Color.HtmlToRgb .. automethod:: Color.RgbToPil .. automethod:: Color.PilToRgb .. automethod:: Color.RgbToWebSafe .. automethod:: Color.RgbToGreyscale .. automethod:: Color.RgbToRyb .. automethod:: Color.RybToRgb .. _instantiation-functions: Instantiation functions ----------------------- The instantiation functions let you create a new instance of the :class:`Color` class from the color components using the color system of your choice. .. automethod:: Color.NewFromRgb .. automethod:: Color.NewFromHsl .. automethod:: Color.NewFromHsv .. automethod:: Color.NewFromYiq .. automethod:: Color.NewFromYuv .. automethod:: Color.NewFromXyz .. automethod:: Color.NewFromLab .. automethod:: Color.NewFromCmy .. automethod:: Color.NewFromCmyk .. automethod:: Color.NewFromHtml .. automethod:: Color.NewFromPil .. _properties: Properties ---------- The properties get the value of the instance in the specified color model. The properties returning calculated values unless marked otherwise. .. note:: All the properties are read-only. You need to make a copy of the instance to modify the color value. .. autoattribute:: Color.alpha *This value is not calculated, the stored value is returned directly.* .. autoattribute:: Color.whiteRef *This value is not calculated, the stored value is returned directly.* .. autoattribute:: Color.rgb *This value is not calculated, the stored value is returned directly.* .. autoattribute:: Color.hue *This value is not calculated, the stored value is returned directly.* .. autoattribute:: Color.hsl *This value is not calculated, the stored value is returned directly.* .. autoattribute:: Color.hsv .. autoattribute:: Color.yiq .. autoattribute:: Color.yuv .. autoattribute:: Color.xyz .. autoattribute:: Color.lab .. autoattribute:: Color.cmy .. autoattribute:: Color.cmyk .. autoattribute:: Color.html .. autoattribute:: Color.pil .. autoattribute:: Color.webSafe .. attribute:: Color.greyscale .. _manipulation-methods: Manipulation methods -------------------- The manipulations methods let you create a new color by changing an existing color properties. .. note:: The methods **do not** modify the current Color instance. They create a new instance or a tuple of new instances with the specified modifications. .. automethod:: Color.ColorWithAlpha .. automethod:: Color.ColorWithWhiteRef .. automethod:: Color.ColorWithHue .. automethod:: Color.ColorWithSaturation .. automethod:: Color.ColorWithLightness .. automethod:: Color.DarkerColor .. automethod:: Color.LighterColor .. automethod:: Color.Saturate .. automethod:: Color.Desaturate .. automethod:: Color.WebSafeDither .. _generation-methods: Generation methods ------------------ The generation methods let you create a color scheme by using a color as the start point. All the method, appart from Gradient and MonochromeScheme, have a 'mode' parameter that let you choose which color wheel should be used to generate the scheme. The following modes are available: :ryb: The `RYB `_ color wheel, or *artistic color wheel*. While scientifically incorrect, it generally produces better schemes than RGB. :rgb: The standard RGB color wheel. .. automethod:: Color.Gradient .. automethod:: Color.ComplementaryColor .. automethod:: Color.MonochromeScheme .. automethod:: Color.TriadicScheme .. automethod:: Color.TetradicScheme .. automethod:: Color.AnalogousScheme .. _blending-methods: Blending methods ---------------- .. automethod:: Color.AlphaBlend .. automethod:: Color.Blend doc/makedoc.cmd 777 0 0 314 11016045432 6723 0@echo off set builder=%1 if [%builder%]==[] set builder=html set target=_build\%builder% if not exist %target%\* md %target% sphinx-build -b %builder% . _build\%builder%\ set target= set builder= doc/Makefile 777 0 0 4446 11015651146 6331 0# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html web pickle htmlhelp latex changes linkcheck help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files (usable by e.g. sphinx-web)" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf _build/* html: mkdir -p _build/html _build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in _build/html." pickle: mkdir -p _build/pickle _build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle @echo @echo "Build finished; now you can process the pickle files or run" @echo " sphinx-web _build/pickle" @echo "to start the sphinx-web server." web: pickle htmlhelp: mkdir -p _build/htmlhelp _build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex @echo @echo "Build finished; the LaTeX files are in _build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p _build/changes _build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo @echo "The overview file is in _build/changes." linkcheck: mkdir -p _build/linkcheck _build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in _build/linkcheck/output.txt." doc/_static/ 777 0 0 0 11025172065 6223 5grapefruit.egg-info/ 777 0 0 0 11025172065 7672 5grapefruit.egg-info/dependency_links.txt 777 0 0 1 11025171026 13737 0 grapefruit.egg-info/PKG-INFO 777 0 0 11264 11025171026 11072 0Metadata-Version: 1.0 Name: grapefruit Version: 0.1a3 Summary: A module to manipulate color information easily. Home-page: http://code.google.com/p/grapefruit/ Author: Xavier Basty Author-email: xbasty@gmail.com License: Apache License 2.0 Download-URL: http://grapefruit.googlecode.com/files/grapefruit-0.1a3.tar.gz Description: ===================== README for GrapeFruit ===================== Installing ============ **From the sources:** Download the latest grapefruit library from: http://code.google.com/p/grapefruit/ Untar the source distribution and run:: $ python setup.py build $ python setup.py install Testing ========= With setuptools installed:: $ python setup.py test Without setuptools installed:: $ python grapefruit_test.py Getting the code ================== View the trunk at: http://grapefruit.googlecode.com/svn/trunk/ Check out the latest development version anonymously with:: $ svn checkout http://grapefruit.googlecode.com/svn/trunk/ GrapeFruit Documentation =============== You can download a compiled version of the documentation at: http://http://code.google.com/p/grapefruit/downloads/list?q=label:Type-Docs The documentation is generated from reStructuredText sources by Sphinx. If you need to build it, go into the doc folder and run:: make Or, if you're running windows:: makedoc.cmd License ========= :: Copyright (c) 2008, Xavier Basty Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 2008-06-15 - Released 0.1a3 - Added Gradient 2008-06-01 - Fixed overflow in alpha blending. 2008-05-29 - Added Saturate/Desaturate. - Added MonochromeScheme - Added the mode parameter to the generation methods to choose the color wheel use for the generation (ryb/rgb). 2008-05-28 - Added the RGB<->RYB hue conversion. - Added an angle parameter to the tetrad scheme to control the shape of the rectangle. 2008-05-27 - Released 0.1a2 2008-05-24 - Fixed the HSL->RGB conversion (the modulo in the hue conversion was 60 instead of 6.0!) Updated the unit tests (which were wrong!) 2008-05-24 Released 0.1a1 - Convert the documentation to Sphinx - Completed the unit tests - Fixed some stupid typos 2008-05-22 - Refactored pretty much everything to more standard "Python coding style". - Replaced the global variables by Color properties. - Moved the module functions to static methods of Color. - Completed the CIE white point dictionary to include all the standard illuminants. - Added doctest for all the functions. - Fixed the conversions factors to get better results (more exact and more symmetric). - Changed the range of the L component from [0~1] to [0~100] (as it should have been). - Added packaging data and setup. - Changed the structure of the unit tests. 2008-05-08 Released 0.1a0 - Initial checkin of grapefruit Keywords: color conversion Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Multimedia :: Graphics grapefruit.egg-info/requires.txt 777 0 0 12 11025171026 12302 0setuptoolsgrapefruit.egg-info/SOURCES.txt 777 0 0 545 11025171030 11614 0CHANGES COPYING LICENSE README TODO grapefruit.py grapefruit_test.py setup.py doc/Makefile doc/conf.py doc/index.rst doc/makedoc.cmd doc/_static/GrapeFruit.png grapefruit.egg-info/PKG-INFO grapefruit.egg-info/SOURCES.txt grapefruit.egg-info/dependency_links.txt grapefruit.egg-info/requires.txt grapefruit.egg-info/top_level.txt grapefruit.egg-info/zip-safegrapefruit.egg-info/top_level.txt 777 0 0 13 11025171026 12435 0grapefruit grapefruit.egg-info/zip-safe 777 0 0 1 11016704142 11322 0 grapefruit.py 777 0 0 152525 11025040714 6702 0#!/usr/bin/python # -*- coding: utf-8 -*-# # Copyright (c) 2008, Xavier Basty # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. '''GrapeFruit - Color manipulation in Python''' # $Id: grapefruit.py 30 2008-06-01 20:44:26Z xbasty $ __author__ = 'Xavier Basty ' __version__ = '0.1a3' # The default white reference, use 2° Standard Observer, D65 (daylight) _DEFAULT_WREF = (0.95043, 1.00000, 1.08890) _oneThird = 1.0 / 3 _srgbGammaCorrInv = 0.03928 / 12.92 _sixteenHundredsixteenth = 16.0 / 116 _RybWheel = ( 0, 26, 52, 83, 120, 130, 141, 151, 162, 177, 190, 204, 218, 232, 246, 261, 275, 288, 303, 317, 330, 338, 345, 352, 360) _RgbWheel = ( 0, 8, 17, 26, 34, 41, 48, 54, 60, 81, 103, 123, 138, 155, 171, 187, 204, 219, 234, 251, 267, 282, 298, 329, 360) class Color: '''Hold a color value. Example usage: To create an instance of the grapefruit.Color from RGB values: >>> import grapefruit >>> r, g, b = 1, 0.5, 0 >>> col = grapefruit.Color.NewFromRgb(r, g, b) To get the values of the color in another colorspace: >>> h, s, v = col.hsv >>> l, a, b = col.lab To get the complementary of a color: >>> compl = col.ComplementaryColor() >>> print compl.hsl (210.0, 1.0, 0.5) To directly convert RGB values to their HSL equivalent: >>> h, s, l = Color.RgbToHsl(r, g, b) ''' WHITE_REFERENCE = { 'std_A' : (1.09847, 1.00000, 0.35582), 'std_B' : (0.99093, 1.00000, 0.85313), 'std_C' : (0.98071, 1.00000, 1.18225), 'std_D50' : (0.96421, 1.00000, 0.82519), 'std_D55' : (0.95680, 1.00000, 0.92148), 'std_D65' : (0.95043, 1.00000, 1.08890), 'std_D75' : (0.94972, 1.00000, 1.22639), 'std_E' : (1.00000, 1.00000, 1.00000), 'std_F1' : (0.92834, 1.00000, 1.03665), 'std_F2' : (0.99145, 1.00000, 0.67316), 'std_F3' : (1.03753, 1.00000, 0.49861), 'std_F4' : (1.09147, 1.00000, 0.38813), 'std_F5' : (0.90872, 1.00000, 0.98723), 'std_F6' : (0.97309, 1.00000, 0.60191), 'std_F7' : (0.95017, 1.00000, 1.08630), 'std_F8' : (0.96413, 1.00000, 0.82333), 'std_F9' : (1.00365, 1.00000, 0.67868), 'std_F10' : (0.96174, 1.00000, 0.81712), 'std_F11' : (1.00899, 1.00000, 0.64262), 'std_F12' : (1.08046, 1.00000, 0.39228), 'sup_A' : (1.11142, 1.00000, 0.35200), 'sup_B' : (0.99178, 1.00000, 0.84349), 'sup_C' : (0.97286, 1.00000, 1.16145), 'sup_D50' : (0.96721, 1.00000, 0.81428), 'sup_D55' : (0.95797, 1.00000, 0.90925), 'sup_D65' : (0.94810, 1.00000, 1.07305), 'sup_D75' : (0.94417, 1.00000, 1.20643), 'sup_E' : (1.00000, 1.00000, 1.00000), 'sup_F1' : (0.94791, 1.00000, 1.03191), 'sup_F2' : (1.03245, 1.00000, 0.68990), 'sup_F3' : (1.08968, 1.00000, 0.51965), 'sup_F4' : (1.14961, 1.00000, 0.40963), 'sup_F5' : (0.93369, 1.00000, 0.98636), 'sup_F6' : (1.02148, 1.00000, 0.62074), 'sup_F7' : (0.95780, 1.00000, 1.07618), 'sup_F8' : (0.97115, 1.00000, 0.81135), 'sup_F9' : (1.02116, 1.00000, 0.67826), 'sup_F10' : (0.99001, 1.00000, 0.83134), 'sup_F11' : (1.03820, 1.00000, 0.65555), 'sup_F12' : (1.11428, 1.00000, 0.40353)} NAMED_COLOR = { 'aliceblue': '#f0f8ff', 'antiquewhite': '#faebd7', 'aqua': '#00ffff', 'aquamarine': '#7fffd4', 'azure': '#f0ffff', 'beige': '#f5f5dc', 'bisque': '#ffe4c4', 'black': '#000000', 'blanchedalmond': '#ffebcd', 'blue': '#0000ff', 'blueviolet': '#8a2be2', 'brown': '#a52a2a', 'burlywood': '#deb887', 'cadetblue': '#5f9ea0', 'chartreuse': '#7fff00', 'chocolate': '#d2691e', 'coral': '#ff7f50', 'cornflowerblue': '#6495ed', 'cornsilk': '#fff8dc', 'crimson': '#dc143c', 'cyan': '#00ffff', 'darkblue': '#00008b', 'darkcyan': '#008b8b', 'darkgoldenrod': '#b8860b', 'darkgray': '#a9a9a9', 'darkgrey': '#a9a9a9', 'darkgreen': '#006400', 'darkkhaki': '#bdb76b', 'darkmagenta': '#8b008b', 'darkolivegreen': '#556b2f', 'darkorange': '#ff8c00', 'darkorchid': '#9932cc', 'darkred': '#8b0000', 'darksalmon': '#e9967a', 'darkseagreen': '#8fbc8f', 'darkslateblue': '#483d8b', 'darkslategray': '#2f4f4f', 'darkslategrey': '#2f4f4f', 'darkturquoise': '#00ced1', 'darkviolet': '#9400d3', 'deeppink': '#ff1493', 'deepskyblue': '#00bfff', 'dimgray': '#696969', 'dimgrey': '#696969', 'dodgerblue': '#1e90ff', 'firebrick': '#b22222', 'floralwhite': '#fffaf0', 'forestgreen': '#228b22', 'fuchsia': '#ff00ff', 'gainsboro': '#dcdcdc', 'ghostwhite': '#f8f8ff', 'gold': '#ffd700', 'goldenrod': '#daa520', 'gray': '#808080', 'grey': '#808080', 'green': '#008000', 'greenyellow': '#adff2f', 'honeydew': '#f0fff0', 'hotpink': '#ff69b4', 'indianred': '#cd5c5c', 'indigo': '#4b0082', 'ivory': '#fffff0', 'khaki': '#f0e68c', 'lavender': '#e6e6fa', 'lavenderblush': '#fff0f5', 'lawngreen': '#7cfc00', 'lemonchiffon': '#fffacd', 'lightblue': '#add8e6', 'lightcoral': '#f08080', 'lightcyan': '#e0ffff', 'lightgoldenrodyellow': '#fafad2', 'lightgreen': '#90ee90', 'lightgray': '#d3d3d3', 'lightgrey': '#d3d3d3', 'lightpink': '#ffb6c1', 'lightsalmon': '#ffa07a', 'lightseagreen': '#20b2aa', 'lightskyblue': '#87cefa', 'lightslategray': '#778899', 'lightslategrey': '#778899', 'lightsteelblue': '#b0c4de', 'lightyellow': '#ffffe0', 'lime': '#00ff00', 'limegreen': '#32cd32', 'linen': '#faf0e6', 'magenta': '#ff00ff', 'maroon': '#800000', 'mediumaquamarine': '#66cdaa', 'mediumblue': '#0000cd', 'mediumorchid': '#ba55d3', 'mediumpurple': '#9370db', 'mediumseagreen': '#3cb371', 'mediumslateblue': '#7b68ee', 'mediumspringgreen': '#00fa9a', 'mediumturquoise': '#48d1cc', 'mediumvioletred': '#c71585', 'midnightblue': '#191970', 'mintcream': '#f5fffa', 'mistyrose': '#ffe4e1', 'moccasin': '#ffe4b5', 'navajowhite': '#ffdead', 'navy': '#000080', 'oldlace': '#fdf5e6', 'olive': '#808000', 'olivedrab': '#6b8e23', 'orange': '#ffa500', 'orangered': '#ff4500', 'orchid': '#da70d6', 'palegoldenrod': '#eee8aa', 'palegreen': '#98fb98', 'paleturquoise': '#afeeee', 'palevioletred': '#db7093', 'papayawhip': '#ffefd5', 'peachpuff': '#ffdab9', 'peru': '#cd853f', 'pink': '#ffc0cb', 'plum': '#dda0dd', 'powderblue': '#b0e0e6', 'purple': '#800080', 'red': '#ff0000', 'rosybrown': '#bc8f8f', 'royalblue': '#4169e1', 'saddlebrown': '#8b4513', 'salmon': '#fa8072', 'sandybrown': '#f4a460', 'seagreen': '#2e8b57', 'seashell': '#fff5ee', 'sienna': '#a0522d', 'silver': '#c0c0c0', 'skyblue': '#87ceeb', 'slateblue': '#6a5acd', 'slategray': '#708090', 'slategrey': '#708090', 'snow': '#fffafa', 'springgreen': '#00ff7f', 'steelblue': '#4682b4', 'tan': '#d2b48c', 'teal': '#008080', 'thistle': '#d8bfd8', 'tomato': '#ff6347', 'turquoise': '#40e0d0', 'violet': '#ee82ee', 'wheat': '#f5deb3', 'white': '#ffffff', 'whitesmoke': '#f5f5f5', 'yellow': '#ffff00', 'yellowgreen': '#9acd32'} def __init__(self, values, mode='rgb', alpha=1.0, wref=_DEFAULT_WREF): '''Instantiate a new grapefruit.Color object. Parameters: :values: The values of this color, in the specified representation. :mode: The representation mode used for values. :alpha: the alpha value (transparency) of this color. :wref: The whitepoint reference, default is 2° D65. ''' if not(isinstance(values, tuple)): raise TypeError, 'values must be a tuple' if mode=='rgb': self.__rgb = values self.__hsl = Color.RgbToHsl(*values) elif mode=='hsl': self.__hsl = values self.__rgb = Color.HslToRgb(*values) else: raise ValueError('Invalid color mode: ' + mode) self.__a = alpha self.__wref = wref def __ne__(self, other): return not self.__eq__(other) def __eq__(self, other): try: if isinstance(other, Color): return (self.__rgb==other.__rgb) and (self.__a==other.__a) if len(other) != 4: return False rgba = self.__rgb + (self.__a,) return reduce(lambda x, y: x and (y[0]==y[1]), zip(rgba, other), True) except TypeError: return False except AttributeError: return False def __repr__(self): return str(self.__rgb + (self.__a,)) def __str__(self): '''A string representation of this grapefruit.Color instance. Returns: The RGBA representation of this grapefruit.Color instance. ''' return '(%g, %g, %g, %g)' % (self.__rgb + (self.__a,)) def __unicode__(self): '''A unicode string representation of this grapefruit.Color instance. Returns: The RGBA representation of this grapefruit.Color instance. ''' return u'(%g, %g, %g, %g)' % (self.__rgb + (self.__a,)) def __iter__(self): return iter(self.__rgb + (self.__a,)) def __len__(self): return 4 @staticmethod def RgbToHsl(r, g, b): '''Convert the color from RGB coordinates to HSL. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (h, s, l) tuple in the range: h[0...360], s[0...1], l[0...1] >>> Color.RgbToHsl(1, 0.5, 0) (30.0, 1.0, 0.5) ''' minVal = min(r, g, b) # min RGB value maxVal = max(r, g, b) # max RGB value l = (maxVal + minVal) / 2.0 if minVal==maxVal: return (0.0, 0.0, l) # achromatic (gray) d = maxVal - minVal # delta RGB value if l < 0.5: s = d / (maxVal + minVal) else: s = d / (2.0 - maxVal - minVal) dr, dg, db = [(maxVal-val) / d for val in (r, g, b)] if r==maxVal: h = db - dg elif g==maxVal: h = 2.0 + dr - db else: h = 4.0 + dg - dr h = (h*60.0) % 360.0 return (h, s, l) @staticmethod def _HueToRgb(n1, n2, h): h %= 6.0 if h < 1.0: return n1 + ((n2-n1) * h) if h < 3.0: return n2 if h < 4.0: return n1 + ((n2-n1) * (4.0 - h)) return n1 @staticmethod def HslToRgb(h, s, l): '''Convert the color from HSL coordinates to RGB. Parameters: :h: The Hue component value [0...1] :s: The Saturation component value [0...1] :l: The Lightness component value [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> Color.HslToRgb(30.0, 1.0, 0.5) (1.0, 0.5, 0.0) ''' if s==0: return (l, l, l) # achromatic (gray) if l<0.5: n2 = l * (1.0 + s) else: n2 = l+s - (l*s) n1 = (2.0 * l) - n2 h /= 60.0 hueToRgb = Color._HueToRgb r = hueToRgb(n1, n2, h + 2) g = hueToRgb(n1, n2, h) b = hueToRgb(n1, n2, h - 2) return (r, g, b) @staticmethod def RgbToHsv(r, g, b): '''Convert the color from RGB coordinates to HSV. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (h, s, v) tuple in the range: h[0...360], s[0...1], v[0...1] >>> Color.RgbToHsv(1, 0.5, 0) (30.0, 1, 1) ''' v = max(r, g, b) d = v - min(r, g, b) if d==0: return (0.0, 0.0, v) s = d / v dr, dg, db = [(v - val) / d for val in (r, g, b)] if r==v: h = db - dg # between yellow & magenta elif g==v: h = 2.0 + dr - db # between cyan & yellow else: # b==v h = 4.0 + dg - dr # between magenta & cyan h = (h*60.0) % 360.0 return (h, s, v) @staticmethod def HsvToRgb(h, s, v): '''Convert the color from RGB coordinates to HSV. Parameters: :h: The Hus component value [0...1] :s: The Saturation component value [0...1] :v: The Value component [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> Color.HslToRgb(30.0, 1.0, 0.5) (1.0, 0.5, 0.0) ''' if s==0: return (v, v, v) # achromatic (gray) h /= 60.0 h = h % 6.0 i = int(h) f = h - i if not(i&1): f = 1-f # if i is even m = v * (1.0 - s) n = v * (1.0 - (s * f)) if i==0: return (v, n, m) if i==1: return (n, v, m) if i==2: return (m, v, n) if i==3: return (m, n, v) if i==4: return (n, m, v) return (v, m, n) @staticmethod def RgbToYiq(r, g, b): '''Convert the color from RGB to YIQ. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (y, i, q) tuple in the range: y[0...1], i[0...1], q[0...1] >>> '(%g, %g, %g)' % Color.RgbToYiq(1, 0.5, 0) '(0.592263, 0.458874, -0.0499818)' ''' y = (r * 0.29895808) + (g * 0.58660979) + (b *0.11443213) i = (r * 0.59590296) - (g * 0.27405705) - (b *0.32184591) q = (r * 0.21133576) - (g * 0.52263517) + (b *0.31129940) return (y, i, q) @staticmethod def YiqToRgb(y, i, q): '''Convert the color from YIQ coordinates to RGB. Parameters: :y: Tte Y component value [0...1] :i: The I component value [0...1] :q: The Q component value [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.YiqToRgb(0.592263, 0.458874, -0.0499818) '(1, 0.5, 5.442e-007)' ''' r = y + (i * 0.9562) + (q * 0.6210) g = y - (i * 0.2717) - (q * 0.6485) b = y - (i * 1.1053) + (q * 1.7020) return (r, g, b) @staticmethod def RgbToYuv(r, g, b): '''Convert the color from RGB coordinates to YUV. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (y, u, v) tuple in the range: y[0...1], u[-0.436...0.436], v[-0.615...0.615] >>> '(%g, %g, %g)' % Color.RgbToYuv(1, 0.5, 0) '(0.5925, -0.29156, 0.357505)' ''' y = (r * 0.29900) + (g * 0.58700) + (b * 0.11400) u = -(r * 0.14713) - (g * 0.28886) + (b * 0.43600) v = (r * 0.61500) - (g * 0.51499) - (b * 0.10001) return (y, u, v) @staticmethod def YuvToRgb(y, u, v): '''Convert the color from YUV coordinates to RGB. Parameters: :y: The Y component value [0...1] :u: The U component value [-0.436...0.436] :v: The V component value [-0.615...0.615] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.YuvToRgb(0.5925, -0.2916, 0.3575) '(0.999989, 0.500015, -6.3276e-005)' ''' r = y + (v * 1.13983) g = y - (u * 0.39465) - (v * 0.58060) b = y + (u * 2.03211) return (r, g, b) @staticmethod def RgbToXyz(r, g, b): '''Convert the color from sRGB to CIE XYZ. The methods assumes that the RGB coordinates are given in the sRGB colorspace (D65). .. note:: Compensation for the sRGB gamma correction is applied before converting. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (x, y, z) tuple in the range: x[0...1], y[0...1], z[0...1] >>> '(%g, %g, %g)' % Color.RgbToXyz(1, 0.5, 0) '(0.488941, 0.365682, 0.0448137)' ''' 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)] x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805) y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722) z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505) return (x, y, z) @staticmethod def XyzToRgb(x, y, z): '''Convert the color from CIE XYZ coordinates to sRGB. .. note:: Compensation for sRGB gamma correction is applied before converting. Parameters: :x: The X component value [0...1] :y: The Y component value [0...1] :z: The Z component value [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.XyzToRgb(0.488941, 0.365682, 0.0448137) '(1, 0.5, 6.81883e-008)' ''' r = (x * 3.2406255) - (y * 1.5372080) - (z * 0.4986286) g = -(x * 0.9689307) + (y * 1.8757561) + (z * 0.0415175) b = (x * 0.0557101) - (y * 0.2040211) + (z * 1.0569959) return tuple((((v <= _srgbGammaCorrInv) and [v * 12.92] or [(1.055 * (v ** (1/2.4))) - 0.055])[0] for v in (r, g, b))) @staticmethod def XyzToLab(x, y, z, wref=_DEFAULT_WREF): '''Convert the color from CIE XYZ to CIE L*a*b*. Parameters: :x: The X component value [0...1] :y: The Y component value [0...1] :z: The Z component value [0...1] :wref: The whitepoint reference, default is 2° D65. Returns: The color as an (L, a, b) tuple in the range: L[0...100], a[-1...1], b[-1...1] >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137) '(66.9518, 0.43084, 0.739692)' >>> '(%g, %g, %g)' % Color.XyzToLab(0.488941, 0.365682, 0.0448137, Color.WHITE_REFERENCE['std_D50']) '(66.9518, 0.411663, 0.67282)' ''' # White point correction x /= wref[0] y /= wref[1] z /= wref[2] # Nonlinear distortion and linear transformation x, y, z = [((v > 0.008856) and [v**_oneThird] or [(7.787 * v) + _sixteenHundredsixteenth])[0] for v in (x, y, z)] # Vector scaling l = (116 * y) - 16 a = 5.0 * (x - y) b = 2.0 * (y - z) return (l, a, b) @staticmethod def LabToXyz(l, a, b, wref=_DEFAULT_WREF): '''Convert the color from CIE L*a*b* to CIE 1931 XYZ. Parameters: :l: The L component [0...100] :a: The a component [-1...1] :b: The a component [-1...1] :wref: The whitepoint reference, default is 2° D65. Returns: The color as an (x, y, z) tuple in the range: x[0...q], y[0...1], z[0...1] >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.43084, 0.739692) '(0.488941, 0.365682, 0.0448137)' >>> '(%g, %g, %g)' % Color.LabToXyz(66.9518, 0.411663, 0.67282, Color.WHITE_REFERENCE['std_D50']) '(0.488941, 0.365682, 0.0448138)' ''' y = (l + 16) / 116 x = (a / 5.0) + y z = y - (b / 2.0) return tuple((((v > 0.206893) and [v**3] or [(v - _sixteenHundredsixteenth) / 7.787])[0] * w for v, w in zip((x, y, z), wref))) @staticmethod def CmykToCmy(c, m, y, k): '''Convert the color from CMYK coordinates to CMY. Parameters: :c: The Cyan component value [0...1] :m: The Magenta component value [0...1] :y: The Yellow component value [0...1] :k: The Black component value [0...1] Returns: The color as an (c, m, y) tuple in the range: c[0...1], m[0...1], y[0...1] >>> '(%g, %g, %g)' % Color.CmykToCmy(1, 0.32, 0, 0.5) '(1, 0.66, 0.5)' ''' mk = 1-k return ((c*mk + k), (m*mk + k), (y*mk + k)) @staticmethod def CmyToCmyk(c, m, y): '''Convert the color from CMY coordinates to CMYK. Parameters: :c: The Cyan component value [0...1] :m: The Magenta component value [0...1] :y: The Yellow component value [0...1] Returns: The color as an (c, m, y, k) tuple in the range: c[0...1], m[0...1], y[0...1], k[0...1] >>> '(%g, %g, %g, %g)' % Color.CmyToCmyk(1, 0.66, 0.5) '(1, 0.32, 0, 0.5)' ''' k = min(c, m, y) if k==1.0: return (0.0, 0.0, 0.0, 1.0) mk = 1-k return ((c-k) / mk, (m-k) / mk, (y-k) / mk, k) @staticmethod def RgbToCmy(r, g, b): '''Convert the color from RGB coordinates to CMY. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (c, m, y) tuple in the range: c[0...1], m[0...1], y[0...1] >>> Color.RgbToCmy(1, 0.5, 0) (0, 0.5, 1) ''' return (1-r, 1-g, 1-b) @staticmethod def CmyToRgb(c, m, y): '''Convert the color from CMY coordinates to RGB. Parameters: :c: The Cyan component value [0...1] :m: The Magenta component value [0...1] :y: The Yellow component value [0...1] Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] >>> Color.CmyToRgb(0, 0.5, 1) (1, 0.5, 0) ''' return (1-c, 1-m, 1-y) @staticmethod def RgbToHtml(r, g, b): '''Convert the color from (r, g, b) to #RRGGBB. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: A CSS string representation of this color (#RRGGBB). >>> Color.RgbToHtml(1, 0.5, 0) '#ff8000' ''' return '#%02x%02x%02x' % tuple((min(round(v*255), 255) for v in (r, g, b))) @staticmethod def HtmlToRgb(html): '''Convert the HTML color to (r, g, b). Parameters: :html: the HTML definition of the color (#RRGGBB or #RGB or a color name). Returns: The color as an (r, g, b) tuple in the range: r[0...1], g[0...1], b[0...1] Throws: :ValueError: If html is neither a known color name or a hexadecimal RGB representation. >>> '(%g, %g, %g)' % Color.HtmlToRgb('#ff8000') '(1, 0.501961, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('ff8000') '(1, 0.501961, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('#f60') '(1, 0.4, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('f60') '(1, 0.4, 0)' >>> '(%g, %g, %g)' % Color.HtmlToRgb('lemonchiffon') '(1, 0.980392, 0.803922)' ''' html = html.strip().lower() if html[0]=='#': html = html[1:] elif Color.NAMED_COLOR.has_key(html): html = Color.NAMED_COLOR[html][1:] if len(html)==6: rgb = html[:2], html[2:4], html[4:] elif len(html)==3: rgb = ['%c%c' % (v,v) for v in html] else: raise ValueError, 'input #%s is not in #RRGGBB format' % html return tuple(((int(n, 16) / 255.0) for n in rgb)) @staticmethod def RgbToPil(r, g, b): '''Convert the color from RGB to a PIL-compatible integer. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: A PIL compatible integer (0xBBGGRR). >>> '0x%06x' % Color.RgbToPil(1, 0.5, 0) '0x0080ff' ''' r, g, b = [min(int(round(v*255)), 255) for v in (r, g, b)] return (b << 16) + (g << 8) + r @staticmethod def PilToRgb(pil): '''Convert the color from a PIL-compatible integer to RGB. Parameters: pil: a PIL compatible color representation (0xBBGGRR) Returns: The color as an (r, g, b) tuple in the range: the range: r: [0...1] g: [0...1] b: [0...1] >>> '(%g, %g, %g)' % Color.PilToRgb(0x0080ff) '(1, 0.501961, 0)' ''' r = 0xff & pil g = 0xff & (pil >> 8) b = 0xff & (pil >> 16) return tuple((v / 255.0 for v in (r, g, b))) @staticmethod def _WebSafeComponent(c, alt=False): '''Convert a color component to its web safe equivalent. Parameters: :c: The component value [0...1] :alt: If True, return the alternative value instead of the nearest one. Returns: The web safe equivalent of the component value. ''' # This sucks, but floating point between 0 and 1 is quite fuzzy... # So we just change the scale a while to make the equality tests # work, otherwise it gets wrong at some decimal far to the right. sc = c * 100.0 # If the color is already safe, return it straight away d = sc % 20 if d==0: return c # Get the lower and upper safe values l = sc - d u = l + 20 # Return the 'closest' value according to the alt flag if alt: if (sc-l) >= (u-sc): return l/100.0 else: return u/100.0 else: if (sc-l) >= (u-sc): return u/100.0 else: return l/100.0 @staticmethod def RgbToWebSafe(r, g, b, alt=False): '''Convert the color from RGB to 'web safe' RGB Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] :alt: If True, use the alternative color instead of the nearest one. Can be used for dithering. Returns: The color as an (r, g, b) tuple in the range: the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.RgbToWebSafe(1, 0.55, 0.0) '(1, 0.6, 0)' ''' webSafeComponent = Color._WebSafeComponent return tuple((webSafeComponent(v, alt) for v in (r, g, b))) @staticmethod def RgbToGreyscale(r, g, b): '''Convert the color from RGB to its greyscale equivalent Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] Returns: The color as an (r, g, b) tuple in the range: the range: r[0...1], g[0...1], b[0...1] >>> '(%g, %g, %g)' % Color.RgbToGreyscale(1, 0.8, 0) '(0.6, 0.6, 0.6)' ''' v = (r + g + b) / 3.0 return (v, v, v) @staticmethod def RgbToRyb(hue): '''Maps a hue on the RGB color wheel to Itten's RYB wheel. Parameters: :hue: The hue on the RGB color wheel [0...360] Returns: An approximation of the corresponding hue on Itten's RYB wheel. >>> Color.RgbToRyb(15) 26 ''' d = hue % 15 i = int(hue / 15) x0 = _RybWheel[i] x1 = _RybWheel[i+1] return x0 + (x1-x0) * d / 15 @staticmethod def RybToRgb(hue): '''Maps a hue on Itten's RYB color wheel to the standard RGB wheel. Parameters: :hue: The hue on Itten's RYB color wheel [0...360] Returns: An approximation of the corresponding hue on the standard RGB wheel. >>> Color.RybToRgb(15) 8 ''' d = hue % 15 i = int(hue / 15) x0 = _RgbWheel[i] x1 = _RgbWheel[i+1] return x0 + (x1-x0) * d / 15 @staticmethod def NewFromRgb(r, g, b, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed RGB values. Parameters: :r: The Red component value [0...1] :g: The Green component value [0...1] :b: The Blue component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> Color.NewFromRgb(1.0, 0.5, 0.0) (1.0, 0.5, 0.0, 1.0) >>> Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) (1.0, 0.5, 0.0, 0.5) ''' return Color((r, g, b), 'rgb', alpha, wref) @staticmethod def NewFromHsl(h, s, l, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed HSL values. Parameters: :h: The Hue component value [0...1] :s: The Saturation component value [0...1] :l: The Lightness component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5) (1.0, 0.5, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5, 0.5) (1.0, 0.5, 0.0, 0.5) ''' return Color((h, s, l), 'hsl', alpha, wref) @staticmethod def NewFromHsv(h, s, v, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed HSV values. Parameters: :h: The Hus component value [0...1] :s: The Saturation component value [0...1] :v: The Value component [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> Color.NewFromHsv(30, 1, 1) (1.0, 0.5, 0.0, 1.0) >>> Color.NewFromHsv(30, 1, 1, 0.5) (1.0, 0.5, 0.0, 0.5) ''' h2, s, l = Color.RgbToHsl(*Color.HsvToRgb(h, s, v)) return Color((h, s, l), 'hsl', alpha, wref) @staticmethod def NewFromYiq(y, i, q, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed YIQ values. Parameters: :y: The Y component value [0...1] :i: The I component value [0...1] :q: The Q component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05)) '(0.999902, 0.499955, -6.6905e-005, 1)' >>> str(Color.NewFromYiq(0.5922, 0.45885,-0.05, 0.5)) '(0.999902, 0.499955, -6.6905e-005, 0.5)' ''' return Color(Color.YiqToRgb(y, i, q), 'rgb', alpha, wref) @staticmethod def NewFromYuv(y, u, v, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed YUV values. Parameters: :y: The Y component value [0...1] :u: The U component value [-0.436...0.436] :v: The V component value [-0.615...0.615] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575)) '(0.999989, 0.500015, -6.3276e-005, 1)' >>> str(Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5)) '(0.999989, 0.500015, -6.3276e-005, 0.5)' ''' return Color(Color.YuvToRgb(y, u, v), 'rgb', alpha, wref) @staticmethod def NewFromXyz(x, y, z, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed CIE-XYZ values. Parameters: :x: The Red component value [0...1] :y: The Green component value [0...1] :z: The Blue component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137)) '(1, 0.5, 6.81883e-008, 1)' >>> str(Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5)) '(1, 0.5, 6.81883e-008, 0.5)' ''' return Color(Color.XyzToRgb(x, y, z), 'rgb', alpha, wref) @staticmethod def NewFromLab(l, a, b, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed CIE-LAB values. Parameters: :l: The L component [0...100] :a: The a component [-1...1] :b: The a component [-1...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692)) '(1, 0.5, 1.09491e-008, 1)' >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=Color.WHITE_REFERENCE['std_D50'])) '(1.01238, 0.492011, -0.14311, 1)' >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5)) '(1, 0.5, 1.09491e-008, 0.5)' >>> str(Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, Color.WHITE_REFERENCE['std_D50'])) '(1.01238, 0.492011, -0.14311, 0.5)' ''' return Color(Color.XyzToRgb(*Color.LabToXyz(l, a, b, wref)), 'rgb', alpha, wref) @staticmethod def NewFromCmy(c, m, y, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed CMY values. Parameters: :c: The Cyan component value [0...1] :m: The Magenta component value [0...1] :y: The Yellow component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> Color.NewFromCmy(0, 0.5, 1) (1, 0.5, 0, 1.0) >>> Color.NewFromCmy(0, 0.5, 1, 0.5) (1, 0.5, 0, 0.5) ''' return Color(Color.CmyToRgb(c, m, y), 'rgb', alpha, wref) @staticmethod def NewFromCmyk(c, m, y, k, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed CMYK values. Parameters: :c: The Cyan component value [0...1] :m: The Magenta component value [0...1] :y: The Yellow component value [0...1] :k: The Black component value [0...1] :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5)) '(0, 0.34, 0.5, 1)' >>> str(Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5)) '(0, 0.34, 0.5, 0.5)' ''' return Color(Color.CmyToRgb(*Color.CmykToCmy(c, m, y, k)), 'rgb', alpha, wref) @staticmethod def NewFromHtml(html, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed HTML color definition. Parameters: :html: The HTML definition of the color (#RRGGBB or #RGB or a color name). :alpha: The color transparency [0...1], default is opaque. :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromHtml('#ff8000')) '(1, 0.501961, 0, 1)' >>> str(Color.NewFromHtml('ff8000')) '(1, 0.501961, 0, 1)' >>> str(Color.NewFromHtml('#f60')) '(1, 0.4, 0, 1)' >>> str(Color.NewFromHtml('f60')) '(1, 0.4, 0, 1)' >>> str(Color.NewFromHtml('lemonchiffon')) '(1, 0.980392, 0.803922, 1)' >>> str(Color.NewFromHtml('#ff8000', 0.5)) '(1, 0.501961, 0, 0.5)' ''' return Color(Color.HtmlToRgb(html), 'rgb', alpha, wref) @staticmethod def NewFromPil(pil, alpha=1.0, wref=_DEFAULT_WREF): '''Create a new instance based on the specifed PIL color. Parameters: :pil: A PIL compatible color representation (0xBBGGRR) :alpha: The color transparency [0...1], default is opaque :wref: The whitepoint reference, default is 2° D65. Returns: A grapefruit.Color instance. >>> str(Color.NewFromPil(0x0080ff)) '(1, 0.501961, 0, 1)' >>> str(Color.NewFromPil(0x0080ff, 0.5)) '(1, 0.501961, 0, 0.5)' ''' return Color(Color.PilToRgb(pil), 'rgb', alpha, wref) def __GetAlpha(self): return self.__a alpha = property(fget=__GetAlpha, doc='The transparency of this color. 0.0 is transparent and 1.0 is fully opaque.') def __GetWRef(self): return self.__wref whiteRef = property(fget=__GetWRef, doc='the white reference point of this color.') def __GetRGB(self): return self.__rgb rgb = property(fget=__GetRGB, doc='The RGB values of this Color.') def __GetHue(self): return self.__hsl[0] hue = property(fget=__GetHue, doc='The hue of this color.') def __GetHSL(self): return self.__hsl hsl = property(fget=__GetHSL, doc='The HSL values of this Color.') def __GetHSV(self): h, s, v = Color.RgbToHsv(*self.__rgb) return (self.__hsl[0], s, v) hsv = property(fget=__GetHSV, doc='The HSV values of this Color.') def __GetYIQ(self): return Color.RgbToYiq(*self.__rgb) yiq = property(fget=__GetYIQ, doc='The YIQ values of this Color.') def __GetYUV(self): return Color.RgbToYuv(*self.__rgb) yuv = property(fget=__GetYUV, doc='The YUV values of this Color.') def __GetXYZ(self): return Color.RgbToXyz(*self.__rgb) xyz = property(fget=__GetXYZ, doc='The CIE-XYZ values of this Color.') def __GetLAB(self): return Color.XyzToLab(wref=self.__wref, *Color.RgbToXyz(*self.__rgb)) lab = property(fget=__GetLAB, doc='The CIE-LAB values of this Color.') def __GetCMY(self): return Color.RgbToCmy(*self.__rgb) cmy = property(fget=__GetCMY, doc='The CMY values of this Color.') def __GetCMYK(self): return Color.CmyToCmyk(*Color.RgbToCmy(*self.__rgb)) cmyk = property(fget=__GetCMYK, doc='The CMYK values of this Color.') def __GetHTML(self): return Color.RgbToHtml(*self.__rgb) html = property(fget=__GetHTML, doc='This Color as an HTML color definition.') def __GetPIL(self): return Color.RgbToPil(*self.__rgb) pil = property(fget=__GetPIL, doc='This Color as a PIL compatible value.') def __GetwebSafe(self): return Color.RgbToWebSafe(*self.__rgb) webSafe = property(fget=__GetwebSafe, doc='The web safe color nearest to this one (RGB).') def __GetGreyscale(self): return Color.RgbToGreyscale(*self.rgb) greyscale = property(fget=__GetGreyscale, doc='The greyscale equivalent to this color (RGB).') def ColorWithAlpha(self, alpha): '''Create a new instance based on this one with a new alpha value. Parameters: :alpha: The transparency of the new color [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromRgb(1.0, 0.5, 0.0, 1.0).ColorWithAlpha(0.5) (1.0, 0.5, 0.0, 0.5) ''' return Color(self.__rgb, 'rgb', alpha, self.__wref) def ColorWithWhiteRef(self, wref, labAsRef=False): '''Create a new instance based on this one with a new white reference. Parameters: :wref: The whitepoint reference. :labAsRef: If True, the L*a*b* values of the current instance are used as reference for the new color; otherwise, the RGB values are used as reference. Returns: A grapefruit.Color instance. >>> c = Color.NewFromRgb(1.0, 0.5, 0.0, 1.0, Color.WHITE_REFERENCE['std_D65']) >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50']) >>> c2.rgb (1.0, 0.5, 0.0) >>> '(%g, %g, %g)' % c2.whiteRef '(0.96721, 1, 0.81428)' >>> c2 = c.ColorWithWhiteRef(Color.WHITE_REFERENCE['sup_D50'], labAsRef=True) >>> '(%g, %g, %g)' % c2.rgb '(1.01463, 0.490339, -0.148131)' >>> '(%g, %g, %g)' % c2.whiteRef '(0.96721, 1, 0.81428)' >>> '(%g, %g, %g)' % c.lab '(66.9518, 0.43084, 0.739692)' >>> '(%g, %g, %g)' % c2.lab '(66.9518, 0.43084, 0.739693)' ''' if labAsRef: l, a, b = self.__GetLAB() return Color.NewFromLab(l, a, b, self.__a, wref) else: return Color(self.__rgb, 'rgb', self.__a, wref) def ColorWithHue(self, hue): '''Create a new instance based on this one with a new hue. Parameters: :hue: The hue of the new color [0...360]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60) (1.0, 1.0, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ColorWithHue(60).hsl (60, 1, 0.5) ''' h, s, l = self.__hsl return Color((hue, s, l), 'hsl', self.__a, self.__wref) def ColorWithSaturation(self, saturation): '''Create a new instance based on this one with a new saturation value. .. note:: The saturation is defined for the HSL mode. Parameters: :saturation: The saturation of the new color [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5) (0.75, 0.5, 0.25, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ColorWithSaturation(0.5).hsl (30, 0.5, 0.5) ''' h, s, l = self.__hsl return Color((h, saturation, l), 'hsl', self.__a, self.__wref) def ColorWithLightness(self, lightness): '''Create a new instance based on this one with a new lightness value. Parameters: :lightness: The lightness of the new color [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25) (0.5, 0.25, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ColorWithLightness(0.25).hsl (30, 1, 0.25) ''' h, s, l = self.__hsl return Color((h, s, lightness), 'hsl', self.__a, self.__wref) def DarkerColor(self, level): '''Create a new instance based on this one but darker. Parameters: :level: The amount by which the color should be darkened to produce the new one [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25) (0.5, 0.25, 0.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).DarkerColor(0.25).hsl (30, 1, 0.25) ''' h, s, l = self.__hsl return Color((h, s, max(l - level, 0)), 'hsl', self.__a, self.__wref) def LighterColor(self, level): '''Create a new instance based on this one but lighter. Parameters: :level: The amount by which the color should be lightened to produce the new one [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25) (1.0, 0.75, 0.5, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).LighterColor(0.25).hsl (30, 1, 0.75) ''' h, s, l = self.__hsl return Color((h, s, min(l + level, 1)), 'hsl', self.__a, self.__wref) def Saturate(self, level): '''Create a new instance based on this one but more saturated. Parameters: :level: The amount by which the color should be saturated to produce the new one [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25) (0.875, 0.5, 0.125, 1.0) >>> Color.NewFromHsl(30, 0.5, 0.5).Saturate(0.25).hsl (30, 0.75, 0.5) ''' h, s, l = self.__hsl return Color((h, min(s + level, 1), l), 'hsl', self.__a, self.__wref) def Desaturate(self, level): '''Create a new instance based on this one but less saturated. Parameters: :level: The amount by which the color should be desaturated to produce the new one [0...1]. Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25) (0.625, 0.5, 0.375, 1.0) >>> Color.NewFromHsl(30, 0.5, 0.5).Desaturate(0.25).hsl (30, 0.25, 0.5) ''' h, s, l = self.__hsl return Color((h, max(s - level, 0), l), 'hsl', self.__a, self.__wref) def WebSafeDither(self): '''Return the two websafe colors nearest to this one. Returns: A tuple of two grapefruit.Color instances which are the two web safe colors closest this one. >>> c = Color.NewFromRgb(1.0, 0.45, 0.0) >>> c1, c2 = c.WebSafeDither() >>> str(c1) '(1, 0.4, 0, 1)' >>> str(c2) '(1, 0.6, 0, 1)' ''' return ( Color(Color.RgbToWebSafe(*self.__rgb), 'rgb', self.__a, self.__wref), Color(Color.RgbToWebSafe(alt=True, *self.__rgb), 'rgb', self.__a, self.__wref)) def Gradient(self, target, steps=100): '''Create a list with the gradient colors between this and the other color. Parameters: :target: The grapefruit.Color at the other end of the gradient. :steps: The number of gradients steps to create. Returns: A list of grapefruit.Color instances. >>> c1 = Color.NewFromRgb(1.0, 0.0, 0.0, alpha=1) >>> c2 = Color.NewFromRgb(0.0, 1.0, 0.0, alpha=0) >>> c1.Gradient(c2, 3) [(0.75, 0.25, 0.0, 0.75), (0.5, 0.5, 0.0, 0.5), (0.25, 0.75, 0.0, 0.25)] ''' gradient = [] rgba1 = self.__rgb + (self.__a,) rgba2 = target.__rgb + (target.__a,) steps += 1 for n in xrange(1, steps): d = 1.0*n/steps r = (rgba1[0]*(1-d)) + (rgba2[0]*d) g = (rgba1[1]*(1-d)) + (rgba2[1]*d) b = (rgba1[2]*(1-d)) + (rgba2[2]*d) a = (rgba1[3]*(1-d)) + (rgba2[3]*d) gradient.append(Color((r, g, b), 'rgb', a, self.__wref)) return gradient def ComplementaryColor(self, mode='ryb'): '''Create a new instance which is the complementary color of this one. Parameters: :mode: Select which color wheel to use for the generation (ryb/rgb). Returns: A grapefruit.Color instance. >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor() (0.0, 0.5, 1.0, 1.0) >>> Color.NewFromHsl(30, 1, 0.5).ComplementaryColor().hsl (210, 1, 0.5) ''' h, s, l = self.__hsl if mode == 'ryb': h = Color.RgbToRyb(h) h = (h+180)%360 if mode == 'ryb': h = Color.RybToRgb(h) return Color((h, s, l), 'hsl', self.__a, self.__wref) def MonochromeScheme(self): '''Return 4 colors in the same hue with varying saturation/lightness. Returns: A tuple of 4 grapefruit.Color in the same hue as this one, with varying saturation/lightness. >>> c = Color.NewFromHsl(30, 0.5, 0.5) >>> ['(%g, %g, %g)' % clr.hsl for clr in c.MonochromeScheme()] ['(30, 0.2, 0.8)', '(30, 0.5, 0.3)', '(30, 0.2, 0.6)', '(30, 0.5, 0.8)'] ''' def _wrap(x, min, thres, plus): if (x-min) < thres: return x + plus else: return x-min h, s, l = self.__hsl s1 = _wrap(s, 0.3, 0.1, 0.3) l1 = _wrap(l, 0.5, 0.2, 0.3) s2 = s l2 = _wrap(l, 0.2, 0.2, 0.6) s3 = s1 l3 = max(0.2, l + (1-l)*0.2) s4 = s l4 = _wrap(l, 0.5, 0.2, 0.3) return ( Color((h, s1, l1), 'hsl', self.__a, self.__wref), Color((h, s2, l2), 'hsl', self.__a, self.__wref), Color((h, s3, l3), 'hsl', self.__a, self.__wref), Color((h, s4, l4), 'hsl', self.__a, self.__wref)) def TriadicScheme(self, angle=120, mode='ryb'): '''Return two colors forming a triad or a split complementary with this one. Parameters: :angle: The angle between the hues of the created colors. The default value makes a triad. :mode: Select which color wheel to use for the generation (ryb/rgb). Returns: A tuple of two grapefruit.Color forming a color triad with this one or a split complementary. >>> c1 = Color.NewFromHsl(30, 1, 0.5) >>> c2, c3 = c1.TriadicScheme() >>> c2.hsl (150.0, 1, 0.5) >>> c3.hsl (270.0, 1, 0.5) >>> c2, c3 = c1.TriadicScheme(40) >>> c2.hsl (190.0, 1, 0.5) >>> c3.hsl (230.0, 1, 0.5) ''' h, s, l = self.__hsl angle = min(angle, 120) / 2.0 if mode == 'ryb': h = Color.RgbToRyb(h) h += 180 h1 = (h - angle) % 360 h2 = (h + angle) % 360 if mode == 'ryb': h1 = Color.RybToRgb(h1) h2 = Color.RybToRgb(h2) return ( Color((h1, s, l), 'hsl', self.__a, self.__wref), Color((h2, s, l), 'hsl', self.__a, self.__wref)) def TetradicScheme(self, angle=30, mode='ryb'): '''Return three colors froming a tetrad with this one. Parameters: :angle: The angle to substract from the adjacent colors hues [-90...90]. You can use an angle of zero to generate a square tetrad. :mode: Select which color wheel to use for the generation (ryb/rgb). Returns: A tuple of three grapefruit.Color forming a color tetrad with this one. >>> col = Color.NewFromHsl(30, 1, 0.5) >>> [c.hsl for c in col.TetradicScheme(mode='rgb', angle=30)] [(90, 1, 0.5), (210, 1, 0.5), (270, 1, 0.5)] ''' h, s, l = self.__hsl if mode == 'ryb': h = Color.RgbToRyb(h) h1 = (h + 90 - angle) % 360 h2 = (h + 180) % 360 h3 = (h + 270 - angle) % 360 if mode == 'ryb': h1 = Color.RybToRgb(h1) h2 = Color.RybToRgb(h2) h3 = Color.RybToRgb(h3) return ( Color((h1, s, l), 'hsl', self.__a, self.__wref), Color((h2, s, l), 'hsl', self.__a, self.__wref), Color((h3, s, l), 'hsl', self.__a, self.__wref)) def AnalogousScheme(self, angle=30, mode='ryb'): '''Return two colors analogous to this one. Args: :angle: The angle between the hues of the created colors and this one. :mode: Select which color wheel to use for the generation (ryb/rgb). Returns: A tuple of grapefruit.Colors analogous to this one. >>> c1 = Color.NewFromHsl(30, 1, 0.5) >>> c2, c3 = c1.AnalogousScheme() >>> c2.hsl (330, 1, 0.5) >>> c3.hsl (90, 1, 0.5) >>> c2, c3 = c1.AnalogousScheme(10) >>> c2.hsl (20, 1, 0.5) >>> c3.hsl (40, 1, 0.5) ''' h, s, l = self.__hsl if mode == 'ryb': h = Color.RgbToRyb(h) h += 360 h1 = (h - angle) % 360 h2 = (h + angle) % 360 if mode == 'ryb': h1 = Color.RybToRgb(h1) h2 = Color.RybToRgb(h2) return (Color((h1, s, l), 'hsl', self.__a, self.__wref), Color((h2, s, l), 'hsl', self.__a, self.__wref)) def AlphaBlend(self, other): '''Alpha-blend this color on the other one. Args: :other: The grapefruit.Color to alpha-blend with this one. Returns: A grapefruit.Color instance which is the result of alpha-blending this color on the other one. >>> c1 = Color.NewFromRgb(1, 0.5, 0, 0.2) >>> c2 = Color.NewFromRgb(1, 1, 1, 0.8) >>> c3 = c1.AlphaBlend(c2) >>> str(c3) '(1, 0.875, 0.75, 0.84)' ''' # get final alpha channel fa = self.__a + other.__a - (self.__a * other.__a) # get percentage of source alpha compared to final alpha if fa==0: sa = 0 else: sa = min(1.0, self.__a/other.__a) # destination percentage is just the additive inverse da = 1.0 - sa sr, sg, sb = [v * sa for v in self.__rgb] dr, dg, db = [v * da for v in other.__rgb] return Color((sr+dr, sg+dg, sb+db), 'rgb', fa, self.__wref) def Blend(self, other, percent=0.5): '''Blend this color with the other one. Args: :other: the grapefruit.Color to blend with this one. Returns: A grapefruit.Color instance which is the result of blending this color on the other one. >>> c1 = Color.NewFromRgb(1, 0.5, 0, 0.2) >>> c2 = Color.NewFromRgb(1, 1, 1, 0.6) >>> c3 = c1.Blend(c2) >>> str(c3) '(1, 0.75, 0.5, 0.4)' ''' dest = 1.0 - percent rgb = tuple(((u * percent) + (v * dest) for u, v in zip(self.__rgb, other.__rgb))) a = (self.__a * percent) + (other.__a * dest) return Color(rgb, 'rgb', a, self.__wref) def _test(): import doctest reload(doctest) doctest.testmod() if __name__=='__main__': _test() grapefruit_test.py 777 0 0 35010 11025044740 7711 0#!/usr/bin/python # -*- coding: utf-8 -*-# # Copyright (c) 2008, Xavier Basty # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. '''Unit tests for the grapefruit module.''' # $Id: grapefruit_test.py 24 2008-05-25 16:23:22Z xbasty $ __author__ = 'xbasty@gmail.com' __version__ = '0.1a3' import unittest import grapefruit class GrapeFruitTestCase(unittest.TestCase): def failUnlessNear(self, first, second, diff=9e-5, msg=None): ''' Fail if the difference between the two objects is greater than a certain amout (default 9e-5). ''' if hasattr(first,'__iter__') and hasattr(second,'__iter__'): if len(first) != len(second): raise self.failureException, (msg or "%r != %r" % (first, second)) for f, s in zip(first, second): if abs(s-f) > diff: raise self.failureException, (msg or "%r != %r @ %f" % (first, second, diff)) elif abs(second-first) > diff: raise self.failureException, (msg or "%r != %r @ %f" % (first, second, diff)) assertNear = failUnlessNear class ConversionTest(GrapeFruitTestCase): '''Test the static color conversion methods.''' def testRgbHsl(self): self.assertNear((30.0, 1.0, 0.5), grapefruit.Color.RgbToHsl(1, 0.5, 0)) self.assertNear((20.0, 1.0, 0.625), grapefruit.Color.RgbToHsl(1, 0.5, 0.25)) #ff8040 self.assertNear((40.0, 1.0, 0.375), grapefruit.Color.RgbToHsl(0.75, 0.5, 0)) #bf8000 self.assertNear((1, 0.5, 0), grapefruit.Color.HslToRgb(30.0, 1.0, 0.5)) self.assertNear((1, 0.5, 0.25), grapefruit.Color.HslToRgb(20.0, 1.0, 0.625)) self.assertNear((0.75, 0.5, 0), grapefruit.Color.HslToRgb(40.0, 1.0, 0.375)) def testRgbHsv(self): self.assertEqual((30.0, 1.0, 1.0), grapefruit.Color.RgbToHsv(1, 0.5, 0)) self.assertEqual((1, 0.5, 0), grapefruit.Color.HsvToRgb(30.0, 1.0, 1.0)) def testRgbYiq(self): self.assertNear((0.5923, 0.4589, -0.05), grapefruit.Color.RgbToYiq(1, 0.5, 0)) self.assertNear((1, 0.5, 0), grapefruit.Color.YiqToRgb(0.5923, 0.4589, -0.05)) def testRgbYuv(self): self.assertNear((0.5925, -0.2916, 0.3575), grapefruit.Color.RgbToYuv(1, 0.5, 0)) self.assertNear((1, 0.5, 0), grapefruit.Color.YuvToRgb(0.5925, -0.2916, 0.3575)) def testRgbXyz(self): self.assertNear((0.4890, 0.3657, 0.04485), grapefruit.Color.RgbToXyz(1, 0.5, 0)) self.assertNear((1, 0.5, 0), grapefruit.Color.XyzToRgb(0.488941, 0.365682, 0.0448137)) def testXyzLab(self): self.assertNear((66.9518, 0.4308, 0.7397), grapefruit.Color.XyzToLab(0.488941, 0.365682, 0.0448137)) self.assertNear((0.4890, 0.3657, 0.0449), grapefruit.Color.LabToXyz(66.9518, 0.4308, 0.7397)) self.assertNear((66.9518, 0.4117, 0.6728), grapefruit.Color.XyzToLab(0.488941, 0.365682, 0.0448137, grapefruit.Color.WHITE_REFERENCE["std_D50"])) self.assertNear((0.4890, 0.3657, 0.0449), grapefruit.Color.LabToXyz(66.9518, 0.4117, 0.6728, grapefruit.Color.WHITE_REFERENCE["std_D50"])) def testCmykCmy(self): self.assertNear((1, 0.32, 0, 0.5), grapefruit.Color.CmyToCmyk(1.0, 0.66, 0.5)) self.assertNear((1.0, 0.66, 0.5), grapefruit.Color.CmykToCmy(1, 0.32, 0, 0.5)) def testRgbCmy(self): self.assertEqual((0, 0.5, 1), grapefruit.Color.RgbToCmy(1, 0.5, 0)) self.assertEqual((1, 0.5, 0), grapefruit.Color.CmyToRgb(0, 0.5, 1)) def testRgbHtml(self): self.assertEqual("#ff8000", grapefruit.Color.RgbToHtml(1, 0.5, 0)) self.assertNear((1.0, 0.5020, 0.0), grapefruit.Color.HtmlToRgb("#ff8000")) self.assertNear((1.0, 0.5020, 0.0), grapefruit.Color.HtmlToRgb("ff8000")) self.assertNear((1.0, 0.4, 0.0), grapefruit.Color.HtmlToRgb("#f60")) self.assertNear((1.0, 0.4, 0.0), grapefruit.Color.HtmlToRgb("f60")) self.assertNear((1.000000, 0.980392, 0.803922), grapefruit.Color.HtmlToRgb("lemonchiffon")) def testRgbPil(self): self.assertNear(0x0080ff, grapefruit.Color.RgbToPil(1, 0.5, 0)) self.assertNear((1.0, 0.5020, 0), grapefruit.Color.PilToRgb(0x0080ff)) def testWebSafeComponent(self): self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.2)) self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.25)) self.assertEqual(0.4, grapefruit.Color._WebSafeComponent(0.3)) self.assertEqual(0.4, grapefruit.Color._WebSafeComponent(0.25, True)) self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.2, True)) self.assertEqual(0.2, grapefruit.Color._WebSafeComponent(0.3, True)) def testRgbToWebSafe(self): self.assertEqual((1.0, 0.6, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.55, 0.0)) self.assertEqual((1.0, 0.4, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.55, 0.0, True)) self.assertEqual((1.0, 0.4, 0.0), grapefruit.Color.RgbToWebSafe(1, 0.5, 0.0, True)) def testRgbToGreyscale(self): self.assertEqual((0.6, 0.6, 0.6), grapefruit.Color.RgbToGreyscale(1, 0.8, 0)) class NewFromTest(GrapeFruitTestCase): '''Test the static color instanciation methods.''' def testNewFromRgb(self): c = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0) self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) c = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0, 0.5) self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) def testNewFromHsl(self): c = grapefruit.Color.NewFromHsl(30, 1, 0.5) self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) c = grapefruit.Color.NewFromHsl(30, 1, 0.5, 0.5) self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) def testNewFromHsv(self): c = grapefruit.Color.NewFromHsv(30, 1, 1) self.assertEqual(c, (1.0, 0.5, 0.0, 1.0)) c = grapefruit.Color.NewFromHsv(30, 1, 1, 0.5) self.assertEqual(c, (1.0, 0.5, 0.0, 0.5)) def testNewFromYiq(self): c = grapefruit.Color.NewFromYiq(0.5923, 0.4589, -0.0499818) self.assertNear(c, (1, 0.5, 0, 1)) c = grapefruit.Color.NewFromYiq(0.5923, 0.4589,-0.05, 0.5) self.assertNear(c, (1, 0.5, 0, 0.5)) def testNewFromYuv(self): c = grapefruit.Color.NewFromYuv(0.5925, -0.2916, 0.3575) self.assertNear(c, (1, 0.5, 0, 1)) c = grapefruit.Color.NewFromYuv(0.5925, -0.2916, 0.3575, 0.5) self.assertNear(c, (1, 0.5, 0, 0.5)) def testNewFromXyz(self): c = grapefruit.Color.NewFromXyz(0.488941, 0.365682, 0.0448137) self.assertNear(c, (1, 0.5, 0, 1)) c = grapefruit.Color.NewFromXyz(0.488941, 0.365682, 0.0448137, 0.5) self.assertNear(c, (1, 0.5, 0, 0.5)) def testNewFromLab(self): c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692) self.assertNear(c, (1, 0.5, 0, 1)) c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, wref=grapefruit.Color.WHITE_REFERENCE["std_D50"]) self.assertNear(c, (1.0123754, 0.492012, -0.143110, 1)) c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5) self.assertNear(c, (1, 0.5, 0, 0.5)) c = grapefruit.Color.NewFromLab(66.9518, 0.43084, 0.739692, 0.5, grapefruit.Color.WHITE_REFERENCE["std_D50"]) self.assertNear(c, (1.0123754, 0.492012, -0.143110, 0.5)) def testNewFromCmy(self): c = grapefruit.Color.NewFromCmy(0, 0.5, 1) self.assertEqual(c, (1, 0.5, 0, 1.0)) c = grapefruit.Color.NewFromCmy(0, 0.5, 1, 0.5) self.assertEqual(c, (1, 0.5, 0, 0.5)) def testNewFromCmyk(self): c = grapefruit.Color.NewFromCmyk(1, 0.32, 0, 0.5) self.assertNear(c, (0, 0.34, 0.5, 1)) c = grapefruit.Color.NewFromCmyk(1, 0.32, 0, 0.5, 0.5) self.assertNear(c, (0, 0.34, 0.5, 0.5)) def testNewFromHtml(self): c = grapefruit.Color.NewFromHtml("#ff8000") self.assertNear(c, (1, 0.5020, 0, 1)) c = grapefruit.Color.NewFromHtml("ff8000") self.assertNear(c, (1, 0.5020, 0, 1)) c = grapefruit.Color.NewFromHtml("#f60") self.assertNear(c, (1, 0.4, 0, 1)) c = grapefruit.Color.NewFromHtml("f60") self.assertNear(c, (1, 0.4, 0, 1)) c = grapefruit.Color.NewFromHtml("lemonchiffon") self.assertNear(c, (1, 0.9804, 0.8039, 1)) c = grapefruit.Color.NewFromHtml("#ff8000", 0.5) self.assertNear(c, (1, 0.5020, 0, 0.5)) def testNewFromPil(self): c = grapefruit.Color.NewFromPil(0x0080ff) self.assertNear(c, (1, 0.5020, 0, 1)) c = grapefruit.Color.NewFromPil(0x0080ff, 0.5) self.assertNear(c, (1, 0.5020, 0, 0.5)) class ColorTest(GrapeFruitTestCase): def setUp(self): self.rgbCol = grapefruit.Color.NewFromRgb(1.0, 0.5, 0.0) self.hslCol = grapefruit.Color.NewFromHsl(30, 1, 0.5) self.hslCol2 = grapefruit.Color.NewFromHsl(30, 0.5, 0.5) def testInit(self): self.assertEqual(grapefruit.Color((1.0, 0.5, 0.0)), (1.0, 0.5, 0.0, 1.0)) self.assertEqual(grapefruit.Color((1.0, 0.5, 0.0), mode='rgb'), (1.0, 0.5, 0.0, 1.0)) self.assertEqual(grapefruit.Color((30, 1, 0.5), mode='hsl'), (1.0, 0.5, 0.0, 1.0)) self.assertRaises(ValueError, grapefruit.Color, (30, 1, 0.5), 'hsv') def testEq(self): self.assertEqual(self.rgbCol, self.hslCol) self.assertEqual(self.rgbCol, (1.0, 0.5, 0.0, 1.0)) self.assertEqual(self.rgbCol, [1.0, 0.5, 0.0, 1.0]) self.assertEqual([1.0, 0.5, 0.0, 1.0], self.rgbCol) self.assertNotEqual(self.rgbCol, '(1.0, 0.5, 0.0, 1.0)') def testRepr(self): self.assertEqual(repr(self.rgbCol), '(1.0, 0.5, 0.0, 1.0)') self.assertEqual(repr(self.hslCol), '(1.0, 0.5, 0.0, 1.0)') def testStr(self): self.assertEqual(str(self.rgbCol), '(1, 0.5, 0, 1)') self.assertEqual(str(self.hslCol), '(1, 0.5, 0, 1)') def testIter(self): self.assertEqual([1, 0.5, 0, 1], list(iter(self.rgbCol))) def testProperties(self): self.assertEqual(self.rgbCol.alpha, 1.0) self.assertEqual(self.rgbCol.whiteRef, grapefruit.Color.WHITE_REFERENCE['std_D65']) self.assertEqual(self.rgbCol.rgb, (1, 0.5, 0)) self.assertEqual(self.hslCol.hue, 30) self.assertEqual(self.rgbCol.hsl, (30, 1, 0.5)) self.assertEqual(self.rgbCol.hsv, (30, 1, 1)) self.assertNear(self.rgbCol.yiq, (0.5923, 0.4589, -0.05)) self.assertNear(self.rgbCol.yuv, (0.5925, -0.2916, 0.3575)) self.assertNear(self.rgbCol.xyz, (0.4890, 0.3657, 0.04485)) self.assertNear(self.rgbCol.lab, (66.9518, 0.4308, 0.7397)) self.assertEqual(self.rgbCol.cmy, (0, 0.5, 1)) self.assertEqual(self.rgbCol.cmyk, (0, 0.5, 1, 0)) self.assertEqual(self.rgbCol.html, '#ff8000') self.assertEqual(self.rgbCol.pil, 0x0080ff) self.assertEqual(self.rgbCol.webSafe, (1, 0.6, 0)) self.assertEqual(self.rgbCol.greyscale, (0.5, 0.5, 0.5)) c = grapefruit.Color.NewFromRgb(1, 0.5, 0, wref=grapefruit.Color.WHITE_REFERENCE['std_D50']) self.assertNear(c.lab, (66.9518, 0.4117, 0.6728)) def testColorWitgAlpha(self): self.assertEqual(self.rgbCol.ColorWithAlpha(0.5), (1, 0.5, 0, 0.5)) def testColorWithWhiteRef(self): self.assertEqual(self.hslCol.ColorWithWhiteRef((0.1, 0.2, 0.3)).whiteRef, (0.1, 0.2, 0.3)) def testColorWithHue(self): self.assertEqual(self.hslCol.ColorWithHue(60), (1.0, 1.0, 0.0, 1.0)) self.assertEqual(self.hslCol.ColorWithHue(60).hsl, (60, 1, 0.5)) def testColorWithSaturation(self): self.assertEqual(self.hslCol.ColorWithSaturation(0.5), (0.75, 0.5, 0.25, 1.0)) self.assertEqual(self.hslCol.ColorWithSaturation(0.5).hsl, (30, 0.5, 0.5)) def testColorWithLightness(self): self.assertEqual(self.hslCol.ColorWithLightness(1), (1.0, 1.0, 1.0, 1.0)) self.assertEqual(self.hslCol.ColorWithLightness(1).hsl, (30, 1.0, 1.0)) def testDarkerColor(self): self.assertNear(self.hslCol.DarkerColor(0.2), (0.6, 0.3, 0.0, 1.0)) self.assertNear(self.hslCol.DarkerColor(0.2).hsl, (30, 1, 0.3)) def testLighterColor(self): self.assertNear(self.hslCol.LighterColor(0.2), (1.0, 0.7, 0.4, 1.0)) self.assertNear(self.hslCol.LighterColor(0.2).hsl, (30, 1, 0.7)) def testSaturate(self): self.assertNear(self.hslCol2.Saturate(0.25), (0.875, 0.5, 0.125, 1.0)) self.assertNear(self.hslCol2.Saturate(0.25).hsl, (30, 0.75, 0.5)) def testDesaturate(self): self.assertNear(self.hslCol2.Desaturate(0.25), (0.625, 0.5, 0.375, 1.0)) self.assertNear(self.hslCol2.Desaturate(0.25).hsl, (30, 0.25, 0.5)) def testWebSafeDither(self): dithered = ( (1.0, 0.6, 0.0, 1.0), (1.0, 0.4, 0.0, 1.0)) self.assertEqual(self.rgbCol.WebSafeDither(), dithered) def testGradient(self): gradient = [ (0.75, 0.25, 0.0, 1.0), (0.5, 0.5, 0.0, 1.0), (0.25, 0.75, 0.0, 1.0)] c1 = grapefruit.Color.NewFromRgb(1.0, 0.0, 0.0) c2 = grapefruit.Color.NewFromRgb(0.0, 1.0, 0.0) self.assertEqual(gradient, c1.Gradient(c2, 3)) def testComplementaryColor(self): self.assertEqual(self.hslCol.ComplementaryColor(mode='rgb').hsl, (210, 1, 0.5)) def testMonochromeScheme(self): monochrome = ( (0.94, 0.8, 0.66, 1.0), # hsl(30, 0.7, 0.8) (0.6, 0.3, 0.0, 1.0), # hsl(30, 1, 0.3) (0.88, 0.6, 0.32, 1.0), # hsl(30, 0.7, 0.6) (1.0, 0.8, 0.6, 1.0)) # hsl(30, 1, 0.8) scheme = self.rgbCol.MonochromeScheme() for i in xrange(len(monochrome)): self.assertNear(scheme[i], monochrome[i]) def testTriadicScheme(self): triad = ( (0.0, 1.0, 0.5, 1.0), (0.5, 0.0, 1.0, 1.0)) self.assertEqual(self.rgbCol.TriadicScheme(mode='rgb'), triad) def testTetradicScheme(self): tetrad = ( (0.5, 1.0, 0.0, 1.0), (0.0, 0.5, 1.0, 1.0), (0.5, 0.0, 1.0, 1.0)) self.assertEqual(self.rgbCol.TetradicScheme(mode='rgb'), tetrad) def testAnalogousScheme(self): scheme = ( (1.0, 0.0, 0.0, 1.0), (1.0, 1.0, 0.0, 1.0)) self.assertEqual(self.rgbCol.AnalogousScheme(mode='rgb'), scheme) def testAlphaBlend(self): c1 = grapefruit.Color.NewFromRgb(1, 0.5, 0, alpha = 0.2) c2 = grapefruit.Color.NewFromRgb(1, 1, 1, alpha = 0.8) self.assertNear(c1.AlphaBlend(c2), (1, 0.875, 0.75, 0.84)) def testBlend(self): c1 = grapefruit.Color.NewFromRgb(1, 0.5, 0, alpha = 0.2) c2 = grapefruit.Color.NewFromRgb(1, 1, 1, alpha = 0.6) self.assertEqual(c1.Blend(c2), (1, 0.75, 0.5, 0.4)) if __name__ == '__main__': unittest.main() pass LICENSE 777 0 0 24325 11015357562 5154 0Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.PKG-INFO 777 0 0 11264 11025171030 5223 0Metadata-Version: 1.0 Name: grapefruit Version: 0.1a3 Summary: A module to manipulate color information easily. Home-page: http://code.google.com/p/grapefruit/ Author: Xavier Basty Author-email: xbasty@gmail.com License: Apache License 2.0 Download-URL: http://grapefruit.googlecode.com/files/grapefruit-0.1a3.tar.gz Description: ===================== README for GrapeFruit ===================== Installing ============ **From the sources:** Download the latest grapefruit library from: http://code.google.com/p/grapefruit/ Untar the source distribution and run:: $ python setup.py build $ python setup.py install Testing ========= With setuptools installed:: $ python setup.py test Without setuptools installed:: $ python grapefruit_test.py Getting the code ================== View the trunk at: http://grapefruit.googlecode.com/svn/trunk/ Check out the latest development version anonymously with:: $ svn checkout http://grapefruit.googlecode.com/svn/trunk/ GrapeFruit Documentation =============== You can download a compiled version of the documentation at: http://http://code.google.com/p/grapefruit/downloads/list?q=label:Type-Docs The documentation is generated from reStructuredText sources by Sphinx. If you need to build it, go into the doc folder and run:: make Or, if you're running windows:: makedoc.cmd License ========= :: Copyright (c) 2008, Xavier Basty Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 2008-06-15 - Released 0.1a3 - Added Gradient 2008-06-01 - Fixed overflow in alpha blending. 2008-05-29 - Added Saturate/Desaturate. - Added MonochromeScheme - Added the mode parameter to the generation methods to choose the color wheel use for the generation (ryb/rgb). 2008-05-28 - Added the RGB<->RYB hue conversion. - Added an angle parameter to the tetrad scheme to control the shape of the rectangle. 2008-05-27 - Released 0.1a2 2008-05-24 - Fixed the HSL->RGB conversion (the modulo in the hue conversion was 60 instead of 6.0!) Updated the unit tests (which were wrong!) 2008-05-24 Released 0.1a1 - Convert the documentation to Sphinx - Completed the unit tests - Fixed some stupid typos 2008-05-22 - Refactored pretty much everything to more standard "Python coding style". - Replaced the global variables by Color properties. - Moved the module functions to static methods of Color. - Completed the CIE white point dictionary to include all the standard illuminants. - Added doctest for all the functions. - Fixed the conversions factors to get better results (more exact and more symmetric). - Changed the range of the L component from [0~1] to [0~100] (as it should have been). - Added packaging data and setup. - Changed the structure of the unit tests. 2008-05-08 Released 0.1a0 - Initial checkin of grapefruit Keywords: color conversion Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Multimedia :: Graphics README 777 0 0 3160 11016061406 4767 0===================== README for GrapeFruit ===================== Installing ============ **From the sources:** Download the latest grapefruit library from: http://code.google.com/p/grapefruit/ Untar the source distribution and run:: $ python setup.py build $ python setup.py install Testing ========= With setuptools installed:: $ python setup.py test Without setuptools installed:: $ python grapefruit_test.py Getting the code ================== View the trunk at: http://grapefruit.googlecode.com/svn/trunk/ Check out the latest development version anonymously with:: $ svn checkout http://grapefruit.googlecode.com/svn/trunk/ GrapeFruit Documentation =============== You can download a compiled version of the documentation at: http://http://code.google.com/p/grapefruit/downloads/list?q=label:Type-Docs The documentation is generated from reStructuredText sources by Sphinx. If you need to build it, go into the doc folder and run:: make Or, if you're running windows:: makedoc.cmd License ========= :: Copyright (c) 2008, Xavier Basty Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. setup.cfg 777 0 0 100 11025171030 5672 0[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 setup.py 777 0 0 4463 11017713446 5641 0#!/usr/bin/python # -*- coding: utf-8 -*-# # Copyright (c) 2008, Xavier Basty # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. '''GrapeFruit setup and build script.''' # $Id: setup.py 24 2008-05-25 16:23:22Z xbasty $ __author__ = 'Xavier Basty ' __version__ = '0.1a3' # The base package metadata to be used by both distutils and setuptools METADATA = dict( name = "grapefruit", version = __version__, py_modules = ['grapefruit'], author = 'Xavier Basty', author_email = 'xbasty@gmail.com', description = 'A module to manipulate color information easily.', license = 'Apache License 2.0', url = 'http://code.google.com/p/grapefruit/', download_url = 'http://grapefruit.googlecode.com/files/grapefruit-%s.tar.gz' % \ __version__, keywords ='color conversion', ) # Extra package metadata to be used only if setuptools is installed SETUPTOOLS_METADATA = dict( install_requires = ['setuptools'], include_package_data = True, zip_safe = True, classifiers = [ 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Multimedia :: Graphics', ], test_suite = 'grapefruit_test.suite', ) def Read(file): return open(file).read() def BuildLongDescription(): return '\n'.join([Read('README'), Read('CHANGES')]) def Main(): # Build the long_description from the README and CHANGES files METADATA['long_description'] = BuildLongDescription() # Use setuptools if available, otherwise fallback and use distutils try: import setuptools METADATA.update(SETUPTOOLS_METADATA) setuptools.setup(**METADATA) except ImportError: import distutils.core distutils.core.setup(**METADATA) if __name__ == '__main__': Main() TODO 777 0 0 530 11017345476 4572 0GrapeFruit's TODO list ---------------------- Patches and bug reports are welcome, just please keep the style consistent with the original source. * grapefruit_test isn't much more that a copy of the doctests right now. Complete it with real unit tests that better define the module behavior. * Add sRGB <-> AdobeRGB conversion.