Merge pull request #53 from ChaoticByte/release/v9.0

Release/v9.0
This commit is contained in:
Julian Müller 2025-02-06 23:02:03 +01:00 committed by GitHub
commit c5c9b9f516
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 585 additions and 747 deletions

1
.gitignore vendored
View file

@ -16,7 +16,6 @@ mono_crash.*.json
*.x86_64
godot.*.template_release.*
dist/*
!dist/.gitkeep
screenshot.png.import

View file

@ -21,12 +21,16 @@ You can find the latest releases [here](https://github.com/ChaoticByte/Fragmente
## Usage
With Fragemented, you are editing images by writing GDShaders. This brings almost endless opportunities to create unique art.
With Fragemented, you are processing images with GDShaders. This brings almost endless opportunities to create unique art.
If you want to learn GDShader, take a look at the [Godot docs](https://docs.godotengine.org/en/stable/tutorials/shaders/).
The repo also includes examples. You can use them as a starting-point to write your own filters.
**The builtin editor got removed** from Fragmented with version **v9.0**. I advise you to write your shaders directly in the Godot Editor.
Besides the regular GDShader stuff, Fragmented also has so-called directives. Those allow to further control the behaviour of the application. The most important directive is `//!load` to load an image.
**To get started, use the project template (see the Releases section of this repo) and open it in Godot.**
The template includes many examples. You can use them as a starting-point to write your own stuff.
Besides the regular GDShader stuff, Fragmented has so-called directives. Those allow to further control the behaviour of the application. **The most important directive is `//!load` to load an image.**
### Load TEXTURE using the `//!load` directive
@ -58,6 +62,8 @@ Example:
//!load ...
//!steps 5
uniform int STEP; // this is mandatory!
void fragment() {
if (STEP == 0) {
...
@ -101,7 +107,22 @@ You can run Fragmented from the commandline or scripts.
### Usage
```
./Fragmented cmd --shader PATH [--load-image PATH]
~ Fragmented CLI ~
-================-
Usage:
./Fragmented <command> <args...>
Commands:
help
| Shows this help text.
apply --shader PATH [--load-image PATH]
| Applies a shader file.
--shader PATH The path to the shader
--output PATH Where to write the resulting image to.
@ -113,8 +134,6 @@ You can run Fragmented from the commandline or scripts.
```
You can also run `./Fragmented cmd help` to show the help message.
### Batch Mode
Since version v8.0, you can pass a directory to `--load-image` and `--output`. This will process all images in the input directory and write the output to the output directory.
@ -124,11 +143,11 @@ Since version v8.0, you can pass a directory to `--load-image` and `--output`. T
#### Examples
```
./Fragmented cmd --shader ./examples/oklab.gdshader --output ./output.png
./Fragmented apply --shader ./examples/oklab.gdshader --output ./output.png
```
```
./Fragmented cmd --shader ./examples/oklab.gdshader --load-image ~/Pictures/test.png --output ./output.png
./Fragmented apply --shader ./examples/oklab.gdshader --load-image ~/Pictures/test.png --output ./output.png
```
## Known Issues

View file

@ -3,12 +3,12 @@
set -e
function log {
echo -e "\033[1;36m* $@\033[0m"
echo -e "\033[1;36m***** $@ *****\033[0m"
}
log
log " "
log "Fragmented - Godot Build Template Builder"
log
log " "
cd $(dirname $0)
log Switched to $(pwd)

30
dist.sh Executable file
View file

@ -0,0 +1,30 @@
set -e
function log {
echo -e "\033[1;36m***** $@ *****\033[0m"
}
mkdir -p dist
log Building application
VERSION="$(godot --headless --no-header -s tools/get_version.gd)"
godot --headless --export-release "Linux/X11" "dist/Fragmented-${VERSION}.x86_64"
log Packing shaderlib
ZIP_PATH_SHADERLIB=$(realpath "dist/Fragmented-${VERSION}_shaderlib.zip")
zip -r "${ZIP_PATH_SHADERLIB}" shaderlib/
log Packing project template
ZIP_PATH_PROJECT_TEMPLATE=$(realpath "dist/Fragmented-${VERSION}_project_template.zip")
rm -f "${ZIP_PATH_PROJECT_TEMPLATE}"
(
cd examples/
mv project.godot_ project.godot && trap "mv project.godot project.godot_" EXIT
zip -r "${ZIP_PATH_PROJECT_TEMPLATE}" *
)

0
dist/.gitkeep vendored
View file

3
examples/0_empty.tscn Normal file
View file

@ -0,0 +1,3 @@
[gd_scene format=3 uid="uid://db2rhq8rwv5wo"]
[node name="Node" type="Node"]

View file

@ -1,7 +1,7 @@
shader_type canvas_item;
//!load ./images/swamp.jpg
#include "res://shaderlib/blur.gdshaderinc"
#include "./shaderlib/blur.gdshaderinc"
void fragment() {
COLOR = gaussian_blur(TEXTURE, UV, 48, 24.0);

View file

@ -1,7 +1,7 @@
shader_type canvas_item;
#include "res://shaderlib/hsv.gdshaderinc"
#include "res://shaderlib/pixelate.gdshaderinc"
#include "./shaderlib/hsv.gdshaderinc"
#include "./shaderlib/pixelate.gdshaderinc"
//!load ./images/swamp.jpg

View file

@ -2,7 +2,7 @@ shader_type canvas_item;
//!load ./images/noisy.png
#include "res://shaderlib/denoise.gdshaderinc"
#include "./shaderlib/denoise.gdshaderinc"
void fragment() {
COLOR = smart_denoise(TEXTURE, UV, 12.0, 1.0, .12);

View file

@ -2,8 +2,8 @@ shader_type canvas_item;
//!load ./images/mountain.jpg
#include "res://shaderlib/kuwahara.gdshaderinc"
#include "res://shaderlib/hsv.gdshaderinc"
#include "./shaderlib/kuwahara.gdshaderinc"
#include "./shaderlib/hsv.gdshaderinc"
void fragment() {
// Kuwahara

View file

@ -3,6 +3,8 @@ shader_type canvas_item;
//!steps 9
//!load ./images/swamp.jpg
uniform int STEP;
const float strength = 0.01;
void fragment() {

View file

@ -1,6 +1,6 @@
shader_type canvas_item;
#include "res://shaderlib/oklab.gdshaderinc"
#include "./shaderlib/oklab.gdshaderinc"
//!load ./images/swamp.jpg

View file

@ -1,7 +1,7 @@
shader_type canvas_item;
#include "res://shaderlib/place_texture.gdshaderinc"
#include "res://shaderlib/common.gdshaderinc"
#include "./shaderlib/place_texture.gdshaderinc"
#include "./shaderlib/common.gdshaderinc"
//!load ./images/swamp.jpg
//!load+ img2 ./images/grass.png

16
examples/project.godot_ Normal file
View file

@ -0,0 +1,16 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="Fragmented Project"
config/features=PackedStringArray("4.3", "Forward Plus")
run/main_scene="res://0_empty.tscn"

1
examples/shaderlib Symbolic link
View file

@ -0,0 +1 @@
../shaderlib

View file

@ -8,7 +8,7 @@ dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter="screenshot.png, examples/*"
exclude_filter="screenshot.png, examples/*, shaderlib/*, tools/*, build-template/*"
export_path="dist/Fragmented.x86_64"
encryption_include_filters=""
encryption_exclude_filters=""

View file

@ -11,11 +11,11 @@ config_version=5
[application]
config/name="Fragmented"
config/version="v8.2"
run/main_scene="res://scenes/main.tscn"
config/version="v9.0"
run/main_scene="res://src/scenes/main.tscn"
config/features=PackedStringArray("4.3", "Mobile")
run/low_processor_mode=true
config/icon="res://assets/icon.png"
config/icon="res://src/assets/icon.png"
[autoload]
@ -24,8 +24,8 @@ ShaderDirectiveParser="*res://src/ShaderDirectiveParser.gd"
[display]
window/size/viewport_width=704
window/size/viewport_height=704
window/size/viewport_width=640
window/size/viewport_height=672
window/energy_saving/keep_screen_on=false
window/subwindows/embed_subwindows=false
window/vsync/vsync_mode=0

View file

@ -1,46 +0,0 @@
[gd_scene load_steps=10 format=3 uid="uid://bjah7k4bxo044"]
[ext_resource type="Script" path="res://src/Main.gd" id="1_2625y"]
[ext_resource type="Script" path="res://src/ImageCompositor.gd" id="2_4thch"]
[ext_resource type="Shader" path="res://src/shader/ivd_outline.gdshader" id="3_6xihe"]
[ext_resource type="Script" path="res://src/ImageViewportDisplay.gd" id="3_n4itb"]
[ext_resource type="Script" path="res://src/UIWindow.gd" id="6_8k0ha"]
[ext_resource type="PackedScene" uid="uid://btgits2mfup0h" path="res://scenes/ui_container.tscn" id="7_5ci0e"]
[ext_resource type="Script" path="res://src/Camera.gd" id="8_mls06"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_y2ea0"]
shader = ExtResource("3_6xihe")
shader_parameter/zoom_level = Vector2(1, 1)
[sub_resource type="ViewportTexture" id="ViewportTexture_lct1c"]
viewport_path = NodePath("Compositor")
[node name="Main" type="Node2D"]
script = ExtResource("1_2625y")
[node name="Compositor" type="SubViewport" parent="."]
unique_name_in_owner = true
script = ExtResource("2_4thch")
[node name="ImageViewportDisplay" type="Sprite2D" parent="."]
unique_name_in_owner = true
material = SubResource("ShaderMaterial_y2ea0")
texture = SubResource("ViewportTexture_lct1c")
script = ExtResource("3_n4itb")
[node name="Camera" type="Camera2D" parent="."]
unique_name_in_owner = true
script = ExtResource("8_mls06")
[node name="EditorWindow" type="Window" parent="."]
unique_name_in_owner = true
disable_3d = true
position = Vector2i(48, 36)
size = Vector2i(704, 704)
visible = false
script = ExtResource("6_8k0ha")
[node name="UserInterfaceContainer" parent="EditorWindow" instance=ExtResource("7_5ci0e")]
unique_name_in_owner = true
[connection signal="close_requested" from="EditorWindow" to="EditorWindow" method="_on_close_requested"]

View file

@ -1,204 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://btgits2mfup0h"]
[ext_resource type="Script" path="res://src/UIAppVersion.gd" id="1_5qvnb"]
[ext_resource type="Script" path="res://src/Editor.gd" id="2_haub5"]
[node name="UserInterfaceContainer" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="AppName" type="Label" parent="."]
layout_mode = 0
offset_left = 24.0
offset_top = 16.0
offset_right = 208.0
offset_bottom = 48.0
theme_override_font_sizes/font_size = 20
text = "Fragmented"
vertical_alignment = 2
[node name="AppVersion" type="Label" parent="."]
layout_mode = 0
offset_left = 152.0
offset_top = 17.0
offset_right = 208.0
offset_bottom = 47.0
theme_override_font_sizes/font_size = 14
text = "v0
"
vertical_alignment = 2
script = ExtResource("1_5qvnb")
[node name="Editor" type="Control" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 24.0
offset_top = 64.0
offset_right = -24.0
offset_bottom = -16.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("2_haub5")
[node name="OpenShaderDialog" type="FileDialog" parent="Editor"]
unique_name_in_owner = true
auto_translate_mode = 1
title = "Load Shader"
size = Vector2i(521, 175)
ok_button_text = "Open"
mode_overrides_title = false
file_mode = 0
access = 2
filters = PackedStringArray("*.gdshader")
use_native_dialog = true
[node name="SaveShaderDialog" type="FileDialog" parent="Editor"]
unique_name_in_owner = true
auto_translate_mode = 1
title = "Save Shader"
size = Vector2i(661, 175)
ok_button_text = "Save"
mode_overrides_title = false
access = 2
filters = PackedStringArray("*.gdshader")
use_native_dialog = true
[node name="SaveImageDialog" type="FileDialog" parent="Editor"]
unique_name_in_owner = true
auto_translate_mode = 1
title = "Export Image"
size = Vector2i(661, 175)
ok_button_text = "Save"
mode_overrides_title = false
access = 2
filters = PackedStringArray("*.png")
use_native_dialog = true
[node name="CodeEdit" type="CodeEdit" parent="Editor"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = 48.0
grow_horizontal = 2
grow_vertical = 2
theme_override_font_sizes/font_size = 14
placeholder_text = "// Test"
wrap_mode = 1
minimap_draw = true
minimap_width = 40
caret_blink = true
draw_control_chars = true
draw_tabs = true
draw_spaces = true
line_length_guidelines = Array[int]([80])
gutters_draw_line_numbers = true
code_completion_enabled = true
indent_automatic = true
auto_brace_completion_enabled = true
auto_brace_completion_highlight_matching = true
[node name="NewShaderButton" type="Button" parent="Editor"]
layout_mode = 1
offset_right = 48.0
offset_bottom = 32.0
text = "New"
[node name="OpenShaderButton" type="Button" parent="Editor"]
layout_mode = 1
offset_left = 56.0
offset_right = 112.0
offset_bottom = 32.0
text = "Open"
[node name="SaveShaderButton" type="Button" parent="Editor"]
layout_mode = 1
offset_left = 120.0
offset_top = -1.0
offset_right = 176.0
offset_bottom = 31.0
text = "Save"
[node name="SaveShaderAsButton" type="Button" parent="Editor"]
layout_mode = 1
offset_left = 184.0
offset_right = 263.0
offset_bottom = 32.0
text = "Save As"
[node name="SaveImageButton" type="Button" parent="Editor"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -72.0
offset_bottom = 32.0
grow_horizontal = 0
disabled = true
text = "Export"
[node name="FitImageButton" type="Button" parent="Editor"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -128.0
offset_right = -80.0
offset_bottom = 32.0
grow_horizontal = 0
text = "Fit"
[node name="ApplyShaderButton" type="Button" parent="Editor"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -232.0
offset_right = -136.0
offset_bottom = 31.0
grow_horizontal = 0
text = "Apply (F5)"
[node name="StatusIndicator" type="TextureButton" parent="Editor"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -76.0
offset_top = 56.0
offset_right = -56.0
offset_bottom = 76.0
grow_horizontal = 0
disabled = true
ignore_texture_size = true
stretch_mode = 0
[node name="ErrorMessageDialog" type="AcceptDialog" parent="Editor"]
unique_name_in_owner = true
title = "Status"
initial_position = 2
size = Vector2i(256, 128)
popup_window = true
ok_button_text = "Close"
[connection signal="file_selected" from="Editor/OpenShaderDialog" to="Editor" method="_on_open_shader_dialog_file_selected"]
[connection signal="file_selected" from="Editor/SaveShaderDialog" to="Editor" method="_on_save_shader_dialog_file_selected"]
[connection signal="file_selected" from="Editor/SaveImageDialog" to="Editor" method="_on_save_image_dialog_file_selected"]
[connection signal="code_completion_requested" from="Editor/CodeEdit" to="Editor" method="_on_code_edit_code_completion_requested"]
[connection signal="pressed" from="Editor/NewShaderButton" to="Editor" method="_on_new_shader_button_pressed"]
[connection signal="pressed" from="Editor/OpenShaderButton" to="Editor" method="_on_open_shader_button_pressed"]
[connection signal="pressed" from="Editor/SaveShaderButton" to="Editor" method="_on_save_shader_button_pressed"]
[connection signal="pressed" from="Editor/SaveShaderAsButton" to="Editor" method="_on_save_shader_as_button_pressed"]
[connection signal="pressed" from="Editor/SaveImageButton" to="Editor" method="_on_save_image_button_pressed"]
[connection signal="pressed" from="Editor/FitImageButton" to="Editor" method="_on_fit_image_button_pressed"]
[connection signal="pressed" from="Editor/ApplyShaderButton" to="Editor" method="_on_apply_shader_button_pressed"]
[connection signal="pressed" from="Editor/StatusIndicator" to="Editor" method="_on_status_indicator_pressed"]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 KiB

After

Width:  |  Height:  |  Size: 528 KiB

Before After
Before After

View file

@ -9,7 +9,7 @@
The fourth value is always alpha.
*/
#include "res://shaderlib/common.gdshaderinc"
#include "./common.gdshaderinc"
vec4 rgb2oklab(vec4 c) {
float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b;

View file

@ -1,9 +1,9 @@
extends Camera2D
var drag = false
@onready var image_viewport_display = %ImageViewportDisplay
var drag = false
func _input(event):
if event.is_action_pressed("zoom_out"):
zoom_out()
@ -20,9 +20,11 @@ func fit_image():
if Filesystem.original_image != null:
var image_size = Filesystem.original_image.get_size()
var viewport_size = get_viewport_rect().size
var zoomf = viewport_size.x / image_size.x / 1.1
if zoomf * image_size.y > viewport_size.y:
zoomf = viewport_size.y / image_size.y / 1.1
var zoomf = 1.0
if viewport_size.x / image_size.x * image_size.y > viewport_size.y:
zoomf = viewport_size.y / image_size.y / 1.25
else:
zoomf = viewport_size.x / image_size.x / 1.2
self.zoom = Vector2(zoomf, zoomf)
self.global_position = Vector2(0, 0)

View file

@ -1,307 +0,0 @@
extends Control
@onready var code_editor = %CodeEdit
@onready var open_shader_dialog = %OpenShaderDialog
@onready var save_shader_dialog = %SaveShaderDialog
@onready var ui_control_filesave = %SaveImageDialog
@onready var save_image_button = %SaveImageButton
@onready var status_indicator = %StatusIndicator
@onready var error_msg_dialog = %ErrorMessageDialog
@onready var main = get_tree().root.get_node("Main")
@onready var compositor = main.get_node("%Compositor")
@onready var camera = main.get_node("%Camera")
#
var status_okay_texture: CompressedTexture2D = preload("uid://m1omb6g45vst")
var status_error_texture: CompressedTexture2D = preload("uid://04iv1gogpuhu")
# # # # # # # # # # #
# GDShader keywords #
# https://github.com/godotengine/godot/blob/e96ad5af98547df71b50c4c4695ac348638113e0/servers/rendering/shader_language.cpp
# https://github.com/godotengine/godot/blob/e96ad5af98547df71b50c4c4695ac348638113e0/servers/rendering/shader_types.cpp
#
const gdshader_boolean_values = [
"true", "false"
]
const gdshader_datatypes = [
"void",
"bool", "bvec2", "bvec3", "bvec4",
"int", "ivec2", "ivec3", "ivec4",
"uint", "uvec2", "uvec3", "uvec4",
"float", "vec2", "vec3", "vec4",
"mat2", "mat3", "mat4",
"sampler2D", "isampler2D", "usampler2D",
"sampler2DArray", "isampler2DArray", "usampler2DArray",
]
const gdshader_precision_modifiers = [
"lowp", "mediump", "heighp"
]
const gdshader_global_space_keywords = [
"uniform", "group_uniforms", "varying", "const",
"struct", "shader_type", "render_mode"
]
const gdshader_uniform_qualifiers = [
"instance", "global"
]
const gdshader_block_keywords = [
"if", "else",
"for", "while", "do",
"switch", "case",
"default", "break", "continue",
"return", "discard"
]
const gdshader_function_specifier_keywords = [
"in", "out", "inout"
]
const gdshader_hints = [
"source_color", "hint_range", "instance_index"
]
const gdshader_sampler_hints = [
"hint_normal",
"hint_default_white", "hint_default_black", "hint_default_transparent",
"hint_anisotropy",
"hint_roughness_r", "hint_roughness_g", "hint_roughness_b", "hint_roughness_a",
"hint_roughness_normal", "hint_roughness_gray",
"hint_screen_texture", "hint_normal_roughness_texture",
"hint_depth_texture",
"filter_nearest", "filter_linear",
"filter_nearest_mipmap", "filter_linear_mipmap",
"filter_nearest_mipmap_anisotropic", "filter_linear_mipmap_anisotropic",
"repeat_enable", "repeat_disable"
]
const gdshader_builtin_functions = [
"radians", "degrees",
"sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh",
"asinh", "acosh", "atanh",
"pow", "exp", "exp2", "log", "log2", "sqrt", "inversesqrt",
"abs", "sign", "floor", "trunc", "round", "roundEven", "ceil", "fract",
"mod", "modf", "min", "max", "clamp",
"mix", "step", "smoothstep",
"isnan", "isinf",
"floatBitsToInt", "floatBitsToUint", "intBitsToFloat", "uintBitsToFloat",
"length", "distance",
"dot", "cross",
"normalize", "reflect", "refract", "faceforward",
"matrixCompMult", "outerProduct", "transpose",
"determinant", "inverse",
"lessThan", "greaterThan", "lessThanEqual", "greaterThanEqual",
"equal", "notEqual",
"any", "all", "not",
"textureSize", "texture", "textureProj", "textureLod", "texelFetch",
"textureProjLod", "textureGrad", "textureProjGrad", "textureGather",
"textureQueryLod", "textureQueryLevels",
"dFdx", "dFdxCoarse", "dFdxFine", "dFdy", "dFdyCoarse", "dFdyFine",
"fwidth", "fwidthCoarse", "fwidthFine"
]
const gdshader_sub_functions = [
"length", "fma",
"packHalf2x16", "packUnorm2x16", "packSnorm2x16", "packUnorm4x8", "packSnorm4x8",
"unpackHalf2x16", "unpackUnorm2x16", "unpackSnorm2x16", "unpackUnorm4x8", "unpackSnorm4x8",
"bitfieldExtract", "bitfieldInsert", "bitfieldReverse", "bitCount",
"findLSB", "findMSB",
"umulExtended", "imulExtended",
"uaddCarry", "usubBorrow",
"ldexp", "frexp"
]
const gdshader_builtins = [
"TIME", "PI", "TAU", "E",
"VERTEX",
"UV",
"COLOR",
"POINT_SIZE",
"AT_LIGHT_PASS",
"TEXTURE_PIXEL_SIZE",
"SHADOW_VERTEX", "LIGHT_VERTEX",
"FRAGCOORD",
"NORMAL", "NORMAL_MAP", "NORMAL_MAP_DEPTH",
"TEXTURE",
"POINT_COORD",
"SPECULAR_SHININESS"
]
const gdshader_preprocessor = [
"define", "undef", "include", "pragma",
"if", "elif", "ifdef", "ifndef", "else", "endif"
]
# shaderlib
var shaderlib_regex = {} # auto-generated
const shaderlib_functions = {
"blur": ["gaussian_blur"],
"common": ["alpha_blend", "cbrt"],
"denoise": ["smart_denoise"],
"hsv": ["rgb2hsv", "hsv2rgb",],
"kuwahara": ["kuwahara",],
"oklab": ["oklab2rgb", "rgb2oklab", "oklab2oklch", "oklch2oklab"],
"pixelate": ["pixelate"],
"place_texture": ["place_texture"],
}
#
# configure Highlighter
#
class ShaderSyntaxHighlighter extends CodeHighlighter:
func _init():
add_color_region("//", "", Color.WEB_GRAY, true)
add_color_region("/*", "*/", Color.WEB_GRAY, false)
function_color = Color.INDIAN_RED
for k in gdshader_boolean_values:
keyword_colors[k] = Color.INDIAN_RED
for k in ( gdshader_datatypes
+ gdshader_hints
+ gdshader_sampler_hints
+ gdshader_global_space_keywords
+ gdshader_function_specifier_keywords
+ gdshader_precision_modifiers
+ gdshader_uniform_qualifiers):
keyword_colors[k] = Color.ORCHID;
for k in gdshader_block_keywords:
keyword_colors[k] = Color.CORAL
for k in gdshader_builtins:
keyword_colors[k] = Color.DARK_TURQUOISE
member_variable_color = Color.LIGHT_BLUE
number_color = Color.AQUA
symbol_color = Color.GRAY
#
# and code completion
#
func _on_code_edit_code_completion_requested():
for k in gdshader_boolean_values:
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, k, k, Color.INDIAN_RED)
for k in ( gdshader_datatypes
+ gdshader_hints
+ gdshader_sampler_hints
+ gdshader_global_space_keywords
+ gdshader_function_specifier_keywords
+ gdshader_precision_modifiers
+ gdshader_uniform_qualifiers):
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_CLASS, k, k, Color.ORCHID)
for k in gdshader_block_keywords:
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, k, k, Color.CORAL)
for k in gdshader_builtins:
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_CONSTANT, k, k, Color.DARK_TURQUOISE)
for k in gdshader_builtin_functions + gdshader_sub_functions:
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_FUNCTION, k, k+"(", Color.INDIAN_RED)
for k in gdshader_preprocessor:
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, "#" + k, k)
# shaderlib #
var shader_code = code_editor.text
for key in shaderlib_regex:
if shaderlib_regex[key].search(shader_code) != null:
if key in shaderlib_functions:
for k in shaderlib_functions[key]:
code_editor.code_completion_prefixes.append(k)
code_editor.add_code_completion_option(CodeEdit.KIND_FUNCTION, k, k+"(", Color.INDIAN_RED)
# # # # # # #
code_editor.update_code_completion_options(true)
#
# # # # # # # # # # # #
func _ready():
# generate regexes
for k in shaderlib_functions:
shaderlib_regex[k] = RegEx.create_from_string(
r'\s*\#include\s+\"res\:\/\/shaderlib\/' + k + r'\.gdshaderinc\"')
#
code_editor.code_completion_enabled = true
code_editor.syntax_highlighter = ShaderSyntaxHighlighter.new()
self.update_code_edit()
func _input(event):
if event.is_action_pressed("apply_shader"):
_on_apply_shader_button_pressed()
elif event.is_action_pressed("save_shader"):
accept_event() # Event is now handled.
_on_save_shader_button_pressed()
func update_code_edit():
code_editor.text = Filesystem.shader_code
enum Status {OKAY, ERROR, UNKNOWN = -1}
func update_status(status: Status, msg: String = ""):
error_msg_dialog.dialog_text = msg
error_msg_dialog.reset_size()
if status == Status.OKAY:
status_indicator.texture_normal = status_okay_texture
elif status == Status.ERROR:
status_indicator.texture_normal = status_error_texture
else:
status_indicator.texture_normal = null
if msg == "":
status_indicator.disabled = true
else:
status_indicator.disabled = false
#
func _on_new_shader_button_pressed():
main.update_title()
Filesystem.reset()
self.update_code_edit()
compositor.update()
update_status(Status.UNKNOWN)
func _on_open_shader_button_pressed():
open_shader_dialog.show()
func _on_save_shader_button_pressed():
Filesystem.shader_code = code_editor.text
if Filesystem.last_shader_savepath == "":
_on_save_shader_as_button_pressed()
else:
_on_save_shader_dialog_file_selected(Filesystem.last_shader_savepath)
func _on_save_shader_as_button_pressed() -> void:
Filesystem.shader_code = code_editor.text
if Filesystem.last_shader_savepath == "":
save_shader_dialog.current_file = "filter.gdshader"
else:
save_shader_dialog.current_path = Filesystem.last_shader_savepath
save_shader_dialog.show()
func _on_fit_image_button_pressed():
camera.fit_image()
func _on_apply_shader_button_pressed():
save_image_button.disabled = true
Filesystem.shader_code = code_editor.text
var errors = await compositor.update()
if len(errors) > 0:
update_status(Status.ERROR, "\n".join(errors))
else:
update_status(Status.OKAY)
save_image_button.disabled = false
func _on_save_image_button_pressed():
if Filesystem.result != null:
ui_control_filesave.current_path = Filesystem.last_image_savepath
ui_control_filesave.show()
#
func _on_open_shader_dialog_file_selected(path: String):
Filesystem.load_shader(path)
main.update_title(path.split("/")[-1])
self.update_code_edit()
self._on_apply_shader_button_pressed()
func _on_save_shader_dialog_file_selected(path):
Filesystem.save_shader(path)
main.update_title(path.split("/")[-1])
func _on_save_image_dialog_file_selected(path):
Filesystem.save_result(path)
#
func _on_status_indicator_pressed() -> void:
error_msg_dialog.show()

