~mmach/netext73/mesa-haswell

« back to all changes in this revision

Viewing changes to src/mesa/main/format_parser.py

  • Committer: mmach
  • Date: 2022-09-22 19:56:13 UTC
  • Revision ID: netbit73@gmail.com-20220922195613-wtik9mmy20tmor0i
2022-09-22 21:17:09

Show diffs side-by-side

added added

removed removed

Lines of Context:
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):
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
 
 
573
 
         channels = _parse_channels(fields[5:9])
574
 
 
575
 
         yield Format(name, layout, block_width, block_height, block_depth, channels, swizzle, colorspace)