| |
| ''' |
| /************************************************************************** |
| * |
| * Copyright 2009 VMware, Inc. |
| * All Rights Reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sub license, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the |
| * next paragraph) shall be included in all copies or substantial portions |
| * of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. |
| * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR |
| * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| * |
| **************************************************************************/ |
| ''' |
| |
| |
| import copy |
| |
| VOID, UNSIGNED, SIGNED, FIXED, FLOAT = range(5) |
| |
| SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W, SWIZZLE_0, SWIZZLE_1, SWIZZLE_NONE, = range(7) |
| |
| PLAIN = 'plain' |
| |
| RGB = 'rgb' |
| SRGB = 'srgb' |
| YUV = 'yuv' |
| ZS = 'zs' |
| |
| |
| def is_pot(x): |
| return (x & (x - 1)) == 0 |
| |
| |
| VERY_LARGE = 99999999999999999999999 |
| |
| |
| class Channel: |
| '''Describe the channel of a color channel.''' |
| |
| def __init__(self, type, norm, pure, size, name=''): |
| self.type = type |
| self.norm = norm |
| self.pure = pure |
| self.size = size |
| self.sign = type in (SIGNED, FIXED, FLOAT) |
| self.name = name |
| |
| def __str__(self): |
| s = str(self.type) |
| if self.norm: |
| s += 'n' |
| if self.pure: |
| s += 'p' |
| s += str(self.size) |
| return s |
| |
| def __repr__(self): |
| return "Channel({})".format(self.__str__()) |
| |
| def __eq__(self, other): |
| if other is None: |
| return False |
| |
| return self.type == other.type and self.norm == other.norm and self.pure == other.pure and self.size == other.size |
| |
| def __ne__(self, other): |
| return not self == other |
| |
| def max(self): |
| '''Maximum representable number.''' |
| if self.type == FLOAT: |
| return VERY_LARGE |
| if self.type == FIXED: |
| return (1 << (self.size // 2)) - 1 |
| if self.norm: |
| return 1 |
| if self.type == UNSIGNED: |
| return (1 << self.size) - 1 |
| if self.type == SIGNED: |
| return (1 << (self.size - 1)) - 1 |
| assert False |
| |
| def min(self): |
| '''Minimum representable number.''' |
| if self.type == FLOAT: |
| return -VERY_LARGE |
| if self.type == FIXED: |
| return -(1 << (self.size // 2)) |
| if self.type == UNSIGNED: |
| return 0 |
| if self.norm: |
| return -1 |
| if self.type == SIGNED: |
| return -(1 << (self.size - 1)) |
| assert False |
| |
| |
| class Format: |
| '''Describe a pixel format.''' |
| |
| def __init__(self, name, layout, block_width, block_height, block_depth, le_channels, le_swizzles, be_channels, be_swizzles, colorspace): |
| self.name = name |
| self.layout = layout |
| self.block_width = block_width |
| self.block_height = block_height |
| self.block_depth = block_depth |
| self.colorspace = colorspace |
| |
| self.le_channels = le_channels |
| self.le_swizzles = le_swizzles |
| |
| le_shift = 0 |
| for channel in self.le_channels: |
| channel.shift = le_shift |
| le_shift += channel.size |
| |
| if be_channels: |
| if self.is_array(): |
| print( |
| "{} is an array format and should not include BE swizzles in the CSV".format(self.name)) |
| exit(1) |
| if self.is_bitmask(): |
| print( |
| "{} is a bitmask format and should not include BE swizzles in the CSV".format(self.name)) |
| exit(1) |
| self.be_channels = be_channels |
| self.be_swizzles = be_swizzles |
| elif self.is_bitmask() and not self.is_array(): |
| # Bitmask formats are "load a word the size of the block and |
| # bitshift channels out of it." However, the channel shifts |
| # defined in u_format_table.c are numbered right-to-left on BE |
| # for some historical reason (see below), which is hard to |
| # change due to llvmpipe, so we also have to flip the channel |
| # order and the channel-to-rgba swizzle values to read |
| # right-to-left from the defined (non-VOID) channels so that the |
| # correct shifts happen. |
| # |
| # This is nonsense, but it's the nonsense that makes |
| # u_format_test pass and you get the right colors in softpipe at |
| # least. |
| chans = self.nr_channels() |
| self.be_channels = self.le_channels[chans - |
| 1::-1] + self.le_channels[chans:4] |
| |
| xyzw = [SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W] |
| chan_map = {SWIZZLE_X: xyzw[chans - 1] if chans >= 1 else SWIZZLE_X, |
| SWIZZLE_Y: xyzw[chans - 2] if chans >= 2 else SWIZZLE_X, |
| SWIZZLE_Z: xyzw[chans - 3] if chans >= 3 else SWIZZLE_X, |
| SWIZZLE_W: xyzw[chans - 4] if chans >= 4 else SWIZZLE_X, |
| SWIZZLE_1: SWIZZLE_1, |
| SWIZZLE_0: SWIZZLE_0, |
| SWIZZLE_NONE: SWIZZLE_NONE} |
| self.be_swizzles = [chan_map[s] for s in self.le_swizzles] |
| else: |
| self.be_channels = copy.deepcopy(le_channels) |
| self.be_swizzles = le_swizzles |
| |
| be_shift = 0 |
| for channel in reversed(self.be_channels): |
| channel.shift = be_shift |
| be_shift += channel.size |
| |
| assert le_shift == be_shift |
| for i in range(4): |
| assert (self.le_swizzles[i] != SWIZZLE_NONE) == ( |
| self.be_swizzles[i] != SWIZZLE_NONE) |
| |
| def __str__(self): |
| return self.name |
| |
| def short_name(self): |
| '''Make up a short norm for a format, suitable to be used as suffix in |
| function names.''' |
| |
| name = self.name |
| if name.startswith('PIPE_FORMAT_'): |
| name = name[len('PIPE_FORMAT_'):] |
| name = name.lower() |
| return name |
| |
| def block_size(self): |
| size = 0 |
| for channel in self.le_channels: |
| size += channel.size |
| return size |
| |
| def nr_channels(self): |
| nr_channels = 0 |
| for channel in self.le_channels: |
| if channel.size: |
| nr_channels += 1 |
| return nr_channels |
| |
| def array_element(self): |
| if self.layout != PLAIN: |
| return None |
| ref_channel = self.le_channels[0] |
| if ref_channel.type == VOID: |
| ref_channel = self.le_channels[1] |
| for channel in self.le_channels: |
| if channel.size and (channel.size != ref_channel.size or channel.size % 8): |
| return None |
| if channel.type != VOID: |
| if channel.type != ref_channel.type: |
| return None |
| if channel.norm != ref_channel.norm: |
| return None |
| if channel.pure != ref_channel.pure: |
| return None |
| return ref_channel |
| |
| def is_array(self): |
| return self.array_element() != None |
| |
| def is_mixed(self): |
| if self.layout != PLAIN: |
| return False |
| ref_channel = self.le_channels[0] |
| if ref_channel.type == VOID: |
| ref_channel = self.le_channels[1] |
| for channel in self.le_channels[1:]: |
| if channel.type != VOID: |
| if channel.type != ref_channel.type: |
| return True |
| if channel.norm != ref_channel.norm: |
| return True |
| if channel.pure != ref_channel.pure: |
| return True |
| return False |
| |
| def is_compressed(self): |
| for channel in self.le_channels: |
| if channel.type != VOID: |
| return False |
| return True |
| |
| def is_unorm(self): |
| # Non-compressed formats all have unorm or srgb in their name. |
| for keyword in ['_UNORM', '_SRGB']: |
| if keyword in self.name: |
| return True |
| |
| # All the compressed formats in GLES3.2 and GL4.6 ("Table 8.14: Generic |
| # and specific compressed internal formats.") that aren't snorm for |
| # border colors are unorm, other than BPTC_*_FLOAT. |
| return self.is_compressed() and not ('FLOAT' in self.name or self.is_snorm()) |
| |
| def is_snorm(self): |
| return '_SNORM' in self.name |
| |
| def is_pot(self): |
| return is_pot(self.block_size()) |
| |
| def is_int(self): |
| if self.layout != PLAIN: |
| return False |
| for channel in self.le_channels: |
| if channel.type not in (VOID, UNSIGNED, SIGNED): |
| return False |
| return True |
| |
| def is_float(self): |
| if self.layout != PLAIN: |
| return False |
| for channel in self.le_channels: |
| if channel.type not in (VOID, FLOAT): |
| return False |
| return True |
| |
| def is_bitmask(self): |
| if self.layout != PLAIN: |
| return False |
| if self.block_size() not in (8, 16, 32): |
| return False |
| for channel in self.le_channels: |
| if channel.type not in (VOID, UNSIGNED, SIGNED): |
| return False |
| return True |
| |
| def is_pure_color(self): |
| if self.layout != PLAIN or self.colorspace == ZS: |
| return False |
| pures = [channel.pure |
| for channel in self.le_channels |
| if channel.type != VOID] |
| for x in pures: |
| assert x == pures[0] |
| return pures[0] |
| |
| def channel_type(self): |
| types = [channel.type |
| for channel in self.le_channels |
| if channel.type != VOID] |
| for x in types: |
| assert x == types[0] |
| return types[0] |
| |
| def is_pure_signed(self): |
| return self.is_pure_color() and self.channel_type() == SIGNED |
| |
| def is_pure_unsigned(self): |
| return self.is_pure_color() and self.channel_type() == UNSIGNED |
| |
| def has_channel(self, id): |
| return self.le_swizzles[id] != SWIZZLE_NONE |
| |
| def has_depth(self): |
| return self.colorspace == ZS and self.has_channel(0) |
| |
| def has_stencil(self): |
| return self.colorspace == ZS and self.has_channel(1) |
| |
| def stride(self): |
| return self.block_size()/8 |
| |
| |
| _type_parse_map = { |
| '': VOID, |
| 'x': VOID, |
| 'u': UNSIGNED, |
| 's': SIGNED, |
| 'h': FIXED, |
| 'f': FLOAT, |
| } |
| |
| _swizzle_parse_map = { |
| 'x': SWIZZLE_X, |
| 'y': SWIZZLE_Y, |
| 'z': SWIZZLE_Z, |
| 'w': SWIZZLE_W, |
| '0': SWIZZLE_0, |
| '1': SWIZZLE_1, |
| '_': SWIZZLE_NONE, |
| } |
| |
| |
| def _parse_channels(fields, layout, colorspace, swizzles): |
| if layout == PLAIN: |
| names = ['']*4 |
| if colorspace in (RGB, SRGB): |
| for i in range(4): |
| swizzle = swizzles[i] |
| if swizzle < 4: |
| names[swizzle] += 'rgba'[i] |
| elif colorspace == ZS: |
| for i in range(4): |
| swizzle = swizzles[i] |
| if swizzle < 4: |
| names[swizzle] += 'zs'[i] |
| else: |
| assert False |
| for i in range(4): |
| if names[i] == '': |
| names[i] = 'x' |
| else: |
| names = ['x', 'y', 'z', 'w'] |
| |
| channels = [] |
| for i in range(0, 4): |
| field = fields[i] |
| if field: |
| type = _type_parse_map[field[0]] |
| if field[1] == 'n': |
| norm = True |
| pure = False |
| size = int(field[2:]) |
| elif field[1] == 'p': |
| pure = True |
| norm = False |
| size = int(field[2:]) |
| else: |
| norm = False |
| pure = False |
| size = int(field[1:]) |
| else: |
| type = VOID |
| norm = False |
| pure = False |
| size = 0 |
| channel = Channel(type, norm, pure, size, names[i]) |
| channels.append(channel) |
| |
| return channels |
| |
| |
| def parse(filename): |
| '''Parse the format description in CSV format in terms of the |
| Channel and Format classes above.''' |
| |
| stream = open(filename) |
| formats = [] |
| for line in stream: |
| try: |
| comment = line.index('#') |
| except ValueError: |
| pass |
| else: |
| line = line[:comment] |
| line = line.strip() |
| if not line: |
| continue |
| |
| fields = [field.strip() for field in line.split(',')] |
| assert(len(fields) == 11 or len(fields) == 16) |
| |
| name = fields[0] |
| layout = fields[1] |
| block_width, block_height, block_depth = map(int, fields[2:5]) |
| colorspace = fields[10] |
| |
| le_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[9]] |
| le_channels = _parse_channels(fields[5:9], layout, colorspace, le_swizzles) |
| |
| be_swizzles = None |
| be_channels = None |
| if len(fields) == 16: |
| be_swizzles = [_swizzle_parse_map[swizzle] |
| |
| for swizzle in fields[15]] |
| be_channels = _parse_channels( |
| |
| fields[11:15], layout, colorspace, be_swizzles) |
| |
| format = Format(name, layout, block_width, block_height, block_depth, le_channels, le_swizzles, be_channels, be_swizzles, colorspace) |
| formats.append(format) |
| return formats |