diff --git a/INSTALL.rst b/INSTALL.rst
new file mode 100644
index 0000000..4ae92b7
--- /dev/null
+++ b/INSTALL.rst
@@ -0,0 +1,30 @@
+=========================
+Installation instructions
+=========================
+
+colortools can be installed using pip::
+
+ $ python -m pip install colortools
+
+This command will fetch the archive and its dependencies from the internet and
+install them. You may need to pick either ``python2``` or ``python3`` in
+order to select the correct python version.
+
+If you've downloaded the tarball, unpack it, and execute::
+
+ $ python setup.py install
+
+In either case, it is possible to perform local user installs by appending the
+``--user`` option.
+
+
+Troubleshoot
+------------
+
+Windows users may find that these command will only works if typed from Python's
+installation directory.
+
+Some Linux distributions (e.g. Ubuntu) install Python without installing pip.
+Please install it before. If you don't have root privileges, download the
+get-pip.py script at https://bootstrap.pypa.io/get-pip.py and execute it as
+``python get-pip.py --user``.
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d1f819f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,14 @@
+colortools.
+Copyright (C) Fábio Macêdo Mendes
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..38e2064
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,6 @@
+include INSTALL.rst
+include README.rst
+include LICENSE
+include VERSION
+include requirements.txt
+include tasks.py
\ No newline at end of file
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..c4d316b
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,48 @@
+The :mod:`colortools` defines a :cls:`Color` object that can be used to
+represent and manipulate colors in various contexts. It was created with the
+:mod:`FGAme` game engine, but it can also be used in other contexts such as
+arts and web applications.
+
+Colors can be initialized from the RGB/RGBA components, in which each component
+is in the 0-255 range.
+
+>>> w1 = Color(255, 255, 255)
+>>> w2 = Color(255, 255, 255, 255)
+>>> w1 == w2
+True
+
+The constructor also accepts color names, hex strings or hex numbers.
+
+>>> Color('white') == Color('#FFF') == Color(0xffffff) == Color(255, 255, 255)
+True
+
+All names in CSS3.0 are accepted (see the complete list `here`).
+It is worth noting that although the *G* in RGB stands for green, the color
+with a full green value is called *lime*, while "green" is **#008000** rather than
+**#00FF00**.
+
+Usage and API
+-------------
+
+Color objects are immutable and behave like a tuple of 4 values.
+
+>>> list(Color('green'))
+[0, 80, 0, 255]
+
+It also exposes several different representations of a color using the
+appropriate attribute.
+
+>>> blue = Color('blue')
+>>> blue.rbg
+(255, 0, 0)
+
+>>> blue.rgbf
+(1.0, 0.0, 0.0)
+
+>>> blue.rgbu
+16711680
+
+Color objects also implement a few useful transformations
+
+>>> blue.darken(0.1) # darken by 10%
+Color(229, 0, 0)
\ No newline at end of file
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..6da28dd
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.1.1
\ No newline at end of file
diff --git a/boilerplate.ini b/boilerplate.ini
new file mode 100644
index 0000000..adf9f13
--- /dev/null
+++ b/boilerplate.ini
@@ -0,0 +1,54 @@
+[options]
+project = colortools
+author = Fábio Macêdo Mendes
+email = fabiomacedomendes@gmail.com
+version = 0.1.0
+license = gpl
+pyname = colortools
+
+[file hashes]
+VERSION = 5dGLTiwdwZdXde44WrQchQ==
+
+.gitignore = iu2Zn1vki51Un7bnWFivjQ==
+
+README.rst = uPdLHOwxE8pcjE53T+O1iQ==
+
+INSTALL.rst = k/F9ZlbJ3CYk9U2SVpTXNw==
+
+LICENSE = HlS8/vbcwNyIFgy6FnicyQ==
+
+tasks.py = 6GOUg08jHdzGhTHggdHrsA==
+
+setup.py = jO1VBZHTTbhmUaSvZLAF7Q==
+
+MANIFEST.in = BR29dThJGj3LpFi457HDsw==
+
+requirements.txt = 9/H6jW0guTB5y4aYQ+29FA==
+
+requirements-dev.txt = IrFEvOtt6TH8c3X1z/Qgrg==
+
+src/colortools/__init__.py = 1zLEKfdEAfCqC71HMe3ifA==
+
+src/colortools/__meta__.py = ITV3jN2HNctrB2gBnc7WNA==
+
+src/colortools/__main__.py = lmKoqR1olD5mZ7TNIYbAug==
+
+src/colortools/tests/test_colortools.py = vb8s3uOxr/85JLBtXNlJ6Q==
+
+docs/conf.py = /k5SqU8PfyrF6IJZu6cPfg==
+
+docs/index.rst = BcUr6MxiaEfZvfinE/Qx7g==
+
+docs/install.rst = uEvES6MgO5srBNLXAVqL/Q==
+
+docs/license.rst = T7JCt0IDBksEuohsOM3rxQ==
+
+docs/apidoc.rst = id1zCZJLYOZFBT5/kbmjBw==
+
+docs/warning.rst = O2SiWXTOqJBpkFGu1aW+VA==
+
+docs/make.bat = AyOyYMZZh/t1xRThRsgGOA==
+
+docs/Makefile = sHJpgsLZvsiYQotqGNXIhw==
+
+
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..ee10e24
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,216 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+ @echo "Please use \`make ' where is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/colortools.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/colortools.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/colortools"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/colortools"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
\ No newline at end of file
diff --git a/docs/apidoc.rst b/docs/apidoc.rst
new file mode 100644
index 0000000..df069bc
--- /dev/null
+++ b/docs/apidoc.rst
@@ -0,0 +1,8 @@
+=============
+API Reference
+=============
+
+API documentation for the colortools module.
+
+.. automodule:: colortools
+ :members:
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..2ce7594
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,289 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# boilerplate documentation build configuration file, created by
+# sphinx-quickstart on Thu Mar 24 05:34:39 2016.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# 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',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.viewcode',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'colortools'
+project_title = project.title()
+author = 'Fábio Macêdo Mendes'
+copyright = '2016, %s' % author
+
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+dirname = os.path.dirname(__file__)
+projdir = os.path.dirname(dirname)
+release = open(os.path.join(projdir, 'VERSION')).read().strip()
+version = '.'.join(release.split('.')[:2])
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# 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 patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# 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'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'alabaster'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = 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']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# 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_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = 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 = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr'
+#html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# Now only 'ja' uses this config value
+#html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = '%sdoc' % project
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+
+# Latex figure (float) alignment
+#'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, '%s.tex' % project, '%s Documentation' % project,
+ author, '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
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, project, '%s Documentation' % project,
+ [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, project, '%s Documentation' % project,
+ author, project, 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..3af341b
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,21 @@
+================================================================================
+Welcome to colortools's documentation!
+================================================================================
+
+.. include:: warning.rst
+
+.. include:: ../README.rst
+
+.. toctree::
+ :maxdepth: 2
+
+ Installation instructions
+ API documentation
+ License
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/install.rst b/docs/install.rst
new file mode 100644
index 0000000..b914117
--- /dev/null
+++ b/docs/install.rst
@@ -0,0 +1 @@
+.. include:: ../INSTALL.rst
\ No newline at end of file
diff --git a/docs/license.rst b/docs/license.rst
new file mode 100644
index 0000000..4836374
--- /dev/null
+++ b/docs/license.rst
@@ -0,0 +1,5 @@
+=======
+License
+=======
+
+.. include:: ../LICENSE
\ No newline at end of file
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..84ad9ce
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,263 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^` where ^ is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ echo. coverage to run coverage check of the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+REM Check if sphinx-build is available and fallback to Python version if any
+%SPHINXBUILD% 1>NUL 2>NUL
+if errorlevel 9009 goto sphinx_python
+goto sphinx_ok
+
+:sphinx_python
+
+set SPHINXBUILD=python -m sphinx.__init__
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+:sphinx_ok
+
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\colortools.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\colortools.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "coverage" (
+ %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of coverage in the sources finished, look at the ^
+results in %BUILDDIR%/coverage/python.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+:end
\ No newline at end of file
diff --git a/docs/warning.rst b/docs/warning.rst
new file mode 100644
index 0000000..f3bf051
--- /dev/null
+++ b/docs/warning.rst
@@ -0,0 +1,9 @@
+.. warning:: Beta software
+ You are using a software that has not reached a stable version yet. Please
+ beware that interfaces might change, APIs might disappear and general
+ breakage can occur before *1.0*.
+
+ If you plan to use this software for something important, please read the
+ roadmap, and the issue tracker in Github. If you are unsure about the
+ future of this project, please talk to the developers, or (better yet) get
+ involved with the development of colortools!
\ No newline at end of file
diff --git a/requirements-dev.txt b/requirements-dev.txt
new file mode 100644
index 0000000..6d33e60
--- /dev/null
+++ b/requirements-dev.txt
@@ -0,0 +1,5 @@
+-e .
+pytest
+boilerplate
+mock
+invoke
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..ecf975e
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+-e .
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..dc81e76
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,60 @@
+# -*- coding: utf8 -*-
+#
+# This file were created by Python Boilerplate. Use boilerplate to start simple
+# usable and best-practices compliant Python projects.
+#
+# Learn more about it at: http://github.com/fabiommendes/boilerplate/
+#
+
+import os
+from setuptools import setup, find_packages
+
+
+# Meta information
+name = 'colortools'
+project = 'colortools'
+author = 'Fábio Macêdo Mendes'
+version = open('VERSION').read().strip()
+dirname = os.path.dirname(__file__)
+
+# Save version and author to __meta__.py
+file_name = os.path.join(dirname, 'src', project, '__meta__.py')
+with open(file_name, 'w', encoding='utf-8') as F:
+ F.write('__version__ = %r\n'
+ '__author__ = %r\n' % (version, author))
+
+setup(
+ # Basic info
+ name=name,
+ version=version,
+ author=author,
+ author_email='fabiomacedomendes@gmail.com',
+ url='',
+ description='A short description for your project.',
+ long_description=open('README.rst').read(),
+
+ # Classifiers (see https://pypi.python.org/pypi?%3Aaction=list_classifiers)
+ classifiers=[
+ 'Development Status :: 3 - Alpha',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: GNU General Public License (GPL)',
+ 'Operating System :: POSIX',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Libraries',
+ ],
+
+ # Packages and depencies
+ package_dir={'': 'src'},
+ packages=find_packages('src'),
+ install_requires=[],
+ extras_require={
+ 'dev': [
+ 'pytest',
+ 'python-boilerplate',
+ ],
+ },
+
+ # Other configurations
+ zip_safe=False,
+ platforms='any',
+)
\ No newline at end of file
diff --git a/src/colortools/__init__.py b/src/colortools/__init__.py
new file mode 100644
index 0000000..e18f8c8
--- /dev/null
+++ b/src/colortools/__init__.py
@@ -0,0 +1,2 @@
+from .__meta__ import __author__, __version__
+from .base import *
\ No newline at end of file
diff --git a/src/colortools/__meta__.py b/src/colortools/__meta__.py
new file mode 100644
index 0000000..27b7bea
--- /dev/null
+++ b/src/colortools/__meta__.py
@@ -0,0 +1,2 @@
+__version__ = '0.1.1'
+__author__ = 'Fábio Macêdo Mendes'
diff --git a/src/colortools/base.py b/src/colortools/base.py
new file mode 100644
index 0000000..2c8da22
--- /dev/null
+++ b/src/colortools/base.py
@@ -0,0 +1,551 @@
+import random
+from functools import lru_cache
+from math import sqrt, acos
+
+__all__ = ['Color', 'colorproperty', 'rgb', 'rgba', 'COLOR_NAMES']
+
+
+class Color(object):
+ """
+ Represents a color.
+ """
+ __slots__ = ['_red', '_green', '_blue', '_alpha']
+ _CACHE = {}
+ _RANGE = (0, 1, 2, 3)
+
+ @classmethod
+ def from_rgba(cls, red=0, green=0, blue=0, alpha=255):
+ """
+ Create color from RGBa components.
+ """
+
+ new = object.__new__(cls)
+ new._fromrgba(red, green, blue, alpha)
+ return new
+
+ def __init__(self, *args, **kwargs):
+ # RGB parameters (with or without alpha)
+ if len(args) in [3, 4]:
+ self._fromrgba(*args, **kwargs)
+
+ elif len(args) == 1:
+ value, = args
+
+ # Start from another color, tuple or list of values
+ if isinstance(value, (list, tuple, Color)):
+ self._fromrgba(*value)
+
+ # Start from string (either hex or color name)
+ elif isinstance(value, str):
+ if value.startswith('#'):
+ self._fromhex(value, **kwargs)
+ elif value == 'random':
+ self._fromrgba(*[random.randint(0, 255) for _ in range(3)])
+ else:
+ self._fromname(value, **kwargs)
+
+ # Start from integer
+ elif isinstance(value, int):
+ r = (value & 0xff0000) >> 16
+ g = (value & 0xff00) >> 8
+ b = (value & 0xff) >> 0
+ self._fromrgba(r, g, b, **kwargs)
+
+ elif len(args) == 0:
+ raise NotImplemented
+ else:
+ TypeError('invalid number of arguments: %s' % len(args))
+
+ def _fromrgba(self, r=0, g=0, b=0, a=255):
+ self._red = int(r)
+ self._green = int(g)
+ self._blue = int(b)
+ self._alpha = int(a)
+
+ def _fromhex(self, value, alpha=255):
+ value = value[1:]
+ if len(value) in [3, 4]:
+ value = ''.join(c + c for c in value)
+ if len(value) == 6:
+ a = alpha
+ r, g, b = (
+ int(value[2 * i:2 * i + 2], 16) for i in range(3))
+ elif len(value) == 8:
+ r, g, b, a = (
+ int(value[2 * i:2 * i + 2], 16) for i in range(4))
+ else:
+ raise ValueError('invalid hex code: #%s' % value)
+ self._fromrgba(r, g, b, a)
+
+ def _fromname(self, value):
+ value = value.lower()
+ self._fromrgba(*self._CACHE[value].rgba)
+
+ @property
+ def red(self):
+ """
+ Red component (from 0 to 255)
+ """
+ return self._red
+
+ @property
+ def green(self):
+ """
+ Green component (from 0 to 255)
+ """
+
+ return self._green
+
+ @property
+ def blue(self):
+ """
+ Blue component (from 0 to 255)
+ """
+
+ return self._blue
+
+ @property
+ def alpha(self):
+ """
+ Alpha component (0: transparent, 255: solid color)
+ """
+
+ return self._alpha
+
+ @property
+ def hue(self):
+ """
+ Hue component (from 0 to 255).
+ """
+
+ return NotImplemented
+
+ @property
+ def saturation(self):
+ """
+ Saturation component (from 0 to 255).
+ """
+
+ return NotImplemented
+
+ @property
+ def intensity(self):
+ """
+ Intensity component (from 0 to 255).
+ """
+
+ return NotImplemented
+
+ @property
+ def rgba(self):
+ """
+ Tuple with (red, green, blue, alpha)
+ """
+ return tuple(self)
+
+ @property
+ def rgb(self):
+ """
+ Tuple with (red, green, blue)
+ """
+ return self[:3]
+
+ @property
+ def rgbf(self):
+ """
+ Floating-point version of rgb, normalized in the [0, 1] interval.
+ """
+
+ return tuple(x / 255. for x in self[:3])
+
+ @property
+ def rgbaf(self):
+ """
+ Floating-point version of rgba, normalized in the [0, 1] interval.
+ """
+
+ return tuple(x / 255. for x in self)
+
+ @property
+ def rgbau(self):
+ """
+ RGBA as a single 32 bit unsigned integer.
+ """
+
+ c = self
+ return (c[0] << 24) + (c[1] << 16) + (c[2] << 8) + c[3]
+
+ @property
+ def rgbu(self):
+ c = self
+ return (c[0] << 16) + (c[1] << 8) + c[2]
+
+ @property
+ def hsi(self):
+ return self.hsia[:-1]
+
+ @property
+ def hsia(self):
+ # (H)ue, (S)aturation, (I)ntensity
+ # ref: https://en.wikipedia.org/wiki/RGB_color_model#Nonlinearity
+ R, G, B, a = self
+
+ I = (R + G + B) / 3
+ S = 1 - min(R, G, B) / I
+
+ # Normalized
+ r, g, b = R / 255, G / 255, B / 255
+ h_numer = acos(((r - g) + (r - b)) / 2)
+ h_denom = sqrt((r - b) ** 2 + (r - b) * (g - b))
+ return h_numer / h_denom, S, I, a
+
+ @property
+ def hsiaf(self):
+ return tuple(x / 255 for x in self.hsia)
+
+ @property
+ def hsif(self):
+ return tuple(x / 255 for x in self.hsi)
+
+ def copy(self, red=None, green=None, blue=None, alpha=None, **kwds):
+ """Copia a cor possivelmente trocando o valor de alguma das componentes
+ RGBA ou componentes HSL ou HSI.
+
+ Examples
+ --------
+
+ >>> color = Color('white')
+ >>> color.copy(red=80, alpha=128)
+ Color(80, 255, 255, 128)
+ """
+ R, G, B, A = self
+
+ if red is not None:
+ R = red
+ if green is not None:
+ G = green
+ if blue is not None:
+ B = blue
+ if alpha is not None:
+ A = alpha
+ if kwds:
+ raise NotImplementedError
+
+ return Color(R, G, B, A)
+
+ # Métodos mágicos
+ def __repr__(self):
+ return 'Color%s' % (tuple(self),)
+
+ def __len__(self):
+ return 4
+
+ def __eq__(self, other):
+ try:
+ return (
+ (self._red == other._red) and
+ (self._green == other._green) and
+ (self._blue == other._blue) and
+ (self._alpha == other._alpha))
+ except (AttributeError, TypeError):
+ if len(other) == 4:
+ return all(x == y for (x, y) in zip(self, other))
+ elif len(other) == 3:
+ return ((self._alpha == 255) and
+ all(x == y for (x, y) in zip(self, other)))
+ return False
+
+ def __iter__(self):
+ yield self._red
+ yield self._green
+ yield self._blue
+ yield self._alpha
+
+ def __getitem__(self, key):
+ if isinstance(key, int):
+ if key == 0:
+ return self._red
+ elif key == 1:
+ return self._green
+ elif key == 2:
+ return self._blue
+ elif key == 3:
+ return self._alpha
+ elif key < 0:
+ key = len(self) - key
+ if key < 0:
+ raise IndexError
+ return self[key]
+ else:
+ return tuple(self[i] for i in self._RANGE[key])
+ raise IndexError(key)
+
+ def __hash__(self):
+ return hash(self.u_rgba)
+
+
+Color._CACHE.update(
+ # HTML 4.01 colors.
+ # See https://en.wikipedia.org/wiki/Web_colors
+ white=Color('#ffffff'),
+ silver=Color('#c0c0c0'),
+ gray=Color('#808080'),
+ black=Color('#000000'),
+ red=Color('#ff0000'),
+ maroon=Color('#800000'),
+ yellow=Color('#ffff00'),
+ olive=Color('#808000'),
+ lime=Color('#00ff00'),
+ green=Color('#008000'),
+ aqua=Color('#00ffff'),
+ teal=Color('#008080'),
+ blue=Color('#0000ff'),
+ navy=Color('#000080'),
+ fuschia=Color('#ff00ff'),
+ purple=Color('#800080'),
+ transparent=Color('#ffffff00'),
+ null=Color('#00000000'),
+)
+
+# SVG extended Colors
+# http://www.w3.org/TR/css3-color/#svg-color
+COLOR_NAMES = [
+ ['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'],
+ ['darkgreen', '#006400'],
+ ['darkgrey', '#A9A9A9'],
+ ['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'],
+ ['green', '#008000'],
+ ['greenyellow', '#ADFF2F'],
+ ['grey', '#808080'],
+ ['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'],
+ ['lightgray', '#D3D3D3'],
+ ['lightgreen', '#90EE90'],
+ ['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']
+]
+for _name, _code in COLOR_NAMES:
+ Color._CACHE.setdefault(_name, Color(_code))
+
+
+class colorproperty(property):
+ """
+ Automatically converts attribute assignments to color objects.
+ """
+
+ def __init__(self, name, default=None):
+ default = (None if default is None else Color(default))
+ attr = '_' + name
+
+ def fget(self):
+ return getattr(self, attr, default)
+
+ def fset(self, value):
+ if value is None:
+ setattr(self, attr, default)
+ elif isinstance(value, Color):
+ setattr(self, attr, value)
+ else:
+ setattr(self, attr, Color(value))
+
+ def fdel(self):
+ if hasattr(self, attr):
+ delattr(self, attr)
+ else:
+ raise AttributeError(name)
+
+ super(colorproperty, self).__init__(fget, fset, fdel)
+
+
+def color(*args):
+ """
+ Faster constructor of Color() objects.
+
+ Accept all positional arguments as Color(), but rejects any keyword
+ arguments.
+
+ It avoids a few expansive checks in the Color() constructor and also caches
+ the results.
+ """
+
+ if len(args) == 1:
+ value = args[0]
+ if isinstance(value, Color):
+ return value
+ elif isinstance(value, str):
+ if not value.startswith('#'):
+ return _cached_hex(value)
+
+ return _cached_color(args)
+
+
+@lru_cache(maxsize=512)
+def _cached_hex(value):
+ return Color(value)
+
+
+@lru_cache(maxsize=512)
+def _cached_color(args):
+ return Color(*args)
+
+
+def rgb(color):
+ """
+ Convert input in a tuple of (red, green, blue) colors.
+
+ Null values are converted to solid black.
+ """
+
+ try:
+ return color.rgb
+ except AttributeError:
+ return Color(color or 'black').rgb
+
+
+def rgba(color):
+ """
+ Convert input in a tuple of (red, green, blue, alpha) components
+
+ Null values are converted to solid black.
+ """
+
+ try:
+ return color.rgba
+ except AttributeError:
+ return Color(color or 'black').rgba
+
+import pgzero
\ No newline at end of file
diff --git a/src/colortools/tests/test_colortools.py b/src/colortools/tests/test_colortools.py
new file mode 100644
index 0000000..7d53c6b
--- /dev/null
+++ b/src/colortools/tests/test_colortools.py
@@ -0,0 +1,47 @@
+import pytest
+from colortools import Color
+
+
+def test_init_rgb_color_correctly():
+ c = Color(1, 2, 3)
+ assert c.red == 1
+ assert c.green == 2
+ assert c.blue == 3
+ assert c.alpha == 255
+
+
+def test_init_from_rgba():
+ assert Color(1, 2, 3, 255) == Color(1, 2, 3)
+
+
+def test_init_color_from_name():
+ assert Color('red') == Color(255, 0, 0)
+ assert Color('lime') == Color(0, 255, 0) # HTML name convention...
+ assert Color('blue') == Color(0, 0, 255)
+
+
+def test_init_from_3_hex():
+ assert Color('#0F0') == Color(0, 255, 0)
+
+
+def test_init_from_4_hex():
+ assert Color('#0F01') == Color(0, 255, 0, 17)
+
+
+def test_init_from_6_hex():
+ assert Color('#00FF00') == Color(0, 255, 0)
+
+
+def test_init_from_8_hex():
+ assert Color('#00FF0080') == Color(0, 255, 0, 128)
+
+
+def test_color_compares_with_tuple():
+ color = Color('red')
+ assert color == (255, 0, 0, 255)
+ assert color == (255, 0, 0)
+
+
+def test_init_color_from_color():
+ black = Color('black')
+ assert Color(black) == black