Compare commits

..

7 Commits
v1.9.0 ... main

11 changed files with 304 additions and 355 deletions

View File

@ -1,3 +1,57 @@
# Image Editor Plus # Image Editor Plus
Image Editor Plus is an add-on that lets you modify your image in seconds. Clear, fill, flip, rotation, adjusting colors, and applying some filters in just a few clicks without external editors such as PhotoShop or Gimp. ![Image Editor Plus](doc/images/featured.png)
Image Editor Plus is a Blender add-on that lets you modify your image in seconds. Clear, fill, flip, rotation, adjusting colors, and applying some filters in just a few clicks without external editors such as PhotoShop or Gimp.
This add-on extends the UV/Image Editor in Blender and provides the following operations.
- Cut/Copy/Paste
- Clear, Fill
- Crop
- Adjust hue/saturation
- Adjust brightness/contrast
- Adjust gamma
- Adjust color curve
- Replace color
- Flip, Rotate
- Canvas size
- Offset
- Apply filters
NOTE: Currently, text editing is provided as a separate add-on: [Image Editor+ Text Tool](https://superhivemarket.com/products/imedp-text-tool)
These image operations can be applied to packed/unpacked images, and reflected to the 3D model view immediately.
You can make a selection to edit the desired area on your image (currently only rectangle selection supported).
![Adjust color](doc/images/adjust_color.png)
It's easy to copy/paste a selection or an entire image. The pasted images are displayed as layers, and they can be moved, rotated or scaled.
NOTE: To display pasted layers in the 3D View, you need to use a [Image Layers Node](https://superhivemarket.com/products/image-layers-node) instead of built-in Texture Node.
![Pasted layers](doc/images/pasted_layers_2.png)
## Filters
By applying the some filters, you can add special effects to your images.
- Blur
- Sharpen
- Add noise
- Pixelize (Mosaic)
- Make seamless
- Normal map
"Make seamless" filter is useful for making your image tileable.
![Filters](doc/images/filters.png)
## Normal Map Generator
Normal Map is a texture where every pixel represents a normal vector and is used to add bumps to a surface.
This add-on uses a height map (black: low, white: high) as input and can convert it to a normal map.
![Normal map](doc/images/normal_map.png)

View File

@ -1,5 +1,5 @@
''' '''
Copyright (C) 2021 - 2024 Akaneyu Copyright (C) 2021 - 2025 Akaneyu
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -18,8 +18,8 @@
bl_info = { bl_info = {
"name": "Image Editor Plus", "name": "Image Editor Plus",
"author": "akaneyu", "author": "akaneyu",
"version": (1, 9, 0), "version": (1, 11, 0),
"blender": (3, 3, 0), "blender": (4, 2, 0),
"location": "Image", "location": "Image",
"warning": "", "warning": "",
"description": "", "description": "",

View File

@ -1,5 +1,5 @@
''' '''
Copyright (C) 2021 - 2024 Akaneyu Copyright (C) 2021 - 2025 Akaneyu
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -136,6 +136,7 @@ def draw_handler():
# release the selection if the image is changed # release the selection if the image is changed
if area_session.selection or area_session.selection_region: if area_session.selection or area_session.selection_region:
if area_session.prev_image:
if img != area_session.prev_image: if img != area_session.prev_image:
cancel_selection(context) cancel_selection(context)

View File

@ -1,5 +1,5 @@
''' '''
Copyright (C) 2021 - 2024 Akaneyu Copyright (C) 2021 - 2025 Akaneyu
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -26,7 +26,9 @@ class IMAGE_EDITOR_PLUS_OT_make_selection(bpy.types.Operator):
bl_idname = "image_editor_plus.make_selection" bl_idname = "image_editor_plus.make_selection"
bl_label = "Make Selection" bl_label = "Make Selection"
def __init__(self): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.lmb = False self.lmb = False
def modal(self, context, event): def modal(self, context, event):
@ -43,12 +45,14 @@ class IMAGE_EDITOR_PLUS_OT_make_selection(bpy.types.Operator):
if area_session.selection_region: if area_session.selection_region:
area_session.selection_region[1] = region_pos area_session.selection_region[1] = region_pos
else:
area_session.selection_region = [region_pos, region_pos]
elif event.type == 'LEFTMOUSE': elif event.type == 'LEFTMOUSE':
if event.value == 'PRESS': if event.value == 'PRESS':
self.lmb = True self.lmb = True
region_pos = [event.mouse_region_x, event.mouse_region_y]
area_session.selection_region = [region_pos, region_pos]
elif event.value == 'RELEASE': elif event.value == 'RELEASE':
self.lmb = False self.lmb = False
@ -397,7 +401,9 @@ class IMAGE_EDITOR_PLUS_OT_move_layer(bpy.types.Operator):
bl_idname = "image_editor_plus.move_layer" bl_idname = "image_editor_plus.move_layer"
bl_label = "Move Layer" bl_label = "Move Layer"
def __init__(self): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.start_input_position = [0, 0] self.start_input_position = [0, 0]
self.start_layer_location = [0, 0] self.start_layer_location = [0, 0]
@ -885,7 +891,9 @@ class IMAGE_EDITOR_PLUS_OT_rotate_layer_arbitrary(bpy.types.Operator):
bl_idname = "image_editor_plus.rotate_layer_arbitrary" bl_idname = "image_editor_plus.rotate_layer_arbitrary"
bl_label = "Rotate Layer Arbitrary" bl_label = "Rotate Layer Arbitrary"
def __init__(self): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.start_input_position = [0, 0] self.start_input_position = [0, 0]
self.start_layer_angle = 0 self.start_layer_angle = 0
@ -967,7 +975,9 @@ class IMAGE_EDITOR_PLUS_OT_scale_layer(bpy.types.Operator):
bl_idname = "image_editor_plus.scale_layer" bl_idname = "image_editor_plus.scale_layer"
bl_label = "Scale Layer" bl_label = "Scale Layer"
def __init__(self): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.start_input_position = [0, 0] self.start_input_position = [0, 0]
self.start_layer_scale_x = 1.0 self.start_layer_scale_x = 1.0
self.start_layer_scale_y = 1.0 self.start_layer_scale_y = 1.0

View File

@ -1,5 +1,5 @@
''' '''
Copyright (C) 2021 - 2024 Akaneyu Copyright (C) 2021 - 2025 Akaneyu
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -283,26 +283,19 @@ class IMAGE_EDITOR_PLUS_OT_change_canvas_size_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_offset_properties(self, context):
if self.preview:
update_offset_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_offset_properties(self, context): def reset_offset_properties(self, context):
if self.reset: if self.reset:
self.reset = False
self.offset_properties.property_unset('offset_x') self.offset_properties.property_unset('offset_x')
self.offset_properties.property_unset('offset_y') self.offset_properties.property_unset('offset_y')
self.property_unset('offset_edge_behavior') self.property_unset('offset_edge_behavior')
update_offset_properties(self, context) update_offset_properties(self, context)
self.reset = False
def update_offset_properties(self, context): def update_offset_properties(self, context):
if self.preview: if self.update_preview:
self.update_preview = False
bpy.ops.image_editor_plus.offset('EXEC_DEFAULT', False, bpy.ops.image_editor_plus.offset('EXEC_DEFAULT', False,
offset_x=self.offset_properties.offset_x, offset_x=self.offset_properties.offset_x,
offset_y=self.offset_properties.offset_y, offset_y=self.offset_properties.offset_y,
@ -317,15 +310,15 @@ class IMAGE_EDITOR_PLUS_OT_offset_dialog(bpy.types.Operator):
"""Offset the image""" """Offset the image"""
bl_idname = 'image_editor_plus.offset_dialog' bl_idname = 'image_editor_plus.offset_dialog'
bl_label = "Offset" bl_label = "Offset"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=preview_offset_properties) update=update_offset_properties,
description='Update preview')
reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_offset_properties) reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_offset_properties)
offset_properties: bpy.props.PointerProperty(options={'SKIP_SAVE'}, offset_properties: bpy.props.PointerProperty(options={'SKIP_SAVE'},
type=IMAGE_EDITOR_PLUS_OffsetPropertyGroup) type=IMAGE_EDITOR_PLUS_OffsetPropertyGroup)
offset_edge_behavior: bpy.props.EnumProperty(options={'SKIP_SAVE'}, items=( offset_edge_behavior: bpy.props.EnumProperty(options={'SKIP_SAVE'}, items=(
('wrap', 'Wrap', 'Wrap image around'), ('wrap', 'Wrap', 'Wrap image around'),
('edge', 'Edge', 'Repeat edge pixels')), ('edge', 'Edge', 'Repeat edge pixels')))
update=update_offset_properties)
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -345,13 +338,11 @@ class IMAGE_EDITOR_PLUS_OT_offset_dialog(bpy.types.Operator):
IMAGE_EDITOR_PLUS_OffsetPropertyGroup.offset_x = \ IMAGE_EDITOR_PLUS_OffsetPropertyGroup.offset_x = \
bpy.props.IntProperty(name='Offset X', subtype='FACTOR', bpy.props.IntProperty(name='Offset X', subtype='FACTOR',
min=-width + 1, max=width - 1, min=-width + 1, max=width - 1)
update=(lambda _self, context: update_offset_properties(self, context)))
IMAGE_EDITOR_PLUS_OffsetPropertyGroup.offset_y = \ IMAGE_EDITOR_PLUS_OffsetPropertyGroup.offset_y = \
bpy.props.IntProperty(name='Offset Y', subtype='FACTOR', bpy.props.IntProperty(name='Offset Y', subtype='FACTOR',
min=-width + 1, max=width - 1, min=-height + 1, max=height - 1)
update=(lambda _self, context: update_offset_properties(self, context)))
update_offset_properties(self, context) update_offset_properties(self, context)
@ -379,7 +370,7 @@ class IMAGE_EDITOR_PLUS_OT_offset_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()
@ -400,26 +391,19 @@ class IMAGE_EDITOR_PLUS_OT_offset_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_adjust_color_properties(self, context):
if self.preview:
update_adjust_color_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_adjust_color_properties(self, context): def reset_adjust_color_properties(self, context):
if self.reset: if self.reset:
self.reset = False
self.property_unset('adjust_hue') self.property_unset('adjust_hue')
self.property_unset('adjust_lightness') self.property_unset('adjust_lightness')
self.property_unset('adjust_saturation') self.property_unset('adjust_saturation')
update_adjust_color_properties(self, context) update_adjust_color_properties(self, context)
self.reset = False
def update_adjust_color_properties(self, context): def update_adjust_color_properties(self, context):
if self.preview: if self.update_preview:
self.update_preview = False
bpy.ops.image_editor_plus.adjust_color('EXEC_DEFAULT', False, bpy.ops.image_editor_plus.adjust_color('EXEC_DEFAULT', False,
adjust_hue=self.adjust_hue, adjust_hue=self.adjust_hue,
adjust_lightness=self.adjust_lightness, adjust_lightness=self.adjust_lightness,
@ -429,19 +413,17 @@ class IMAGE_EDITOR_PLUS_OT_adjust_color_dialog(bpy.types.Operator):
"""Adjust hue/saturation/lightness of the image""" """Adjust hue/saturation/lightness of the image"""
bl_idname = 'image_editor_plus.adjust_color_dialog' bl_idname = 'image_editor_plus.adjust_color_dialog'
bl_label = "Hue/Saturation" bl_label = "Hue/Saturation"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=preview_adjust_color_properties) update=update_adjust_color_properties,
description='Update preview')
reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, reset: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=reset_adjust_color_properties) update=reset_adjust_color_properties)
adjust_hue: bpy.props.FloatProperty(name='Hue', subtype='FACTOR', adjust_hue: bpy.props.FloatProperty(name='Hue', subtype='FACTOR',
min=-180, max=180, default=0, options={'SKIP_SAVE'}, min=-180, max=180, default=0, options={'SKIP_SAVE'})
update=update_adjust_color_properties)
adjust_lightness: bpy.props.FloatProperty(name='Lightness', subtype='PERCENTAGE', adjust_lightness: bpy.props.FloatProperty(name='Lightness', subtype='PERCENTAGE',
min=0, max=200, default=100, options={'SKIP_SAVE'}, min=0, max=200, default=100, options={'SKIP_SAVE'})
update=update_adjust_color_properties)
adjust_saturation: bpy.props.FloatProperty(name='Saturation', subtype='PERCENTAGE', adjust_saturation: bpy.props.FloatProperty(name='Saturation', subtype='PERCENTAGE',
min=0, max=200, default=100, options={'SKIP_SAVE'}, min=0, max=200, default=100, options={'SKIP_SAVE'})
update=update_adjust_color_properties)
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -478,7 +460,7 @@ class IMAGE_EDITOR_PLUS_OT_adjust_color_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()
@ -501,25 +483,18 @@ class IMAGE_EDITOR_PLUS_OT_adjust_color_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_adjust_brightness_properties(self, context):
if self.preview:
update_adjust_brightness_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_adjust_brightness_properties(self, context): def reset_adjust_brightness_properties(self, context):
if self.reset: if self.reset:
self.reset = False
self.property_unset('adjust_brightness') self.property_unset('adjust_brightness')
self.property_unset('adjust_contrast') self.property_unset('adjust_contrast')
update_adjust_brightness_properties(self, context) update_adjust_brightness_properties(self, context)
self.reset = False
def update_adjust_brightness_properties(self, context): def update_adjust_brightness_properties(self, context):
if self.preview: if self.update_preview:
self.update_preview = False
bpy.ops.image_editor_plus.adjust_brightness('EXEC_DEFAULT', False, bpy.ops.image_editor_plus.adjust_brightness('EXEC_DEFAULT', False,
adjust_brightness=self.adjust_brightness, adjust_brightness=self.adjust_brightness,
adjust_contrast=self.adjust_contrast) adjust_contrast=self.adjust_contrast)
@ -528,16 +503,15 @@ class IMAGE_EDITOR_PLUS_OT_adjust_brightness_dialog(bpy.types.Operator):
"""Adjust brightness/contrast of the image""" """Adjust brightness/contrast of the image"""
bl_idname = 'image_editor_plus.adjust_brightness_dialog' bl_idname = 'image_editor_plus.adjust_brightness_dialog'
bl_label = "Brightness/Contrast" bl_label = "Brightness/Contrast"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=preview_adjust_brightness_properties) update=update_adjust_brightness_properties,
description='Update preview')
reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, reset: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=reset_adjust_brightness_properties) update=reset_adjust_brightness_properties)
adjust_brightness: bpy.props.FloatProperty(name='Brightness', subtype='PERCENTAGE', adjust_brightness: bpy.props.FloatProperty(name='Brightness', subtype='PERCENTAGE',
min=0, max=200, default=100, options={'SKIP_SAVE'}, min=0, max=200, default=100, options={'SKIP_SAVE'})
update=update_adjust_brightness_properties)
adjust_contrast: bpy.props.FloatProperty(name='Contrast', subtype='PERCENTAGE', adjust_contrast: bpy.props.FloatProperty(name='Contrast', subtype='PERCENTAGE',
min=0, max=200, default=100, options={'SKIP_SAVE'}, min=0, max=200, default=100, options={'SKIP_SAVE'})
update=update_adjust_brightness_properties)
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -573,7 +547,7 @@ class IMAGE_EDITOR_PLUS_OT_adjust_brightness_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()
@ -591,24 +565,17 @@ class IMAGE_EDITOR_PLUS_OT_adjust_brightness_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_adjust_gamma_properties(self, context):
if self.preview:
update_adjust_gamma_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_adjust_gamma_properties(self, context): def reset_adjust_gamma_properties(self, context):
if self.reset: if self.reset:
self.reset = False
self.property_unset('adjust_gamma') self.property_unset('adjust_gamma')
update_adjust_gamma_properties(self, context) update_adjust_gamma_properties(self, context)
self.reset = False
def update_adjust_gamma_properties(self, context): def update_adjust_gamma_properties(self, context):
if self.preview: if self.update_preview:
self.update_preview = False
bpy.ops.image_editor_plus.adjust_gamma('EXEC_DEFAULT', False, bpy.ops.image_editor_plus.adjust_gamma('EXEC_DEFAULT', False,
adjust_gamma=self.adjust_gamma) adjust_gamma=self.adjust_gamma)
@ -616,13 +583,13 @@ class IMAGE_EDITOR_PLUS_OT_adjust_gamma_dialog(bpy.types.Operator):
"""Adjust gamma of the image""" """Adjust gamma of the image"""
bl_idname = 'image_editor_plus.adjust_gamma_dialog' bl_idname = 'image_editor_plus.adjust_gamma_dialog'
bl_label = "Gamma" bl_label = "Gamma"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=preview_adjust_gamma_properties) update=update_adjust_gamma_properties,
description='Update preview')
reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, reset: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=reset_adjust_gamma_properties) update=reset_adjust_gamma_properties)
adjust_gamma: bpy.props.FloatProperty(name='Gamma', subtype='FACTOR', adjust_gamma: bpy.props.FloatProperty(name='Gamma', subtype='FACTOR',
min=0.01, soft_max=3.0, default=1.0, options={'SKIP_SAVE'}, min=0.01, soft_max=3.0, default=1.0, options={'SKIP_SAVE'})
update=update_adjust_gamma_properties)
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -657,7 +624,7 @@ class IMAGE_EDITOR_PLUS_OT_adjust_gamma_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()
@ -670,33 +637,23 @@ class IMAGE_EDITOR_PLUS_OT_adjust_gamma_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_adjust_color_curve_properties(self, context):
if not self.preview:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_adjust_color_curve_properties(self, context): def reset_adjust_color_curve_properties(self, context):
if self.reset: if self.reset:
app.reset_curve_mapping()
self.reset = False self.reset = False
app.reset_curve_mapping()
update_adjust_color_curve_properties(self, context)
def update_adjust_color_curve_properties(self, context): def update_adjust_color_curve_properties(self, context):
if self.update_preview: if self.update_preview:
self.update_preview = False self.update_preview = False
if self.preview:
bpy.ops.image_editor_plus.adjust_color_curve('EXEC_DEFAULT', False) bpy.ops.image_editor_plus.adjust_color_curve('EXEC_DEFAULT', False)
class IMAGE_EDITOR_PLUS_OT_adjust_color_curve_dialog(bpy.types.Operator): class IMAGE_EDITOR_PLUS_OT_adjust_color_curve_dialog(bpy.types.Operator):
"""Adjust color curve of the image""" """Adjust color curve of the image"""
bl_idname = 'image_editor_plus.adjust_curve_dialog' bl_idname = 'image_editor_plus.adjust_curve_dialog'
bl_label = "Adjust Color Curve" bl_label = "Adjust Color Curve"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'},
update=preview_adjust_color_curve_properties,
description='Preview manually (Need an update operation)')
update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=update_adjust_color_curve_properties, update=update_adjust_color_curve_properties,
description='Update preview') description='Update preview')
@ -735,8 +692,7 @@ class IMAGE_EDITOR_PLUS_OT_adjust_color_curve_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
row.prop(self, 'update_preview', text='Update', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()
@ -749,26 +705,19 @@ class IMAGE_EDITOR_PLUS_OT_adjust_color_curve_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_replace_color_properties(self, context):
if self.preview:
update_replace_color_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_replace_color_properties(self, context): def reset_replace_color_properties(self, context):
if self.reset: if self.reset:
self.reset = False
self.property_unset('source_color') self.property_unset('source_color')
self.property_unset('replace_color') self.property_unset('replace_color')
self.property_unset('color_threshold') self.property_unset('color_threshold')
update_replace_color_properties(self, context) update_replace_color_properties(self, context)
self.reset = False
def update_replace_color_properties(self, context): def update_replace_color_properties(self, context):
if self.preview: if self.update_preview:
self.update_preview = False
bpy.ops.image_editor_plus.replace_color('EXEC_DEFAULT', False, bpy.ops.image_editor_plus.replace_color('EXEC_DEFAULT', False,
source_color=self.source_color, source_color=self.source_color,
replace_color=self.replace_color, replace_color=self.replace_color,
@ -778,19 +727,17 @@ class IMAGE_EDITOR_PLUS_OT_replace_color_dialog(bpy.types.Operator):
"""Replace one color in the image with another""" """Replace one color in the image with another"""
bl_idname = 'image_editor_plus.replace_color_dialog' bl_idname = 'image_editor_plus.replace_color_dialog'
bl_label = "Replace Color" bl_label = "Replace Color"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=preview_replace_color_properties) update=update_replace_color_properties,
description='Update preview')
reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, reset: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=reset_replace_color_properties) update=reset_replace_color_properties)
source_color: bpy.props.FloatVectorProperty(name='Source Color', subtype='COLOR_GAMMA', source_color: bpy.props.FloatVectorProperty(name='Source Color', subtype='COLOR_GAMMA',
min=0, max=1.0, size=3, default=(1.0, 1.0, 1.0), options={'SKIP_SAVE'}, min=0, max=1.0, size=3, default=(1.0, 1.0, 1.0), options={'SKIP_SAVE'}) # no alpha
update=update_replace_color_properties) # no alpha
replace_color: bpy.props.FloatVectorProperty(name='Replace Color', subtype='COLOR_GAMMA', replace_color: bpy.props.FloatVectorProperty(name='Replace Color', subtype='COLOR_GAMMA',
min=0, max=1.0, size=4, default=(0, 0, 0, 1.0), options={'SKIP_SAVE'}, min=0, max=1.0, size=4, default=(0, 0, 0, 1.0), options={'SKIP_SAVE'})
update=update_replace_color_properties)
color_threshold: bpy.props.FloatProperty(name='Threshold', subtype='FACTOR', color_threshold: bpy.props.FloatProperty(name='Threshold', subtype='FACTOR',
min=0, max=1.0, default=0.1, options={'SKIP_SAVE'}, min=0, max=1.0, default=0.1, options={'SKIP_SAVE'})
update=update_replace_color_properties)
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -827,7 +774,7 @@ class IMAGE_EDITOR_PLUS_OT_replace_color_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()
@ -850,25 +797,18 @@ class IMAGE_EDITOR_PLUS_OT_replace_color_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_blur_properties(self, context):
if self.preview:
update_blur_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_blur_properties(self, context): def reset_blur_properties(self, context):
if self.reset: if self.reset:
self.reset = False
self.property_unset('blur_size') self.property_unset('blur_size')
self.property_unset('expand_layer') self.property_unset('expand_layer')
update_blur_properties(self, context) update_blur_properties(self, context)
self.reset = False
def update_blur_properties(self, context): def update_blur_properties(self, context):
if self.preview: if self.update_preview:
self.update_preview = False
bpy.ops.image_editor_plus.blur('EXEC_DEFAULT', False, bpy.ops.image_editor_plus.blur('EXEC_DEFAULT', False,
blur_size=self.blur_size, blur_size=self.blur_size,
expand_layer=False) expand_layer=False)
@ -877,14 +817,13 @@ class IMAGE_EDITOR_PLUS_OT_blur_dialog(bpy.types.Operator):
"""Blur the image""" """Blur the image"""
bl_idname = 'image_editor_plus.blur_dialog' bl_idname = 'image_editor_plus.blur_dialog'
bl_label = "Blur (Gaussian Blur)" bl_label = "Blur (Gaussian Blur)"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=preview_blur_properties) update=update_blur_properties,
description='Update preview')
reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_blur_properties) reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_blur_properties)
blur_size: bpy.props.FloatProperty(name='Size', subtype='FACTOR', blur_size: bpy.props.FloatProperty(name='Size', subtype='FACTOR',
min=0, soft_max=10.0, default=3.0, options={'SKIP_SAVE'}, min=0, soft_max=10.0, default=3.0, options={'SKIP_SAVE'})
update=update_blur_properties) expand_layer: bpy.props.BoolProperty(name='Expand Layer', default=True, options={'SKIP_SAVE'})
expand_layer: bpy.props.BoolProperty(name='Expand Layer', default=True, options={'SKIP_SAVE'},
update=update_blur_properties)
def invoke(self, context, event): def invoke(self, context, event):
session = app.get_session() session = app.get_session()
@ -926,7 +865,7 @@ class IMAGE_EDITOR_PLUS_OT_blur_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()
@ -943,26 +882,19 @@ class IMAGE_EDITOR_PLUS_OT_blur_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_sharpen_properties(self, context):
if self.preview:
update_sharpen_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_sharpen_properties(self, context): def reset_sharpen_properties(self, context):
if self.reset: if self.reset:
self.reset = False
self.property_unset('sharpen_radius') self.property_unset('sharpen_radius')
self.property_unset('sharpen_amount') self.property_unset('sharpen_amount')
self.property_unset('sharpen_threshold') self.property_unset('sharpen_threshold')
update_sharpen_properties(self, context) update_sharpen_properties(self, context)
self.reset = False
def update_sharpen_properties(self, context): def update_sharpen_properties(self, context):
if self.preview: if self.update_preview:
self.update_preview = False
bpy.ops.image_editor_plus.sharpen('EXEC_DEFAULT', False, bpy.ops.image_editor_plus.sharpen('EXEC_DEFAULT', False,
sharpen_radius=self.sharpen_radius, sharpen_radius=self.sharpen_radius,
sharpen_amount=self.sharpen_amount, sharpen_amount=self.sharpen_amount,
@ -972,18 +904,16 @@ class IMAGE_EDITOR_PLUS_OT_sharpen_dialog(bpy.types.Operator):
"""Sharpen the image""" """Sharpen the image"""
bl_idname = 'image_editor_plus.sharpen_dialog' bl_idname = 'image_editor_plus.sharpen_dialog'
bl_label = "Sharpen (Unsharp Mask)" bl_label = "Sharpen (Unsharp Mask)"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=preview_sharpen_properties) update=update_sharpen_properties,
description='Update preview')
reset: bpy.props.BoolProperty(update=reset_sharpen_properties, options={'SKIP_SAVE'}) reset: bpy.props.BoolProperty(update=reset_sharpen_properties, options={'SKIP_SAVE'})
sharpen_radius: bpy.props.FloatProperty(name='Radius', subtype='FACTOR', sharpen_radius: bpy.props.FloatProperty(name='Radius', subtype='FACTOR',
min=0, soft_max=10.0, default=3.0, options={'SKIP_SAVE'}, min=0, soft_max=10.0, default=3.0, options={'SKIP_SAVE'})
update=update_sharpen_properties)
sharpen_amount: bpy.props.FloatProperty(name='Amount', subtype='FACTOR', sharpen_amount: bpy.props.FloatProperty(name='Amount', subtype='FACTOR',
min=0, soft_max=10.0, default=0.5, options={'SKIP_SAVE'}, min=0, soft_max=10.0, default=0.5, options={'SKIP_SAVE'})
update=update_sharpen_properties)
sharpen_threshold: bpy.props.FloatProperty(name='Threshold', subtype='FACTOR', sharpen_threshold: bpy.props.FloatProperty(name='Threshold', subtype='FACTOR',
min=0, soft_max=1.0, default=0, options={'SKIP_SAVE'}, min=0, soft_max=1.0, default=0, options={'SKIP_SAVE'})
update=update_sharpen_properties)
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -1020,7 +950,7 @@ class IMAGE_EDITOR_PLUS_OT_sharpen_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()
@ -1043,24 +973,17 @@ class IMAGE_EDITOR_PLUS_OT_sharpen_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_add_noise_properties(self, context):
if self.preview:
update_add_noise_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_add_noise_properties(self, context): def reset_add_noise_properties(self, context):
if self.reset: if self.reset:
self.reset = False
self.property_unset('add_noise_intensity') self.property_unset('add_noise_intensity')
update_add_noise_properties(self, context) update_add_noise_properties(self, context)
self.reset = False
def update_add_noise_properties(self, context): def update_add_noise_properties(self, context):
if self.preview: if self.update_preview:
self.update_preview = False
bpy.ops.image_editor_plus.add_noise('EXEC_DEFAULT', False, bpy.ops.image_editor_plus.add_noise('EXEC_DEFAULT', False,
add_noise_intensity=self.add_noise_intensity) add_noise_intensity=self.add_noise_intensity)
@ -1068,12 +991,12 @@ class IMAGE_EDITOR_PLUS_OT_add_noise_dialog(bpy.types.Operator):
"""Add some noise to the image""" """Add some noise to the image"""
bl_idname = 'image_editor_plus.add_noise_dialog' bl_idname = 'image_editor_plus.add_noise_dialog'
bl_label = "Add Noise (Gaussian Noise)" bl_label = "Add Noise (Gaussian Noise)"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=preview_add_noise_properties) update=update_add_noise_properties,
description='Update preview')
reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_add_noise_properties) reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_add_noise_properties)
add_noise_intensity: bpy.props.FloatProperty(name='Intensity', subtype='FACTOR', add_noise_intensity: bpy.props.FloatProperty(name='Intensity', subtype='FACTOR',
min=0, soft_max=10.0, default=0.1, options={'SKIP_SAVE'}, min=0, soft_max=10.0, default=0.1, options={'SKIP_SAVE'})
update=update_add_noise_properties)
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -1108,7 +1031,7 @@ class IMAGE_EDITOR_PLUS_OT_add_noise_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()
@ -1121,24 +1044,17 @@ class IMAGE_EDITOR_PLUS_OT_add_noise_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_pixelize_properties(self, context):
if self.preview:
update_pixelize_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_pixelize_properties(self, context): def reset_pixelize_properties(self, context):
if self.reset: if self.reset:
self.reset = False
self.property_unset('pixelize_pixel_size') self.property_unset('pixelize_pixel_size')
update_pixelize_properties(self, context) update_pixelize_properties(self, context)
self.reset = False
def update_pixelize_properties(self, context): def update_pixelize_properties(self, context):
if self.preview: if self.update_preview:
self.update_preview = False
bpy.ops.image_editor_plus.pixelize('EXEC_DEFAULT', False, bpy.ops.image_editor_plus.pixelize('EXEC_DEFAULT', False,
pixelize_pixel_size=self.pixelize_pixel_size) pixelize_pixel_size=self.pixelize_pixel_size)
@ -1146,12 +1062,12 @@ class IMAGE_EDITOR_PLUS_OT_pixelize_dialog(bpy.types.Operator):
"""Pixelize the image""" """Pixelize the image"""
bl_idname = 'image_editor_plus.pixelize_dialog' bl_idname = 'image_editor_plus.pixelize_dialog'
bl_label = "Pixelize" bl_label = "Pixelize"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=preview_pixelize_properties) update=update_pixelize_properties,
description='Update preview')
reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_pixelize_properties) reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_pixelize_properties)
pixelize_pixel_size: bpy.props.IntProperty(name='Pixel Size', subtype='FACTOR', pixelize_pixel_size: bpy.props.IntProperty(name='Pixel Size', subtype='FACTOR',
min=1, soft_max=64, default=16, options={'SKIP_SAVE'}, min=1, soft_max=64, default=16, options={'SKIP_SAVE'})
update=update_pixelize_properties)
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -1186,7 +1102,7 @@ class IMAGE_EDITOR_PLUS_OT_pixelize_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()
@ -1199,25 +1115,13 @@ class IMAGE_EDITOR_PLUS_OT_pixelize_dialog(bpy.types.Operator):
row.column() row.column()
row.prop(self, 'reset', text='Reset', toggle=True) row.prop(self, 'reset', text='Reset', toggle=True)
def preview_make_seamless_properties(self, context):
if self.preview:
update_make_seamless_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def update_make_seamless_properties(self, context): def update_make_seamless_properties(self, context):
if self.preview:
bpy.ops.image_editor_plus.make_seamless('EXEC_DEFAULT', False) bpy.ops.image_editor_plus.make_seamless('EXEC_DEFAULT', False)
class IMAGE_EDITOR_PLUS_OT_make_seamless_dialog(bpy.types.Operator): class IMAGE_EDITOR_PLUS_OT_make_seamless_dialog(bpy.types.Operator):
"""Turn the image into seamless tile""" """Turn the image into seamless tile"""
bl_idname = 'image_editor_plus.make_seamless_dialog' bl_idname = 'image_editor_plus.make_seamless_dialog'
bl_label = "Make Seamless" bl_label = "Make Seamless"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'},
update=preview_make_seamless_properties)
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
@ -1247,33 +1151,22 @@ class IMAGE_EDITOR_PLUS_OT_make_seamless_dialog(bpy.types.Operator):
app.refresh_image(context) app.refresh_image(context)
def draw(self, context): def draw(self, context):
layout = self.layout pass
row = layout.split(align=True)
row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM')
def preview_normal_map_properties(self, context):
if self.preview:
update_normal_map_properties(self, context)
else:
img = app.get_target_image(context)
if img:
app.revert_image_cache(img)
app.refresh_image(context)
def reset_normal_map_properties(self, context): def reset_normal_map_properties(self, context):
if self.reset: if self.reset:
self.reset = False
self.property_unset('scale') self.property_unset('scale')
self.property_unset('flip_x') self.property_unset('flip_x')
self.property_unset('flip_y') self.property_unset('flip_y')
self.property_unset('full_z') self.property_unset('full_z')
update_normal_map_properties(self, context) update_normal_map_properties(self, context)
self.reset = False
def update_normal_map_properties(self, context): def update_normal_map_properties(self, context):
if self.preview: if self.update_preview:
self.update_preview = False
bpy.ops.image_editor_plus.normal_map('EXEC_DEFAULT', False, bpy.ops.image_editor_plus.normal_map('EXEC_DEFAULT', False,
scale=self.scale, scale=self.scale,
flip_x=self.flip_x, flip_x=self.flip_x,
@ -1284,18 +1177,15 @@ class IMAGE_EDITOR_PLUS_OT_normal_map_dialog(bpy.types.Operator):
"""Generate a normal map""" """Generate a normal map"""
bl_idname = 'image_editor_plus.normal_map_dialog' bl_idname = 'image_editor_plus.normal_map_dialog'
bl_label = "Normal Map" bl_label = "Normal Map"
preview: bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}, update_preview: bpy.props.BoolProperty(options={'SKIP_SAVE'},
update=preview_normal_map_properties) update=update_normal_map_properties,
description='Update preview')
reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_normal_map_properties) reset: bpy.props.BoolProperty(options={'SKIP_SAVE'}, update=reset_normal_map_properties)
scale: bpy.props.FloatProperty(name='Scale', subtype='FACTOR', scale: bpy.props.FloatProperty(name='Scale', subtype='FACTOR',
min=0, soft_max=250.0, default=10.0, options={'SKIP_SAVE'}, min=0, soft_max=250.0, default=10.0, options={'SKIP_SAVE'})
update=update_normal_map_properties) flip_x: bpy.props.BoolProperty(name='Flip X', default=False, options={'SKIP_SAVE'})
flip_x: bpy.props.BoolProperty(name='Flip X', default=False, options={'SKIP_SAVE'}, flip_y: bpy.props.BoolProperty(name='Flip Y', default=False, options={'SKIP_SAVE'})
update=update_normal_map_properties) full_z: bpy.props.BoolProperty(name='Full Range for Z', default=False, options={'SKIP_SAVE'})
flip_y: bpy.props.BoolProperty(name='Flip Y', default=False, options={'SKIP_SAVE'},
update=update_normal_map_properties)
full_z: bpy.props.BoolProperty(name='Full Range for Z', default=False, options={'SKIP_SAVE'},
update=update_normal_map_properties)
def invoke(self, context, event): def invoke(self, context, event):
session = app.get_session() session = app.get_session()
@ -1339,7 +1229,7 @@ class IMAGE_EDITOR_PLUS_OT_normal_map_dialog(bpy.types.Operator):
row = layout.split(align=True) row = layout.split(align=True)
row.column() row.column()
row.prop(self, 'preview', text='Preview', toggle=True, icon='VIEWZOOM') row.prop(self, 'update_preview', text='Update Preview', toggle=True, icon='FILE_REFRESH')
layout.separator() layout.separator()

