Merge pull request #2114 from uploadcare/getrgb

ImageColor.getrgb hexadecimal RGBA
This commit is contained in:
Hugo 2016-09-19 13:02:53 +03:00 committed by GitHub
commit 79fe70a71a
2 changed files with 137 additions and 30 deletions

View File

@ -31,35 +31,46 @@ def getrgb(color):
:param color: A color string :param color: A color string
:return: ``(red, green, blue[, alpha])`` :return: ``(red, green, blue[, alpha])``
""" """
try: color = color.lower()
rgb = colormap[color]
except KeyError: rgb = colormap.get(color, None)
try:
# fall back on case-insensitive lookup
rgb = colormap[color.lower()]
except KeyError:
rgb = None
# found color in cache
if rgb: if rgb:
if isinstance(rgb, tuple): if isinstance(rgb, tuple):
return rgb return rgb
colormap[color] = rgb = getrgb(rgb) colormap[color] = rgb = getrgb(rgb)
return rgb return rgb
# check for known string formats # check for known string formats
m = re.match("#\w\w\w$", color) if re.match('#[a-f0-9]{3}$', color):
if m:
return ( return (
int(color[1]*2, 16), int(color[1]*2, 16),
int(color[2]*2, 16), int(color[2]*2, 16),
int(color[3]*2, 16) int(color[3]*2, 16),
) )
m = re.match("#\w\w\w\w\w\w$", color)
if m: if re.match('#[a-f0-9]{4}$', color):
return (
int(color[1]*2, 16),
int(color[2]*2, 16),
int(color[3]*2, 16),
int(color[4]*2, 16),
)
if re.match('#[a-f0-9]{6}$', color):
return ( return (
int(color[1:3], 16), int(color[1:3], 16),
int(color[3:5], 16), int(color[3:5], 16),
int(color[5:7], 16) int(color[5:7], 16),
) )
if re.match('#[a-f0-9]{8}$', color):
return (
int(color[1:3], 16),
int(color[3:5], 16),
int(color[5:7], 16),
int(color[7:9], 16),
)
m = re.match("rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) m = re.match("rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color)
if m: if m:
return ( return (
@ -67,6 +78,7 @@ def getrgb(color):
int(m.group(2)), int(m.group(2)),
int(m.group(3)) int(m.group(3))
) )
m = re.match("rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) m = re.match("rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color)
if m: if m:
return ( return (
@ -74,6 +86,7 @@ def getrgb(color):
int((int(m.group(2)) * 255) / 100.0 + 0.5), int((int(m.group(2)) * 255) / 100.0 + 0.5),
int((int(m.group(3)) * 255) / 100.0 + 0.5) int((int(m.group(3)) * 255) / 100.0 + 0.5)
) )
m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color)
if m: if m:
from colorsys import hls_to_rgb from colorsys import hls_to_rgb
@ -87,6 +100,7 @@ def getrgb(color):
int(rgb[1] * 255 + 0.5), int(rgb[1] * 255 + 0.5),
int(rgb[2] * 255 + 0.5) int(rgb[2] * 255 + 0.5)
) )
m = re.match("rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", m = re.match("rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$",
color) color)
if m: if m:

View File

