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