Subversion Repositories Kolibri OS

Rev

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