View file

@ -1,25 +1,31 @@
extends Node
@onready var template_shader: Shader = load("res://src/shader/template.gdshader")
@onready var shader_code: String = template_shader.code
var cwd = "."
var shader_path = "":
get():
return shader_path
set(v):
var old = shader_path
shader_path = v
if "/" in v: # update current working directory
cwd = v.substr(0, v.rfind("/"))
if old != shader_path:
store_last_opened_file()
var shader: Shader:
get():
print("Load ", shader_path)
return load(shader_path)
var original_image: ImageTexture
var additional_images: Dictionary
var result: Image
var cwd = "."
var last_image_savepath = ""
var last_shader_savepath = ""
var last_original_image_path = ""
func reset():
self.shader_code = self.template_shader.code
self.last_image_savepath = ""
self.last_shader_savepath = ""
self.last_original_image_path = ""
self.original_image = null
self.result = null
func get_absolute_path(p: String) -> String:
# this only works on Linux!
if !p.begins_with("/"):
@ -27,14 +33,14 @@ func get_absolute_path(p: String) -> String:
return p
func load_original_image(path: String) -> String: # returns an error message
print("Load ", path)
var img = Image.new()
var err = img.load(path)
if err == OK:
original_image = ImageTexture.create_from_image(img)
if path != self.last_original_image_path:
self.last_original_image_path = path
if self.last_image_savepath == "":
if self.last_image_savepath == "" or path != self.last_original_image_path:
self.last_image_savepath = path
self.last_original_image_path = path
return ""
return error_string(err) + " " + path
@ -42,6 +48,7 @@ func clear_additional_images():
additional_images.clear()
func load_additional_image(key: String, path: String) -> String: # returns Error Message String
print("Load ", path)
var img = Image.new()
var err = img.load(path)
if err == OK:
@ -58,33 +65,13 @@ func save_result(path: String):
else:
self.last_image_savepath = path
func load_shader(path: String):
print("Load ", path)
var file = FileAccess.open(path, FileAccess.READ)
if file != null:
self.shader_code = file.get_as_text()
if "/" in path: # update current working directory
self.cwd = path.substr(0, path.rfind("/"))
self.last_shader_savepath = path
store_last_opened_file()
func save_shader(path: String):
print("Save ", path)
var file = FileAccess.open(path, FileAccess.WRITE)
file.store_string(self.shader_code)
file.flush()
if "/" in path: # update current working directory
self.cwd = path.substr(0, path.rfind("/"))
self.last_shader_savepath = path
store_last_opened_file()
func store_last_opened_file():
var f = FileAccess.open("user://last_opened", FileAccess.WRITE)
if f != null:
f.store_pascal_string(last_shader_savepath)
f.store_pascal_string(shader_path)
f.flush()
func remember_last_opened_file():
var f = FileAccess.open("user://last_opened", FileAccess.READ)
if f != null:
last_shader_savepath = f.get_pascal_string()
shader_path = f.get_pascal_string()

