Home | History | Annotate | Download | only in util
      1 #!/usr/bin/env python
      2 
      3 '''
      4 /**************************************************************************
      5  *
      6  * Copyright 2009 VMware, Inc.
      7  * All Rights Reserved.
      8  *
      9  * Permission is hereby granted, free of charge, to any person obtaining a
     10  * copy of this software and associated documentation files (the
     11  * "Software"), to deal in the Software without restriction, including
     12  * without limitation the rights to use, copy, modify, merge, publish,
     13  * distribute, sub license, and/or sell copies of the Software, and to
     14  * permit persons to whom the Software is furnished to do so, subject to
     15  * the following conditions:
     16  *
     17  * The above copyright notice and this permission notice (including the
     18  * next paragraph) shall be included in all copies or substantial portions
     19  * of the Software.
     20  *
     21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     24  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
     25  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     26  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     27  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     28  *
     29  **************************************************************************/
     30 '''
     31 
     32 
     33 VOID, UNSIGNED, SIGNED, FIXED, FLOAT = range(5)
     34 
     35 SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W, SWIZZLE_0, SWIZZLE_1, SWIZZLE_NONE, = range(7)
     36 
     37 PLAIN = 'plain'
     38 
     39 RGB = 'rgb'
     40 SRGB = 'srgb'
     41 YUV = 'yuv'
     42 ZS = 'zs'
     43 
     44 
     45 def is_pot(x):
     46    return (x & (x - 1)) == 0
     47 
     48 
     49 VERY_LARGE = 99999999999999999999999
     50 
     51 
     52 class Channel:
     53     '''Describe the channel of a color channel.'''
     54     
     55     def __init__(self, type, norm, pure, size, name = ''):
     56         self.type = type
     57         self.norm = norm
     58         self.pure = pure
     59         self.size = size
     60         self.sign = type in (SIGNED, FIXED, FLOAT)
     61         self.name = name
     62 
     63     def __str__(self):
     64         s = str(self.type)
     65         if self.norm:
     66             s += 'n'
     67         if self.pure:
     68             s += 'p'
     69         s += str(self.size)
     70         return s
     71 
     72     def __eq__(self, other):
     73         return self.type == other.type and self.norm == other.norm and self.pure == other.pure and self.size == other.size
     74 
     75     def max(self):
     76         '''Maximum representable number.'''
     77         if self.type == FLOAT:
     78             return VERY_LARGE
     79         if self.type == FIXED:
     80             return (1 << (self.size/2)) - 1
     81         if self.norm:
     82             return 1
     83         if self.type == UNSIGNED:
     84             return (1 << self.size) - 1
     85         if self.type == SIGNED:
     86             return (1 << (self.size - 1)) - 1
     87         assert False
     88     
     89     def min(self):
     90         '''Minimum representable number.'''
     91         if self.type == FLOAT:
     92             return -VERY_LARGE
     93         if self.type == FIXED:
     94             return -(1 << (self.size/2))
     95         if self.type == UNSIGNED:
     96             return 0
     97         if self.norm:
     98             return -1
     99         if self.type == SIGNED:
    100             return -(1 << (self.size - 1))
    101         assert False
    102 
    103 
    104 class Format:
    105     '''Describe a pixel format.'''
    106 
    107     def __init__(self, name, layout, block_width, block_height, le_channels, le_swizzles, be_channels, be_swizzles, colorspace):
    108         self.name = name
    109         self.layout = layout
    110         self.block_width = block_width
    111         self.block_height = block_height
    112         self.le_channels = le_channels
    113         self.le_swizzles = le_swizzles
    114         self.be_channels = be_channels
    115         self.be_swizzles = be_swizzles
    116         self.name = name
    117         self.colorspace = colorspace
    118 
    119     def __str__(self):
    120         return self.name
    121 
    122     def short_name(self):
    123         '''Make up a short norm for a format, suitable to be used as suffix in
    124         function names.'''
    125 
    126         name = self.name
    127         if name.startswith('PIPE_FORMAT_'):
    128             name = name[len('PIPE_FORMAT_'):]
    129         name = name.lower()
    130         return name
    131 
    132     def block_size(self):
    133         size = 0
    134         for channel in self.le_channels:
    135             size += channel.size
    136         return size
    137 
    138     def nr_channels(self):
    139         nr_channels = 0
    140         for channel in self.le_channels:
    141             if channel.size:
    142                 nr_channels += 1
    143         return nr_channels
    144 
    145     def array_element(self):
    146         if self.layout != PLAIN:
    147             return None
    148         ref_channel = self.le_channels[0]
    149         if ref_channel.type == VOID:
    150            ref_channel = self.le_channels[1]
    151         for channel in self.le_channels:
    152             if channel.size and (channel.size != ref_channel.size or channel.size % 8):
    153                 return None
    154             if channel.type != VOID:
    155                 if channel.type != ref_channel.type:
    156                     return None
    157                 if channel.norm != ref_channel.norm:
    158                     return None
    159                 if channel.pure != ref_channel.pure:
    160                     return None
    161         return ref_channel
    162 
    163     def is_array(self):
    164         return self.array_element() != None
    165 
    166     def is_mixed(self):
    167         if self.layout != PLAIN:
    168             return False
    169         ref_channel = self.le_channels[0]
    170         if ref_channel.type == VOID:
    171            ref_channel = self.le_channels[1]
    172         for channel in self.le_channels[1:]:
    173             if channel.type != VOID:
    174                 if channel.type != ref_channel.type:
    175                     return True
    176                 if channel.norm != ref_channel.norm:
    177                     return True
    178                 if channel.pure != ref_channel.pure:
    179                     return True
    180         return False
    181 
    182     def is_pot(self):
    183         return is_pot(self.block_size())
    184 
    185     def is_int(self):
    186         if self.layout != PLAIN:
    187             return False
    188         for channel in self.le_channels:
    189             if channel.type not in (VOID, UNSIGNED, SIGNED):
    190                 return False
    191         return True
    192 
    193     def is_float(self):
    194         if self.layout != PLAIN:
    195             return False
    196         for channel in self.le_channels:
    197             if channel.type not in (VOID, FLOAT):
    198                 return False
    199         return True
    200 
    201     def is_bitmask(self):
    202         if self.layout != PLAIN:
    203             return False
    204         if self.block_size() not in (8, 16, 32):
    205             return False
    206         for channel in self.le_channels:
    207             if channel.type not in (VOID, UNSIGNED, SIGNED):
    208                 return False
    209         return True
    210 
    211     def is_pure_color(self):
    212         if self.layout != PLAIN or self.colorspace == ZS:
    213             return False
    214         pures = [channel.pure
    215                  for channel in self.le_channels
    216                  if channel.type != VOID]
    217         for x in pures:
    218            assert x == pures[0]
    219         return pures[0]
    220 
    221     def channel_type(self):
    222         types = [channel.type
    223                  for channel in self.le_channels
    224                  if channel.type != VOID]
    225         for x in types:
    226            assert x == types[0]
    227         return types[0]
    228 
    229     def is_pure_signed(self):
    230         return self.is_pure_color() and self.channel_type() == SIGNED
    231 
    232     def is_pure_unsigned(self):
    233         return self.is_pure_color() and self.channel_type() == UNSIGNED
    234 
    235     def has_channel(self, id):
    236         return self.le_swizzles[id] != SWIZZLE_NONE
    237 
    238     def has_depth(self):
    239         return self.colorspace == ZS and self.has_channel(0)
    240 
    241     def has_stencil(self):
    242         return self.colorspace == ZS and self.has_channel(1)
    243 
    244     def stride(self):
    245         return self.block_size()/8
    246 
    247 
    248 _type_parse_map = {
    249     '':  VOID,
    250     'x': VOID,
    251     'u': UNSIGNED,
    252     's': SIGNED,
    253     'h': FIXED,
    254     'f': FLOAT,
    255 }
    256 
    257 _swizzle_parse_map = {
    258     'x': SWIZZLE_X,
    259     'y': SWIZZLE_Y,
    260     'z': SWIZZLE_Z,
    261     'w': SWIZZLE_W,
    262     '0': SWIZZLE_0,
    263     '1': SWIZZLE_1,
    264     '_': SWIZZLE_NONE,
    265 }
    266 
    267 def _parse_channels(fields, layout, colorspace, swizzles):
    268     if layout == PLAIN:
    269         names = ['']*4
    270         if colorspace in (RGB, SRGB):
    271             for i in range(4):
    272                 swizzle = swizzles[i]
    273                 if swizzle < 4:
    274                     names[swizzle] += 'rgba'[i]
    275         elif colorspace == ZS:
    276             for i in range(4):
    277                 swizzle = swizzles[i]
    278                 if swizzle < 4:
    279                     names[swizzle] += 'zs'[i]
    280         else:
    281             assert False
    282         for i in range(4):
    283             if names[i] == '':
    284                 names[i] = 'x'
    285     else:
    286         names = ['x', 'y', 'z', 'w']
    287 
    288     channels = []
    289     for i in range(0, 4):
    290         field = fields[i]
    291         if field:
    292             type = _type_parse_map[field[0]]
    293             if field[1] == 'n':
    294                 norm = True
    295                 pure = False
    296                 size = int(field[2:])
    297             elif field[1] == 'p':
    298                 pure = True
    299                 norm = False
    300                 size = int(field[2:])
    301             else:
    302                 norm = False
    303                 pure = False
    304                 size = int(field[1:])
    305         else:
    306             type = VOID
    307             norm = False
    308             pure = False
    309             size = 0
    310         channel = Channel(type, norm, pure, size, names[i])
    311         channels.append(channel)
    312 
    313     return channels
    314 
    315 def parse(filename):
    316     '''Parse the format description in CSV format in terms of the
    317     Channel and Format classes above.'''
    318 
    319     stream = open(filename)
    320     formats = []
    321     for line in stream:
    322         try:
    323             comment = line.index('#')
    324         except ValueError:
    325             pass
    326         else:
    327             line = line[:comment]
    328         line = line.strip()
    329         if not line:
    330             continue
    331 
    332         fields = [field.strip() for field in line.split(',')]
    333         if len (fields) == 10:
    334            fields += fields[4:9]
    335         assert len (fields) == 15
    336         
    337         name = fields[0]
    338         layout = fields[1]
    339         block_width, block_height = map(int, fields[2:4])
    340         colorspace = fields[9]
    341 
    342         le_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[8]]
    343         le_channels = _parse_channels(fields[4:8], layout, colorspace, le_swizzles)
    344 
    345         be_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[14]]
    346         be_channels = _parse_channels(fields[10:14], layout, colorspace, be_swizzles)
    347 
    348         le_shift = 0
    349         for channel in le_channels:
    350             channel.shift = le_shift
    351             le_shift += channel.size
    352 
    353         be_shift = 0
    354         for channel in be_channels[3::-1]:
    355             channel.shift = be_shift
    356             be_shift += channel.size
    357 
    358         assert le_shift == be_shift
    359         for i in range(4):
    360             assert (le_swizzles[i] != SWIZZLE_NONE) == (be_swizzles[i] != SWIZZLE_NONE)
    361 
    362         format = Format(name, layout, block_width, block_height, le_channels, le_swizzles, be_channels, be_swizzles, colorspace)
    363         formats.append(format)
    364     return formats
    365 
    366