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 32from __future__ import division 33 34 35VOID, UNSIGNED, SIGNED, FIXED, FLOAT = range(5) 36 37SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W, SWIZZLE_0, SWIZZLE_1, SWIZZLE_NONE, = range(7) 38 39PLAIN = 'plain' 40 41RGB = 'rgb' 42SRGB = 'srgb' 43YUV = 'yuv' 44ZS = 'zs' 45 46 47def is_pot(x): 48 return (x & (x - 1)) == 0 49 50 51VERY_LARGE = 99999999999999999999999 52 53 54class Channel: 55 '''Describe the channel of a color channel.''' 56 57 def __init__(self, type, norm, pure, size, name = ''): 58 self.type = type 59 self.norm = norm 60 self.pure = pure 61 self.size = size 62 self.sign = type in (SIGNED, FIXED, FLOAT) 63 self.name = name 64 65 def __str__(self): 66 s = str(self.type) 67 if self.norm: 68 s += 'n' 69 if self.pure: 70 s += 'p' 71 s += str(self.size) 72 return s 73 74 def __eq__(self, other): 75 if other is None: 76 return False 77 78 return self.type == other.type and self.norm == other.norm and self.pure == other.pure and self.size == other.size 79 80 def __ne__(self, other): 81 return not self == other 82 83 def max(self): 84 '''Maximum representable number.''' 85 if self.type == FLOAT: 86 return VERY_LARGE 87 if self.type == FIXED: 88 return (1 << (self.size // 2)) - 1 89 if self.norm: 90 return 1 91 if self.type == UNSIGNED: 92 return (1 << self.size) - 1 93 if self.type == SIGNED: 94 return (1 << (self.size - 1)) - 1 95 assert False 96 97 def min(self): 98 '''Minimum representable number.''' 99 if self.type == FLOAT: 100 return -VERY_LARGE 101 if self.type == FIXED: 102 return -(1 << (self.size // 2)) 103 if self.type == UNSIGNED: 104 return 0 105 if self.norm: 106 return -1 107 if self.type == SIGNED: 108 return -(1 << (self.size - 1)) 109 assert False 110 111 112class Format: 113 '''Describe a pixel format.''' 114 115 def __init__(self, name, layout, block_width, block_height, block_depth, le_channels, le_swizzles, be_channels, be_swizzles, colorspace): 116 self.name = name 117 self.layout = layout 118 self.block_width = block_width 119 self.block_height = block_height 120 self.block_depth = block_depth 121 self.le_channels = le_channels 122 self.le_swizzles = le_swizzles 123 self.be_channels = be_channels 124 self.be_swizzles = be_swizzles 125 self.name = name 126 self.colorspace = colorspace 127 128 def __str__(self): 129 return self.name 130 131 def short_name(self): 132 '''Make up a short norm for a format, suitable to be used as suffix in 133 function names.''' 134 135 name = self.name 136 if name.startswith('PIPE_FORMAT_'): 137 name = name[len('PIPE_FORMAT_'):] 138 name = name.lower() 139 return name 140 141 def block_size(self): 142 size = 0 143 for channel in self.le_channels: 144 size += channel.size 145 return size 146 147 def nr_channels(self): 148 nr_channels = 0 149 for channel in self.le_channels: 150 if channel.size: 151 nr_channels += 1 152 return nr_channels 153 154 def array_element(self): 155 if self.layout != PLAIN: 156 return None 157 ref_channel = self.le_channels[0] 158 if ref_channel.type == VOID: 159 ref_channel = self.le_channels[1] 160 for channel in self.le_channels: 161 if channel.size and (channel.size != ref_channel.size or channel.size % 8): 162 return None 163 if channel.type != VOID: 164 if channel.type != ref_channel.type: 165 return None 166 if channel.norm != ref_channel.norm: 167 return None 168 if channel.pure != ref_channel.pure: 169 return None 170 return ref_channel 171 172 def is_array(self): 173 return self.array_element() != None 174 175 def is_mixed(self): 176 if self.layout != PLAIN: 177 return False 178 ref_channel = self.le_channels[0] 179 if ref_channel.type == VOID: 180 ref_channel = self.le_channels[1] 181 for channel in self.le_channels[1:]: 182 if channel.type != VOID: 183 if channel.type != ref_channel.type: 184 return True 185 if channel.norm != ref_channel.norm: 186 return True 187 if channel.pure != ref_channel.pure: 188 return True 189 return False 190 191 def is_compressed(self): 192 for channel in self.le_channels: 193 if channel.type != VOID: 194 return False 195 return True 196 197 def is_unorm(self): 198 # Non-compressed formats all have unorm or srgb in their name. 199 for keyword in ['_UNORM', '_SRGB']: 200 if keyword in self.name: 201 return True 202 203 # All the compressed formats in GLES3.2 and GL4.6 ("Table 8.14: Generic 204 # and specific compressed internal formats.") that aren't snorm for 205 # border colors are unorm, other than BPTC_*_FLOAT. 206 return self.is_compressed() and not ('FLOAT' in self.name or self.is_snorm()) 207 208 def is_snorm(self): 209 return '_SNORM' in self.name 210 211 def is_pot(self): 212 return is_pot(self.block_size()) 213 214 def is_int(self): 215 if self.layout != PLAIN: 216 return False 217 for channel in self.le_channels: 218 if channel.type not in (VOID, UNSIGNED, SIGNED): 219 return False 220 return True 221 222 def is_float(self): 223 if self.layout != PLAIN: 224 return False 225 for channel in self.le_channels: 226 if channel.type not in (VOID, FLOAT): 227 return False 228 return True 229 230 def is_bitmask(self): 231 if self.layout != PLAIN: 232 return False 233 if self.block_size() not in (8, 16, 32): 234 return False 235 for channel in self.le_channels: 236 if channel.type not in (VOID, UNSIGNED, SIGNED): 237 return False 238 return True 239 240 def is_pure_color(self): 241 if self.layout != PLAIN or self.colorspace == ZS: 242 return False 243 pures = [channel.pure 244 for channel in self.le_channels 245 if channel.type != VOID] 246 for x in pures: 247 assert x == pures[0] 248 return pures[0] 249 250 def channel_type(self): 251 types = [channel.type 252 for channel in self.le_channels 253 if channel.type != VOID] 254 for x in types: 255 assert x == types[0] 256 return types[0] 257 258 def is_pure_signed(self): 259 return self.is_pure_color() and self.channel_type() == SIGNED 260 261 def is_pure_unsigned(self): 262 return self.is_pure_color() and self.channel_type() == UNSIGNED 263 264 def has_channel(self, id): 265 return self.le_swizzles[id] != SWIZZLE_NONE 266 267 def has_depth(self): 268 return self.colorspace == ZS and self.has_channel(0) 269 270 def has_stencil(self): 271 return self.colorspace == ZS and self.has_channel(1) 272 273 def stride(self): 274 return self.block_size()/8 275 276 277_type_parse_map = { 278 '': VOID, 279 'x': VOID, 280 'u': UNSIGNED, 281 's': SIGNED, 282 'h': FIXED, 283 'f': FLOAT, 284} 285 286_swizzle_parse_map = { 287 'x': SWIZZLE_X, 288 'y': SWIZZLE_Y, 289 'z': SWIZZLE_Z, 290 'w': SWIZZLE_W, 291 '0': SWIZZLE_0, 292 '1': SWIZZLE_1, 293 '_': SWIZZLE_NONE, 294} 295 296def _parse_channels(fields, layout, colorspace, swizzles): 297 if layout == PLAIN: 298 names = ['']*4 299 if colorspace in (RGB, SRGB): 300 for i in range(4): 301 swizzle = swizzles[i] 302 if swizzle < 4: 303 names[swizzle] += 'rgba'[i] 304 elif colorspace == ZS: 305 for i in range(4): 306 swizzle = swizzles[i] 307 if swizzle < 4: 308 names[swizzle] += 'zs'[i] 309 else: 310 assert False 311 for i in range(4): 312 if names[i] == '': 313 names[i] = 'x' 314 else: 315 names = ['x', 'y', 'z', 'w'] 316 317 channels = [] 318 for i in range(0, 4): 319 field = fields[i] 320 if field: 321 type = _type_parse_map[field[0]] 322 if field[1] == 'n': 323 norm = True 324 pure = False 325 size = int(field[2:]) 326 elif field[1] == 'p': 327 pure = True 328 norm = False 329 size = int(field[2:]) 330 else: 331 norm = False 332 pure = False 333 size = int(field[1:]) 334 else: 335 type = VOID 336 norm = False 337 pure = False 338 size = 0 339 channel = Channel(type, norm, pure, size, names[i]) 340 channels.append(channel) 341 342 return channels 343 344def parse(filename): 345 '''Parse the format description in CSV format in terms of the 346 Channel and Format classes above.''' 347 348 stream = open(filename) 349 formats = [] 350 for line in stream: 351 try: 352 comment = line.index('#') 353 except ValueError: 354 pass 355 else: 356 line = line[:comment] 357 line = line.strip() 358 if not line: 359 continue 360 361 fields = [field.strip() for field in line.split(',')] 362 if len (fields) == 11: 363 fields += fields[5:10] 364 assert len (fields) == 16 365 366 name = fields[0] 367 layout = fields[1] 368 block_width, block_height, block_depth = map(int, fields[2:5]) 369 colorspace = fields[10] 370 371 le_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[9]] 372 le_channels = _parse_channels(fields[5:9], layout, colorspace, le_swizzles) 373 374 be_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[15]] 375 be_channels = _parse_channels(fields[11:15], layout, colorspace, be_swizzles) 376 377 le_shift = 0 378 for channel in le_channels: 379 channel.shift = le_shift 380 le_shift += channel.size 381 382 be_shift = 0 383 for channel in be_channels[3::-1]: 384 channel.shift = be_shift 385 be_shift += channel.size 386 387 assert le_shift == be_shift 388 for i in range(4): 389 assert (le_swizzles[i] != SWIZZLE_NONE) == (be_swizzles[i] != SWIZZLE_NONE) 390 391 format = Format(name, layout, block_width, block_height, block_depth, le_channels, le_swizzles, be_channels, be_swizzles, colorspace) 392 formats.append(format) 393 return formats 394 395