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 VERY_LARGE
= 99999999999999999999999
46 """Describes a color channel."""
48 def __init__(self
, type, norm
, size
):
52 self
.sign
= type in (SIGNED
, FLOAT
)
53 self
.name
= None # Set when the channels are added to the format
54 self
.shift
= -1 # Set when the channels are added to the format
55 self
.index
= -1 # Set when the channels are added to the format
64 def __eq__(self
, other
):
65 return self
.type == other
.type and self
.norm
== other
.norm
and self
.size
== other
.size
68 """Returns the maximum representable number."""
69 if self
.type == FLOAT
:
73 if self
.type == UNSIGNED
:
74 return (1 << self
.size
) - 1
75 if self
.type == SIGNED
:
76 return (1 << (self
.size
- 1)) - 1
80 """Returns the minimum representable number."""
81 if self
.type == FLOAT
:
83 if self
.type == UNSIGNED
:
87 if self
.type == SIGNED
:
88 return -(1 << (self
.size
- 1))
92 """Returns the value that represents 1.0f."""
93 if self
.type == UNSIGNED
:
94 return (1 << self
.size
) - 1
95 if self
.type == SIGNED
:
96 return (1 << (self
.size
- 1)) - 1
101 """Returns the datatype corresponding to a channel type and size"""
102 return _get_datatype(self
.type, self
.size
)
105 """Describes a swizzle operation.
107 A Swizzle is a mapping from one set of channels in one format to the
108 channels in another. Each channel in the destination format is
109 associated with one of the following constants:
111 * SWIZZLE_X: The first channel in the source format
112 * SWIZZLE_Y: The second channel in the source format
113 * SWIZZLE_Z: The third channel in the source format
114 * SWIZZLE_W: The fourth channel in the source format
115 * SWIZZLE_ZERO: The numeric constant 0
116 * SWIZZLE_ONE: THe numeric constant 1
117 * SWIZZLE_NONE: No data available for this channel
119 Sometimes a Swizzle is represented by a 4-character string. In this
120 case, the source channels are represented by the characters "x", "y",
121 "z", and "w"; the numeric constants are represented as "0" and "1"; and
122 no mapping is represented by "_". For instance, the map from
123 luminance-alpha to rgba is given by "xxxy" because each of the three rgb
124 channels maps to the first luminance-alpha channel and the alpha channel
125 maps to second luminance-alpha channel. The mapping from bgr to rgba is
126 given by "zyx1" because the first three colors are reversed and alpha is
130 __identity_str
= 'xyzw01_'
140 def __init__(self
, swizzle
):
141 """Creates a Swizzle object from a string or array."""
142 if isinstance(swizzle
, str):
143 swizzle
= [Swizzle
.__identity
_str
.index(c
) for c
in swizzle
]
145 swizzle
= list(swizzle
)
147 assert isinstance(s
, int) and 0 <= s
and s
<= Swizzle
.SWIZZLE_NONE
149 assert len(swizzle
) <= 4
151 self
.__list
= swizzle
+ [Swizzle
.SWIZZLE_NONE
] * (4 - len(swizzle
))
152 assert len(self
.__list
) == 4
155 """Returns an iterator that iterates over this Swizzle.
157 The values that the iterator produces are described by the SWIZZLE_*
160 return self
.__list
.__iter
__()
163 """Returns a string representation of this Swizzle."""
164 return ''.join(Swizzle
.__identity
_str
[i
] for i
in self
.__list
)
166 def __getitem__(self
, idx
):
167 """Returns the SWIZZLE_* constant for the given destination channel.
169 Valid values for the destination channel include any of the SWIZZLE_*
170 constants or any of the following single-character strings: "x", "y",
171 "z", "w", "r", "g", "b", "a", "z" "s".
174 if isinstance(idx
, int):
175 assert idx
>= Swizzle
.SWIZZLE_X
and idx
<= Swizzle
.SWIZZLE_NONE
176 if idx
<= Swizzle
.SWIZZLE_W
:
177 return self
.__list
.__getitem
__(idx
)
180 elif isinstance(idx
, str):
182 idx
= 'xyzw'.find(idx
)
184 idx
= 'rgba'.find(idx
)
189 return self
.__list
.__getitem
__(idx
)
193 def __mul__(self
, other
):
194 """Returns the composition of this Swizzle with another Swizzle.
196 The resulting swizzle is such that, for any valid input to
197 __getitem__, (a * b)[i] = a[b[i]].
199 assert isinstance(other
, Swizzle
)
200 return Swizzle(self
[x
] for x
in other
)
203 """Returns a pseudo-inverse of this swizzle.
205 Since swizzling isn't necisaraly a bijection, a Swizzle can never
206 be truely inverted. However, the swizzle returned is *almost* the
207 inverse of this swizzle in the sense that, for each i in range(3),
208 a[a.inverse()[i]] is either i or SWIZZLE_NONE. If swizzle is just
209 a permutation with no channels added or removed, then this
210 function returns the actual inverse.
212 This "pseudo-inverse" idea can be demonstrated by mapping from
213 luminance-alpha to rgba that is given by "xxxy". To get from rgba
214 to lumanence-alpha, we use Swizzle("xxxy").inverse() or "xw__".
215 This maps the first component in the lumanence-alpha texture is
216 the red component of the rgba image and the second to the alpha
217 component, exactly as you would expect.
219 rev
= [Swizzle
.SWIZZLE_NONE
] * 4
222 if self
.__list
[j
] == i
and rev
[i
] == Swizzle
.SWIZZLE_NONE
:
228 """Describes a pixel format."""
230 def __init__(self
, name
, layout
, block_width
, block_height
, channels
, swizzle
, colorspace
):
231 """Constructs a Format from some metadata and a list of channels.
233 The channel objects must be unique to this Format and should not be
234 re-used to construct another Format. This is because certain channel
235 information such as shift, offset, and the channel name are set when
236 the Format is created and are calculated based on the entire list of
240 name -- Name of the format such as 'MESA_FORMAT_A8R8G8B8'
241 layout -- One of 'array', 'packed' 'other', or a compressed layout
242 block_width -- The block width if the format is compressed, 1 otherwise
243 block_height -- The block height 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
.channels
= channels
253 assert isinstance(swizzle
, Swizzle
)
254 self
.swizzle
= swizzle
256 assert colorspace
in (RGB
, SRGB
, YUV
, ZS
)
257 self
.colorspace
= colorspace
261 if self
.colorspace
in (RGB
, SRGB
):
262 for (i
, s
) in enumerate(swizzle
):
264 chan_names
[s
] += 'rgba'[i
]
265 elif colorspace
== ZS
:
266 for (i
, s
) in enumerate(swizzle
):
268 chan_names
[s
] += 'zs'[i
]
270 chan_names
= ['x', 'y', 'z', 'w']
272 for c
, name
in zip(self
.channels
, chan_names
):
273 assert c
.name
is None
283 # Set indices and offsets
284 if self
.layout
== PACKED
:
286 for channel
in self
.channels
:
287 assert channel
.shift
== -1
288 channel
.shift
= shift
289 shift
+= channel
.size
290 for idx
, channel
in enumerate(self
.channels
):
291 assert channel
.index
== -1
294 pass # Shift means nothing here
299 def short_name(self
):
300 """Returns a short name for a format.
302 The short name should be suitable to be used as suffix in function
307 if name
.startswith('MESA_FORMAT_'):
308 name
= name
[len('MESA_FORMAT_'):]
312 def block_size(self
):
313 """Returns the block size (in bits) of the format."""
315 for channel
in self
.channels
:
319 def num_channels(self
):
320 """Returns the number of channels in the format."""
322 for channel
in self
.channels
:
327 def array_element(self
):
328 """Returns a non-void channel if this format is an array, otherwise None.
330 If the returned channel is not None, then this format can be
331 considered to be an array of num_channels() channels identical to the
334 if self
.layout
== ARRAY
:
335 return self
.channels
[0]
336 elif self
.layout
== PACKED
:
337 ref_channel
= self
.channels
[0]
338 if ref_channel
.type == VOID
:
339 ref_channel
= self
.channels
[1]
340 for channel
in self
.channels
:
341 if channel
.size
== 0 or channel
.type == VOID
:
343 if channel
.size
!= ref_channel
.size
or channel
.size
% 8 != 0:
345 if channel
.type != ref_channel
.type:
347 if channel
.norm
!= ref_channel
.norm
:
354 """Returns true if this format can be considered an array format.
356 This function will return true if self.layout == 'array'. However,
357 some formats, such as MESA_FORMAT_A8G8B8R8, can be considered as
358 array formats even though they are technically packed.
360 return self
.array_element() != None
362 def is_compressed(self
):
363 """Returns true if this is a compressed format."""
364 return self
.block_width
!= 1 or self
.block_height
!= 1
367 """Returns true if this format is an integer format.
371 if self
.layout
not in (ARRAY
, PACKED
):
373 for channel
in self
.channels
:
374 if channel
.type not in (VOID
, UNSIGNED
, SIGNED
):
379 """Returns true if this format is an floating-point format."""
380 if self
.layout
not in (ARRAY
, PACKED
):
382 for channel
in self
.channels
:
383 if channel
.type not in (VOID
, FLOAT
):
387 def channel_type(self
):
388 """Returns the type of the channels in this format."""
390 for c
in self
.channels
:
395 assert c
.type == _type
398 def channel_size(self
):
399 """Returns the size (in bits) of the channels in this format.
401 This function should only be called if all of the channels have the
402 same size. This is always the case if is_array() returns true.
405 for c
in self
.channels
:
410 assert c
.size
== size
413 def max_channel_size(self
):
414 """Returns the size of the largest channel."""
416 for c
in self
.channels
:
419 size
= max(size
, c
.size
)
422 def is_normalized(self
):
423 """Returns true if this format is normalized.
425 While only integer formats can be normalized, not all integer formats
426 are normalized. Normalized integer formats are those where the
427 integer value is re-interpreted as a fixed point value in the range
431 for c
in self
.channels
:
436 assert c
.norm
== norm
439 def has_channel(self
, name
):
440 """Returns true if this format has the given channel."""
441 if self
.is_compressed():
442 # Compressed formats are a bit tricky because the list of channels
443 # contains a single channel of type void. Since we don't have any
444 # channel information there, we pull it from the swizzle.
445 if str(self
.swizzle
) == 'xxxx':
447 elif str(self
.swizzle
)[0:3] in ('xxx', 'yyy'):
451 return self
.swizzle
['a'] <= Swizzle
.SWIZZLE_W
455 return self
.swizzle
[name
] <= Swizzle
.SWIZZLE_W
459 for channel
in self
.channels
:
460 if channel
.name
== name
:
464 def get_channel(self
, name
):
465 """Returns the channel with the given name if it exists."""
466 for channel
in self
.channels
:
467 if channel
.name
== name
:
472 """Returns the datatype corresponding to a format's channel type and size"""
473 if self
.layout
== PACKED
:
474 if self
.block_size() == 8:
476 if self
.block_size() == 16:
478 if self
.block_size() == 32:
483 return _get_datatype(self
.channel_type(), self
.channel_size())
485 def _get_datatype(type, size
):
493 elif type == UNSIGNED
:
514 def _parse_channels(fields
, layout
, colorspace
, swizzle
):
520 type = field
[0] if field
[0] else 'x'
524 size
= int(field
[2:])
527 size
= int(field
[1:])
529 channel
= Channel(type, norm
, size
)
530 channels
.append(channel
)
535 """Parse a format descrition in CSV format.
537 This function parses the given CSV file and returns an iterable of
540 with
open(filename
) as stream
:
543 comment
= line
.index('#')
547 line
= line
[:comment
]
552 fields
= [field
.strip() for field
in line
.split(',')]
556 block_width
= int(fields
[2])
557 block_height
= int(fields
[3])
558 colorspace
= fields
[9]
561 swizzle
= Swizzle(fields
[8])
563 sys
.exit("error parsing swizzle for format " + name
)
564 channels
= _parse_channels(fields
[4:8], layout
, colorspace
, swizzle
)
566 yield Format(name
, layout
, block_width
, block_height
, channels
, swizzle
, colorspace
)