mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-07 10:59:49 +00:00
Add an offset image effect
Some functionality as the move tool, but with more precision, wrap around ability and of course animation.
This commit is contained in:
parent
81c0b49c59
commit
08064098b8
|
@ -148,6 +148,16 @@ msgstr ""
|
||||||
msgid "Resize Canvas"
|
msgid "Resize Canvas"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Offset Image"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Offset:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Found in the Offset Image dialog. It's a checkbox that, if enabled, wraps around the image if pixels go out of canvas bounds.
|
||||||
|
msgid "Wrap around:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Centralize Image"
|
msgid "Centralize Image"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ enum ViewMenu {
|
||||||
enum WindowMenu { WINDOW_OPACITY, PANELS, LAYOUTS, MOVABLE_PANELS, ZEN_MODE, FULLSCREEN_MODE }
|
enum WindowMenu { WINDOW_OPACITY, PANELS, LAYOUTS, MOVABLE_PANELS, ZEN_MODE, FULLSCREEN_MODE }
|
||||||
enum ImageMenu {
|
enum ImageMenu {
|
||||||
RESIZE_CANVAS,
|
RESIZE_CANVAS,
|
||||||
|
OFFSET_IMAGE,
|
||||||
SCALE_IMAGE,
|
SCALE_IMAGE,
|
||||||
CROP_IMAGE,
|
CROP_IMAGE,
|
||||||
FLIP,
|
FLIP,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class_name ImageEffect
|
class_name ImageEffect
|
||||||
extends ConfirmationDialog
|
extends ConfirmationDialog
|
||||||
# Parent class for all image effects
|
## Parent class for all image effects
|
||||||
# Methods that have "pass" are meant to be replaced by the inherited Scripts
|
## Methods that have "pass" are meant to be replaced by the inherited scripts
|
||||||
|
|
||||||
enum { SELECTED_CELS, FRAME, ALL_FRAMES, ALL_PROJECTS }
|
enum { SELECTED_CELS, FRAME, ALL_FRAMES, ALL_PROJECTS }
|
||||||
|
|
||||||
|
|
24
src/Shaders/OffsetPixels.gdshader
Normal file
24
src/Shaders/OffsetPixels.gdshader
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
uniform sampler2D selection;
|
||||||
|
uniform vec2 offset = vec2(0.0); // In pixels
|
||||||
|
uniform bool wrap_around = false;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
vec4 original_color = texture(TEXTURE, UV);
|
||||||
|
vec4 selection_color = texture(selection, UV);
|
||||||
|
|
||||||
|
vec2 uv = UV - (offset * TEXTURE_PIXEL_SIZE); // Offset the uv by an amount of pixels
|
||||||
|
if (wrap_around) {
|
||||||
|
uv = fract(uv);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 output = texture(TEXTURE, uv);
|
||||||
|
// Cut original selected content
|
||||||
|
original_color.a = 0.0 * step(0.5, selection_color.a) + original_color.a * step(selection_color.a, 0.5);
|
||||||
|
output.a = min(step(uv.x, 1.0) * step(0.0, uv.x), output.a); // Remove left and right edges
|
||||||
|
output.a = min(step(uv.y, 1.0) * step(0.0, uv.y), output.a); // Remove up and bottom edges
|
||||||
|
|
||||||
|
selection_color = texture(selection, uv);
|
||||||
|
COLOR = mix(original_color, output, selection_color.a);
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=14 format=2]
|
[gd_scene load_steps=15 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://src/UI/Dialogs/ImageEffects/FlipImageDialog.tscn" type="PackedScene" id=1]
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/FlipImageDialog.tscn" type="PackedScene" id=1]
|
||||||
[ext_resource path="res://src/UI/Dialogs/ImageEffects/InvertColorsDialog.tscn" type="PackedScene" id=2]
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/InvertColorsDialog.tscn" type="PackedScene" id=2]
|
||||||
|
@ -6,6 +6,7 @@
|
||||||
[ext_resource path="res://src/UI/Dialogs/ImageEffects/DropShadowDialog.tscn" type="PackedScene" id=4]
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/DropShadowDialog.tscn" type="PackedScene" id=4]
|
||||||
[ext_resource path="res://src/UI/Dialogs/ImageEffects/GradientMapDialog.tscn" type="PackedScene" id=5]
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/GradientMapDialog.tscn" type="PackedScene" id=5]
|
||||||
[ext_resource path="res://src/UI/Dialogs/ImageEffects/Posterize.tscn" type="PackedScene" id=6]
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/Posterize.tscn" type="PackedScene" id=6]
|
||||||
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/OffsetImage.tscn" type="PackedScene" id=7]
|
||||||
[ext_resource path="res://src/UI/Dialogs/ImageEffects/ResizeCanvas.tscn" type="PackedScene" id=8]
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/ResizeCanvas.tscn" type="PackedScene" id=8]
|
||||||
[ext_resource path="res://src/UI/Dialogs/ImageEffects/RotateImage.tscn" type="PackedScene" id=9]
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/RotateImage.tscn" type="PackedScene" id=9]
|
||||||
[ext_resource path="res://src/UI/Dialogs/ImageEffects/ShaderEffect.tscn" type="PackedScene" id=10]
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/ShaderEffect.tscn" type="PackedScene" id=10]
|
||||||
|
@ -17,6 +18,10 @@
|
||||||
[node name="ImageEffects" type="Control"]
|
[node name="ImageEffects" type="Control"]
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="OffsetImage" parent="." instance=ExtResource( 7 )]
|
||||||
|
margin_right = 376.0
|
||||||
|
margin_bottom = 370.0
|
||||||
|
|
||||||
[node name="ScaleImage" parent="." instance=ExtResource( 14 )]
|
[node name="ScaleImage" parent="." instance=ExtResource( 14 )]
|
||||||
margin_bottom = 127.0
|
margin_bottom = 127.0
|
||||||
|
|
||||||
|
|
54
src/UI/Dialogs/ImageEffects/OffsetImage.gd
Normal file
54
src/UI/Dialogs/ImageEffects/OffsetImage.gd
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
extends ImageEffect
|
||||||
|
|
||||||
|
enum Animate { OFFSET_X, OFFSET_Y }
|
||||||
|
|
||||||
|
var shader: Shader = preload("res://src/Shaders/OffsetPixels.gdshader")
|
||||||
|
var wrap_around := false
|
||||||
|
|
||||||
|
onready var offset_sliders := $VBoxContainer/OffsetOptions/OffsetSliders as ValueSliderV2
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
var sm := ShaderMaterial.new()
|
||||||
|
sm.shader = shader
|
||||||
|
preview.set_material(sm)
|
||||||
|
# Set as in the Animate enum
|
||||||
|
animate_panel.add_float_property(
|
||||||
|
"Offset X", $VBoxContainer/OffsetOptions/OffsetSliders.get_sliders()[0]
|
||||||
|
)
|
||||||
|
animate_panel.add_float_property(
|
||||||
|
"Offset Y", $VBoxContainer/OffsetOptions/OffsetSliders.get_sliders()[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _about_to_show() -> void:
|
||||||
|
offset_sliders.min_value = -Global.current_project.size
|
||||||
|
offset_sliders.max_value = Global.current_project.size
|
||||||
|
._about_to_show()
|
||||||
|
|
||||||
|
|
||||||
|
func commit_action(cel: Image, project: Project = Global.current_project) -> void:
|
||||||
|
var offset_x := animate_panel.get_animated_value(commit_idx, Animate.OFFSET_X)
|
||||||
|
var offset_y := animate_panel.get_animated_value(commit_idx, Animate.OFFSET_Y)
|
||||||
|
var offset := Vector2(offset_x, offset_y)
|
||||||
|
var selection_tex := ImageTexture.new()
|
||||||
|
if selection_checkbox.pressed and project.has_selection:
|
||||||
|
selection_tex.create_from_image(project.selection_map, 0)
|
||||||
|
|
||||||
|
var params := {"offset": offset, "wrap_around": wrap_around, "selection": selection_tex}
|
||||||
|
if !confirmed:
|
||||||
|
for param in params:
|
||||||
|
preview.material.set_shader_param(param, params[param])
|
||||||
|
else:
|
||||||
|
var gen := ShaderImageEffect.new()
|
||||||
|
gen.generate_image(cel, shader, params, project.size)
|
||||||
|
yield(gen, "done")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_OffsetSliders_value_changed(_value: Vector2) -> void:
|
||||||
|
update_preview()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_WrapCheckBox_toggled(button_pressed: bool) -> void:
|
||||||
|
wrap_around = button_pressed
|
||||||
|
update_preview()
|
71
src/UI/Dialogs/ImageEffects/OffsetImage.tscn
Normal file
71
src/UI/Dialogs/ImageEffects/OffsetImage.tscn
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
[gd_scene load_steps=4 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/ImageEffectParent.tscn" type="PackedScene" id=1]
|
||||||
|
[ext_resource path="res://src/UI/Nodes/ValueSliderV2.tscn" type="PackedScene" id=2]
|
||||||
|
[ext_resource path="res://src/UI/Dialogs/ImageEffects/OffsetImage.gd" type="Script" id=3]
|
||||||
|
|
||||||
|
[node name="OffsetImage" instance=ExtResource( 1 )]
|
||||||
|
window_title = "Offset Image"
|
||||||
|
script = ExtResource( 3 )
|
||||||
|
|
||||||
|
[node name="VBoxContainer" parent="." index="3"]
|
||||||
|
margin_bottom = 344.0
|
||||||
|
|
||||||
|
[node name="AspectRatioContainer" parent="VBoxContainer" index="1"]
|
||||||
|
margin_bottom = 224.0
|
||||||
|
|
||||||
|
[node name="Preview" parent="VBoxContainer/AspectRatioContainer" index="0"]
|
||||||
|
margin_left = 80.0
|
||||||
|
margin_right = 280.0
|
||||||
|
margin_bottom = 200.0
|
||||||
|
|
||||||
|
[node name="OffsetOptions" type="GridContainer" parent="VBoxContainer" index="2"]
|
||||||
|
margin_top = 228.0
|
||||||
|
margin_right = 360.0
|
||||||
|
margin_bottom = 308.0
|
||||||
|
columns = 2
|
||||||
|
|
||||||
|
[node name="OffsetLabel" type="Label" parent="VBoxContainer/OffsetOptions" index="0"]
|
||||||
|
margin_top = 19.0
|
||||||
|
margin_right = 178.0
|
||||||
|
margin_bottom = 33.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "Offset:"
|
||||||
|
|
||||||
|
[node name="OffsetSliders" parent="VBoxContainer/OffsetOptions" index="1" instance=ExtResource( 2 )]
|
||||||
|
margin_left = 182.0
|
||||||
|
margin_right = 360.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
min_value = Vector2( -64, -64 )
|
||||||
|
max_value = Vector2( 64, 64 )
|
||||||
|
allow_greater = true
|
||||||
|
allow_lesser = true
|
||||||
|
show_ratio = true
|
||||||
|
suffix_x = "px"
|
||||||
|
suffix_y = "px"
|
||||||
|
|
||||||
|
[node name="WrapLabel" type="Label" parent="VBoxContainer/OffsetOptions" index="2"]
|
||||||
|
margin_top = 61.0
|
||||||
|
margin_right = 178.0
|
||||||
|
margin_bottom = 75.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "Wrap around:"
|
||||||
|
|
||||||
|
[node name="WrapCheckBox" type="CheckBox" parent="VBoxContainer/OffsetOptions" index="3"]
|
||||||
|
margin_left = 182.0
|
||||||
|
margin_top = 56.0
|
||||||
|
margin_right = 360.0
|
||||||
|
margin_bottom = 80.0
|
||||||
|
mouse_default_cursor_shape = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "On"
|
||||||
|
|
||||||
|
[node name="OptionsContainer" parent="VBoxContainer" index="3"]
|
||||||
|
margin_top = 312.0
|
||||||
|
margin_bottom = 336.0
|
||||||
|
|
||||||
|
[node name="AnimateDialog" parent="." index="4"]
|
||||||
|
margin_bottom = 344.0
|
||||||
|
|
||||||
|
[connection signal="value_changed" from="VBoxContainer/OffsetOptions/OffsetSliders" to="." method="_on_OffsetSliders_value_changed"]
|
||||||
|
[connection signal="toggled" from="VBoxContainer/OffsetOptions/WrapCheckBox" to="." method="_on_WrapCheckBox_toggled"]
|
|
@ -273,6 +273,7 @@ func _setup_image_menu() -> void:
|
||||||
# Order as in Global.ImageMenu enum
|
# Order as in Global.ImageMenu enum
|
||||||
var image_menu_items := [
|
var image_menu_items := [
|
||||||
"Resize Canvas",
|
"Resize Canvas",
|
||||||
|
"Offset Image",
|
||||||
"Scale Image",
|
"Scale Image",
|
||||||
"Crop Image",
|
"Crop Image",
|
||||||
"Mirror Image",
|
"Mirror Image",
|
||||||
|
@ -650,6 +651,9 @@ func image_menu_id_pressed(id: int) -> void:
|
||||||
Global.ImageMenu.SCALE_IMAGE:
|
Global.ImageMenu.SCALE_IMAGE:
|
||||||
_popup_dialog(Global.control.get_node("Dialogs/ImageEffects/ScaleImage"))
|
_popup_dialog(Global.control.get_node("Dialogs/ImageEffects/ScaleImage"))
|
||||||
|
|
||||||
|
Global.ImageMenu.OFFSET_IMAGE:
|
||||||
|
_popup_dialog(Global.control.get_node("Dialogs/ImageEffects/OffsetImage"))
|
||||||
|
|
||||||
Global.ImageMenu.CENTRALIZE_IMAGE:
|
Global.ImageMenu.CENTRALIZE_IMAGE:
|
||||||
DrawingAlgos.centralize()
|
DrawingAlgos.centralize()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue