Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5564 | serge | 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, pure, size, name = ''): |
||
56 | self.type = type |
||
57 | self.norm = norm |
||
58 | self.pure = pure |
||
59 | self.size = size |
||
60 | self.sign = type in (SIGNED, FIXED, FLOAT) |
||
61 | self.name = name |
||
62 | |||
63 | def __str__(self): |
||
64 | s = str(self.type) |
||
65 | if self.norm: |
||
66 | s += 'n' |
||
67 | if self.pure: |
||
68 | s += 'p' |
||
69 | s += str(self.size) |
||
70 | return s |
||
71 | |||
72 | def __eq__(self, other): |
||
73 | return self.type == other.type and self.norm == other.norm and self.pure == other.pure and self.size == other.size |
||
74 | |||
75 | def max(self): |
||
76 | '''Maximum representable number.''' |
||
77 | if self.type == FLOAT: |
||
78 | return VERY_LARGE |
||
79 | if self.type == FIXED: |
||
80 | return (1 << (self.size/2)) - 1 |
||
81 | if self.norm: |
||
82 | return 1 |
||
83 | if self.type == UNSIGNED: |
||
84 | return (1 << self.size) - 1 |
||
85 | if self.type == SIGNED: |
||
86 | return (1 << (self.size - 1)) - 1 |
||
87 | assert False |
||
88 | |||
89 | def min(self): |
||
90 | '''Minimum representable number.''' |
||
91 | if self.type == FLOAT: |
||
92 | return -VERY_LARGE |
||
93 | if self.type == FIXED: |
||
94 | return -(1 << (self.size/2)) |
||
95 | if self.type == UNSIGNED: |
||
96 | return 0 |
||
97 | if self.norm: |
||
98 | return -1 |
||
99 | if self.type == SIGNED: |
||
100 | return -(1 << (self.size - 1)) |
||
101 | assert False |
||
102 | |||
103 | |||
104 | class Format: |
||
105 | '''Describe a pixel format.''' |
||
106 | |||
107 | def __init__(self, name, layout, block_width, block_height, le_channels, le_swizzles, be_channels, be_swizzles, colorspace): |
||
108 | self.name = name |
||
109 | self.layout = layout |
||
110 | self.block_width = block_width |
||
111 | self.block_height = block_height |
||
112 | self.le_channels = le_channels |
||
113 | self.le_swizzles = le_swizzles |
||
114 | self.be_channels = be_channels |
||
115 | self.be_swizzles = be_swizzles |
||
116 | self.name = name |
||
117 | self.colorspace = colorspace |
||
118 | |||
119 | def __str__(self): |
||
120 | return self.name |
||
121 | |||
122 | def short_name(self): |
||
123 | '''Make up a short norm for a format, suitable to be used as suffix in |
||
124 | function names.''' |
||
125 | |||
126 | name = self.name |
||
127 | if name.startswith('PIPE_FORMAT_'): |
||
128 | name = name[len('PIPE_FORMAT_'):] |
||
129 | name = name.lower() |
||
130 | return name |
||
131 | |||
132 | def block_size(self): |
||
133 | size = 0 |
||
134 | for channel in self.le_channels: |
||
135 | size += channel.size |
||
136 | return size |
||
137 | |||
138 | def nr_channels(self): |
||
139 | nr_channels = 0 |
||
140 | for channel in self.le_channels: |
||
141 | if channel.size: |
||
142 | nr_channels += 1 |
||
143 | return nr_channels |
||
144 | |||
145 | def array_element(self): |
||
146 | if self.layout != PLAIN: |
||
147 | return None |
||
148 | ref_channel = self.le_channels[0] |
||
149 | if ref_channel.type == VOID: |
||
150 | ref_channel = self.le_channels[1] |
||
151 | for channel in self.le_channels: |
||
152 | if channel.size and (channel.size != ref_channel.size or channel.size % 8): |
||
153 | return None |
||
154 | if channel.type != VOID: |
||
155 | if channel.type != ref_channel.type: |
||
156 | return None |
||
157 | if channel.norm != ref_channel.norm: |
||
158 | return None |
||
159 | if channel.pure != ref_channel.pure: |
||
160 | return None |
||
161 | return ref_channel |
||
162 | |||
163 | def is_array(self): |
||
164 | return self.array_element() != None |
||
165 | |||
166 | def is_mixed(self): |
||
167 | if self.layout != PLAIN: |
||
168 | return False |
||
169 | ref_channel = self.le_channels[0] |
||
170 | if ref_channel.type == VOID: |
||
171 | ref_channel = self.le_channels[1] |
||
172 | for channel in self.le_channels[1:]: |
||
173 | if channel.type != VOID: |
||
174 | if channel.type != ref_channel.type: |
||
175 | return True |
||
176 | if channel.norm != ref_channel.norm: |
||
177 | return True |
||
178 | if channel.pure != ref_channel.pure: |
||
179 | return True |
||
180 | return False |
||
181 | |||
182 | def is_pot(self): |
||
183 | return is_pot(self.block_size()) |
||
184 | |||
185 | def is_int(self): |
||
186 | if self.layout != PLAIN: |
||
187 | return False |
||
188 | for channel in self.le_channels: |
||
189 | if channel.type not in (VOID, UNSIGNED, SIGNED): |
||
190 | return False |
||
191 | return True |
||
192 | |||
193 | def is_float(self): |
||
194 | if self.layout != PLAIN: |
||
195 | return False |
||
196 | for channel in self.le_channels: |
||
197 | if channel.type not in (VOID, FLOAT): |
||
198 | return False |
||
199 | return True |
||
200 | |||
201 | def is_bitmask(self): |
||
202 | if self.layout != PLAIN: |
||
203 | return False |
||
204 | if self.block_size() not in (8, 16, 32): |
||
205 | return False |
||
206 | for channel in self.le_channels: |
||
207 | if channel.type not in (VOID, UNSIGNED, SIGNED): |
||
208 | return False |
||
209 | return True |
||
210 | |||
211 | def is_pure_color(self): |
||
212 | if self.layout != PLAIN or self.colorspace == ZS: |
||
213 | return False |
||
214 | pures = [channel.pure |
||
215 | for channel in self.le_channels |
||
216 | if channel.type != VOID] |
||
217 | for x in pures: |
||
218 | assert x == pures[0] |
||
219 | return pures[0] |
||
220 | |||
221 | def channel_type(self): |
||
222 | types = [channel.type |
||
223 | for channel in self.le_channels |
||
224 | if channel.type != VOID] |
||
225 | for x in types: |
||
226 | assert x == types[0] |
||
227 | return types[0] |
||
228 | |||
229 | def is_pure_signed(self): |
||
230 | return self.is_pure_color() and self.channel_type() == SIGNED |
||
231 | |||
232 | def is_pure_unsigned(self): |
||
233 | return self.is_pure_color() and self.channel_type() == UNSIGNED |
||
234 | |||
235 | def has_channel(self, id): |
||
236 | return self.le_swizzles[id] != SWIZZLE_NONE |
||
237 | |||
238 | def has_depth(self): |
||
239 | return self.colorspace == ZS and self.has_channel(0) |
||
240 | |||
241 | def has_stencil(self): |
||
242 | return self.colorspace == ZS and self.has_channel(1) |
||
243 | |||
244 | def stride(self): |
||
245 | return self.block_size()/8 |
||
246 | |||
247 | |||
248 | _type_parse_map = { |
||
249 | '': VOID, |
||
250 | 'x': VOID, |
||
251 | 'u': UNSIGNED, |
||
252 | 's': SIGNED, |
||
253 | 'h': FIXED, |
||
254 | 'f': FLOAT, |
||
255 | } |
||
256 | |||
257 | _swizzle_parse_map = { |
||
258 | 'x': SWIZZLE_X, |
||
259 | 'y': SWIZZLE_Y, |
||
260 | 'z': SWIZZLE_Z, |
||
261 | 'w': SWIZZLE_W, |
||
262 | '0': SWIZZLE_0, |
||
263 | '1': SWIZZLE_1, |
||
264 | '_': SWIZZLE_NONE, |
||
265 | } |
||
266 | |||
267 | def _parse_channels(fields, layout, colorspace, swizzles): |
||
268 | if layout == PLAIN: |
||
269 | names = ['']*4 |
||
270 | if colorspace in (RGB, SRGB): |
||
271 | for i in range(4): |
||
272 | swizzle = swizzles[i] |
||
273 | if swizzle < 4: |
||
274 | names[swizzle] += 'rgba'[i] |
||
275 | elif colorspace == ZS: |
||
276 | for i in range(4): |
||
277 | swizzle = swizzles[i] |
||
278 | if swizzle < 4: |
||
279 | names[swizzle] += 'zs'[i] |
||
280 | else: |
||
281 | assert False |
||
282 | for i in range(4): |
||
283 | if names[i] == '': |
||
284 | names[i] = 'x' |
||
285 | else: |
||
286 | names = ['x', 'y', 'z', 'w'] |
||
287 | |||
288 | channels = [] |
||
289 | for i in range(0, 4): |
||
290 | field = fields[i] |
||
291 | if field: |
||
292 | type = _type_parse_map[field[0]] |
||
293 | if field[1] == 'n': |
||
294 | norm = True |
||
295 | pure = False |
||
296 | size = int(field[2:]) |
||
297 | elif field[1] == 'p': |
||
298 | pure = True |
||
299 | norm = False |
||
300 | size = int(field[2:]) |
||
301 | else: |
||
302 | norm = False |
||
303 | pure = False |
||
304 | size = int(field[1:]) |
||
305 | else: |
||
306 | type = VOID |
||
307 | norm = False |
||
308 | pure = False |
||
309 | size = 0 |
||
310 | channel = Channel(type, norm, pure, size, names[i]) |
||
311 | channels.append(channel) |
||
312 | |||
313 | return channels |
||
314 | |||
315 | def parse(filename): |
||
316 | '''Parse the format descrition in CSV format in terms of the |
||
317 | Channel and Format classes above.''' |
||
318 | |||
319 | stream = open(filename) |
||
320 | formats = [] |
||
321 | for line in stream: |
||
322 | try: |
||
323 | comment = line.index('#') |
||
324 | except ValueError: |
||
325 | pass |
||
326 | else: |
||
327 | line = line[:comment] |
||
328 | line = line.strip() |
||
329 | if not line: |
||
330 | continue |
||
331 | |||
332 | fields = [field.strip() for field in line.split(',')] |
||
333 | if len (fields) == 10: |
||
334 | fields += fields[4:9] |
||
335 | assert len (fields) == 15 |
||
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 | le_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[8]] |
||
343 | le_channels = _parse_channels(fields[4:8], layout, colorspace, le_swizzles) |
||
344 | |||
345 | be_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[14]] |
||
346 | be_channels = _parse_channels(fields[10:14], layout, colorspace, be_swizzles) |
||
347 | |||
348 | le_shift = 0 |
||
349 | for channel in le_channels: |
||
350 | channel.shift = le_shift |
||
351 | le_shift += channel.size |
||
352 | |||
353 | be_shift = 0 |
||
354 | for channel in be_channels[3::-1]: |
||
355 | channel.shift = be_shift |
||
356 | be_shift += channel.size |
||
357 | |||
358 | assert le_shift == be_shift |
||
359 | for i in range(4): |
||
360 | assert (le_swizzles[i] != SWIZZLE_NONE) == (be_swizzles[i] != SWIZZLE_NONE) |
||
361 | |||
362 | format = Format(name, layout, block_width, block_height, le_channels, le_swizzles, be_channels, be_swizzles, colorspace) |
||
363 | formats.append(format) |
||
364 | return formats>>><>><>><>><>><> |
||
365 |