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.
41 def is_power_of_two(x
):
42 return not bool(x
& (x
- 1))
44 VERY_LARGE
= 99999999999999999999999
47 """Describes a color channel."""
49 def __init__(self
, type, norm
, size
):
53 self
.sign
= type in (SIGNED
, FLOAT
)
54 self
.name
= None # Set when the channels are added to the format
55 self
.shift
= -1 # Set when the channels are added to the format
56 self
.index
= -1 # Set when the channels are added to the format
65 def __eq__(self
, other
):
66 return self
.type == other
.type and self
.norm
== other
.norm
and self
.size
== other
.size
69 """Returns the maximum representable number."""
70 if self
.type == FLOAT
:
74 if self
.type == UNSIGNED
:
75 return (1 << self
.size
) - 1
76 if self
.type == SIGNED
:
77 return (1 << (self
.size
- 1)) - 1
81 """Returns the minimum representable number."""
82 if self
.type == FLOAT
:
84 if self
.type == UNSIGNED
:
88 if self
.type == SIGNED
:
89 return -(1 << (self
.size
- 1))
93 """Returns the value that represents 1.0f."""
94 if self
.type == UNSIGNED
:
95 return (1 << self
.size
) - 1
96 if self
.type == SIGNED
:
97 return (1 << (self
.size
- 1)) - 1
101 def is_power_of_two(self
):
102 """Returns true if the size of this channel is a power of two."""
103 return is_power_of_two(self
.size
)
106 """Returns the datatype corresponding to a channel type and size"""
107 return _get_datatype(self
.type, self
.size
)
110 """Describes a swizzle operation.
112 A Swizzle is a mapping from one set of channels in one format to the
113 channels in another. Each channel in the destination format is
114 associated with one of the following constants:
116 * SWIZZLE_X: The first channel in the source format
117 * SWIZZLE_Y: The second channel in the source format
118 * SWIZZLE_Z: The third channel in the source format
119 * SWIZZLE_W: The fourth channel in the source format
120 * SWIZZLE_ZERO: The numeric constant 0
121 * SWIZZLE_ONE: THe numeric constant 1
122 * SWIZZLE_NONE: No data available for this channel
124 Sometimes a Swizzle is represented by a 4-character string. In this
125 case, the source channels are represented by the characters "x", "y",
126 "z", and "w"; the numeric constants are represented as "0" and "1"; and
127 no mapping is represented by "_". For instance, the map from
128 luminance-alpha to rgba is given by "xxxy" because each of the three rgb
129 channels maps to the first luminance-alpha channel and the alpha channel
130 maps to second luminance-alpha channel. The mapping from bgr to rgba is
131 given by "zyx1" because the first three colors are reversed and alpha is
135 __identity_str
= 'xyzw01_'
145 def __init__(self
, swizzle
):
146 """Creates a Swizzle object from a string or array."""
147 if isinstance(swizzle
, str):
148 swizzle
= [Swizzle
.__identity
_str
.index(c
) for c
in swizzle
]
150 swizzle
= list(swizzle
)
152 assert isinstance(s
, int) and 0 <= s
and s
<= Swizzle
.SWIZZLE_NONE
154 assert len(swizzle
) <= 4
156 self
.__list
= swizzle
+ [Swizzle
.SWIZZLE_NONE
] * (4 - len(swizzle
))
157 assert len(self
.__list
) == 4
160 """Returns an iterator that iterates over this Swizzle.
162 The values that the iterator produces are described by the SWIZZLE_*
165 return self
.__list
.__iter
__()
168 """Returns a string representation of this Swizzle."""
169 return ''.join(Swizzle
.__identity
_str
[i
] for i
in self
.__list
)
171 def __getitem__(self
, idx
):
172 """Returns the SWIZZLE_* constant for the given destination channel.
174 Valid values for the destination channel include any of the SWIZZLE_*
175 constants or any of the following single-character strings: "x", "y",
176 "z", "w", "r", "g", "b", "a", "z" "s".
179 if isinstance(idx
, int):
180 assert idx
>= Swizzle
.SWIZZLE_X
and idx
<= Swizzle
.SWIZZLE_NONE
181 if idx
<= Swizzle
.SWIZZLE_W
:
182 return self
.__list
.__getitem
__(idx
)
185 elif isinstance(idx
, str):
187 idx
= 'xyzw'.find(idx
)
189 idx
= 'rgba'.find(idx
)
194 return self
.__list
.__getitem
__(idx
)
198 def __mul__(self
, other
):
199 """Returns the composition of this Swizzle with another Swizzle.
201 The resulting swizzle is such that, for any valid input to
202 __getitem__, (a * b)[i] = a[b[i]].
204 assert isinstance(other
, Swizzle
)
205 return Swizzle(self
[x
] for x
in other
)
208 """Returns a pseudo-inverse of this swizzle.
210 Since swizzling isn't necisaraly a bijection, a Swizzle can never
211 be truely inverted. However, the swizzle returned is *almost* the
212 inverse of this swizzle in the sense that, for each i in range(3),
213 a[a.inverse()[i]] is either i or SWIZZLE_NONE. If swizzle is just
214 a permutation with no channels added or removed, then this
215 function returns the actual inverse.
217 This "pseudo-inverse" idea can be demonstrated by mapping from
218 luminance-alpha to rgba that is given by "xxxy". To get from rgba
219 to lumanence-alpha, we use Swizzle("xxxy").inverse() or "xw__".
220 This maps the first component in the lumanence-alpha texture is
221 the red component of the rgba image and the second to the alpha
222 component, exactly as you would expect.
224 rev
= [Swizzle
.SWIZZLE_NONE
] * 4
227 if self
.__list
[j
] == i
and rev
[i
] == Swizzle
.SWIZZLE_NONE
:
233 """Describes a pixel format."""
235 def __init__(self
, name
, layout
, block_width
, block_height
, channels
, swizzle
, colorspace
):
236 """Constructs a Format from some metadata and a list of channels.
238 The channel objects must be unique to this Format and should not be
239 re-used to construct another Format. This is because certain channel
240 information such as shift, offset, and the channel name are set when
241 the Format is created and are calculated based on the entire list of
245 name -- Name of the format such as 'MESA_FORMAT_A8R8G8B8'
246 layout -- One of 'array', 'packed' 'other', or a compressed layout
247 block_width -- The block width if the format is compressed, 1 otherwise
248 block_height -- The block height if the format is compressed, 1 otherwise
249 channels -- A list of Channel objects
250 swizzle -- A Swizzle from this format to rgba
251 colorspace -- one of 'rgb', 'srgb', 'yuv', or 'zs'
255 self
.block_width
= block_width
256 self
.block_height
= block_height
257 self
.channels
= channels
258 assert isinstance(swizzle
, Swizzle
)
259 self
.swizzle
= swizzle
261 assert colorspace
in (RGB
, SRGB
, YUV
, ZS
)
262 self
.colorspace
= colorspace
266 if self
.colorspace
in (RGB
, SRGB
):
267 for (i
, s
) in enumerate(swizzle
):
269 chan_names
[s
] += 'rgba'[i
]
270 elif colorspace
== ZS
:
271 for (i
, s
) in enumerate(swizzle
):
273 chan_names
[s
] += 'zs'[i
]
275 chan_names
= ['x', 'y', 'z', 'w']
277 for c
, name
in zip(self
.channels
, chan_names
):
278 assert c
.name
is None
288 # Set indices and offsets
289 if self
.layout
== PACKED
:
291 for channel
in self
.channels
:
292 assert channel
.shift
== -1
293 channel
.shift
= shift
294 shift
+= channel
.size
295 for idx
, channel
in enumerate(self
.channels
):
296 assert channel
.index
== -1
299 pass # Shift means nothing here
304 def short_name(self
):
305 """Returns a short name for a format.
307 The short name should be suitable to be used as suffix in function
312 if name
.startswith('MESA_FORMAT_'):
313 name
= name
[len('MESA_FORMAT_'):]
317 def block_size(self
):
318 """Returns the block size (in bits) of the format."""
320 for channel
in self
.channels
:
324 def num_channels(self
):
325 """Returns the number of channels in the format."""
327 for channel
in self
.channels
:
332 def array_element(self
):
333 """Returns a non-void channel if this format is an array, otherwise None.
335 If the returned channel is not None, then this format can be
336 considered to be an array of num_channels() channels identical to the
339 if self
.layout
== ARRAY
:
340 return self
.channels
[0]
341 elif self
.layout
== PACKED
:
342 ref_channel
= self
.channels
[0]
343 if ref_channel
.type == VOID
:
344 ref_channel
= self
.channels
[1]
345 for channel
in self
.channels
:
346 if channel
.size
== 0 or channel
.type == VOID
:
348 if channel
.size
!= ref_channel
.size
or channel
.size
% 8 != 0:
350 if channel
.type != ref_channel
.type:
352 if channel
.norm
!= ref_channel
.norm
:
359 """Returns true if this format can be considered an array format.
361 This function will return true if self.layout == 'array'. However,
362 some formats, such as MESA_FORMAT_A8G8B8R8, can be considered as
363 array formats even though they are technically packed.
365 return self
.array_element() != None
367 def is_compressed(self
):
368 """Returns true if this is a compressed format."""
369 return self
.block_width
!= 1 or self
.block_height
!= 1
372 """Returns true if this format is an integer format.
376 if self
.layout
not in (ARRAY
, PACKED
):
378 for channel
in self
.channels
:
379 if channel
.type not in (VOID
, UNSIGNED
, SIGNED
):
384 """Returns true if this format is an floating-point format."""
385 if self
.layout
not in (ARRAY
, PACKED
):
387 for channel
in self
.channels
:
388 if channel
.type not in (VOID
, FLOAT
):
392 def channel_type(self
):
393 """Returns the type of the channels in this format."""
395 for c
in self
.channels
:
400 assert c
.type == _type
403 def channel_size(self
):
404 """Returns the size (in bits) of the channels in this format.
406 This function should only be called if all of the channels have the
407 same size. This is always the case if is_array() returns true.
410 for c
in self
.channels
:
415 assert c
.size
== size
418 def max_channel_size(self
):
419 """Returns the size of the largest channel."""
421 for c
in self
.channels
:
424 size
= max(size
, c
.size
)
427 def is_normalized(self
):
428 """Returns true if this format is normalized.
430 While only integer formats can be normalized, not all integer formats
431 are normalized. Normalized integer formats are those where the
432 integer value is re-interpreted as a fixed point value in the range
436 for c
in self
.channels
:
441 assert c
.norm
== norm
444 def has_channel(self
, name
):
445 """Returns true if this format has the given channel."""
446 if self
.is_compressed():
447 # Compressed formats are a bit tricky because the list of channels
448 # contains a single channel of type void. Since we don't have any
449 # channel information there, we pull it from the swizzle.
450 if str(self
.swizzle
) == 'xxxx':
452 elif str(self
.swizzle
)[0:3] in ('xxx', 'yyy'):
456 return self
.swizzle
['a'] <= Swizzle
.SWIZZLE_W
460 return self
.swizzle
[name
] <= Swizzle
.SWIZZLE_W
464 for channel
in self
.channels
:
465 if channel
.name
== name
:
469 def get_channel(self
, name
):
470 """Returns the channel with the given name if it exists."""
471 for channel
in self
.channels
:
472 if channel
.name
== name
:
477 """Returns the datatype corresponding to a format's channel type and size"""
478 if self
.layout
== PACKED
:
479 if self
.block_size() == 8:
481 if self
.block_size() == 16:
483 if self
.block_size() == 32:
488 return _get_datatype(self
.channel_type(), self
.channel_size())
490 def _get_datatype(type, size
):
498 elif type == UNSIGNED
:
519 def _parse_channels(fields
, layout
, colorspace
, swizzle
):
525 type = field
[0] if field
[0] else 'x'
529 size
= int(field
[2:])
532 size
= int(field
[1:])
534 channel
= Channel(type, norm
, size
)
535 channels
.append(channel
)
540 """Parse a format descrition in CSV format.
542 This function parses the given CSV file and returns an iterable of
545 with
open(filename
) as stream
:
548 comment
= line
.index('#')
552 line
= line
[:comment
]
557 fields
= [field
.strip() for field
in line
.split(',')]
561 block_width
= int(fields
[2])
562 block_height
= int(fields
[3])
563 colorspace
= fields
[9]
565 swizzle
= Swizzle(fields
[8])
566 channels
= _parse_channels(fields
[4:8], layout
, colorspace
, swizzle
)
568 yield Format(name
, layout
, block_width
, block_height
, channels
, swizzle
, colorspace
)