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