From a1bae2663e95a8ebe851453b254c980402d2dad0 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 2 Dec 2019 10:26:17 +0100 Subject: Palettes: Import ASE and Krita/Gimp palettes Differential revision: https://developer.blender.org/D6247 --- io_import_palette/__init__.py | 131 +++++++++++++++++++++++++++++++ io_import_palette/import_ase.py | 161 ++++++++++++++++++++++++++++++++++++++ io_import_palette/import_krita.py | 67 ++++++++++++++++ 3 files changed, 359 insertions(+) create mode 100644 io_import_palette/__init__.py create mode 100644 io_import_palette/import_ase.py create mode 100644 io_import_palette/import_krita.py (limited to 'io_import_palette') diff --git a/io_import_palette/__init__.py b/io_import_palette/__init__.py new file mode 100644 index 00000000..431179ca --- /dev/null +++ b/io_import_palette/__init__.py @@ -0,0 +1,131 @@ +# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +bl_info = { + "name": "Import Palettes", + "author": "Antonio Vazquez", + "version": (1, 0, 0), + "blender": (2, 81, 6), + "location": "File > Import", + "description": "Import Palettes", + "warning": "", + "category": "Import-Export"} + +import sys +import os + +# ---------------------------------------------- +# Add to Phyton path (once only) +# ---------------------------------------------- +path = sys.path +flag = False +for item in path: + if "io_import_palette" in item: + flag = True +if flag is False: + sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'io_import_palette')) + +# ---------------------------------------------- +# Import modules +# ---------------------------------------------- +if "bpy" in locals(): + import imp + + imp.reload(import_ase) + imp.reload(import_krita) +else: + import import_ase + import import_krita + +import bpy +from bpy.props import ( + StringProperty, +) +from bpy_extras.io_utils import ( + ImportHelper, + path_reference_mode, +) + + +class ImportASE(bpy.types.Operator, ImportHelper): + """Load a Palette File""" + bl_idname = "import_ase.read" + bl_label = "Import ASE" + bl_options = {'PRESET', 'UNDO'} + + filename_ext = ".ase" + filter_glob: StringProperty( + default="*.ase", + options={'HIDDEN'}, + ) + + def execute(self, context): + return import_ase.load(context, self.properties.filepath) + + def draw(self, context): + pass + + +class importKPL(bpy.types.Operator, ImportHelper): + """Load a File""" + bl_idname = "import_krita.read" + bl_label = "Import Palette" + bl_options = {'PRESET', 'UNDO'} + + filename_ext = ".kpl" + filter_glob: StringProperty( + default="*.kpl;*gpl", + options={'HIDDEN'}, + ) + + def execute(self, context): + return import_krita.load(context, self.properties.filepath) + + def draw(self, context): + pass + + +def menu_func_import(self, context): + self.layout.operator(importKPL.bl_idname, text="KPL Palette (.kpl)") + self.layout.operator(ImportASE.bl_idname, text="ASE Palette (.ase)") + + +classes = ( + ImportASE, + importKPL, +) + + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + bpy.types.TOPBAR_MT_file_import.append(menu_func_import) + + +def unregister(): + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) + + for cls in classes: + bpy.utils.unregister_class(cls) + + +if __name__ == "__main__": + register() diff --git a/io_import_palette/import_ase.py b/io_import_palette/import_ase.py new file mode 100644 index 00000000..cd5cbee3 --- /dev/null +++ b/io_import_palette/import_ase.py @@ -0,0 +1,161 @@ +# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# This ASE converion use code from Marcos A Ojeda http://generic.cx/ +# +# With notes from +# http://iamacamera.org/default.aspx?id=109 by Carl Camera and +# http://www.colourlovers.com/ase.phps by Chris Williams + +# + + +""" +This script imports a ASE Palette to Blender. + +Usage: +Run this script from "File->Import" menu and then load the desired ASE file. +""" + +import bpy +import os +import struct + + +def parse_chunk(fd): + chunk_type = fd.read(2) + while chunk_type: + if chunk_type == b'\x00\x01': + # a single color + o = dict_for_chunk(fd) + yield o + + elif chunk_type == b'\xC0\x01': + # folder/palette + o = dict_for_chunk(fd) + o['swatches'] = [x for x in colors(fd)] + yield o + + elif chunk_type == b'\xC0\x02': + # this signals the end of a folder + assert fd.read(4) == b'\x00\x00\x00\x00' + pass + + else: + # the file is malformed? + assert chunk_type in [ + b'\xC0\x01', b'\x00\x01', b'\xC0\x02', b'\x00\x02'] + pass + + chunk_type = fd.read(2) + + +def colors(fd): + chunk_type = fd.read(2) + while chunk_type in [b'\x00\x01', b'\x00\x02']: + d = dict_for_chunk(fd) + yield d + chunk_type = fd.read(2) + fd.seek(-2, os.SEEK_CUR) + + +def dict_for_chunk(fd): + chunk_length = struct.unpack(">I", fd.read(4))[0] + data = fd.read(chunk_length) + + title_length = (struct.unpack(">H", data[:2])[0]) * 2 + title = data[2:2 + title_length].decode("utf-16be").strip('\0') + color_data = data[2 + title_length:] + + output = { + 'name': str(title), + 'type': 'Color Group' # default to color group + } + + if color_data: + fmt = {b'RGB': '!fff', b'Gray': '!f', b'CMYK': '!ffff', b'LAB': '!fff'} + color_mode = struct.unpack("!4s", color_data[:4])[0].strip() + color_values = list(struct.unpack(fmt[color_mode], color_data[4:-2])) + + color_types = ['Global', 'Spot', 'Process'] + swatch_type_index = struct.unpack(">h", color_data[-2:])[0] + swatch_type = color_types[swatch_type_index] + + output.update({ + 'data': { + 'mode': color_mode.decode('utf-8'), + 'values': color_values + }, + 'type': str(swatch_type) + }) + + return output + + +def parse(filename): + with open(filename, "rb") as data: + header, v_major, v_minor, chunk_count = struct.unpack("!4sHHI", data.read(12)) + + assert header == b"ASEF" + assert (v_major, v_minor) == (1, 0) + + return [c for c in parse_chunk(data)] + + +def load(context, filepath): + output = parse(filepath) + + (path, filename) = os.path.split(filepath) + + pal = None + + for elm in output: + valid = False + data = elm['data'] + color = [0, 0, 0] + val = data['values'] + + if data['mode'] == 'RGB': + valid = True + color[0] = val[0] + color[1] = val[1] + color[2] = val[2] + elif data['mode'] == 'Gray': + valid = True + color[0] = val[0] + color[1] = val[0] + color[2] = val[0] + elif data['mode'] == 'CMYK': + valid = True + color[0] = (1.0 - val[0]) * (1.0 - val[3]) + color[1] = (1.0 - val[1]) * (1.0 - val[3]) + color[2] = (1.0 - val[2]) * (1.0 - val[3]) + + # Create palette color + if valid: + # Create Palette + if pal is None: + pal = bpy.data.palettes.new(name=filename) + + # Create Color + col = pal.colors.new() + col.color[0] = color[0] + col.color[1] = color[1] + col.color[2] = color[2] + + return {'FINISHED'} diff --git a/io_import_palette/import_krita.py b/io_import_palette/import_krita.py new file mode 100644 index 00000000..779bc8f3 --- /dev/null +++ b/io_import_palette/import_krita.py @@ -0,0 +1,67 @@ +# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + + +""" +This script imports a Krita/Gimp Palette to Blender. + +Usage: +Run this script from "File->Import" menu and then load the desired KPL file. +""" + +import bpy +import os +import struct + + +def load(context, filepath): + (path, filename) = os.path.split(filepath) + + pal = None + valid = False + finput = open(filepath) + line = finput.readline() + + while line: + if valid: + # Create Palette + if pal is None: + pal = bpy.data.palettes.new(name=filename) + + # Create Color + values = line.split() + col = [0, 0, 0] + col[0] = int(values[0]) / 255.0 + col[1] = int(values[1]) / 255.0 + col[2] = int(values[2]) / 255.0 + + palcol = pal.colors.new() + palcol.color[0] = col[0] + palcol.color[1] = col[1] + palcol.color[2] = col[2] + + if line[0] == '#': + valid = True + + line = finput.readline() + + finput.close() + + return {'FINISHED'} -- cgit v1.2.3