Home | History | Annotate | Download | only in main
      1 #
      2 # Copyright 2009 VMware, Inc.
      3 # Copyright 2014 Intel Corporation
      4 # All Rights Reserved.
      5 #
      6 # Permission is hereby granted, free of charge, to any person obtaining a
      7 # copy of this software and associated documentation files (the
      8 # "Software"), to deal in the Software without restriction, including
      9 # without limitation the rights to use, copy, modify, merge, publish,
     10 # distribute, sub license, and/or sell copies of the Software, and to
     11 # permit persons to whom the Software is furnished to do so, subject to
     12 # the following conditions:
     13 #
     14 # The above copyright notice and this permission notice (including the
     15 # next paragraph) shall be included in all copies or substantial portions
     16 # of the Software.
     17 #
     18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     21 # IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
     22 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     23 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     24 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25 
     26 import sys
     27 
     28 VOID = 'x'
     29 UNSIGNED = 'u'
     30 SIGNED = 's'
     31 FLOAT = 'f'
     32 
     33 ARRAY = 'array'
     34 PACKED = 'packed'
     35 OTHER = 'other'
     36 
     37 RGB = 'rgb'
     38 SRGB = 'srgb'
     39 YUV = 'yuv'
     40 ZS = 'zs'
     41 
     42 VERY_LARGE = 99999999999999999999999
     43 
     44 class Channel:
     45    """Describes a color channel."""
     46 
     47    def __init__(self, type, norm, size):
     48       self.type = type
     49       self.norm = norm
     50       self.size = size
     51       self.sign = type in (SIGNED, FLOAT)
     52       self.name = None # Set when the channels are added to the format
     53       self.shift = -1 # Set when the channels are added to the format
     54       self.index = -1 # Set when the channels are added to the format
     55 
     56    def __str__(self):
     57       s = str(self.type)
     58       if self.norm:
     59          s += 'n'
     60       s += str(self.size)
     61       return s
     62 
     63    def __eq__(self, other):
     64       return self.type == other.type and self.norm == other.norm and self.size == other.size
     65 
     66    def max(self):
     67       """Returns the maximum representable number."""
     68       if self.type == FLOAT:
     69          return VERY_LARGE
     70       if self.norm:
     71          return 1
     72       if self.type == UNSIGNED:
     73          return (1 << self.size) - 1
     74       if self.type == SIGNED:
     75          return (1 << (self.size - 1)) - 1
     76       assert False
     77 
     78    def min(self):
     79       """Returns the minimum representable number."""
     80       if self.type == FLOAT:
     81          return -VERY_LARGE
     82       if self.type == UNSIGNED:
     83          return 0
     84       if self.norm:
     85          return -1
     86       if self.type == SIGNED:
     87          return -(1 << (self.size - 1))
     88       assert False
     89 
     90    def one(self):
     91       """Returns the value that represents 1.0f."""
     92       if self.type == UNSIGNED:
     93          return (1 << self.size) - 1
     94       if self.type == SIGNED:
     95          return (1 << (self.size - 1)) - 1
     96       else:
     97          return 1
     98 
     99    def datatype(self):
    100       """Returns the datatype corresponding to a channel type and size"""
    101       return _get_datatype(self.type, self.size)
    102 
    103 class Swizzle:
    104    """Describes a swizzle operation.
    105 
    106    A Swizzle is a mapping from one set of channels in one format to the
    107    channels in another.  Each channel in the destination format is
    108    associated with one of the following constants:
    109 
    110     * SWIZZLE_X: The first channel in the source format
    111     * SWIZZLE_Y: The second channel in the source format
    112     * SWIZZLE_Z: The third channel in the source format
    113     * SWIZZLE_W: The fourth channel in the source format
    114     * SWIZZLE_ZERO: The numeric constant 0
    115     * SWIZZLE_ONE: THe numeric constant 1
    116     * SWIZZLE_NONE: No data available for this channel
    117 
    118    Sometimes a Swizzle is represented by a 4-character string.  In this
    119    case, the source channels are represented by the characters "x", "y",
    120    "z", and "w"; the numeric constants are represented as "0" and "1"; and
    121    no mapping is represented by "_".  For instance, the map from
    122    luminance-alpha to rgba is given by "xxxy" because each of the three rgb
    123    channels maps to the first luminance-alpha channel and the alpha channel
    124    maps to second luminance-alpha channel.  The mapping from bgr to rgba is
    125    given by "zyx1" because the first three colors are reversed and alpha is
    126    always 1.
    127    """
    128 
    129    __identity_str = 'xyzw01_'
    130 
    131    SWIZZLE_X = 0
    132    SWIZZLE_Y = 1
    133    SWIZZLE_Z = 2
    134    SWIZZLE_W = 3
    135    SWIZZLE_ZERO = 4
    136    SWIZZLE_ONE = 5
    137    SWIZZLE_NONE = 6
    138 
    139    def __init__(self, swizzle):
    140       """Creates a Swizzle object from a string or array."""
    141       if isinstance(swizzle, str):
    142          swizzle = [Swizzle.__identity_str.index(c) for c in swizzle]
    143       else:
    144          swizzle = list(swizzle)
    145          for s in swizzle:
    146             assert isinstance(s, int) and 0 <= s and s <= Swizzle.SWIZZLE_NONE
    147 
    148       assert len(swizzle) <= 4
    149 
    150       self.__list = swizzle + [Swizzle.SWIZZLE_NONE] * (4 - len(swizzle))
    151       assert len(self.__list) == 4
    152 
    153    def __iter__(self):
    154       """Returns an iterator that iterates over this Swizzle.
    155 
    156       The values that the iterator produces are described by the SWIZZLE_*
    157       constants.
    158       """
    159       return self.__list.__iter__()
    160 
    161    def __str__(self):
    162       """Returns a string representation of this Swizzle."""
    163       return ''.join(Swizzle.__identity_str[i] for i in self.__list)
    164 
    165    def __getitem__(self, idx):
    166       """Returns the SWIZZLE_* constant for the given destination channel.
    167 
    168       Valid values for the destination channel include any of the SWIZZLE_*
    169       constants or any of the following single-character strings: "x", "y",
    170       "z", "w", "r", "g", "b", "a", "z" "s".
    171       """
    172 
    173       if isinstance(idx, int):
    174          assert idx >= Swizzle.SWIZZLE_X and idx <= Swizzle.SWIZZLE_NONE
    175          if idx <= Swizzle.SWIZZLE_W:
    176             return self.__list.__getitem__(idx)
    177          else:
    178             return idx
    179       elif isinstance(idx, str):
    180          if idx in 'xyzw':
    181             idx = 'xyzw'.find(idx)
    182          elif idx in 'rgba':
    183             idx = 'rgba'.find(idx)
    184          elif idx in 'zs':
    185             idx = 'zs'.find(idx)
    186          else:
    187             assert False
    188          return self.__list.__getitem__(idx)
    189       else:
    190          assert False
    191 
    192    def __mul__(self, other):
    193       """Returns the composition of this Swizzle with another Swizzle.
    194 
    195       The resulting swizzle is such that, for any valid input to
    196       __getitem__, (a * b)[i] = a[b[i]].
    197       """
    198       assert isinstance(other, Swizzle)
    199       return Swizzle(self[x] for x in other)
    200 
    201    def inverse(self):
    202       """Returns a pseudo-inverse of this swizzle.
    203 
    204       Since swizzling isn't necisaraly a bijection, a Swizzle can never
    205       be truely inverted.  However, the swizzle returned is *almost* the
    206       inverse of this swizzle in the sense that, for each i in range(3),
    207       a[a.inverse()[i]] is either i or SWIZZLE_NONE.  If swizzle is just
    208       a permutation with no channels added or removed, then this
    209       function returns the actual inverse.
    210 
    211       This "pseudo-inverse" idea can be demonstrated by mapping from
    212       luminance-alpha to rgba that is given by "xxxy".  To get from rgba
    213       to lumanence-alpha, we use Swizzle("xxxy").inverse() or "xw__".
    214       This maps the first component in the lumanence-alpha texture is
    215       the red component of the rgba image and the second to the alpha
    216       component, exactly as you would expect.
    217       """
    218       rev = [Swizzle.SWIZZLE_NONE] * 4
    219       for i in xrange(4):
    220          for j in xrange(4):
    221             if self.__list[j] == i and rev[i] == Swizzle.SWIZZLE_NONE:
    222                rev[i] = j
    223       return Swizzle(rev)
    224 
    225 
    226 class Format:
    227    """Describes a pixel format."""
    228 
    229    def __init__(self, name, layout, block_width, block_height, block_depth, channels, swizzle, colorspace):
    230       """Constructs a Format from some metadata and a list of channels.
    231 
    232       The channel objects must be unique to this Format and should not be
    233       re-used to construct another Format.  This is because certain channel
    234       information such as shift, offset, and the channel name are set when
    235       the Format is created and are calculated based on the entire list of
    236       channels.
    237 
    238       Arguments:
    239       name -- Name of the format such as 'MESA_FORMAT_A8R8G8B8'
    240       layout -- One of 'array', 'packed' 'other', or a compressed layout
    241       block_width -- The block width if the format is compressed, 1 otherwise
    242       block_height -- The block height if the format is compressed, 1 otherwise
    243       block_depth -- The block depth if the format is compressed, 1 otherwise
    244       channels -- A list of Channel objects
    245       swizzle -- A Swizzle from this format to rgba
    246       colorspace -- one of 'rgb', 'srgb', 'yuv', or 'zs'
    247       """
    248       self.name = name
    249       self.layout = layout
    250       self.block_width = block_width
    251       self.block_height = block_height
    252       self.block_depth = block_depth
    253       self.channels = channels
    254       assert isinstance(swizzle, Swizzle)
    255       self.swizzle = swizzle
    256       self.name = name
    257       assert colorspace in (RGB, SRGB, YUV, ZS)
    258       self.colorspace = colorspace
    259 
    260       # Name the channels
    261       chan_names = ['']*4
    262       if self.colorspace in (RGB, SRGB):
    263          for (i, s) in enumerate(swizzle):
    264             if s < 4:
    265                chan_names[s] += 'rgba'[i]
    266       elif colorspace == ZS:
    267          for (i, s) in enumerate(swizzle):
    268             if s < 4:
    269                chan_names[s] += 'zs'[i]
    270       else:
    271          chan_names = ['x', 'y', 'z', 'w']
    272 
    273       for c, name in zip(self.channels, chan_names):
    274          assert c.name is None
    275          if name == 'rgb':
    276             c.name = 'l'
    277          elif name == 'rgba':
    278             c.name = 'i'
    279          elif name == '':
    280             c.name = 'x'
    281          else:
    282             c.name = name
    283 
    284       # Set indices and offsets
    285       if self.layout == PACKED:
    286          shift = 0
    287          for channel in self.channels:
    288             assert channel.shift == -1
    289             channel.shift = shift
    290             shift += channel.size
    291       for idx, channel in enumerate(self.channels):
    292          assert channel.index == -1
    293          channel.index = idx
    294       else:
    295          pass # Shift means nothing here
    296 
    297    def __str__(self):
    298       return self.name
    299 
    300    def short_name(self):
    301       """Returns a short name for a format.
    302 
    303       The short name should be suitable to be used as suffix in function
    304       names.
    305       """
    306 
    307       name = self.name
    308       if name.startswith('MESA_FORMAT_'):
    309          name = name[len('MESA_FORMAT_'):]
    310       name = name.lower()
    311       return name
    312 
    313    def block_size(self):
    314       """Returns the block size (in bits) of the format."""
    315       size = 0
    316       for channel in self.channels:
    317          size += channel.size
    318       return size
    319 
    320    def num_channels(self):
    321       """Returns the number of channels in the format."""
    322       nr_channels = 0
    323       for channel in self.channels:
    324          if channel.size:
    325             nr_channels += 1
    326       return nr_channels
    327 
    328    def array_element(self):
    329       """Returns a non-void channel if this format is an array, otherwise None.
    330 
    331       If the returned channel is not None, then this format can be
    332       considered to be an array of num_channels() channels identical to the
    333       returned channel.
    334       """
    335       if self.layout == ARRAY:
    336          return self.channels[0]
    337       elif self.layout == PACKED:
    338          ref_channel = self.channels[0]
    339          if ref_channel.type == VOID:
    340             ref_channel = self.channels[1]
    341          for channel in self.channels:
    342             if channel.size == 0 or channel.type == VOID:
    343                continue
    344             if channel.size != ref_channel.size or channel.size % 8 != 0:
    345                return None
    346             if channel.type != ref_channel.type:
    347                return None
    348             if channel.norm != ref_channel.norm:
    349                return None
    350          return ref_channel
    351       else:
    352          return None
    353 
    354    def is_array(self):
    355       """Returns true if this format can be considered an array format.
    356 
    357       This function will return true if self.layout == 'array'.  However,
    358       some formats, such as MESA_FORMAT_A8G8B8R8, can be considered as
    359       array formats even though they are technically packed.
    360       """
    361       return self.array_element() != None
    362 
    363    def is_compressed(self):
    364       """Returns true if this is a compressed format."""
    365       return self.block_width != 1 or self.block_height != 1 or self.block_depth != 1
    366 
    367    def is_int(self):
    368       """Returns true if this format is an integer format.
    369 
    370       See also: is_norm()
    371       """
    372       if self.layout not in (ARRAY, PACKED):
    373          return False
    374       for channel in self.channels:
    375          if channel.type not in (VOID, UNSIGNED, SIGNED):
    376             return False
    377       return True
    378 
    379    def is_float(self):
    380       """Returns true if this format is an floating-point format."""
    381       if self.layout not in (ARRAY, PACKED):
    382          return False
    383       for channel in self.channels:
    384          if channel.type not in (VOID, FLOAT):
    385             return False
    386       return True
    387 
    388    def channel_type(self):
    389       """Returns the type of the channels in this format."""
    390       _type = VOID
    391       for c in self.channels:
    392          if c.type == VOID:
    393             continue
    394          if _type == VOID:
    395             _type = c.type
    396          assert c.type == _type
    397       return _type
    398 
    399    def channel_size(self):
    400       """Returns the size (in bits) of the channels in this format.
    401 
    402       This function should only be called if all of the channels have the
    403       same size.  This is always the case if is_array() returns true.
    404       """
    405       size = None
    406       for c in self.channels:
    407          if c.type == VOID:
    408             continue
    409          if size is None:
    410             size = c.size
    411          assert c.size == size
    412       return size
    413 
    414    def max_channel_size(self):
    415       """Returns the size of the largest channel."""
    416       size = 0
    417       for c in self.channels:
    418          if c.type == VOID:
    419             continue
    420          size = max(size, c.size)
    421       return size
    422 
    423    def is_normalized(self):
    424       """Returns true if this format is normalized.
    425 
    426       While only integer formats can be normalized, not all integer formats
    427       are normalized.  Normalized integer formats are those where the
    428       integer value is re-interpreted as a fixed point value in the range
    429       [0, 1].
    430       """
    431       norm = None
    432       for c in self.channels:
    433          if c.type == VOID:
    434             continue
    435          if norm is None:
    436             norm = c.norm
    437          assert c.norm == norm
    438       return norm
    439 
    440    def has_channel(self, name):
    441       """Returns true if this format has the given channel."""
    442       if self.is_compressed():
    443          # Compressed formats are a bit tricky because the list of channels
    444          # contains a single channel of type void.  Since we don't have any
    445          # channel information there, we pull it from the swizzle.
    446          if str(self.swizzle) == 'xxxx':
    447             return name == 'i'
    448          elif str(self.swizzle)[0:3] in ('xxx', 'yyy'):
    449             if name == 'l':
    450                return True
    451             elif name == 'a':
    452                return self.swizzle['a'] <= Swizzle.SWIZZLE_W
    453             else:
    454                return False
    455          elif name in 'rgba':
    456             return self.swizzle[name] <= Swizzle.SWIZZLE_W
    457          else:
    458             return False
    459       else:
    460          for channel in self.channels:
    461             if channel.name == name:
    462                return True
    463          return False
    464 
    465    def get_channel(self, name):
    466       """Returns the channel with the given name if it exists."""
    467       for channel in self.channels:
    468          if channel.name == name:
    469             return channel
    470       return None
    471 
    472    def datatype(self):
    473       """Returns the datatype corresponding to a format's channel type and size"""
    474       if self.layout == PACKED:
    475          if self.block_size() == 8:
    476             return 'uint8_t'
    477          if self.block_size() == 16:
    478             return 'uint16_t'
    479          if self.block_size() == 32:
    480             return 'uint32_t'
    481          else:
    482             assert False
    483       else:
    484          return _get_datatype(self.channel_type(), self.channel_size())
    485 
    486 def _get_datatype(type, size):
    487    if type == FLOAT:
    488       if size == 32:
    489          return 'float'
    490       elif size == 16:
    491          return 'uint16_t'
    492       else:
    493          assert False
    494    elif type == UNSIGNED:
    495       if size <= 8:
    496          return 'uint8_t'
    497       elif size <= 16:
    498          return 'uint16_t'
    499       elif size <= 32:
    500          return 'uint32_t'
    501       else:
    502          assert False
    503    elif type == SIGNED:
    504       if size <= 8:
    505          return 'int8_t'
    506       elif size <= 16:
    507          return 'int16_t'
    508       elif size <= 32:
    509          return 'int32_t'
    510       else:
    511          assert False
    512    else:
    513       assert False
    514 
    515 def _parse_channels(fields, layout, colorspace, swizzle):
    516    channels = []
    517    for field in fields:
    518       if not field:
    519          continue
    520 
    521       type = field[0] if field[0] else 'x'
    522 
    523       if field[1] == 'n':
    524          norm = True
    525          size = int(field[2:])
    526       else:
    527          norm = False
    528          size = int(field[1:])
    529 
    530       channel = Channel(type, norm, size)
    531       channels.append(channel)
    532 
    533    return channels
    534 
    535 def parse(filename):
    536    """Parse a format description in CSV format.
    537 
    538    This function parses the given CSV file and returns an iterable of
    539    channels."""
    540 
    541    with open(filename) as stream:
    542       for line in stream:
    543          try:
    544             comment = line.index('#')
    545          except ValueError:
    546             pass
    547          else:
    548             line = line[:comment]
    549          line = line.strip()
    550          if not line:
    551             continue
    552 
    553          fields = [field.strip() for field in line.split(',')]
    554 
    555          name = fields[0]
    556          layout = fields[1]
    557          block_width = int(fields[2])
    558          block_height = int(fields[3])
    559          block_depth = int(fields[4])
    560          colorspace = fields[10]
    561 
    562          try:
    563             swizzle = Swizzle(fields[9])
    564          except:
    565             sys.exit("error parsing swizzle for format " + name)
    566          channels = _parse_channels(fields[5:9], layout, colorspace, swizzle)
    567 
    568          yield Format(name, layout, block_width, block_height, block_depth, channels, swizzle, colorspace)
    569