View file

@ -37,17 +37,19 @@ func validate_shader_compilation(shader: Shader) -> bool:
# test if uniform list is empty -> if it is empty, the shader compilation failed
return len(shader.get_shader_uniform_list()) > 0
func inject_step_uniform(shader_code: String) -> Shader:
var shader = Shader.new()
# this should run after validate_shader_compilation()
var fragment_function_match = _fragment_function_regex.search(shader_code)
shader.code = shader_code.insert(fragment_function_match.get_start(), "\nuniform int STEP;")
return shader
func shader_has_step_uniform(shader: Shader) -> bool:
for u in shader.get_shader_uniform_list():
if u["name"] == "STEP" && u["type"] == 2:
return true
return false
func update(overwrite_image_path: String = "") -> Array: # returns error messages (strings)
# inject STEP uniform & get number of steps
var shader: Shader = inject_step_uniform(Filesystem.shader_code)
var shader = Filesystem.shader # read from disk
if shader == null:
return ["No shader opened!"]
# get number of steps & check if code has STEP uniform
var steps: int = ShaderDirectiveParser.parse_steps_directive(shader.code)
var has_step_uniform: bool = shader_has_step_uniform(shader)
# validate shader
if not validate_shader_compilation(shader):
return ["Shader compilation failed!"]
@ -94,6 +96,7 @@ func update(overwrite_image_path: String = "") -> Array: # returns error message
image_sprite.material = mat
# iterate n times
for i in range(steps):
if has_step_uniform:
# set STEP param
mat.set_shader_parameter("STEP", i)
# Get viewport texture

