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