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 """Describes a swizzle operation.
108 A Swizzle is a mapping from one set of channels in one format to the
109 channels in another. Each channel in the destination format is
110 associated with one of the following constants:
112 * SWIZZLE_X: The first channel in the source format
113 * SWIZZLE_Y: The second channel in the source format
114 * SWIZZLE_Z: The third channel in the source format
115 * SWIZZLE_W: The fourth channel in the source format
116 * SWIZZLE_ZERO: The numeric constant 0
117 * SWIZZLE_ONE: THe numeric constant 1
118 * SWIZZLE_NONE: No data available for this channel
120 Sometimes a Swizzle is represented by a 4-character string. In this
121 case, the source channels are represented by the characters "x", "y",
122 "z", and "w"; the numeric constants are represented as "0" and "1"; and
123 no mapping is represented by "_". For instance, the map from
124 luminance-alpha to rgba is given by "xxxy" because each of the three rgb
125 channels maps to the first luminance-alpha channel and the alpha channel
126 maps to second luminance-alpha channel. The mapping from bgr to rgba is
127 given by "zyx1" because the first three colors are reversed and alpha is
131 __identity_str
= 'xyzw01_'
141 def __init__(self
, swizzle
):
142 """Creates a Swizzle object from a string or array."""
143 if isinstance(swizzle
, str):
144 swizzle
= [Swizzle
.__identity
_str
.index(c
) for c
in swizzle
]
146 swizzle
= list(swizzle
)
148 assert isinstance(s
, int) and 0 <= s
and s
<= Swizzle
.SWIZZLE_NONE
150 assert len(swizzle
) <= 4
152 self
.__list
= swizzle
+ [Swizzle
.SWIZZLE_NONE
] * (4 - len(swizzle
))
153 assert len(self
.__list
) == 4
156 """Returns an iterator that iterates over this Swizzle.
158 The values that the iterator produces are described by the SWIZZLE_*
161 return self
.__list
.__iter
__()
164 """Returns a string representation of this Swizzle."""
165 return ''.join(Swizzle
.__identity
_str
[i
] for i
in self
.__list
)
167 def __getitem__(self
, idx
):
168 """Returns the SWIZZLE_* constant for the given destination channel.
170 Valid values for the destination channel include any of the SWIZZLE_*
171 constants or any of the following single-character strings: "x", "y",
172 "z", "w", "r", "g", "b", "a", "z" "s".
175 if isinstance(idx
, int):
176 assert idx
>= Swizzle
.SWIZZLE_X
and idx
<= Swizzle
.SWIZZLE_NONE
177 if idx
<= Swizzle
.SWIZZLE_W
:
178 return self
.__list
.__getitem
__(idx
)
181 elif isinstance(idx
, str):
183 idx
= 'xyzw'.find(idx
)
185 idx
= 'rgba'.find(idx
)
190 return self
.__list
.__getitem
__(idx
)
194 def __mul__(self
, other
):
195 """Returns the composition of this Swizzle with another Swizzle.
197 The resulting swizzle is such that, for any valid input to
198 __getitem__, (a * b)[i] = a[b[i]].
200 assert isinstance(other
, Swizzle
)
201 return Swizzle(self
[x
] for x
in other
)
204 """Returns a pseudo-inverse of this swizzle.
206 Since swizzling isn't necisaraly a bijection, a Swizzle can never
207 be truely inverted. However, the swizzle returned is *almost* the
208 inverse of this swizzle in the sense that, for each i in range(3),
209 a[a.inverse()[i]] is either i or SWIZZLE_NONE. If swizzle is just
210 a permutation with no channels added or removed, then this
211 function returns the actual inverse.
213 This "pseudo-inverse" idea can be demonstrated by mapping from
214 luminance-alpha to rgba that is given by "xxxy". To get from rgba
215 to lumanence-alpha, we use Swizzle("xxxy").inverse() or "xw__".
216 This maps the first component in the lumanence-alpha texture is
217 the red component of the rgba image and the second to the alpha
218 component, exactly as you would expect.
220 rev
= [Swizzle
.SWIZZLE_NONE
] * 4
223 if self
.__list
[j
] == i
and rev
[i
] == Swizzle
.SWIZZLE_NONE
:
229 """Describes a pixel format."""
231 def __init__(self
, name
, layout
, block_width
, block_height
, channels
, swizzle
, colorspace
):
232 """Constructs a Format from some metadata and a list of channels.
234 The channel objects must be unique to this Format and should not be
235 re-used to construct another Format. This is because certain channel
236 information such as shift, offset, and the channel name are set when
237 the Format is created and are calculated based on the entire list of
241 name -- Name of the format such as 'MESA_FORMAT_A8R8G8B8'
242 layout -- One of 'array', 'packed' 'other', or a compressed layout
243 block_width -- The block width if the format is compressed, 1 otherwise
244 block_height -- The block height if the format is compressed, 1 otherwise
245 channels -- A list of Channel objects
246 swizzle -- A Swizzle from this format to rgba
247 colorspace -- one of 'rgb', 'srgb', 'yuv', or 'zs'
251 self
.block_width
= block_width
252 self
.block_height
= block_height
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
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
:
472 def _parse_channels(fields
, layout
, colorspace
, swizzle
):
478 type = field
[0] if field
[0] else 'x'
482 size
= int(field
[2:])
485 size
= int(field
[1:])
487 channel
= Channel(type, norm
, size
)
488 channels
.append(channel
)
493 """Parse a format descrition in CSV format.
495 This function parses the given CSV file and returns an iterable of
498 with
open(filename
) as stream
:
501 comment
= line
.index('#')
505 line
= line
[:comment
]
510 fields
= [field
.strip() for field
in line
.split(',')]
514 block_width
= int(fields
[2])
515 block_height
= int(fields
[3])
516 colorspace
= fields
[9]
518 swizzle
= Swizzle(fields
[8])
519 channels
= _parse_channels(fields
[4:8], layout
, colorspace
, swizzle
)
521 yield Format(name
, layout
, block_width
, block_height
, channels
, swizzle
, colorspace
)