9f6ac36de1178352e59aa6f823b775eb1468b814
[mesa.git] / src / gallium / auxiliary / util / u_format_parse.py
1 #!/usr/bin/env python
2
3 '''
4 /**************************************************************************
5 *
6 * Copyright 2009 VMware, Inc.
7 * All Rights Reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the
11 * "Software"), to deal in the Software without restriction, including
12 * without limitation the rights to use, copy, modify, merge, publish,
13 * distribute, sub license, and/or sell copies of the Software, and to
14 * permit persons to whom the Software is furnished to do so, subject to
15 * the following conditions:
16 *
17 * The above copyright notice and this permission notice (including the
18 * next paragraph) shall be included in all copies or substantial portions
19 * of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
24 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
25 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 *
29 **************************************************************************/
30 '''
31
32
33 import sys
34
35 VOID, UNSIGNED, SIGNED, FIXED, FLOAT = range(5)
36
37 SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W, SWIZZLE_0, SWIZZLE_1, SWIZZLE_NONE, = range(7)
38
39 PLAIN = 'plain'
40
41 RGB = 'rgb'
42 SRGB = 'srgb'
43 YUV = 'yuv'
44 ZS = 'zs'
45
46
47 # Not cross-compiler friendly
48 is_big_endian = sys.byteorder == 'big'
49
50 def is_pot(x):
51 return (x & (x - 1)) == 0
52
53
54 VERY_LARGE = 99999999999999999999999
55
56
57 class Channel:
58 '''Describe the channel of a color channel.'''
59
60 def __init__(self, type, norm, pure, size, name = ''):
61 self.type = type
62 self.norm = norm
63 self.pure = pure
64 self.size = size
65 self.sign = type in (SIGNED, FIXED, FLOAT)
66 self.name = name
67
68 def __str__(self):
69 s = str(self.type)
70 if self.norm:
71 s += 'n'
72 if self.pure:
73 s += 'p'
74 s += str(self.size)
75 return s
76
77 def __eq__(self, other):
78 return self.type == other.type and self.norm == other.norm and self.pure == other.pure and self.size == other.size
79
80 def max(self):
81 '''Maximum representable number.'''
82 if self.type == FLOAT:
83 return VERY_LARGE
84 if self.type == FIXED:
85 return (1 << (self.size/2)) - 1
86 if self.norm:
87 return 1
88 if self.type == UNSIGNED:
89 return (1 << self.size) - 1
90 if self.type == SIGNED:
91 return (1 << (self.size - 1)) - 1
92 assert False
93
94 def min(self):
95 '''Minimum representable number.'''
96 if self.type == FLOAT:
97 return -VERY_LARGE
98 if self.type == FIXED:
99 return -(1 << (self.size/2))
100 if self.type == UNSIGNED:
101 return 0
102 if self.norm:
103 return -1
104 if self.type == SIGNED:
105 return -(1 << (self.size - 1))
106 assert False
107
108
109 class Format:
110 '''Describe a pixel format.'''
111
112 def __init__(self, name, layout, block_width, block_height, channels, swizzles, colorspace):
113 self.name = name
114 self.layout = layout
115 self.block_width = block_width
116 self.block_height = block_height
117 self.channels = channels
118 self.swizzles = swizzles
119 self.name = name
120 self.colorspace = colorspace
121
122 def __str__(self):
123 return self.name
124
125 def short_name(self):
126 '''Make up a short norm for a format, suitable to be used as suffix in
127 function names.'''
128
129 name = self.name
130 if name.startswith('PIPE_FORMAT_'):
131 name = name[len('PIPE_FORMAT_'):]
132 name = name.lower()
133 return name
134
135 def block_size(self):
136 size = 0
137 for channel in self.channels:
138 size += channel.size
139 return size
140
141 def nr_channels(self):
142 nr_channels = 0
143 for channel in self.channels:
144 if channel.size:
145 nr_channels += 1
146 return nr_channels
147
148 def array_element(self):
149 if self.layout != PLAIN:
150 return None
151 ref_channel = self.channels[0]
152 if ref_channel.type == VOID:
153 ref_channel = self.channels[1]
154 for channel in self.channels:
155 if channel.size and (channel.size != ref_channel.size or channel.size % 8):
156 return None
157 if channel.type != VOID:
158 if channel.type != ref_channel.type:
159 return None
160 if channel.norm != ref_channel.norm:
161 return None
162 if channel.pure != ref_channel.pure:
163 return None
164 return ref_channel
165
166 def is_array(self):
167 return self.array_element() != None
168
169 def is_mixed(self):
170 if self.layout != PLAIN:
171 return False
172 ref_channel = self.channels[0]
173 if ref_channel.type == VOID:
174 ref_channel = self.channels[1]
175 for channel in self.channels[1:]:
176 if channel.type != VOID:
177 if channel.type != ref_channel.type:
178 return True
179 if channel.norm != ref_channel.norm:
180 return True
181 if channel.pure != ref_channel.pure:
182 return True
183 return False
184
185 def is_pot(self):
186 return is_pot(self.block_size())
187
188 def is_int(self):
189 if self.layout != PLAIN:
190 return False
191 for channel in self.channels:
192 if channel.type not in (VOID, UNSIGNED, SIGNED):
193 return False
194 return True
195
196 def is_float(self):
197 if self.layout != PLAIN:
198 return False
199 for channel in self.channels:
200 if channel.type not in (VOID, FLOAT):
201 return False
202 return True
203
204 def is_bitmask(self):
205 if self.layout != PLAIN:
206 return False
207 if self.block_size() not in (8, 16, 32):
208 return False
209 for channel in self.channels:
210 if channel.type not in (VOID, UNSIGNED, SIGNED):
211 return False
212 return True
213
214 def is_pure_color(self):
215 if self.layout != PLAIN or self.colorspace == ZS:
216 return False
217 pures = [channel.pure
218 for channel in self.channels
219 if channel.type != VOID]
220 for x in pures:
221 assert x == pures[0]
222 return pures[0]
223
224 def channel_type(self):
225 types = [channel.type
226 for channel in self.channels
227 if channel.type != VOID]
228 for x in types:
229 assert x == types[0]
230 return types[0]
231
232 def is_pure_signed(self):
233 return self.is_pure_color() and self.channel_type() == SIGNED
234
235 def is_pure_unsigned(self):
236 return self.is_pure_color() and self.channel_type() == UNSIGNED
237
238 def has_channel(self, id):
239 return self.swizzles[id] != SWIZZLE_NONE
240
241 def has_depth(self):
242 return self.colorspace == ZS and self.has_channel(0)
243
244 def has_stencil(self):
245 return self.colorspace == ZS and self.has_channel(1)
246
247 def stride(self):
248 return self.block_size()/8
249
250
251 _type_parse_map = {
252 '': VOID,
253 'x': VOID,
254 'u': UNSIGNED,
255 's': SIGNED,
256 'h': FIXED,
257 'f': FLOAT,
258 }
259
260 _swizzle_parse_map = {
261 'x': SWIZZLE_X,
262 'y': SWIZZLE_Y,
263 'z': SWIZZLE_Z,
264 'w': SWIZZLE_W,
265 '0': SWIZZLE_0,
266 '1': SWIZZLE_1,
267 '_': SWIZZLE_NONE,
268 }
269
270 def _parse_channels(fields, layout, colorspace, swizzles):
271 if layout == PLAIN:
272 names = ['']*4
273 if colorspace in (RGB, SRGB):
274 for i in range(4):
275 swizzle = swizzles[i]
276 if swizzle < 4:
277 names[swizzle] += 'rgba'[i]
278 elif colorspace == ZS:
279 for i in range(4):
280 swizzle = swizzles[i]
281 if swizzle < 4:
282 names[swizzle] += 'zs'[i]
283 else:
284 assert False
285 for i in range(4):
286 if names[i] == '':
287 names[i] = 'x'
288 else:
289 names = ['x', 'y', 'z', 'w']
290
291 channels = []
292 for i in range(0, 4):
293 field = fields[i]
294 if field:
295 type = _type_parse_map[field[0]]
296 if field[1] == 'n':
297 norm = True
298 pure = False
299 size = int(field[2:])
300 elif field[1] == 'p':
301 pure = True
302 norm = False
303 size = int(field[2:])
304 else:
305 norm = False
306 pure = False
307 size = int(field[1:])
308 else:
309 type = VOID
310 norm = False
311 pure = False
312 size = 0
313 channel = Channel(type, norm, pure, size, names[i])
314 channels.append(channel)
315
316 return channels
317
318 def parse(filename):
319 '''Parse the format descrition in CSV format in terms of the
320 Channel and Format classes above.'''
321
322 stream = open(filename)
323 formats = []
324 for line in stream:
325 try:
326 comment = line.index('#')
327 except ValueError:
328 pass
329 else:
330 line = line[:comment]
331 line = line.strip()
332 if not line:
333 continue
334
335 fields = [field.strip() for field in line.split(',')]
336
337 name = fields[0]
338 layout = fields[1]
339 block_width, block_height = map(int, fields[2:4])
340 colorspace = fields[9]
341
342 swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[8]]
343 channels = _parse_channels(fields[4:8], layout, colorspace, swizzles)
344
345 shift = 0
346 for channel in channels[3::-1] if is_big_endian else channels:
347 channel.shift = shift
348 shift += channel.size
349
350 format = Format(name, layout, block_width, block_height, channels, swizzles, colorspace)
351 formats.append(format)
352 return formats
353