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