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 33VOID, UNSIGNED, SIGNED, FIXED, FLOAT = range(5) 34 35SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W, SWIZZLE_0, SWIZZLE_1, SWIZZLE_NONE, = range(7) 36 37PLAIN = 'plain' 38 39RGB = 'rgb' 40SRGB = 'srgb' 41YUV = 'yuv' 42ZS = 'zs' 43 44 45def is_pot(x): 46 return (x & (x - 1)) == 0 47 48 49VERY_LARGE = 99999999999999999999999 50 51 52class 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 104class Format: 105 '''Describe a pixel format.''' 106 107 def __init__(self, name, layout, block_width, block_height, le_channels, le_swizzles, be_channels, be_swizzles, colorspace): 108 self.name = name 109 self.layout = layout 110 self.block_width = block_width 111 self.block_height = block_height 112 self.le_channels = le_channels 113 self.le_swizzles = le_swizzles 114 self.be_channels = be_channels 115 self.be_swizzles = be_swizzles 116 self.name = name 117 self.colorspace = colorspace 118 119 def __str__(self): 120 return self.name 121 122 def short_name(self): 123 '''Make up a short norm for a format, suitable to be used as suffix in 124 function names.''' 125 126 name = self.name 127 if name.startswith('PIPE_FORMAT_'): 128 name = name[len('PIPE_FORMAT_'):] 129 name = name.lower() 130 return name 131 132 def block_size(self): 133 size = 0 134 for channel in self.le_channels: 135 size += channel.size 136 return size 137 138 def nr_channels(self): 139 nr_channels = 0 140 for channel in self.le_channels: 141 if channel.size: 142 nr_channels += 1 143 return nr_channels 144 145 def array_element(self): 146 if self.layout != PLAIN: 147 return None 148 ref_channel = self.le_channels[0] 149 if ref_channel.type == VOID: 150 ref_channel = self.le_channels[1] 151 for channel in self.le_channels: 152 if channel.size and (channel.size != ref_channel.size or channel.size % 8): 153 return None 154 if channel.type != VOID: 155 if channel.type != ref_channel.type: 156 return None 157 if channel.norm != ref_channel.norm: 158 return None 159 if channel.pure != ref_channel.pure: 160 return None 161 return ref_channel 162 163 def is_array(self): 164 return self.array_element() != None 165 166 def is_mixed(self): 167 if self.layout != PLAIN: 168 return False 169 ref_channel = self.le_channels[0] 170 if ref_channel.type == VOID: 171 ref_channel = self.le_channels[1] 172 for channel in self.le_channels[1:]: 173 if channel.type != VOID: 174 if channel.type != ref_channel.type: 175 return True 176 if channel.norm != ref_channel.norm: 177 return True 178 if channel.pure != ref_channel.pure: 179 return True 180 return False 181 182 def is_pot(self): 183 return is_pot(self.block_size()) 184 185 def is_int(self): 186 if self.layout != PLAIN: 187 return False 188 for channel in self.le_channels: 189 if channel.type not in (VOID, UNSIGNED, SIGNED): 190 return False 191 return True 192 193 def is_float(self): 194 if self.layout != PLAIN: 195 return False 196 for channel in self.le_channels: 197 if channel.type not in (VOID, FLOAT): 198 return False 199 return True 200 201 def is_bitmask(self): 202 if self.layout != PLAIN: 203 return False 204 if self.block_size() not in (8, 16, 32): 205 return False 206 for channel in self.le_channels: 207 if channel.type not in (VOID, UNSIGNED, SIGNED): 208 return False 209 return True 210 211 def is_pure_color(self): 212 if self.layout != PLAIN or self.colorspace == ZS: 213 return False 214 pures = [channel.pure 215 for channel in self.le_channels 216 if channel.type != VOID] 217 for x in pures: 218 assert x == pures[0] 219 return pures[0] 220 221 def channel_type(self): 222 types = [channel.type 223 for channel in self.le_channels 224 if channel.type != VOID] 225 for x in types: 226 assert x == types[0] 227 return types[0] 228 229 def is_pure_signed(self): 230 return self.is_pure_color() and self.channel_type() == SIGNED 231 232 def is_pure_unsigned(self): 233 return self.is_pure_color() and self.channel_type() == UNSIGNED 234 235 def has_channel(self, id): 236 return self.le_swizzles[id] != SWIZZLE_NONE 237 238 def has_depth(self): 239 return self.colorspace == ZS and self.has_channel(0) 240 241 def has_stencil(self): 242 return self.colorspace == ZS and self.has_channel(1) 243 244 def stride(self): 245 return self.block_size()/8 246 247 248_type_parse_map = { 249 '': VOID, 250 'x': VOID, 251 'u': UNSIGNED, 252 's': SIGNED, 253 'h': FIXED, 254 'f': FLOAT, 255} 256 257_swizzle_parse_map = { 258 'x': SWIZZLE_X, 259 'y': SWIZZLE_Y, 260 'z': SWIZZLE_Z, 261 'w': SWIZZLE_W, 262 '0': SWIZZLE_0, 263 '1': SWIZZLE_1, 264 '_': SWIZZLE_NONE, 265} 266 267def _parse_channels(fields, layout, colorspace, swizzles): 268 if layout == PLAIN: 269 names = ['']*4 270 if colorspace in (RGB, SRGB): 271 for i in range(4): 272 swizzle = swizzles[i] 273 if swizzle < 4: 274 names[swizzle] += 'rgba'[i] 275 elif colorspace == ZS: 276 for i in range(4): 277 swizzle = swizzles[i] 278 if swizzle < 4: 279 names[swizzle] += 'zs'[i] 280 else: 281 assert False 282 for i in range(4): 283 if names[i] == '': 284 names[i] = 'x' 285 else: 286 names = ['x', 'y', 'z', 'w'] 287 288 channels = [] 289 for i in range(0, 4): 290 field = fields[i] 291 if field: 292 type = _type_parse_map[field[0]] 293 if field[1] == 'n': 294 norm = True 295 pure = False 296 size = int(field[2:]) 297 elif field[1] == 'p': 298 pure = True 299 norm = False 300 size = int(field[2:]) 301 else: 302 norm = False 303 pure = False 304 size = int(field[1:]) 305 else: 306 type = VOID 307 norm = False 308 pure = False 309 size = 0 310 channel = Channel(type, norm, pure, size, names[i]) 311 channels.append(channel) 312 313 return channels 314 315def parse(filename): 316 '''Parse the format description in CSV format in terms of the 317 Channel and Format classes above.''' 318 319 stream = open(filename) 320 formats = [] 321 for line in stream: 322 try: 323 comment = line.index('#') 324 except ValueError: 325 pass 326 else: 327 line = line[:comment] 328 line = line.strip() 329 if not line: 330 continue 331 332 fields = [field.strip() for field in line.split(',')] 333 if len (fields) == 10: 334 fields += fields[4:9] 335 assert len (fields) == 15 336 337 name = fields[0] 338 layout = fields[1] 339 block_width, block_height = map(int, fields[2:4]) 340 colorspace = fields[9] 341 342 le_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[8]] 343 le_channels = _parse_channels(fields[4:8], layout, colorspace, le_swizzles) 344 345 be_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[14]] 346 be_channels = _parse_channels(fields[10:14], layout, colorspace, be_swizzles) 347 348 le_shift = 0 349 for channel in le_channels: 350 channel.shift = le_shift 351 le_shift += channel.size 352 353 be_shift = 0 354 for channel in be_channels[3::-1]: 355 channel.shift = be_shift 356 be_shift += channel.size 357 358 assert le_shift == be_shift 359 for i in range(4): 360 assert (le_swizzles[i] != SWIZZLE_NONE) == (be_swizzles[i] != SWIZZLE_NONE) 361 362 format = Format(name, layout, block_width, block_height, le_channels, le_swizzles, be_channels, be_swizzles, colorspace) 363 formats.append(format) 364 return formats 365 366