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, channels, swizzles, colorspace):
108        self.name = name
109        self.layout = layout
110        self.block_width = block_width
111        self.block_height = block_height
112        self.channels = channels
113        self.swizzles = swizzles
114        self.name = name
115        self.colorspace = colorspace
116
117    def __str__(self):
118        return self.name
119
120    def short_name(self):
121        '''Make up a short norm for a format, suitable to be used as suffix in
122        function names.'''
123
124        name = self.name
125        if name.startswith('PIPE_FORMAT_'):
126            name = name[len('PIPE_FORMAT_'):]
127        name = name.lower()
128        return name
129
130    def block_size(self):
131        size = 0
132        for channel in self.channels:
133            size += channel.size
134        return size
135
136    def nr_channels(self):
137        nr_channels = 0
138        for channel in self.channels:
139            if channel.size:
140                nr_channels += 1
141        return nr_channels
142
143    def is_array(self):
144        if self.layout != PLAIN:
145            return False
146        ref_channel = self.channels[0]
147        for channel in self.channels[1:]:
148            if channel.size and (channel.size != ref_channel.size or channel.size % 8):
149                return False
150        return True
151
152    def is_mixed(self):
153        if self.layout != PLAIN:
154            return False
155        ref_channel = self.channels[0]
156        if ref_channel.type == VOID:
157           ref_channel = self.channels[1]
158        for channel in self.channels[1:]:
159            if channel.type != VOID:
160                if channel.type != ref_channel.type:
161                    return True
162                if channel.norm != ref_channel.norm:
163                    return True
164                if channel.pure != ref_channel.pure:
165                    return True
166        return False
167
168    def is_pot(self):
169        return is_pot(self.block_size())
170
171    def is_int(self):
172        if self.layout != PLAIN:
173            return False
174        for channel in self.channels:
175            if channel.type not in (VOID, UNSIGNED, SIGNED):
176                return False
177        return True
178
179    def is_float(self):
180        if self.layout != PLAIN:
181            return False
182        for channel in self.channels:
183            if channel.type not in (VOID, FLOAT):
184                return False
185        return True
186
187    def is_bitmask(self):
188        if self.layout != PLAIN:
189            return False
190        if self.block_size() not in (8, 16, 32):
191            return False
192        for channel in self.channels:
193            if channel.type not in (VOID, UNSIGNED, SIGNED):
194                return False
195        return True
196
197    def inv_swizzles(self):
198        '''Return an array[4] of inverse swizzle terms'''
199        '''Only pick the first matching value to avoid l8 getting blue and i8 getting alpha'''
200        inv_swizzle = [None]*4
201        for i in range(4):
202            swizzle = self.swizzles[i]
203            if swizzle < 4 and inv_swizzle[swizzle] == None:
204                inv_swizzle[swizzle] = i
205        return inv_swizzle
206
207    def stride(self):
208        return self.block_size()/8
209
210
211_type_parse_map = {
212    '':  VOID,
213    'x': VOID,
214    'u': UNSIGNED,
215    's': SIGNED,
216    'h': FIXED,
217    'f': FLOAT,
218}
219
220_swizzle_parse_map = {
221    'x': SWIZZLE_X,
222    'y': SWIZZLE_Y,
223    'z': SWIZZLE_Z,
224    'w': SWIZZLE_W,
225    '0': SWIZZLE_0,
226    '1': SWIZZLE_1,
227    '_': SWIZZLE_NONE,
228}
229
230def parse(filename):
231    '''Parse the format descrition in CSV format in terms of the
232    Channel and Format classes above.'''
233
234    stream = open(filename)
235    formats = []
236    for line in stream:
237        try:
238            comment = line.index('#')
239        except ValueError:
240            pass
241        else:
242            line = line[:comment]
243        line = line.strip()
244        if not line:
245            continue
246
247        fields = [field.strip() for field in line.split(',')]
248
249        name = fields[0]
250        layout = fields[1]
251        block_width, block_height = map(int, fields[2:4])
252
253        swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[8]]
254        colorspace = fields[9]
255
256        if layout == PLAIN:
257            names = ['']*4
258            if colorspace in (RGB, SRGB):
259                for i in range(4):
260                    swizzle = swizzles[i]
261                    if swizzle < 4:
262                        names[swizzle] += 'rgba'[i]
263            elif colorspace == ZS:
264                for i in range(4):
265                    swizzle = swizzles[i]
266                    if swizzle < 4:
267                        names[swizzle] += 'zs'[i]
268            else:
269                assert False
270            for i in range(4):
271                if names[i] == '':
272                    names[i] = 'x'
273        else:
274            names = ['x', 'y', 'z', 'w']
275
276        channels = []
277        for i in range(0, 4):
278            field = fields[4 + i]
279            if field:
280                type = _type_parse_map[field[0]]
281                if field[1] == 'n':
282                    norm = True
283                    pure = False
284                    size = int(field[2:])
285                elif field[1] == 'p':
286                    pure = True
287                    norm = False
288                    size = int(field[2:])
289                else:
290                    norm = False
291                    pure = False
292                    size = int(field[1:])
293            else:
294                type = VOID
295                norm = False
296                pure = False
297                size = 0
298            channel = Channel(type, norm, pure, size, names[i])
299            channels.append(channel)
300
301        format = Format(name, layout, block_width, block_height, channels, swizzles, colorspace)
302        formats.append(format)
303    return formats
304
305