Added few more stubs so that control reaches to DestroyDevice().
[mesa.git] / src / mesa / main / format_parser.py
1 #
2 # Copyright 2009 VMware, Inc.
3 # Copyright 2014 Intel Corporation
4 # All Rights Reserved.
5 #
6 # Permission is hereby granted, free of charge, to any person obtaining a
7 # copy of this software and associated documentation files (the
8 # "Software"), to deal in the Software without restriction, including
9 # without limitation the rights to use, copy, modify, merge, publish,
10 # distribute, sub license, and/or sell copies of the Software, and to
11 # permit persons to whom the Software is furnished to do so, subject to
12 # the following conditions:
13 #
14 # The above copyright notice and this permission notice (including the
15 # next paragraph) shall be included in all copies or substantial portions
16 # of the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 # IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26 import sys
27
28 VOID = 'x'
29 UNSIGNED = 'u'
30 SIGNED = 's'
31 FLOAT = 'f'
32
33 ARRAY = 'array'
34 PACKED = 'packed'
35 OTHER = 'other'
36
37 RGB = 'rgb'
38 SRGB = 'srgb'
39 YUV = 'yuv'
40 ZS = 'zs'
41
42 VERY_LARGE = 99999999999999999999999
43
44 class Channel:
45 """Describes a color channel."""
46
47 def __init__(self, type, norm, size):
48 self.type = type
49 self.norm = norm
50 self.size = size
51 self.sign = type in (SIGNED, FLOAT)
52 self.name = None # Set when the channels are added to the format
53 self.shift = -1 # Set when the channels are added to the format
54 self.index = -1 # Set when the channels are added to the format
55
56 def __str__(self):
57 s = str(self.type)
58 if self.norm:
59 s += 'n'
60 s += str(self.size)
61 return s
62
63 def __eq__(self, other):
64 if other is None:
65 return False
66
67 return self.type == other.type and self.norm == other.norm and self.size == other.size
68
69 def __ne__(self, other):
70 return not self.__eq__(other)
71
72 def max(self):
73 """Returns the maximum representable number."""
74 if self.type == FLOAT:
75 return VERY_LARGE
76 if self.norm:
77 return 1
78 if self.type == UNSIGNED:
79 return (1 << self.size) - 1
80 if self.type == SIGNED:
81 return (1 << (self.size - 1)) - 1
82 assert False
83
84 def min(self):
85 """Returns the minimum representable number."""
86 if self.type == FLOAT:
87 return -VERY_LARGE
88 if self.type == UNSIGNED:
89 return 0
90 if self.norm:
91 return -1
92 if self.type == SIGNED:
93 return -(1 << (self.size - 1))
94 assert False
95
96 def one(self):
97 """Returns the value that represents 1.0f."""
98 if self.type == UNSIGNED:
99 return (1 << self.size) - 1
100 if self.type == SIGNED:
101 return (1 << (self.size - 1)) - 1
102 else:
103 return 1
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 range(4):
226 for j in range(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, block_depth, 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 block_depth -- The block depth if the format is compressed, 1 otherwise
250 channels -- A list of Channel objects
251 swizzle -- A Swizzle from this format to rgba
252 colorspace -- one of 'rgb', 'srgb', 'yuv', or 'zs'
253 """
254 self.name = name
255 self.layout = layout
256 self.block_width = block_width
257 self.block_height = block_height
258 self.block_depth = block_depth
259 self.channels = channels
260 assert isinstance(swizzle, Swizzle)
261 self.swizzle = swizzle
262 self.name = name
263 assert colorspace in (RGB, SRGB, YUV, ZS)
264 self.colorspace = colorspace
265
266 # Name the channels
267 chan_names = ['']*4
268 if self.colorspace in (RGB, SRGB):
269 for (i, s) in enumerate(swizzle):
270 if s < 4:
271 chan_names[s] += 'rgba'[i]
272 elif colorspace == ZS:
273 for (i, s) in enumerate(swizzle):
274 if s < 4:
275 chan_names[s] += 'zs'[i]
276 else:
277 chan_names = ['x', 'y', 'z', 'w']
278
279 for c, name in zip(self.channels, chan_names):
280 assert c.name is None
281 if name == 'rgb':
282 c.name = 'l'
283 elif name == 'rgba':
284 c.name = 'i'
285 elif name == '':
286 c.name = 'x'
287 else:
288 c.name = name
289
290 # Set indices and offsets
291 if self.layout == PACKED:
292 shift = 0
293 for channel in self.channels:
294 assert channel.shift == -1
295 channel.shift = shift
296 shift += channel.size
297 for idx, channel in enumerate(self.channels):
298 assert channel.index == -1
299 channel.index = idx
300 else:
301 pass # Shift means nothing here
302
303 def __str__(self):
304 return self.name
305
306 def short_name(self):
307 """Returns a short name for a format.
308
309 The short name should be suitable to be used as suffix in function
310 names.
311 """
312
313 name = self.name
314 if name.startswith('MESA_FORMAT_'):
315 name = name[len('MESA_FORMAT_'):]
316 name = name.lower()
317 return name
318
319 def block_size(self):
320 """Returns the block size (in bits) of the format."""
321 size = 0
322 for channel in self.channels:
323 size += channel.size
324 return size
325
326 def num_channels(self):
327 """Returns the number of channels in the format."""
328 nr_channels = 0
329 for channel in self.channels:
330 if channel.size:
331 nr_channels += 1
332 return nr_channels
333
334 def array_element(self):
335 """Returns a non-void channel if this format is an array, otherwise None.
336
337 If the returned channel is not None, then this format can be
338 considered to be an array of num_channels() channels identical to the
339 returned channel.
340 """
341 if self.layout == ARRAY:
342 return self.channels[0]
343 elif self.layout == PACKED:
344 ref_channel = self.channels[0]
345 if ref_channel.type == VOID:
346 ref_channel = self.channels[1]
347 for channel in self.channels:
348 if channel.size == 0 or channel.type == VOID:
349 continue
350 if channel.size != ref_channel.size or channel.size % 8 != 0:
351 return None
352 if channel.type != ref_channel.type:
353 return None
354 if channel.norm != ref_channel.norm:
355 return None
356 return ref_channel
357 else:
358 return None
359
360 def is_array(self):
361 """Returns true if this format can be considered an array format.
362
363 This function will return true if self.layout == 'array'. However,
364 some formats, such as MESA_FORMAT_A8G8B8R8, can be considered as
365 array formats even though they are technically packed.
366 """
367 return self.array_element() != None
368
369 def is_compressed(self):
370 """Returns true if this is a compressed format."""
371 return self.block_width != 1 or self.block_height != 1 or self.block_depth != 1
372
373 def is_int(self):
374 """Returns true if this format is an integer format.
375
376 See also: is_norm()
377 """
378 if self.layout not in (ARRAY, PACKED):
379 return False
380 for channel in self.channels:
381 if channel.type not in (VOID, UNSIGNED, SIGNED):
382 return False
383 return True
384
385 def is_float(self):
386 """Returns true if this format is an floating-point format."""
387 if self.layout not in (ARRAY, PACKED):
388 return False
389 for channel in self.channels:
390 if channel.type not in (VOID, FLOAT):
391 return False
392 return True
393
394 def channel_type(self):
395 """Returns the type of the channels in this format."""
396 _type = VOID
397 for c in self.channels:
398 if c.type == VOID:
399 continue
400 if _type == VOID:
401 _type = c.type
402 assert c.type == _type
403 return _type
404
405 def channel_size(self):
406 """Returns the size (in bits) of the channels in this format.
407
408 This function should only be called if all of the channels have the
409 same size. This is always the case if is_array() returns true.
410 """
411 size = None
412 for c in self.channels:
413 if c.type == VOID:
414 continue
415 if size is None:
416 size = c.size
417 assert c.size == size
418 return size
419
420 def max_channel_size(self):
421 """Returns the size of the largest channel."""
422 size = 0
423 for c in self.channels:
424 if c.type == VOID:
425 continue
426 size = max(size, c.size)
427 return size
428
429 def is_normalized(self):
430 """Returns true if this format is normalized.
431
432 While only integer formats can be normalized, not all integer formats
433 are normalized. Normalized integer formats are those where the
434 integer value is re-interpreted as a fixed point value in the range
435 [0, 1].
436 """
437 norm = None
438 for c in self.channels:
439 if c.type == VOID:
440 continue
441 if norm is None:
442 norm = c.norm
443 assert c.norm == norm
444 return norm
445
446 def has_channel(self, name):
447 """Returns true if this format has the given channel."""
448 if self.is_compressed():
449 # Compressed formats are a bit tricky because the list of channels
450 # contains a single channel of type void. Since we don't have any
451 # channel information there, we pull it from the swizzle.
452 if str(self.swizzle) == 'xxxx':
453 return name == 'i'
454 elif str(self.swizzle)[0:3] in ('xxx', 'yyy'):
455 if name == 'l':
456 return True
457 elif name == 'a':
458 return self.swizzle['a'] <= Swizzle.SWIZZLE_W
459 else:
460 return False
461 elif name in 'rgba':
462 return self.swizzle[name] <= Swizzle.SWIZZLE_W
463 else:
464 return False
465 else:
466 for channel in self.channels:
467 if channel.name == name:
468 return True
469 return False
470
471 def get_channel(self, name):
472 """Returns the channel with the given name if it exists."""
473 for channel in self.channels:
474 if channel.name == name:
475 return channel
476 return None
477
478 def datatype(self):
479 """Returns the datatype corresponding to a format's channel type and size"""
480 if self.layout == PACKED:
481 if self.block_size() == 8:
482 return 'uint8_t'
483 if self.block_size() == 16:
484 return 'uint16_t'
485 if self.block_size() == 32:
486 return 'uint32_t'
487 else:
488 assert False
489 else:
490 return _get_datatype(self.channel_type(), self.channel_size())
491
492 def _get_datatype(type, size):
493 if type == FLOAT:
494 if size == 32:
495 return 'float'
496 elif size == 16:
497 return 'uint16_t'
498 else:
499 assert False
500 elif type == UNSIGNED:
501 if size <= 8:
502 return 'uint8_t'
503 elif size <= 16:
504 return 'uint16_t'
505 elif size <= 32:
506 return 'uint32_t'
507 else:
508 assert False
509 elif type == SIGNED:
510 if size <= 8:
511 return 'int8_t'
512 elif size <= 16:
513 return 'int16_t'
514 elif size <= 32:
515 return 'int32_t'
516 else:
517 assert False
518 else:
519 assert False
520
521 def _parse_channels(fields, layout, colorspace, swizzle):
522 channels = []
523 for field in fields:
524 if not field:
525 continue
526
527 type = field[0] if field[0] else 'x'
528
529 if field[1] == 'n':
530 norm = True
531 size = int(field[2:])
532 else:
533 norm = False
534 size = int(field[1:])
535
536 channel = Channel(type, norm, size)
537 channels.append(channel)
538
539 return channels
540
541 def parse(filename):
542 """Parse a format description in CSV format.
543
544 This function parses the given CSV file and returns an iterable of
545 channels."""
546
547 with open(filename) as stream:
548 for line in stream:
549 try:
550 comment = line.index('#')
551 except ValueError:
552 pass
553 else:
554 line = line[:comment]
555 line = line.strip()
556 if not line:
557 continue
558
559 fields = [field.strip() for field in line.split(',')]
560
561 name = fields[0]
562 layout = fields[1]
563 block_width = int(fields[2])
564 block_height = int(fields[3])
565 block_depth = int(fields[4])
566 colorspace = fields[10]
567
568 try:
569 swizzle = Swizzle(fields[9])
570 except:
571 sys.exit("error parsing swizzle for format " + name)
572 channels = _parse_channels(fields[5:9], layout, colorspace, swizzle)
573
574 yield Format(name, layout, block_width, block_height, block_depth, channels, swizzle, colorspace)