| # encoding=utf-8 |
| # Copyright © 2016 Intel Corporation |
| |
| # Permission is hereby granted, free of charge, to any person obtaining a copy |
| # of this software and associated documentation files (the "Software"), to deal |
| # in the Software without restriction, including without limitation the rights |
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| # copies of the Software, and to permit persons to whom the Software is |
| # furnished to do so, subject to the following conditions: |
| |
| # The above copyright notice and this permission notice shall be included in |
| # all copies or substantial portions of the Software. |
| |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| # SOFTWARE. |
| |
| """Generates isl_format_layout.c.""" |
| |
| from __future__ import absolute_import, division, print_function |
| import argparse |
| import csv |
| import re |
| |
| from mako import template |
| |
| # Load the template, ensure that __future__.division is imported, and set the |
| # bytes encoding to be utf-8. This last bit is important to getting simple |
| # consistent behavior for python 3 when we get there. |
| TEMPLATE = template.Template(future_imports=['division'], |
| output_encoding='utf-8', |
| text="""\ |
| /* This file is autogenerated by gen_format_layout.py. DO NOT EDIT! */ |
| |
| /* |
| * Copyright 2015 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| #include "isl/isl.h" |
| |
| const struct isl_format_layout |
| isl_format_layouts[] = { |
| % for format in formats: |
| [ISL_FORMAT_${format.name}] = { |
| .format = ISL_FORMAT_${format.name}, |
| .name = "ISL_FORMAT_${format.name}", |
| .bpb = ${format.bpb}, |
| .bw = ${format.bw}, |
| .bh = ${format.bh}, |
| .bd = ${format.bd}, |
| .channels = { |
| % for mask in ['r', 'g', 'b', 'a', 'l', 'i', 'p']: |
| <% channel = getattr(format, mask, None) %>\\ |
| % if channel.type is not None: |
| .${mask} = { ISL_${channel.type}, ${channel.start}, ${channel.size} }, |
| % else: |
| .${mask} = {}, |
| % endif |
| % endfor |
| }, |
| .uniform_channel_type = ISL_${format.uniform_channel_type}, |
| .colorspace = ISL_COLORSPACE_${format.colorspace}, |
| .txc = ISL_TXC_${format.txc}, |
| }, |
| |
| % endfor |
| }; |
| |
| bool |
| isl_format_is_valid(enum isl_format format) |
| { |
| if (format >= sizeof(isl_format_layouts) / sizeof(isl_format_layouts[0])) |
| return false; |
| return isl_format_layouts[format].name; |
| } |
| |
| enum isl_format |
| isl_format_srgb_to_linear(enum isl_format format) |
| { |
| switch (format) { |
| % for srgb, rgb in srgb_to_linear_map: |
| case ISL_FORMAT_${srgb}: |
| return ISL_FORMAT_${rgb}; |
| %endfor |
| default: |
| return format; |
| } |
| } |
| """) |
| |
| |
| class Channel(object): |
| """Class representing a Channel. |
| |
| Converts the csv encoded data into the format that the template (and thus |
| the consuming C code) expects. |
| |
| """ |
| # If the csv file grew very large this class could be put behind a factory |
| # to increase efficiency. Right now though it's fast enough that It didn't |
| # seem worthwhile to add all of the boilerplate |
| _types = { |
| 'x': 'void', |
| 'r': 'raw', |
| 'un': 'unorm', |
| 'sn': 'snorm', |
| 'uf': 'ufloat', |
| 'sf': 'sfloat', |
| 'ux': 'ufixed', |
| 'sx': 'sfixed', |
| 'ui': 'uint', |
| 'si': 'sint', |
| 'us': 'uscaled', |
| 'ss': 'sscaled', |
| } |
| _splitter = re.compile(r'\s*(?P<type>[a-z]+)(?P<size>[0-9]+)') |
| |
| def __init__(self, line): |
| # If the line is just whitespace then just set everything to None to |
| # save on the regex cost and let the template skip on None. |
| if line.isspace(): |
| self.size = None |
| self.type = None |
| else: |
| grouped = self._splitter.match(line) |
| self.type = self._types[grouped.group('type')].upper() |
| self.size = int(grouped.group('size')) |
| |
| # Default the start bit to -1 |
| self.start = -1 |
| |
| |
| class Format(object): |
| """Class taht contains all values needed by the template.""" |
| def __init__(self, line): |
| # pylint: disable=invalid-name |
| self.name = line[0].strip() |
| |
| # Future division makes this work in python 2. |
| self.bpb = int(line[1]) |
| self.bw = line[2].strip() |
| self.bh = line[3].strip() |
| self.bd = line[4].strip() |
| self.r = Channel(line[5]) |
| self.g = Channel(line[6]) |
| self.b = Channel(line[7]) |
| self.a = Channel(line[8]) |
| self.l = Channel(line[9]) |
| self.i = Channel(line[10]) |
| self.p = Channel(line[11]) |
| |
| # Set the start bit value for each channel |
| self.order = line[12].strip() |
| bit = 0 |
| for c in self.order: |
| chan = getattr(self, c) |
| chan.start = bit |
| bit = bit + chan.size |
| |
| # Set the uniform channel type, if the format has one. |
| # |
| # Iterate over all channels, not just those in self.order, because |
| # some formats have an empty 'order' field in the CSV (such as |
| # YCRCB_NORMAL). |
| self.uniform_channel_type = 'VOID' |
| for chan in self.channels: |
| if chan.type in (None, 'VOID'): |
| pass |
| elif self.uniform_channel_type == 'VOID': |
| self.uniform_channel_type = chan.type |
| elif self.uniform_channel_type == chan.type: |
| pass |
| else: |
| self.uniform_channel_type = 'VOID' |
| break |
| |
| # alpha doesn't have a colorspace of it's own. |
| self.colorspace = line[13].strip().upper() |
| if self.colorspace in ['']: |
| self.colorspace = 'NONE' |
| |
| # This sets it to the line value, or if it's an empty string 'NONE' |
| self.txc = line[14].strip().upper() or 'NONE' |
| |
| |
| @property |
| def channels(self): |
| yield self.r |
| yield self.g |
| yield self.b |
| yield self.a |
| yield self.l |
| yield self.i |
| yield self.p |
| |
| |
| def reader(csvfile): |
| """Wrapper around csv.reader that skips comments and blanks.""" |
| # csv.reader actually reads the file one line at a time (it was designed to |
| # open excel generated sheets), so hold the file until all of the lines are |
| # read. |
| with open(csvfile, 'r') as f: |
| for line in csv.reader(f): |
| if line and not line[0].startswith('#'): |
| yield line |
| |
| def get_srgb_to_linear_map(formats): |
| """Compute a map from sRGB to linear formats. |
| |
| This function uses some probably somewhat fragile string munging to do |
| the conversion. However, we do assert that, if it's SRGB, the munging |
| succeeded so that gives some safety. |
| """ |
| names = {f.name for f in formats} |
| for fmt in formats: |
| if fmt.colorspace != 'SRGB': |
| continue |
| |
| replacements = [ |
| ('_SRGB', ''), |
| ('SRGB', 'RGB'), |
| ('U8SRGB', 'FLT16'), |
| ] |
| |
| found = False |
| for rep in replacements: |
| rgb_name = fmt.name.replace(rep[0], rep[1]) |
| if rgb_name in names: |
| found = True |
| yield fmt.name, rgb_name |
| break |
| |
| # We should have found a format name |
| assert found |
| |
| def main(): |
| """Main function.""" |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--csv', action='store', help='The CSV file to parse.') |
| parser.add_argument( |
| '--out', |
| action='store', |
| help='The location to put the generated C file.') |
| args = parser.parse_args() |
| |
| # This generator opens and writes the file itself, and it does so in bytes |
| # mode. This solves both python 2 vs 3 problems and solves the locale |
| # problem: Unicode can be rendered even if the shell calling this script |
| # doesn't. |
| with open(args.out, 'wb') as f: |
| formats = [Format(l) for l in reader(args.csv)] |
| try: |
| # This basically does lazy evaluation and initialization, which |
| # saves on memory and startup overhead. |
| f.write(TEMPLATE.render( |
| formats = formats, |
| srgb_to_linear_map = list(get_srgb_to_linear_map(formats)), |
| )) |
| except Exception: |
| # In the even there's an error this imports some helpers from mako |
| # to print a useful stack trace and prints it, then exits with |
| # status 1, if python is run with debug; otherwise it just raises |
| # the exception |
| if __debug__: |
| import sys |
| from mako import exceptions |
| print(exceptions.text_error_template().render(), |
| file=sys.stderr) |
| sys.exit(1) |
| raise |
| |
| |
| if __name__ == '__main__': |
| main() |