1 2''' 3/************************************************************************** 4 * 5 * Copyright 2009 VMware, Inc. 6 * All Rights Reserved. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sub license, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice (including the 17 * next paragraph) shall be included in all copies or substantial portions 18 * of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 23 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 24 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 * 28 **************************************************************************/ 29''' 30 31 32import copy 33 34VOID, UNSIGNED, SIGNED, FIXED, FLOAT = range(5) 35 36SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W, SWIZZLE_0, SWIZZLE_1, SWIZZLE_NONE, = range(7) 37 38PLAIN = 'plain' 39 40RGB = 'rgb' 41SRGB = 'srgb' 42YUV = 'yuv' 43ZS = 'zs' 44 45 46def is_pot(x): 47 return (x & (x - 1)) == 0 48 49 50VERY_LARGE = 99999999999999999999999 51 52 53class Channel: 54 '''Describe the channel of a color channel.''' 55 56 def __init__(self, type, norm, pure, size, name=''): 57 self.type = type 58 self.norm = norm 59 self.pure = pure 60 self.size = size 61 self.sign = type in (SIGNED, FIXED, FLOAT) 62 self.name = name 63 64 def __str__(self): 65 s = str(self.type) 66 if self.norm: 67 s += 'n' 68 if self.pure: 69 s += 'p' 70 s += str(self.size) 71 return s 72 73 def __repr__(self): 74 return "Channel({})".format(self.__str__()) 75 76 def __eq__(self, other): 77 if other is None: 78 return False 79 80 return self.type == other.type and self.norm == other.norm and self.pure == other.pure and self.size == other.size 81 82 def __ne__(self, other): 83 return not self == other 84 85 def max(self): 86 '''Maximum representable number.''' 87 if self.type == FLOAT: 88 return VERY_LARGE 89 if self.type == FIXED: 90 return (1 << (self.size // 2)) - 1 91 if self.norm: 92 return 1 93 if self.type == UNSIGNED: 94 return (1 << self.size) - 1 95 if self.type == SIGNED: 96 return (1 << (self.size - 1)) - 1 97 assert False 98 99 def min(self): 100 '''Minimum representable number.''' 101 if self.type == FLOAT: 102 return -VERY_LARGE 103 if self.type == FIXED: 104 return -(1 << (self.size // 2)) 105 if self.type == UNSIGNED: 106 return 0 107 if self.norm: 108 return -1 109 if self.type == SIGNED: 110 return -(1 << (self.size - 1)) 111 assert False 112 113 114class Format: 115 '''Describe a pixel format.''' 116 117 def __init__(self, name, layout, block_width, block_height, block_depth, le_channels, le_swizzles, be_channels, be_swizzles, colorspace): 118 self.name = name 119 self.layout = layout 120 self.block_width = block_width 121 self.block_height = block_height 122 self.block_depth = block_depth 123 self.colorspace = colorspace 124 125 self.le_channels = le_channels 126 self.le_swizzles = le_swizzles 127 128 le_shift = 0 129 for channel in self.le_channels: 130 channel.shift = le_shift 131 le_shift += channel.size 132 133 if be_channels: 134 if self.is_array(): 135 print( 136 "{} is an array format and should not include BE swizzles in the CSV".format(self.name)) 137 exit(1) 138 if self.is_bitmask(): 139 print( 140 "{} is a bitmask format and should not include BE swizzles in the CSV".format(self.name)) 141 exit(1) 142 self.be_channels = be_channels 143 self.be_swizzles = be_swizzles 144 elif self.is_bitmask() and not self.is_array(): 145 # Bitmask formats are "load a word the size of the block and 146 # bitshift channels out of it." However, the channel shifts 147 # defined in u_format_table.c are numbered right-to-left on BE 148 # for some historical reason (see below), which is hard to 149 # change due to llvmpipe, so we also have to flip the channel 150 # order and the channel-to-rgba swizzle values to read 151 # right-to-left from the defined (non-VOID) channels so that the 152 # correct shifts happen. 153 # 154 # This is nonsense, but it's the nonsense that makes 155 # u_format_test pass and you get the right colors in softpipe at 156 # least. 157 chans = self.nr_channels() 158 self.be_channels = self.le_channels[chans - 159 1::-1] + self.le_channels[chans:4] 160 161 xyzw = [SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W] 162 chan_map = {SWIZZLE_X: xyzw[chans - 1] if chans >= 1 else SWIZZLE_X, 163 SWIZZLE_Y: xyzw[chans - 2] if chans >= 2 else SWIZZLE_X, 164 SWIZZLE_Z: xyzw[chans - 3] if chans >= 3 else SWIZZLE_X, 165 SWIZZLE_W: xyzw[chans - 4] if chans >= 4 else SWIZZLE_X, 166 SWIZZLE_1: SWIZZLE_1, 167 SWIZZLE_0: SWIZZLE_0, 168 SWIZZLE_NONE: SWIZZLE_NONE} 169 self.be_swizzles = [chan_map[s] for s in self.le_swizzles] 170 else: 171 self.be_channels = copy.deepcopy(le_channels) 172 self.be_swizzles = le_swizzles 173 174 be_shift = 0 175 for channel in reversed(self.be_channels): 176 channel.shift = be_shift 177 be_shift += channel.size 178 179 assert le_shift == be_shift 180 for i in range(4): 181 assert (self.le_swizzles[i] != SWIZZLE_NONE) == ( 182 self.be_swizzles[i] != SWIZZLE_NONE) 183 184 def __str__(self): 185 return self.name 186 187 def short_name(self): 188 '''Make up a short norm for a format, suitable to be used as suffix in 189 function names.''' 190 191 name = self.name 192 if name.startswith('PIPE_FORMAT_'): 193 name = name[len('PIPE_FORMAT_'):] 194 name = name.lower() 195 return name 196 197 def block_size(self): 198 size = 0 199 for channel in self.le_channels: 200 size += channel.size 201 return size 202 203 def nr_channels(self): 204 nr_channels = 0 205 for channel in self.le_channels: 206 if channel.size: 207 nr_channels += 1 208 return nr_channels 209 210 def array_element(self): 211 if self.layout != PLAIN: 212 return None 213 ref_channel = self.le_channels[0] 214 if ref_channel.type == VOID: 215 ref_channel = self.le_channels[1] 216 for channel in self.le_channels: 217 if channel.size and (channel.size != ref_channel.size or channel.size % 8): 218 return None 219 if channel.type != VOID: 220 if channel.type != ref_channel.type: 221 return None 222 if channel.norm != ref_channel.norm: 223 return None 224 if channel.pure != ref_channel.pure: 225 return None 226 return ref_channel 227 228 def is_array(self): 229 return self.array_element() != None 230 231 def is_mixed(self): 232 if self.layout != PLAIN: 233 return False 234 ref_channel = self.le_channels[0] 235 if ref_channel.type == VOID: 236 ref_channel = self.le_channels[1] 237 for channel in self.le_channels[1:]: 238 if channel.type != VOID: 239 if channel.type != ref_channel.type: 240 return True 241 if channel.norm != ref_channel.norm: 242 return True 243 if channel.pure != ref_channel.pure: 244 return True 245 return False 246 247 def is_compressed(self): 248 for channel in self.le_channels: 249 if channel.type != VOID: 250 return False 251 return True 252 253 def is_unorm(self): 254 # Non-compressed formats all have unorm or srgb in their name. 255 for keyword in ['_UNORM', '_SRGB']: 256 if keyword in self.name: 257 return True 258 259 # All the compressed formats in GLES3.2 and GL4.6 ("Table 8.14: Generic 260 # and specific compressed internal formats.") that aren't snorm for 261 # border colors are unorm, other than BPTC_*_FLOAT. 262 return self.is_compressed() and not ('FLOAT' in self.name or self.is_snorm()) 263 264 def is_snorm(self): 265 return '_SNORM' in self.name 266 267 def is_pot(self): 268 return is_pot(self.block_size()) 269 270 def is_int(self): 271 if self.layout != PLAIN: 272 return False 273 for channel in self.le_channels: 274 if channel.type not in (VOID, UNSIGNED, SIGNED): 275 return False 276 return True 277 278 def is_float(self): 279 if self.layout != PLAIN: 280 return False 281 for channel in self.le_channels: 282 if channel.type not in (VOID, FLOAT): 283 return False 284 return True 285 286 def is_bitmask(self): 287 if self.layout != PLAIN: 288 return False 289 if self.block_size() not in (8, 16, 32): 290 return False 291 for channel in self.le_channels: 292 if channel.type not in (VOID, UNSIGNED, SIGNED): 293 return False 294 return True 295 296 def is_pure_color(self): 297 if self.layout != PLAIN or self.colorspace == ZS: 298 return False 299 pures = [channel.pure 300 for channel in self.le_channels 301 if channel.type != VOID] 302 for x in pures: 303 assert x == pures[0] 304 return pures[0] 305 306 def channel_type(self): 307 types = [channel.type 308 for channel in self.le_channels 309 if channel.type != VOID] 310 for x in types: 311 assert x == types[0] 312 return types[0] 313 314 def is_pure_signed(self): 315 return self.is_pure_color() and self.channel_type() == SIGNED 316 317 def is_pure_unsigned(self): 318 return self.is_pure_color() and self.channel_type() == UNSIGNED 319 320 def has_channel(self, id): 321 return self.le_swizzles[id] != SWIZZLE_NONE 322 323 def has_depth(self): 324 return self.colorspace == ZS and self.has_channel(0) 325 326 def has_stencil(self): 327 return self.colorspace == ZS and self.has_channel(1) 328 329 def stride(self): 330 return self.block_size()/8 331 332 333_type_parse_map = { 334 '': VOID, 335 'x': VOID, 336 'u': UNSIGNED, 337 's': SIGNED, 338 'h': FIXED, 339 'f': FLOAT, 340} 341 342_swizzle_parse_map = { 343 'x': SWIZZLE_X, 344 'y': SWIZZLE_Y, 345 'z': SWIZZLE_Z, 346 'w': SWIZZLE_W, 347 '0': SWIZZLE_0, 348 '1': SWIZZLE_1, 349 '_': SWIZZLE_NONE, 350} 351 352 353def _parse_channels(fields, layout, colorspace, swizzles): 354 if layout == PLAIN: 355 names = ['']*4 356 if colorspace in (RGB, SRGB): 357 for i in range(4): 358 swizzle = swizzles[i] 359 if swizzle < 4: 360 names[swizzle] += 'rgba'[i] 361 elif colorspace == ZS: 362 for i in range(4): 363 swizzle = swizzles[i] 364 if swizzle < 4: 365 names[swizzle] += 'zs'[i] 366 else: 367 assert False 368 for i in range(4): 369 if names[i] == '': 370 names[i] = 'x' 371 else: 372 names = ['x', 'y', 'z', 'w'] 373 374 channels = [] 375 for i in range(0, 4): 376 field = fields[i] 377 if field: 378 type = _type_parse_map[field[0]] 379 if field[1] == 'n': 380 norm = True 381 pure = False 382 size = int(field[2:]) 383 elif field[1] == 'p': 384 pure = True 385 norm = False 386 size = int(field[2:]) 387 else: 388 norm = False 389 pure = False 390 size = int(field[1:]) 391 else: 392 type = VOID 393 norm = False 394 pure = False 395 size = 0 396 channel = Channel(type, norm, pure, size, names[i]) 397 channels.append(channel) 398 399 return channels 400 401 402def parse(filename): 403 '''Parse the format description in CSV format in terms of the 404 Channel and Format classes above.''' 405 406 stream = open(filename) 407 formats = [] 408 for line in stream: 409 try: 410 comment = line.index('#') 411 except ValueError: 412 pass 413 else: 414 line = line[:comment] 415 line = line.strip() 416 if not line: 417 continue 418 419 fields = [field.strip() for field in line.split(',')] 420 assert(len(fields) == 11 or len(fields) == 16) 421 422 name = fields[0] 423 layout = fields[1] 424 block_width, block_height, block_depth = map(int, fields[2:5]) 425 colorspace = fields[10] 426 427 le_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[9]] 428 le_channels = _parse_channels(fields[5:9], layout, colorspace, le_swizzles) 429 430 be_swizzles = None 431 be_channels = None 432 if len(fields) == 16: 433 be_swizzles = [_swizzle_parse_map[swizzle] 434 435 for swizzle in fields[15]] 436 be_channels = _parse_channels( 437 438 fields[11:15], layout, colorspace, be_swizzles) 439 440 format = Format(name, layout, block_width, block_height, block_depth, le_channels, le_swizzles, be_channels, be_swizzles, colorspace) 441 formats.append(format) 442 return formats 443