From a580a4e13774774fc5f775b36a432d8f77aba2ef Mon Sep 17 00:00:00 2001 From: akaneyu Date: Mon, 20 May 2024 23:53:16 +0900 Subject: [PATCH] Implement Canvas Size feature --- addon/__init__.py | 4 +- addon/operators.py | 73 ++++++++++++++++++++++++++++++++++- addon/ui.py | 94 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 168 insertions(+), 3 deletions(-) diff --git a/addon/__init__.py b/addon/__init__.py index 4c39775..174a58f 100644 --- a/addon/__init__.py +++ b/addon/__init__.py @@ -18,7 +18,7 @@ bl_info = { "name": "Image Editor Plus", "author": "akaneyu", - "version": (1, 8, 0), + "version": (1, 8, 1), "blender": (3, 3, 0), "location": "Image", "warning": "", @@ -64,6 +64,7 @@ classes = [ operators.IMAGE_EDITOR_PLUS_OT_flip, operators.IMAGE_EDITOR_PLUS_OT_rotate, operators.IMAGE_EDITOR_PLUS_OT_scale, + operators.IMAGE_EDITOR_PLUS_OT_change_canvas_size, operators.IMAGE_EDITOR_PLUS_OT_flip_layer, operators.IMAGE_EDITOR_PLUS_OT_rotate_layer, operators.IMAGE_EDITOR_PLUS_OT_rotate_layer_arbitrary, @@ -80,6 +81,7 @@ classes = [ operators.IMAGE_EDITOR_PLUS_OT_pixelize, operators.IMAGE_EDITOR_PLUS_OT_make_seamless, ui.IMAGE_EDITOR_PLUS_OT_scale_dialog, + ui.IMAGE_EDITOR_PLUS_OT_change_canvas_size_dialog, ui.IMAGE_EDITOR_PLUS_OffsetPropertyGroup, ui.IMAGE_EDITOR_PLUS_OT_offset_dialog, ui.IMAGE_EDITOR_PLUS_OT_adjust_color_dialog, diff --git a/addon/operators.py b/addon/operators.py index 10380c8..bee9e2c 100644 --- a/addon/operators.py +++ b/addon/operators.py @@ -1,5 +1,5 @@ ''' - Copyright (C) 2021 - 2023 Akaneyu + Copyright (C) 2021 - 2024 Akaneyu 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 @@ -764,6 +764,77 @@ class IMAGE_EDITOR_PLUS_OT_scale(bpy.types.Operator): return {'FINISHED'} +class IMAGE_EDITOR_PLUS_OT_change_canvas_size(bpy.types.Operator): + """Apply settings""" + bl_idname = "image_editor_plus.change_canvas_size" + bl_label = "Change Canvas Size" + width: bpy.props.IntProperty() + height: bpy.props.IntProperty() + expand_from_center: bpy.props.BoolProperty() + use_background_color: bpy.props.BoolProperty() + + def execute(self, context): + wm = context.window_manager + + props = wm.imageeditorplus_properties + if self.use_background_color: + color = props.background_color[:] + (1.0,) + else: + color = (0, 0, 0, 0) + + img = context.area.spaces.active.image + if not img: + return {'CANCELLED'} + + width, height = img.size + pixels = utils.read_pixels_from_image(img) + + if self.width > width or self.height > height: + expand_width = self.width - width if self.width > width else 0 + expand_height = self.height - height if self.height > height else 0 + + if self.expand_from_center: + expand_left = int(expand_width / 2) + expand_top = int(expand_height / 2) + expand_right = expand_width - expand_left + expand_bottom = expand_height - expand_top + else: + expand_left, expand_top = 0, 0 + expand_right = expand_width + expand_bottom = expand_height + + new_pixels = np.pad(pixels, ((expand_bottom, expand_top), + (expand_left, expand_right), (0, 0)), + constant_values=np.array(((color, color), (color, color), (0, 0)), + dtype=object)) + else: + new_pixels = pixels + + if self.width < width or self.height < height: + shrink_width = width - self.width if self.width < width else 0 + shrink_height = height - self.height if self.height < height else 0 + + if self.expand_from_center: + shrink_left = int(shrink_width / 2) + shrink_top = int(shrink_height / 2) + else: + shrink_left, shrink_top = 0, 0 + + if self.height < height: + new_pixels = new_pixels[height - self.height - shrink_top:height - shrink_top, + shrink_left:self.width + shrink_left] + else: + new_pixels = new_pixels[0:self.height, + shrink_left:self.width + shrink_left] + + img.scale(self.width, self.height) + + utils.write_pixels_to_image(img, new_pixels) + + app.refresh_image(context) + + return {'FINISHED'} + class IMAGE_EDITOR_PLUS_OT_flip_layer(bpy.types.Operator): """Flip the layer""" bl_idname = "image_editor_plus.flip_layer" diff --git a/addon/ui.py b/addon/ui.py index bcbdcc0..daaa4f9 100644 --- a/addon/ui.py +++ b/addon/ui.py @@ -1,5 +1,5 @@ ''' - Copyright (C) 2021 - 2023 Akaneyu + Copyright (C) 2021 - 2024 Akaneyu 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 @@ -199,6 +199,90 @@ class IMAGE_EDITOR_PLUS_OT_scale_dialog(bpy.types.Operator): row.column() row.prop(self, 'reset', text='Reset', toggle=True) +def reset_canvas_size_properties(self, context): + if self.reset: + self.width = self.original_width + self.height = self.original_height + self.expand_from_center = False + self.use_background_color = False + + self.reset = False + +class IMAGE_EDITOR_PLUS_OT_change_canvas_size_dialog(bpy.types.Operator): + """Change the canvas size""" + bl_idname = 'image_editor_plus.change_canvas_size_dialog' + bl_label = "Canvas Size" + reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_canvas_size_properties) + width: bpy.props.IntProperty(name='Width', + min=1, options={'SKIP_SAVE'}) + height: bpy.props.IntProperty(name='Height', + min=1, options={'SKIP_SAVE'}) + expand_from_center: bpy.props.BoolProperty(name='Expand from Center', default=False) + use_background_color: bpy.props.BoolProperty(name='Use Background Color', default=False) + original_width: bpy.props.IntProperty() + original_height: bpy.props.IntProperty() + + def invoke(self, context, event): + wm = context.window_manager + + img = context.area.spaces.active.image + if not img: + return {'CANCELLED'} + + width, height = img.size + + self.original_width = width + self.original_height = height + self.width = width + self.height = height + + return wm.invoke_props_dialog(self) + + def execute(self, context): + width = self.width + height = self.height + + if width < 1: + width = 1 + if height < 1: + height = 1 + + bpy.ops.image_editor_plus.change_canvas_size('EXEC_DEFAULT', False, + width=width, height=height, expand_from_center=self.expand_from_center, + use_background_color=self.use_background_color) + + return {'FINISHED'} + + def draw(self, context): + layout = self.layout + + row = layout.row() + + row = layout.split(align=True) + row.alignment = 'RIGHT' + row.label(text='Width') + row.prop(self, 'width', text='') + + row = layout.split(align=True) + row.alignment = 'RIGHT' + row.label(text='Height') + row.prop(self, 'height', text='') + + row = layout.split(align=True) + row.column() + row.prop(self, 'expand_from_center') + + row = layout.split(align=True) + row.column() + row.prop(self, 'use_background_color') + + row = layout.split(align=True) + row.column() + + row = layout.split(factor=0.7) + row.column() + row.prop(self, 'reset', text='Reset', toggle=True) + def preview_offset_properties(self, context): if self.preview: update_offset_properties(self, context) @@ -1261,6 +1345,9 @@ class IMAGE_EDITOR_PLUS_MT_transform_menu(bpy.types.Menu): op = layout.operator(IMAGE_EDITOR_PLUS_OT_scale_dialog.bl_idname, text='Scale...', icon_value=get_icon_id('scale')) + op = layout.operator(IMAGE_EDITOR_PLUS_OT_change_canvas_size_dialog.bl_idname, text='Canvas Size...', + icon_value=get_icon_id('scale')) + class IMAGE_EDITOR_PLUS_MT_transform_layer_menu(bpy.types.Menu): bl_idname = "IMAGE_EDITOR_PLUS_MT_transform_layer_menu" bl_label = "Transform Layer" @@ -1536,6 +1623,11 @@ class IMAGE_EDITOR_PLUS_PT_transform_panel(bpy.types.Panel): op = row.operator(IMAGE_EDITOR_PLUS_OT_scale_dialog.bl_idname, text='Scale...', icon_value=get_icon_id('scale')) + row = layout.row() + op = row.operator(IMAGE_EDITOR_PLUS_OT_change_canvas_size_dialog.bl_idname, + text='Canvas Size...', + icon_value=get_icon_id('scale')) + class IMAGE_EDITOR_PLUS_PT_transform_layer_panel(bpy.types.Panel): bl_label = "Transform Layer" bl_space_type = "IMAGE_EDITOR"