Added few more stubs so that control reaches to DestroyDevice().
[mesa.git] / main / format_parser.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2009 VMware, Inc.
4 # Copyright 2014 Intel Corporation
5 # All Rights Reserved.
6 #
7 # Permission is hereby granted, free of charge, to any person obtaining a
8 # copy of this software and associated documentation files (the
9 # "Software"), to deal in the Software without restriction, including
10 # without limitation the rights to use, copy, modify, merge, publish,
11 # distribute, sub license, and/or sell copies of the Software, and to
12 # permit persons to whom the Software is furnished to do so, subject to
13 # the following conditions:
14 #
15 # The above copyright notice and this permission notice (including the
16 # next paragraph) shall be included in all copies or substantial portions
17 # of the Software.
18 #
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 # IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
23 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
27 VOID = 'x'
28 UNSIGNED = 'u'
29 SIGNED = 's'
30 FLOAT = 'f'
31
32 ARRAY = 'array'
33 PACKED = 'packed'
34 OTHER = 'other'
35
36 RGB = 'rgb'
37 SRGB = 'srgb'
38 YUV = 'yuv'
39 ZS = 'zs'
40
41 def is_power_of_two(x):
42 return not bool(x & (x - 1))
43
44 VERY_LARGE = 99999999999999999999999
45
46 class Channel:
47 """Describes a color channel."""
48
49 def __init__(self, type, norm, size):
50 self.type = type
51 self.norm = norm
52 self.size = size
53 self.sign = type in (SIGNED, FLOAT)
54 self.name = None # Set when the channels are added to the format
55 self.shift = -1 # Set when the channels are added to the format
56 self.index = -1 # Set when the channels are added to the format
57
58 def __str__(self):
59 s = str(self.type)
60 if self.norm:
61 s += 'n'
62 s += str(self.size)
63 return s
64
65 def __eq__(self, other):
66 return self.type == other.type and self.norm == other.norm and self.size == other.size
67
68 def max(self):
69 """Returns the maximum representable number."""
70 if self.type == FLOAT:
71 return VERY_LARGE
72 if self.norm:
73 return 1
74 if self.type == UNSIGNED:
75 return (1 << self.size) - 1
76 if self.type == SIGNED:
77 return (1 << (self.size - 1)) - 1
78 assert False
79
80 def min(self):
81 """Returns the minimum representable number."""
82 if self.type == FLOAT:
83 return -VERY_LARGE
84 if self.type == UNSIGNED:
85 return 0
86 if self.norm:
87 return -1
88 if self.type == SIGNED:
89 return -(1 << (self.size - 1))
90 assert False
91
92 def one(self):
93 """Returns the value that represents 1.0f."""
94 if self.type == UNSIGNED:
95 return (1 << self.size) - 1
96 if self.type == SIGNED:
97 return (1 << (self.size - 1)) - 1
98 else:
99 return 1
100
101 def is_power_of_two(self):
102 """Returns true if the size of this channel is a power of two."""
103 return is_power_of_two(self.size)
104
105 def datatype(self):
106 """Returns the datatype corresponding to a channel type and size"""
107 return _get_datatype(self.type, self.size)
108
109 class Swizzle:
110 """Describes a swizzle operation.
111
112 A Swizzle is a mapping from one set of channels in one format to the
113 channels in another. Each channel in the destination format is
114 associated with one of the following constants:
115
116 * SWIZZLE_X: The first channel in the source format
117 * SWIZZLE_Y: The second channel in the source format
118 * SWIZZLE_Z: The third channel in the source format
119 * SWIZZLE_W: The fourth channel in the source format
120 * SWIZZLE_ZERO: The numeric constant 0
121 * SWIZZLE_ONE: THe numeric constant 1
122 * SWIZZLE_NONE: No data available for this channel
123
124 Sometimes a Swizzle is represented by a 4-character string. In this
125 case, the source channels are represented by the characters "x", "y",
126 "z", and "w"; the numeric constants are represented as "0" and "1"; and
127 no mapping is represented by "_". For instance, the map from
128 luminance-alpha to rgba is given by "xxxy" because each of the three rgb
129 channels maps to the first luminance-alpha channel and the alpha channel
130 maps to second luminance-alpha channel. The mapping from bgr to rgba is
131 given by "zyx1" because the first three colors are reversed and alpha is
132 always 1.
133 """
134
135 __identity_str = 'xyzw01_'
136
137 SWIZZLE_X = 0
138 SWIZZLE_Y = 1
139 SWIZZLE_Z = 2
140 SWIZZLE_W = 3
141 SWIZZLE_ZERO = 4
142 SWIZZLE_ONE = 5
143 SWIZZLE_NONE = 6
144
145 def __init__(self, swizzle):
146 """Creates a Swizzle object from a string or array."""
147 if isinstance(swizzle, str):
148 swizzle = [Swizzle.__identity_str.index(c) for c in swizzle]
149 else:
150 swizzle = list(swizzle)
151 for s in swizzle:
152 assert isinstance(s, int) and 0 <= s and s <= Swizzle.SWIZZLE_NONE
153
154 assert len(swizzle) <= 4
155
156 self.__list = swizzle + [Swizzle.SWIZZLE_NONE] * (4 - len(swizzle))
157 assert len(self.__list) == 4
158
159 def __iter__(self):
160 """Returns an iterator that iterates over this Swizzle.
161
162 The values that the iterator produces are described by the SWIZZLE_*
163 constants.
164 """
165 return self.__list.__iter__()
166
167 def __str__(self):
168 """Returns a string representation of this Swizzle."""
169 return ''.join(Swizzle.__identity_str[i] for i in self.__list)
170
171 def __getitem__(self, idx):
172 """Returns the SWIZZLE_* constant for the given destination channel.
173
174 Valid values for the destination channel include any of the SWIZZLE_*
175 constants or any of the following single-character strings: "x", "y",
176 "z", "w", "r", "g", "b", "a", "z" "s".
177 """
178
179 if isinstance(idx, int):
180 assert idx >= Swizzle.SWIZZLE_X and idx <= Swizzle.SWIZZLE_NONE
181 if idx <= Swizzle.SWIZZLE_W:
182 return self.__list.__getitem__(idx)
183 else:
184 return idx
185 elif isinstance(idx, str):
186 if idx in 'xyzw':
187 idx = 'xyzw'.find(idx)
188 elif idx in 'rgba':
189 idx = 'rgba'.find(idx)
190 elif idx in 'zs':
191 idx = 'zs'.find(idx)
192 else:
193 assert False
194 return self.__list.__getitem__(idx)
195 else:
196 assert False
197
198 def __mul__(self, other):
199 """Returns the composition of this Swizzle with another Swizzle.
200
201 The resulting swizzle is such that, for any valid input to
202 __getitem__, (a * b)[i] = a[b[i]].
203 """
204 assert isinstance(other, Swizzle)
205 return Swizzle(self[x] for x in other)
206
207 def inverse(self):
208 """Returns a pseudo-inverse of this swizzle.
209
210 Since swizzling isn't necisaraly a bijection, a Swizzle can never
211 be truely inverted. However, the swizzle returned is *almost* the
212 inverse of this swizzle in the sense that, for each i in range(3),
213 a[a.inverse()[i]] is either i or SWIZZLE_NONE. If swizzle is just
214 a permutation with no channels added or removed, then this
215 function returns the actual inverse.
216
217 This "pseudo-inverse" idea can be demonstrated by mapping from
218 luminance-alpha to rgba that is given by "xxxy". To get from rgba
219 to lumanence-alpha, we use Swizzle("xxxy").inverse() or "xw__".
220 This maps the first component in the lumanence-alpha texture is
221 the red component of the rgba image and the second to the alpha
222 component, exactly as you would expect.
223 """
224 rev = [Swizzle.SWIZZLE_NONE] * 4
225 for i in xrange(4):
226 for j in xrange(4):
227 if self.__list[j] == i and rev[i] == Swizzle.SWIZZLE_NONE:
228 rev[i] = j
229 return Swizzle(rev)
230
231
232 class Format:
233 """Describes a pixel format."""
234
235 def __init__(self, name, layout, block_width, block_height, channels, swizzle, colorspace):
236 """Constructs a Format from some metadata and a list of channels.
237
238 The channel objects must be unique to this Format and should not be
239 re-used to construct another Format. This is because certain channel
240 information such as shift, offset, and the channel name are set when
241 the Format is created and are calculated based on the entire list of
242 channels.
243
244 Arguments:
245 name -- Name of the format such as 'MESA_FORMAT_A8R8G8B8'
246 layout -- One of 'array', 'packed' 'other', or a compressed layout
247 block_width -- The block width if the format is compressed, 1 otherwise
248 block_height -- The block height if the format is compressed, 1 otherwise
249 channels -- A list of Channel objects
250 swizzle -- A Swizzle from this format to rgba
251 colorspace -- one of 'rgb', 'srgb', 'yuv', or 'zs'
252 """
253 self.name = name
254 self.layout = layout
255 self.block_width = block_width
256 self.block_height = block_height
257 self.channels = channels
258 assert isinstance(swizzle, Swizzle)
259 self.swizzle = swizzle
260 self.name = name
261 assert colorspace in (RGB, SRGB, YUV, ZS)
262 self.colorspace = colorspace
263
264 # Name the channels
265 chan_names = ['']*4
266 if self.colorspace in (RGB, SRGB):
267 for (i, s) in enumerate(swizzle):
268 if s < 4:
269 chan_names[s] += 'rgba'[i]
270 elif colorspace == ZS:
271 for (i, s) in enumerate(swizzle):
272 if s < 4:
273 chan_names[s] += 'zs'[i]
274 else:
275 chan_names = ['x', 'y', 'z', 'w']
276
277 for c, name in zip(self.channels, chan_names):
278 assert c.name is None
279 if name == 'rgb':
280 c.name = 'l'
281 elif name == 'rgba':
282 c.name = 'i'
283 elif name == '':
284 c.name = 'x'
285 else:
286 c.name = name
287
288 # Set indices and offsets
289 if self.layout == PACKED:
290 shift = 0
291 for channel in self.channels:
292 assert channel.shift == -1
293 channel.shift = shift
294 shift += channel.size
295 for idx, channel in enumerate(self.channels):
296 assert channel.index == -1
297 channel.index = idx
298 else:
299 pass # Shift means nothing here
300
301 def __str__(self):
302 return self.name
303
304 def short_name(self):
305 """Returns a short name for a format.
306
307 The short name should be suitable to be used as suffix in function
308 names.
309 """
310
311 name = self.name
312 if name.startswith('MESA_FORMAT_'):
313 name = name[len('MESA_FORMAT_'):]
314 name = name.lower()
315 return name
316
317 def block_size(self):
318 """Returns the block size (in bits) of the format."""
319 size = 0
320 for channel in self.channels:
321 size += channel.size
322 return size
323
324 def num_channels(self):
325 """Returns the number of channels in the format."""
326 nr_channels = 0
327 for channel in self.channels:
328 if channel.size:
329 nr_channels += 1
330 return nr_channels
331
332 def array_element(self):
333 """Returns a non-void channel if this format is an array, otherwise None.
334
335 If the returned channel is not None, then this format can be
336 considered to be an array of num_channels() channels identical to the
337 returned channel.
338 """
339 if self.layout == ARRAY:
340 return self.channels[0]
341 elif self.layout == PACKED:
342 ref_channel = self.channels[0]
343 if ref_channel.type == VOID:
344 ref_channel = self.channels[1]
345 for channel in self.channels:
346 if channel.size == 0 or channel.type == VOID:
347 continue
348 if channel.size != ref_channel.size or channel.size % 8 != 0:
349 return None
350 if channel.type != ref_channel.type:
351 return None
352 if channel.norm != ref_channel.norm:
353 return None
354 return ref_channel
355 else:
356 return None
357
358 def is_array(self):
359 """Returns true if this format can be considered an array format.
360
361 This function will return true if self.layout == 'array'. However,
362 some formats, such as MESA_FORMAT_A8G8B8R8, can be considered as
363 array formats even though they are technically packed.
364 """
365 return self.array_element() != None
366
367 def is_compressed(self):
368 """Returns true if this is a compressed format."""
369 return self.block_width != 1 or self.block_height != 1
370
371 def is_int(self):
372 """Returns true if this format is an integer format.
373
374 See also: is_norm()
375 """
376 if self.layout not in (ARRAY, PACKED):
377 return False
378 for channel in self.channels:
379 if channel.type not in (VOID, UNSIGNED, SIGNED):
380 return False
381 return True
382
383 def is_float(self):
384 """Returns true if this format is an floating-point format."""
385 if self.layout not in (ARRAY, PACKED):
386 return False
387 for channel in self.channels:
388 if channel.type not in (VOID, FLOAT):
389 return False
390 return True
391
392 def channel_type(self):
393 """Returns the type of the channels in this format."""
394 _type = VOID
395 for c in self.channels:
396 if c.type == VOID:
397 continue
398 if _type == VOID:
399 _type = c.type
400 assert c.type == _type
401 return _type
402
403 def channel_size(self):
404 """Returns the size (in bits) of the channels in this format.
405
406 This function should only be called if all of the channels have the
407 same size. This is always the case if is_array() returns true.
408 """
409 size = None
410 for c in self.channels:
411 if c.type == VOID:
412 continue
413 if size is None:
414 size = c.size
415 assert c.size == size
416 return size
417
418 def max_channel_size(self):
419 """Returns the size of the largest channel."""
420 size = 0
421 for c in self.channels:
422 if c.type == VOID:
423 continue
424 size = max(size, c.size)
425 return size
426
427 def is_normalized(self):
428 """Returns true if this format is normalized.
429
430 While only integer formats can be normalized, not all integer formats
431 are normalized. Normalized integer formats are those where the
432 integer value is re-interpreted as a fixed point value in the range
433 [0, 1].
434 """
435 norm = None
436 for c in self.channels:
437 if c.type == VOID:
438 continue
439 if norm is None:
440 norm = c.norm
441 assert c.norm == norm
442 return norm
443
444 def has_channel(self, name):
445 """Returns true if this format has the given channel."""
446 if self.is_compressed():
447 # Compressed formats are a bit tricky because the list of channels
448 # contains a single channel of type void. Since we don't have any
449 # channel information there, we pull it from the swizzle.
450 if str(self.swizzle) == 'xxxx':
451 return name == 'i'
452 elif str(self.swizzle)[0:3] in ('xxx', 'yyy'):
453 if name == 'l':
454 return True
455 elif name == 'a':
456 return self.swizzle['a'] <= Swizzle.SWIZZLE_W
457 else:
458 return False
459 elif name in 'rgba':
460 return self.swizzle[name] <= Swizzle.SWIZZLE_W
461 else:
462 return False
463 else:
464 for channel in self.channels:
465 if channel.name == name:
466 return True
467 return False
468
469 def get_channel(self, name):
470 """Returns the channel with the given name if it exists."""
471 for channel in self.channels:
472 if channel.name == name:
473 return channel
474 return None
475
476 def datatype(self):
477 """Returns the datatype corresponding to a format's channel type and size"""
478 if self.layout == PACKED:
479 if self.block_size() == 8:
480 return 'uint8_t'
481 if self.block_size() == 16:
482 return 'uint16_t'
483 if self.block_size() == 32:
484 return 'uint32_t'
485 else:
486 assert False
487 else:
488 return _get_datatype(self.channel_type(), self.channel_size())
489
490 def _get_datatype(type, size):
491 if type == FLOAT:
492 if size == 32:
493 return 'float'
494 elif size == 16:
495 return 'uint16_t'
496 else:
497 assert False
498 elif type == UNSIGNED:
499 if size <= 8:
500 return 'uint8_t'
501 elif size <= 16:
502 return 'uint16_t'
503 elif size <= 32:
504 return 'uint32_t'
505 else:
506 assert False
507 elif type == SIGNED:
508 if size <= 8:
509 return 'int8_t'
510 elif size <= 16:
511 return 'int16_t'
512 elif size <= 32:
513 return 'int32_t'
514 else:
515 assert False
516 else:
517 assert False
518
519 def _parse_channels(fields, layout, colorspace, swizzle):
520 channels = []
521 for field in fields:
522 if not field:
523 continue
524
525 type = field[0] if field[0] else 'x'
526
527 if field[1] == 'n':
528 norm = True
529 size = int(field[2:])
530 else:
531 norm = False
532 size = int(field[1:])
533
534 channel = Channel(type, norm, size)
535 channels.append(channel)
536
537 return channels
538
539 def parse(filename):
540 """Parse a format descrition in CSV format.
541
542 This function parses the given CSV file and returns an iterable of
543 channels."""
544
545 with open(filename) as stream:
546 for line in stream:
547 try:
548 comment = line.index('#')
549 except ValueError:
550 pass
551 else:
552 line = line[:comment]
553 line = line.strip()
554 if not line:
555 continue
556
557 fields = [field.strip() for field in line.split(',')]
558
559 name = fields[0]
560 layout = fields[1]
561 block_width = int(fields[2])
562 block_height = int(fields[3])
563 colorspace = fields[9]
564
565 swizzle = Swizzle(fields[8])
566 channels = _parse_channels(fields[4:8], layout, colorspace, swizzle)
567
568 yield Format(name, layout, block_width, block_height, channels, swizzle, colorspace)