3 # Copyright 2009 VMware, Inc.
4 # Copyright 2014 Intel Corporation
7 # Permission is hereby granted, free of charge, to any person obtaining a
8 # copy of this software and associated documentation files (the
9 # "Software"), to deal in the Software without restriction, including
10 # without limitation the rights to use, copy, modify, merge, publish,
11 # distribute, sub license, and/or sell copies of the Software, and to
12 # permit persons to whom the Software is furnished to do so, subject to
13 # the following conditions:
15 # The above copyright notice and this permission notice (including the
16 # next paragraph) shall be included in all copies or substantial portions
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 # IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
23 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43 def is_power_of_two(x
):
44 return not bool(x
& (x
- 1))
46 VERY_LARGE
= 99999999999999999999999
49 """Describes a color channel."""
51 def __init__(self
, type, norm
, size
):
55 self
.sign
= type in (SIGNED
, FLOAT
)
56 self
.name
= None # Set when the channels are added to the format
57 self
.shift
= -1 # Set when the channels are added to the format
58 self
.index
= -1 # Set when the channels are added to the format
67 def __eq__(self
, other
):
68 return self
.type == other
.type and self
.norm
== other
.norm
and self
.size
== other
.size
71 """Returns the maximum representable number."""
72 if self
.type == FLOAT
:
76 if self
.type == UNSIGNED
:
77 return (1 << self
.size
) - 1
78 if self
.type == SIGNED
:
79 return (1 << (self
.size
- 1)) - 1
83 """Returns the minimum representable number."""
84 if self
.type == FLOAT
:
86 if self
.type == UNSIGNED
:
90 if self
.type == SIGNED
:
91 return -(1 << (self
.size
- 1))
95 """Returns the value that represents 1.0f."""
96 if self
.type == UNSIGNED
:
97 return (1 << self
.size
) - 1
98 if self
.type == SIGNED
:
99 return (1 << (self
.size
- 1)) - 1
103 def is_power_of_two(self
):
104 """Returns true if the size of this channel is a power of two."""
105 return is_power_of_two(self
.size
)
108 """Returns the datatype corresponding to a channel type and size"""
109 return _get_datatype(self
.type, self
.size
)
112 """Describes a swizzle operation.
114 A Swizzle is a mapping from one set of channels in one format to the
115 channels in another. Each channel in the destination format is
116 associated with one of the following constants:
118 * SWIZZLE_X: The first channel in the source format
119 * SWIZZLE_Y: The second channel in the source format
120 * SWIZZLE_Z: The third channel in the source format
121 * SWIZZLE_W: The fourth channel in the source format
122 * SWIZZLE_ZERO: The numeric constant 0
123 * SWIZZLE_ONE: THe numeric constant 1
124 * SWIZZLE_NONE: No data available for this channel
126 Sometimes a Swizzle is represented by a 4-character string. In this
127 case, the source channels are represented by the characters "x", "y",
128 "z", and "w"; the numeric constants are represented as "0" and "1"; and
129 no mapping is represented by "_". For instance, the map from
130 luminance-alpha to rgba is given by "xxxy" because each of the three rgb
131 channels maps to the first luminance-alpha channel and the alpha channel
132 maps to second luminance-alpha channel. The mapping from bgr to rgba is
133 given by "zyx1" because the first three colors are reversed and alpha is
137 __identity_str
= 'xyzw01_'
147 def __init__(self
, swizzle
):
148 """Creates a Swizzle object from a string or array."""
149 if isinstance(swizzle
, str):
150 swizzle
= [Swizzle
.__identity
_str
.index(c
) for c
in swizzle
]
152 swizzle
= list(swizzle
)
154 assert isinstance(s
, int) and 0 <= s
and s
<= Swizzle
.SWIZZLE_NONE
156 assert len(swizzle
) <= 4
158 self
.__list
= swizzle
+ [Swizzle
.SWIZZLE_NONE
] * (4 - len(swizzle
))
159 assert len(self
.__list
) == 4
162 """Returns an iterator that iterates over this Swizzle.
164 The values that the iterator produces are described by the SWIZZLE_*
167 return self
.__list
.__iter
__()
170 """Returns a string representation of this Swizzle."""
171 return ''.join(Swizzle
.__identity
_str
[i
] for i
in self
.__list
)
173 def __getitem__(self
, idx
):
174 """Returns the SWIZZLE_* constant for the given destination channel.
176 Valid values for the destination channel include any of the SWIZZLE_*
177 constants or any of the following single-character strings: "x", "y",
178 "z", "w", "r", "g", "b", "a", "z" "s".
181 if isinstance(idx
, int):
182 assert idx
>= Swizzle
.SWIZZLE_X
and idx
<= Swizzle
.SWIZZLE_NONE
183 if idx
<= Swizzle
.SWIZZLE_W
:
184 return self
.__list
.__getitem
__(idx
)
187 elif isinstance(idx
, str):
189 idx
= 'xyzw'.find(idx
)
191 idx
= 'rgba'.find(idx
)
196 return self
.__list
.__getitem
__(idx
)
200 def __mul__(self
, other
):
201 """Returns the composition of this Swizzle with another Swizzle.
203 The resulting swizzle is such that, for any valid input to
204 __getitem__, (a * b)[i] = a[b[i]].
206 assert isinstance(other
, Swizzle
)
207 return Swizzle(self
[x
] for x
in other
)
210 """Returns a pseudo-inverse of this swizzle.
212 Since swizzling isn't necisaraly a bijection, a Swizzle can never
213 be truely inverted. However, the swizzle returned is *almost* the
214 inverse of this swizzle in the sense that, for each i in range(3),
215 a[a.inverse()[i]] is either i or SWIZZLE_NONE. If swizzle is just
216 a permutation with no channels added or removed, then this
217 function returns the actual inverse.
219 This "pseudo-inverse" idea can be demonstrated by mapping from
220 luminance-alpha to rgba that is given by "xxxy". To get from rgba
221 to lumanence-alpha, we use Swizzle("xxxy").inverse() or "xw__".
222 This maps the first component in the lumanence-alpha texture is
223 the red component of the rgba image and the second to the alpha
224 component, exactly as you would expect.
226 rev
= [Swizzle
.SWIZZLE_NONE
] * 4
229 if self
.__list
[j
] == i
and rev
[i
] == Swizzle
.SWIZZLE_NONE
:
235 """Describes a pixel format."""
237 def __init__(self
, name
, layout
, block_width
, block_height
, channels
, swizzle
, colorspace
):
238 """Constructs a Format from some metadata and a list of channels.
240 The channel objects must be unique to this Format and should not be
241 re-used to construct another Format. This is because certain channel
242 information such as shift, offset, and the channel name are set when
243 the Format is created and are calculated based on the entire list of
247 name -- Name of the format such as 'MESA_FORMAT_A8R8G8B8'
248 layout -- One of 'array', 'packed' 'other', or a compressed layout
249 block_width -- The block width if the format is compressed, 1 otherwise
250 block_height -- The block height if the format is compressed, 1 otherwise
251 channels -- A list of Channel objects
252 swizzle -- A Swizzle from this format to rgba
253 colorspace -- one of 'rgb', 'srgb', 'yuv', or 'zs'
257 self
.block_width
= block_width
258 self
.block_height
= block_height
259 self
.channels
= channels
260 assert isinstance(swizzle
, Swizzle
)
261 self
.swizzle
= swizzle
263 assert colorspace
in (RGB
, SRGB
, YUV
, ZS
)
264 self
.colorspace
= colorspace
268 if self
.colorspace
in (RGB
, SRGB
):
269 for (i
, s
) in enumerate(swizzle
):
271 chan_names
[s
] += 'rgba'[i
]
272 elif colorspace
== ZS
:
273 for (i
, s
) in enumerate(swizzle
):
275 chan_names
[s
] += 'zs'[i
]
277 chan_names
= ['x', 'y', 'z', 'w']
279 for c
, name
in zip(self
.channels
, chan_names
):
280 assert c
.name
is None
290 # Set indices and offsets
291 if self
.layout
== PACKED
:
293 for channel
in self
.channels
:
294 assert channel
.shift
== -1
295 channel
.shift
= shift
296 shift
+= channel
.size
297 for idx
, channel
in enumerate(self
.channels
):
298 assert channel
.index
== -1
301 pass # Shift means nothing here
306 def short_name(self
):
307 """Returns a short name for a format.
309 The short name should be suitable to be used as suffix in function
314 if name
.startswith('MESA_FORMAT_'):
315 name
= name
[len('MESA_FORMAT_'):]
319 def block_size(self
):
320 """Returns the block size (in bits) of the format."""
322 for channel
in self
.channels
:
326 def num_channels(self
):
327 """Returns the number of channels in the format."""
329 for channel
in self
.channels
:
334 def array_element(self
):
335 """Returns a non-void channel if this format is an array, otherwise None.
337 If the returned channel is not None, then this format can be
338 considered to be an array of num_channels() channels identical to the
341 if self
.layout
== ARRAY
:
342 return self
.channels
[0]
343 elif self
.layout
== PACKED
:
344 ref_channel
= self
.channels
[0]
345 if ref_channel
.type == VOID
:
346 ref_channel
= self
.channels
[1]
347 for channel
in self
.channels
:
348 if channel
.size
== 0 or channel
.type == VOID
:
350 if channel
.size
!= ref_channel
.size
or channel
.size
% 8 != 0:
352 if channel
.type != ref_channel
.type:
354 if channel
.norm
!= ref_channel
.norm
:
361 """Returns true if this format can be considered an array format.
363 This function will return true if self.layout == 'array'. However,
364 some formats, such as MESA_FORMAT_A8G8B8R8, can be considered as
365 array formats even though they are technically packed.
367 return self
.array_element() != None
369 def is_compressed(self
):
370 """Returns true if this is a compressed format."""
371 return self
.block_width
!= 1 or self
.block_height
!= 1
374 """Returns true if this format is an integer format.
378 if self
.layout
not in (ARRAY
, PACKED
):
380 for channel
in self
.channels
:
381 if channel
.type not in (VOID
, UNSIGNED
, SIGNED
):
386 """Returns true if this format is an floating-point format."""
387 if self
.layout
not in (ARRAY
, PACKED
):
389 for channel
in self
.channels
:
390 if channel
.type not in (VOID
, FLOAT
):
394 def channel_type(self
):
395 """Returns the type of the channels in this format."""
397 for c
in self
.channels
:
402 assert c
.type == _type
405 def channel_size(self
):
406 """Returns the size (in bits) of the channels in this format.
408 This function should only be called if all of the channels have the
409 same size. This is always the case if is_array() returns true.
412 for c
in self
.channels
:
417 assert c
.size
== size
420 def max_channel_size(self
):
421 """Returns the size of the largest channel."""
423 for c
in self
.channels
:
426 size
= max(size
, c
.size
)
429 def is_normalized(self
):
430 """Returns true if this format is normalized.
432 While only integer formats can be normalized, not all integer formats
433 are normalized. Normalized integer formats are those where the
434 integer value is re-interpreted as a fixed point value in the range
438 for c
in self
.channels
:
443 assert c
.norm
== norm
446 def has_channel(self
, name
):
447 """Returns true if this format has the given channel."""
448 if self
.is_compressed():
449 # Compressed formats are a bit tricky because the list of channels
450 # contains a single channel of type void. Since we don't have any
451 # channel information there, we pull it from the swizzle.
452 if str(self
.swizzle
) == 'xxxx':
454 elif str(self
.swizzle
)[0:3] in ('xxx', 'yyy'):
458 return self
.swizzle
['a'] <= Swizzle
.SWIZZLE_W
462 return self
.swizzle
[name
] <= Swizzle
.SWIZZLE_W
466 for channel
in self
.channels
:
467 if channel
.name
== name
:
471 def get_channel(self
, name
):
472 """Returns the channel with the given name if it exists."""
473 for channel
in self
.channels
:
474 if channel
.name
== name
:
479 """Returns the datatype corresponding to a format's channel type and size"""
480 if self
.layout
== PACKED
:
481 if self
.block_size() == 8:
483 if self
.block_size() == 16:
485 if self
.block_size() == 32:
490 return _get_datatype(self
.channel_type(), self
.channel_size())
492 def _get_datatype(type, size
):
500 elif type == UNSIGNED
:
521 def _parse_channels(fields
, layout
, colorspace
, swizzle
):
527 type = field
[0] if field
[0] else 'x'
531 size
= int(field
[2:])
534 size
= int(field
[1:])
536 channel
= Channel(type, norm
, size
)
537 channels
.append(channel
)
542 """Parse a format descrition in CSV format.
544 This function parses the given CSV file and returns an iterable of
547 with
open(filename
) as stream
:
550 comment
= line
.index('#')
554 line
= line
[:comment
]
559 fields
= [field
.strip() for field
in line
.split(',')]
563 block_width
= int(fields
[2])
564 block_height
= int(fields
[3])
565 colorspace
= fields
[9]
568 swizzle
= Swizzle(fields
[8])
570 sys
.exit("error parsing swizzle for format " + name
)
571 channels
= _parse_channels(fields
[4:8], layout
, colorspace
, swizzle
)
573 yield Format(name
, layout
, block_width
, block_height
, channels
, swizzle
, colorspace
)