@ -6,20 +6,115 @@ from PIL import ImageColor
class TestImageColor(PillowTestCase): class TestImageColor(PillowTestCase):
def test_sanity(self): def test_hash(self):
# short 3 components
self.assertEqual((255, 0, 0), ImageColor.getrgb("#f00")) self.assertEqual((255, 0, 0), ImageColor.getrgb("#f00"))
self.assertEqual((255, 0, 0), ImageColor.getrgb("#ff0000")) self.assertEqual((0, 255, 0), ImageColor.getrgb("#0f0"))
self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)")) self.assertEqual((0, 0, 255), ImageColor.getrgb("#00f"))
self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(255, 0, 0)"))
self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)"))
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)"))
self.assertEqual((255, 0, 0, 0), ImageColor.getrgb("rgba(255,0,0,0)"))
self.assertEqual(
(255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)"))
self.assertEqual((255, 0, 0), ImageColor.getrgb("red"))
self.assertRaises(ValueError, # short 4 components
lambda: ImageColor.getrgb("invalid color")) self.assertEqual((255, 0, 0, 0), ImageColor.getrgb("#f000"))
self.assertEqual((0, 255, 0, 0), ImageColor.getrgb("#0f00"))
self.assertEqual((0, 0, 255, 0), ImageColor.getrgb("#00f0"))
self.assertEqual((0, 0, 0, 255), ImageColor.getrgb("#000f"))
# long 3 components
self.assertEqual((222, 0, 0), ImageColor.getrgb("#de0000"))
self.assertEqual((0, 222, 0), ImageColor.getrgb("#00de00"))
self.assertEqual((0, 0, 222), ImageColor.getrgb("#0000de"))
# long 4 components
self.assertEqual((222, 0, 0, 0), ImageColor.getrgb("#de000000"))
self.assertEqual((0, 222, 0, 0), ImageColor.getrgb("#00de0000"))
self.assertEqual((0, 0, 222, 0), ImageColor.getrgb("#0000de00"))
self.assertEqual((0, 0, 0, 222), ImageColor.getrgb("#000000de"))
# case insensitivity
self.assertEqual(ImageColor.getrgb("#DEF"), ImageColor.getrgb("#def"))
self.assertEqual(ImageColor.getrgb("#CDEF"), ImageColor.getrgb("#cdef"))
self.assertEqual(ImageColor.getrgb("#DEFDEF"),
ImageColor.getrgb("#defdef"))
self.assertEqual(ImageColor.getrgb("#CDEFCDEF"),
ImageColor.getrgb("#cdefcdef"))
# not a number
self.assertRaises(ValueError, ImageColor.getrgb, "#fo0")
self.assertRaises(ValueError, ImageColor.getrgb, "#fo00")
self.assertRaises(ValueError, ImageColor.getrgb, "#fo0000")
self.assertRaises(ValueError, ImageColor.getrgb, "#fo000000")
# wrong number of components
self.assertRaises(ValueError, ImageColor.getrgb, "#f0000")
self.assertRaises(ValueError, ImageColor.getrgb, "#f000000")
self.assertRaises(ValueError, ImageColor.getrgb, "#f00000000")
self.assertRaises(ValueError, ImageColor.getrgb, "#f000000000")
self.assertRaises(ValueError, ImageColor.getrgb, "#f00000 ")
def test_colormap(self):
self.assertEqual((0, 0, 0), ImageColor.getrgb("black"))
self.assertEqual((255, 255, 255), ImageColor.getrgb("white"))
self.assertEqual((255, 255, 255), ImageColor.getrgb("WHITE"))
self.assertRaises(ValueError, ImageColor.getrgb, "black ")
def test_functions(self):
# rgb numbers
self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)"))
self.assertEqual((0, 255, 0), ImageColor.getrgb("rgb(0,255,0)"))
self.assertEqual((0, 0, 255), ImageColor.getrgb("rgb(0,0,255)"))
# percents
self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)"))
self.assertEqual((0, 255, 0), ImageColor.getrgb("rgb(0%,100%,0%)"))
self.assertEqual((0, 0, 255), ImageColor.getrgb("rgb(0%,0%,100%)"))
# rgba numbers
self.assertEqual((255, 0, 0, 0), ImageColor.getrgb("rgba(255,0,0,0)"))
self.assertEqual((0, 255, 0, 0), ImageColor.getrgb("rgba(0,255,0,0)"))
self.assertEqual((0, 0, 255, 0), ImageColor.getrgb("rgba(0,0,255,0)"))
self.assertEqual((0, 0, 0, 255), ImageColor.getrgb("rgba(0,0,0,255)"))
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(0,100%,50%)"))
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(360,100%,50%)"))
self.assertEqual((0, 255, 255), ImageColor.getrgb("hsl(180,100%,50%)"))
# case insensitivity
self.assertEqual(ImageColor.getrgb("RGB(255,0,0)"),
ImageColor.getrgb("rgb(255,0,0)"))
self.assertEqual(ImageColor.getrgb("RGB(100%,0%,0%)"),
ImageColor.getrgb("rgb(100%,0%,0%)"))
self.assertEqual(ImageColor.getrgb("RGBA(255,0,0,0)"),
ImageColor.getrgb("rgba(255,0,0,0)"))
self.assertEqual(ImageColor.getrgb("HSL(0,100%,50%)"),
ImageColor.getrgb("hsl(0,100%,50%)"))
# space agnosticism
self.assertEqual((255, 0, 0),
ImageColor.getrgb("rgb( 255 , 0 , 0 )"))
self.assertEqual((255, 0, 0),
ImageColor.getrgb("rgb( 100% , 0% , 0% )"))
self.assertEqual((255, 0, 0, 0),
ImageColor.getrgb("rgba( 255 , 0 , 0 , 0 )"))
self.assertEqual((255, 0, 0),
ImageColor.getrgb("hsl( 0 , 100% , 50% )"))
# wrong number of components
self.assertRaises(ValueError, ImageColor.getrgb, "rgb(255,0)")
self.assertRaises(ValueError, ImageColor.getrgb, "rgb(255,0,0,0)")
self.assertRaises(ValueError, ImageColor.getrgb, "rgb(100%,0%)")
self.assertRaises(ValueError, ImageColor.getrgb, "rgb(100%,0%,0)")
self.assertRaises(ValueError, ImageColor.getrgb, "rgb(100%,0%,0 %)")
self.assertRaises(ValueError, ImageColor.getrgb, "rgb(100%,0%,0%,0%)")
self.assertRaises(ValueError, ImageColor.getrgb, "rgba(255,0,0)")
self.assertRaises(ValueError, ImageColor.getrgb, "rgba(255,0,0,0,0)")
self.assertRaises(ValueError, ImageColor.getrgb, "hsl(0,100%)")
self.assertRaises(ValueError, ImageColor.getrgb, "hsl(0,100%,0%,0%)")
self.assertRaises(ValueError, ImageColor.getrgb, "hsl(0%,100%,50%)")
self.assertRaises(ValueError, ImageColor.getrgb, "hsl(0,100,50%)")
self.assertRaises(ValueError, ImageColor.getrgb, "hsl(0,100%,50)")
# look for rounding errors (based on code by Tim Hatch) # look for rounding errors (based on code by Tim Hatch)
def test_rounding_errors(self): def test_rounding_errors(self):
@ -27,11 +122,9 @@ class TestImageColor(PillowTestCase):
for color in list(ImageColor.colormap.keys()): for color in list(ImageColor.colormap.keys()):
expected = Image.new( expected = Image.new(
"RGB", (1, 1), color).convert("L").getpixel((0, 0)) "RGB", (1, 1), color).convert("L").getpixel((0, 0))
actual = Image.new("L", (1, 1), color).getpixel((0, 0)) actual = ImageColor.getcolor(color, 'L')
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
self.assertEqual((0, 0, 0), ImageColor.getcolor("black", "RGB"))
self.assertEqual((255, 255, 255), ImageColor.getcolor("white", "RGB"))
self.assertEqual( self.assertEqual(
(0, 255, 115), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGB")) (0, 255, 115), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGB"))
Image.new("RGB", (1, 1), "white") Image.new("RGB", (1, 1), "white")