View File

@ -1,5 +1,5 @@
''' '''
Copyright (C) 2020 - 2024 Akaneyu Copyright (C) 2020 - 2025 Akaneyu
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -18,15 +18,12 @@
import sys import sys
import math import math
import gpu import gpu
from gpu.shader import create_from_info
from gpu_extras.batch import batch_for_shader from gpu_extras.batch import batch_for_shader
from mathutils import Matrix, Vector from mathutils import Matrix, Vector
import numpy as np import numpy as np
default_vertex_shader = ''' default_vertex_shader = '''
uniform mat4 ModelViewProjectionMatrix;
in vec2 pos;
void main() void main()
{ {
gl_Position = ModelViewProjectionMatrix * vec4(pos, 0, 1.0); gl_Position = ModelViewProjectionMatrix * vec4(pos, 0, 1.0);
@ -34,10 +31,6 @@ void main()
''' '''
default_fragment_shader = ''' default_fragment_shader = '''
uniform vec4 color;
out vec4 fragColor;
void main() void main()
{ {
fragColor = color; fragColor = color;
@ -45,34 +38,18 @@ void main()
''' '''
dotted_line_vertex_shader = ''' dotted_line_vertex_shader = '''
uniform mat4 ModelViewProjectionMatrix;
in vec2 pos;
in float arcLength;
out float arcLengthOut;
void main() void main()
{ {
arcLengthOut = arcLength; arcLengthInter = arcLength;
gl_Position = ModelViewProjectionMatrix * vec4(pos, 0, 1.0); gl_Position = ModelViewProjectionMatrix * vec4(pos, 0, 1.0);
} }
''' '''
dotted_line_fragment_shader = ''' dotted_line_fragment_shader = '''
uniform float scale;
uniform float offset;
uniform vec4 color1;
uniform vec4 color2;
in float arcLengthOut;
out vec4 fragColor;
void main() void main()
{ {
if (step(sin((arcLengthOut + offset) * scale), 0.5) == 1) { if (step(sin((arcLengthInter + offset) * scale), 0.5) == 1) {
fragColor = color1; fragColor = color1;
} else { } else {
fragColor = color2; fragColor = color2;
@ -81,13 +58,6 @@ void main()
''' '''
image_vertex_shader = ''' image_vertex_shader = '''
uniform mat4 ModelViewProjectionMatrix;
in vec2 pos;
in vec2 texCoord;
out vec2 texCoordOut;
void main() void main()
{ {
gl_Position = ModelViewProjectionMatrix * vec4(pos, 0, 1.0); gl_Position = ModelViewProjectionMatrix * vec4(pos, 0, 1.0);
@ -96,12 +66,6 @@ void main()
''' '''
image_fragment_shader = ''' image_fragment_shader = '''
uniform sampler2D image;
in vec2 texCoordOut;
out vec4 fragColor;
void main() void main()
{ {
fragColor = texture(image, texCoordOut); fragColor = texture(image, texCoordOut);
@ -118,18 +82,48 @@ def make_scale_matrix(scale):
class UIRenderer: class UIRenderer:
def __init__(self): def __init__(self):
self.default_shader = gpu.types.GPUShader(default_vertex_shader, default_shader_info = gpu.types.GPUShaderCreateInfo()
default_fragment_shader) default_shader_info.push_constant('MAT4', 'ModelViewProjectionMatrix')
self.default_shader_u_color = self.default_shader.uniform_from_name("color") default_shader_info.push_constant('VEC4', 'color')
default_shader_info.vertex_in(0, 'VEC2', 'pos')
default_shader_info.fragment_out(0, 'VEC4', 'fragColor')
default_shader_info.vertex_source(default_vertex_shader)
default_shader_info.fragment_source(default_fragment_shader)
self.default_shader = create_from_info(default_shader_info)
self.default_shader_u_color = self.default_shader.uniform_from_name('color')
self.dotted_line_shader = gpu.types.GPUShader(dotted_line_vertex_shader, dotted_line_shader_inter = gpu.types.GPUStageInterfaceInfo("dotted_line")
dotted_line_fragment_shader) dotted_line_shader_inter.smooth('FLOAT', "arcLengthInter")
dotted_line_shader_info = gpu.types.GPUShaderCreateInfo()
dotted_line_shader_info.push_constant('MAT4', 'ModelViewProjectionMatrix')
dotted_line_shader_info.push_constant('FLOAT', 'scale')
dotted_line_shader_info.push_constant('FLOAT', 'offset')
dotted_line_shader_info.push_constant('VEC4', 'color1')
dotted_line_shader_info.push_constant('VEC4', 'color2')
dotted_line_shader_info.vertex_in(0, 'VEC2', 'pos')
dotted_line_shader_info.vertex_in(1, 'FLOAT', 'arcLength')
dotted_line_shader_info.vertex_out(dotted_line_shader_inter)
dotted_line_shader_info.fragment_out(0, 'VEC4', 'fragColor')
dotted_line_shader_info.vertex_source(dotted_line_vertex_shader)
dotted_line_shader_info.fragment_source(dotted_line_fragment_shader)
self.dotted_line_shader = create_from_info(dotted_line_shader_info)
self.dotted_line_shader_u_color1 = self.dotted_line_shader.uniform_from_name("color1") self.dotted_line_shader_u_color1 = self.dotted_line_shader.uniform_from_name("color1")
self.dotted_line_shader_u_color2 = self.dotted_line_shader.uniform_from_name("color2") self.dotted_line_shader_u_color2 = self.dotted_line_shader.uniform_from_name("color2")
#self.image_shader = gpu.shader.from_builtin('2D_IMAGE') image_shader_inter = gpu.types.GPUStageInterfaceInfo("image_shader")
self.image_shader = gpu.types.GPUShader(image_vertex_shader, image_shader_inter.smooth('VEC2', "texCoordOut")
image_fragment_shader)
image_shader_info = gpu.types.GPUShaderCreateInfo()
image_shader_info.push_constant('MAT4', 'ModelViewProjectionMatrix')
image_shader_info.sampler(0, 'FLOAT_2D', 'image')
image_shader_info.vertex_in(0, 'VEC2', 'pos')
image_shader_info.vertex_in(1, 'VEC2', 'texCoord')
image_shader_info.vertex_out(image_shader_inter)
image_shader_info.fragment_out(0, 'VEC4', 'fragColor')
image_shader_info.vertex_source(image_vertex_shader)
image_shader_info.fragment_source(image_fragment_shader)
self.image_shader = create_from_info(image_shader_info)
def render_selection_frame(self, pos, size, rot=0, scale=(1.0, 1.0)): def render_selection_frame(self, pos, size, rot=0, scale=(1.0, 1.0)):
width, height = size[0], size[1] width, height = size[0], size[1]

BIN
doc/images/adjust_color.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 KiB

BIN
doc/images/featured.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

BIN
doc/images/filters.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

BIN
doc/images/normal_map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB