1848b8605Smrg#
2848b8605Smrg# Copyright 2009 VMware, Inc.
3848b8605Smrg# Copyright 2014 Intel Corporation
4848b8605Smrg# All Rights Reserved.
5848b8605Smrg#
6848b8605Smrg# Permission is hereby granted, free of charge, to any person obtaining a
7848b8605Smrg# copy of this software and associated documentation files (the
8848b8605Smrg# "Software"), to deal in the Software without restriction, including
9848b8605Smrg# without limitation the rights to use, copy, modify, merge, publish,
10848b8605Smrg# distribute, sub license, and/or sell copies of the Software, and to
11848b8605Smrg# permit persons to whom the Software is furnished to do so, subject to
12848b8605Smrg# the following conditions:
13848b8605Smrg#
14848b8605Smrg# The above copyright notice and this permission notice (including the
15848b8605Smrg# next paragraph) shall be included in all copies or substantial portions
16848b8605Smrg# of the Software.
17848b8605Smrg#
18848b8605Smrg# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19848b8605Smrg# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20848b8605Smrg# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21848b8605Smrg# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22848b8605Smrg# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23848b8605Smrg# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24848b8605Smrg# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25848b8605Smrg
26b8e80941Smrgimport sys
27b8e80941Smrg
28848b8605SmrgVOID = 'x'
29848b8605SmrgUNSIGNED = 'u'
30848b8605SmrgSIGNED = 's'
31848b8605SmrgFLOAT = 'f'
32848b8605Smrg
33848b8605SmrgARRAY = 'array'
34848b8605SmrgPACKED = 'packed'
35848b8605SmrgOTHER = 'other'
36848b8605Smrg
37848b8605SmrgRGB = 'rgb'
38848b8605SmrgSRGB = 'srgb'
39848b8605SmrgYUV = 'yuv'
40848b8605SmrgZS = 'zs'
41848b8605Smrg
42848b8605SmrgVERY_LARGE = 99999999999999999999999
43848b8605Smrg
44848b8605Smrgclass Channel:
45848b8605Smrg   """Describes a color channel."""
46848b8605Smrg
47848b8605Smrg   def __init__(self, type, norm, size):
48848b8605Smrg      self.type = type
49848b8605Smrg      self.norm = norm
50848b8605Smrg      self.size = size
51848b8605Smrg      self.sign = type in (SIGNED, FLOAT)
52848b8605Smrg      self.name = None # Set when the channels are added to the format
53848b8605Smrg      self.shift = -1 # Set when the channels are added to the format
54848b8605Smrg      self.index = -1 # Set when the channels are added to the format
55848b8605Smrg
56848b8605Smrg   def __str__(self):
57848b8605Smrg      s = str(self.type)
58848b8605Smrg      if self.norm:
59848b8605Smrg         s += 'n'
60848b8605Smrg      s += str(self.size)
61848b8605Smrg      return s
62848b8605Smrg
63848b8605Smrg   def __eq__(self, other):
64b8e80941Smrg      if other is None:
65b8e80941Smrg         return False
66b8e80941Smrg
67848b8605Smrg      return self.type == other.type and self.norm == other.norm and self.size == other.size
68848b8605Smrg
69b8e80941Smrg   def __ne__(self, other):
70b8e80941Smrg      return not self.__eq__(other)
71b8e80941Smrg
72848b8605Smrg   def max(self):
73848b8605Smrg      """Returns the maximum representable number."""
74848b8605Smrg      if self.type == FLOAT:
75848b8605Smrg         return VERY_LARGE
76848b8605Smrg      if self.norm:
77848b8605Smrg         return 1
78848b8605Smrg      if self.type == UNSIGNED:
79848b8605Smrg         return (1 << self.size) - 1
80848b8605Smrg      if self.type == SIGNED:
81848b8605Smrg         return (1 << (self.size - 1)) - 1
82848b8605Smrg      assert False
83848b8605Smrg
84848b8605Smrg   def min(self):
85848b8605Smrg      """Returns the minimum representable number."""
86848b8605Smrg      if self.type == FLOAT:
87848b8605Smrg         return -VERY_LARGE
88848b8605Smrg      if self.type == UNSIGNED:
89848b8605Smrg         return 0
90848b8605Smrg      if self.norm:
91848b8605Smrg         return -1
92848b8605Smrg      if self.type == SIGNED:
93848b8605Smrg         return -(1 << (self.size - 1))
94848b8605Smrg      assert False
95848b8605Smrg
96848b8605Smrg   def one(self):
97848b8605Smrg      """Returns the value that represents 1.0f."""
98848b8605Smrg      if self.type == UNSIGNED:
99848b8605Smrg         return (1 << self.size) - 1
100848b8605Smrg      if self.type == SIGNED:
101848b8605Smrg         return (1 << (self.size - 1)) - 1
102848b8605Smrg      else:
103848b8605Smrg         return 1
104848b8605Smrg
105b8e80941Smrg   def datatype(self):
106b8e80941Smrg      """Returns the datatype corresponding to a channel type and size"""
107b8e80941Smrg      return _get_datatype(self.type, self.size)
108848b8605Smrg
109848b8605Smrgclass Swizzle:
110848b8605Smrg   """Describes a swizzle operation.
111848b8605Smrg
112848b8605Smrg   A Swizzle is a mapping from one set of channels in one format to the
113848b8605Smrg   channels in another.  Each channel in the destination format is
114848b8605Smrg   associated with one of the following constants:
115848b8605Smrg
116848b8605Smrg    * SWIZZLE_X: The first channel in the source format
117848b8605Smrg    * SWIZZLE_Y: The second channel in the source format
118848b8605Smrg    * SWIZZLE_Z: The third channel in the source format
119848b8605Smrg    * SWIZZLE_W: The fourth channel in the source format
120848b8605Smrg    * SWIZZLE_ZERO: The numeric constant 0
121848b8605Smrg    * SWIZZLE_ONE: THe numeric constant 1
122848b8605Smrg    * SWIZZLE_NONE: No data available for this channel
123848b8605Smrg
124848b8605Smrg   Sometimes a Swizzle is represented by a 4-character string.  In this
125848b8605Smrg   case, the source channels are represented by the characters "x", "y",
126848b8605Smrg   "z", and "w"; the numeric constants are represented as "0" and "1"; and
127848b8605Smrg   no mapping is represented by "_".  For instance, the map from
128848b8605Smrg   luminance-alpha to rgba is given by "xxxy" because each of the three rgb
129848b8605Smrg   channels maps to the first luminance-alpha channel and the alpha channel
130848b8605Smrg   maps to second luminance-alpha channel.  The mapping from bgr to rgba is
131848b8605Smrg   given by "zyx1" because the first three colors are reversed and alpha is
132848b8605Smrg   always 1.
133848b8605Smrg   """
134848b8605Smrg
135848b8605Smrg   __identity_str = 'xyzw01_'
136848b8605Smrg
137848b8605Smrg   SWIZZLE_X = 0
138848b8605Smrg   SWIZZLE_Y = 1
139848b8605Smrg   SWIZZLE_Z = 2
140848b8605Smrg   SWIZZLE_W = 3
141848b8605Smrg   SWIZZLE_ZERO = 4
142848b8605Smrg   SWIZZLE_ONE = 5
143848b8605Smrg   SWIZZLE_NONE = 6
144848b8605Smrg
145848b8605Smrg   def __init__(self, swizzle):
146848b8605Smrg      """Creates a Swizzle object from a string or array."""
147848b8605Smrg      if isinstance(swizzle, str):
148848b8605Smrg         swizzle = [Swizzle.__identity_str.index(c) for c in swizzle]
149848b8605Smrg      else:
150848b8605Smrg         swizzle = list(swizzle)
151848b8605Smrg         for s in swizzle:
152848b8605Smrg            assert isinstance(s, int) and 0 <= s and s <= Swizzle.SWIZZLE_NONE
153848b8605Smrg
154848b8605Smrg      assert len(swizzle) <= 4
155848b8605Smrg
156848b8605Smrg      self.__list = swizzle + [Swizzle.SWIZZLE_NONE] * (4 - len(swizzle))
157848b8605Smrg      assert len(self.__list) == 4
158848b8605Smrg
159848b8605Smrg   def __iter__(self):
160848b8605Smrg      """Returns an iterator that iterates over this Swizzle.
161848b8605Smrg
162848b8605Smrg      The values that the iterator produces are described by the SWIZZLE_*
163848b8605Smrg      constants.
164848b8605Smrg      """
165848b8605Smrg      return self.__list.__iter__()
166848b8605Smrg
167848b8605Smrg   def __str__(self):
168848b8605Smrg      """Returns a string representation of this Swizzle."""
169848b8605Smrg      return ''.join(Swizzle.__identity_str[i] for i in self.__list)
170848b8605Smrg
171848b8605Smrg   def __getitem__(self, idx):
172848b8605Smrg      """Returns the SWIZZLE_* constant for the given destination channel.
173848b8605Smrg
174848b8605Smrg      Valid values for the destination channel include any of the SWIZZLE_*
175848b8605Smrg      constants or any of the following single-character strings: "x", "y",
176848b8605Smrg      "z", "w", "r", "g", "b", "a", "z" "s".
177848b8605Smrg      """
178848b8605Smrg
179848b8605Smrg      if isinstance(idx, int):
180848b8605Smrg         assert idx >= Swizzle.SWIZZLE_X and idx <= Swizzle.SWIZZLE_NONE
181848b8605Smrg         if idx <= Swizzle.SWIZZLE_W:
182848b8605Smrg            return self.__list.__getitem__(idx)
183848b8605Smrg         else:
184848b8605Smrg            return idx
185848b8605Smrg      elif isinstance(idx, str):
186848b8605Smrg         if idx in 'xyzw':
187848b8605Smrg            idx = 'xyzw'.find(idx)
188848b8605Smrg         elif idx in 'rgba':
189848b8605Smrg            idx = 'rgba'.find(idx)
190848b8605Smrg         elif idx in 'zs':
191848b8605Smrg            idx = 'zs'.find(idx)
192848b8605Smrg         else:
193848b8605Smrg            assert False
194848b8605Smrg         return self.__list.__getitem__(idx)
195848b8605Smrg      else:
196848b8605Smrg         assert False
197848b8605Smrg
198848b8605Smrg   def __mul__(self, other):
199848b8605Smrg      """Returns the composition of this Swizzle with another Swizzle.
200848b8605Smrg
201848b8605Smrg      The resulting swizzle is such that, for any valid input to
202848b8605Smrg      __getitem__, (a * b)[i] = a[b[i]].
203848b8605Smrg      """
204848b8605Smrg      assert isinstance(other, Swizzle)
205848b8605Smrg      return Swizzle(self[x] for x in other)
206848b8605Smrg
207848b8605Smrg   def inverse(self):
208848b8605Smrg      """Returns a pseudo-inverse of this swizzle.
209848b8605Smrg
210848b8605Smrg      Since swizzling isn't necisaraly a bijection, a Swizzle can never
211848b8605Smrg      be truely inverted.  However, the swizzle returned is *almost* the
212848b8605Smrg      inverse of this swizzle in the sense that, for each i in range(3),
213848b8605Smrg      a[a.inverse()[i]] is either i or SWIZZLE_NONE.  If swizzle is just
214848b8605Smrg      a permutation with no channels added or removed, then this
215848b8605Smrg      function returns the actual inverse.
216848b8605Smrg
217848b8605Smrg      This "pseudo-inverse" idea can be demonstrated by mapping from
218848b8605Smrg      luminance-alpha to rgba that is given by "xxxy".  To get from rgba
219848b8605Smrg      to lumanence-alpha, we use Swizzle("xxxy").inverse() or "xw__".
220848b8605Smrg      This maps the first component in the lumanence-alpha texture is
221848b8605Smrg      the red component of the rgba image and the second to the alpha
222848b8605Smrg      component, exactly as you would expect.
223848b8605Smrg      """
224848b8605Smrg      rev = [Swizzle.SWIZZLE_NONE] * 4
225b8e80941Smrg      for i in range(4):
226b8e80941Smrg         for j in range(4):
227848b8605Smrg            if self.__list[j] == i and rev[i] == Swizzle.SWIZZLE_NONE:
228848b8605Smrg               rev[i] = j
229848b8605Smrg      return Swizzle(rev)
230848b8605Smrg
231848b8605Smrg
232848b8605Smrgclass Format:
233848b8605Smrg   """Describes a pixel format."""
234848b8605Smrg
235b8e80941Smrg   def __init__(self, name, layout, block_width, block_height, block_depth, channels, swizzle, colorspace):
236848b8605Smrg      """Constructs a Format from some metadata and a list of channels.
237848b8605Smrg
238848b8605Smrg      The channel objects must be unique to this Format and should not be
239848b8605Smrg      re-used to construct another Format.  This is because certain channel
240848b8605Smrg      information such as shift, offset, and the channel name are set when
241848b8605Smrg      the Format is created and are calculated based on the entire list of
242848b8605Smrg      channels.
243848b8605Smrg
244848b8605Smrg      Arguments:
245848b8605Smrg      name -- Name of the format such as 'MESA_FORMAT_A8R8G8B8'
246848b8605Smrg      layout -- One of 'array', 'packed' 'other', or a compressed layout
247848b8605Smrg      block_width -- The block width if the format is compressed, 1 otherwise
248848b8605Smrg      block_height -- The block height if the format is compressed, 1 otherwise
249b8e80941Smrg      block_depth -- The block depth if the format is compressed, 1 otherwise
250848b8605Smrg      channels -- A list of Channel objects
251848b8605Smrg      swizzle -- A Swizzle from this format to rgba
252848b8605Smrg      colorspace -- one of 'rgb', 'srgb', 'yuv', or 'zs'
253848b8605Smrg      """
254848b8605Smrg      self.name = name
255848b8605Smrg      self.layout = layout
256848b8605Smrg      self.block_width = block_width
257848b8605Smrg      self.block_height = block_height
258b8e80941Smrg      self.block_depth = block_depth
259848b8605Smrg      self.channels = channels
260848b8605Smrg      assert isinstance(swizzle, Swizzle)
261848b8605Smrg      self.swizzle = swizzle
262848b8605Smrg      self.name = name
263848b8605Smrg      assert colorspace in (RGB, SRGB, YUV, ZS)
264848b8605Smrg      self.colorspace = colorspace
265848b8605Smrg
266848b8605Smrg      # Name the channels
267848b8605Smrg      chan_names = ['']*4
268848b8605Smrg      if self.colorspace in (RGB, SRGB):
269848b8605Smrg         for (i, s) in enumerate(swizzle):
270848b8605Smrg            if s < 4:
271848b8605Smrg               chan_names[s] += 'rgba'[i]
272848b8605Smrg      elif colorspace == ZS:
273848b8605Smrg         for (i, s) in enumerate(swizzle):
274848b8605Smrg            if s < 4:
275848b8605Smrg               chan_names[s] += 'zs'[i]
276848b8605Smrg      else:
277848b8605Smrg         chan_names = ['x', 'y', 'z', 'w']
278848b8605Smrg
279848b8605Smrg      for c, name in zip(self.channels, chan_names):
280848b8605Smrg         assert c.name is None
281848b8605Smrg         if name == 'rgb':
282848b8605Smrg            c.name = 'l'
283848b8605Smrg         elif name == 'rgba':
284848b8605Smrg            c.name = 'i'
285848b8605Smrg         elif name == '':
286848b8605Smrg            c.name = 'x'
287848b8605Smrg         else:
288848b8605Smrg            c.name = name
289848b8605Smrg
290848b8605Smrg      # Set indices and offsets
291848b8605Smrg      if self.layout == PACKED:
292848b8605Smrg         shift = 0
293848b8605Smrg         for channel in self.channels:
294848b8605Smrg            assert channel.shift == -1
295848b8605Smrg            channel.shift = shift
296848b8605Smrg            shift += channel.size
297848b8605Smrg      for idx, channel in enumerate(self.channels):
298848b8605Smrg         assert channel.index == -1
299848b8605Smrg         channel.index = idx
300848b8605Smrg      else:
301848b8605Smrg         pass # Shift means nothing here
302848b8605Smrg
303848b8605Smrg   def __str__(self):
304848b8605Smrg      return self.name
305848b8605Smrg
306848b8605Smrg   def short_name(self):
307848b8605Smrg      """Returns a short name for a format.
308848b8605Smrg
309848b8605Smrg      The short name should be suitable to be used as suffix in function
310848b8605Smrg      names.
311848b8605Smrg      """
312848b8605Smrg
313848b8605Smrg      name = self.name
314848b8605Smrg      if name.startswith('MESA_FORMAT_'):
315848b8605Smrg         name = name[len('MESA_FORMAT_'):]
316848b8605Smrg      name = name.lower()
317848b8605Smrg      return name
318848b8605Smrg
319848b8605Smrg   def block_size(self):
320848b8605Smrg      """Returns the block size (in bits) of the format."""
321848b8605Smrg      size = 0
322848b8605Smrg      for channel in self.channels:
323848b8605Smrg         size += channel.size
324848b8605Smrg      return size
325848b8605Smrg
326848b8605Smrg   def num_channels(self):
327848b8605Smrg      """Returns the number of channels in the format."""
328848b8605Smrg      nr_channels = 0
329848b8605Smrg      for channel in self.channels:
330848b8605Smrg         if channel.size:
331848b8605Smrg            nr_channels += 1
332848b8605Smrg      return nr_channels
333848b8605Smrg
334848b8605Smrg   def array_element(self):
335848b8605Smrg      """Returns a non-void channel if this format is an array, otherwise None.
336848b8605Smrg
337848b8605Smrg      If the returned channel is not None, then this format can be
338848b8605Smrg      considered to be an array of num_channels() channels identical to the
339848b8605Smrg      returned channel.
340848b8605Smrg      """
341848b8605Smrg      if self.layout == ARRAY:
342848b8605Smrg         return self.channels[0]
343848b8605Smrg      elif self.layout == PACKED:
344848b8605Smrg         ref_channel = self.channels[0]
345848b8605Smrg         if ref_channel.type == VOID:
346848b8605Smrg            ref_channel = self.channels[1]
347848b8605Smrg         for channel in self.channels:
348848b8605Smrg            if channel.size == 0 or channel.type == VOID:
349848b8605Smrg               continue
350848b8605Smrg            if channel.size != ref_channel.size or channel.size % 8 != 0:
351848b8605Smrg               return None
352848b8605Smrg            if channel.type != ref_channel.type:
353848b8605Smrg               return None
354848b8605Smrg            if channel.norm != ref_channel.norm:
355848b8605Smrg               return None
356848b8605Smrg         return ref_channel
357848b8605Smrg      else:
358848b8605Smrg         return None
359848b8605Smrg
360848b8605Smrg   def is_array(self):
361848b8605Smrg      """Returns true if this format can be considered an array format.
362848b8605Smrg
363848b8605Smrg      This function will return true if self.layout == 'array'.  However,
364848b8605Smrg      some formats, such as MESA_FORMAT_A8G8B8R8, can be considered as
365848b8605Smrg      array formats even though they are technically packed.
366848b8605Smrg      """
367848b8605Smrg      return self.array_element() != None
368848b8605Smrg
369848b8605Smrg   def is_compressed(self):
370848b8605Smrg      """Returns true if this is a compressed format."""
371b8e80941Smrg      return self.block_width != 1 or self.block_height != 1 or self.block_depth != 1
372848b8605Smrg
373848b8605Smrg   def is_int(self):
374848b8605Smrg      """Returns true if this format is an integer format.
375848b8605Smrg
376848b8605Smrg      See also: is_norm()
377848b8605Smrg      """
378848b8605Smrg      if self.layout not in (ARRAY, PACKED):
379848b8605Smrg         return False
380848b8605Smrg      for channel in self.channels:
381848b8605Smrg         if channel.type not in (VOID, UNSIGNED, SIGNED):
382848b8605Smrg            return False
383848b8605Smrg      return True
384848b8605Smrg
385848b8605Smrg   def is_float(self):
386848b8605Smrg      """Returns true if this format is an floating-point format."""
387848b8605Smrg      if self.layout not in (ARRAY, PACKED):
388848b8605Smrg         return False
389848b8605Smrg      for channel in self.channels:
390848b8605Smrg         if channel.type not in (VOID, FLOAT):
391848b8605Smrg            return False
392848b8605Smrg      return True
393848b8605Smrg
394848b8605Smrg   def channel_type(self):
395848b8605Smrg      """Returns the type of the channels in this format."""
396848b8605Smrg      _type = VOID
397848b8605Smrg      for c in self.channels:
398848b8605Smrg         if c.type == VOID:
399848b8605Smrg            continue
400848b8605Smrg         if _type == VOID:
401848b8605Smrg            _type = c.type
402848b8605Smrg         assert c.type == _type
403848b8605Smrg      return _type
404848b8605Smrg
405848b8605Smrg   def channel_size(self):
406848b8605Smrg      """Returns the size (in bits) of the channels in this format.
407848b8605Smrg
408848b8605Smrg      This function should only be called if all of the channels have the
409848b8605Smrg      same size.  This is always the case if is_array() returns true.
410848b8605Smrg      """
411848b8605Smrg      size = None
412848b8605Smrg      for c in self.channels:
413848b8605Smrg         if c.type == VOID:
414848b8605Smrg            continue
415848b8605Smrg         if size is None:
416848b8605Smrg            size = c.size
417848b8605Smrg         assert c.size == size
418848b8605Smrg      return size
419848b8605Smrg
420848b8605Smrg   def max_channel_size(self):
421848b8605Smrg      """Returns the size of the largest channel."""
422848b8605Smrg      size = 0
423848b8605Smrg      for c in self.channels:
424848b8605Smrg         if c.type == VOID:
425848b8605Smrg            continue
426848b8605Smrg         size = max(size, c.size)
427848b8605Smrg      return size
428848b8605Smrg
429848b8605Smrg   def is_normalized(self):
430848b8605Smrg      """Returns true if this format is normalized.
431848b8605Smrg
432848b8605Smrg      While only integer formats can be normalized, not all integer formats
433848b8605Smrg      are normalized.  Normalized integer formats are those where the
434848b8605Smrg      integer value is re-interpreted as a fixed point value in the range
435848b8605Smrg      [0, 1].
436848b8605Smrg      """
437848b8605Smrg      norm = None
438848b8605Smrg      for c in self.channels:
439848b8605Smrg         if c.type == VOID:
440848b8605Smrg            continue
441848b8605Smrg         if norm is None:
442848b8605Smrg            norm = c.norm
443848b8605Smrg         assert c.norm == norm
444848b8605Smrg      return norm
445848b8605Smrg
446848b8605Smrg   def has_channel(self, name):
447848b8605Smrg      """Returns true if this format has the given channel."""
448848b8605Smrg      if self.is_compressed():
449848b8605Smrg         # Compressed formats are a bit tricky because the list of channels
450848b8605Smrg         # contains a single channel of type void.  Since we don't have any
451848b8605Smrg         # channel information there, we pull it from the swizzle.
452848b8605Smrg         if str(self.swizzle) == 'xxxx':
453848b8605Smrg            return name == 'i'
454848b8605Smrg         elif str(self.swizzle)[0:3] in ('xxx', 'yyy'):
455848b8605Smrg            if name == 'l':
456848b8605Smrg               return True
457848b8605Smrg            elif name == 'a':
458848b8605Smrg               return self.swizzle['a'] <= Swizzle.SWIZZLE_W
459848b8605Smrg            else:
460848b8605Smrg               return False
461848b8605Smrg         elif name in 'rgba':
462848b8605Smrg            return self.swizzle[name] <= Swizzle.SWIZZLE_W
463848b8605Smrg         else:
464848b8605Smrg            return False
465848b8605Smrg      else:
466848b8605Smrg         for channel in self.channels:
467848b8605Smrg            if channel.name == name:
468848b8605Smrg               return True
469848b8605Smrg         return False
470848b8605Smrg
471848b8605Smrg   def get_channel(self, name):
472848b8605Smrg      """Returns the channel with the given name if it exists."""
473848b8605Smrg      for channel in self.channels:
474848b8605Smrg         if channel.name == name:
475848b8605Smrg            return channel
476848b8605Smrg      return None
477848b8605Smrg
478b8e80941Smrg   def datatype(self):
479b8e80941Smrg      """Returns the datatype corresponding to a format's channel type and size"""
480b8e80941Smrg      if self.layout == PACKED:
481b8e80941Smrg         if self.block_size() == 8:
482b8e80941Smrg            return 'uint8_t'
483b8e80941Smrg         if self.block_size() == 16:
484b8e80941Smrg            return 'uint16_t'
485b8e80941Smrg         if self.block_size() == 32:
486b8e80941Smrg            return 'uint32_t'
487b8e80941Smrg         else:
488b8e80941Smrg            assert False
489b8e80941Smrg      else:
490b8e80941Smrg         return _get_datatype(self.channel_type(), self.channel_size())
491b8e80941Smrg
492b8e80941Smrgdef _get_datatype(type, size):
493b8e80941Smrg   if type == FLOAT:
494b8e80941Smrg      if size == 32:
495b8e80941Smrg         return 'float'
496b8e80941Smrg      elif size == 16:
497b8e80941Smrg         return 'uint16_t'
498b8e80941Smrg      else:
499b8e80941Smrg         assert False
500b8e80941Smrg   elif type == UNSIGNED:
501b8e80941Smrg      if size <= 8:
502b8e80941Smrg         return 'uint8_t'
503b8e80941Smrg      elif size <= 16:
504b8e80941Smrg         return 'uint16_t'
505b8e80941Smrg      elif size <= 32:
506b8e80941Smrg         return 'uint32_t'
507b8e80941Smrg      else:
508b8e80941Smrg         assert False
509b8e80941Smrg   elif type == SIGNED:
510b8e80941Smrg      if size <= 8:
511b8e80941Smrg         return 'int8_t'
512b8e80941Smrg      elif size <= 16:
513b8e80941Smrg         return 'int16_t'
514b8e80941Smrg      elif size <= 32:
515b8e80941Smrg         return 'int32_t'
516b8e80941Smrg      else:
517b8e80941Smrg         assert False
518b8e80941Smrg   else:
519b8e80941Smrg      assert False
520b8e80941Smrg
521848b8605Smrgdef _parse_channels(fields, layout, colorspace, swizzle):
522848b8605Smrg   channels = []
523848b8605Smrg   for field in fields:
524848b8605Smrg      if not field:
525848b8605Smrg         continue
526848b8605Smrg
527848b8605Smrg      type = field[0] if field[0] else 'x'
528848b8605Smrg
529848b8605Smrg      if field[1] == 'n':
530848b8605Smrg         norm = True
531848b8605Smrg         size = int(field[2:])
532848b8605Smrg      else:
533848b8605Smrg         norm = False
534848b8605Smrg         size = int(field[1:])
535848b8605Smrg
536848b8605Smrg      channel = Channel(type, norm, size)
537848b8605Smrg      channels.append(channel)
538848b8605Smrg
539848b8605Smrg   return channels
540848b8605Smrg
541848b8605Smrgdef parse(filename):
542b8e80941Smrg   """Parse a format description in CSV format.
543848b8605Smrg
544848b8605Smrg   This function parses the given CSV file and returns an iterable of
545848b8605Smrg   channels."""
546848b8605Smrg
547848b8605Smrg   with open(filename) as stream:
548848b8605Smrg      for line in stream:
549848b8605Smrg         try:
550848b8605Smrg            comment = line.index('#')
551848b8605Smrg         except ValueError:
552848b8605Smrg            pass
553848b8605Smrg         else:
554848b8605Smrg            line = line[:comment]
555848b8605Smrg         line = line.strip()
556848b8605Smrg         if not line:
557848b8605Smrg            continue
558848b8605Smrg
559848b8605Smrg         fields = [field.strip() for field in line.split(',')]
560848b8605Smrg
561848b8605Smrg         name = fields[0]
562848b8605Smrg         layout = fields[1]
563848b8605Smrg         block_width = int(fields[2])
564848b8605Smrg         block_height = int(fields[3])
565b8e80941Smrg         block_depth = int(fields[4])
566b8e80941Smrg         colorspace = fields[10]
567848b8605Smrg
568b8e80941Smrg         try:
569b8e80941Smrg            swizzle = Swizzle(fields[9])
570b8e80941Smrg         except:
571b8e80941Smrg            sys.exit("error parsing swizzle for format " + name)
572b8e80941Smrg         channels = _parse_channels(fields[5:9], layout, colorspace, swizzle)
573848b8605Smrg
574b8e80941Smrg         yield Format(name, layout, block_width, block_height, block_depth, channels, swizzle, colorspace)
575