mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-12 16:53:07 +00:00
Compare commits
10 commits
04ebe18e75
...
a772a41158
Author | SHA1 | Date | |
---|---|---|---|
|
a772a41158 | ||
|
fede2d8e6f | ||
|
d0ecf3b03d | ||
|
3d65e48c92 | ||
|
aa1731b701 | ||
|
558140b309 | ||
|
849b815562 | ||
|
3615ce087c | ||
|
2d28136449 | ||
|
5bfe44a202 |
|
@ -156,6 +156,18 @@ msgstr ""
|
||||||
msgid "Percentage"
|
msgid "Percentage"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Found in the create new image dialog. Allows users to change the color mode of the new project, such as RGBA or indexed mode.
|
||||||
|
msgid "Color mode:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Found in the image menu. A submenu that allows users to change the color mode of the project, such as RGBA or indexed mode.
|
||||||
|
msgid "Color Mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Found in the image menu, under the "Color Mode" submenu. Refers to the indexed color mode. See this wikipedia page for more information: https://en.wikipedia.org/wiki/Indexed_color
|
||||||
|
msgid "Indexed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Found in the image menu. Sets the size of the project to be the same as the size of the active selection.
|
#. Found in the image menu. Sets the size of the project to be the same as the size of the active selection.
|
||||||
msgid "Crop to Selection"
|
msgid "Crop to Selection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
BIN
assets/graphics/misc/x_minus_y_mirror_off.png
Normal file
BIN
assets/graphics/misc/x_minus_y_mirror_off.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
34
assets/graphics/misc/x_minus_y_mirror_off.png.import
Normal file
34
assets/graphics/misc/x_minus_y_mirror_off.png.import
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://1kj5gcswa3t2"
|
||||||
|
path="res://.godot/imported/x_minus_y_mirror_off.png-da237e3b5b7ad1dfef1c935385f53dc5.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/graphics/misc/x_minus_y_mirror_off.png"
|
||||||
|
dest_files=["res://.godot/imported/x_minus_y_mirror_off.png-da237e3b5b7ad1dfef1c935385f53dc5.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
|
BIN
assets/graphics/misc/x_minus_y_mirror_on.png
Normal file
BIN
assets/graphics/misc/x_minus_y_mirror_on.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 187 B |
34
assets/graphics/misc/x_minus_y_mirror_on.png.import
Normal file
34
assets/graphics/misc/x_minus_y_mirror_on.png.import
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dn14bkxwdqsfk"
|
||||||
|
path="res://.godot/imported/x_minus_y_mirror_on.png-0e9186904d8241facc4a0c1190f32c53.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/graphics/misc/x_minus_y_mirror_on.png"
|
||||||
|
dest_files=["res://.godot/imported/x_minus_y_mirror_on.png-0e9186904d8241facc4a0c1190f32c53.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
|
BIN
assets/graphics/misc/xy_mirror_off.png
Normal file
BIN
assets/graphics/misc/xy_mirror_off.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 183 B |
34
assets/graphics/misc/xy_mirror_off.png.import
Normal file
34
assets/graphics/misc/xy_mirror_off.png.import
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dlxhm0ronna25"
|
||||||
|
path="res://.godot/imported/xy_mirror_off.png-8d2fd9ebdf350f0cd384fdf39fed4ec1.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/graphics/misc/xy_mirror_off.png"
|
||||||
|
dest_files=["res://.godot/imported/xy_mirror_off.png-8d2fd9ebdf350f0cd384fdf39fed4ec1.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
|
BIN
assets/graphics/misc/xy_mirror_on.png
Normal file
BIN
assets/graphics/misc/xy_mirror_on.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 185 B |
34
assets/graphics/misc/xy_mirror_on.png.import
Normal file
34
assets/graphics/misc/xy_mirror_on.png.import
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cu2uqp5oupt80"
|
||||||
|
path="res://.godot/imported/xy_mirror_on.png-95d443df3b6d17add41283bdd720ea7e.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/graphics/misc/xy_mirror_on.png"
|
||||||
|
dest_files=["res://.godot/imported/xy_mirror_on.png-95d443df3b6d17add41283bdd720ea7e.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
|
264
installer/pixelorama pl.nsi
Normal file
264
installer/pixelorama pl.nsi
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
; Pixelorama Installer NSIS Script
|
||||||
|
; Copyright Xenofon Konitsas (huskee) 2021
|
||||||
|
; Licensed under the MIT License
|
||||||
|
|
||||||
|
|
||||||
|
; Helper variables so that we don't change 20 instances of the version for every update
|
||||||
|
|
||||||
|
!define APPNAME "Pixelorama"
|
||||||
|
!define APPVERSION "v0.11.3"
|
||||||
|
!define COMPANYNAME "Orama Interactive"
|
||||||
|
|
||||||
|
|
||||||
|
; Include the Modern UI library
|
||||||
|
|
||||||
|
!include "MUI2.nsh"
|
||||||
|
!include "x64.nsh"
|
||||||
|
|
||||||
|
|
||||||
|
; Basic Installer Info
|
||||||
|
|
||||||
|
Name "${APPNAME} ${APPVERSION}"
|
||||||
|
OutFile "${APPNAME}_${APPVERSION}_setup.exe"
|
||||||
|
Unicode True
|
||||||
|
|
||||||
|
|
||||||
|
; Default installation folder
|
||||||
|
|
||||||
|
InstallDir "$APPDATA\${COMPANYNAME}\${APPNAME}"
|
||||||
|
|
||||||
|
|
||||||
|
; Get installation folder from registry if available
|
||||||
|
|
||||||
|
InstallDirRegKey HKCU "Software\${COMPANYNAME}\${APPNAME}" "InstallDir"
|
||||||
|
|
||||||
|
|
||||||
|
; Request application privileges for Vista and later
|
||||||
|
|
||||||
|
RequestExecutionLevel admin
|
||||||
|
|
||||||
|
|
||||||
|
; Interface Settings
|
||||||
|
|
||||||
|
!define MUI_ICON "assets\pixel-install.ico"
|
||||||
|
!define MUI_UNICON "assets\pixel-uninstall.ico"
|
||||||
|
!define MUI_WELCOMEFINISHPAGE_BITMAP "assets\wizard.bmp"
|
||||||
|
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "assets\wizard.bmp"
|
||||||
|
!define MUI_HEADERIMAGE
|
||||||
|
!define MUI_HEADERIMAGE_RIGHT
|
||||||
|
!define MUI_HEADERIMAGE_BITMAP "assets\header.bmp"
|
||||||
|
!define MUI_HEADERIMAGE_UNBITMAP "assets\header.bmp"
|
||||||
|
!define MUI_ABORTWARNING
|
||||||
|
!define MUI_COMPONENTSPAGE_SMALLDESC
|
||||||
|
!define MUI_FINISHPAGE_NOAUTOCLOSE
|
||||||
|
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
|
||||||
|
!define MUI_FINISHPAGE_RUN "$INSTDIR\pixelorama.exe"
|
||||||
|
|
||||||
|
; Language selection settings
|
||||||
|
|
||||||
|
!define MUI_LANGDLL_ALLLANGUAGES
|
||||||
|
## Remember the installer language
|
||||||
|
!define MUI_LANGDLL_REGISTRY_ROOT HKCU
|
||||||
|
!define MUI_LANGDLL_REGISTRY_KEY "Software\${COMPANYNAME}\${APPNAME}"
|
||||||
|
!define MUI_LANGDLL_REGISTRY_VALUENAME "Język instalatora"
|
||||||
|
|
||||||
|
|
||||||
|
; Installer pages
|
||||||
|
|
||||||
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
|
!insertmacro MUI_PAGE_LICENSE "Licencja"
|
||||||
|
!insertmacro MUI_PAGE_COMPONENTS
|
||||||
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
!insertmacro MUI_PAGE_FINISH
|
||||||
|
|
||||||
|
!insertmacro MUI_UNPAGE_WELCOME
|
||||||
|
!insertmacro MUI_UNPAGE_COMPONENTS
|
||||||
|
!insertmacro MUI_UNPAGE_CONFIRM
|
||||||
|
!insertmacro MUI_UNPAGE_INSTFILES
|
||||||
|
!insertmacro MUI_UNPAGE_FINISH
|
||||||
|
|
||||||
|
|
||||||
|
; Multilingual support
|
||||||
|
|
||||||
|
!insertmacro MUI_LANGUAGE "Polski"
|
||||||
|
;@INSERT_TRANSLATIONS@
|
||||||
|
|
||||||
|
|
||||||
|
; Assign language strings to installer/uninstaller section names
|
||||||
|
|
||||||
|
LangString SecInstall ${LANG_ENGLISH} "Zainstaluj ${APPNAME}"
|
||||||
|
LangString SecStartmenu ${LANG_ENGLISH} "Utwórz skróty w menu Start (opcjonalnie)"
|
||||||
|
LangString SecDesktop ${LANG_ENGLISH} "Utwórz skrót na pulpicie (opcjonalnie)"
|
||||||
|
LangString un.SecUninstall ${LANG_ENGLISH} "Odinstaluj ${APPNAME} ${APPVERSION}"
|
||||||
|
LangString un.SecConfig ${LANG_ENGLISH} "Usuń pliki konfiguracyjne (opcjonalnie)"
|
||||||
|
|
||||||
|
|
||||||
|
; Installer sections
|
||||||
|
|
||||||
|
Section "$(SecInstall)" SecInstall ; Main install section
|
||||||
|
|
||||||
|
SectionIn RO ; Non optional section
|
||||||
|
|
||||||
|
; Set the installation folder as the output directory
|
||||||
|
SetOutPath "$INSTDIR"
|
||||||
|
|
||||||
|
; Copy all files to install directory
|
||||||
|
${If} ${RunningX64}
|
||||||
|
File "..\build\windows-64bit\pixelorama.exe"
|
||||||
|
File "..\build\windows-64bit\pixelorama.pck"
|
||||||
|
${Else}
|
||||||
|
File "..\build\windows-32bit\pixelorama.exe"
|
||||||
|
File "..\build\windows-32bit\pixelorama.pck"
|
||||||
|
${EndIf}
|
||||||
|
File "..\assets\graphics\icons\pxo.ico"
|
||||||
|
|
||||||
|
SetOutPath "$INSTDIR\pixelorama_data"
|
||||||
|
File /nonfatal /r "..\build\pixelorama_data\*"
|
||||||
|
|
||||||
|
; Store installation folder in the registry
|
||||||
|
WriteRegStr HKCU "Software\${COMPANYNAME}\${APPNAME}" "InstallDir" $INSTDIR
|
||||||
|
|
||||||
|
; Create uninstaller
|
||||||
|
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
|
; Create Add/Remove Programs entry
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||||
|
"DisplayName" "${APPNAME}"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||||
|
"UninstallString" "$INSTDIR\uninstall.exe"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||||
|
"DisplayIcon" "$INSTDIR\pixelorama.exe,0"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||||
|
"InstallLocation" "$INSTDIR"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||||
|
"Publisher" "${COMPANYNAME}"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||||
|
"HelpLink" "https://orama-interactive.github.io/Pixelorama-Docs"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||||
|
"DisplayVersion" "${APPVERSION}"
|
||||||
|
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||||
|
"NoModify" 1
|
||||||
|
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||||
|
"NoRepair" 1
|
||||||
|
|
||||||
|
; Associate .pxo files with Pixelorama
|
||||||
|
WriteRegStr HKCR ".pxo" "" "Pixelorama project"
|
||||||
|
WriteRegStr HKCR ".pxo" "ContentType" "image/pixelorama"
|
||||||
|
WriteRegStr HKCR ".pxo" "PerceivedType" "document"
|
||||||
|
|
||||||
|
WriteRegStr HKCR "Pixelorama project" "" "Pixelorama project"
|
||||||
|
WriteRegStr HKCR "Pixelorama project\shell" "" "open"
|
||||||
|
WriteRegStr HKCR "Pixelorama project\DefaultIcon" "" "$INSTDIR\pxo.ico"
|
||||||
|
|
||||||
|
WriteRegStr HKCR "Pixelorama project\shell\open\command" "" '$INSTDIR\${APPNAME}.exe "%1"'
|
||||||
|
WriteRegStr HKCR "Pixelorama project\shell\edit" "" "Edytuj projekt w ${APPNAME}"
|
||||||
|
WriteRegStr HKCR "Pixelorama project\shell\edit\command" "" '$INSTDIR\${APPNAME}.exe "%1"'
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
|
||||||
|
Section /o "$(SecStartmenu)" SecStartmenu ; Create Start Menu shortcuts
|
||||||
|
|
||||||
|
; Create folder in Start Menu\Programs and create shortcuts for app and uninstaller
|
||||||
|
CreateDirectory "$SMPROGRAMS\${COMPANYNAME}"
|
||||||
|
|
||||||
|
CreateShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME} ${APPVERSION}.lnk" "$INSTDIR\Pixelorama.exe"
|
||||||
|
CreateShortCut "$SMPROGRAMS\${COMPANYNAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
|
||||||
|
Section /o "$(SecDesktop)" SecDesktop ; Create Desktop shortcut
|
||||||
|
|
||||||
|
; Create shortcut for app on desktop
|
||||||
|
CreateShortCut "$DESKTOP\${APPNAME} ${APPVERSION}.lnk" "$INSTDIR\Pixelorama.exe"
|
||||||
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
|
||||||
|
; Installer functions
|
||||||
|
|
||||||
|
Function .onInit
|
||||||
|
!insertmacro MUI_LANGDLL_DISPLAY
|
||||||
|
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
|
||||||
|
; Uninstaller sections
|
||||||
|
|
||||||
|
Section "un.$(un.SecUninstall)" un.SecUninstall ; Main uninstall section
|
||||||
|
|
||||||
|
SectionIn RO
|
||||||
|
|
||||||
|
; Delete all files and folders created by the installer
|
||||||
|
Delete "$INSTDIR\uninstall.exe"
|
||||||
|
Delete "$INSTDIR\Pixelorama.exe"
|
||||||
|
Delete "$INSTDIR\Pixelorama.pck"
|
||||||
|
Delete "$INSTDIR\pxo.ico"
|
||||||
|
RMDir /r "$INSTDIR\pixelorama_data"
|
||||||
|
RMDir "$INSTDIR"
|
||||||
|
|
||||||
|
; Delete shortcuts
|
||||||
|
RMDir /r "$SMPROGRAMS\${COMPANYNAME}"
|
||||||
|
Delete "$DESKTOP\${APPNAME} ${APPVERSION}.lnk"
|
||||||
|
|
||||||
|
; Delete the install folder
|
||||||
|
SetOutPath "$APPDATA"
|
||||||
|
RMDir /r "${COMPANYNAME}"
|
||||||
|
|
||||||
|
; If empty, delete the application's registry key
|
||||||
|
DeleteRegKey /ifempty HKCU "Software\${COMPANYNAME}\${APPNAME}"
|
||||||
|
|
||||||
|
; Delete the Add/Remove Programs entry
|
||||||
|
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
|
||||||
|
|
||||||
|
; Delete the .pxo file association
|
||||||
|
DeleteRegKey HKCR "Pixelorama project"
|
||||||
|
DeleteRegKey HKCR ".pxo"
|
||||||
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
|
||||||
|
Section "un.$(un.SecConfig)" un.SecConfig ; Configuration removal section
|
||||||
|
|
||||||
|
; Delete the application's settings file
|
||||||
|
Delete "$APPDATA\Godot\app_userdata\${APPNAME}\cache.ini"
|
||||||
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
; Uninstaller functions
|
||||||
|
|
||||||
|
Function un.onInit
|
||||||
|
!insertmacro MUI_UNGETLANGUAGE
|
||||||
|
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
|
||||||
|
; Section description language strings for multilingual support
|
||||||
|
|
||||||
|
LangString DESC_SecInstall ${LANG_ENGLISH} "Instalowanie ${APPNAME} ${APPVERSION}."
|
||||||
|
LangString DESC_SecStartmenu ${LANG_ENGLISH} "Tworzenie skrótów w menu Start ${APPNAME}."
|
||||||
|
LangString DESC_SecDesktop ${LANG_ENGLISH} "Tworzenie skrótu na pulpicie dla ${APPNAME}."
|
||||||
|
LangString DESC_un.SecUninstall ${LANG_ENGLISH} "Odinstalowywanie ${APPNAME} ${APPVERSION} i usuwanie wszystkich skrótów."
|
||||||
|
LangString DESC_un.SecConfig ${LANG_ENGLISH} "Usuwanie plików konfiguracyjnych ${APPNAME}."
|
||||||
|
|
||||||
|
|
||||||
|
; Assign language strings to installer/uninstaller descriptions
|
||||||
|
|
||||||
|
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||||
|
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall)
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${SecStartmenu} $(DESC_SecStartmenu)
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${SecDesktop} $(DESC_SecDesktop)
|
||||||
|
|
||||||
|
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||||
|
|
||||||
|
|
||||||
|
!insertmacro MUI_UNFUNCTION_DESCRIPTION_BEGIN
|
||||||
|
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${un.SecUninstall} $(DESC_un.SecUninstall)
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${un.SecConfig} $(DESC_un.SecConfig)
|
||||||
|
|
||||||
|
!insertmacro MUI_UNFUNCTION_DESCRIPTION_END
|
||||||
|
|
||||||
|
|
|
@ -217,7 +217,7 @@ func get_ellipse_points_filled(pos: Vector2i, size: Vector2i, thickness := 1) ->
|
||||||
|
|
||||||
func scale_3x(sprite: Image, tol := 0.196078) -> Image:
|
func scale_3x(sprite: Image, tol := 0.196078) -> Image:
|
||||||
var scaled := Image.create(
|
var scaled := Image.create(
|
||||||
sprite.get_width() * 3, sprite.get_height() * 3, false, Image.FORMAT_RGBA8
|
sprite.get_width() * 3, sprite.get_height() * 3, sprite.has_mipmaps(), sprite.get_format()
|
||||||
)
|
)
|
||||||
var width_minus_one := sprite.get_width() - 1
|
var width_minus_one := sprite.get_width() - 1
|
||||||
var height_minus_one := sprite.get_height() - 1
|
var height_minus_one := sprite.get_height() - 1
|
||||||
|
@ -509,6 +509,8 @@ func similar_colors(c1: Color, c2: Color, tol := 0.392157) -> bool:
|
||||||
func center(indices: Array) -> void:
|
func center(indices: Array) -> void:
|
||||||
var project := Global.current_project
|
var project := Global.current_project
|
||||||
Global.canvas.selection.transform_content_confirm()
|
Global.canvas.selection.transform_content_confirm()
|
||||||
|
var redo_data := {}
|
||||||
|
var undo_data := {}
|
||||||
project.undos += 1
|
project.undos += 1
|
||||||
project.undo_redo.create_action("Center Frames")
|
project.undo_redo.create_action("Center Frames")
|
||||||
for frame in indices:
|
for frame in indices:
|
||||||
|
@ -528,15 +530,20 @@ func center(indices: Array) -> void:
|
||||||
for cel in project.frames[frame].cels:
|
for cel in project.frames[frame].cels:
|
||||||
if not cel is PixelCel:
|
if not cel is PixelCel:
|
||||||
continue
|
continue
|
||||||
var sprite := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var cel_image := (cel as PixelCel).get_image()
|
||||||
sprite.blend_rect(cel.image, used_rect, offset)
|
var tmp_centered := project.new_empty_image()
|
||||||
Global.undo_redo_compress_images({cel.image: sprite.data}, {cel.image: cel.image.data})
|
tmp_centered.blend_rect(cel.image, used_rect, offset)
|
||||||
|
var centered := ImageExtended.new()
|
||||||
|
centered.copy_from_custom(tmp_centered, cel_image.is_indexed)
|
||||||
|
centered.add_data_to_dictionary(redo_data, cel_image)
|
||||||
|
cel_image.add_data_to_dictionary(undo_data)
|
||||||
|
Global.undo_redo_compress_images(redo_data, undo_data)
|
||||||
project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true))
|
project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true))
|
||||||
project.undo_redo.add_do_method(Global.undo_or_redo.bind(false))
|
project.undo_redo.add_do_method(Global.undo_or_redo.bind(false))
|
||||||
project.undo_redo.commit_action()
|
project.undo_redo.commit_action()
|
||||||
|
|
||||||
|
|
||||||
func scale_image(width: int, height: int, interpolation: int) -> void:
|
func scale_project(width: int, height: int, interpolation: int) -> void:
|
||||||
var redo_data := {}
|
var redo_data := {}
|
||||||
var undo_data := {}
|
var undo_data := {}
|
||||||
for f in Global.current_project.frames:
|
for f in Global.current_project.frames:
|
||||||
|
@ -544,30 +551,47 @@ func scale_image(width: int, height: int, interpolation: int) -> void:
|
||||||
var cel := f.cels[i]
|
var cel := f.cels[i]
|
||||||
if not cel is PixelCel:
|
if not cel is PixelCel:
|
||||||
continue
|
continue
|
||||||
var sprite := Image.new()
|
var cel_image := (cel as PixelCel).get_image()
|
||||||
sprite.copy_from(cel.get_image())
|
var sprite := _resize_image(cel_image, width, height, interpolation) as ImageExtended
|
||||||
if interpolation == Interpolation.SCALE3X:
|
sprite.add_data_to_dictionary(redo_data, cel_image)
|
||||||
var times := Vector2i(
|
cel_image.add_data_to_dictionary(undo_data)
|
||||||
ceili(width / (3.0 * sprite.get_width())),
|
|
||||||
ceili(height / (3.0 * sprite.get_height()))
|
|
||||||
)
|
|
||||||
for _j in range(maxi(times.x, times.y)):
|
|
||||||
sprite.copy_from(scale_3x(sprite))
|
|
||||||
sprite.resize(width, height, Image.INTERPOLATE_NEAREST)
|
|
||||||
elif interpolation == Interpolation.CLEANEDGE:
|
|
||||||
var gen := ShaderImageEffect.new()
|
|
||||||
gen.generate_image(sprite, clean_edge_shader, {}, Vector2i(width, height))
|
|
||||||
elif interpolation == Interpolation.OMNISCALE and omniscale_shader:
|
|
||||||
var gen := ShaderImageEffect.new()
|
|
||||||
gen.generate_image(sprite, omniscale_shader, {}, Vector2i(width, height))
|
|
||||||
else:
|
|
||||||
sprite.resize(width, height, interpolation)
|
|
||||||
redo_data[cel.image] = sprite.data
|
|
||||||
undo_data[cel.image] = cel.image.data
|
|
||||||
|
|
||||||
general_do_and_undo_scale(width, height, redo_data, undo_data)
|
general_do_and_undo_scale(width, height, redo_data, undo_data)
|
||||||
|
|
||||||
|
|
||||||
|
func _resize_image(
|
||||||
|
image: Image, width: int, height: int, interpolation: Image.Interpolation
|
||||||
|
) -> Image:
|
||||||
|
var new_image: Image
|
||||||
|
if image is ImageExtended:
|
||||||
|
new_image = ImageExtended.new()
|
||||||
|
new_image.is_indexed = image.is_indexed
|
||||||
|
new_image.copy_from(image)
|
||||||
|
new_image.select_palette("", false)
|
||||||
|
else:
|
||||||
|
new_image = Image.new()
|
||||||
|
new_image.copy_from(image)
|
||||||
|
if interpolation == Interpolation.SCALE3X:
|
||||||
|
var times := Vector2i(
|
||||||
|
ceili(width / (3.0 * new_image.get_width())),
|
||||||
|
ceili(height / (3.0 * new_image.get_height()))
|
||||||
|
)
|
||||||
|
for _j in range(maxi(times.x, times.y)):
|
||||||
|
new_image.copy_from(scale_3x(new_image))
|
||||||
|
new_image.resize(width, height, Image.INTERPOLATE_NEAREST)
|
||||||
|
elif interpolation == Interpolation.CLEANEDGE:
|
||||||
|
var gen := ShaderImageEffect.new()
|
||||||
|
gen.generate_image(new_image, clean_edge_shader, {}, Vector2i(width, height), false)
|
||||||
|
elif interpolation == Interpolation.OMNISCALE and omniscale_shader:
|
||||||
|
var gen := ShaderImageEffect.new()
|
||||||
|
gen.generate_image(new_image, omniscale_shader, {}, Vector2i(width, height), false)
|
||||||
|
else:
|
||||||
|
new_image.resize(width, height, interpolation)
|
||||||
|
if new_image is ImageExtended:
|
||||||
|
new_image.on_size_changed()
|
||||||
|
return new_image
|
||||||
|
|
||||||
|
|
||||||
## Sets the size of the project to be the same as the size of the active selection.
|
## Sets the size of the project to be the same as the size of the active selection.
|
||||||
func crop_to_selection() -> void:
|
func crop_to_selection() -> void:
|
||||||
if not Global.current_project.has_selection:
|
if not Global.current_project.has_selection:
|
||||||
|
@ -577,13 +601,13 @@ func crop_to_selection() -> void:
|
||||||
Global.canvas.selection.transform_content_confirm()
|
Global.canvas.selection.transform_content_confirm()
|
||||||
var rect: Rect2i = Global.canvas.selection.big_bounding_rectangle
|
var rect: Rect2i = Global.canvas.selection.big_bounding_rectangle
|
||||||
# Loop through all the cels to crop them
|
# Loop through all the cels to crop them
|
||||||
for f in Global.current_project.frames:
|
for cel in Global.current_project.get_all_pixel_cels():
|
||||||
for cel in f.cels:
|
var cel_image := cel.get_image()
|
||||||
if not cel is PixelCel:
|
var tmp_cropped := cel_image.get_region(rect)
|
||||||
continue
|
var cropped := ImageExtended.new()
|
||||||
var sprite := cel.get_image().get_region(rect)
|
cropped.copy_from_custom(tmp_cropped, cel_image.is_indexed)
|
||||||
redo_data[cel.image] = sprite.data
|
cropped.add_data_to_dictionary(redo_data, cel_image)
|
||||||
undo_data[cel.image] = cel.image.data
|
cel_image.add_data_to_dictionary(undo_data)
|
||||||
|
|
||||||
general_do_and_undo_scale(rect.size.x, rect.size.y, redo_data, undo_data)
|
general_do_and_undo_scale(rect.size.x, rect.size.y, redo_data, undo_data)
|
||||||
|
|
||||||
|
@ -615,13 +639,13 @@ func crop_to_content() -> void:
|
||||||
var redo_data := {}
|
var redo_data := {}
|
||||||
var undo_data := {}
|
var undo_data := {}
|
||||||
# Loop through all the cels to trim them
|
# Loop through all the cels to trim them
|
||||||
for f in Global.current_project.frames:
|
for cel in Global.current_project.get_all_pixel_cels():
|
||||||
for cel in f.cels:
|
var cel_image := cel.get_image()
|
||||||
if not cel is PixelCel:
|
var tmp_cropped := cel_image.get_region(used_rect)
|
||||||
continue
|
var cropped := ImageExtended.new()
|
||||||
var sprite := cel.get_image().get_region(used_rect)
|
cropped.copy_from_custom(tmp_cropped, cel_image.is_indexed)
|
||||||
redo_data[cel.image] = sprite.data
|
cropped.add_data_to_dictionary(redo_data, cel_image)
|
||||||
undo_data[cel.image] = cel.image.data
|
cel_image.add_data_to_dictionary(undo_data)
|
||||||
|
|
||||||
general_do_and_undo_scale(width, height, redo_data, undo_data)
|
general_do_and_undo_scale(width, height, redo_data, undo_data)
|
||||||
|
|
||||||
|
@ -629,18 +653,17 @@ func crop_to_content() -> void:
|
||||||
func resize_canvas(width: int, height: int, offset_x: int, offset_y: int) -> void:
|
func resize_canvas(width: int, height: int, offset_x: int, offset_y: int) -> void:
|
||||||
var redo_data := {}
|
var redo_data := {}
|
||||||
var undo_data := {}
|
var undo_data := {}
|
||||||
for f in Global.current_project.frames:
|
for cel in Global.current_project.get_all_pixel_cels():
|
||||||
for cel in f.cels:
|
var cel_image := cel.get_image()
|
||||||
if not cel is PixelCel:
|
var resized := ImageExtended.create_custom(
|
||||||
continue
|
width, height, cel_image.has_mipmaps(), cel_image.get_format(), cel_image.is_indexed
|
||||||
var sprite := Image.create(width, height, false, Image.FORMAT_RGBA8)
|
)
|
||||||
sprite.blend_rect(
|
resized.blend_rect(
|
||||||
cel.get_image(),
|
cel_image, Rect2i(Vector2i.ZERO, cel_image.get_size()), Vector2i(offset_x, offset_y)
|
||||||
Rect2i(Vector2i.ZERO, Global.current_project.size),
|
)
|
||||||
Vector2i(offset_x, offset_y)
|
resized.convert_rgb_to_indexed()
|
||||||
)
|
resized.add_data_to_dictionary(redo_data, cel_image)
|
||||||
redo_data[cel.image] = sprite.data
|
cel_image.add_data_to_dictionary(undo_data)
|
||||||
undo_data[cel.image] = cel.image.data
|
|
||||||
|
|
||||||
general_do_and_undo_scale(width, height, redo_data, undo_data)
|
general_do_and_undo_scale(width, height, redo_data, undo_data)
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ func cache_blended_frames(project := Global.current_project) -> void:
|
||||||
blended_frames.clear()
|
blended_frames.clear()
|
||||||
var frames := _calculate_frames(project)
|
var frames := _calculate_frames(project)
|
||||||
for frame in frames:
|
for frame in frames:
|
||||||
var image := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var image := project.new_empty_image()
|
||||||
_blend_layers(image, frame)
|
_blend_layers(image, frame)
|
||||||
blended_frames[frame] = image
|
blended_frames[frame] = image
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ func process_spritesheet(project := Global.current_project) -> void:
|
||||||
spritesheet_columns = temp
|
spritesheet_columns = temp
|
||||||
var width := project.size.x * spritesheet_columns
|
var width := project.size.x * spritesheet_columns
|
||||||
var height := project.size.y * spritesheet_rows
|
var height := project.size.y * spritesheet_rows
|
||||||
var whole_image := Image.create(width, height, false, Image.FORMAT_RGBA8)
|
var whole_image := Image.create(width, height, false, project.get_image_format())
|
||||||
var origin := Vector2i.ZERO
|
var origin := Vector2i.ZERO
|
||||||
var hh := 0
|
var hh := 0
|
||||||
var vv := 0
|
var vv := 0
|
||||||
|
@ -287,10 +287,10 @@ func process_animation(project := Global.current_project) -> void:
|
||||||
ProcessedImage.new(image, project.frames.find(frame), duration)
|
ProcessedImage.new(image, project.frames.find(frame), duration)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
var image := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var image := project.new_empty_image()
|
||||||
image.copy_from(blended_frames[frame])
|
image.copy_from(blended_frames[frame])
|
||||||
if erase_unselected_area and project.has_selection:
|
if erase_unselected_area and project.has_selection:
|
||||||
var crop := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var crop := project.new_empty_image()
|
||||||
var selection_image = project.selection_map.return_cropped_copy(project.size)
|
var selection_image = project.selection_map.return_cropped_copy(project.size)
|
||||||
crop.blit_rect_mask(
|
crop.blit_rect_mask(
|
||||||
image, selection_image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO
|
image, selection_image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO
|
||||||
|
|
|
@ -46,6 +46,7 @@ enum WindowMenu { WINDOW_OPACITY, PANELS, LAYOUTS, MOVABLE_PANELS, ZEN_MODE, FUL
|
||||||
## Enumeration of items present in the Image Menu.
|
## Enumeration of items present in the Image Menu.
|
||||||
enum ImageMenu {
|
enum ImageMenu {
|
||||||
PROJECT_PROPERTIES,
|
PROJECT_PROPERTIES,
|
||||||
|
COLOR_MODE,
|
||||||
RESIZE_CANVAS,
|
RESIZE_CANVAS,
|
||||||
SCALE_IMAGE,
|
SCALE_IMAGE,
|
||||||
CROP_TO_SELECTION,
|
CROP_TO_SELECTION,
|
||||||
|
@ -178,10 +179,14 @@ var can_draw := true
|
||||||
var move_guides_on_canvas := true
|
var move_guides_on_canvas := true
|
||||||
|
|
||||||
var play_only_tags := true ## If [code]true[/code], animation plays only on frames of the same tag.
|
var play_only_tags := true ## If [code]true[/code], animation plays only on frames of the same tag.
|
||||||
## (Intended to be used as getter only) Tells if the x-symmetry guide ( -- ) is visible.
|
## If true, the x symmetry guide ( -- ) is visible.
|
||||||
var show_x_symmetry_axis := false
|
var show_x_symmetry_axis := false
|
||||||
## (Intended to be used as getter only) Tells if the y-symmetry guide ( | ) is visible.
|
## If true, the y symmetry guide ( | ) is visible.
|
||||||
var show_y_symmetry_axis := false
|
var show_y_symmetry_axis := false
|
||||||
|
## If true, the x=y symmetry guide ( / ) is visible.
|
||||||
|
var show_xy_symmetry_axis := false
|
||||||
|
## If true, the x==y symmetry guide ( \ ) is visible.
|
||||||
|
var show_x_minus_y_symmetry_axis := false
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
## Found in Preferences. If [code]true[/code], the last saved project will open on startup.
|
## Found in Preferences. If [code]true[/code], the last saved project will open on startup.
|
||||||
|
@ -1113,8 +1118,17 @@ func undo_redo_compress_images(
|
||||||
func undo_redo_draw_op(
|
func undo_redo_draw_op(
|
||||||
image: Image, new_size: Vector2i, compressed_image_data: PackedByteArray, buffer_size: int
|
image: Image, new_size: Vector2i, compressed_image_data: PackedByteArray, buffer_size: int
|
||||||
) -> void:
|
) -> void:
|
||||||
var decompressed := compressed_image_data.decompress(buffer_size)
|
if image is ImageExtended and image.is_indexed:
|
||||||
image.set_data(new_size.x, new_size.y, image.has_mipmaps(), image.get_format(), decompressed)
|
# If using indexed mode,
|
||||||
|
# just convert the indices to RGB instead of setting the image data directly.
|
||||||
|
if image.get_size() != new_size:
|
||||||
|
image.crop(new_size.x, new_size.y)
|
||||||
|
image.convert_indexed_to_rgb()
|
||||||
|
else:
|
||||||
|
var decompressed := compressed_image_data.decompress(buffer_size)
|
||||||
|
image.set_data(
|
||||||
|
new_size.x, new_size.y, image.has_mipmaps(), image.get_format(), decompressed
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
## This method is used to write project setting overrides to the override.cfg file, located
|
## This method is used to write project setting overrides to the override.cfg file, located
|
||||||
|
|
|
@ -150,7 +150,7 @@ func handle_loading_aimg(path: String, frames: Array) -> void:
|
||||||
if not frames_agree:
|
if not frames_agree:
|
||||||
frame.duration = aimg_frame.duration * project.fps
|
frame.duration = aimg_frame.duration * project.fps
|
||||||
var content := aimg_frame.content
|
var content := aimg_frame.content
|
||||||
content.convert(Image.FORMAT_RGBA8)
|
content.convert(project.get_image_format())
|
||||||
frame.cels.append(PixelCel.new(content, 1))
|
frame.cels.append(PixelCel.new(content, 1))
|
||||||
project.frames.append(frame)
|
project.frames.append(frame)
|
||||||
|
|
||||||
|
@ -389,18 +389,23 @@ func save_pxo_file(
|
||||||
var frame_index := 1
|
var frame_index := 1
|
||||||
for frame in project.frames:
|
for frame in project.frames:
|
||||||
if not autosave and include_blended:
|
if not autosave and include_blended:
|
||||||
var blended := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var blended := project.new_empty_image()
|
||||||
DrawingAlgos.blend_layers(blended, frame, Vector2i.ZERO, project)
|
DrawingAlgos.blend_layers(blended, frame, Vector2i.ZERO, project)
|
||||||
zip_packer.start_file("image_data/final_images/%s" % frame_index)
|
zip_packer.start_file("image_data/final_images/%s" % frame_index)
|
||||||
zip_packer.write_file(blended.get_data())
|
zip_packer.write_file(blended.get_data())
|
||||||
zip_packer.close_file()
|
zip_packer.close_file()
|
||||||
var cel_index := 1
|
var cel_index := 1
|
||||||
for cel in frame.cels:
|
for cel in frame.cels:
|
||||||
var cel_image := cel.get_image()
|
var cel_image := cel.get_image() as ImageExtended
|
||||||
if is_instance_valid(cel_image) and cel is PixelCel:
|
if is_instance_valid(cel_image) and cel is PixelCel:
|
||||||
zip_packer.start_file("image_data/frames/%s/layer_%s" % [frame_index, cel_index])
|
zip_packer.start_file("image_data/frames/%s/layer_%s" % [frame_index, cel_index])
|
||||||
zip_packer.write_file(cel_image.get_data())
|
zip_packer.write_file(cel_image.get_data())
|
||||||
zip_packer.close_file()
|
zip_packer.close_file()
|
||||||
|
zip_packer.start_file(
|
||||||
|
"image_data/frames/%s/indices_layer_%s" % [frame_index, cel_index]
|
||||||
|
)
|
||||||
|
zip_packer.write_file(cel_image.indices_image.get_data())
|
||||||
|
zip_packer.close_file()
|
||||||
cel_index += 1
|
cel_index += 1
|
||||||
frame_index += 1
|
frame_index += 1
|
||||||
var brush_index := 0
|
var brush_index := 0
|
||||||
|
@ -457,12 +462,13 @@ func save_pxo_file(
|
||||||
|
|
||||||
func open_image_as_new_tab(path: String, image: Image) -> void:
|
func open_image_as_new_tab(path: String, image: Image) -> void:
|
||||||
var project := Project.new([], path.get_file(), image.get_size())
|
var project := Project.new([], path.get_file(), image.get_size())
|
||||||
project.layers.append(PixelLayer.new(project))
|
var layer := PixelLayer.new(project)
|
||||||
|
project.layers.append(layer)
|
||||||
Global.projects.append(project)
|
Global.projects.append(project)
|
||||||
|
|
||||||
var frame := Frame.new()
|
var frame := Frame.new()
|
||||||
image.convert(Image.FORMAT_RGBA8)
|
image.convert(project.get_image_format())
|
||||||
frame.cels.append(PixelCel.new(image, 1))
|
frame.cels.append(layer.new_cel_from_image(image))
|
||||||
|
|
||||||
project.frames.append(frame)
|
project.frames.append(frame)
|
||||||
set_new_imported_tab(project, path)
|
set_new_imported_tab(project, path)
|
||||||
|
@ -475,15 +481,18 @@ func open_image_as_spritesheet_tab_smart(
|
||||||
frame_size = image.get_size()
|
frame_size = image.get_size()
|
||||||
sliced_rects.append(Rect2i(Vector2i.ZERO, frame_size))
|
sliced_rects.append(Rect2i(Vector2i.ZERO, frame_size))
|
||||||
var project := Project.new([], path.get_file(), frame_size)
|
var project := Project.new([], path.get_file(), frame_size)
|
||||||
project.layers.append(PixelLayer.new(project))
|
var layer := PixelLayer.new(project)
|
||||||
|
project.layers.append(layer)
|
||||||
Global.projects.append(project)
|
Global.projects.append(project)
|
||||||
for rect in sliced_rects:
|
for rect in sliced_rects:
|
||||||
var offset: Vector2 = (0.5 * (frame_size - rect.size)).floor()
|
var offset: Vector2 = (0.5 * (frame_size - rect.size)).floor()
|
||||||
var frame := Frame.new()
|
var frame := Frame.new()
|
||||||
var cropped_image := Image.create(frame_size.x, frame_size.y, false, Image.FORMAT_RGBA8)
|
var cropped_image := Image.create(
|
||||||
image.convert(Image.FORMAT_RGBA8)
|
frame_size.x, frame_size.y, false, project.get_image_format()
|
||||||
|
)
|
||||||
|
image.convert(project.get_image_format())
|
||||||
cropped_image.blit_rect(image, rect, offset)
|
cropped_image.blit_rect(image, rect, offset)
|
||||||
frame.cels.append(PixelCel.new(cropped_image, 1))
|
frame.cels.append(layer.new_cel_from_image(cropped_image))
|
||||||
project.frames.append(frame)
|
project.frames.append(frame)
|
||||||
set_new_imported_tab(project, path)
|
set_new_imported_tab(project, path)
|
||||||
|
|
||||||
|
@ -494,7 +503,8 @@ func open_image_as_spritesheet_tab(path: String, image: Image, horiz: int, vert:
|
||||||
var frame_width := image.get_size().x / horiz
|
var frame_width := image.get_size().x / horiz
|
||||||
var frame_height := image.get_size().y / vert
|
var frame_height := image.get_size().y / vert
|
||||||
var project := Project.new([], path.get_file(), Vector2(frame_width, frame_height))
|
var project := Project.new([], path.get_file(), Vector2(frame_width, frame_height))
|
||||||
project.layers.append(PixelLayer.new(project))
|
var layer := PixelLayer.new(project)
|
||||||
|
project.layers.append(layer)
|
||||||
Global.projects.append(project)
|
Global.projects.append(project)
|
||||||
for yy in range(vert):
|
for yy in range(vert):
|
||||||
for xx in range(horiz):
|
for xx in range(horiz):
|
||||||
|
@ -503,8 +513,8 @@ func open_image_as_spritesheet_tab(path: String, image: Image, horiz: int, vert:
|
||||||
Rect2i(frame_width * xx, frame_height * yy, frame_width, frame_height)
|
Rect2i(frame_width * xx, frame_height * yy, frame_width, frame_height)
|
||||||
)
|
)
|
||||||
project.size = cropped_image.get_size()
|
project.size = cropped_image.get_size()
|
||||||
cropped_image.convert(Image.FORMAT_RGBA8)
|
cropped_image.convert(project.get_image_format())
|
||||||
frame.cels.append(PixelCel.new(cropped_image, 1))
|
frame.cels.append(layer.new_cel_from_image(cropped_image))
|
||||||
project.frames.append(frame)
|
project.frames.append(frame)
|
||||||
set_new_imported_tab(project, path)
|
set_new_imported_tab(project, path)
|
||||||
|
|
||||||
|
@ -562,12 +572,12 @@ func open_image_as_spritesheet_layer_smart(
|
||||||
if f >= start_frame and f < (start_frame + sliced_rects.size()):
|
if f >= start_frame and f < (start_frame + sliced_rects.size()):
|
||||||
# Slice spritesheet
|
# Slice spritesheet
|
||||||
var offset: Vector2 = (0.5 * (frame_size - sliced_rects[f - start_frame].size)).floor()
|
var offset: Vector2 = (0.5 * (frame_size - sliced_rects[f - start_frame].size)).floor()
|
||||||
image.convert(Image.FORMAT_RGBA8)
|
image.convert(project.get_image_format())
|
||||||
var cropped_image := Image.create(
|
var cropped_image := Image.create(
|
||||||
project_width, project_height, false, Image.FORMAT_RGBA8
|
project_width, project_height, false, project.get_image_format()
|
||||||
)
|
)
|
||||||
cropped_image.blit_rect(image, sliced_rects[f - start_frame], offset)
|
cropped_image.blit_rect(image, sliced_rects[f - start_frame], offset)
|
||||||
cels.append(PixelCel.new(cropped_image))
|
cels.append(layer.new_cel_from_image(cropped_image))
|
||||||
else:
|
else:
|
||||||
cels.append(layer.new_empty_cel())
|
cels.append(layer.new_empty_cel())
|
||||||
|
|
||||||
|
@ -644,16 +654,16 @@ func open_image_as_spritesheet_layer(
|
||||||
# Slice spritesheet
|
# Slice spritesheet
|
||||||
var xx := (f - start_frame) % horizontal
|
var xx := (f - start_frame) % horizontal
|
||||||
var yy := (f - start_frame) / horizontal
|
var yy := (f - start_frame) / horizontal
|
||||||
image.convert(Image.FORMAT_RGBA8)
|
image.convert(project.get_image_format())
|
||||||
var cropped_image := Image.create(
|
var cropped_image := Image.create(
|
||||||
project_width, project_height, false, Image.FORMAT_RGBA8
|
project_width, project_height, false, project.get_image_format()
|
||||||
)
|
)
|
||||||
cropped_image.blit_rect(
|
cropped_image.blit_rect(
|
||||||
image,
|
image,
|
||||||
Rect2i(frame_width * xx, frame_height * yy, frame_width, frame_height),
|
Rect2i(frame_width * xx, frame_height * yy, frame_width, frame_height),
|
||||||
Vector2i.ZERO
|
Vector2i.ZERO
|
||||||
)
|
)
|
||||||
cels.append(PixelCel.new(cropped_image))
|
cels.append(layer.new_cel_from_image(cropped_image))
|
||||||
else:
|
else:
|
||||||
cels.append(layer.new_empty_cel())
|
cels.append(layer.new_empty_cel())
|
||||||
|
|
||||||
|
@ -687,12 +697,18 @@ func open_image_at_cel(image: Image, layer_index := 0, frame_index := 0) -> void
|
||||||
var cel := project.frames[frame_index].cels[layer_index]
|
var cel := project.frames[frame_index].cels[layer_index]
|
||||||
if not cel is PixelCel:
|
if not cel is PixelCel:
|
||||||
return
|
return
|
||||||
image.convert(Image.FORMAT_RGBA8)
|
image.convert(project.get_image_format())
|
||||||
var cel_image := Image.create(project_width, project_height, false, Image.FORMAT_RGBA8)
|
var cel_image := (cel as PixelCel).get_image()
|
||||||
cel_image.blit_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
|
var new_cel_image := ImageExtended.create_custom(
|
||||||
Global.undo_redo_compress_images(
|
project_width, project_height, false, project.get_image_format(), cel_image.is_indexed
|
||||||
{cel.image: cel_image.data}, {cel.image: cel.image.data}, project
|
|
||||||
)
|
)
|
||||||
|
new_cel_image.blit_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
|
||||||
|
new_cel_image.convert_rgb_to_indexed()
|
||||||
|
var redo_data := {}
|
||||||
|
new_cel_image.add_data_to_dictionary(redo_data, cel_image)
|
||||||
|
var undo_data := {}
|
||||||
|
cel_image.add_data_to_dictionary(undo_data)
|
||||||
|
Global.undo_redo_compress_images(redo_data, undo_data, project)
|
||||||
|
|
||||||
project.undo_redo.add_do_property(project, "selected_cels", [])
|
project.undo_redo.add_do_property(project, "selected_cels", [])
|
||||||
project.undo_redo.add_do_method(project.change_cel.bind(frame_index, layer_index))
|
project.undo_redo.add_do_method(project.change_cel.bind(frame_index, layer_index))
|
||||||
|
@ -716,11 +732,14 @@ func open_image_as_new_frame(
|
||||||
|
|
||||||
var frame := Frame.new()
|
var frame := Frame.new()
|
||||||
for i in project.layers.size():
|
for i in project.layers.size():
|
||||||
if i == layer_index:
|
var layer := project.layers[i]
|
||||||
image.convert(Image.FORMAT_RGBA8)
|
if i == layer_index and layer is PixelLayer:
|
||||||
var cel_image := Image.create(project_width, project_height, false, Image.FORMAT_RGBA8)
|
image.convert(project.get_image_format())
|
||||||
|
var cel_image := Image.create(
|
||||||
|
project_width, project_height, false, project.get_image_format()
|
||||||
|
)
|
||||||
cel_image.blit_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
|
cel_image.blit_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
|
||||||
frame.cels.append(PixelCel.new(cel_image, 1))
|
frame.cels.append(layer.new_cel_from_image(cel_image))
|
||||||
else:
|
else:
|
||||||
frame.cels.append(project.layers[i].new_empty_cel())
|
frame.cels.append(project.layers[i].new_empty_cel())
|
||||||
if not undo:
|
if not undo:
|
||||||
|
@ -753,10 +772,12 @@ func open_image_as_new_layer(image: Image, file_name: String, frame_index := 0)
|
||||||
Global.current_project.undo_redo.create_action("Add Layer")
|
Global.current_project.undo_redo.create_action("Add Layer")
|
||||||
for i in project.frames.size():
|
for i in project.frames.size():
|
||||||
if i == frame_index:
|
if i == frame_index:
|
||||||
image.convert(Image.FORMAT_RGBA8)
|
image.convert(project.get_image_format())
|
||||||
var cel_image := Image.create(project_width, project_height, false, Image.FORMAT_RGBA8)
|
var cel_image := Image.create(
|
||||||
|
project_width, project_height, false, project.get_image_format()
|
||||||
|
)
|
||||||
cel_image.blit_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
|
cel_image.blit_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
|
||||||
cels.append(PixelCel.new(cel_image, 1))
|
cels.append(layer.new_cel_from_image(cel_image))
|
||||||
else:
|
else:
|
||||||
cels.append(layer.new_empty_cel())
|
cels.append(layer.new_empty_cel())
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,14 @@ signal options_reset
|
||||||
|
|
||||||
enum Dynamics { NONE, PRESSURE, VELOCITY }
|
enum Dynamics { NONE, PRESSURE, VELOCITY }
|
||||||
|
|
||||||
|
const XY_LINE := Vector2(-0.707107, 0.707107)
|
||||||
|
const X_MINUS_Y_LINE := Vector2(0.707107, 0.707107)
|
||||||
|
|
||||||
var picking_color_for := MOUSE_BUTTON_LEFT
|
var picking_color_for := MOUSE_BUTTON_LEFT
|
||||||
var horizontal_mirror := false
|
var horizontal_mirror := false
|
||||||
var vertical_mirror := false
|
var vertical_mirror := false
|
||||||
|
var diagonal_xy_mirror := false
|
||||||
|
var diagonal_x_minus_y_mirror := false
|
||||||
var pixel_perfect := false
|
var pixel_perfect := false
|
||||||
var alpha_locked := false
|
var alpha_locked := false
|
||||||
|
|
||||||
|
@ -524,20 +529,51 @@ func get_mirrored_positions(
|
||||||
) -> Array[Vector2i]:
|
) -> Array[Vector2i]:
|
||||||
var positions: Array[Vector2i] = []
|
var positions: Array[Vector2i] = []
|
||||||
if horizontal_mirror:
|
if horizontal_mirror:
|
||||||
var mirror_x := pos
|
var mirror_x := calculate_mirror_horizontal(pos, project, offset)
|
||||||
mirror_x.x = project.x_symmetry_point - pos.x + offset
|
|
||||||
positions.append(mirror_x)
|
positions.append(mirror_x)
|
||||||
if vertical_mirror:
|
if vertical_mirror:
|
||||||
var mirror_xy := mirror_x
|
positions.append(calculate_mirror_vertical(mirror_x, project, offset))
|
||||||
mirror_xy.y = project.y_symmetry_point - pos.y + offset
|
else:
|
||||||
positions.append(mirror_xy)
|
if diagonal_xy_mirror:
|
||||||
|
positions.append(calculate_mirror_xy(mirror_x, project))
|
||||||
|
if diagonal_x_minus_y_mirror:
|
||||||
|
positions.append(calculate_mirror_x_minus_y(mirror_x, project))
|
||||||
if vertical_mirror:
|
if vertical_mirror:
|
||||||
var mirror_y := pos
|
var mirror_y := calculate_mirror_vertical(pos, project, offset)
|
||||||
mirror_y.y = project.y_symmetry_point - pos.y + offset
|
|
||||||
positions.append(mirror_y)
|
positions.append(mirror_y)
|
||||||
|
if diagonal_xy_mirror:
|
||||||
|
positions.append(calculate_mirror_xy(mirror_y, project))
|
||||||
|
if diagonal_x_minus_y_mirror:
|
||||||
|
positions.append(calculate_mirror_x_minus_y(mirror_y, project))
|
||||||
|
if diagonal_xy_mirror:
|
||||||
|
var mirror_diagonal := calculate_mirror_xy(pos, project)
|
||||||
|
positions.append(mirror_diagonal)
|
||||||
|
if not horizontal_mirror and not vertical_mirror and diagonal_x_minus_y_mirror:
|
||||||
|
positions.append(calculate_mirror_x_minus_y(mirror_diagonal, project))
|
||||||
|
if diagonal_x_minus_y_mirror:
|
||||||
|
positions.append(calculate_mirror_x_minus_y(pos, project))
|
||||||
return positions
|
return positions
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_mirror_horizontal(pos: Vector2i, project: Project, offset := 0) -> Vector2i:
|
||||||
|
return Vector2i(project.x_symmetry_point - pos.x + offset, pos.y)
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_mirror_vertical(pos: Vector2i, project: Project, offset := 0) -> Vector2i:
|
||||||
|
return Vector2i(pos.x, project.y_symmetry_point - pos.y + offset)
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_mirror_xy(pos: Vector2i, project: Project) -> Vector2i:
|
||||||
|
return Vector2i(Vector2(pos).reflect(XY_LINE).round()) + Vector2i(project.xy_symmetry_point)
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_mirror_x_minus_y(pos: Vector2i, project: Project) -> Vector2i:
|
||||||
|
return (
|
||||||
|
Vector2i(Vector2(pos).reflect(X_MINUS_Y_LINE).round())
|
||||||
|
+ Vector2i(project.x_minus_y_symmetry_point)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func set_button_size(button_size: int) -> void:
|
func set_button_size(button_size: int) -> void:
|
||||||
var size := Vector2(24, 24) if button_size == Global.ButtonSize.SMALL else Vector2(32, 32)
|
var size := Vector2(24, 24) if button_size == Global.ButtonSize.SMALL else Vector2(32, 32)
|
||||||
if not is_instance_valid(_tool_buttons):
|
if not is_instance_valid(_tool_buttons):
|
||||||
|
|
|
@ -10,9 +10,7 @@ func _init(_opacity := 1.0) -> void:
|
||||||
|
|
||||||
|
|
||||||
func get_image() -> Image:
|
func get_image() -> Image:
|
||||||
var image := Image.create(
|
var image := Global.current_project.new_empty_image()
|
||||||
Global.current_project.size.x, Global.current_project.size.y, false, Image.FORMAT_RGBA8
|
|
||||||
)
|
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,17 +4,17 @@ extends BaseCel
|
||||||
## The term "cel" comes from "celluloid" (https://en.wikipedia.org/wiki/Cel).
|
## The term "cel" comes from "celluloid" (https://en.wikipedia.org/wiki/Cel).
|
||||||
|
|
||||||
## This variable is where the image data of the cel are.
|
## This variable is where the image data of the cel are.
|
||||||
var image: Image:
|
var image: ImageExtended:
|
||||||
set = image_changed
|
set = image_changed
|
||||||
|
|
||||||
|
|
||||||
func _init(_image := Image.new(), _opacity := 1.0) -> void:
|
func _init(_image: ImageExtended, _opacity := 1.0) -> void:
|
||||||
image_texture = ImageTexture.new()
|
image_texture = ImageTexture.new()
|
||||||
image = _image # Set image and call setter
|
image = _image # Set image and call setter
|
||||||
opacity = _opacity
|
opacity = _opacity
|
||||||
|
|
||||||
|
|
||||||
func image_changed(value: Image) -> void:
|
func image_changed(value: ImageExtended) -> void:
|
||||||
image = value
|
image = value
|
||||||
if not image.is_empty() and is_instance_valid(image_texture):
|
if not image.is_empty() and is_instance_valid(image_texture):
|
||||||
image_texture.set_image(image)
|
image_texture.set_image(image)
|
||||||
|
@ -48,7 +48,7 @@ func copy_content():
|
||||||
return copy_image
|
return copy_image
|
||||||
|
|
||||||
|
|
||||||
func get_image() -> Image:
|
func get_image() -> ImageExtended:
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
class_name Drawer
|
class_name Drawer
|
||||||
|
|
||||||
|
const NUMBER_OF_DRAWERS := 8
|
||||||
|
|
||||||
var pixel_perfect := false:
|
var pixel_perfect := false:
|
||||||
set(value):
|
set(value):
|
||||||
pixel_perfect = value
|
pixel_perfect = value
|
||||||
if pixel_perfect:
|
if pixel_perfect:
|
||||||
drawers = pixel_perfect_drawers.duplicate()
|
drawers = pixel_perfect_drawers.duplicate()
|
||||||
else:
|
else:
|
||||||
drawers = [simple_drawer, simple_drawer, simple_drawer, simple_drawer]
|
_create_simple_drawers()
|
||||||
var color_op := ColorOp.new()
|
var color_op := ColorOp.new()
|
||||||
|
|
||||||
var simple_drawer := SimpleDrawer.new()
|
var simple_drawer := SimpleDrawer.new()
|
||||||
var pixel_perfect_drawers: Array[PixelPerfectDrawer] = [
|
var pixel_perfect_drawers: Array[PixelPerfectDrawer] = []
|
||||||
PixelPerfectDrawer.new(),
|
var drawers := []
|
||||||
PixelPerfectDrawer.new(),
|
|
||||||
PixelPerfectDrawer.new(),
|
|
||||||
PixelPerfectDrawer.new()
|
|
||||||
]
|
|
||||||
var drawers := [simple_drawer, simple_drawer, simple_drawer, simple_drawer]
|
|
||||||
|
|
||||||
|
|
||||||
class ColorOp:
|
class ColorOp:
|
||||||
|
@ -27,12 +24,12 @@ class ColorOp:
|
||||||
|
|
||||||
|
|
||||||
class SimpleDrawer:
|
class SimpleDrawer:
|
||||||
func set_pixel(image: Image, position: Vector2i, color: Color, op: ColorOp) -> void:
|
func set_pixel(image: ImageExtended, position: Vector2i, color: Color, op: ColorOp) -> void:
|
||||||
var color_old := image.get_pixelv(position)
|
var color_old := image.get_pixelv(position)
|
||||||
var color_str := color.to_html()
|
var color_str := color.to_html()
|
||||||
var color_new := op.process(Color(color_str), color_old)
|
var color_new := op.process(Color(color_str), color_old)
|
||||||
if not color_new.is_equal_approx(color_old):
|
if not color_new.is_equal_approx(color_old):
|
||||||
image.set_pixelv(position, color_new)
|
image.set_pixelv_custom(position, color_new)
|
||||||
|
|
||||||
|
|
||||||
class PixelPerfectDrawer:
|
class PixelPerfectDrawer:
|
||||||
|
@ -43,11 +40,11 @@ class PixelPerfectDrawer:
|
||||||
func reset() -> void:
|
func reset() -> void:
|
||||||
last_pixels = [null, null]
|
last_pixels = [null, null]
|
||||||
|
|
||||||
func set_pixel(image: Image, position: Vector2i, color: Color, op: ColorOp) -> void:
|
func set_pixel(image: ImageExtended, position: Vector2i, color: Color, op: ColorOp) -> void:
|
||||||
var color_old := image.get_pixelv(position)
|
var color_old := image.get_pixelv(position)
|
||||||
var color_str := color.to_html()
|
var color_str := color.to_html()
|
||||||
last_pixels.push_back([position, color_old])
|
last_pixels.push_back([position, color_old])
|
||||||
image.set_pixelv(position, op.process(Color(color_str), color_old))
|
image.set_pixelv_custom(position, op.process(Color(color_str), color_old))
|
||||||
|
|
||||||
var corner = last_pixels.pop_front()
|
var corner = last_pixels.pop_front()
|
||||||
var neighbour = last_pixels[0]
|
var neighbour = last_pixels[0]
|
||||||
|
@ -56,10 +53,25 @@ class PixelPerfectDrawer:
|
||||||
return
|
return
|
||||||
|
|
||||||
if position - corner[0] in CORNERS and position - neighbour[0] in NEIGHBOURS:
|
if position - corner[0] in CORNERS and position - neighbour[0] in NEIGHBOURS:
|
||||||
image.set_pixel(neighbour[0].x, neighbour[0].y, neighbour[1])
|
image.set_pixel_custom(neighbour[0].x, neighbour[0].y, neighbour[1])
|
||||||
last_pixels[0] = corner
|
last_pixels[0] = corner
|
||||||
|
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
drawers.resize(NUMBER_OF_DRAWERS)
|
||||||
|
pixel_perfect_drawers.resize(NUMBER_OF_DRAWERS)
|
||||||
|
for i in NUMBER_OF_DRAWERS:
|
||||||
|
drawers[i] = simple_drawer
|
||||||
|
pixel_perfect_drawers[i] = PixelPerfectDrawer.new()
|
||||||
|
|
||||||
|
|
||||||
|
func _create_simple_drawers() -> void:
|
||||||
|
drawers = []
|
||||||
|
drawers.resize(NUMBER_OF_DRAWERS)
|
||||||
|
for i in NUMBER_OF_DRAWERS:
|
||||||
|
drawers[i] = simple_drawer
|
||||||
|
|
||||||
|
|
||||||
func reset() -> void:
|
func reset() -> void:
|
||||||
for drawer in pixel_perfect_drawers:
|
for drawer in pixel_perfect_drawers:
|
||||||
drawer.reset()
|
drawer.reset()
|
||||||
|
@ -72,7 +84,12 @@ func set_pixel(image: Image, position: Vector2i, color: Color, ignore_mirroring
|
||||||
SteamManager.set_achievement("ACH_FIRST_PIXEL")
|
SteamManager.set_achievement("ACH_FIRST_PIXEL")
|
||||||
if ignore_mirroring:
|
if ignore_mirroring:
|
||||||
return
|
return
|
||||||
if not Tools.horizontal_mirror and not Tools.vertical_mirror:
|
if (
|
||||||
|
not Tools.horizontal_mirror
|
||||||
|
and not Tools.vertical_mirror
|
||||||
|
and not Tools.diagonal_xy_mirror
|
||||||
|
and not Tools.diagonal_x_minus_y_mirror
|
||||||
|
):
|
||||||
return
|
return
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
var mirrored_positions := Tools.get_mirrored_positions(position, project)
|
var mirrored_positions := Tools.get_mirrored_positions(position, project)
|
||||||
|
|
|
@ -170,12 +170,12 @@ func _get_undo_data(project: Project) -> Dictionary:
|
||||||
var data := {}
|
var data := {}
|
||||||
var images := _get_selected_draw_images(project)
|
var images := _get_selected_draw_images(project)
|
||||||
for image in images:
|
for image in images:
|
||||||
data[image] = image.data
|
image.add_data_to_dictionary(data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
func _get_selected_draw_images(project: Project) -> Array[Image]:
|
func _get_selected_draw_images(project: Project) -> Array[ImageExtended]:
|
||||||
var images: Array[Image] = []
|
var images: Array[ImageExtended] = []
|
||||||
if affect == SELECTED_CELS:
|
if affect == SELECTED_CELS:
|
||||||
for cel_index in project.selected_cels:
|
for cel_index in project.selected_cels:
|
||||||
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
||||||
|
|
176
src/Classes/ImageExtended.gd
Normal file
176
src/Classes/ImageExtended.gd
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
class_name ImageExtended
|
||||||
|
extends Image
|
||||||
|
|
||||||
|
## A custom [Image] class that implements support for indexed mode.
|
||||||
|
## Before implementing indexed mode, we just used the [Image] class.
|
||||||
|
## In indexed mode, each pixel is assigned to a number that references a palette color.
|
||||||
|
## This essentially means that the colors of the image are restricted to a specific palette,
|
||||||
|
## and they will automatically get updated when you make changes to that palette, or when
|
||||||
|
## you switch to a different one.
|
||||||
|
|
||||||
|
const TRANSPARENT := Color(0)
|
||||||
|
const SET_INDICES := preload("res://src/Shaders/SetIndices.gdshader")
|
||||||
|
const INDEXED_TO_RGB := preload("res://src/Shaders/IndexedToRGB.gdshader")
|
||||||
|
|
||||||
|
## If [code]true[/code], the image uses indexed mode.
|
||||||
|
var is_indexed := false
|
||||||
|
## The [Palette] the image is currently using for indexed mode.
|
||||||
|
var current_palette := Palettes.current_palette
|
||||||
|
## An [Image] that contains the index of each pixel of the main image for indexed mode.
|
||||||
|
## The indices are stored in the red channel of this image, by diving each index by 255.
|
||||||
|
## This means that there can be a maximum index size of 255. 0 means that the pixel is transparent.
|
||||||
|
var indices_image := Image.create_empty(1, 1, false, Image.FORMAT_R8)
|
||||||
|
## A [PackedColorArray] containing all of the colors of the [member current_palette].
|
||||||
|
var palette := PackedColorArray()
|
||||||
|
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
indices_image.fill(TRANSPARENT)
|
||||||
|
Palettes.palette_selected.connect(select_palette)
|
||||||
|
|
||||||
|
|
||||||
|
## Equivalent of [method Image.create_empty], but returns [ImageExtended] instead.
|
||||||
|
## If [param _is_indexed] is [code]true[/code], the image that is being returned uses indexed mode.
|
||||||
|
static func create_custom(
|
||||||
|
width: int, height: int, mipmaps: bool, format: Image.Format, _is_indexed := false
|
||||||
|
) -> ImageExtended:
|
||||||
|
var new_image := ImageExtended.new()
|
||||||
|
new_image.crop(width, height)
|
||||||
|
if mipmaps:
|
||||||
|
new_image.generate_mipmaps()
|
||||||
|
new_image.convert(format)
|
||||||
|
new_image.fill(TRANSPARENT)
|
||||||
|
new_image.is_indexed = _is_indexed
|
||||||
|
if new_image.is_indexed:
|
||||||
|
new_image.resize_indices()
|
||||||
|
new_image.select_palette("", false)
|
||||||
|
return new_image
|
||||||
|
|
||||||
|
|
||||||
|
## Equivalent of [method Image.copy_from], but also handles the logic necessary for indexed mode.
|
||||||
|
## If [param _is_indexed] is [code]true[/code], the image is set to be using indexed mode.
|
||||||
|
func copy_from_custom(image: Image, indexed := is_indexed) -> void:
|
||||||
|
is_indexed = indexed
|
||||||
|
copy_from(image)
|
||||||
|
if is_indexed:
|
||||||
|
resize_indices()
|
||||||
|
select_palette("", false)
|
||||||
|
convert_rgb_to_indexed()
|
||||||
|
|
||||||
|
|
||||||
|
## Selects a new palette to use in indexed mode.
|
||||||
|
func select_palette(_name: String, convert_to_rgb := true) -> void:
|
||||||
|
current_palette = Palettes.current_palette
|
||||||
|
if not is_instance_valid(current_palette) or not is_indexed:
|
||||||
|
return
|
||||||
|
update_palette()
|
||||||
|
if not current_palette.data_changed.is_connected(update_palette):
|
||||||
|
current_palette.data_changed.connect(update_palette)
|
||||||
|
if not current_palette.data_changed.is_connected(convert_indexed_to_rgb):
|
||||||
|
current_palette.data_changed.connect(convert_indexed_to_rgb)
|
||||||
|
if convert_to_rgb:
|
||||||
|
convert_indexed_to_rgb()
|
||||||
|
|
||||||
|
|
||||||
|
## Updates [member palette] to contain the colors of [member current_palette].
|
||||||
|
func update_palette() -> void:
|
||||||
|
if palette.size() != current_palette.colors.size():
|
||||||
|
palette.resize(current_palette.colors.size())
|
||||||
|
for i in current_palette.colors:
|
||||||
|
palette[i] = current_palette.colors[i].color
|
||||||
|
|
||||||
|
|
||||||
|
## Displays the actual RGBA values of each pixel in the image from indexed mode.
|
||||||
|
func convert_indexed_to_rgb() -> void:
|
||||||
|
if not is_indexed:
|
||||||
|
return
|
||||||
|
var palette_image := Palettes.current_palette.convert_to_image()
|
||||||
|
var palette_texture := ImageTexture.create_from_image(palette_image)
|
||||||
|
var shader_image_effect := ShaderImageEffect.new()
|
||||||
|
var indices_texture := ImageTexture.create_from_image(indices_image)
|
||||||
|
var params := {"palette_texture": palette_texture, "indices_texture": indices_texture}
|
||||||
|
shader_image_effect.generate_image(self, INDEXED_TO_RGB, params, get_size(), false)
|
||||||
|
Global.canvas.queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
## Automatically maps each color of the image's pixel to the closest color of the palette,
|
||||||
|
## by finding the palette color's index and storing it in [member indices_image].
|
||||||
|
func convert_rgb_to_indexed() -> void:
|
||||||
|
if not is_indexed:
|
||||||
|
return
|
||||||
|
var palette_image := Palettes.current_palette.convert_to_image()
|
||||||
|
var palette_texture := ImageTexture.create_from_image(palette_image)
|
||||||
|
var params := {
|
||||||
|
"palette_texture": palette_texture, "rgb_texture": ImageTexture.create_from_image(self)
|
||||||
|
}
|
||||||
|
var shader_image_effect := ShaderImageEffect.new()
|
||||||
|
shader_image_effect.generate_image(
|
||||||
|
indices_image, SET_INDICES, params, indices_image.get_size(), false
|
||||||
|
)
|
||||||
|
convert_indexed_to_rgb()
|
||||||
|
|
||||||
|
|
||||||
|
## Resizes indices and calls [method convert_rgb_to_indexed] when the image's size changes
|
||||||
|
## and indexed mode is enabled.
|
||||||
|
func on_size_changed() -> void:
|
||||||
|
if is_indexed:
|
||||||
|
resize_indices()
|
||||||
|
convert_rgb_to_indexed()
|
||||||
|
|
||||||
|
|
||||||
|
## Resizes [indices_image] to the image's size.
|
||||||
|
func resize_indices() -> void:
|
||||||
|
indices_image.crop(get_width(), get_height())
|
||||||
|
|
||||||
|
|
||||||
|
## Equivalent of [method Image.set_pixel_custom],
|
||||||
|
## but also handles the logic necessary for indexed mode.
|
||||||
|
func set_pixel_custom(x: int, y: int, color: Color) -> void:
|
||||||
|
set_pixelv_custom(Vector2i(x, y), color)
|
||||||
|
|
||||||
|
|
||||||
|
## Equivalent of [method Image.set_pixelv_custom],
|
||||||
|
## but also handles the logic necessary for indexed mode.
|
||||||
|
func set_pixelv_custom(point: Vector2i, color: Color) -> void:
|
||||||
|
var new_color := color
|
||||||
|
if is_indexed:
|
||||||
|
var color_to_fill := TRANSPARENT
|
||||||
|
var color_index := 0
|
||||||
|
if not color.is_equal_approx(TRANSPARENT):
|
||||||
|
if palette.has(color):
|
||||||
|
color_index = palette.find(color)
|
||||||
|
else: # Find the most similar color
|
||||||
|
var smaller_distance := color_distance(color, palette[0])
|
||||||
|
for i in palette.size():
|
||||||
|
var swatch := palette[i]
|
||||||
|
if is_zero_approx(swatch.a): # Skip transparent colors
|
||||||
|
continue
|
||||||
|
var dist := color_distance(color, swatch)
|
||||||
|
if dist < smaller_distance:
|
||||||
|
smaller_distance = dist
|
||||||
|
color_index = i
|
||||||
|
indices_image.set_pixelv(point, Color((color_index + 1) / 255.0, 0, 0, 0))
|
||||||
|
color_to_fill = palette[color_index]
|
||||||
|
new_color = color_to_fill
|
||||||
|
else:
|
||||||
|
indices_image.set_pixelv(point, TRANSPARENT)
|
||||||
|
new_color = TRANSPARENT
|
||||||
|
set_pixelv(point, new_color)
|
||||||
|
|
||||||
|
|
||||||
|
## Finds the distance between colors [param c1] and [param c2].
|
||||||
|
func color_distance(c1: Color, c2: Color) -> float:
|
||||||
|
var v1 := Vector4(c1.r, c1.g, c1.b, c1.a)
|
||||||
|
var v2 := Vector4(c2.r, c2.g, c2.b, c2.a)
|
||||||
|
return v2.distance_to(v1)
|
||||||
|
|
||||||
|
|
||||||
|
## Adds image data to a [param dict] [Dictionary]. Used for undo/redo.
|
||||||
|
func add_data_to_dictionary(dict: Dictionary, other_image: ImageExtended = null) -> void:
|
||||||
|
# The order matters! Setting self's data first would make undo/redo appear to work incorrectly.
|
||||||
|
if is_instance_valid(other_image):
|
||||||
|
dict[other_image.indices_image] = indices_image.data
|
||||||
|
dict[other_image] = data
|
||||||
|
else:
|
||||||
|
dict[indices_image] = indices_image.data
|
||||||
|
dict[self] = data
|
|
@ -218,11 +218,16 @@ func link_cel(cel: BaseCel, link_set = null) -> void:
|
||||||
## This method is not destructive as it does NOT change the data of the image,
|
## This method is not destructive as it does NOT change the data of the image,
|
||||||
## it just returns a copy.
|
## it just returns a copy.
|
||||||
func display_effects(cel: BaseCel, image_override: Image = null) -> Image:
|
func display_effects(cel: BaseCel, image_override: Image = null) -> Image:
|
||||||
var image := Image.new()
|
var image := ImageExtended.new()
|
||||||
if is_instance_valid(image_override):
|
if is_instance_valid(image_override):
|
||||||
image.copy_from(image_override)
|
if image_override is ImageExtended:
|
||||||
|
image.is_indexed = image_override.is_indexed
|
||||||
|
image.copy_from_custom(image_override)
|
||||||
else:
|
else:
|
||||||
image.copy_from(cel.get_image())
|
var cel_image := cel.get_image()
|
||||||
|
if cel_image is ImageExtended:
|
||||||
|
image.is_indexed = cel_image.is_indexed
|
||||||
|
image.copy_from_custom(cel_image)
|
||||||
if not effects_enabled:
|
if not effects_enabled:
|
||||||
return image
|
return image
|
||||||
var image_size := image.get_size()
|
var image_size := image.get_size()
|
||||||
|
|
|
@ -13,7 +13,9 @@ func _init(_project: Project, _name := "") -> void:
|
||||||
|
|
||||||
## Blends all of the images of children layer of the group layer into a single image.
|
## Blends all of the images of children layer of the group layer into a single image.
|
||||||
func blend_children(frame: Frame, origin := Vector2i.ZERO, apply_effects := true) -> Image:
|
func blend_children(frame: Frame, origin := Vector2i.ZERO, apply_effects := true) -> Image:
|
||||||
var image := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var image := ImageExtended.create_custom(
|
||||||
|
project.size.x, project.size.y, false, project.get_image_format(), project.is_indexed()
|
||||||
|
)
|
||||||
var children := get_children(false)
|
var children := get_children(false)
|
||||||
if children.size() <= 0:
|
if children.size() <= 0:
|
||||||
return image
|
return image
|
||||||
|
@ -66,7 +68,7 @@ func blend_children(frame: Frame, origin := Vector2i.ZERO, apply_effects := true
|
||||||
|
|
||||||
|
|
||||||
func _include_child_in_blending(
|
func _include_child_in_blending(
|
||||||
image: Image,
|
image: ImageExtended,
|
||||||
layer: BaseLayer,
|
layer: BaseLayer,
|
||||||
frame: Frame,
|
frame: Frame,
|
||||||
textures: Array[Image],
|
textures: Array[Image],
|
||||||
|
@ -100,7 +102,7 @@ func _include_child_in_blending(
|
||||||
## Gets called recursively if the child group has children groups of its own,
|
## Gets called recursively if the child group has children groups of its own,
|
||||||
## and they are also set to pass through mode.
|
## and they are also set to pass through mode.
|
||||||
func _blend_child_group(
|
func _blend_child_group(
|
||||||
image: Image,
|
image: ImageExtended,
|
||||||
layer: BaseLayer,
|
layer: BaseLayer,
|
||||||
frame: Frame,
|
frame: Frame,
|
||||||
textures: Array[Image],
|
textures: Array[Image],
|
||||||
|
|
|
@ -28,9 +28,19 @@ func get_layer_type() -> int:
|
||||||
|
|
||||||
|
|
||||||
func new_empty_cel() -> BaseCel:
|
func new_empty_cel() -> BaseCel:
|
||||||
var image := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var format := project.get_image_format()
|
||||||
|
var is_indexed := project.is_indexed()
|
||||||
|
var image := ImageExtended.create_custom(
|
||||||
|
project.size.x, project.size.y, false, format, is_indexed
|
||||||
|
)
|
||||||
return PixelCel.new(image)
|
return PixelCel.new(image)
|
||||||
|
|
||||||
|
|
||||||
|
func new_cel_from_image(image: Image) -> PixelCel:
|
||||||
|
var pixelorama_image := ImageExtended.new()
|
||||||
|
pixelorama_image.copy_from_custom(image, project.is_indexed())
|
||||||
|
return PixelCel.new(pixelorama_image)
|
||||||
|
|
||||||
|
|
||||||
func can_layer_get_drawn() -> bool:
|
func can_layer_get_drawn() -> bool:
|
||||||
return is_visible_in_hierarchy() && !is_locked_in_hierarchy()
|
return is_visible_in_hierarchy() && !is_locked_in_hierarchy()
|
||||||
|
|
|
@ -9,6 +9,8 @@ signal about_to_deserialize(dict: Dictionary)
|
||||||
signal resized
|
signal resized
|
||||||
signal timeline_updated
|
signal timeline_updated
|
||||||
|
|
||||||
|
const INDEXED_MODE := Image.FORMAT_MAX + 1
|
||||||
|
|
||||||
var name := "":
|
var name := "":
|
||||||
set(value):
|
set(value):
|
||||||
name = value
|
name = value
|
||||||
|
@ -21,6 +23,17 @@ var undo_redo := UndoRedo.new()
|
||||||
var tiles: Tiles
|
var tiles: Tiles
|
||||||
var undos := 0 ## The number of times we added undo properties
|
var undos := 0 ## The number of times we added undo properties
|
||||||
var can_undo := true
|
var can_undo := true
|
||||||
|
var color_mode: int = Image.FORMAT_RGBA8:
|
||||||
|
set(value):
|
||||||
|
if color_mode != value:
|
||||||
|
color_mode = value
|
||||||
|
for cel in get_all_pixel_cels():
|
||||||
|
var image := cel.get_image()
|
||||||
|
image.is_indexed = is_indexed()
|
||||||
|
if image.is_indexed:
|
||||||
|
image.resize_indices()
|
||||||
|
image.select_palette("", false)
|
||||||
|
image.convert_rgb_to_indexed()
|
||||||
var fill_color := Color(0)
|
var fill_color := Color(0)
|
||||||
var has_changed := false:
|
var has_changed := false:
|
||||||
set(value):
|
set(value):
|
||||||
|
@ -56,8 +69,12 @@ var user_data := "" ## User defined data, set in the project properties.
|
||||||
|
|
||||||
var x_symmetry_point: float
|
var x_symmetry_point: float
|
||||||
var y_symmetry_point: float
|
var y_symmetry_point: float
|
||||||
|
var xy_symmetry_point: Vector2
|
||||||
|
var x_minus_y_symmetry_point: Vector2
|
||||||
var x_symmetry_axis := SymmetryGuide.new()
|
var x_symmetry_axis := SymmetryGuide.new()
|
||||||
var y_symmetry_axis := SymmetryGuide.new()
|
var y_symmetry_axis := SymmetryGuide.new()
|
||||||
|
var diagonal_xy_symmetry_axis := SymmetryGuide.new()
|
||||||
|
var diagonal_x_minus_y_symmetry_axis := SymmetryGuide.new()
|
||||||
|
|
||||||
var selection_map := SelectionMap.new()
|
var selection_map := SelectionMap.new()
|
||||||
## This is useful for when the selection is outside of the canvas boundaries,
|
## This is useful for when the selection is outside of the canvas boundaries,
|
||||||
|
@ -98,17 +115,32 @@ func _init(_frames: Array[Frame] = [], _name := tr("untitled"), _size := Vector2
|
||||||
|
|
||||||
x_symmetry_point = size.x - 1
|
x_symmetry_point = size.x - 1
|
||||||
y_symmetry_point = size.y - 1
|
y_symmetry_point = size.y - 1
|
||||||
x_symmetry_axis.type = x_symmetry_axis.Types.HORIZONTAL
|
xy_symmetry_point = Vector2i(size.y, size.x) - Vector2i.ONE
|
||||||
|
x_minus_y_symmetry_point = Vector2(maxi(size.x - size.y, 0), maxi(size.y - size.x, 0))
|
||||||
|
x_symmetry_axis.type = Guide.Types.HORIZONTAL
|
||||||
x_symmetry_axis.project = self
|
x_symmetry_axis.project = self
|
||||||
x_symmetry_axis.add_point(Vector2(-19999, y_symmetry_point / 2 + 0.5))
|
x_symmetry_axis.add_point(Vector2(-19999, y_symmetry_point / 2 + 0.5))
|
||||||
x_symmetry_axis.add_point(Vector2(19999, y_symmetry_point / 2 + 0.5))
|
x_symmetry_axis.add_point(Vector2(19999, y_symmetry_point / 2 + 0.5))
|
||||||
Global.canvas.add_child(x_symmetry_axis)
|
Global.canvas.add_child(x_symmetry_axis)
|
||||||
y_symmetry_axis.type = y_symmetry_axis.Types.VERTICAL
|
|
||||||
|
y_symmetry_axis.type = Guide.Types.VERTICAL
|
||||||
y_symmetry_axis.project = self
|
y_symmetry_axis.project = self
|
||||||
y_symmetry_axis.add_point(Vector2(x_symmetry_point / 2 + 0.5, -19999))
|
y_symmetry_axis.add_point(Vector2(x_symmetry_point / 2 + 0.5, -19999))
|
||||||
y_symmetry_axis.add_point(Vector2(x_symmetry_point / 2 + 0.5, 19999))
|
y_symmetry_axis.add_point(Vector2(x_symmetry_point / 2 + 0.5, 19999))
|
||||||
Global.canvas.add_child(y_symmetry_axis)
|
Global.canvas.add_child(y_symmetry_axis)
|
||||||
|
|
||||||
|
diagonal_xy_symmetry_axis.type = Guide.Types.XY
|
||||||
|
diagonal_xy_symmetry_axis.project = self
|
||||||
|
diagonal_xy_symmetry_axis.add_point(Vector2(19999, -19999))
|
||||||
|
diagonal_xy_symmetry_axis.add_point(Vector2(-19999, 19999) + xy_symmetry_point + Vector2.ONE)
|
||||||
|
Global.canvas.add_child(diagonal_xy_symmetry_axis)
|
||||||
|
|
||||||
|
diagonal_x_minus_y_symmetry_axis.type = Guide.Types.X_MINUS_Y
|
||||||
|
diagonal_x_minus_y_symmetry_axis.project = self
|
||||||
|
diagonal_x_minus_y_symmetry_axis.add_point(Vector2(-19999, -19999))
|
||||||
|
diagonal_x_minus_y_symmetry_axis.add_point(Vector2(19999, 19999) + x_minus_y_symmetry_point)
|
||||||
|
Global.canvas.add_child(diagonal_x_minus_y_symmetry_axis)
|
||||||
|
|
||||||
if OS.get_name() == "Web":
|
if OS.get_name() == "Web":
|
||||||
export_directory_path = "user://"
|
export_directory_path = "user://"
|
||||||
else:
|
else:
|
||||||
|
@ -176,11 +208,26 @@ func new_empty_frame() -> Frame:
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
|
|
||||||
|
## Returns a new [Image] of size [member size] and format [method get_image_format].
|
||||||
|
func new_empty_image() -> Image:
|
||||||
|
return Image.create(size.x, size.y, false, get_image_format())
|
||||||
|
|
||||||
|
|
||||||
## Returns the currently selected [BaseCel].
|
## Returns the currently selected [BaseCel].
|
||||||
func get_current_cel() -> BaseCel:
|
func get_current_cel() -> BaseCel:
|
||||||
return frames[current_frame].cels[current_layer]
|
return frames[current_frame].cels[current_layer]
|
||||||
|
|
||||||
|
|
||||||
|
func get_image_format() -> Image.Format:
|
||||||
|
if color_mode == INDEXED_MODE:
|
||||||
|
return Image.FORMAT_RGBA8
|
||||||
|
return color_mode
|
||||||
|
|
||||||
|
|
||||||
|
func is_indexed() -> bool:
|
||||||
|
return color_mode == INDEXED_MODE
|
||||||
|
|
||||||
|
|
||||||
func selection_map_changed() -> void:
|
func selection_map_changed() -> void:
|
||||||
var image_texture: ImageTexture
|
var image_texture: ImageTexture
|
||||||
has_selection = !selection_map.is_invisible()
|
has_selection = !selection_map.is_invisible()
|
||||||
|
@ -255,6 +302,7 @@ func serialize() -> Dictionary:
|
||||||
"pxo_version": ProjectSettings.get_setting("application/config/Pxo_Version"),
|
"pxo_version": ProjectSettings.get_setting("application/config/Pxo_Version"),
|
||||||
"size_x": size.x,
|
"size_x": size.x,
|
||||||
"size_y": size.y,
|
"size_y": size.y,
|
||||||
|
"color_mode": color_mode,
|
||||||
"tile_mode_x_basis_x": tiles.x_basis.x,
|
"tile_mode_x_basis_x": tiles.x_basis.x,
|
||||||
"tile_mode_x_basis_y": tiles.x_basis.y,
|
"tile_mode_x_basis_y": tiles.x_basis.y,
|
||||||
"tile_mode_y_basis_x": tiles.y_basis.x,
|
"tile_mode_y_basis_x": tiles.y_basis.x,
|
||||||
|
@ -288,6 +336,7 @@ func deserialize(dict: Dictionary, zip_reader: ZIPReader = null, file: FileAcces
|
||||||
size.y = dict.size_y
|
size.y = dict.size_y
|
||||||
tiles.tile_size = size
|
tiles.tile_size = size
|
||||||
selection_map.crop(size.x, size.y)
|
selection_map.crop(size.x, size.y)
|
||||||
|
color_mode = dict.get("color_mode", color_mode)
|
||||||
if dict.has("tile_mode_x_basis_x") and dict.has("tile_mode_x_basis_y"):
|
if dict.has("tile_mode_x_basis_x") and dict.has("tile_mode_x_basis_y"):
|
||||||
tiles.x_basis.x = dict.tile_mode_x_basis_x
|
tiles.x_basis.x = dict.tile_mode_x_basis_x
|
||||||
tiles.x_basis.y = dict.tile_mode_x_basis_y
|
tiles.x_basis.y = dict.tile_mode_x_basis_y
|
||||||
|
@ -311,20 +360,33 @@ func deserialize(dict: Dictionary, zip_reader: ZIPReader = null, file: FileAcces
|
||||||
for cel in frame.cels:
|
for cel in frame.cels:
|
||||||
match int(dict.layers[cel_i].get("type", Global.LayerTypes.PIXEL)):
|
match int(dict.layers[cel_i].get("type", Global.LayerTypes.PIXEL)):
|
||||||
Global.LayerTypes.PIXEL:
|
Global.LayerTypes.PIXEL:
|
||||||
var image := Image.new()
|
var image: Image
|
||||||
|
var indices_data := PackedByteArray()
|
||||||
if is_instance_valid(zip_reader): # For pxo files saved in 1.0+
|
if is_instance_valid(zip_reader): # For pxo files saved in 1.0+
|
||||||
var image_data := zip_reader.read_file(
|
var path := "image_data/frames/%s/layer_%s" % [frame_i + 1, cel_i + 1]
|
||||||
"image_data/frames/%s/layer_%s" % [frame_i + 1, cel_i + 1]
|
var image_data := zip_reader.read_file(path)
|
||||||
)
|
|
||||||
image = Image.create_from_data(
|
image = Image.create_from_data(
|
||||||
size.x, size.y, false, Image.FORMAT_RGBA8, image_data
|
size.x, size.y, false, get_image_format(), image_data
|
||||||
)
|
)
|
||||||
|
var indices_path := (
|
||||||
|
"image_data/frames/%s/indices_layer_%s" % [frame_i + 1, cel_i + 1]
|
||||||
|
)
|
||||||
|
if zip_reader.file_exists(indices_path):
|
||||||
|
indices_data = zip_reader.read_file(indices_path)
|
||||||
elif is_instance_valid(file): # For pxo files saved in 0.x
|
elif is_instance_valid(file): # For pxo files saved in 0.x
|
||||||
var buffer := file.get_buffer(size.x * size.y * 4)
|
var buffer := file.get_buffer(size.x * size.y * 4)
|
||||||
image = Image.create_from_data(
|
image = Image.create_from_data(
|
||||||
size.x, size.y, false, Image.FORMAT_RGBA8, buffer
|
size.x, size.y, false, get_image_format(), buffer
|
||||||
)
|
)
|
||||||
cels.append(PixelCel.new(image))
|
var pixelorama_image := ImageExtended.new()
|
||||||
|
pixelorama_image.is_indexed = is_indexed()
|
||||||
|
if not indices_data.is_empty() and is_indexed():
|
||||||
|
pixelorama_image.indices_image = Image.create_from_data(
|
||||||
|
size.x, size.y, false, Image.FORMAT_R8, indices_data
|
||||||
|
)
|
||||||
|
pixelorama_image.copy_from(image)
|
||||||
|
pixelorama_image.select_palette("", true)
|
||||||
|
cels.append(PixelCel.new(pixelorama_image))
|
||||||
Global.LayerTypes.GROUP:
|
Global.LayerTypes.GROUP:
|
||||||
cels.append(GroupCel.new())
|
cels.append(GroupCel.new())
|
||||||
Global.LayerTypes.THREE_D:
|
Global.LayerTypes.THREE_D:
|
||||||
|
@ -559,6 +621,16 @@ func find_first_drawable_cel(frame := frames[current_frame]) -> BaseCel:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
## Returns an [Array] of type [PixelCel] containing all of the pixel cels of the project.
|
||||||
|
func get_all_pixel_cels() -> Array[PixelCel]:
|
||||||
|
var cels: Array[PixelCel]
|
||||||
|
for frame in frames:
|
||||||
|
for cel in frame.cels:
|
||||||
|
if cel is PixelCel:
|
||||||
|
cels.append(cel)
|
||||||
|
return cels
|
||||||
|
|
||||||
|
|
||||||
## Re-order layers to take each cel's z-index into account. If all z-indexes are 0,
|
## Re-order layers to take each cel's z-index into account. If all z-indexes are 0,
|
||||||
## then the order of drawing is the same as the order of the layers itself.
|
## then the order of drawing is the same as the order of the layers itself.
|
||||||
func order_layers(frame_index := current_frame) -> void:
|
func order_layers(frame_index := current_frame) -> void:
|
||||||
|
|
|
@ -5,7 +5,9 @@ extends RefCounted
|
||||||
signal done
|
signal done
|
||||||
|
|
||||||
|
|
||||||
func generate_image(img: Image, shader: Shader, params: Dictionary, size: Vector2i) -> void:
|
func generate_image(
|
||||||
|
img: Image, shader: Shader, params: Dictionary, size: Vector2i, respect_indexed := true
|
||||||
|
) -> void:
|
||||||
# duplicate shader before modifying code to avoid affecting original resource
|
# duplicate shader before modifying code to avoid affecting original resource
|
||||||
var resized_width := false
|
var resized_width := false
|
||||||
var resized_height := false
|
var resized_height := false
|
||||||
|
@ -60,4 +62,6 @@ func generate_image(img: Image, shader: Shader, params: Dictionary, size: Vector
|
||||||
img.crop(img.get_width() - 1, img.get_height())
|
img.crop(img.get_width() - 1, img.get_height())
|
||||||
if resized_height:
|
if resized_height:
|
||||||
img.crop(img.get_width(), img.get_height() - 1)
|
img.crop(img.get_width(), img.get_height() - 1)
|
||||||
|
if img is ImageExtended and respect_indexed:
|
||||||
|
img.convert_rgb_to_indexed()
|
||||||
done.emit()
|
done.emit()
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
shader_type canvas_item;
|
shader_type canvas_item;
|
||||||
render_mode unshaded;
|
render_mode unshaded;
|
||||||
|
|
||||||
|
#include "res://src/Shaders/FindPaletteColorIndex.gdshaderinc"
|
||||||
uniform sampler2D palette_texture : filter_nearest;
|
uniform sampler2D palette_texture : filter_nearest;
|
||||||
uniform sampler2D selection : filter_nearest;
|
uniform sampler2D selection : filter_nearest;
|
||||||
|
|
||||||
|
@ -10,18 +11,9 @@ vec4 swap_color(vec4 color) {
|
||||||
if (color.a <= 0.01) {
|
if (color.a <= 0.01) {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
int color_index = 0;
|
|
||||||
int n_of_colors = textureSize(palette_texture, 0).x;
|
int n_of_colors = textureSize(palette_texture, 0).x;
|
||||||
float smaller_distance = distance(color, texture(palette_texture, vec2(0.0)));
|
int color_index = find_index(color, n_of_colors, palette_texture);
|
||||||
for (int i = 0; i <= n_of_colors; i++) {
|
|
||||||
vec2 uv = vec2(float(i) / float(n_of_colors), 0.0);
|
|
||||||
vec4 palette_color = texture(palette_texture, uv);
|
|
||||||
float dist = distance(color, palette_color);
|
|
||||||
if (dist < smaller_distance) {
|
|
||||||
smaller_distance = dist;
|
|
||||||
color_index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return texture(palette_texture, vec2(float(color_index) / float(n_of_colors), 0.0));
|
return texture(palette_texture, vec2(float(color_index) / float(n_of_colors), 0.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
src/Shaders/FindPaletteColorIndex.gdshaderinc
Normal file
14
src/Shaders/FindPaletteColorIndex.gdshaderinc
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
int find_index(vec4 color, int n_of_colors, sampler2D palette_texture) {
|
||||||
|
int color_index = 0;
|
||||||
|
float smaller_distance = distance(color, texture(palette_texture, vec2(0.0)));
|
||||||
|
for (int i = 0; i <= n_of_colors; i++) {
|
||||||
|
vec2 uv = vec2(float(i) / float(n_of_colors), 0.0);
|
||||||
|
vec4 palette_color = texture(palette_texture, uv);
|
||||||
|
float dist = distance(color, palette_color);
|
||||||
|
if (dist < smaller_distance) {
|
||||||
|
smaller_distance = dist;
|
||||||
|
color_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return color_index;
|
||||||
|
}
|
28
src/Shaders/IndexedToRGB.gdshader
Normal file
28
src/Shaders/IndexedToRGB.gdshader
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
shader_type canvas_item;
|
||||||
|
render_mode unshaded;
|
||||||
|
|
||||||
|
const float EPSILON = 0.0001;
|
||||||
|
|
||||||
|
uniform sampler2D palette_texture : filter_nearest;
|
||||||
|
uniform sampler2D indices_texture : filter_nearest;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
float index = texture(indices_texture, UV).r;
|
||||||
|
if (index <= EPSILON) { // If index is zero, make it transparent
|
||||||
|
COLOR = vec4(0.0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float n_of_colors = float(textureSize(palette_texture, 0).x);
|
||||||
|
index -= 1.0 / 255.0;
|
||||||
|
float index_normalized = ((index * 255.0)) / n_of_colors;
|
||||||
|
if (index_normalized + EPSILON < 1.0) {
|
||||||
|
COLOR = texture(palette_texture, vec2(index_normalized + EPSILON, 0.0));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If index is bigger than the size of the palette, make it transparent.
|
||||||
|
// This happens when switching to a palette, where the previous palette was bigger
|
||||||
|
// than the newer one, and the current index is out of bounds of the new one.
|
||||||
|
COLOR = vec4(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/Shaders/SetIndices.gdshader
Normal file
18
src/Shaders/SetIndices.gdshader
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
shader_type canvas_item;
|
||||||
|
render_mode unshaded;
|
||||||
|
|
||||||
|
#include "res://src/Shaders/FindPaletteColorIndex.gdshaderinc"
|
||||||
|
uniform sampler2D rgb_texture : filter_nearest;
|
||||||
|
uniform sampler2D palette_texture : filter_nearest;
|
||||||
|
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
vec4 color = texture(rgb_texture, UV);
|
||||||
|
if (color.a <= 0.01) {
|
||||||
|
COLOR.r = 0.0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int color_index = find_index(color, textureSize(palette_texture, 0).x, palette_texture);
|
||||||
|
COLOR.r = float(color_index + 1) / 255.0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,7 +35,7 @@ var _line_polylines := []
|
||||||
|
|
||||||
# Memorize some stuff when doing brush strokes
|
# Memorize some stuff when doing brush strokes
|
||||||
var _stroke_project: Project
|
var _stroke_project: Project
|
||||||
var _stroke_images: Array[Image] = []
|
var _stroke_images: Array[ImageExtended] = []
|
||||||
var _is_mask_size_zero := true
|
var _is_mask_size_zero := true
|
||||||
var _circle_tool_shortcut: Array[Vector2i]
|
var _circle_tool_shortcut: Array[Vector2i]
|
||||||
|
|
||||||
|
@ -730,8 +730,8 @@ func _get_undo_data() -> Dictionary:
|
||||||
for cel in cels:
|
for cel in cels:
|
||||||
if not cel is PixelCel:
|
if not cel is PixelCel:
|
||||||
continue
|
continue
|
||||||
var image := cel.get_image()
|
var image := (cel as PixelCel).get_image()
|
||||||
data[image] = image.data
|
image.add_data_to_dictionary(data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -168,18 +168,9 @@ func draw_preview() -> void:
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(points[i]):
|
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(points[i]):
|
||||||
image.set_pixelv(points[i], Color.WHITE)
|
image.set_pixelv(points[i], Color.WHITE)
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
if Tools.horizontal_mirror:
|
for point in mirror_array(points):
|
||||||
for point in mirror_array(points, true, false):
|
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
image.set_pixelv(point, Color.WHITE)
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(points, true, true):
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(points, false, true):
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
var texture := ImageTexture.create_from_image(image)
|
var texture := ImageTexture.create_from_image(image)
|
||||||
canvas.texture = texture
|
canvas.texture = texture
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -205,18 +205,33 @@ func snap_position(pos: Vector2) -> Vector2:
|
||||||
return pos
|
return pos
|
||||||
|
|
||||||
|
|
||||||
func mirror_array(array: Array[Vector2i], h: bool, v: bool) -> Array[Vector2i]:
|
## Returns an array that mirrors each point of the [param array].
|
||||||
|
## An optional [param callable] can be passed, which gets called for each type of symmetry.
|
||||||
|
func mirror_array(array: Array[Vector2i], callable := func(_array): pass) -> Array[Vector2i]:
|
||||||
var new_array: Array[Vector2i] = []
|
var new_array: Array[Vector2i] = []
|
||||||
var project := Global.current_project
|
var project := Global.current_project
|
||||||
for point in array:
|
if Tools.horizontal_mirror and Tools.vertical_mirror:
|
||||||
if h and v:
|
var hv_array: Array[Vector2i] = []
|
||||||
new_array.append(
|
for point in array:
|
||||||
Vector2i(project.x_symmetry_point - point.x, project.y_symmetry_point - point.y)
|
var mirror_x := Tools.calculate_mirror_horizontal(point, project)
|
||||||
)
|
hv_array.append(Tools.calculate_mirror_vertical(mirror_x, project))
|
||||||
elif h:
|
if callable.is_valid():
|
||||||
new_array.append(Vector2i(project.x_symmetry_point - point.x, point.y))
|
callable.call(hv_array)
|
||||||
elif v:
|
new_array += hv_array
|
||||||
new_array.append(Vector2i(point.x, project.y_symmetry_point - point.y))
|
if Tools.horizontal_mirror:
|
||||||
|
var h_array: Array[Vector2i] = []
|
||||||
|
for point in array:
|
||||||
|
h_array.append(Tools.calculate_mirror_horizontal(point, project))
|
||||||
|
if callable.is_valid():
|
||||||
|
callable.call(h_array)
|
||||||
|
new_array += h_array
|
||||||
|
if Tools.vertical_mirror:
|
||||||
|
var v_array: Array[Vector2i] = []
|
||||||
|
for point in array:
|
||||||
|
v_array.append(Tools.calculate_mirror_vertical(point, project))
|
||||||
|
if callable.is_valid():
|
||||||
|
callable.call(v_array)
|
||||||
|
new_array += v_array
|
||||||
|
|
||||||
return new_array
|
return new_array
|
||||||
|
|
||||||
|
@ -299,12 +314,12 @@ func _get_draw_rect() -> Rect2i:
|
||||||
return Rect2i(Vector2i.ZERO, Global.current_project.size)
|
return Rect2i(Vector2i.ZERO, Global.current_project.size)
|
||||||
|
|
||||||
|
|
||||||
func _get_draw_image() -> Image:
|
func _get_draw_image() -> ImageExtended:
|
||||||
return Global.current_project.get_current_cel().get_image()
|
return Global.current_project.get_current_cel().get_image()
|
||||||
|
|
||||||
|
|
||||||
func _get_selected_draw_images() -> Array[Image]:
|
func _get_selected_draw_images() -> Array[ImageExtended]:
|
||||||
var images: Array[Image] = []
|
var images: Array[ImageExtended] = []
|
||||||
var project := Global.current_project
|
var project := Global.current_project
|
||||||
for cel_index in project.selected_cels:
|
for cel_index in project.selected_cels:
|
||||||
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
||||||
|
|
|
@ -220,7 +220,7 @@ func fill_in_color(pos: Vector2i) -> void:
|
||||||
if project.has_selection:
|
if project.has_selection:
|
||||||
selection = project.selection_map.return_cropped_copy(project.size)
|
selection = project.selection_map.return_cropped_copy(project.size)
|
||||||
else:
|
else:
|
||||||
selection = Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
selection = project.new_empty_image()
|
||||||
selection.fill(Color(1, 1, 1, 1))
|
selection.fill(Color(1, 1, 1, 1))
|
||||||
|
|
||||||
selection_tex = ImageTexture.create_from_image(selection)
|
selection_tex = ImageTexture.create_from_image(selection)
|
||||||
|
@ -263,7 +263,7 @@ func fill_in_selection() -> void:
|
||||||
var images := _get_selected_draw_images()
|
var images := _get_selected_draw_images()
|
||||||
if _fill_with == FillWith.COLOR or _pattern == null:
|
if _fill_with == FillWith.COLOR or _pattern == null:
|
||||||
if project.has_selection:
|
if project.has_selection:
|
||||||
var filler := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var filler := project.new_empty_image()
|
||||||
filler.fill(tool_slot.color)
|
filler.fill(tool_slot.color)
|
||||||
var rect: Rect2i = Global.canvas.selection.big_bounding_rectangle
|
var rect: Rect2i = Global.canvas.selection.big_bounding_rectangle
|
||||||
var selection_map_copy := project.selection_map.return_cropped_copy(project.size)
|
var selection_map_copy := project.selection_map.return_cropped_copy(project.size)
|
||||||
|
@ -284,7 +284,7 @@ func fill_in_selection() -> void:
|
||||||
if project.has_selection:
|
if project.has_selection:
|
||||||
selection = project.selection_map.return_cropped_copy(project.size)
|
selection = project.selection_map.return_cropped_copy(project.size)
|
||||||
else:
|
else:
|
||||||
selection = Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
selection = project.new_empty_image()
|
||||||
selection.fill(Color(1, 1, 1, 1))
|
selection.fill(Color(1, 1, 1, 1))
|
||||||
|
|
||||||
selection_tex = ImageTexture.create_from_image(selection)
|
selection_tex = ImageTexture.create_from_image(selection)
|
||||||
|
@ -461,7 +461,7 @@ func _compute_segments_for_image(
|
||||||
done = false
|
done = false
|
||||||
|
|
||||||
|
|
||||||
func _color_segments(image: Image) -> void:
|
func _color_segments(image: ImageExtended) -> void:
|
||||||
if _fill_with == FillWith.COLOR or _pattern == null:
|
if _fill_with == FillWith.COLOR or _pattern == null:
|
||||||
# This is needed to ensure that the color used to fill is not wrong, due to float
|
# This is needed to ensure that the color used to fill is not wrong, due to float
|
||||||
# rounding issues.
|
# rounding issues.
|
||||||
|
@ -472,7 +472,7 @@ func _color_segments(image: Image) -> void:
|
||||||
var p := _allegro_image_segments[c]
|
var p := _allegro_image_segments[c]
|
||||||
for px in range(p.left_position, p.right_position + 1):
|
for px in range(p.left_position, p.right_position + 1):
|
||||||
# We don't have to check again whether the point being processed is within the bounds
|
# We don't have to check again whether the point being processed is within the bounds
|
||||||
image.set_pixel(px, p.y, color)
|
image.set_pixel_custom(px, p.y, color)
|
||||||
else:
|
else:
|
||||||
# shortcircuit tests for patternfills
|
# shortcircuit tests for patternfills
|
||||||
var pattern_size := _pattern.image.get_size()
|
var pattern_size := _pattern.image.get_size()
|
||||||
|
@ -484,11 +484,11 @@ func _color_segments(image: Image) -> void:
|
||||||
_set_pixel_pattern(image, px, p.y, pattern_size)
|
_set_pixel_pattern(image, px, p.y, pattern_size)
|
||||||
|
|
||||||
|
|
||||||
func _set_pixel_pattern(image: Image, x: int, y: int, pattern_size: Vector2i) -> void:
|
func _set_pixel_pattern(image: ImageExtended, x: int, y: int, pattern_size: Vector2i) -> void:
|
||||||
var px := (x + _offset_x) % pattern_size.x
|
var px := (x + _offset_x) % pattern_size.x
|
||||||
var py := (y + _offset_y) % pattern_size.y
|
var py := (y + _offset_y) % pattern_size.y
|
||||||
var pc := _pattern.image.get_pixel(px, py)
|
var pc := _pattern.image.get_pixel(px, py)
|
||||||
image.set_pixel(x, y, pc)
|
image.set_pixel_custom(x, y, pc)
|
||||||
|
|
||||||
|
|
||||||
func commit_undo() -> void:
|
func commit_undo() -> void:
|
||||||
|
@ -514,12 +514,12 @@ func _get_undo_data() -> Dictionary:
|
||||||
if Global.animation_timeline.animation_timer.is_stopped():
|
if Global.animation_timeline.animation_timer.is_stopped():
|
||||||
var images := _get_selected_draw_images()
|
var images := _get_selected_draw_images()
|
||||||
for image in images:
|
for image in images:
|
||||||
data[image] = image.data
|
image.add_data_to_dictionary(data)
|
||||||
else:
|
else:
|
||||||
for frame in Global.current_project.frames:
|
for frame in Global.current_project.frames:
|
||||||
var cel := frame.cels[Global.current_project.current_layer]
|
var cel := frame.cels[Global.current_project.current_layer]
|
||||||
if not cel is PixelCel:
|
if not cel is PixelCel:
|
||||||
continue
|
continue
|
||||||
var image := cel.get_image()
|
var image := (cel as PixelCel).get_image()
|
||||||
data[image] = image.data
|
image.add_data_to_dictionary(data)
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -141,18 +141,9 @@ func draw_preview() -> void:
|
||||||
image.set_pixelv(points[i], Color.WHITE)
|
image.set_pixelv(points[i], Color.WHITE)
|
||||||
|
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
if Tools.horizontal_mirror:
|
for point in mirror_array(points):
|
||||||
for point in mirror_array(points, true, false):
|
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
image.set_pixelv(point, Color.WHITE)
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(points, true, true):
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(points, false, true):
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
var texture := ImageTexture.create_from_image(image)
|
var texture := ImageTexture.create_from_image(image)
|
||||||
previews.texture = texture
|
previews.texture = texture
|
||||||
|
|
||||||
|
@ -203,7 +194,7 @@ func _draw_shape() -> void:
|
||||||
commit_undo()
|
commit_undo()
|
||||||
|
|
||||||
|
|
||||||
func _draw_pixel(point: Vector2i, images: Array[Image]) -> void:
|
func _draw_pixel(point: Vector2i, images: Array[ImageExtended]) -> void:
|
||||||
if Global.current_project.can_pixel_get_drawn(point):
|
if Global.current_project.can_pixel_get_drawn(point):
|
||||||
for image in images:
|
for image in images:
|
||||||
_drawer.set_pixel(image, point, tool_slot.color)
|
_drawer.set_pixel(image, point, tool_slot.color)
|
||||||
|
|
|
@ -122,6 +122,7 @@ func _draw_brush_image(image: Image, src_rect: Rect2i, dst: Vector2i) -> void:
|
||||||
var images := _get_selected_draw_images()
|
var images := _get_selected_draw_images()
|
||||||
for draw_image in images:
|
for draw_image in images:
|
||||||
draw_image.blit_rect_mask(_clear_image, image, src_rect, dst)
|
draw_image.blit_rect_mask(_clear_image, image, src_rect, dst)
|
||||||
|
draw_image.convert_rgb_to_indexed()
|
||||||
else:
|
else:
|
||||||
for xx in image.get_size().x:
|
for xx in image.get_size().x:
|
||||||
for yy in image.get_size().y:
|
for yy in image.get_size().y:
|
||||||
|
|
|
@ -157,18 +157,9 @@ func draw_preview() -> void:
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
||||||
image.set_pixelv(point, Color.WHITE)
|
image.set_pixelv(point, Color.WHITE)
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
if Tools.horizontal_mirror:
|
for point in mirror_array(points):
|
||||||
for point in mirror_array(points, true, false):
|
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
image.set_pixelv(point, Color.WHITE)
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(points, true, true):
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(points, false, true):
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
var texture := ImageTexture.create_from_image(image)
|
var texture := ImageTexture.create_from_image(image)
|
||||||
canvas.texture = texture
|
canvas.texture = texture
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -211,6 +211,7 @@ func _draw_brush_image(brush_image: Image, src_rect: Rect2i, dst: Vector2i) -> v
|
||||||
draw_image.blit_rect_mask(brush_image, mask, src_rect, dst)
|
draw_image.blit_rect_mask(brush_image, mask, src_rect, dst)
|
||||||
else:
|
else:
|
||||||
draw_image.blit_rect(brush_image, src_rect, dst)
|
draw_image.blit_rect(brush_image, src_rect, dst)
|
||||||
|
draw_image.convert_rgb_to_indexed()
|
||||||
else:
|
else:
|
||||||
for draw_image in images:
|
for draw_image in images:
|
||||||
if Tools.alpha_locked:
|
if Tools.alpha_locked:
|
||||||
|
@ -218,3 +219,4 @@ func _draw_brush_image(brush_image: Image, src_rect: Rect2i, dst: Vector2i) -> v
|
||||||
draw_image.blend_rect_mask(brush_image, mask, src_rect, dst)
|
draw_image.blend_rect_mask(brush_image, mask, src_rect, dst)
|
||||||
else:
|
else:
|
||||||
draw_image.blend_rect(brush_image, src_rect, dst)
|
draw_image.blend_rect(brush_image, src_rect, dst)
|
||||||
|
draw_image.convert_rgb_to_indexed()
|
||||||
|
|
|
@ -63,18 +63,9 @@ func draw_preview() -> void:
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(points[i]):
|
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(points[i]):
|
||||||
image.set_pixelv(points[i], Color.WHITE)
|
image.set_pixelv(points[i], Color.WHITE)
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
if Tools.horizontal_mirror:
|
for point in mirror_array(points):
|
||||||
for point in mirror_array(points, true, false):
|
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
image.set_pixelv(point, Color.WHITE)
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(points, true, true):
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(points, false, true):
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(point):
|
|
||||||
image.set_pixelv(point, Color.WHITE)
|
|
||||||
var texture := ImageTexture.create_from_image(image)
|
var texture := ImageTexture.create_from_image(image)
|
||||||
canvas.texture = texture
|
canvas.texture = texture
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -46,27 +46,12 @@ func draw_preview() -> void:
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
image.set_pixelv(draw_point, Color.WHITE)
|
||||||
|
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
if Tools.horizontal_mirror:
|
for point in mirror_array(_draw_points):
|
||||||
for point in mirror_array(_draw_points, true, false):
|
var draw_point := point
|
||||||
var draw_point := point
|
if Global.mirror_view: # This fixes previewing in mirror mode
|
||||||
if Global.mirror_view: # This fixes previewing in mirror mode
|
draw_point.x = image.get_width() - draw_point.x - 1
|
||||||
draw_point.x = image.get_width() - draw_point.x - 1
|
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
image.set_pixelv(draw_point, Color.WHITE)
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(_draw_points, true, true):
|
|
||||||
var draw_point := point
|
|
||||||
if Global.mirror_view: # This fixes previewing in mirror mode
|
|
||||||
draw_point.x = image.get_width() - draw_point.x - 1
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(_draw_points, false, true):
|
|
||||||
var draw_point := point
|
|
||||||
if Global.mirror_view: # This fixes previewing in mirror mode
|
|
||||||
draw_point.x = image.get_width() - draw_point.x - 1
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
|
||||||
var texture := ImageTexture.create_from_image(image)
|
var texture := ImageTexture.create_from_image(image)
|
||||||
canvas.texture = texture
|
canvas.texture = texture
|
||||||
else:
|
else:
|
||||||
|
@ -85,19 +70,10 @@ func apply_selection(_position) -> void:
|
||||||
if _draw_points.size() > 3:
|
if _draw_points.size() > 3:
|
||||||
if _intersect:
|
if _intersect:
|
||||||
project.selection_map.clear()
|
project.selection_map.clear()
|
||||||
lasso_selection(project.selection_map, previous_selection_map, _draw_points)
|
lasso_selection(_draw_points, project.selection_map, previous_selection_map)
|
||||||
|
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
if Tools.horizontal_mirror:
|
var callable := lasso_selection.bind(project.selection_map, previous_selection_map)
|
||||||
var mirror_x := mirror_array(_draw_points, true, false)
|
mirror_array(_draw_points, callable)
|
||||||
lasso_selection(project.selection_map, previous_selection_map, mirror_x)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
var mirror_xy := mirror_array(_draw_points, true, true)
|
|
||||||
lasso_selection(project.selection_map, previous_selection_map, mirror_xy)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
var mirror_y := mirror_array(_draw_points, false, true)
|
|
||||||
lasso_selection(project.selection_map, previous_selection_map, mirror_y)
|
|
||||||
|
|
||||||
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
|
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
|
||||||
else:
|
else:
|
||||||
if !cleared:
|
if !cleared:
|
||||||
|
@ -109,7 +85,7 @@ func apply_selection(_position) -> void:
|
||||||
|
|
||||||
|
|
||||||
func lasso_selection(
|
func lasso_selection(
|
||||||
selection_map: SelectionMap, previous_selection_map: SelectionMap, points: Array[Vector2i]
|
points: Array[Vector2i], selection_map: SelectionMap, previous_selection_map: SelectionMap
|
||||||
) -> void:
|
) -> void:
|
||||||
var selection_size := selection_map.get_size()
|
var selection_size := selection_map.get_size()
|
||||||
var bounding_rect := Rect2i(points[0], Vector2i.ZERO)
|
var bounding_rect := Rect2i(points[0], Vector2i.ZERO)
|
||||||
|
|
|
@ -74,27 +74,12 @@ func draw_preview() -> void:
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
image.set_pixelv(draw_point, Color.WHITE)
|
||||||
|
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
if Tools.horizontal_mirror:
|
for point in mirror_array(_draw_points):
|
||||||
for point in mirror_array(_draw_points, true, false):
|
var draw_point := point
|
||||||
var draw_point := point
|
if Global.mirror_view: # This fixes previewing in mirror mode
|
||||||
if Global.mirror_view: # This fixes previewing in mirror mode
|
draw_point.x = image.get_width() - draw_point.x - 1
|
||||||
draw_point.x = image.get_width() - draw_point.x - 1
|
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
image.set_pixelv(draw_point, Color.WHITE)
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(_draw_points, true, true):
|
|
||||||
var draw_point := point
|
|
||||||
if Global.mirror_view: # This fixes previewing in mirror mode
|
|
||||||
draw_point.x = image.get_width() - draw_point.x - 1
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(_draw_points, false, true):
|
|
||||||
var draw_point := point
|
|
||||||
if Global.mirror_view: # This fixes previewing in mirror mode
|
|
||||||
draw_point.x = image.get_width() - draw_point.x - 1
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
|
||||||
var texture := ImageTexture.create_from_image(image)
|
var texture := ImageTexture.create_from_image(image)
|
||||||
canvas.texture = texture
|
canvas.texture = texture
|
||||||
else:
|
else:
|
||||||
|
@ -115,18 +100,9 @@ func apply_selection(pos: Vector2i) -> void:
|
||||||
if _intersect:
|
if _intersect:
|
||||||
project.selection_map.clear()
|
project.selection_map.clear()
|
||||||
paint_selection(project.selection_map, previous_selection_map, _draw_points)
|
paint_selection(project.selection_map, previous_selection_map, _draw_points)
|
||||||
|
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
if Tools.horizontal_mirror:
|
var mirror := mirror_array(_draw_points)
|
||||||
var mirror_x := mirror_array(_draw_points, true, false)
|
paint_selection(project.selection_map, previous_selection_map, mirror)
|
||||||
paint_selection(project.selection_map, previous_selection_map, mirror_x)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
var mirror_xy := mirror_array(_draw_points, true, true)
|
|
||||||
paint_selection(project.selection_map, previous_selection_map, mirror_xy)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
var mirror_y := mirror_array(_draw_points, false, true)
|
|
||||||
paint_selection(project.selection_map, previous_selection_map, mirror_y)
|
|
||||||
|
|
||||||
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
|
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
|
||||||
else:
|
else:
|
||||||
if !cleared:
|
if !cleared:
|
||||||
|
|
|
@ -81,27 +81,12 @@ func draw_preview() -> void:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
if Tools.horizontal_mirror:
|
for point in mirror_array(preview_draw_points):
|
||||||
for point in mirror_array(preview_draw_points, true, false):
|
var draw_point := point
|
||||||
var draw_point := point
|
if Global.mirror_view: # This fixes previewing in mirror mode
|
||||||
if Global.mirror_view: # This fixes previewing in mirror mode
|
draw_point.x = image.get_width() - draw_point.x - 1
|
||||||
draw_point.x = image.get_width() - draw_point.x - 1
|
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
image.set_pixelv(draw_point, Color.WHITE)
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(preview_draw_points, true, true):
|
|
||||||
var draw_point := point
|
|
||||||
if Global.mirror_view: # This fixes previewing in mirror mode
|
|
||||||
draw_point.x = image.get_width() - draw_point.x - 1
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
for point in mirror_array(preview_draw_points, false, true):
|
|
||||||
var draw_point := point
|
|
||||||
if Global.mirror_view: # This fixes previewing in mirror mode
|
|
||||||
draw_point.x = image.get_width() - draw_point.x - 1
|
|
||||||
if Rect2i(Vector2i.ZERO, image.get_size()).has_point(draw_point):
|
|
||||||
image.set_pixelv(draw_point, Color.WHITE)
|
|
||||||
var texture := ImageTexture.create_from_image(image)
|
var texture := ImageTexture.create_from_image(image)
|
||||||
previews.texture = texture
|
previews.texture = texture
|
||||||
else:
|
else:
|
||||||
|
@ -122,19 +107,10 @@ func apply_selection(pos: Vector2i) -> void:
|
||||||
if _draw_points.size() > 3:
|
if _draw_points.size() > 3:
|
||||||
if _intersect:
|
if _intersect:
|
||||||
project.selection_map.clear()
|
project.selection_map.clear()
|
||||||
lasso_selection(project.selection_map, previous_selection_map, _draw_points)
|
lasso_selection(_draw_points, project.selection_map, previous_selection_map)
|
||||||
|
|
||||||
# Handle mirroring
|
# Handle mirroring
|
||||||
if Tools.horizontal_mirror:
|
var callable := lasso_selection.bind(project.selection_map, previous_selection_map)
|
||||||
var mirror_x := mirror_array(_draw_points, true, false)
|
mirror_array(_draw_points, callable)
|
||||||
lasso_selection(project.selection_map, previous_selection_map, mirror_x)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
var mirror_xy := mirror_array(_draw_points, true, true)
|
|
||||||
lasso_selection(project.selection_map, previous_selection_map, mirror_xy)
|
|
||||||
if Tools.vertical_mirror:
|
|
||||||
var mirror_y := mirror_array(_draw_points, false, true)
|
|
||||||
lasso_selection(project.selection_map, previous_selection_map, mirror_y)
|
|
||||||
|
|
||||||
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
|
Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect()
|
||||||
else:
|
else:
|
||||||
if !cleared:
|
if !cleared:
|
||||||
|
@ -152,7 +128,7 @@ func _clear() -> void:
|
||||||
|
|
||||||
|
|
||||||
func lasso_selection(
|
func lasso_selection(
|
||||||
selection_map: SelectionMap, previous_selection_map: SelectionMap, points: Array[Vector2i]
|
points: Array[Vector2i], selection_map: SelectionMap, previous_selection_map: SelectionMap
|
||||||
) -> void:
|
) -> void:
|
||||||
var selection_size := selection_map.get_size()
|
var selection_size := selection_map.get_size()
|
||||||
var bounding_rect := Rect2i(points[0], Vector2i.ZERO)
|
var bounding_rect := Rect2i(points[0], Vector2i.ZERO)
|
||||||
|
|
|
@ -8,7 +8,7 @@ var _content_transformation_check := false
|
||||||
var _snap_to_grid := false ## Mouse Click + Ctrl
|
var _snap_to_grid := false ## Mouse Click + Ctrl
|
||||||
var _undo_data := {}
|
var _undo_data := {}
|
||||||
|
|
||||||
@onready var selection_node: Node2D = Global.canvas.selection
|
@onready var selection_node := Global.canvas.selection
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
|
@ -78,19 +78,15 @@ func draw_end(pos: Vector2i) -> void:
|
||||||
and _content_transformation_check == selection_node.is_moving_content
|
and _content_transformation_check == selection_node.is_moving_content
|
||||||
):
|
):
|
||||||
pos = _snap_position(pos)
|
pos = _snap_position(pos)
|
||||||
var project := Global.current_project
|
if Global.current_project.has_selection:
|
||||||
|
|
||||||
if project.has_selection:
|
|
||||||
selection_node.move_borders_end()
|
selection_node.move_borders_end()
|
||||||
else:
|
else:
|
||||||
var pixel_diff := pos - _start_pos
|
var pixel_diff := pos - _start_pos
|
||||||
Global.canvas.move_preview_location = Vector2i.ZERO
|
Global.canvas.move_preview_location = Vector2i.ZERO
|
||||||
var images := _get_selected_draw_images()
|
var images := _get_selected_draw_images()
|
||||||
for image in images:
|
for image in images:
|
||||||
var image_copy := Image.new()
|
_move_image(image, pixel_diff)
|
||||||
image_copy.copy_from(image)
|
_move_image(image.indices_image, pixel_diff)
|
||||||
image.fill(Color(0, 0, 0, 0))
|
|
||||||
image.blit_rect(image_copy, Rect2i(Vector2i.ZERO, project.size), pixel_diff)
|
|
||||||
_commit_undo("Draw")
|
_commit_undo("Draw")
|
||||||
|
|
||||||
_start_pos = Vector2.INF
|
_start_pos = Vector2.INF
|
||||||
|
@ -99,6 +95,13 @@ func draw_end(pos: Vector2i) -> void:
|
||||||
Global.canvas.measurements.update_measurement(Global.MeasurementMode.NONE)
|
Global.canvas.measurements.update_measurement(Global.MeasurementMode.NONE)
|
||||||
|
|
||||||
|
|
||||||
|
func _move_image(image: Image, pixel_diff: Vector2i) -> void:
|
||||||
|
var image_copy := Image.new()
|
||||||
|
image_copy.copy_from(image)
|
||||||
|
image.fill(Color(0, 0, 0, 0))
|
||||||
|
image.blit_rect(image_copy, Rect2i(Vector2i.ZERO, image.get_size()), pixel_diff)
|
||||||
|
|
||||||
|
|
||||||
func _snap_position(pos: Vector2) -> Vector2:
|
func _snap_position(pos: Vector2) -> Vector2:
|
||||||
if Input.is_action_pressed("transform_snap_axis"):
|
if Input.is_action_pressed("transform_snap_axis"):
|
||||||
var angle := pos.angle_to_point(_start_pos)
|
var angle := pos.angle_to_point(_start_pos)
|
||||||
|
@ -155,6 +158,6 @@ func _get_undo_data() -> Dictionary:
|
||||||
for cel in cels:
|
for cel in cels:
|
||||||
if not cel is PixelCel:
|
if not cel is PixelCel:
|
||||||
continue
|
continue
|
||||||
var image: Image = cel.image
|
var image := (cel as PixelCel).get_image()
|
||||||
data[image] = image.data
|
image.add_data_to_dictionary(data)
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -149,12 +149,14 @@ func text_to_pixels() -> void:
|
||||||
RenderingServer.free_rid(canvas)
|
RenderingServer.free_rid(canvas)
|
||||||
RenderingServer.free_rid(ci_rid)
|
RenderingServer.free_rid(ci_rid)
|
||||||
RenderingServer.free_rid(texture)
|
RenderingServer.free_rid(texture)
|
||||||
viewport_texture.convert(Image.FORMAT_RGBA8)
|
viewport_texture.convert(image.get_format())
|
||||||
|
|
||||||
text_edit.queue_free()
|
text_edit.queue_free()
|
||||||
text_edit = null
|
text_edit = null
|
||||||
if not viewport_texture.is_empty():
|
if not viewport_texture.is_empty():
|
||||||
image.copy_from(viewport_texture)
|
image.copy_from(viewport_texture)
|
||||||
|
if image is ImageExtended:
|
||||||
|
image.convert_rgb_to_indexed()
|
||||||
commit_undo("Draw", undo_data)
|
commit_undo("Draw", undo_data)
|
||||||
|
|
||||||
|
|
||||||
|
@ -179,7 +181,7 @@ func _get_undo_data() -> Dictionary:
|
||||||
var data := {}
|
var data := {}
|
||||||
var images := _get_selected_draw_images()
|
var images := _get_selected_draw_images()
|
||||||
for image in images:
|
for image in images:
|
||||||
data[image] = image.data
|
image.add_data_to_dictionary(data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class_name Guide
|
class_name Guide
|
||||||
extends Line2D
|
extends Line2D
|
||||||
|
|
||||||
enum Types { HORIZONTAL, VERTICAL }
|
enum Types { HORIZONTAL, VERTICAL, XY, X_MINUS_Y }
|
||||||
|
|
||||||
const INPUT_WIDTH := 4
|
const INPUT_WIDTH := 4
|
||||||
|
|
||||||
|
@ -31,12 +31,13 @@ func _input(_event: InputEvent) -> void:
|
||||||
if type == Types.HORIZONTAL:
|
if type == Types.HORIZONTAL:
|
||||||
point0.y -= width * INPUT_WIDTH
|
point0.y -= width * INPUT_WIDTH
|
||||||
point1.y += width * INPUT_WIDTH
|
point1.y += width * INPUT_WIDTH
|
||||||
else:
|
elif type == Types.VERTICAL:
|
||||||
point0.x -= width * INPUT_WIDTH
|
point0.x -= width * INPUT_WIDTH
|
||||||
point1.x += width * INPUT_WIDTH
|
point1.x += width * INPUT_WIDTH
|
||||||
var rect := Rect2()
|
var rect := Rect2()
|
||||||
rect.position = point0
|
rect.position = point0
|
||||||
rect.end = point1
|
rect.end = point1
|
||||||
|
rect = rect.abs()
|
||||||
if (
|
if (
|
||||||
Input.is_action_just_pressed(&"left_mouse")
|
Input.is_action_just_pressed(&"left_mouse")
|
||||||
and Global.can_draw
|
and Global.can_draw
|
||||||
|
@ -55,7 +56,7 @@ func _input(_event: InputEvent) -> void:
|
||||||
var yy := snappedf(mouse_pos.y, 0.5)
|
var yy := snappedf(mouse_pos.y, 0.5)
|
||||||
points[0].y = yy
|
points[0].y = yy
|
||||||
points[1].y = yy
|
points[1].y = yy
|
||||||
else:
|
elif type == Types.VERTICAL:
|
||||||
var xx := snappedf(mouse_pos.x, 0.5)
|
var xx := snappedf(mouse_pos.x, 0.5)
|
||||||
points[0].x = xx
|
points[0].x = xx
|
||||||
points[1].x = xx
|
points[1].x = xx
|
||||||
|
@ -221,14 +222,22 @@ func set_color(color: Color) -> void:
|
||||||
default_color = color
|
default_color = color
|
||||||
|
|
||||||
|
|
||||||
|
func get_direction() -> Vector2:
|
||||||
|
return points[0].direction_to(points[1])
|
||||||
|
|
||||||
|
|
||||||
func _project_switched() -> void:
|
func _project_switched() -> void:
|
||||||
if self in Global.current_project.guides:
|
if self in Global.current_project.guides:
|
||||||
visible = Global.show_guides
|
visible = Global.show_guides
|
||||||
if self is SymmetryGuide:
|
if self is SymmetryGuide:
|
||||||
if type == Types.HORIZONTAL:
|
if type == Types.HORIZONTAL:
|
||||||
visible = Global.show_x_symmetry_axis and Global.show_guides
|
visible = Global.show_x_symmetry_axis and Global.show_guides
|
||||||
else:
|
elif type == Types.VERTICAL:
|
||||||
visible = Global.show_y_symmetry_axis and Global.show_guides
|
visible = Global.show_y_symmetry_axis and Global.show_guides
|
||||||
|
elif type == Types.XY:
|
||||||
|
visible = Global.show_xy_symmetry_axis and Global.show_guides
|
||||||
|
elif type == Types.X_MINUS_Y:
|
||||||
|
visible = Global.show_x_minus_y_symmetry_axis and Global.show_guides
|
||||||
else:
|
else:
|
||||||
visible = false
|
visible = false
|
||||||
|
|
||||||
|
|
|
@ -516,6 +516,7 @@ func transform_content_confirm() -> void:
|
||||||
Rect2i(Vector2i.ZERO, project.selection_map.get_size()),
|
Rect2i(Vector2i.ZERO, project.selection_map.get_size()),
|
||||||
big_bounding_rectangle.position
|
big_bounding_rectangle.position
|
||||||
)
|
)
|
||||||
|
cel_image.convert_rgb_to_indexed()
|
||||||
project.selection_map.move_bitmap_values(project)
|
project.selection_map.move_bitmap_values(project)
|
||||||
commit_undo("Move Selection", undo_data)
|
commit_undo("Move Selection", undo_data)
|
||||||
|
|
||||||
|
@ -605,13 +606,13 @@ func get_undo_data(undo_image: bool) -> Dictionary:
|
||||||
if undo_image:
|
if undo_image:
|
||||||
var images := _get_selected_draw_images()
|
var images := _get_selected_draw_images()
|
||||||
for image in images:
|
for image in images:
|
||||||
data[image] = image.data
|
image.add_data_to_dictionary(data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
func _get_selected_draw_cels() -> Array[BaseCel]:
|
func _get_selected_draw_cels() -> Array[PixelCel]:
|
||||||
var cels: Array[BaseCel] = []
|
var cels: Array[PixelCel] = []
|
||||||
var project := Global.current_project
|
var project := Global.current_project
|
||||||
for cel_index in project.selected_cels:
|
for cel_index in project.selected_cels:
|
||||||
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
||||||
|
@ -622,8 +623,8 @@ func _get_selected_draw_cels() -> Array[BaseCel]:
|
||||||
return cels
|
return cels
|
||||||
|
|
||||||
|
|
||||||
func _get_selected_draw_images() -> Array[Image]:
|
func _get_selected_draw_images() -> Array[ImageExtended]:
|
||||||
var images: Array[Image] = []
|
var images: Array[ImageExtended] = []
|
||||||
var project := Global.current_project
|
var project := Global.current_project
|
||||||
for cel_index in project.selected_cels:
|
for cel_index in project.selected_cels:
|
||||||
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
||||||
|
@ -794,14 +795,14 @@ func delete(selected_cels := true) -> void:
|
||||||
return
|
return
|
||||||
|
|
||||||
var undo_data_tmp := get_undo_data(true)
|
var undo_data_tmp := get_undo_data(true)
|
||||||
var images: Array[Image]
|
var images: Array[ImageExtended]
|
||||||
if selected_cels:
|
if selected_cels:
|
||||||
images = _get_selected_draw_images()
|
images = _get_selected_draw_images()
|
||||||
else:
|
else:
|
||||||
images = [project.get_current_cel().get_image()]
|
images = [project.get_current_cel().get_image()]
|
||||||
|
|
||||||
if project.has_selection:
|
if project.has_selection:
|
||||||
var blank := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var blank := project.new_empty_image()
|
||||||
var selection_map_copy := project.selection_map.return_cropped_copy(project.size)
|
var selection_map_copy := project.selection_map.return_cropped_copy(project.size)
|
||||||
for image in images:
|
for image in images:
|
||||||
image.blit_rect_mask(
|
image.blit_rect_mask(
|
||||||
|
@ -870,13 +871,16 @@ func _project_switched() -> void:
|
||||||
|
|
||||||
func _get_preview_image() -> void:
|
func _get_preview_image() -> void:
|
||||||
var project := Global.current_project
|
var project := Global.current_project
|
||||||
var blended_image := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var blended_image := project.new_empty_image()
|
||||||
DrawingAlgos.blend_layers(
|
DrawingAlgos.blend_layers(
|
||||||
blended_image, project.frames[project.current_frame], Vector2i.ZERO, project, true
|
blended_image, project.frames[project.current_frame], Vector2i.ZERO, project, true
|
||||||
)
|
)
|
||||||
if original_preview_image.is_empty():
|
if original_preview_image.is_empty():
|
||||||
original_preview_image = Image.create(
|
original_preview_image = Image.create(
|
||||||
big_bounding_rectangle.size.x, big_bounding_rectangle.size.y, false, Image.FORMAT_RGBA8
|
big_bounding_rectangle.size.x,
|
||||||
|
big_bounding_rectangle.size.y,
|
||||||
|
false,
|
||||||
|
project.get_image_format()
|
||||||
)
|
)
|
||||||
var selection_map_copy := project.selection_map.return_cropped_copy(project.size)
|
var selection_map_copy := project.selection_map.return_cropped_copy(project.size)
|
||||||
original_preview_image.blit_rect_mask(
|
original_preview_image.blit_rect_mask(
|
||||||
|
@ -892,11 +896,11 @@ func _get_preview_image() -> void:
|
||||||
var clear_image := Image.create(
|
var clear_image := Image.create(
|
||||||
original_preview_image.get_width(),
|
original_preview_image.get_width(),
|
||||||
original_preview_image.get_height(),
|
original_preview_image.get_height(),
|
||||||
false,
|
original_preview_image.has_mipmaps(),
|
||||||
Image.FORMAT_RGBA8
|
original_preview_image.get_format()
|
||||||
)
|
)
|
||||||
for cel in _get_selected_draw_cels():
|
for cel in _get_selected_draw_cels():
|
||||||
var cel_image: Image = cel.get_image()
|
var cel_image := cel.get_image()
|
||||||
cel.transformed_content = _get_selected_image(cel_image)
|
cel.transformed_content = _get_selected_image(cel_image)
|
||||||
cel_image.blit_rect_mask(
|
cel_image.blit_rect_mask(
|
||||||
clear_image,
|
clear_image,
|
||||||
|
@ -911,7 +915,10 @@ func _get_preview_image() -> void:
|
||||||
func _get_selected_image(cel_image: Image) -> Image:
|
func _get_selected_image(cel_image: Image) -> Image:
|
||||||
var project := Global.current_project
|
var project := Global.current_project
|
||||||
var image := Image.create(
|
var image := Image.create(
|
||||||
big_bounding_rectangle.size.x, big_bounding_rectangle.size.y, false, Image.FORMAT_RGBA8
|
big_bounding_rectangle.size.x,
|
||||||
|
big_bounding_rectangle.size.y,
|
||||||
|
false,
|
||||||
|
project.get_image_format()
|
||||||
)
|
)
|
||||||
var selection_map_copy := project.selection_map.return_cropped_copy(project.size)
|
var selection_map_copy := project.selection_map.return_cropped_copy(project.size)
|
||||||
image.blit_rect_mask(cel_image, selection_map_copy, big_bounding_rectangle, Vector2i.ZERO)
|
image.blit_rect_mask(cel_image, selection_map_copy, big_bounding_rectangle, Vector2i.ZERO)
|
||||||
|
|
|
@ -52,7 +52,9 @@ var templates: Array[Template] = [
|
||||||
@onready var height_value := %HeightValue as SpinBox
|
@onready var height_value := %HeightValue as SpinBox
|
||||||
@onready var portrait_button := %PortraitButton as Button
|
@onready var portrait_button := %PortraitButton as Button
|
||||||
@onready var landscape_button := %LandscapeButton as Button
|
@onready var landscape_button := %LandscapeButton as Button
|
||||||
|
@onready var name_input := $VBoxContainer/FillColorContainer/NameInput as LineEdit
|
||||||
@onready var fill_color_node := %FillColor as ColorPickerButton
|
@onready var fill_color_node := %FillColor as ColorPickerButton
|
||||||
|
@onready var color_mode := $VBoxContainer/FillColorContainer/ColorMode as OptionButton
|
||||||
@onready var recent_templates_list := %RecentTemplates as ItemList
|
@onready var recent_templates_list := %RecentTemplates as ItemList
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,13 +125,14 @@ func _on_CreateNewImage_confirmed() -> void:
|
||||||
if recent_sizes.size() > 10:
|
if recent_sizes.size() > 10:
|
||||||
recent_sizes.resize(10)
|
recent_sizes.resize(10)
|
||||||
Global.config_cache.set_value("templates", "recent_sizes", recent_sizes)
|
Global.config_cache.set_value("templates", "recent_sizes", recent_sizes)
|
||||||
var fill_color: Color = fill_color_node.color
|
var fill_color := fill_color_node.color
|
||||||
|
var proj_name := name_input.text
|
||||||
var proj_name: String = $VBoxContainer/ProjectName/NameInput.text
|
|
||||||
if !proj_name.is_valid_filename():
|
if !proj_name.is_valid_filename():
|
||||||
proj_name = tr("untitled")
|
proj_name = tr("untitled")
|
||||||
|
|
||||||
var new_project := Project.new([], proj_name, image_size)
|
var new_project := Project.new([], proj_name, image_size)
|
||||||
|
if color_mode.selected == 1:
|
||||||
|
new_project.color_mode = Project.INDEXED_MODE
|
||||||
new_project.layers.append(PixelLayer.new(new_project))
|
new_project.layers.append(PixelLayer.new(new_project))
|
||||||
new_project.fill_color = fill_color
|
new_project.fill_color = fill_color
|
||||||
new_project.frames.append(new_project.new_empty_frame())
|
new_project.frames.append(new_project.new_empty_frame())
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
|
|
||||||
[node name="CreateNewImage" type="ConfirmationDialog"]
|
[node name="CreateNewImage" type="ConfirmationDialog"]
|
||||||
title = "New..."
|
title = "New..."
|
||||||
size = Vector2i(384, 330)
|
position = Vector2i(0, 36)
|
||||||
|
size = Vector2i(434, 330)
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||||
|
@ -22,19 +23,6 @@ offset_right = -8.0
|
||||||
offset_bottom = -49.0
|
offset_bottom = -49.0
|
||||||
size_flags_horizontal = 0
|
size_flags_horizontal = 0
|
||||||
|
|
||||||
[node name="ProjectName" type="HBoxContainer" parent="VBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="NameLabel" type="Label" parent="VBoxContainer/ProjectName"]
|
|
||||||
custom_minimum_size = Vector2(100, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Project Name:"
|
|
||||||
|
|
||||||
[node name="NameInput" type="LineEdit" parent="VBoxContainer/ProjectName"]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
placeholder_text = "Enter name... (Default \"untitled\")"
|
|
||||||
|
|
||||||
[node name="ImageSize" type="Label" parent="VBoxContainer"]
|
[node name="ImageSize" type="Label" parent="VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Image Size"
|
text = "Image Size"
|
||||||
|
@ -42,49 +30,48 @@ text = "Image Size"
|
||||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer"]
|
[node name="HSeparator" type="HSeparator" parent="VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="VBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
[node name="SizeContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="Templates" type="VBoxContainer" parent="VBoxContainer/VBoxContainer"]
|
[node name="SizeOptions" type="VBoxContainer" parent="VBoxContainer/SizeContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="TemplatesContainer" type="HBoxContainer" parent="VBoxContainer/VBoxContainer/Templates"]
|
[node name="TemplatesContainer" type="HBoxContainer" parent="VBoxContainer/SizeContainer/SizeOptions"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="TemplatesLabel" type="Label" parent="VBoxContainer/VBoxContainer/Templates/TemplatesContainer"]
|
[node name="TemplatesLabel" type="Label" parent="VBoxContainer/SizeContainer/SizeOptions/TemplatesContainer"]
|
||||||
custom_minimum_size = Vector2(100, 0)
|
custom_minimum_size = Vector2(100, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Templates:"
|
text = "Templates:"
|
||||||
|
|
||||||
[node name="TemplatesOptions" type="OptionButton" parent="VBoxContainer/VBoxContainer/Templates/TemplatesContainer"]
|
[node name="TemplatesOptions" type="OptionButton" parent="VBoxContainer/SizeContainer/SizeOptions/TemplatesContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
mouse_default_cursor_shape = 2
|
mouse_default_cursor_shape = 2
|
||||||
toggle_mode = false
|
toggle_mode = false
|
||||||
item_count = 1
|
|
||||||
selected = 0
|
selected = 0
|
||||||
|
item_count = 1
|
||||||
popup/item_0/text = "Default"
|
popup/item_0/text = "Default"
|
||||||
popup/item_0/id = 0
|
|
||||||
|
|
||||||
[node name="SizeContainer" type="HBoxContainer" parent="VBoxContainer/VBoxContainer/Templates"]
|
[node name="WidthHeightContainer" type="HBoxContainer" parent="VBoxContainer/SizeContainer/SizeOptions"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/VBoxContainer/Templates/SizeContainer"]
|
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="WidthContainer" type="HBoxContainer" parent="VBoxContainer/VBoxContainer/Templates/SizeContainer/VBoxContainer"]
|
[node name="WidthContainer" type="HBoxContainer" parent="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="WidthLabel" type="Label" parent="VBoxContainer/VBoxContainer/Templates/SizeContainer/VBoxContainer/WidthContainer"]
|
[node name="WidthLabel" type="Label" parent="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer/VBoxContainer/WidthContainer"]
|
||||||
custom_minimum_size = Vector2(100, 0)
|
custom_minimum_size = Vector2(100, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Width:"
|
text = "Width:"
|
||||||
|
|
||||||
[node name="WidthValue" type="SpinBox" parent="VBoxContainer/VBoxContainer/Templates/SizeContainer/VBoxContainer/WidthContainer"]
|
[node name="WidthValue" type="SpinBox" parent="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer/VBoxContainer/WidthContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
@ -94,15 +81,15 @@ max_value = 16384.0
|
||||||
value = 64.0
|
value = 64.0
|
||||||
suffix = "px"
|
suffix = "px"
|
||||||
|
|
||||||
[node name="HeightContainer" type="HBoxContainer" parent="VBoxContainer/VBoxContainer/Templates/SizeContainer/VBoxContainer"]
|
[node name="HeightContainer" type="HBoxContainer" parent="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="HeightLabel" type="Label" parent="VBoxContainer/VBoxContainer/Templates/SizeContainer/VBoxContainer/HeightContainer"]
|
[node name="HeightLabel" type="Label" parent="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer/VBoxContainer/HeightContainer"]
|
||||||
custom_minimum_size = Vector2(100, 0)
|
custom_minimum_size = Vector2(100, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Height:"
|
text = "Height:"
|
||||||
|
|
||||||
[node name="HeightValue" type="SpinBox" parent="VBoxContainer/VBoxContainer/Templates/SizeContainer/VBoxContainer/HeightContainer"]
|
[node name="HeightValue" type="SpinBox" parent="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer/VBoxContainer/HeightContainer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
@ -112,11 +99,11 @@ max_value = 16384.0
|
||||||
value = 64.0
|
value = 64.0
|
||||||
suffix = "px"
|
suffix = "px"
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/VBoxContainer/Templates/SizeContainer" groups=["UIButtons"]]
|
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer" groups=["UIButtons"]]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
texture = ExtResource("6")
|
texture = ExtResource("6")
|
||||||
|
|
||||||
[node name="AspectRatioButton" type="TextureButton" parent="VBoxContainer/VBoxContainer/Templates/SizeContainer/TextureRect" groups=["UIButtons"]]
|
[node name="AspectRatioButton" type="TextureButton" parent="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer/TextureRect" groups=["UIButtons"]]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
anchor_left = 0.5
|
anchor_left = 0.5
|
||||||
|
@ -133,31 +120,10 @@ toggle_mode = true
|
||||||
texture_normal = ExtResource("4")
|
texture_normal = ExtResource("4")
|
||||||
texture_pressed = ExtResource("5")
|
texture_pressed = ExtResource("5")
|
||||||
|
|
||||||
[node name="VSeparator" type="VSeparator" parent="VBoxContainer/VBoxContainer"]
|
[node name="SizeButtonsContainer" type="HBoxContainer" parent="VBoxContainer/SizeContainer/SizeOptions"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/VBoxContainer"]
|
[node name="PortraitButton" type="Button" parent="VBoxContainer/SizeContainer/SizeOptions/SizeButtonsContainer" groups=["UIButtons"]]
|
||||||
clip_contents = true
|
|
||||||
custom_minimum_size = Vector2(150, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
focus_mode = 2
|
|
||||||
mouse_filter = 0
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="VBoxContainer/VBoxContainer/VBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Recent:"
|
|
||||||
|
|
||||||
[node name="RecentTemplates" type="ItemList" parent="VBoxContainer/VBoxContainer/VBoxContainer"]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
size_flags_vertical = 3
|
|
||||||
allow_reselect = true
|
|
||||||
|
|
||||||
[node name="SizeButtonsContainer" type="HBoxContainer" parent="VBoxContainer"]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="PortraitButton" type="Button" parent="VBoxContainer/SizeButtonsContainer" groups=["UIButtons"]]
|
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(24, 24)
|
custom_minimum_size = Vector2(24, 24)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
@ -166,7 +132,7 @@ focus_mode = 0
|
||||||
mouse_default_cursor_shape = 2
|
mouse_default_cursor_shape = 2
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/SizeButtonsContainer/PortraitButton"]
|
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/SizeContainer/SizeOptions/SizeButtonsContainer/PortraitButton"]
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
anchor_left = 0.5
|
anchor_left = 0.5
|
||||||
anchor_top = 0.5
|
anchor_top = 0.5
|
||||||
|
@ -178,7 +144,7 @@ offset_right = 8.0
|
||||||
offset_bottom = 8.0
|
offset_bottom = 8.0
|
||||||
texture = ExtResource("2")
|
texture = ExtResource("2")
|
||||||
|
|
||||||
[node name="LandscapeButton" type="Button" parent="VBoxContainer/SizeButtonsContainer" groups=["UIButtons"]]
|
[node name="LandscapeButton" type="Button" parent="VBoxContainer/SizeContainer/SizeOptions/SizeButtonsContainer" groups=["UIButtons"]]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(24, 24)
|
custom_minimum_size = Vector2(24, 24)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
@ -187,7 +153,7 @@ focus_mode = 0
|
||||||
mouse_default_cursor_shape = 2
|
mouse_default_cursor_shape = 2
|
||||||
toggle_mode = true
|
toggle_mode = true
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/SizeButtonsContainer/LandscapeButton"]
|
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/SizeContainer/SizeOptions/SizeButtonsContainer/LandscapeButton"]
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
anchor_left = 0.5
|
anchor_left = 0.5
|
||||||
anchor_top = 0.5
|
anchor_top = 0.5
|
||||||
|
@ -199,12 +165,49 @@ offset_right = 8.0
|
||||||
offset_bottom = 8.0
|
offset_bottom = 8.0
|
||||||
texture = ExtResource("3")
|
texture = ExtResource("3")
|
||||||
|
|
||||||
[node name="FillColorContainer" type="HBoxContainer" parent="VBoxContainer"]
|
[node name="VSeparator" type="VSeparator" parent="VBoxContainer/SizeContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/SizeContainer"]
|
||||||
|
clip_contents = true
|
||||||
|
custom_minimum_size = Vector2(150, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
focus_mode = 2
|
||||||
|
mouse_filter = 0
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="VBoxContainer/SizeContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Recent:"
|
||||||
|
|
||||||
|
[node name="RecentTemplates" type="ItemList" parent="VBoxContainer/SizeContainer/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
allow_reselect = true
|
||||||
|
|
||||||
|
[node name="HSeparator2" type="HSeparator" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="FillColorContainer" type="GridContainer" parent="VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
columns = 2
|
||||||
|
|
||||||
|
[node name="NameLabel" type="Label" parent="VBoxContainer/FillColorContainer"]
|
||||||
|
custom_minimum_size = Vector2(100, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "Project Name:"
|
||||||
|
|
||||||
|
[node name="NameInput" type="LineEdit" parent="VBoxContainer/FillColorContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
placeholder_text = "Enter name... (Default \"untitled\")"
|
||||||
|
|
||||||
[node name="FillColorLabel" type="Label" parent="VBoxContainer/FillColorContainer"]
|
[node name="FillColorLabel" type="Label" parent="VBoxContainer/FillColorContainer"]
|
||||||
custom_minimum_size = Vector2(100, 0)
|
custom_minimum_size = Vector2(100, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
text = "Fill with color:"
|
text = "Fill with color:"
|
||||||
|
|
||||||
[node name="FillColor" type="ColorPickerButton" parent="VBoxContainer/FillColorContainer"]
|
[node name="FillColor" type="ColorPickerButton" parent="VBoxContainer/FillColorContainer"]
|
||||||
|
@ -215,13 +218,28 @@ size_flags_horizontal = 3
|
||||||
mouse_default_cursor_shape = 2
|
mouse_default_cursor_shape = 2
|
||||||
color = Color(0, 0, 0, 0)
|
color = Color(0, 0, 0, 0)
|
||||||
|
|
||||||
|
[node name="ColorModeLabel" type="Label" parent="VBoxContainer/FillColorContainer"]
|
||||||
|
custom_minimum_size = Vector2(100, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "Color mode:"
|
||||||
|
|
||||||
|
[node name="ColorMode" type="OptionButton" parent="VBoxContainer/FillColorContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
mouse_default_cursor_shape = 2
|
||||||
|
selected = 0
|
||||||
|
item_count = 2
|
||||||
|
popup/item_0/text = "RGBA"
|
||||||
|
popup/item_1/text = "Indexed"
|
||||||
|
popup/item_1/id = 1
|
||||||
|
|
||||||
[connection signal="about_to_popup" from="." to="." method="_on_CreateNewImage_about_to_show"]
|
[connection signal="about_to_popup" from="." to="." method="_on_CreateNewImage_about_to_show"]
|
||||||
[connection signal="confirmed" from="." to="." method="_on_CreateNewImage_confirmed"]
|
[connection signal="confirmed" from="." to="." method="_on_CreateNewImage_confirmed"]
|
||||||
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
|
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
|
||||||
[connection signal="item_selected" from="VBoxContainer/VBoxContainer/Templates/TemplatesContainer/TemplatesOptions" to="." method="_on_TemplatesOptions_item_selected"]
|
[connection signal="item_selected" from="VBoxContainer/SizeContainer/SizeOptions/TemplatesContainer/TemplatesOptions" to="." method="_on_TemplatesOptions_item_selected"]
|
||||||
[connection signal="value_changed" from="VBoxContainer/VBoxContainer/Templates/SizeContainer/VBoxContainer/WidthContainer/WidthValue" to="." method="_on_SizeValue_value_changed"]
|
[connection signal="value_changed" from="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer/VBoxContainer/WidthContainer/WidthValue" to="." method="_on_SizeValue_value_changed"]
|
||||||
[connection signal="value_changed" from="VBoxContainer/VBoxContainer/Templates/SizeContainer/VBoxContainer/HeightContainer/HeightValue" to="." method="_on_SizeValue_value_changed"]
|
[connection signal="value_changed" from="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer/VBoxContainer/HeightContainer/HeightValue" to="." method="_on_SizeValue_value_changed"]
|
||||||
[connection signal="toggled" from="VBoxContainer/VBoxContainer/Templates/SizeContainer/TextureRect/AspectRatioButton" to="." method="_on_AspectRatioButton_toggled"]
|
[connection signal="toggled" from="VBoxContainer/SizeContainer/SizeOptions/WidthHeightContainer/TextureRect/AspectRatioButton" to="." method="_on_AspectRatioButton_toggled"]
|
||||||
[connection signal="item_selected" from="VBoxContainer/VBoxContainer/VBoxContainer/RecentTemplates" to="." method="_on_RecentTemplates_item_selected"]
|
[connection signal="toggled" from="VBoxContainer/SizeContainer/SizeOptions/SizeButtonsContainer/PortraitButton" to="." method="_on_PortraitButton_toggled"]
|
||||||
[connection signal="toggled" from="VBoxContainer/SizeButtonsContainer/PortraitButton" to="." method="_on_PortraitButton_toggled"]
|
[connection signal="toggled" from="VBoxContainer/SizeContainer/SizeOptions/SizeButtonsContainer/LandscapeButton" to="." method="_on_LandscapeButton_toggled"]
|
||||||
[connection signal="toggled" from="VBoxContainer/SizeButtonsContainer/LandscapeButton" to="." method="_on_LandscapeButton_toggled"]
|
[connection signal="item_selected" from="VBoxContainer/SizeContainer/VBoxContainer/RecentTemplates" to="." method="_on_RecentTemplates_item_selected"]
|
||||||
|
|
|
@ -89,7 +89,7 @@ func commit_action(cel: Image, project := Global.current_project) -> void:
|
||||||
selection_tex = ImageTexture.create_from_image(selection)
|
selection_tex = ImageTexture.create_from_image(selection)
|
||||||
|
|
||||||
if !_type_is_shader():
|
if !_type_is_shader():
|
||||||
var blank := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
var blank := project.new_empty_image()
|
||||||
cel.blit_rect_mask(
|
cel.blit_rect_mask(
|
||||||
blank, selection, Rect2i(Vector2i.ZERO, cel.get_size()), Vector2i.ZERO
|
blank, selection, Rect2i(Vector2i.ZERO, cel.get_size()), Vector2i.ZERO
|
||||||
)
|
)
|
||||||
|
@ -136,6 +136,8 @@ func commit_action(cel: Image, project := Global.current_project) -> void:
|
||||||
cel.blend_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
|
cel.blend_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
|
||||||
else:
|
else:
|
||||||
cel.blit_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
|
cel.blit_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
|
||||||
|
if cel is ImageExtended:
|
||||||
|
cel.convert_rgb_to_indexed()
|
||||||
|
|
||||||
|
|
||||||
func _type_is_shader() -> bool:
|
func _type_is_shader() -> bool:
|
||||||
|
|
|
@ -34,7 +34,7 @@ func _on_ScaleImage_confirmed() -> void:
|
||||||
var width: int = width_value.value
|
var width: int = width_value.value
|
||||||
var height: int = height_value.value
|
var height: int = height_value.value
|
||||||
var interpolation: int = interpolation_type.selected
|
var interpolation: int = interpolation_type.selected
|
||||||
DrawingAlgos.scale_image(width, height, interpolation)
|
DrawingAlgos.scale_project(width, height, interpolation)
|
||||||
|
|
||||||
|
|
||||||
func _on_visibility_changed() -> void:
|
func _on_visibility_changed() -> void:
|
||||||
|
|
|
@ -22,7 +22,7 @@ func refresh_list() -> void:
|
||||||
animation_tags_list.clear()
|
animation_tags_list.clear()
|
||||||
get_ok_button().disabled = true
|
get_ok_button().disabled = true
|
||||||
for tag: AnimationTag in from_project.animation_tags:
|
for tag: AnimationTag in from_project.animation_tags:
|
||||||
var img = Image.create(from_project.size.x, from_project.size.y, true, Image.FORMAT_RGBA8)
|
var img := from_project.new_empty_image()
|
||||||
DrawingAlgos.blend_layers(
|
DrawingAlgos.blend_layers(
|
||||||
img, from_project.frames[tag.from - 1], Vector2i.ZERO, from_project
|
img, from_project.frames[tag.from - 1], Vector2i.ZERO, from_project
|
||||||
)
|
)
|
||||||
|
@ -186,9 +186,7 @@ func add_animation(indices: Array, destination: int, from_tag: AnimationTag = nu
|
||||||
# add more types here if they have a copy_content() method
|
# add more types here if they have a copy_content() method
|
||||||
if src_cel is PixelCel:
|
if src_cel is PixelCel:
|
||||||
var src_img = src_cel.copy_content()
|
var src_img = src_cel.copy_content()
|
||||||
var copy := Image.create(
|
var copy := project.new_empty_image()
|
||||||
project.size.x, project.size.y, false, Image.FORMAT_RGBA8
|
|
||||||
)
|
|
||||||
copy.blit_rect(
|
copy.blit_rect(
|
||||||
src_img, Rect2(Vector2.ZERO, src_img.get_size()), Vector2.ZERO
|
src_img, Rect2(Vector2.ZERO, src_img.get_size()), Vector2.ZERO
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
extends AcceptDialog
|
extends AcceptDialog
|
||||||
|
|
||||||
@onready var size_value_label := $GridContainer/SizeValueLabel as Label
|
@onready var size_value_label := $GridContainer/SizeValueLabel as Label
|
||||||
|
@onready var color_mode_value_label := $GridContainer/ColorModeValueLabel as Label
|
||||||
@onready var frames_value_label := $GridContainer/FramesValueLabel as Label
|
@onready var frames_value_label := $GridContainer/FramesValueLabel as Label
|
||||||
@onready var layers_value_label := $GridContainer/LayersValueLabel as Label
|
@onready var layers_value_label := $GridContainer/LayersValueLabel as Label
|
||||||
@onready var name_line_edit := $GridContainer/NameLineEdit as LineEdit
|
@onready var name_line_edit := $GridContainer/NameLineEdit as LineEdit
|
||||||
|
@ -10,6 +11,12 @@ extends AcceptDialog
|
||||||
func _on_visibility_changed() -> void:
|
func _on_visibility_changed() -> void:
|
||||||
Global.dialog_open(visible)
|
Global.dialog_open(visible)
|
||||||
size_value_label.text = str(Global.current_project.size)
|
size_value_label.text = str(Global.current_project.size)
|
||||||
|
if Global.current_project.get_image_format() == Image.FORMAT_RGBA8:
|
||||||
|
color_mode_value_label.text = "RGBA8"
|
||||||
|
else:
|
||||||
|
color_mode_value_label.text = str(Global.current_project.get_image_format())
|
||||||
|
if Global.current_project.is_indexed():
|
||||||
|
color_mode_value_label.text += " (%s)" % tr("Indexed")
|
||||||
frames_value_label.text = str(Global.current_project.frames.size())
|
frames_value_label.text = str(Global.current_project.frames.size())
|
||||||
layers_value_label.text = str(Global.current_project.layers.size())
|
layers_value_label.text = str(Global.current_project.layers.size())
|
||||||
name_line_edit.text = Global.current_project.name
|
name_line_edit.text = Global.current_project.name
|
||||||
|
|
|
@ -24,6 +24,16 @@ layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
text = "64x64"
|
text = "64x64"
|
||||||
|
|
||||||
|
[node name="ColorModeLabel" type="Label" parent="GridContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "Color mode:"
|
||||||
|
|
||||||
|
[node name="ColorModeValueLabel" type="Label" parent="GridContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "RGBA8"
|
||||||
|
|
||||||
[node name="FramesLabel" type="Label" parent="GridContainer"]
|
[node name="FramesLabel" type="Label" parent="GridContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
@ -32,7 +42,7 @@ text = "Frames:"
|
||||||
[node name="FramesValueLabel" type="Label" parent="GridContainer"]
|
[node name="FramesValueLabel" type="Label" parent="GridContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
text = "64x64"
|
text = "1"
|
||||||
|
|
||||||
[node name="LayersLabel" type="Label" parent="GridContainer"]
|
[node name="LayersLabel" type="Label" parent="GridContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
@ -42,7 +52,7 @@ text = "Layers:"
|
||||||
[node name="LayersValueLabel" type="Label" parent="GridContainer"]
|
[node name="LayersValueLabel" type="Label" parent="GridContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
text = "64x64"
|
text = "1"
|
||||||
|
|
||||||
[node name="NameLabel" type="Label" parent="GridContainer"]
|
[node name="NameLabel" type="Label" parent="GridContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
|
@ -3,6 +3,8 @@ extends PanelContainer
|
||||||
@onready var grid_container: GridContainer = find_child("GridContainer")
|
@onready var grid_container: GridContainer = find_child("GridContainer")
|
||||||
@onready var horizontal_mirror: BaseButton = grid_container.get_node("Horizontal")
|
@onready var horizontal_mirror: BaseButton = grid_container.get_node("Horizontal")
|
||||||
@onready var vertical_mirror: BaseButton = grid_container.get_node("Vertical")
|
@onready var vertical_mirror: BaseButton = grid_container.get_node("Vertical")
|
||||||
|
@onready var diagonal_xy_mirror: BaseButton = grid_container.get_node("DiagonalXY")
|
||||||
|
@onready var diagonal_x_minus_y_mirror: BaseButton = grid_container.get_node("DiagonalXMinusY")
|
||||||
@onready var pixel_perfect: BaseButton = grid_container.get_node("PixelPerfect")
|
@onready var pixel_perfect: BaseButton = grid_container.get_node("PixelPerfect")
|
||||||
@onready var alpha_lock: BaseButton = grid_container.get_node("AlphaLock")
|
@onready var alpha_lock: BaseButton = grid_container.get_node("AlphaLock")
|
||||||
@onready var dynamics: Button = $"%Dynamics"
|
@onready var dynamics: Button = $"%Dynamics"
|
||||||
|
@ -39,25 +41,25 @@ func _on_resized() -> void:
|
||||||
grid_container.columns = column_n
|
grid_container.columns = column_n
|
||||||
|
|
||||||
|
|
||||||
func _on_Horizontal_toggled(button_pressed: bool) -> void:
|
func _on_Horizontal_toggled(toggled_on: bool) -> void:
|
||||||
Tools.horizontal_mirror = button_pressed
|
Tools.horizontal_mirror = toggled_on
|
||||||
Global.config_cache.set_value("tools", "horizontal_mirror", button_pressed)
|
Global.config_cache.set_value("tools", "horizontal_mirror", toggled_on)
|
||||||
Global.show_y_symmetry_axis = button_pressed
|
Global.show_y_symmetry_axis = toggled_on
|
||||||
Global.current_project.y_symmetry_axis.visible = (
|
Global.current_project.y_symmetry_axis.visible = (
|
||||||
Global.show_y_symmetry_axis and Global.show_guides
|
Global.show_y_symmetry_axis and Global.show_guides
|
||||||
)
|
)
|
||||||
|
|
||||||
var texture_button: TextureRect = horizontal_mirror.get_node("TextureRect")
|
var texture_button: TextureRect = horizontal_mirror.get_node("TextureRect")
|
||||||
var file_name := "horizontal_mirror_on.png"
|
var file_name := "horizontal_mirror_on.png"
|
||||||
if !button_pressed:
|
if not toggled_on:
|
||||||
file_name = "horizontal_mirror_off.png"
|
file_name = "horizontal_mirror_off.png"
|
||||||
Global.change_button_texturerect(texture_button, file_name)
|
Global.change_button_texturerect(texture_button, file_name)
|
||||||
|
|
||||||
|
|
||||||
func _on_Vertical_toggled(button_pressed: bool) -> void:
|
func _on_Vertical_toggled(toggled_on: bool) -> void:
|
||||||
Tools.vertical_mirror = button_pressed
|
Tools.vertical_mirror = toggled_on
|
||||||
Global.config_cache.set_value("tools", "vertical_mirror", button_pressed)
|
Global.config_cache.set_value("tools", "vertical_mirror", toggled_on)
|
||||||
Global.show_x_symmetry_axis = button_pressed
|
Global.show_x_symmetry_axis = toggled_on
|
||||||
# If the button is not pressed but another button is, keep the symmetry guide visible
|
# If the button is not pressed but another button is, keep the symmetry guide visible
|
||||||
Global.current_project.x_symmetry_axis.visible = (
|
Global.current_project.x_symmetry_axis.visible = (
|
||||||
Global.show_x_symmetry_axis and Global.show_guides
|
Global.show_x_symmetry_axis and Global.show_guides
|
||||||
|
@ -65,11 +67,43 @@ func _on_Vertical_toggled(button_pressed: bool) -> void:
|
||||||
|
|
||||||
var texture_button: TextureRect = vertical_mirror.get_node("TextureRect")
|
var texture_button: TextureRect = vertical_mirror.get_node("TextureRect")
|
||||||
var file_name := "vertical_mirror_on.png"
|
var file_name := "vertical_mirror_on.png"
|
||||||
if !button_pressed:
|
if not toggled_on:
|
||||||
file_name = "vertical_mirror_off.png"
|
file_name = "vertical_mirror_off.png"
|
||||||
Global.change_button_texturerect(texture_button, file_name)
|
Global.change_button_texturerect(texture_button, file_name)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_diagonal_xy_toggled(toggled_on: bool) -> void:
|
||||||
|
Tools.diagonal_xy_mirror = toggled_on
|
||||||
|
Global.config_cache.set_value("tools", "diagonal_xy_mirror", toggled_on)
|
||||||
|
Global.show_xy_symmetry_axis = toggled_on
|
||||||
|
# If the button is not pressed but another button is, keep the symmetry guide visible
|
||||||
|
Global.current_project.diagonal_xy_symmetry_axis.visible = (
|
||||||
|
Global.show_xy_symmetry_axis and Global.show_guides
|
||||||
|
)
|
||||||
|
|
||||||
|
var texture_button: TextureRect = diagonal_xy_mirror.get_node("TextureRect")
|
||||||
|
var file_name := "xy_mirror_on.png"
|
||||||
|
if not toggled_on:
|
||||||
|
file_name = "xy_mirror_off.png"
|
||||||
|
Global.change_button_texturerect(texture_button, file_name)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_diagonal_x_minus_y_toggled(toggled_on: bool) -> void:
|
||||||
|
Tools.diagonal_x_minus_y_mirror = toggled_on
|
||||||
|
Global.config_cache.set_value("tools", "diagonal_x_minus_y_mirror", toggled_on)
|
||||||
|
Global.show_x_minus_y_symmetry_axis = toggled_on
|
||||||
|
# If the button is not pressed but another button is, keep the symmetry guide visible
|
||||||
|
Global.current_project.diagonal_x_minus_y_symmetry_axis.visible = (
|
||||||
|
Global.show_x_minus_y_symmetry_axis and Global.show_guides
|
||||||
|
)
|
||||||
|
|
||||||
|
var texture_button: TextureRect = diagonal_x_minus_y_mirror.get_node("TextureRect")
|
||||||
|
var file_name := "x_minus_y_mirror_on.png"
|
||||||
|
if not toggled_on:
|
||||||
|
file_name = "x_minus_y_mirror_off.png"
|
||||||
|
Global.change_button_texturerect(texture_button, file_name)
|
||||||
|
|
||||||
|
|
||||||
func _on_PixelPerfect_toggled(button_pressed: bool) -> void:
|
func _on_PixelPerfect_toggled(button_pressed: bool) -> void:
|
||||||
Tools.pixel_perfect = button_pressed
|
Tools.pixel_perfect = button_pressed
|
||||||
Global.config_cache.set_value("tools", "pixel_perfect", button_pressed)
|
Global.config_cache.set_value("tools", "pixel_perfect", button_pressed)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=25 format=3 uid="uid://wo0hqxkst808"]
|
[gd_scene load_steps=27 format=3 uid="uid://wo0hqxkst808"]
|
||||||
|
|
||||||
[ext_resource type="Texture2D" uid="uid://cjrokejjsp5dm" path="res://assets/graphics/misc/horizontal_mirror_off.png" id="1"]
|
[ext_resource type="Texture2D" uid="uid://cjrokejjsp5dm" path="res://assets/graphics/misc/horizontal_mirror_off.png" id="1"]
|
||||||
[ext_resource type="Texture2D" uid="uid://hiduvaa73fr6" path="res://assets/graphics/misc/vertical_mirror_off.png" id="2"]
|
[ext_resource type="Texture2D" uid="uid://hiduvaa73fr6" path="res://assets/graphics/misc/vertical_mirror_off.png" id="2"]
|
||||||
|
@ -6,8 +6,10 @@
|
||||||
[ext_resource type="Texture2D" uid="uid://ct8wn8m6x4m54" path="res://assets/graphics/misc/value_arrow.svg" id="3_faalk"]
|
[ext_resource type="Texture2D" uid="uid://ct8wn8m6x4m54" path="res://assets/graphics/misc/value_arrow.svg" id="3_faalk"]
|
||||||
[ext_resource type="Texture2D" uid="uid://22h12g8p3jtd" path="res://assets/graphics/misc/pixel_perfect_off.png" id="4"]
|
[ext_resource type="Texture2D" uid="uid://22h12g8p3jtd" path="res://assets/graphics/misc/pixel_perfect_off.png" id="4"]
|
||||||
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="5"]
|
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="5"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://dlxhm0ronna25" path="res://assets/graphics/misc/xy_mirror_off.png" id="5_hcmgx"]
|
||||||
[ext_resource type="Texture2D" uid="uid://j8eywwy082a4" path="res://assets/graphics/misc/alpha_lock_off.png" id="5_jv20x"]
|
[ext_resource type="Texture2D" uid="uid://j8eywwy082a4" path="res://assets/graphics/misc/alpha_lock_off.png" id="5_jv20x"]
|
||||||
[ext_resource type="Texture2D" uid="uid://dg3dumyfj1682" path="res://assets/graphics/misc/dynamics.png" id="6"]
|
[ext_resource type="Texture2D" uid="uid://dg3dumyfj1682" path="res://assets/graphics/misc/dynamics.png" id="6"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://1kj5gcswa3t2" path="res://assets/graphics/misc/x_minus_y_mirror_off.png" id="6_sw8fy"]
|
||||||
[ext_resource type="Texture2D" uid="uid://di8au2u87jgv5" path="res://assets/graphics/misc/uncheck.png" id="7"]
|
[ext_resource type="Texture2D" uid="uid://di8au2u87jgv5" path="res://assets/graphics/misc/uncheck.png" id="7"]
|
||||||
[ext_resource type="Script" path="res://src/UI/GlobalToolOptions/DynamicsPanel.gd" id="7_iqcw1"]
|
[ext_resource type="Script" path="res://src/UI/GlobalToolOptions/DynamicsPanel.gd" id="7_iqcw1"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bmsc0s03pwji4" path="res://src/UI/Nodes/MaxMinEdit.tscn" id="8"]
|
[ext_resource type="PackedScene" uid="uid://bmsc0s03pwji4" path="res://src/UI/Nodes/MaxMinEdit.tscn" id="8"]
|
||||||
|
@ -179,6 +181,108 @@ grow_vertical = 2
|
||||||
texture = ExtResource("3_faalk")
|
texture = ExtResource("3_faalk")
|
||||||
stretch_mode = 3
|
stretch_mode = 3
|
||||||
|
|
||||||
|
[node name="DiagonalXY" type="Button" parent="ScrollContainer/CenterContainer/GridContainer" groups=["UIButtons"]]
|
||||||
|
visible = false
|
||||||
|
custom_minimum_size = Vector2(46, 32)
|
||||||
|
layout_mode = 2
|
||||||
|
tooltip_text = "Enable vertical mirrored drawing"
|
||||||
|
mouse_default_cursor_shape = 2
|
||||||
|
toggle_mode = true
|
||||||
|
shortcut = SubResource("Shortcut_ai7qc")
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/CenterContainer/GridContainer/DiagonalXY"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 4
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = 5.0
|
||||||
|
offset_top = -10.0
|
||||||
|
offset_right = 25.0
|
||||||
|
offset_bottom = 10.0
|
||||||
|
grow_vertical = 2
|
||||||
|
texture = ExtResource("5_hcmgx")
|
||||||
|
|
||||||
|
[node name="MirrorOptions" type="MenuButton" parent="ScrollContainer/CenterContainer/GridContainer/DiagonalXY"]
|
||||||
|
visible = false
|
||||||
|
custom_minimum_size = Vector2(20, 0)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 6
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -20.0
|
||||||
|
offset_top = -10.0
|
||||||
|
offset_bottom = 10.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_default_cursor_shape = 2
|
||||||
|
item_count = 2
|
||||||
|
popup/item_0/text = "Move to canvas center"
|
||||||
|
popup/item_1/text = "Move to view center"
|
||||||
|
popup/item_1/id = 1
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/CenterContainer/GridContainer/DiagonalXY/MirrorOptions"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
texture = ExtResource("3_faalk")
|
||||||
|
stretch_mode = 3
|
||||||
|
|
||||||
|
[node name="DiagonalXMinusY" type="Button" parent="ScrollContainer/CenterContainer/GridContainer" groups=["UIButtons"]]
|
||||||
|
visible = false
|
||||||
|
custom_minimum_size = Vector2(46, 32)
|
||||||
|
layout_mode = 2
|
||||||
|
tooltip_text = "Enable vertical mirrored drawing"
|
||||||
|
mouse_default_cursor_shape = 2
|
||||||
|
toggle_mode = true
|
||||||
|
shortcut = SubResource("Shortcut_ai7qc")
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/CenterContainer/GridContainer/DiagonalXMinusY"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 4
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = 5.0
|
||||||
|
offset_top = -10.0
|
||||||
|
offset_right = 25.0
|
||||||
|
offset_bottom = 10.0
|
||||||
|
grow_vertical = 2
|
||||||
|
texture = ExtResource("6_sw8fy")
|
||||||
|
|
||||||
|
[node name="MirrorOptions" type="MenuButton" parent="ScrollContainer/CenterContainer/GridContainer/DiagonalXMinusY"]
|
||||||
|
visible = false
|
||||||
|
custom_minimum_size = Vector2(20, 0)
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 6
|
||||||
|
anchor_left = 1.0
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -20.0
|
||||||
|
offset_top = -10.0
|
||||||
|
offset_bottom = 10.0
|
||||||
|
grow_horizontal = 0
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_default_cursor_shape = 2
|
||||||
|
item_count = 2
|
||||||
|
popup/item_0/text = "Move to canvas center"
|
||||||
|
popup/item_1/text = "Move to view center"
|
||||||
|
popup/item_1/id = 1
|
||||||
|
|
||||||
|
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/CenterContainer/GridContainer/DiagonalXMinusY/MirrorOptions"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
texture = ExtResource("3_faalk")
|
||||||
|
stretch_mode = 3
|
||||||
|
|
||||||
[node name="PixelPerfect" type="Button" parent="ScrollContainer/CenterContainer/GridContainer" groups=["UIButtons"]]
|
[node name="PixelPerfect" type="Button" parent="ScrollContainer/CenterContainer/GridContainer" groups=["UIButtons"]]
|
||||||
custom_minimum_size = Vector2(32, 32)
|
custom_minimum_size = Vector2(32, 32)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
@ -546,6 +650,8 @@ offset_bottom = 23.0
|
||||||
[connection signal="resized" from="." to="." method="_on_resized"]
|
[connection signal="resized" from="." to="." method="_on_resized"]
|
||||||
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/Horizontal" to="." method="_on_Horizontal_toggled"]
|
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/Horizontal" to="." method="_on_Horizontal_toggled"]
|
||||||
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/Vertical" to="." method="_on_Vertical_toggled"]
|
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/Vertical" to="." method="_on_Vertical_toggled"]
|
||||||
|
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/DiagonalXY" to="." method="_on_diagonal_xy_toggled"]
|
||||||
|
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/DiagonalXMinusY" to="." method="_on_diagonal_x_minus_y_toggled"]
|
||||||
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/PixelPerfect" to="." method="_on_PixelPerfect_toggled"]
|
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/PixelPerfect" to="." method="_on_PixelPerfect_toggled"]
|
||||||
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/AlphaLock" to="." method="_on_alpha_lock_toggled"]
|
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/AlphaLock" to="." method="_on_alpha_lock_toggled"]
|
||||||
[connection signal="pressed" from="ScrollContainer/CenterContainer/GridContainer/Dynamics" to="." method="_on_Dynamics_pressed"]
|
[connection signal="pressed" from="ScrollContainer/CenterContainer/GridContainer/Dynamics" to="." method="_on_Dynamics_pressed"]
|
||||||
|
|
|
@ -70,7 +70,7 @@ class Recorder:
|
||||||
image = recorder_panel.get_window().get_texture().get_image()
|
image = recorder_panel.get_window().get_texture().get_image()
|
||||||
else:
|
else:
|
||||||
var frame := project.frames[project.current_frame]
|
var frame := project.frames[project.current_frame]
|
||||||
image = Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
image = project.new_empty_image()
|
||||||
DrawingAlgos.blend_layers(image, frame, Vector2i.ZERO, project)
|
DrawingAlgos.blend_layers(image, frame, Vector2i.ZERO, project)
|
||||||
|
|
||||||
if recorder_panel.resize_percent != 100:
|
if recorder_panel.resize_percent != 100:
|
||||||
|
|
|
@ -140,8 +140,10 @@ func _input(event: InputEvent) -> void:
|
||||||
var timeline_rect := Rect2(global_position, size)
|
var timeline_rect := Rect2(global_position, size)
|
||||||
if timeline_rect.has_point(mouse_pos):
|
if timeline_rect.has_point(mouse_pos):
|
||||||
if Input.is_key_pressed(KEY_CTRL):
|
if Input.is_key_pressed(KEY_CTRL):
|
||||||
cel_size += (2 * int(event.is_action("zoom_in")) - 2 * int(event.is_action("zoom_out")))
|
var zoom := 2 * int(event.is_action("zoom_in")) - 2 * int(event.is_action("zoom_out"))
|
||||||
get_viewport().set_input_as_handled()
|
cel_size += zoom
|
||||||
|
if zoom != 0:
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
func reset_settings() -> void:
|
func reset_settings() -> void:
|
||||||
|
@ -1045,9 +1047,10 @@ func _on_MergeDownLayer_pressed() -> void:
|
||||||
top_cels.append(top_cel) # Store for undo purposes
|
top_cels.append(top_cel) # Store for undo purposes
|
||||||
|
|
||||||
var top_image := top_layer.display_effects(top_cel)
|
var top_image := top_layer.display_effects(top_cel)
|
||||||
var bottom_cel := frame.cels[bottom_layer.index]
|
var bottom_cel := frame.cels[bottom_layer.index] as PixelCel
|
||||||
|
var bottom_image := bottom_cel.get_image()
|
||||||
var textures: Array[Image] = []
|
var textures: Array[Image] = []
|
||||||
textures.append(bottom_cel.get_image())
|
textures.append(bottom_image)
|
||||||
textures.append(top_image)
|
textures.append(top_image)
|
||||||
var metadata_image := Image.create(2, 4, false, Image.FORMAT_R8)
|
var metadata_image := Image.create(2, 4, false, Image.FORMAT_R8)
|
||||||
DrawingAlgos.set_layer_metadata_image(bottom_layer, bottom_cel, metadata_image, 0)
|
DrawingAlgos.set_layer_metadata_image(bottom_layer, bottom_cel, metadata_image, 0)
|
||||||
|
@ -1058,12 +1061,17 @@ func _on_MergeDownLayer_pressed() -> void:
|
||||||
var params := {
|
var params := {
|
||||||
"layers": texture_array, "metadata": ImageTexture.create_from_image(metadata_image)
|
"layers": texture_array, "metadata": ImageTexture.create_from_image(metadata_image)
|
||||||
}
|
}
|
||||||
var bottom_image := Image.create(
|
var new_bottom_image := ImageExtended.create_custom(
|
||||||
top_image.get_width(), top_image.get_height(), false, top_image.get_format()
|
top_image.get_width(),
|
||||||
|
top_image.get_height(),
|
||||||
|
top_image.has_mipmaps(),
|
||||||
|
top_image.get_format(),
|
||||||
|
project.is_indexed()
|
||||||
)
|
)
|
||||||
|
# Merge the image itself.
|
||||||
var gen := ShaderImageEffect.new()
|
var gen := ShaderImageEffect.new()
|
||||||
gen.generate_image(bottom_image, DrawingAlgos.blend_layers_shader, params, project.size)
|
gen.generate_image(new_bottom_image, DrawingAlgos.blend_layers_shader, params, project.size)
|
||||||
|
new_bottom_image.convert_rgb_to_indexed()
|
||||||
if (
|
if (
|
||||||
bottom_cel.link_set != null
|
bottom_cel.link_set != null
|
||||||
and bottom_cel.link_set.size() > 1
|
and bottom_cel.link_set.size() > 1
|
||||||
|
@ -1074,14 +1082,14 @@ func _on_MergeDownLayer_pressed() -> void:
|
||||||
project.undo_redo.add_undo_method(
|
project.undo_redo.add_undo_method(
|
||||||
bottom_layer.link_cel.bind(bottom_cel, bottom_cel.link_set)
|
bottom_layer.link_cel.bind(bottom_cel, bottom_cel.link_set)
|
||||||
)
|
)
|
||||||
project.undo_redo.add_do_property(bottom_cel, "image", bottom_image)
|
project.undo_redo.add_do_property(bottom_cel, "image", new_bottom_image)
|
||||||
project.undo_redo.add_undo_property(bottom_cel, "image", bottom_cel.image)
|
project.undo_redo.add_undo_property(bottom_cel, "image", bottom_cel.image)
|
||||||
else:
|
else:
|
||||||
Global.undo_redo_compress_images(
|
var redo_data := {}
|
||||||
{bottom_cel.image: bottom_image.data},
|
var undo_data := {}
|
||||||
{bottom_cel.image: bottom_cel.image.data},
|
new_bottom_image.add_data_to_dictionary(redo_data, bottom_image)
|
||||||
project
|
bottom_image.add_data_to_dictionary(undo_data)
|
||||||
)
|
Global.undo_redo_compress_images(redo_data, undo_data, project)
|
||||||
|
|
||||||
project.undo_redo.add_do_method(project.remove_layers.bind([top_layer.index]))
|
project.undo_redo.add_do_method(project.remove_layers.bind([top_layer.index]))
|
||||||
project.undo_redo.add_undo_method(
|
project.undo_redo.add_undo_method(
|
||||||
|
|
|
@ -154,13 +154,19 @@ func _apply_effect(layer: BaseLayer, effect: LayerEffect) -> void:
|
||||||
var undo_data := {}
|
var undo_data := {}
|
||||||
for frame in Global.current_project.frames:
|
for frame in Global.current_project.frames:
|
||||||
var cel := frame.cels[layer.index]
|
var cel := frame.cels[layer.index]
|
||||||
var new_image := Image.new()
|
var new_image := ImageExtended.new()
|
||||||
new_image.copy_from(cel.get_image())
|
var cel_image := cel.get_image()
|
||||||
|
if cel_image is ImageExtended:
|
||||||
|
new_image.is_indexed = cel_image.is_indexed
|
||||||
|
new_image.copy_from_custom(cel_image)
|
||||||
var image_size := new_image.get_size()
|
var image_size := new_image.get_size()
|
||||||
var shader_image_effect := ShaderImageEffect.new()
|
var shader_image_effect := ShaderImageEffect.new()
|
||||||
shader_image_effect.generate_image(new_image, effect.shader, effect.params, image_size)
|
shader_image_effect.generate_image(new_image, effect.shader, effect.params, image_size)
|
||||||
redo_data[cel.image] = new_image.data
|
if cel_image is ImageExtended:
|
||||||
undo_data[cel.image] = cel.image.data
|
redo_data[cel_image.indices_image] = new_image.indices_image.data
|
||||||
|
undo_data[cel_image.indices_image] = cel_image.indices_image.data
|
||||||
|
redo_data[cel_image] = new_image.data
|
||||||
|
undo_data[cel_image] = cel_image.data
|
||||||
Global.current_project.undos += 1
|
Global.current_project.undos += 1
|
||||||
Global.current_project.undo_redo.create_action("Apply layer effect")
|
Global.current_project.undo_redo.create_action("Apply layer effect")
|
||||||
Global.undo_redo_compress_images(redo_data, undo_data)
|
Global.undo_redo_compress_images(redo_data, undo_data)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
extends Panel
|
extends Panel
|
||||||
|
|
||||||
|
enum ColorModes { RGBA, INDEXED }
|
||||||
|
|
||||||
const DOCS_URL := "https://www.oramainteractive.com/Pixelorama-Docs/"
|
const DOCS_URL := "https://www.oramainteractive.com/Pixelorama-Docs/"
|
||||||
const ISSUES_URL := "https://github.com/Orama-Interactive/Pixelorama/issues"
|
const ISSUES_URL := "https://github.com/Orama-Interactive/Pixelorama/issues"
|
||||||
const SUPPORT_URL := "https://www.patreon.com/OramaInteractive"
|
const SUPPORT_URL := "https://www.patreon.com/OramaInteractive"
|
||||||
|
@ -56,6 +58,7 @@ var about_dialog := Dialog.new("res://src/UI/Dialogs/AboutDialog.tscn")
|
||||||
@onready var greyscale_vision: ColorRect = main_ui.find_child("GreyscaleVision")
|
@onready var greyscale_vision: ColorRect = main_ui.find_child("GreyscaleVision")
|
||||||
@onready var tile_mode_submenu := PopupMenu.new()
|
@onready var tile_mode_submenu := PopupMenu.new()
|
||||||
@onready var selection_modify_submenu := PopupMenu.new()
|
@onready var selection_modify_submenu := PopupMenu.new()
|
||||||
|
@onready var color_mode_submenu := PopupMenu.new()
|
||||||
@onready var snap_to_submenu := PopupMenu.new()
|
@onready var snap_to_submenu := PopupMenu.new()
|
||||||
@onready var panels_submenu := PopupMenu.new()
|
@onready var panels_submenu := PopupMenu.new()
|
||||||
@onready var layouts_submenu := PopupMenu.new()
|
@onready var layouts_submenu := PopupMenu.new()
|
||||||
|
@ -124,6 +127,7 @@ func _project_switched() -> void:
|
||||||
_update_file_menu_buttons(project)
|
_update_file_menu_buttons(project)
|
||||||
for j in Tiles.MODE.values():
|
for j in Tiles.MODE.values():
|
||||||
tile_mode_submenu.set_item_checked(j, j == project.tiles.mode)
|
tile_mode_submenu.set_item_checked(j, j == project.tiles.mode)
|
||||||
|
_check_color_mode_submenu_item(project)
|
||||||
|
|
||||||
_update_current_frame_mark()
|
_update_current_frame_mark()
|
||||||
|
|
||||||
|
@ -396,19 +400,34 @@ 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 := {
|
||||||
"Project Properties": "project_properties",
|
"Project Properties": "project_properties",
|
||||||
|
"Color Mode": "",
|
||||||
"Resize Canvas": "resize_canvas",
|
"Resize Canvas": "resize_canvas",
|
||||||
"Scale Image": "scale_image",
|
"Scale Image": "scale_image",
|
||||||
"Crop to Selection": "crop_to_selection",
|
"Crop to Selection": "crop_to_selection",
|
||||||
"Crop to Content": "crop_to_content",
|
"Crop to Content": "crop_to_content",
|
||||||
}
|
}
|
||||||
var i := 0
|
for i in image_menu_items.size():
|
||||||
for item in image_menu_items:
|
var item: String = image_menu_items.keys()[i]
|
||||||
_set_menu_shortcut(image_menu_items[item], image_menu, i, item)
|
if item == "Color Mode":
|
||||||
i += 1
|
_setup_color_mode_submenu(item)
|
||||||
|
else:
|
||||||
|
_set_menu_shortcut(image_menu_items[item], image_menu, i, item)
|
||||||
image_menu.set_item_disabled(Global.ImageMenu.CROP_TO_SELECTION, true)
|
image_menu.set_item_disabled(Global.ImageMenu.CROP_TO_SELECTION, true)
|
||||||
image_menu.id_pressed.connect(image_menu_id_pressed)
|
image_menu.id_pressed.connect(image_menu_id_pressed)
|
||||||
|
|
||||||
|
|
||||||
|
func _setup_color_mode_submenu(item: String) -> void:
|
||||||
|
color_mode_submenu.set_name("color_mode_submenu")
|
||||||
|
color_mode_submenu.add_radio_check_item("RGBA", ColorModes.RGBA)
|
||||||
|
color_mode_submenu.set_item_checked(ColorModes.RGBA, true)
|
||||||
|
color_mode_submenu.add_radio_check_item("Indexed", ColorModes.INDEXED)
|
||||||
|
color_mode_submenu.hide_on_checkable_item_selection = false
|
||||||
|
|
||||||
|
color_mode_submenu.id_pressed.connect(_color_mode_submenu_id_pressed)
|
||||||
|
image_menu.add_child(color_mode_submenu)
|
||||||
|
image_menu.add_submenu_item(item, color_mode_submenu.get_name())
|
||||||
|
|
||||||
|
|
||||||
func _setup_effects_menu() -> void:
|
func _setup_effects_menu() -> void:
|
||||||
# Order as in Global.EffectMenu enum
|
# Order as in Global.EffectMenu enum
|
||||||
var menu_items := {
|
var menu_items := {
|
||||||
|
@ -687,6 +706,38 @@ func _selection_modify_submenu_id_pressed(id: int) -> void:
|
||||||
modify_selection.node.type = id
|
modify_selection.node.type = id
|
||||||
|
|
||||||
|
|
||||||
|
func _color_mode_submenu_id_pressed(id: ColorModes) -> void:
|
||||||
|
var project := Global.current_project
|
||||||
|
var old_color_mode := project.color_mode
|
||||||
|
var redo_data := {}
|
||||||
|
var undo_data := {}
|
||||||
|
for cel in project.get_all_pixel_cels():
|
||||||
|
cel.get_image().add_data_to_dictionary(undo_data)
|
||||||
|
# Change the color mode directly before undo/redo in order to affect the images,
|
||||||
|
# so we can store them as redo data.
|
||||||
|
if id == ColorModes.RGBA:
|
||||||
|
project.color_mode = Image.FORMAT_RGBA8
|
||||||
|
else:
|
||||||
|
project.color_mode = Project.INDEXED_MODE
|
||||||
|
for cel in project.get_all_pixel_cels():
|
||||||
|
cel.get_image().add_data_to_dictionary(redo_data)
|
||||||
|
project.undo_redo.create_action("Change color mode")
|
||||||
|
project.undos += 1
|
||||||
|
project.undo_redo.add_do_property(project, "color_mode", project.color_mode)
|
||||||
|
project.undo_redo.add_undo_property(project, "color_mode", old_color_mode)
|
||||||
|
Global.undo_redo_compress_images(redo_data, undo_data, project)
|
||||||
|
project.undo_redo.add_do_method(_check_color_mode_submenu_item.bind(project))
|
||||||
|
project.undo_redo.add_undo_method(_check_color_mode_submenu_item.bind(project))
|
||||||
|
project.undo_redo.add_do_method(Global.undo_or_redo.bind(false))
|
||||||
|
project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true))
|
||||||
|
project.undo_redo.commit_action()
|
||||||
|
|
||||||
|
|
||||||
|
func _check_color_mode_submenu_item(project: Project) -> void:
|
||||||
|
color_mode_submenu.set_item_checked(ColorModes.RGBA, project.color_mode == Image.FORMAT_RGBA8)
|
||||||
|
color_mode_submenu.set_item_checked(ColorModes.INDEXED, project.is_indexed())
|
||||||
|
|
||||||
|
|
||||||
func _snap_to_submenu_id_pressed(id: int) -> void:
|
func _snap_to_submenu_id_pressed(id: int) -> void:
|
||||||
if id == 0:
|
if id == 0:
|
||||||
Global.snap_to_rectangular_grid_boundary = !Global.snap_to_rectangular_grid_boundary
|
Global.snap_to_rectangular_grid_boundary = !Global.snap_to_rectangular_grid_boundary
|
||||||
|
@ -784,8 +835,12 @@ func _toggle_show_guides() -> void:
|
||||||
if guide is SymmetryGuide:
|
if guide is SymmetryGuide:
|
||||||
if guide.type == Guide.Types.HORIZONTAL:
|
if guide.type == Guide.Types.HORIZONTAL:
|
||||||
guide.visible = Global.show_x_symmetry_axis and Global.show_guides
|
guide.visible = Global.show_x_symmetry_axis and Global.show_guides
|
||||||
else:
|
elif guide.type == Guide.Types.VERTICAL:
|
||||||
guide.visible = Global.show_y_symmetry_axis and Global.show_guides
|
guide.visible = Global.show_y_symmetry_axis and Global.show_guides
|
||||||
|
elif guide.type == Guide.Types.XY:
|
||||||
|
guide.visible = Global.show_xy_symmetry_axis and Global.show_guides
|
||||||
|
elif guide.type == Guide.Types.X_MINUS_Y:
|
||||||
|
guide.visible = Global.show_x_minus_y_symmetry_axis and Global.show_guides
|
||||||
|
|
||||||
|
|
||||||
func _toggle_show_mouse_guides() -> void:
|
func _toggle_show_mouse_guides() -> void:
|
||||||
|
|
Loading…
Reference in a new issue