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, channels, swizzles, colorspace): 108 self.name = name 109 self.layout = layout 110 self.block_width = block_width 111 self.block_height = block_height 112 self.channels = channels 113 self.swizzles = swizzles 114 self.name = name 115 self.colorspace = colorspace 116 117 def __str__(self): 118 return self.name 119 120 def short_name(self): 121 '''Make up a short norm for a format, suitable to be used as suffix in 122 function names.''' 123 124 name = self.name 125 if name.startswith('PIPE_FORMAT_'): 126 name = name[len('PIPE_FORMAT_'):] 127 name = name.lower() 128 return name 129 130 def block_size(self): 131 size = 0 132 for channel in self.channels: 133 size += channel.size 134 return size 135 136 def nr_channels(self): 137 nr_channels = 0 138 for channel in self.channels: 139 if channel.size: 140 nr_channels += 1 141 return nr_channels 142 143 def is_array(self): 144 if self.layout != PLAIN: 145 return False 146 ref_channel = self.channels[0] 147 for channel in self.channels[1:]: 148 if channel.size and (channel.size != ref_channel.size or channel.size % 8): 149 return False 150 return True 151 152 def is_mixed(self): 153 if self.layout != PLAIN: 154 return False 155 ref_channel = self.channels[0] 156 if ref_channel.type == VOID: 157 ref_channel = self.channels[1] 158 for channel in self.channels[1:]: 159 if channel.type != VOID: 160 if channel.type != ref_channel.type: 161 return True 162 if channel.norm != ref_channel.norm: 163 return True 164 if channel.pure != ref_channel.pure: 165 return True 166 return False 167 168 def is_pot(self): 169 return is_pot(self.block_size()) 170 171 def is_int(self): 172 if self.layout != PLAIN: 173 return False 174 for channel in self.channels: 175 if channel.type not in (VOID, UNSIGNED, SIGNED): 176 return False 177 return True 178 179 def is_float(self): 180 if self.layout != PLAIN: 181 return False 182 for channel in self.channels: 183 if channel.type not in (VOID, FLOAT): 184 return False 185 return True 186 187 def is_bitmask(self): 188 if self.layout != PLAIN: 189 return False 190 if self.block_size() not in (8, 16, 32): 191 return False 192 for channel in self.channels: 193 if channel.type not in (VOID, UNSIGNED, SIGNED): 194 return False 195 return True 196 197 def inv_swizzles(self): 198 '''Return an array[4] of inverse swizzle terms''' 199 '''Only pick the first matching value to avoid l8 getting blue and i8 getting alpha''' 200 inv_swizzle = [None]*4 201 for i in range(4): 202 swizzle = self.swizzles[i] 203 if swizzle < 4 and inv_swizzle[swizzle] == None: 204 inv_swizzle[swizzle] = i 205 return inv_swizzle 206 207 def stride(self): 208 return self.block_size()/8 209 210 211 _type_parse_map = { 212 '': VOID, 213 'x': VOID, 214 'u': UNSIGNED, 215 's': SIGNED, 216 'h': FIXED, 217 'f': FLOAT, 218 } 219 220 _swizzle_parse_map = { 221 'x': SWIZZLE_X, 222 'y': SWIZZLE_Y, 223 'z': SWIZZLE_Z, 224 'w': SWIZZLE_W, 225 '0': SWIZZLE_0, 226 '1': SWIZZLE_1, 227 '_': SWIZZLE_NONE, 228 } 229 230 def parse(filename): 231 '''Parse the format descrition in CSV format in terms of the 232 Channel and Format classes above.''' 233 234 stream = open(filename) 235 formats = [] 236 for line in stream: 237 try: 238 comment = line.index('#') 239 except ValueError: 240 pass 241 else: 242 line = line[:comment] 243 line = line.strip() 244 if not line: 245 continue 246 247 fields = [field.strip() for field in line.split(',')] 248 249 name = fields[0] 250 layout = fields[1] 251 block_width, block_height = map(int, fields[2:4]) 252 253 swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[8]] 254 colorspace = fields[9] 255 256 if layout == PLAIN: 257 names = ['']*4 258 if colorspace in (RGB, SRGB): 259 for i in range(4): 260 swizzle = swizzles[i] 261 if swizzle < 4: 262 names[swizzle] += 'rgba'[i] 263 elif colorspace == ZS: 264 for i in range(4): 265 swizzle = swizzles[i] 266 if swizzle < 4: 267 names[swizzle] += 'zs'[i] 268 else: 269 assert False 270 for i in range(4): 271 if names[i] == '': 272 names[i] = 'x' 273 else: 274 names = ['x', 'y', 'z', 'w'] 275 276 channels = [] 277 for i in range(0, 4): 278 field = fields[4 + i] 279 if field: 280 type = _type_parse_map[field[0]] 281 if field[1] == 'n': 282 norm = True 283 pure = False 284 size = int(field[2:]) 285 elif field[1] == 'p': 286 pure = True 287 norm = False 288 size = int(field[2:]) 289 else: 290 norm = False 291 pure = False 292 size = int(field[1:]) 293 else: 294 type = VOID 295 norm = False 296 pure = False 297 size = 0 298 channel = Channel(type, norm, pure, size, names[i]) 299 channels.append(channel) 300 301 format = Format(name, layout, block_width, block_height, channels, swizzles, colorspace) 302 formats.append(format) 303 return formats 304 305