1# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
2# All rights reserved.
3#
4# This software is provided without warranty under the terms of the BSD
5# license included in LICENSE.txt and may be redistributed only under
6# the conditions described in the aforementioned license. The license
7# is also available online at http://www.enthought.com/licenses/BSD.txt
8#
9# Thanks for using Enthought open source!
10
11""" Routines supporting color computations
12
13Most of what is needed is provided by Python's builtin colorsys module,
14but we need a few additional routines for things that are not covered by
15that code.
16"""
17
18
19def channels_to_ints(channels, maximum=255):
20    """ Convert an iterable of floating point channel values to integers.
21
22    Values are rounded to the nearest integer, rather than truncated.
23
24    Parameters
25    ----------
26    channels : iterable of float
27        An iterable of channel values, each value between 0.0 and 1.0,
28        inclusive.
29    maximum : int
30        The maximum value of the integer range.  Common values are 15,
31        65535 or 255, which is the default.
32
33    Returns
34    -------
35    values : tuple of int
36        A tuple of values as integers between 0 and max, inclusive.
37    """
38    return tuple(int(round(channel * maximum)) for channel in channels)
39
40
41def ints_to_channels(values, maximum=255):
42    """ Convert an iterable of integers to floating point channel values.
43
44    Parameters
45    ----------
46    values : tuple of int
47        An iterable of values as integers between 0 and max, inclusive.
48    maximum : int
49        The maximum value of the integer range.  Common values are 15,
50        65535 or 255, which is the default.
51
52    Returns
53    -------
54    channels : iterable of float
55        A tuple of channel values, each value between 0.0 and 1.0,
56        inclusive.
57    """
58    return tuple(value / maximum for value in values)
59
60
61def relative_luminance(rgb):
62    """ The relative luminance of the color.
63
64    This value is the critical value when comparing colors for contrast when
65    displayed next to each other, in particular for readability of text.
66
67    Parameters
68    ----------
69    rgb : tuple of red, green, blue values
70        A tuple of values representing red, green and blue components of
71        the color, as values from 0.0 to 1.0.
72
73    Returns
74    -------
75    luminance : float
76        The relative luminance of the color.
77
78    References
79    ----------
80    Web Contrast Accessibility Guidelines
81    https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
82    """
83    gamma_corrected = [
84        x/12.92 if x <= 0.03928 else ((x + 0.055)/1.055)**2.4
85        for x in rgb
86    ]
87    luminance = (
88        0.2126 * gamma_corrected[0]
89        + 0.7152 * gamma_corrected[1]
90        + 0.0722 * gamma_corrected[2]
91    )
92    return luminance
93
94
95def is_dark(rgb):
96    """ Is the color dark to human perception?
97
98    A color is dark if white contasts better with it according to the WC3
99    definition of contrast ratio.  This is allows GUI code to choose either
100    black or white as a contrasting color for things like text on a colored
101    background.
102
103    Parameters
104    ----------
105    rgb : tuple of red, green, blue values
106        A tuple of values representing red, green and blue components of
107        the color, as values from 0.0 to 1.0.
108
109    References
110    ----------
111    Understanding Web Contrast Accessibility Guidelines
112    https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html#contrast-ratiodef
113    """
114    lumininance = relative_luminance(rgb)
115    black_contrast = (lumininance + 0.05) / 0.05
116    white_contrast = 1.05 / (lumininance + 0.05)
117    return white_contrast > black_contrast
118