From 56b994620850decd8a24cecaaeee86b874958051 Mon Sep 17 00:00:00 2001 From: Campbell Barton <ideasman42@gmail.com> Date: Wed, 27 Jun 2018 17:17:33 +0200 Subject: [PATCH] blender_theme_as_c: Util to update theme source file Currently bone wire colors hard coded, TODO. --- utils/blender_theme_as_c.py | 307 ++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100755 utils/blender_theme_as_c.py diff --git a/utils/blender_theme_as_c.py b/utils/blender_theme_as_c.py new file mode 100755 index 0000000..65718af --- /dev/null +++ b/utils/blender_theme_as_c.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python3 + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** + +# <pep8 compliant> + +""" +Generates 'userdef_default_theme.c' from a 'userpref.blend' file. + +Pass your user preferenes blend file to this script to update the C source file. + +eg: + + ./source/tools/utils/blender_theme_as_c.py ~/.config/blender/2.80/config/userpref.blend + +.. or find the latest: + + ./source/tools/utils/blender_theme_as_c.py $(find ~/.config/blender -name "userpref.blend" | sort | tail -1) +""" + +C_SOURCE_HEADER = r'''/* + * Generated by 'source/tools/utils/blender_theme_as_c.py' + * + * Do not hand edit this file! + */ + +#include "DNA_userdef_types.h" + +#include "BLO_readfile.h" + +#ifdef __LITTLE_ENDIAN__ +# define RGBA(c) {((c) >> 24) & 0xff, ((c) >> 16) & 0xff, ((c) >> 8) & 0xff, (c) & 0xff} +# define RGB(c) {((c) >> 16) & 0xff, ((c) >> 8) & 0xff, (c) & 0xff} +#else +# define RGBA(c) {(c) & 0xff, ((c) >> 8) & 0xff, ((c) >> 16) & 0xff, ((c) >> 24) & 0xff} +# define RGB(c) {(c) & 0xff, ((c) >> 8) & 0xff, ((c) >> 16) & 0xff} +#endif + +''' + +# TODO, support arrays properly, +# these variables hardly change so hard code for now. +TARM_WORKAROUND = '''\t\t{ +\t\t\t.solid = RGBA(0x9a0000ff), +\t\t\t.select = RGBA(0xbd1111ff), +\t\t\t.active = RGBA(0xf70a0aff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0xf74018ff), +\t\t\t.select = RGBA(0xf66913ff), +\t\t\t.active = RGBA(0xfa9900ff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x1e9109ff), +\t\t\t.select = RGBA(0x59b70bff), +\t\t\t.active = RGBA(0x83ef1dff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x0a3694ff), +\t\t\t.select = RGBA(0x3667dfff), +\t\t\t.active = RGBA(0x5ec1efff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0xa9294eff), +\t\t\t.select = RGBA(0xc1416aff), +\t\t\t.active = RGBA(0xf05d91ff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x430c78ff), +\t\t\t.select = RGBA(0x543aa3ff), +\t\t\t.active = RGBA(0x8764d5ff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x24785aff), +\t\t\t.select = RGBA(0x3c9579ff), +\t\t\t.active = RGBA(0x6fb6abff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x4b707cff), +\t\t\t.select = RGBA(0x6a8691ff), +\t\t\t.active = RGBA(0x9bc2cdff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0xf4c90cff), +\t\t\t.select = RGBA(0xeec236ff), +\t\t\t.active = RGBA(0xf3ff00ff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x1e2024ff), +\t\t\t.select = RGBA(0x484c56ff), +\t\t\t.active = RGBA(0xffffffff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x6f2f6aff), +\t\t\t.select = RGBA(0x9845beff), +\t\t\t.active = RGBA(0xd330d6ff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x6c8e22ff), +\t\t\t.select = RGBA(0x7fb022ff), +\t\t\t.active = RGBA(0xbbef5bff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x8d8d8dff), +\t\t\t.select = RGBA(0xb0b0b0ff), +\t\t\t.active = RGBA(0xdededeff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x834326ff), +\t\t\t.select = RGBA(0x8b5811ff), +\t\t\t.active = RGBA(0xbd6a11ff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x08310eff), +\t\t\t.select = RGBA(0x1c430bff), +\t\t\t.active = RGBA(0x34622bff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x000000ff), +\t\t\t.select = RGBA(0x000000ff), +\t\t\t.active = RGBA(0x000000ff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x000000ff), +\t\t\t.select = RGBA(0x000000ff), +\t\t\t.active = RGBA(0x000000ff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x000000ff), +\t\t\t.select = RGBA(0x000000ff), +\t\t\t.active = RGBA(0x000000ff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x000000ff), +\t\t\t.select = RGBA(0x000000ff), +\t\t\t.active = RGBA(0x000000ff), +\t\t}, +\t\t{ +\t\t\t.solid = RGBA(0x000000ff), +\t\t\t.select = RGBA(0x000000ff), +\t\t\t.active = RGBA(0x000000ff), +\t\t}, +''' + + +def round_float_32(f): + from struct import pack, unpack + return unpack("f", pack("f", f))[0] + + +def repr_f32(f): + f_round = round_float_32(f) + f_str = repr(f) + f_str_frac = f_str.partition(".")[2] + if not f_str_frac: + return f_str + for i in range(1, len(f_str_frac)): + f_test = round(f, i) + f_test_round = round_float_32(f_test) + if f_test_round == f_round: + return "%.*f" % (i, f_test) + return f_str + + +# Avoid maintaining multiple blendfile modules +import os +import sys +sys.path.append(os.path.join( + os.path.dirname(__file__), + "..", "..", "..", + "release", "scripts", "addons", "io_blend_utils", "blend", +)) + +source_dst = os.path.join( + os.path.dirname(__file__), + "..", "..", "..", + "release", "datafiles", "userdef", "userdef_default_theme.c" +) +del sys + + +def theme_data(userpref_filename): + import blendfile + blend = blendfile.open_blend(userpref_filename) + u = next((c for c in blend.blocks if c.code == b'USER'), None) + # theme_type = b.sdna_index_from_id[b'bTheme'] + t = u.get_pointer((b'themes', b'first')) + t.refine_type(b'bTheme') + return blend, t + + +def is_ignore_dna_name(name): + if name.startswith(b'_') or name == b'pad': + return True + elif name.startswith(b'pad') and name[3:].isdigit(): + return True + else: + return False + + +def write_member(fw, indent, b, theme, ls): + path_old = () + + for key, value in ls: + key = key if type(key) is tuple else (key,) + path_new = key[:-1] + + if tuple(path_new) != tuple(path_old): + if path_old: + p = len(path_old) - 1 + while p >= 0 and (p >= len(path_new) or path_new[p] != path_old[p]): + indent = p + 1 + fw('\t' * indent) + fw('},\n') + p -= 1 + del p + + p = 0 + for p in range(min(len(path_old), len(path_new))): + if path_old[p] != key[p]: + break + else: + p = p + 1 + + for i, c in enumerate(path_new[p:]): + indent = p + i + 1 + fw('\t' * indent) + attr = c.decode('ascii') + fw(f'.{attr} = {{\n') + + # Evil, tarm array workaround. + if key[0] == b'tarm': + if path_old[0] != b'tarm': + fw(TARM_WORKAROUND) + path_old = path_new + continue + + if not is_ignore_dna_name(key[-1]): + indent = '\t' * (len(path_new) + 1) + attr = key[-1].decode('ascii') + if isinstance(value, float): + if value != 0.0: + value_repr = repr_f32(value) + fw(f'{indent}.{attr} = {value_repr}f,\n') + elif isinstance(value, int): + if value != 0: + fw(f'{indent}.{attr} = {value},\n') + elif isinstance(value, bytes): + if set(value) != {0}: + if len(value) == 3: + value_repr = "".join(f'{ub:02x}' for ub in value) + fw(f'{indent}.{attr} = RGB(0x{value_repr}),\n') + elif len(value) == 4: + value_repr = "".join(f'{ub:02x}' for ub in value) + fw(f'{indent}.{attr} = RGBA(0x{value_repr}),\n') + else: + value = value.rstrip(b'\x00') + is_ascii = True + for ub in value: + if not (ub >= 32 and ub < 127): + is_ascii = False + break + if is_ascii: + value_repr = value.decode('ascii') + fw(f'{indent}.{attr} = "{value_repr}",\n') + else: + fw(f'{indent}.{attr} = {{{value_repr}}},\n') + else: + fw(f'{indent}.{attr} = {value},\n') + path_old = path_new + + +def convert_data(blend, theme, f): + fw = f.write + fw(C_SOURCE_HEADER) + fw('const bTheme U_theme_default = {\n') + ls = list(theme.items_recursive_iter(use_nil=False)) + write_member(fw, 1, blend, theme, ls) + + fw('};\n') + + +def main(): + import sys + blend, theme = theme_data(sys.argv[-1]) + with open(source_dst, 'w', encoding='utf-8') as fh: + convert_data(blend, theme, fh) + + +if __name__ == "__main__": + main() -- GitLab