359 lines
11 KiB
Python
Raw Normal View History

2012-03-30 16:12:24 +00:00
# -*- coding: utf8 -*-
2012-01-28 14:52:09 +00:00
"""
2012-03-30 16:12:24 +00:00
.. module:: lesscpy.lessc.color
:synopsis: Lesscpy Color functions
2012-01-28 14:52:09 +00:00
Copyright (c)
See LICENSE for details.
2012-03-30 16:12:24 +00:00
.. moduleauthor:: Jóhann T. Maríusson <jtm@robot.is>
2012-01-28 14:52:09 +00:00
"""
import colorsys
2012-03-10 16:27:14 +00:00
from . import utility
2012-01-28 14:52:09 +00:00
2012-03-10 16:27:14 +00:00
class Color():
2012-01-28 14:52:09 +00:00
def process(self, expression):
""" Process color expression
2012-03-30 16:12:24 +00:00
args:
expression (tuple): color expression
returns:
str
2012-01-28 14:52:09 +00:00
"""
a, o, b = expression
2012-03-10 16:27:14 +00:00
c1 = self._hextorgb(a)
c2 = self._hextorgb(b)
2012-01-28 14:52:09 +00:00
r = ['#']
for i in range(3):
v = self.operate(c1[i], c2[i], o)
if v > 0xff: v = 0xff
if v < 0: v = 0
r.append("%02x" % v)
return ''.join(r)
2012-03-30 16:12:24 +00:00
def operate(self, left, right, operation):
2012-01-28 14:52:09 +00:00
""" Do operation on colors
2012-03-30 16:12:24 +00:00
args:
left (str): left side
right (str): right side
operation (str): Operation
returns:
str
2012-01-28 14:52:09 +00:00
"""
operation = {
'+': '__add__',
'-': '__sub__',
'*': '__mul__',
'/': '__truediv__'
2012-03-30 16:12:24 +00:00
}.get(operation)
return getattr(left, operation)(right)
2012-01-28 14:52:09 +00:00
2012-03-10 16:27:14 +00:00
def rgb(self, *args):
2012-03-30 16:12:24 +00:00
""" Translate rgb(...) to color string
raises:
ValueError
returns:
str
2012-03-10 16:27:14 +00:00
"""
if len(args) == 4:
return self.rgba(*args)
elif len(args) == 3:
try:
return self._rgbatohex(map(int, args))
except ValueError:
if all((a for a in args
if a[-1] == '%'
and 100 >= int(a[:-1]) >= 0)):
return self._rgbatohex([int(a[:-1]) * 255 / 100.0
for a in args])
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-10 16:27:14 +00:00
def rgba(self, *args):
2012-03-30 16:12:24 +00:00
""" Translate rgba(...) to color string
raises:
ValueError
returns:
str
2012-03-10 16:27:14 +00:00
"""
if len(args) == 4:
try:
return self._rgbatohex(map(int, args))
except ValueError:
if all((a for a in args
if a[-1] == '%'
and 100 >= int(a[:-1]) >= 0)):
return self._rgbatohex([int(a[:-1]) * 255 / 100.0
for a in args])
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-10 16:27:14 +00:00
def hsl(self, *args):
2012-03-30 16:12:24 +00:00
""" Translate hsl(...) to color string
raises:
ValueError
returns:
str
2012-03-10 16:27:14 +00:00
"""
if len(args) == 4:
return self.hsla(*args)
elif len(args) == 3:
h, s, l = args
if type(l) == str: l = int(l.strip('%'))
if type(s) == str: s = int(s.strip('%'))
rgb = colorsys.hls_to_rgb(int(h) / 360, l / 100, s / 100)
color = (round(c * 255) for c in rgb)
return self._rgbatohex(color)
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-10 16:27:14 +00:00
def hsla(self, *args):
2012-03-30 16:12:24 +00:00
""" Translate hsla(...) to color string
raises:
ValueError
returns:
str
2012-03-10 16:27:14 +00:00
"""
if len(args) == 4:
h, s, l, a = args
if type(l) == str: l = int(l.strip('%'))
if type(s) == str: s = int(s.strip('%'))
rgb = colorsys.hls_to_rgb(int(h) / 360, l / 100, s / 100)
color = [round(c * 255) for c in rgb]
color.append(round(float(a[:-1]) / 100.0, 2))
return "rgba(%s,%s,%s,%s)" % tuple(color)
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def hue(self, color, *args):
""" Return the hue value of a color
args:
color (str): color
raises:
ValueError
returns:
float
"""
if color:
h, l, s = self._hextohls(color)
return round(h * 360.0, 3)
2012-03-11 14:47:58 +00:00
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def saturation(self, color, *args):
""" Return the saturation value of a color
args:
color (str): color
raises:
ValueError
returns:
float
"""
if color:
h, l, s = self._hextohls(color)
return s * 100.0
2012-03-11 14:47:58 +00:00
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def lightness(self, color, *args):
""" Return the lightness value of a color
args:
color (str): color
raises:
ValueError
returns:
float
"""
if color:
h, l, s = self._hextohls(color)
return l * 100.0
2012-03-11 14:47:58 +00:00
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-10 16:27:14 +00:00
def opacity(self, *args):
2012-01-28 14:52:09 +00:00
"""
"""
2012-03-10 16:27:14 +00:00
pass
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def lighten(self, color, diff, *args):
""" Lighten a color
args:
color (str): color
diff (str): percentage
returns:
str
"""
if color and diff:
2012-03-10 16:27:14 +00:00
return self._ophsl(color, diff, 1, '__add__')
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def darken(self, color, diff, *args):
""" Darken a color
args:
color (str): color
diff (str): percentage
returns:
str
"""
if color and diff:
2012-03-10 16:27:14 +00:00
return self._ophsl(color, diff, 1, '__sub__')
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def saturate(self, color, diff, *args):
""" Saturate a color
args:
color (str): color
diff (str): percentage
returns:
str
"""
if color and diff:
2012-03-10 16:27:14 +00:00
return self._ophsl(color, diff, 2, '__add__')
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def desaturate(self, color, diff, *args):
""" Desaturate a color
args:
color (str): color
diff (str): percentage
returns:
str
"""
if color and diff:
2012-03-10 16:27:14 +00:00
return self._ophsl(color, diff, 2, '__sub__')
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def _clamp(self, value):
# Clamp value
return min(1, max(0, value))
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def grayscale(self, color, *args):
""" Simply 100% desaturate.
args:
color (str): color
returns:
str
"""
if color:
return self.desaturate(color, 100.0)
2012-03-10 16:27:14 +00:00
raise ValueError('Illegal color values')
2012-03-30 16:12:24 +00:00
def greyscale(self, color, *args):
"""Wrapper for grayscale, other spelling
2012-01-28 14:52:09 +00:00
"""
2012-03-30 16:12:24 +00:00
return self.grayscale(color, *args)
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def spin(self, color, degree, *args):
""" Spin color by degree. (Increase / decrease hue)
args:
color (str): color
degree (str): percentage
raises:
ValueError
returns:
str
"""
if color and degree:
if type(degree) == str:
degree = int(degree.strip('%'))
2012-03-10 16:27:14 +00:00
h, l, s = self._hextohls(color)
2012-03-30 16:12:24 +00:00
h = ((h * 360.0) + degree) % 360.0
h = 360.0 + h if h < 0 else h
rgb = colorsys.hls_to_rgb(h / 360.0, l, s)
2012-03-10 16:27:14 +00:00
color = (round(c * 255) for c in rgb)
return self._rgbatohex(color)
raise ValueError('Illegal color values')
2012-01-28 14:52:09 +00:00
2012-03-30 16:12:24 +00:00
def mix(self, color1, color2, weight=50, *args):
"""This algorithm factors in both the user-provided weight
2012-03-11 14:47:58 +00:00
and the difference between the alpha values of the two colors
to decide how to perform the weighted average of the two RGB values.
It works by first normalizing both parameters to be within [-1, 1],
where 1 indicates "only use color1", -1 indicates "only use color 0",
and all values in between indicated a proportionately weighted average.
Once we have the normalized variables w and a,
we apply the formula (w + a)/(1 + w*a)
to get the combined weight (in [-1, 1]) of color1.
This formula has two especially nice properties:
* When either w or a are -1 or 1, the combined weight is also that number
(cases where w * a == -1 are undefined, and handled as a special case).
* When a is 0, the combined weight is w, and vice versa
Finally, the weight of color1 is renormalized to be within [0, 1]
and the weight of color2 is given by 1 minus the weight of color1.
2012-03-10 16:27:14 +00:00
Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
http://sass-lang.com
2012-03-30 16:12:24 +00:00
args:
color1 (str): first color
color2 (str): second color
weight (int/str): weight
raises:
ValueError
returns:
str
"""
if color1 and color2:
if type(weight) == str:
weight = int(weight.strip('%'))
weight = ((weight / 100.0) * 2) - 1
rgb1 = self._hextorgb(color1)
rgb2 = self._hextorgb(color2)
alpha = 0
w1 = (((weight if weight * alpha == -1
else weight + alpha) / (1 + weight * alpha)) + 1)
2012-03-11 14:47:58 +00:00
w1 = w1 / 2.0
2012-03-10 16:27:14 +00:00
w2 = 1 - w1
rgb = [
rgb1[0] * w1 + rgb2[0] * w2,
rgb1[1] * w1 + rgb2[1] * w2,
rgb1[2] * w1 + rgb2[2] * w2,
]
return self._rgbatohex(rgb)
raise ValueError('Illegal color values')
def fmt(self, color):
2012-03-30 16:12:24 +00:00
""" Format CSS Hex color code.
uppercase becomes lowercase, 3 digit codes expand to 6 digit.
args:
color (str): color
raises:
ValueError
returns:
str
2012-01-28 14:52:09 +00:00
"""
2012-03-10 16:27:14 +00:00
if utility.is_color(color):
color = color.lower().strip('#')
if len(color) in [3, 4]:
2012-01-28 14:52:09 +00:00
color = ''.join([c * 2 for c in color])
return '#%s' % color
raise ValueError('Cannot format non-color')
2012-03-10 16:27:14 +00:00
def _rgbatohex(self, rgba):
2012-01-28 14:52:09 +00:00
return '#%s' % ''.join(["%02x" % v for v in
[0xff
if h > 0xff else
0 if h < 0 else h
2012-03-10 16:27:14 +00:00
for h in rgba]
2012-01-28 14:52:09 +00:00
])
2012-03-10 16:27:14 +00:00
def _hextorgb(self, hex):
hex = hex.strip()
if hex[0] == '#':
hex = hex.strip('#').strip(';')
if len(hex) == 3:
hex = [c * 2 for c in hex]
else:
hex = [hex[i:i+2] for i in range(0, len(hex), 2)]
return tuple(int(c, 16) for c in hex)
return [int(hex, 16)] * 3
def _hextohls(self, hex):
rgb = self._hextorgb(hex)
return colorsys.rgb_to_hls(*[c / 255.0 for c in rgb])
def _ophsl(self, color, diff, idx, op):
if type(diff) == str: diff = int(diff.strip('%'))
hls = list(self._hextohls(color))
2012-03-30 16:12:24 +00:00
hls[idx] = self._clamp(getattr(hls[idx], op)(diff / 100))
2012-03-10 16:27:14 +00:00
rgb = colorsys.hls_to_rgb(*hls)
color = (round(c * 255) for c in rgb)
return self._rgbatohex(color)
2012-01-28 14:52:09 +00:00