2 # Copyright 2009 VMware, Inc.
3 # Copyright 2014 Intel Corporation
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:
14 # The above copyright notice and this permission notice (including the
15 # next paragraph) shall be included in all copies or substantial portions
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.
42 VERY_LARGE
= 99999999999999999999999
45 """Describes a color channel."""
47 def __init__(self
, type, norm
, 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
63 def __eq__(self
, other
):
64 return self
.type == other
.type and self
.norm
== other
.norm
and self
.size
== other
.size
67 """Returns the maximum representable number."""
68 if self
.type == FLOAT
:
72 if self
.type == UNSIGNED
:
73 return (1 << self
.size
) - 1
74 if self
.type == SIGNED
:
75 return (1 << (self
.size
- 1)) - 1
79 """Returns the minimum representable number."""
80 if self
.type == FLOAT
:
82 if self
.type == UNSIGNED
:
86 if self
.type == SIGNED
:
87 return -(1 << (self
.size
- 1))
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
100 """Returns the datatype corresponding to a channel type and size"""
101 return _get_datatype(self
.type, self
.size
)
104 """Describes a swizzle operation.
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:
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
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
129 __identity_str
= 'xyzw01_'
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
]
144 swizzle
= list(swizzle
)
146 assert isinstance(s
, int) and 0 <= s
and s
<= Swizzle
.SWIZZLE_NONE
148 assert len(swizzle
) <= 4
150 self
.__list
= swizzle
+ [Swizzle
.SWIZZLE_NONE
] * (4 - len(swizzle
))
151 assert len(self
.__list
) == 4
154 """Returns an iterator that iterates over this Swizzle.
156 The values that the iterator produces are described by the SWIZZLE_*
159 return self
.__list
.__iter
__()
162 """Returns a string representation of this Swizzle."""
163 return ''.join(Swizzle
.__identity
_str
[i
] for i
in self
.__list
)
165 def __getitem__(self
, idx
):
166 """Returns the SWIZZLE_* constant for the given destination channel.
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".
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
)
179 elif isinstance(idx
, str):
181 idx
= 'xyzw'.find(idx
)
183 idx
= 'rgba'.find(idx
)
188 return self
.__list
.__getitem
__(idx
)
192 def __mul__(self
, other
):
193 """Returns the composition of this Swizzle with another Swizzle.
195 The resulting swizzle is such that, for any valid input to
196 __getitem__, (a * b)[i] = a[b[i]].
198 assert isinstance(other
, Swizzle
)
199 return Swizzle(self
[x
] for x
in other
)
202 """Returns a pseudo-inverse of this swizzle.
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.
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.
218 rev
= [Swizzle
.SWIZZLE_NONE
] * 4
221 if self
.__list
[j
] == i
and rev
[i
] == Swizzle
.SWIZZLE_NONE
:
227 """Describes a pixel format."""
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.
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
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'
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
257 assert colorspace
in (RGB
, SRGB
, YUV
, ZS
)
258 self
.colorspace
= colorspace
262 if self
.colorspace
in (RGB
, SRGB
):
263 for (i
, s
) in enumerate(swizzle
):
265 chan_names
[s
] += 'rgba'[i
]
266 elif colorspace
== ZS
:
267 for (i
, s
) in enumerate(swizzle
):
269 chan_names
[s
] += 'zs'[i
]
271 chan_names
= ['x', 'y', 'z', 'w']
273 for c
, name
in zip(self
.channels
, chan_names
):
274 assert c
.name
is None
284 # Set indices and offsets
285 if self
.layout
== PACKED
:
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
295 pass # Shift means nothing here
300 def short_name(self
):
301 """Returns a short name for a format.
303 The short name should be suitable to be used as suffix in function
308 if name
.startswith('MESA_FORMAT_'):
309 name
= name
[len('MESA_FORMAT_'):]
313 def block_size(self
):
314 """Returns the block size (in bits) of the format."""
316 for channel
in self
.channels
:
320 def num_channels(self
):
321 """Returns the number of channels in the format."""
323 for channel
in self
.channels
:
328 def array_element(self
):
329 """Returns a non-void channel if this format is an array, otherwise None.
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
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
:
344 if channel
.size
!= ref_channel
.size
or channel
.size
% 8 != 0:
346 if channel
.type != ref_channel
.type:
348 if channel
.norm
!= ref_channel
.norm
:
355 """Returns true if this format can be considered an array format.
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.
361 return self
.array_element() != None
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
368 """Returns true if this format is an integer format.
372 if self
.layout
not in (ARRAY
, PACKED
):
374 for channel
in self
.channels
:
375 if channel
.type not in (VOID
, UNSIGNED
, SIGNED
):
380 """Returns true if this format is an floating-point format."""
381 if self
.layout
not in (ARRAY
, PACKED
):
383 for channel
in self
.channels
:
384 if channel
.type not in (VOID
, FLOAT
):
388 def channel_type(self
):
389 """Returns the type of the channels in this format."""
391 for c
in self
.channels
:
396 assert c
.type == _type
399 def channel_size(self
):
400 """Returns the size (in bits) of the channels in this format.
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.
406 for c
in self
.channels
:
411 assert c
.size
== size
414 def max_channel_size(self
):
415 """Returns the size of the largest channel."""
417 for c
in self
.channels
:
420 size
= max(size
, c
.size
)
423 def is_normalized(self
):
424 """Returns true if this format is normalized.
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
432 for c
in self
.channels
:
437 assert c
.norm
== norm
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':
448 elif str(self
.swizzle
)[0:3] in ('xxx', 'yyy'):
452 return self
.swizzle
['a'] <= Swizzle
.SWIZZLE_W
456 return self
.swizzle
[name
] <= Swizzle
.SWIZZLE_W
460 for channel
in self
.channels
:
461 if channel
.name
== name
:
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
:
473 """Returns the datatype corresponding to a format's channel type and size"""
474 if self
.layout
== PACKED
:
475 if self
.block_size() == 8:
477 if self
.block_size() == 16:
479 if self
.block_size() == 32:
484 return _get_datatype(self
.channel_type(), self
.channel_size())
486 def _get_datatype(type, size
):
494 elif type == UNSIGNED
:
515 def _parse_channels(fields
, layout
, colorspace
, swizzle
):
521 type = field
[0] if field
[0] else 'x'
525 size
= int(field
[2:])
528 size
= int(field
[1:])
530 channel
= Channel(type, norm
, size
)
531 channels
.append(channel
)
536 """Parse a format description in CSV format.
538 This function parses the given CSV file and returns an iterable of
541 with
open(filename
) as stream
:
544 comment
= line
.index('#')
548 line
= line
[:comment
]
553 fields
= [field
.strip() for field
in line
.split(',')]
557 block_width
= int(fields
[2])
558 block_height
= int(fields
[3])
559 block_depth
= int(fields
[4])
560 colorspace
= fields
[10]
563 swizzle
= Swizzle(fields
[9])
565 sys
.exit("error parsing swizzle for format " + name
)
566 channels
= _parse_channels(fields
[5:9], layout
, colorspace
, swizzle
)
568 yield Format(name
, layout
, block_width
, block_height
, block_depth
, channels
, swizzle
, colorspace
)