Removed all editor functionality and improved new viewer-only UI #54

This commit is contained in:
ChaoticByte 2025-02-03 17:40:56 +01:00
parent e40ab18683
commit 9f5011aaac
No known key found for this signature in database
21 changed files with 391 additions and 642 deletions

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,34 @@
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_code: String:
get():
print("Load ", shader_path)
var file = FileAccess.open(shader_path, FileAccess.READ)
if file != null:
return file.get_as_text()
else: return ""
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 +36,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 +51,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 +68,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

@ -45,8 +45,11 @@ func inject_step_uniform(shader_code: String) -> Shader:
return shader
func update(overwrite_image_path: String = "") -> Array: # returns error messages (strings)
var original_shader_code = Filesystem.shader_code # read from disk
if original_shader_code == "":
return ["No shader loaded!"]
# inject STEP uniform & get number of steps
var shader: Shader = inject_step_uniform(Filesystem.shader_code)
var shader: Shader = inject_step_uniform(original_shader_code)
var steps: int = ShaderDirectiveParser.parse_steps_directive(shader.code)
# validate shader
if not validate_shader_compilation(shader):

View file

@ -4,8 +4,6 @@ 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 _ready():
@ -139,22 +137,12 @@ func cli_dump_shaderlib():
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 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"]

80
src/assets/error.svg Normal file
View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="30"
height="30"
viewBox="0 0 30 30"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="error.svg"
inkscape:export-filename="error.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="22.627417"
inkscape:cx="12.28598"
inkscape:cy="16.108776"
inkscape:window-width="1854"
inkscape:window-height="1011"
inkscape:window-x="66"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid9"
originx="0"
originy="0" />
</sodipodi:namedview>
<defs
id="defs2">
<rect
x="-19.109422"
y="15.119667"
width="23.697131"
height="28.949477"
id="rect4519" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="display:none;opacity:0.75;fill:#ff3815;fill-opacity:1;stroke:#ff3815;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 4,25 H 26 L 15,6 Z"
id="path11345"
sodipodi:nodetypes="cccc" />
<circle
style="opacity:0.75;fill:#ff3815;fill-opacity:1;stroke:none;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path15121"
cx="15"
cy="15"
r="13" />
<g
aria-label="!"
transform="matrix(0.80238423,0,0,0.80238423,20.811527,-5.9033718)"
id="text4517"
style="font-size:24px;white-space:pre;shape-inside:url(#rect4519);display:inline;fill:#ffffff;fill-opacity:1">
<path
d="m -5.6133052,29.164717 h -3.264 L -9.5630195,16.081288 h 4.6354286 z m -4.0045715,4.580571 q 0,-1.261715 0.6857143,-1.755429 0.6857143,-0.521142 1.6731429,-0.521142 0.96,0 1.6457143,0.521142 0.6857143,0.493714 0.6857143,1.755429 0,1.206857 -0.6857143,1.755429 -0.6857143,0.521142 -1.6457143,0.521142 -0.9874286,0 -1.6731429,-0.521142 -0.6857143,-0.548572 -0.6857143,-1.755429 z"
style="font-weight:bold;-inkscape-font-specification:'sans-serif Bold';text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke-width:1.14286"
id="path4674" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://04iv1gogpuhu"
path="res://.godot/imported/error.svg-28fb29635cf59d39cabf7052619f602f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://src/assets/error.svg"
dest_files=["res://.godot/imported/error.svg-28fb29635cf59d39cabf7052619f602f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

BIN
src/assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://kqwc4avs2xdp"
path="res://.godot/imported/icon.png-d8298ab6eda392a806be6bb7eec65b9c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://src/assets/icon.png"
dest_files=["res://.godot/imported/icon.png-d8298ab6eda392a806be6bb7eec65b9c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

81
src/assets/okay.svg Normal file
View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="30"
height="30"
viewBox="0 0 30 30"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="success.svg"
inkscape:export-filename="error.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="22.627417"
inkscape:cx="3.314563"
inkscape:cy="14.385204"
inkscape:window-width="1854"
inkscape:window-height="1011"
inkscape:window-x="66"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid9"
originx="0"
originy="0" />
</sodipodi:namedview>
<defs
id="defs2">
<rect
x="0"
y="0"
width="30"
height="30"
id="rect15390" />
<rect
x="-19.109422"
y="15.119667"
width="23.697131"
height="28.949477"
id="rect4519" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="display:none;opacity:0.75;fill:#ff3815;fill-opacity:1;stroke:#ff3815;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 4,25 H 26 L 15,6 Z"
id="path11345"
sodipodi:nodetypes="cccc" />
<circle
style="opacity:0.75;fill:#15ff1e;fill-opacity:1;stroke:none;stroke-width:6;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path15121"
cx="15"
cy="15"
r="13" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 8.1284306,14.403599 5.2374004,4.840883 8.39167,-9.6564053"
id="path20589"
sodipodi:nodetypes="ccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://m1omb6g45vst"
path="res://.godot/imported/okay.svg-de66a022ef37753b085371b7c60aefd1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://src/assets/okay.svg"
dest_files=["res://.godot/imported/okay.svg-de66a022ef37753b085371b7c60aefd1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

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" path="res://src/Main.gd" id="1_64y3g"]
[ext_resource type="Script" path="res://src/ImageCompositor.gd" id="2_4ykh7"]
[ext_resource type="Shader" path="res://src/shader/ivd_outline.gdshader" id="3_0fllm"]
[ext_resource type="Script" path="res://src/ImageViewportDisplay.gd" id="4_pbpx2"]
[ext_resource type="Script" 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" path="res://src/MainUI.gd" id="7_5puhk"]
[ext_resource type="Script" 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")