u_format_parse.py revision f93b6d8cc5b0e3830e0c095772795e606fd1fe3a
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
33VOID, UNSIGNED, SIGNED, FIXED, FLOAT = range(5)
34
35SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W, SWIZZLE_0, SWIZZLE_1, SWIZZLE_NONE, = range(7)
36
37PLAIN = 'plain'
38
39RGB = 'rgb'
40SRGB = 'srgb'
41YUV = 'yuv'
42ZS = 'zs'
43
44
45def is_pot(x):
46   return (x & (x - 1)) == 0
47
48
49VERY_LARGE = 99999999999999999999999
50
51
52class 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
104class 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
267def _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
315def 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
366