Changeset 3645:05e4e1a87e14 in livinglogic.python.xist

Show
Ignore:
Timestamp:
12/25/08 23:09:41 (10 years ago)
Author:
Walter Doerwald <walter@…>
Branch:
default
Message:

Simplify ll.color and add color objects to UL4.

Files:
6 modified

Legend:

Unmodified
Added
Removed
  • NEWS.rst

    r3638 r3645  
     1Changes in 3.6 (released ??/??/2009) 
     2------------------------------------ 
     3 
     4*   The module :mod:`ll.color` has been greatly simplified. All methods that 
     5    treat the components as 4 bit or floating point values have been removed. The 
     6    rests of the properties have been turned into methods. 
     7 
     8*   Support for color objects has been added to UL4. 
     9 
     10 
    111Changes in 3.5 (released 12/05/2008) 
    212------------------------------------ 
  • docs/UL4.rst

    r3624 r3645  
    6666    *   floats 
    6767    *   date objects 
     68    *   color objects 
    6869    *   The "null" value (``None``) 
    6970    *   boolean values (``True`` and ``False``) 
     
    7273    *   templates 
    7374 
    74 This is similar to what JSON_ supports (except for date objects and templates). 
     75This is similar to what JSON_ supports (except for date objects, color objects 
     76and templates). 
    7577 
    7678    .. _JSON: http://www.json.org/ 
     
    135137-------------- 
    136138 
    137 Date constants can be created like this: 
     139Date objects have a date and time including microseconds. Date constants can be 
     140created like this: 
    138141 
    139142    *   ``2008-12-24T`` 
     
    144147 
    145148    *   ``2008-12-24T12:34:56.987654`` 
     149 
     150 
     151Color constants 
     152--------------- 
     153 
     154Color values are 8 bit red, green, blue and alpha values. Color constants can 
     155be created like this: 
     156 
     157    *   ``#fff`` 
     158 
     159    *   ``#fff8`` 
     160 
     161    *   ``#0063a8`` 
     162 
     163    *   ``#0063a880`` 
     164 
     165The variants with 3 or 6 hex digits will create a color object with an alpha 
     166value of 255. 
    146167 
    147168 
     
    239260------------ 
    240261 
    241 The ``continue`` tag can be used skip the rest of the loop body of the innermost 
    242 running loop. 
     262The ``continue`` tag can be used to skip the rest of the loop body of the 
     263innermost running loop. 
    243264 
    244265 
     
    524545``str(foo)`` converts ``foo`` to a string. If ``foo`` is ``None`` the result 
    525546will be the empty string. For lists and dictionaries the exact format is 
    526 undefined, but should follow Python's repr format. 
     547undefined, but should follow Python's repr format. For color objects the result 
     548is a CSS expression (e.g. ``"#fff"``). 
    527549 
    528550 
     
    591613    abr 
    592614 
    593 Supported arguments are iterable objects, i.e. strings, lists and dictionaries. 
     615Supported arguments are iterable objects, i.e. strings, lists, dictionaries 
     616and colors. 
    594617 
    595618 
     
    651674``type`` returns the type of the object as a string. Possible return values are 
    652675``"none"``, ``"bool"``, ``"int"``, ``"float"``, ``"str"``, ``"list"``, 
    653 ``"dict"`` and ``"template"``. (If the type isn't recognized ``None`` is 
    654 returned.) 
     676``"dict"``, ``"date"``, ``"color"`` and ``"template"``. (If the type isn't 
     677recognized ``None`` is returned.) 
     678 
     679 
     680``color`` 
     681::::::::: 
     682 
     683``color`` returns a color object. It can be called with 
     684 
     685    *   one argument, which must be a string in CSS format; 
     686    *   three arguments, the red, green and blue values. The alpha value will be 
     687        set to 255; 
     688    *   four arguments, the red, green, blue and alpha values. 
    655689 
    656690 
  • src/ll/color.py

    r3644 r3645  
    99 
    1010 
     11from __future__ import division 
     12 
    1113""" 
    1214:mod:`ll.color` provides classes and functions for handling RGB colors. 
     
    2729    components. 
    2830    """ 
    29     def __new__(cls, r8=0x0, g8=0x0, b8=0x0, a8=0xff): 
     31    def __new__(cls, r=0x0, g=0x0, b=0x0, a=0xff): 
    3032        """ 
    3133        Create a :class:`Color` with the 8 bit red, green, blue and alpha 
    32         components :var:`r8`, :var:`g8`, :var:`b8` and :var:`a8`. Values will be 
     34        components :var:`r`, :var:`g`, :var:`b` and :var:`a`. Values will be 
    3335        clipped to the range [0; 255]. 
    3436        """ 
    35         return tuple.__new__(cls, (max(0, min(int(r8), 255)), max(0, min(int(g8), 255)), max(0, min(int(b8), 255)), max(0, min(int(a8), 255)))) 
    36  
    37     @classmethod 
    38     def fromrgba(cls, r, g, b, a=1.0): 
    39         """ 
    40         Create a :class:`Color` object from the RGB values :var:`r`, :var:`g` and 
    41         :var:`b` and the alpha value :var:`a`. All values will be clipped to the 
    42         range [0; 1]. 
    43         """ 
    44         return cls(r*0xff, g*0xff, b*0xff, a*0xff) 
    45  
    46     @classmethod 
    47     def fromrgba4(cls, r4, g4, b4, a4=0xf): 
    48         """ 
    49         Create a :class:`Color` object from the 4 bit RGB values :var:`r4`, 
    50         :var:`g4` and :var:`b4` and the 4 bit alpha value :var:`a4`. 
    51         """ 
    52         return cls(r4*0x11, g4*0x11, b4*0x11, a4*0x11) 
    53  
    54     @classmethod 
    55     def fromrgba8(cls, r8, g8, b8, a8=0xff): 
    56         """ 
    57         Create a :class:`Color` object from the 8 bit RGB values :var:`r8`, 
    58         :var:`g8` and :var:`b8` and the 8 bit alpha value :var:`a8`. 
    59         """ 
    60         return cls(r8, g8, b8, a8) 
    61  
    62     @classmethod 
    63     def fromint4(cls, int4): 
    64         """ 
    65         Create a :class:`Color` object from the 16 bit RGB integer :var:`int4`. 
    66         See the :prop:`int4` property for more info. 
    67         """ 
    68         return cls.fromrgba4(int4>>12 & 0xf, int4>>8 & 0xf, int4>>4 & 0xf, int4 & 0xf) 
    69  
    70     @classmethod 
    71     def fromint8(cls, int8): 
    72         """ 
    73         Create a :class:`Color` object from the 32 bit RGBA integer :var:`int8`. 
    74         See the :prop:`int8` property for more info. 
    75         """ 
    76         return cls.fromrgba8(int8>>24 & 0xff, int8>>16 & 0xff, int8>>8 & 0xff, int8 & 0xff) 
     37        return tuple.__new__(cls, (max(0, min(int(r), 255)), max(0, min(int(g), 255)), max(0, min(int(b), 255)), max(0, min(int(a), 255)))) 
    7738 
    7839    @classmethod 
     
    8849        if s.startswith("#"): 
    8950            if len(s) == 4: 
    90                 return cls.fromrgba4(int(s[1], 16), int(s[2], 16), int(s[3], 16)) 
     51                return cls(17*int(s[1], 16), 17*int(s[2], 16), 17*int(s[3], 16)) 
    9152            elif len(s) == 7: 
    92                 return cls.fromrgba8(int(s[1:3], 16), int(s[3:5], 16), int(s[5:], 16)) 
     53                return cls(int(s[1:3], 16), int(s[3:5], 16), int(s[5:], 16)) 
    9354        elif s.startswith("rgb(") and s.endswith(")"): 
    9455            channels = [] 
     
    140101 
    141102    def __repr__(self): 
    142         if self.a8 != 0xff: 
     103        if self[3] != 0xff: 
    143104            return "Color(0x%02x, 0x%02x, 0x%02x, 0x%02x)" % self 
    144105        else: 
     
    146107 
    147108    def __str__(self): 
    148         return self.css 
    149  
    150     @property 
    151     def r(self): 
    152         """ 
    153         The red value as a float between 0.0 and 1.0. 
    154         """ 
    155         return self[0]/255. 
    156  
    157     @property 
    158     def g(self): 
    159         """ 
    160         The green value as a float between 0.0 and 1.0. 
    161         """ 
    162         return self[1]/255. 
    163  
    164     @property 
    165     def b(self): 
    166         """ 
    167         The blue value as a float between 0.0 and 1.0. 
    168         """ 
    169         return self[2]/255. 
    170  
    171     @property 
    172     def a(self): 
    173         """ 
    174         The alpha value as a float between 0.0 and 1.0. 
    175         """ 
    176         return self[3]/255. 
    177  
    178     @property 
    179     def r4(self): 
    180         """ 
    181         The red value as an int between 0 and 15. 
    182         """ 
    183         return int(self[0]/17.) 
    184  
    185     @property 
    186     def g4(self): 
    187         """ 
    188         The green value as an int between 0 and 15. 
    189         """ 
    190         return int(self[1]/17.) 
    191  
    192     @property 
    193     def b4(self): 
    194         """ 
    195         The blue value as an int between 0 and 15. 
    196         """ 
    197         return int(self[2]/17) 
    198  
    199     @property 
    200     def a4(self): 
    201         """ 
    202         The alpha value as an int between 0 and 15. 
    203         """ 
    204         return int(self[3]/17.) 
    205  
    206     @property 
    207     def r8(self): 
    208         """ 
    209         The red value as an int between 0 and 255. 
    210         """ 
    211         return self[0] 
    212  
    213     @property 
    214     def g8(self): 
    215         """ 
    216         The green value as an int between 0 and 255. 
    217         """ 
    218         return self[1] 
    219  
    220     @property 
    221     def b8(self): 
    222         """ 
    223         The blue value as an int between 0 and 255. 
    224         """ 
    225         return self[2] 
    226  
    227     @property 
    228     def a8(self): 
    229         """ 
    230         The alpha value as an int between 0 and 255. 
    231         """ 
    232         return self[3] 
    233  
    234     @property 
    235     def int4(self): 
    236         """ 
    237         The RGB value as a 16 bit integer. Red is bit 12-15, green is bit 8-11, 
    238         blue is bit 4-7 and alpha is bit 0-3) 
    239         """ 
    240         rgba4 = self.rgba4 
    241         return (rgba4[0]<<12) + (rgba4[1]<<8) + (rgba4[2]<<4) + rgba4[3] 
    242  
    243     @property 
    244     def int8(self): 
    245         """ 
    246         The RGB value as a 32 bit integer. Red is bit 24-31, green is bit 16-23, 
    247         blue is bit 8-15 and alpha is bit 0-7) 
    248         """ 
    249         rgba8 = self.rgba8 
    250         return (rgba8[0]<<24) + (rgba8[1]<<16) + (rgba8[2]<<8) + rgba8[3] 
    251  
    252     @property 
    253     def rgb(self): 
    254         """ 
    255         The red, green and blue value as a float tuple with values between 
    256         0.0 and 1.0. 
    257         """ 
    258         return (self.r, self.g, self.b) 
    259  
    260     @property 
    261     def rgba(self): 
    262         """ 
    263         The red, green, blue and alpha value as a float tuple with values between 
    264         0.0 and 1.0. 
    265         """ 
    266         return (self.r, self.g, self.b, self.a) 
    267  
    268     @property 
    269     def rgb4(self): 
    270         """ 
    271         The red, green and blue value as an int tuple with values between 
    272         0 and 15. 
    273         """ 
    274         return (self.r4, self.g4, self.b4) 
    275  
    276     @property 
    277     def rgba4(self): 
    278         """ 
    279         The red, green, blue and alpha value as an int tuple with values between 
    280         0 and 15. 
    281         """ 
    282         return (self.r4, self.g4, self.b4, self.a4) 
    283  
    284     @property 
    285     def rgb8(self): 
    286         """ 
    287         The red, green and blue value as an int tuple with values between 
    288         0 and 255. 
    289         """ 
    290         return self[:3] 
    291  
    292     @property 
    293     def rgba8(self): 
    294         """ 
    295         The red, green, blue and alpha value as an int tuple with values between 
    296         0 and 255. 
    297         """ 
    298         return tuple(self) 
    299  
    300     @property 
    301     def css(self): 
    302         """ 
    303         :var:`self` formatted as a &css; color string. 
    304         """ 
    305         if self.a8 != 0xff: 
    306             return "rgba(%d,%d,%d,%.3f)" % (self.r8, self.g8, self.b8, self.a) 
     109        """ 
     110        :var:`self` formatted as a CSS color string. 
     111        """ 
     112        if self[3] != 0xff: 
     113            return "rgba(%d,%d,%d,%.3f)" % (self[0], self[1], self[2], self[3]/255.) 
    307114        else: 
    308             s = "#%02x%02x%02x" % (self.r8, self.g8, self.b8) 
     115            s = "#%02x%02x%02x" % (self[0], self[1], self[2]) 
    309116            if s[1]==s[2] and s[3]==s[4] and s[5]==s[6]: 
    310117                s = "#%s%s%s" % (s[1], s[3], s[5]) 
    311118        return s 
    312119 
    313     @property 
     120    def r(self): 
     121        """ 
     122        The red value as an int between 0 and 255. 
     123        """ 
     124        return self[0] 
     125 
     126    def g(self): 
     127        """ 
     128        The green value as an int between 0 and 255. 
     129        """ 
     130        return self[1] 
     131 
     132    def b(self): 
     133        """ 
     134        The blue value as an int between 0 and 255. 
     135        """ 
     136        return self[2] 
     137 
     138    def a(self): 
     139        """ 
     140        The alpha value as an int between 0 and 255. 
     141        """ 
     142        return self[3] 
     143 
     144    def rgb(self): 
     145        """ 
     146        The red, green and blue value as a float tuple with values between 
     147        0 and 255. 
     148        """ 
     149        return (self.r(), self.g(), self.b()) 
     150 
     151    def rgba(self): 
     152        """ 
     153        The red, green, blue and alpha value as a float tuple with values between 
     154        0 and 255. 
     155        """ 
     156        return (self.r(), self.g(), self.b(), self.a()) 
     157 
    314158    def hsv(self): 
    315159        """ 
     
    317161        All three values are between 0.0 and 1.0. 
    318162        """ 
    319         return colorsys.rgb_to_hsv(*self.rgb) 
    320  
    321     @property 
     163        return colorsys.rgb_to_hsv(self[0]/255., self[1]/255., self[2]/255.) 
     164 
    322165    def hsva(self): 
    323166        """ 
     
    325168        All four values are between 0.0 and 1.0. 
    326169        """ 
    327         return self.hsv + (self.a,) 
    328  
    329     @property 
     170        return self.hsv() + (self[3]/255.,) 
     171 
    330172    def hls(self): 
    331173        """ 
     
    333175        values are between 0.0 and 1.0. 
    334176        """ 
    335         return colorsys.rgb_to_hls(*self.rgb) 
    336  
    337     @property 
     177        return colorsys.rgb_to_hls(self[0]/255., self[1]/255., self[2]/255.) 
     178 
    338179    def hlsa(self): 
    339180        """ 
     
    341182        All four values are between 0.0 and 1.0. 
    342183        """ 
    343         return self.hls + (self.a,) 
     184        return self.hls() + (self[3]/255.,) 
    344185 
    345186    @property 
     
    348189        The luminance value from the :prop:`hls` property. 
    349190        """ 
    350         return colorsys.rgb_to_hls(*self.rgb)[1] 
    351  
    352     def combine(self, r8=None, g8=None, b8=None, a8=None): 
     191        return colorsys.rgb_to_hls(self[0]/255., self[1]/255., self[2]/255.)[1] 
     192 
     193    def combine(self, r=None, g=None, b=None, a=None): 
    353194        """ 
    354195        Return a copy of :var:`self` with some of the values replaced by the 
     
    356197        """ 
    357198        channels = list(self) 
    358         if r8 is not None: 
    359             channels[0] = r8 
    360         if g8 is not None: 
    361             channels[1] = g8 
    362         if b8 is not None: 
    363             channels[2] = b8 
    364         if a8 is not None: 
    365             channels[3] = a8 
     199        if r is not None: 
     200            channels[0] = r 
     201        if g is not None: 
     202            channels[1] = g 
     203        if b is not None: 
     204            channels[2] = b 
     205        if a is not None: 
     206            channels[3] = a 
    366207        return self.__class__(*channels) 
    367208 
    368209    def withlum(self, lum): 
    369         (h, l, s, a) = self.hlsa 
     210        (h, l, s, a) = self.hlsa() 
    370211        return self.fromhls(h, max(0., min(lum, 1.)), s, a) 
    371212 
     
    374215        Return a copy of :var:`self` with :var:`f` added to the luminocity. 
    375216        """ 
    376         (h, l, s, a) = self.hlsa 
     217        (h, l, s, a) = self.hlsa() 
    377218        return self.fromhlsa(h, l+f, s, a) 
    378219 
     
    393234 
    394235    def __add__(self, other): 
    395         return self.__class__(0.5*(self[0]+other[0]), 0.5*(self[1]+other[1]), 0.5*(self[2]+other[2]), 1.-(1-self[3])*(1.-other[3])) 
     236        return self.__class__(0.5*(self[0]+other[0]), 0.5*(self[1]+other[1]), 0.5*(self[2]+other[2]), 255-(255-self[3])*(255-other[3])/255.) 
    396237 
    397238    def __mul__(self, factor): 
     
    401242        return self.__class__(factor*self[0], factor*self[1], factor*self[2], self[3]) 
    402243 
    403     def __div__(self, other): 
     244    def __div__(self, factor): 
     245        return self.__class__(self[0]/factor, self[1]/factor, self[2]/factor, self[3]) 
     246 
     247    def __mod__(self, other): 
    404248        """ 
    405249        Blends :var:`self` with the background color :var:`other` according to the 
     
    410254        sa = self[3]/255. 
    411255        rsa = 1.-sa 
    412         return self.__class__(self[0]*sa+rsa*other[0], self[1]*sa+rsa*other[1], self[2]*sa+rsa*other[2], 1.-rsa*(1.-other[3]/255.)) 
     256        return self.__class__(self[0]*sa+rsa*other[0], self[1]*sa+rsa*other[1], self[2]*sa+rsa*other[2], 255-rsa*(255-other[3])) 
    413257 
    414258 
     
    417261### 
    418262 
    419 maroon = Color.fromrgba8(0x80, 0x00, 0x00) 
    420 red = Color.fromrgba8(0xff, 0x00, 0x00) 
    421 orange = Color.fromrgba8(0xff, 0xA5, 0x00) 
    422 yellow = Color.fromrgba8(0xff, 0xff, 0x00) 
    423 olive = Color.fromrgba8(0x80, 0x80, 0x00) 
    424 purple = Color.fromrgba8(0x80, 0x00, 0x80) 
    425 fuchsia = Color.fromrgba8(0xff, 0x00, 0xff) 
    426 white = Color.fromrgba8(0xff, 0xff, 0xff) 
    427 lime = Color.fromrgba8(0x00, 0xff, 0x00) 
    428 green = Color.fromrgba8(0x00, 0x80, 0x00) 
    429 navy = Color.fromrgba8(0x00, 0x00, 0x80) 
    430 blue = Color.fromrgba8(0x00, 0x00, 0xff) 
    431 aqua = Color.fromrgba8(0x00, 0xff, 0xff) 
    432 teal = Color.fromrgba8(0x00, 0x80, 0x80) 
    433 black = Color.fromrgba8(0x00, 0x00, 0x00) 
    434 silver = Color.fromrgba8(0xc0, 0xc0, 0xc0) 
    435 gray = Color.fromrgba8(0x80, 0x80, 0x80) 
     263maroon = Color(0x80, 0x00, 0x00) 
     264red = Color(0xff, 0x00, 0x00) 
     265orange = Color(0xff, 0xa5, 0x00) 
     266yellow = Color(0xff, 0xff, 0x00) 
     267olive = Color(0x80, 0x80, 0x00) 
     268purple = Color(0x80, 0x00, 0x80) 
     269fuchsia = Color(0xff, 0x00, 0xff) 
     270white = Color(0xff, 0xff, 0xff) 
     271lime = Color(0x00, 0xff, 0x00) 
     272green = Color(0x00, 0x80, 0x00) 
     273navy = Color(0x00, 0x00, 0x80) 
     274blue = Color(0x00, 0x00, 0xff) 
     275aqua = Color(0x00, 0xff, 0xff) 
     276teal = Color(0x00, 0x80, 0x80) 
     277black = Color(0x00, 0x00, 0x00) 
     278silver = Color(0xc0, 0xc0, 0xc0) 
     279gray = Color(0x80, 0x80, 0x80) 
    436280 
    437281# aliases 
     
    507351            for i in xrange(3): 
    508352                channels[i] += weight*arg[i] 
    509             channels[3] += weight*(1.-arg[3]) 
     353            channels[3] += weight*(255-arg[3]) 
    510354        elif isinstance(arg, tuple): 
    511355            sumweights += arg[1] 
    512356            for i in xrange(3): 
    513357                channels[i] += arg[1]*arg[0][i] 
    514             channels[3] += weight*(1.-arg[0][3]) 
     358            channels[3] += weight*(255-arg[0][3]) 
    515359        else: 
    516360            weight = arg 
    517361    if not sumweights: 
    518362        raise ValueError("at least one of the weights must be >0") 
    519     return Color(channels[0]/sumweights, channels[1]/sumweights, channels[2]/sumweights, 1.-sumweights*channels[3]) 
     363    return Color(channels[0]/sumweights, channels[1]/sumweights, channels[2]/sumweights, 255-sumweights*channels[3]) 
  • src/ll/ul4c.py

    r3642 r3645  
    2626import re, datetime, StringIO, locale 
    2727 
    28 from ll import spark 
     28from ll import spark, color 
    2929 
    3030 
     
    169169    """ 
    170170    Exception that is raised by the renderer if the function to be executed by 
    171     the ``callfunc0``, ``callfunc1``, ``callfunc2`` or ``callfunc3`` opcodes is 
    172     unknown. 
     171    the ``callfunc0``, ``callfunc1``, ``callfunc2``, ``callfunc3`` or 
     172    ``callfunc4`` opcodes is unknown. 
    173173    """ 
    174174 
     
    268268        Load the date value :attr:`arg` into register :attr:`r1`. :attr:`arg` must 
    269269        be in ISO format (e.g. ``2008-07-02T11:05:55.460464``). 
     270 
     271    ``"loadcolor"``: 
     272        Load the color value :attr:`arg` into register :attr:`r1`. :attr:`arg` must 
     273        be in the format ``rrggbbaa``). 
    270274 
    271275    ``"buildlist"``: 
     
    415419        :attr:`r2`, :attr:`r3` and :attr:`r4` as the three arguments and store 
    416420        the return value in register :attr:`r1`. 
     421 
     422    ``"callfunc4"``: 
     423        Call the function named :attr:`arg` with the contents of register 
     424        :attr:`r2`, :attr:`r3`, :attr:`r4` and :attr:`r5` as the four arguments 
     425        and store the return value in register :attr:`r1`. 
    417426 
    418427    ``"callmeth0"``: 
     
    485494        elif self.code == "loaddate": 
    486495            return "r%r = %s" % (self.r1, self.arg) 
     496        elif self.code == "loadcolor": 
     497            return "r%r = #%s" % (self.r1, self.arg) 
    487498        elif self.code == "buildlist": 
    488499            return "r%r = []" % (self.r1) 
     
    579590        elif self.code == "callfunc3": 
    580591            return "r%r = %s(r%r, r%r, r%r)" % (self.r1, self.arg, self.r2, self.r3, self.r4) 
     592        elif self.code == "callfunc4": 
     593            return "r%r = %s(r%r, r%r, r%r, r%r)" % (self.r1, self.arg, self.r2, self.r3, self.r4, self.r5) 
    581594        elif self.code == "callmeth0": 
    582595            return "r%r = r%r.%s()" % (self.r1, self.r2, self.arg) 
     
    606619    returns a generator) or :meth:`renders` (which returns a string). 
    607620    """ 
    608     version = "6" 
     621    version = "7" 
    609622 
    610623    def __init__(self): 
     
    799812        indent -= 1 
    800813        _code("from ll.misc import xmlescape") 
    801         _code("from ll import ul4c") 
     814        _code("from ll import ul4c, color") 
    802815        _code("source = %r" % self.source) 
    803816        _code('variables = dict((key.decode("utf-8"), value) for (key, value) in variables.iteritems())') # FIXME: This can be dropped in Python 3.0 where strings are unicode 
     
    839852                elif opcode.code == "loaddate": 
    840853                    _code("reg%d = datetime.datetime(%s)" % (opcode.r1, ", ".join(str(int(p)) for p in datesplitter.split(opcode.arg)))) 
     854                elif opcode.code == "loadcolor": 
     855                    _code("reg%d = color.Color(0x%s, 0x%s, 0x%s, 0x%s)" % (opcode.r1, opcode.arg[:2], opcode.arg[2:4], opcode.arg[4:6], opcode.arg[6:])) 
    841856                elif opcode.code == "buildlist": 
    842857                    _code("reg%d = []" % opcode.r1) 
     
    9901005                    elif opcode.arg == "type": 
    9911006                        _code("reg%d = ul4c._type(reg%d)" % (opcode.r1, opcode.r2)) 
     1007                    elif opcode.arg == "color": 
     1008                        _code("reg%d = color.Color.fromcss(reg%d)" % (opcode.r1, opcode.r2)) 
    9921009                    else: 
    9931010                        raise UnknownFunctionError(opcode.arg) 
     
    10061023                    elif opcode.arg == "zip": 
    10071024                        _code("reg%d = itertools.izip(reg%d, reg%d, reg%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
     1025                    elif opcode.arg == "color": 
     1026                        _code("reg%d = color.Color(reg%d, reg%d, reg%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
     1027                    else: 
     1028                        raise UnknownFunctionError(opcode.arg) 
     1029                elif opcode.code == "callfunc4": 
     1030                    if opcode.arg == "color": 
     1031                        _code("reg%d = color.Color(reg%d, reg%d, reg%d, reg%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4, opcode.r5)) 
    10081032                    else: 
    10091033                        raise UnknownFunctionError(opcode.arg) 
    10101034                elif opcode.code == "callmeth0": 
    1011                     if opcode.arg in ("split", "rsplit", "strip", "lstrip", "rstrip", "upper", "lower", "capitalize", "isoformat"): 
     1035                    if opcode.arg in ("split", "rsplit", "strip", "lstrip", "rstrip", "upper", "lower", "capitalize", "isoformat", "r", "g", "b", "a", "hls", "hlsa", "hsv", "hsva"): 
    10121036                        _code("reg%d = reg%d.%s()" % (opcode.r1, opcode.r2, opcode.arg)) 
    10131037                    elif opcode.arg == "items": 
     
    13681392 
    13691393 
     1394class Color(Value): 
     1395    type = "color" 
     1396 
     1397    def compile(self, template): 
     1398        r = template._allocreg() 
     1399        template.opcode("load%s" % self.type, r1=r, arg="%02x%02x%02x%02x" % self.value) 
     1400        return r 
     1401 
     1402 
    13701403class List(AST): 
    13711404    def __init__(self, start, end, *items): 
     
    16821715            template.opcode("callfunc0", r1=r, arg=self.name.name) 
    16831716            return r 
    1684         elif len(self.args) > 3: 
    1685             raise ValueError("%d arguments not supported" % len(self.args)) 
     1717        elif len(self.args) > 4: 
     1718            raise ValueError("%d function arguments not supported" % len(self.args)) 
    16861719        else: 
    16871720            rs = [arg.compile(template) for arg in self.args] 
    1688             template.opcode("callfunc%d" % len(self.args), rs[0], *rs, **dict(arg=self.name.name)) # Replace **dict(arg=) with arg= in Python 2.6? 
     1721            template.opcode("callfunc%d" % len(self.args), rs[0], *rs, **dict(arg=self.name.name)) # FIXME: Replace **dict(arg=) with arg= in Python 2.6? 
    16891722            for i in xrange(1, len(self.args)): 
    16901723                template._freereg(rs[i]) 
     
    17071740    def compile(self, template): 
    17081741        if len(self.args) > 3: 
    1709             raise ValueError("%d arguments not supported" % len(self.args)) 
     1742            raise ValueError("%d method arguments not supported" % len(self.args)) 
    17101743        ro = self.obj.compile(template) 
    17111744        rs = [arg.compile(template) for arg in self.args] 
     
    17661799        return self.rv 
    17671800 
     1801    # Color tokens must be in the order of decreasing length 
     1802    @spark.token("\\#[0-9a-fA-F]{8}", "default") 
     1803    def color8(self, start, end, s): 
     1804        self.rv.append(Color(start, end, color.Color(int(s[1:3], 16), int(s[3:5], 16), int(s[5:7], 16), int(s[7:], 16)))) 
     1805 
     1806    @spark.token("\\#[0-9a-fA-F]{6}", "default") 
     1807    def color6(self, start, end, s): 
     1808        self.rv.append(Color(start, end, color.Color(int(s[1:3], 16), int(s[3:5], 16), int(s[5:], 16)))) 
     1809 
     1810    @spark.token("\\#[0-9a-fA-F]{4}", "default") 
     1811    def color4(self, start, end, s): 
     1812        self.rv.append(Color(start, end, color.Color(17*int(s[1], 16), 17*int(s[2], 16), 17*int(s[3], 16), 17*int(s[4], 16)))) 
     1813 
     1814    @spark.token("\\#[0-9a-fA-F]{3}", "default") 
     1815    def color3(self, start, end, s): 
     1816        self.rv.append(Color(start, end, color.Color(17*int(s[1], 16), 17*int(s[2], 16), 17*int(s[3], 16)))) 
     1817 
    17681818    # Must be before the int and float constants 
    17691819    @spark.token("\\d{4}-\\d{2}-\\d{2}T(\\d{2}:\\d{2}(:\\d{2}(\\.\\d{6})?)?)?", "default") 
     
    19391989        elif isinstance(value, basestring): 
    19401990            return Str(start, end, value) 
     1991        elif isinstance(value, color.Color): 
     1992            return Color(start, end, value) 
    19411993        else: 
    19421994            raise TypeError("can't convert %r" % value) 
     
    19441996    # To implement operator precedence, each expression rule has the precedence in its name. The highest precedence is 11 for atomic expressions. 
    19451997    # Each expression can have only expressions as parts which have the some or a higher precedence with two exceptions: 
    1946     #    1) Expressions where there's no ambiguity, like the index for a getitem/getslice or function/method arguments; 
    1947     #    2) Brackets, which can be used to boost the precedence of an expression to the level of an atomic expression. 
     1998    #    1. Expressions where there's no ambiguity, like the index for a getitem/getslice or function/method arguments; 
     1999    #    2. Brackets, which can be used to boost the precedence of an expression to the level of an atomic expression. 
    19482000 
    19492001    @spark.production('expr11 ::= none') 
     
    19542006    @spark.production('expr11 ::= float') 
    19552007    @spark.production('expr11 ::= date') 
     2008    @spark.production('expr11 ::= color') 
    19562009    @spark.production('expr11 ::= name') 
    19572010    def expr_atom(self, atom): 
     
    20352088    def expr_callfunc3(self, name, _0, arg0, _1, arg1, _2, arg2, _3): 
    20362089        return CallFunc(name.start, _3.end, name, [arg0, arg1, arg2]) 
     2090 
     2091    @spark.production('expr10 ::= name ( expr0 , expr0 , expr0 , expr0 )') 
     2092    def expr_callfunc4(self, name, _0, arg0, _1, arg1, _2, arg2, _3, arg3, _4): 
     2093        return CallFunc(name.start, _4.end, name, [arg0, arg1, arg2, arg3]) 
    20372094 
    20382095    @spark.production('expr9 ::= expr9 . name') 
     
    24032460    elif isinstance(obj, (datetime.datetime, datetime.date)): 
    24042461        return u"date" 
     2462    elif isinstance(obj, color.Color): 
     2463        return u"color" 
    24052464    elif isinstance(obj, (list, tuple)): 
    24062465        return u"list" 
  • test/test_color.py

    r3629 r3645  
    2020 
    2121 
    22 def test_fromrgba(): 
    23     assert color.Color.fromrgba(0, 0, 0, 0) == (0, 0, 0, 0) 
    24     assert color.Color.fromrgba(4.2, 4.2, 4.2, 4.2) == (255, 255, 255, 255) 
    25  
    26  
    27 def test_fromrgba4(): 
    28     assert color.Color.fromrgba4(0x1, 0x2, 0x3, 0x4) == (0x11, 0x22, 0x33, 0x44) 
    29  
    30  
    31 def test_fromrgba8(): 
    32     assert color.Color.fromrgba8(0x12, 0x34, 0x56, 0x78) == (0x12, 0x34, 0x56, 0x78) 
    33  
    34  
    35 def test_fromint4(): 
    36     assert color.Color.fromint4(0x1234) == (0x11, 0x22, 0x33, 0x44) 
    37  
    38  
    39 def test_fromint8(): 
    40     assert color.Color.fromint8(0x12345678) == (0x12, 0x34, 0x56, 0x78) 
    41  
    42  
    4322def test_fromcss(): 
    4423    assert color.Color.fromcss("red") == (0xff, 0x0, 0x0, 0xff) 
     
    6039 
    6140 
     41def test_str(): 
     42    assert str(color.Color(0x12, 0x34, 0x56)) == "#123456" 
     43    assert str(color.Color(0x12, 0x34, 0x56, 0x78)) == "rgba(18,52,86,0.471)" 
     44 
     45 
    6246def test_r_g_b(): 
    6347    c = color.Color(0x12, 0x34, 0x56, 0x78) 
    64     assert c.r == 0x12/255. 
    65     assert c.g == 0x34/255. 
    66     assert c.b == 0x56/255. 
    67     assert c.a == 0x78/255. 
    68  
    69  
    70 def test_r4_g4_b4(): 
    71     c = color.Color(0x12, 0x34, 0x56, 0x78) 
    72     assert c.r4 == 0x1 
    73     assert c.g4 == 0x3 
    74     assert c.b4 == 0x5 
    75     assert c.a4 == 0x7 
    76  
    77  
    78 def test_r8_g8_b8(): 
    79     c = color.Color(0x12, 0x34, 0x56, 0x78) 
    80     assert c.r8 == 0x12 
    81     assert c.g8 == 0x34 
    82     assert c.b8 == 0x56 
    83     assert c.a8 == 0x78 
    84  
    85  
    86 def test_int4(): 
    87     assert color.Color(0x12, 0x34, 0x56, 0x78).int4 == 0x1357 
    88  
    89  
    90 def test_int8(): 
    91     assert color.Color(0x12, 0x34, 0x56, 0x78).int8 == 0x12345678 
     48    assert c.r() == 0x12 
     49    assert c.g() == 0x34 
     50    assert c.b() == 0x56 
     51    assert c.a() == 0x78 
    9252 
    9353 
    9454def test_rgb(): 
    95     assert color.Color(0x33, 0x66, 0x99, 0xcc).rgb == (0.2, 0.4, 0.6) 
     55    assert color.Color(0x12, 0x34, 0x56, 0x78).rgb() == (0x12, 0x34, 0x56) 
    9656 
    9757 
    9858def test_rgba(): 
    99     assert color.Color(0x33, 0x66, 0x99, 0xcc).rgba == (0.2, 0.4, 0.6, 0.8) 
    100  
    101  
    102 def test_rgb4(): 
    103     assert color.Color(0x12, 0x34, 0x56, 0x78).rgb4 == (0x1, 0x3, 0x5) 
    104  
    105  
    106 def test_rgba4(): 
    107     assert color.Color(0x12, 0x34, 0x56, 0x78).rgba4 == (0x1, 0x3, 0x5, 0x7) 
    108  
    109  
    110 def test_rgb8(): 
    111     assert color.Color(0x12, 0x34, 0x56, 0x78).rgb8 == (0x12, 0x34, 0x56) 
    112  
    113  
    114 def test_rgba8(): 
    115     assert color.Color(0x12, 0x34, 0x56, 0x78).rgba8 == (0x12, 0x34, 0x56, 0x78) 
    116  
    117  
    118 def test_css(): 
    119     assert color.Color(0x12, 0x34, 0x56).css == "#123456" 
    120     assert color.Color(0x12, 0x34, 0x56, 0x78).css == "rgba(18,52,86,0.471)" 
     59    assert color.Color(0x12, 0x34, 0x56, 0x78).rgba() == (0x12, 0x34, 0x56, 0x78) 
    12160 
    12261 
    12362def test_combine(): 
    124     assert color.Color(0x12, 0x34, 0x56).combine(r8=0x78) == (0x78, 0x34, 0x56, 0xff) 
    125     assert color.Color(0x12, 0x34, 0x56).combine(g8=0x78) == (0x12, 0x78, 0x56, 0xff) 
    126     assert color.Color(0x12, 0x34, 0x56).combine(b8=0x78) == (0x12, 0x34, 0x78, 0xff) 
    127     assert color.Color(0x12, 0x34, 0x56).combine(a8=0x78) == (0x12, 0x34, 0x56, 0x78) 
     63    assert color.Color(0x12, 0x34, 0x56).combine(r=0x78) == (0x78, 0x34, 0x56, 0xff) 
     64    assert color.Color(0x12, 0x34, 0x56).combine(g=0x78) == (0x12, 0x78, 0x56, 0xff) 
     65    assert color.Color(0x12, 0x34, 0x56).combine(b=0x78) == (0x12, 0x34, 0x78, 0xff) 
     66    assert color.Color(0x12, 0x34, 0x56).combine(a=0x78) == (0x12, 0x34, 0x56, 0x78) 
     67 
     68 
     69def test_add(): 
     70    assert color.Color(0x12, 0x13, 0x14) + color.Color(0x36, 0x37, 0x38) == color.Color(0x24, 0x25, 0x26) 
     71 
     72 
     73def test_mul(): 
     74    assert 2*color.Color(0x12, 0x34, 0x56) == color.Color(0x24, 0x68, 0xac) 
     75    assert color.Color(0x12, 0x34, 0x56)*2 == color.Color(0x24, 0x68, 0xac) 
     76 
     77 
     78def test_div(): 
     79    assert color.Color(0x24, 0x68, 0xac)/2 == color.Color(0x12, 0x34, 0x56) 
     80 
     81 
     82def test_mod(): 
     83    assert color.Color(0x80, 0x80, 0x80) % color.Color(0xff, 0xff, 0xff) == color.Color(0x80, 0x80, 0x80) 
     84    assert color.Color(0x80, 0x80, 0x80, 0x00) % color.Color(0xff, 0xff, 0xff) == color.Color(0xff, 0xff, 0xff) 
     85    assert color.Color(0x80, 0x80, 0x80, 0x80) % color.Color(0xff, 0xff, 0xff) == color.Color(0xbf, 0xbf, 0xbf) 
  • test/test_ul4.py

    r3641 r3645  
    191191    check('2000-02-29T12:34:56.987654', u'<?print 2000-02-29T12:34:56.987654.isoformat()?>') 
    192192    check('yes', u'<?if 2000-02-29T12:34:56.987654?>yes<?else?>no<?end if?>') 
     193 
     194 
     195def test_color(): 
     196    check('255,255,255,255', u'<?code c = #fff?><?print c[0]?>,<?print c[1]?>,<?print c[2]?>,<?print c[3]?>') 
     197    check('255,255,255,255', u'<?code c = #ffffff?><?print c[0]?>,<?print c[1]?>,<?print c[2]?>,<?print c[3]?>') 
     198    check('18,52,86,255', u'<?code c = #123456?><?print c[0]?>,<?print c[1]?>,<?print c[2]?>,<?print c[3]?>') 
     199    check('17,34,51,68', u'<?code c = #1234?><?print c[0]?>,<?print c[1]?>,<?print c[2]?>,<?print c[3]?>') 
     200    check('18,52,86,120', u'<?code c = #12345678?><?print c[0]?>,<?print c[1]?>,<?print c[2]?>,<?print c[3]?>') 
     201    check('yes', u'<?if #fff?>yes<?else?>no<?end if?>') 
    193202 
    194203 
     
    973982 
    974983 
     984def test_method_r_g_b_a(): 
     985    check('0x11', u'<?code c = #123?><?print hex(c.r())?>') 
     986    check('0x22', u'<?code c = #123?><?print hex(c.g())?>') 
     987    check('0x33', u'<?code c = #123?><?print hex(c.b())?>') 
     988    check('0xff', u'<?code c = #123?><?print hex(c.a())?>') 
     989 
     990 
     991def test_method_hls(): 
     992    check('0', u'<?code c = #fff?><?print int(c.hls()[0])?>') 
     993    check('1', u'<?code c = #fff?><?print int(c.hls()[1])?>') 
     994    check('0', u'<?code c = #fff?><?print int(c.hls()[2])?>') 
     995 
     996 
     997def test_method_hlsa(): 
     998    check('0', u'<?code c = #fff?><?print int(c.hlsa()[0])?>') 
     999    check('1', u'<?code c = #fff?><?print int(c.hlsa()[1])?>') 
     1000    check('0', u'<?code c = #fff?><?print int(c.hlsa()[2])?>') 
     1001    check('1', u'<?code c = #fff?><?print int(c.hlsa()[3])?>') 
     1002 
     1003 
     1004def test_method_hsv(): 
     1005    check('0', u'<?code c = #fff?><?print int(c.hsv()[0])?>') 
     1006    check('0', u'<?code c = #fff?><?print int(c.hsv()[1])?>') 
     1007    check('1', u'<?code c = #fff?><?print int(c.hsv()[2])?>') 
     1008 
     1009 
     1010def test_method_hsva(): 
     1011    check('0', u'<?code c = #fff?><?print int(c.hsva()[0])?>') 
     1012    check('0', u'<?code c = #fff?><?print int(c.hsva()[1])?>') 
     1013    check('1', u'<?code c = #fff?><?print int(c.hsva()[2])?>') 
     1014    check('1', u'<?code c = #fff?><?print int(c.hsva()[3])?>') 
     1015 
     1016 
    9751017def test_render(): 
    9761018    t = ul4c.compile(u'<?print prefix?><?print data?><?print suffix?>')