Merge commit 'origin/7.8'
[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 VOID, UNSIGNED, SIGNED, FIXED, FLOAT = range(5)
34
35 SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W, SWIZZLE_0, SWIZZLE_1, SWIZZLE_NONE, = range(7)
36
37 PLAIN = 'plain'
38
39 RGB = 'rgb'
40 SRGB = 'srgb'
41 YUV = 'yuv'
42 ZS = 'zs'
43
44
45 def is_pot(x):
46 return (x & (x - 1)) == 0;
47
48
49 VERY_LARGE = 99999999999999999999999
50
51
52 class Channel:
53 '''Describe the channel of a color channel.'''
54
55 def __init__(self, type, norm, size, name = ''):
56 self.type = type
57 self.norm = norm
58 self.size = size
59 self.sign = type in (SIGNED, FIXED, FLOAT)
60 self.name = name
61
62 def __str__(self):
63 s = str(self.type)
64 if self.norm:
65 s += 'n'
66 s += str(self.size)
67 return s
68
69 def __eq__(self, other):
70 return self.type == other.type and self.norm == other.norm and self.size == other.size
71
72 def max(self):
73 '''Maximum representable number.'''
74 if self.type == FLOAT:
75 return VERY_LARGE
76 if self.type == FIXED:
77 return (1 << (self.size/2)) - 1
78 if self.norm:
79 return 1
80 if self.type == UNSIGNED:
81 return (1 << self.size) - 1
82 if self.type == SIGNED:
83 return (1 << (self.size - 1)) - 1
84 assert False
85
86 def min(self):
87 '''Minimum representable number.'''
88 if self.type == FLOAT:
89 return -VERY_LARGE
90 if self.type == FIXED:
91 return -(1 << (self.size/2))
92 if self.type == UNSIGNED:
93 return 0
94 if self.norm:
95 return -1
96 if self.type == SIGNED:
97 return -(1 << (self.size - 1))
98 assert False
99
100
101 class Format:
102 '''Describe a pixel format.'''
103
104 def __init__(self, name, layout, block_width, block_height, channels, swizzles, colorspace):
105 self.name = name
106 self.layout = layout
107 self.block_width = block_width
108 self.block_height = block_height
109 self.channels = channels
110 self.swizzles = swizzles
111 self.name = name
112 self.colorspace = colorspace
113
114 def __str__(self):
115 return self.name
116
117 def short_name(self):
118 '''Make up a short norm for a format, suitable to be used as suffix in
119 function names.'''
120
121 name = self.name
122 if name.startswith('PIPE_FORMAT_'):
123 name = name[len('PIPE_FORMAT_'):]
124 name = name.lower()
125 return name
126
127 def block_size(self):
128 size = 0
129 for channel in self.channels:
130 size += channel.size
131 return size
132
133 def nr_channels(self):
134 nr_channels = 0
135 for channel in self.channels:
136 if channel.size:
137 nr_channels += 1
138 return nr_channels
139
140 def is_array(self):
141 if self.layout != PLAIN:
142 return False
143 ref_channel = self.channels[0]
144 for channel in self.channels[1:]:
145 if channel.size and (channel.size != ref_channel.size or channel.size % 8):
146 return False
147 return True
148
149 def is_mixed(self):
150 if self.layout != PLAIN:
151 return False
152 ref_channel = self.channels[0]
153 if ref_channel.type == VOID:
154 ref_channel = self.channels[1]
155 for channel in self.channels[1:]:
156 if channel.type != VOID:
157 if channel.type != ref_channel.type:
158 return True
159 if channel.norm != ref_channel.norm:
160 return True
161 return False
162
163 def is_pot(self):
164 return is_pot(self.block_size())
165
166 def is_int(self):
167 if self.layout != PLAIN:
168 return False
169 for channel in self.channels:
170 if channel.type not in (VOID, UNSIGNED, SIGNED):
171 return False
172 return True
173
174 def is_float(self):
175 if self.layout != PLAIN:
176 return False
177 for channel in self.channels:
178 if channel.type not in (VOID, FLOAT):
179 return False
180 return True
181
182 def is_bitmask(self):
183 if self.layout != PLAIN:
184 return False
185 if self.block_size() not in (8, 16, 32):
186 return False
187 for channel in self.channels:
188 if channel.type not in (VOID, UNSIGNED, SIGNED):
189 return False
190 return True
191
192 def inv_swizzles(self):
193 '''Return an array[4] of inverse swizzle terms'''
194 inv_swizzle = [None]*4
195 for i in range(4):
196 swizzle = self.swizzles[i]
197 if swizzle < 4:
198 inv_swizzle[swizzle] = i
199 return inv_swizzle
200
201 def stride(self):
202 return self.block_size()/8
203
204
205 _type_parse_map = {
206 '': VOID,
207 'x': VOID,
208 'u': UNSIGNED,
209 's': SIGNED,
210 'h': FIXED,
211 'f': FLOAT,
212 }
213
214 _swizzle_parse_map = {
215 'x': SWIZZLE_X,
216 'y': SWIZZLE_Y,
217 'z': SWIZZLE_Z,
218 'w': SWIZZLE_W,
219 '0': SWIZZLE_0,
220 '1': SWIZZLE_1,
221 '_': SWIZZLE_NONE,
222 }
223
224 def parse(filename):
225 '''Parse the format descrition in CSV format in terms of the
226 Channel and Format classes above.'''
227
228 stream = open(filename)
229 formats = []
230 for line in stream:
231 try:
232 comment = line.index('#')
233 except ValueError:
234 pass
235 else:
236 line = line[:comment]
237 line = line.strip()
238 if not line:
239 continue
240
241 fields = [field.strip() for field in line.split(',')]
242
243 name = fields[0]
244 layout = fields[1]
245 block_width, block_height = map(int, fields[2:4])
246
247 swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[8]]
248 colorspace = fields[9]
249
250 if layout == PLAIN:
251 names = ['']*4
252 if colorspace in (RGB, SRGB):
253 for i in range(4):
254 swizzle = swizzles[i]
255 if swizzle < 4:
256 names[swizzle] += 'rgba'[i]
257 elif colorspace == ZS:
258 for i in range(4):
259 swizzle = swizzles[i]
260 if swizzle < 4:
261 names[swizzle] += 'zs'[i]
262 else:
263 assert False
264 for i in range(4):
265 if names[i] == '':
266 names[i] = 'x'
267 else:
268 names = ['x', 'y', 'z', 'w']
269
270 channels = []
271 for i in range(0, 4):
272 field = fields[4 + i]
273 if field:
274 type = _type_parse_map[field[0]]
275 if field[1] == 'n':
276 norm = True
277 size = int(field[2:])
278 else:
279 norm = False
280 size = int(field[1:])
281 else:
282 type = VOID
283 norm = False
284 size = 0
285 channel = Channel(type, norm, size, names[i])
286 channels.append(channel)
287
288 format = Format(name, layout, block_width, block_height, channels, swizzles, colorspace)
289 formats.append(format)
290 return formats
291