View file

@ -4,14 +4,17 @@ const BATCH_MODE_SUPPORTED_EXTS = [
".bmp", ".dds", ".exr", ".hdr", ".jpeg", ".jpg", ".ktx", ".png", ".svg", ".webp"
]
@onready var editor_window = %EditorWindow
@onready var ui_container = %UserInterfaceContainer
@onready var app_name = ProjectSettings.get_setting("application/config/name")
func show_help():
print(
"Usage:\n\n",
"./Fragmented cmd --shader PATH [--load-image PATH]\n\n",
"./Fragmented <command> <args...>\n\n",
"Commands:\n\n",
" help\n\n",
" | Shows this help text.\n\n",
" apply --shader PATH [--load-image PATH]\n\n",
" | Applies a shader file.\n\n",
" --shader PATH The path to the shader\n",
" --output PATH Where to write the resulting image to.\n",
" In batch mode, this must be a folder.\n",
@ -41,9 +44,10 @@ func cli_handle_errors(errors: Array) -> int:
printerr(e)
return n_errors
func _ready():
var args = OS.get_cmdline_args()
if "cmd" in args: # commandline interface
func cli(args: PackedStringArray):
print(
"~ Fragmented CLI ~\n",
"-================-\n")
if "help" in args:
show_help()
get_tree().quit(1)
@ -67,7 +71,7 @@ func _ready():
else:
batch_mode = true
#
Filesystem.load_shader(kwargs["--shader"])
Filesystem.shader_path = kwargs["--shader"]
#
if batch_mode:
var in_dir_path = load_image_dir.get_current_dir()
@ -100,24 +104,23 @@ func _ready():
get_tree().quit(0)
else:
get_tree().quit(1)
else:
func prepare_gui():
update_title()
# position windows
get_window().position = Vector2i(
editor_window.position.x + editor_window.size.x + 50,
editor_window.position.y)
get_window().min_size = Vector2i(400, 400)
editor_window.min_size = Vector2i(560, 400)
editor_window.show()
# Load last opened file
Filesystem.remember_last_opened_file()
if Filesystem.last_shader_savepath != "":
ui_container.get_node("Editor")._on_open_shader_dialog_file_selected(Filesystem.last_shader_savepath)
%MainUI._on_apply_shader_button_pressed()
func _ready():
var args = OS.get_cmdline_args()
if len(args) > 0 and args[0] in ["apply", "help"]:
# use the commandline interface
cli(args)
else:
prepare_gui()
func update_title(current_file: String = ""):
if current_file == "":
get_window().title = app_name + " - Viewer"
editor_window.title = app_name + " - Editor"
else:
get_window().title = current_file + " - " + app_name + " - Viewer"
editor_window.title = current_file + " - " + app_name + " - Editor"

99
src/MainUI.gd Normal file
View file

@ -0,0 +1,99 @@
extends Control
@onready var open_shader_dialog = %OpenShaderDialog
@onready var save_image_dialog = %SaveImageDialog
@onready var open_shader_button = %OpenShaderButton
@onready var save_image_button = %SaveImageButton
@onready var fit_image_button = %FitImageButton
@onready var apply_shader_button = %ApplyShaderButton
@onready var status_indicator = %StatusIndicator
@onready var error_msg_dialog = %ErrorMessageDialog
@onready var main = get_tree().root.get_node("Main")
@onready var compositor = %Compositor
@onready var camera = %Camera
var status_okay_texture: CompressedTexture2D = preload("uid://m1omb6g45vst")
var status_error_texture: CompressedTexture2D = preload("uid://04iv1gogpuhu")
enum Status {OKAY, ERROR, UNKNOWN = -1}
#
func _input(event):
if event.is_action_pressed("apply_shader"):
_on_apply_shader_button_pressed()
elif event.is_action_pressed("save_shader"):
accept_event() # Event is now handled.
#
func set_buttons_disabled(disabled: bool):
for b in [open_shader_button, save_image_button, fit_image_button, apply_shader_button, status_indicator]:
b.disabled = disabled
func _on_open_shader_button_pressed():
set_buttons_disabled(true)
open_shader_dialog.show()
func _on_fit_image_button_pressed():
camera.fit_image()
func _on_apply_shader_button_pressed():
set_buttons_disabled(true)
var errors = await compositor.update()
set_buttons_disabled(false)
if len(errors) > 0:
update_status(Status.ERROR, "\n".join(errors))
else:
update_status(Status.OKAY)
status_indicator.disabled = true
func _on_save_image_button_pressed():
if Filesystem.result != null:
set_buttons_disabled(true)
save_image_dialog.current_path = Filesystem.last_image_savepath
save_image_dialog.show()
#
func _on_open_shader_dialog_file_selected(path: String):
Filesystem.shader_path = path
main.update_title(path.split("/")[-1])
self._on_apply_shader_button_pressed()
func _on_open_shader_dialog_canceled() -> void:
set_buttons_disabled(false)
func _on_open_shader_dialog_confirmed() -> void:
set_buttons_disabled(false)
func _on_save_image_dialog_file_selected(path):
Filesystem.save_result(path)
func _on_save_image_dialog_canceled() -> void:
set_buttons_disabled(false)
func _on_save_image_dialog_confirmed() -> void:
set_buttons_disabled(false)
#
func update_status(status: Status, msg: String = ""):
error_msg_dialog.dialog_text = msg
error_msg_dialog.reset_size()
if status == Status.OKAY:
status_indicator.texture_normal = status_okay_texture
elif status == Status.ERROR:
status_indicator.texture_normal = status_error_texture
else:
status_indicator.texture_normal = null
if msg == "":
status_indicator.disabled = true
else:
status_indicator.disabled = false
func _on_status_indicator_pressed() -> void:
error_msg_dialog.show()

View file

@ -1,4 +0,0 @@
extends Label
func _ready():
text = ProjectSettings.get_setting("application/config/version")

View file

@ -1,4 +0,0 @@
extends Window
func _on_close_requested() -> void:
get_tree().quit()

8
src/VersionLabel.gd Normal file
View file

@ -0,0 +1,8 @@
extends Label
func _ready():
text = ProjectSettings.get_setting("application/config/name") \
+ " " \
+ ProjectSettings.get_setting("application/config/version") \
+ " | Godot " \
+ Engine.get_version_info()["string"]

View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before After
Before After

View file

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://04iv1gogpuhu"
path="res://.godot/imported/error.svg-75fe5f417585e01e99de8885f1f45c3b.ctex"
path="res://.godot/imported/error.svg-28fb29635cf59d39cabf7052619f602f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/error.svg"
dest_files=["res://.godot/imported/error.svg-75fe5f417585e01e99de8885f1f45c3b.ctex"]
source_file="res://src/assets/error.svg"
dest_files=["res://.godot/imported/error.svg-28fb29635cf59d39cabf7052619f602f.ctex"]
[params]

View file

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Before After
Before After

View file

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://kqwc4avs2xdp"
path="res://.godot/imported/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.ctex"
path="res://.godot/imported/icon.png-d8298ab6eda392a806be6bb7eec65b9c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icon.png"
dest_files=["res://.godot/imported/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.ctex"]
source_file="res://src/assets/icon.png"
dest_files=["res://.godot/imported/icon.png-d8298ab6eda392a806be6bb7eec65b9c.ctex"]
[params]

View file

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

View file

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://m1omb6g45vst"
path="res://.godot/imported/okay.svg-7f6df15523471a86f27b6817e9528a4e.ctex"
path="res://.godot/imported/okay.svg-de66a022ef37753b085371b7c60aefd1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/okay.svg"
dest_files=["res://.godot/imported/okay.svg-7f6df15523471a86f27b6817e9528a4e.ctex"]
source_file="res://src/assets/okay.svg"
dest_files=["res://.godot/imported/okay.svg-de66a022ef37753b085371b7c60aefd1.ctex"]
[params]

174
src/scenes/main.tscn Normal file
View file

@ -0,0 +1,174 @@
[gd_scene load_steps=12 format=3 uid="uid://bjah7k4bxo044"]
[ext_resource type="Script" uid="uid://n6vumf2emghl" path="res://src/Main.gd" id="1_64y3g"]
[ext_resource type="Script" path="res://src/ImageCompositor.gd" id="2_4ykh7"]
[ext_resource type="Shader" uid="uid://on6oju7dt0dj" path="res://src/shader/ivd_outline.gdshader" id="3_0fllm"]
[ext_resource type="Script" uid="uid://c51lc0hv2d7uq" path="res://src/ImageViewportDisplay.gd" id="4_pbpx2"]
[ext_resource type="Script" uid="uid://t71vu44i5cr5" path="res://src/Camera.gd" id="5_hkdq6"]
[ext_resource type="Theme" uid="uid://cwqlns34rj3vx" path="res://src/theme.tres" id="6_rjp5f"]
[ext_resource type="Script" uid="uid://e5gf0r42elmx" path="res://src/MainUI.gd" id="7_5puhk"]
[ext_resource type="Script" uid="uid://b254xv4j2uexg" path="res://src/VersionLabel.gd" id="8_kod8x"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_y2ea0"]
shader = ExtResource("3_0fllm")
shader_parameter/zoom_level = Vector2(1, 1)
[sub_resource type="ViewportTexture" id="ViewportTexture_lct1c"]
viewport_path = NodePath("Compositor")
[sub_resource type="LabelSettings" id="LabelSettings_6o860"]
font_size = 12
font_color = Color(1, 1, 1, 0.509804)
shadow_color = Color(0, 0, 0, 1)
[node name="Main" type="Node2D"]
script = ExtResource("1_64y3g")
[node name="Compositor" type="SubViewport" parent="."]
unique_name_in_owner = true
script = ExtResource("2_4ykh7")
[node name="ImageViewportDisplay" type="Sprite2D" parent="."]
unique_name_in_owner = true
material = SubResource("ShaderMaterial_y2ea0")
texture = SubResource("ViewportTexture_lct1c")
script = ExtResource("4_pbpx2")
[node name="Camera" type="Camera2D" parent="."]
unique_name_in_owner = true
offset = Vector2(0, -64)
script = ExtResource("5_hkdq6")
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="MainUI" type="Control" parent="CanvasLayer"]
unique_name_in_owner = true
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
pivot_offset = Vector2(320, 320)
theme = ExtResource("6_rjp5f")
script = ExtResource("7_5puhk")
[node name="OpenShaderDialog" type="FileDialog" parent="CanvasLayer/MainUI"]
unique_name_in_owner = true
auto_translate_mode = 1
title = "Load Shader"
size = Vector2i(521, 175)
ok_button_text = "Open"
mode_overrides_title = false
file_mode = 0
access = 2
filters = PackedStringArray("*.gdshader")
use_native_dialog = true
[node name="SaveImageDialog" type="FileDialog" parent="CanvasLayer/MainUI"]
unique_name_in_owner = true
auto_translate_mode = 1
title = "Export Image"
size = Vector2i(661, 175)
ok_button_text = "Save"
mode_overrides_title = false
access = 2
filters = PackedStringArray("*.png")
use_native_dialog = true
[node name="ErrorMessageDialog" type="AcceptDialog" parent="CanvasLayer/MainUI"]
unique_name_in_owner = true
auto_translate_mode = 1
title = "Status"
initial_position = 2
size = Vector2i(256, 128)
popup_window = true
ok_button_text = "Close"
[node name="OpenShaderButton" type="Button" parent="CanvasLayer/MainUI"]
unique_name_in_owner = true
layout_mode = 1
offset_left = 16.0
offset_top = 16.0
offset_right = 128.0
offset_bottom = 48.0
text = "Open Shader"
[node name="SaveImageButton" type="Button" parent="CanvasLayer/MainUI"]
unique_name_in_owner = true
layout_mode = 1
offset_left = 144.0
offset_top = 16.0
offset_right = 216.0
offset_bottom = 48.0
disabled = true
text = "Export"
[node name="FitImageButton" type="Button" parent="CanvasLayer/MainUI"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -176.0
offset_top = 16.0
offset_right = -128.0
offset_bottom = 48.0
grow_horizontal = 0
text = "Fit"
[node name="ApplyShaderButton" type="Button" parent="CanvasLayer/MainUI"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -112.0
offset_top = 16.0
offset_right = -16.0
offset_bottom = 48.0
grow_horizontal = 0
text = "Apply (F5)"
[node name="StatusIndicator" type="TextureButton" parent="CanvasLayer/MainUI"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -220.0
offset_top = 21.0
offset_right = -196.0
offset_bottom = 45.0
grow_horizontal = 0
disabled = true
ignore_texture_size = true
stretch_mode = 0
[node name="VersionLabel" type="Label" parent="CanvasLayer/MainUI"]
layout_mode = 1
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 16.0
offset_top = -24.0
offset_right = -16.0
grow_horizontal = 2
grow_vertical = 0
label_settings = SubResource("LabelSettings_6o860")
horizontal_alignment = 1
vertical_alignment = 1
script = ExtResource("8_kod8x")
[connection signal="canceled" from="CanvasLayer/MainUI/OpenShaderDialog" to="CanvasLayer/MainUI" method="_on_open_shader_dialog_canceled"]
[connection signal="confirmed" from="CanvasLayer/MainUI/OpenShaderDialog" to="CanvasLayer/MainUI" method="_on_open_shader_dialog_confirmed"]
[connection signal="file_selected" from="CanvasLayer/MainUI/OpenShaderDialog" to="CanvasLayer/MainUI" method="_on_open_shader_dialog_file_selected"]
[connection signal="canceled" from="CanvasLayer/MainUI/SaveImageDialog" to="CanvasLayer/MainUI" method="_on_save_image_dialog_canceled"]
[connection signal="confirmed" from="CanvasLayer/MainUI/SaveImageDialog" to="CanvasLayer/MainUI" method="_on_save_image_dialog_confirmed"]
[connection signal="file_selected" from="CanvasLayer/MainUI/SaveImageDialog" to="CanvasLayer/MainUI" method="_on_save_image_dialog_file_selected"]
[connection signal="pressed" from="CanvasLayer/MainUI/OpenShaderButton" to="CanvasLayer/MainUI" method="_on_open_shader_button_pressed"]
[connection signal="pressed" from="CanvasLayer/MainUI/SaveImageButton" to="CanvasLayer/MainUI" method="_on_save_image_button_pressed"]
[connection signal="pressed" from="CanvasLayer/MainUI/FitImageButton" to="CanvasLayer/MainUI" method="_on_fit_image_button_pressed"]
[connection signal="pressed" from="CanvasLayer/MainUI/ApplyShaderButton" to="CanvasLayer/MainUI" method="_on_apply_shader_button_pressed"]
[connection signal="pressed" from="CanvasLayer/MainUI/StatusIndicator" to="CanvasLayer/MainUI" method="_on_status_indicator_pressed"]

View file

@ -1,7 +0,0 @@
shader_type canvas_item;
//!load /path/to/your/image
void fragment() {
// Called for every pixel the material is visible on.
}

57
src/theme.tres Normal file
View file

@ -0,0 +1,57 @@
[gd_resource type="Theme" load_steps=4 format=3 uid="uid://cwqlns34rj3vx"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bm5o2"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0.1, 0.1, 0.1, 0.3)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(1, 1, 1, 0.27451)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l0k8a"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0.225, 0.225, 0.225, 0.6)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(1, 1, 1, 0.784314)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1dkyv"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0.1, 0.1, 0.1, 0.6)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(1, 1, 1, 0.509804)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[resource]
Button/styles/disabled = SubResource("StyleBoxFlat_bm5o2")
Button/styles/hover = SubResource("StyleBoxFlat_l0k8a")
Button/styles/normal = SubResource("StyleBoxFlat_1dkyv")

7
tools/get_version.gd Normal file
View file

@ -0,0 +1,7 @@
extends SceneTree
# godot --headless --no-header -s tools/get_version.gd
func _init() -> void:
print(ProjectSettings.get_setting("application/config/version"))
quit(0)