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
26import sys
27
28VOID = 'x'
29UNSIGNED = 'u'
30SIGNED = 's'
31FLOAT = 'f'
32
33ARRAY = 'array'
34PACKED = 'packed'
35OTHER = 'other'
36
37RGB = 'rgb'
38SRGB = 'srgb'
39YUV = 'yuv'
40ZS = 'zs'
41
42VERY_LARGE = 99999999999999999999999
43
44class 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
109class 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
232class 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
492def _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
521def _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
541def 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)
576