diff --git a/PixelComposer.resource_order b/PixelComposer.resource_order
index e335144b9..48051bc66 100644
--- a/PixelComposer.resource_order
+++ b/PixelComposer.resource_order
@@ -1,9 +1,9 @@
{
"FolderOrderSettings": [
{"name":"_Extensions","order":9,"path":"folders/_Extensions.yy",},
- {"name":"BBMOD","order":9,"path":"folders/_Extensions/BBMOD.yy",},
+ {"name":"BBMOD","order":8,"path":"folders/_Extensions/BBMOD.yy",},
{"name":"Math","order":1,"path":"folders/_Extensions/BBMOD/Math.yy",},
- {"name":"MAC","order":7,"path":"folders/_Extensions/MAC.yy",},
+ {"name":"MAC","order":6,"path":"folders/_Extensions/MAC.yy",},
{"name":"addons","order":11,"path":"folders/addons.yy",},
{"name":"custom","order":4,"path":"folders/addons/custom.yy",},
{"name":"key displayer","order":2,"path":"folders/addons/key displayer.yy",},
@@ -102,7 +102,7 @@
{"name":"filter","order":141,"path":"folders/nodes/icons/filter.yy",},
{"name":"fluidSim","order":147,"path":"folders/nodes/icons/fluidSim.yy",},
{"name":"generator","order":142,"path":"folders/nodes/icons/generator.yy",},
- {"name":"input","order":143,"path":"folders/nodes/icons/input.yy",},
+ {"name":"IO","order":143,"path":"folders/nodes/icons/IO.yy",},
{"name":"node","order":139,"path":"folders/nodes/icons/node.yy",},
{"name":"render","order":140,"path":"folders/nodes/icons/render.yy",},
{"name":"rigidSim","order":146,"path":"folders/nodes/icons/rigidSim.yy",},
@@ -233,7 +233,7 @@
{"name":"fd_rectangle_set_velocity_time_step","order":17,"path":"scripts/fd_rectangle_set_velocity_time_step/fd_rectangle_set_velocity_time_step.yy",},
{"name":"fd_rectangle_set_collision_mask_sprite","order":1,"path":"scripts/fd_rectangle_set_collision_mask_sprite/fd_rectangle_set_collision_mask_sprite.yy",},
{"name":"sh_flip","order":7,"path":"shaders/sh_flip/sh_flip.yy",},
- {"name":"libdlgmodule","order":3,"path":"extensions/libdlgmodule/libdlgmodule.yy",},
+ {"name":"libdlgmodule","order":2,"path":"extensions/libdlgmodule/libdlgmodule.yy",},
{"name":"s_node_alpha_grey","order":4,"path":"sprites/s_node_alpha_grey/s_node_alpha_grey.yy",},
{"name":"fd_rectangle_set_pressure_iteration_type","order":10,"path":"scripts/fd_rectangle_set_pressure_iteration_type/fd_rectangle_set_pressure_iteration_type.yy",},
{"name":"s_node_sort_array","order":29,"path":"sprites/s_node_sort_array/s_node_sort_array.yy",},
@@ -319,6 +319,7 @@
{"name":"__node","order":9,"path":"scripts/__node/__node.yy",},
{"name":"fd_rectangle_add_velocity","order":5,"path":"scripts/fd_rectangle_add_velocity/fd_rectangle_add_velocity.yy",},
{"name":"sh_level_selector","order":23,"path":"shaders/sh_level_selector/sh_level_selector.yy",},
+ {"name":"byte_writer","order":8,"path":"scripts/byte_writer/byte_writer.yy",},
{"name":"checkboxGroup","order":28,"path":"scripts/checkboxGroup/checkboxGroup.yy",},
{"name":"node_FXAA","order":8,"path":"scripts/node_FXAA/node_FXAA.yy",},
{"name":"s_node_pixel_find","order":13,"path":"sprites/s_node_pixel_find/s_node_pixel_find.yy",},
@@ -435,7 +436,7 @@
{"name":"draw_text_delimiter","order":14,"path":"scripts/draw_text_delimiter/draw_text_delimiter.yy",},
{"name":"s_node_path_anchor","order":13,"path":"sprites/s_node_path_anchor/s_node_path_anchor.yy",},
{"name":"node_array_get","order":10,"path":"scripts/node_array_get/node_array_get.yy",},
- {"name":"Apollo","order":6,"path":"extensions/Apollo/Apollo.yy",},
+ {"name":"Apollo","order":5,"path":"extensions/Apollo/Apollo.yy",},
{"name":"sh_grid","order":14,"path":"shaders/sh_grid/sh_grid.yy",},
{"name":"sh_twirl","order":3,"path":"shaders/sh_twirl/sh_twirl.yy",},
{"name":"s_node_shape","order":14,"path":"sprites/s_node_shape/s_node_shape.yy",},
@@ -512,7 +513,7 @@
{"name":"_node_fluid_nodes","order":7,"path":"scripts/_node_fluid_nodes/_node_fluid_nodes.yy",},
{"name":"sh_noise","order":16,"path":"shaders/sh_noise/sh_noise.yy",},
{"name":"sh_skew","order":6,"path":"shaders/sh_skew/sh_skew.yy",},
- {"name":"libxprocess","order":5,"path":"extensions/libxprocess/libxprocess.yy",},
+ {"name":"libxprocess","order":4,"path":"extensions/libxprocess/libxprocess.yy",},
{"name":"fd_rectangle_get_pressure_height","order":16,"path":"scripts/fd_rectangle_get_pressure_height/fd_rectangle_get_pressure_height.yy",},
{"name":"node_VFX_effect_destroy","order":12,"path":"scripts/node_VFX_effect_destroy/node_VFX_effect_destroy.yy",},
{"name":"node_cache","order":9,"path":"scripts/node_cache/node_cache.yy",},
@@ -642,7 +643,7 @@
{"name":"_draw_defines","order":21,"path":"scripts/_draw_defines/_draw_defines.yy",},
{"name":"sh_color_replace","order":8,"path":"shaders/sh_color_replace/sh_color_replace.yy",},
{"name":"__surface","order":8,"path":"scripts/__surface/__surface.yy",},
- {"name":"clipboard","order":10,"path":"extensions/clipboard/clipboard.yy",},
+ {"name":"clipboard","order":9,"path":"extensions/clipboard/clipboard.yy",},
{"name":"rotator","order":11,"path":"scripts/rotator/rotator.yy",},
{"name":"s_node_edge_detect","order":22,"path":"sprites/s_node_edge_detect/s_node_edge_detect.yy",},
{"name":"node_fluid_add_collider","order":6,"path":"scripts/node_fluid_add_collider/node_fluid_add_collider.yy",},
@@ -687,6 +688,7 @@
{"name":"sh_invert","order":25,"path":"shaders/sh_invert/sh_invert.yy",},
{"name":"preview_overlay_puppet","order":4,"path":"scripts/preview_overlay_puppet/preview_overlay_puppet.yy",},
{"name":"s_icon_64","order":2,"path":"sprites/s_icon_64/s_icon_64.yy",},
+ {"name":"panel_graph_export_image","order":4,"path":"scripts/panel_graph_export_image/panel_graph_export_image.yy",},
{"name":"s_node_gradient_replace","order":18,"path":"sprites/s_node_gradient_replace/s_node_gradient_replace.yy",},
{"name":"node_perlin_smear","order":6,"path":"scripts/node_perlin_smear/node_perlin_smear.yy",},
{"name":"node_alpha_cutoff","order":10,"path":"scripts/node_alpha_cutoff/node_alpha_cutoff.yy",},
@@ -768,6 +770,7 @@
{"name":"fd_rectangle_set_repeat","order":12,"path":"scripts/fd_rectangle_set_repeat/fd_rectangle_set_repeat.yy",},
{"name":"sh_fd_calculate_velocity_divergence_glsl","order":10,"path":"shaders/sh_fd_calculate_velocity_divergence_glsl/sh_fd_calculate_velocity_divergence_glsl.yy",},
{"name":"node_simple_shape","order":4,"path":"scripts/node_simple_shape/node_simple_shape.yy",},
+ {"name":"node_wav_file_write","order":3,"path":"scripts/node_wav_file_write/node_wav_file_write.yy",},
{"name":"node_random","order":5,"path":"scripts/node_random/node_random.yy",},
{"name":"node_atlas","order":2,"path":"scripts/node_atlas/node_atlas.yy",},
{"name":"sh_blend_sat","order":18,"path":"shaders/sh_blend_sat/sh_blend_sat.yy",},
@@ -824,7 +827,7 @@
{"name":"o_dialog_animation","order":1,"path":"objects/o_dialog_animation/o_dialog_animation.yy",},
{"name":"s_gizmo","order":4,"path":"sprites/s_gizmo/s_gizmo.yy",},
{"name":"s_node_3d_plane","order":6,"path":"sprites/s_node_3d_plane/s_node_3d_plane.yy",},
- {"name":"Regex","order":8,"path":"extensions/Regex/Regex.yy",},
+ {"name":"Regex","order":7,"path":"extensions/Regex/Regex.yy",},
{"name":"s_node_path_shift","order":4,"path":"sprites/s_node_path_shift/s_node_path_shift.yy",},
{"name":"s_node_grid_tri","order":6,"path":"sprites/s_node_grid_tri/s_node_grid_tri.yy",},
{"name":"s_node_local_analyze","order":52,"path":"sprites/s_node_local_analyze/s_node_local_analyze.yy",},
@@ -986,7 +989,7 @@
{"name":"BBMOD_Matrix","order":2,"path":"scripts/BBMOD_Matrix/BBMOD_Matrix.yy",},
{"name":"pack_shelf","order":1,"path":"scripts/pack_shelf/pack_shelf.yy",},
{"name":"s_node_path_trim","order":6,"path":"sprites/s_node_path_trim/s_node_path_trim.yy",},
- {"name":"libfilesystem","order":4,"path":"extensions/libfilesystem/libfilesystem.yy",},
+ {"name":"libfilesystem","order":3,"path":"extensions/libfilesystem/libfilesystem.yy",},
{"name":"node_channels_hsv","order":1,"path":"scripts/node_channels_hsv/node_channels_hsv.yy",},
{"name":"sh_stripe","order":10,"path":"shaders/sh_stripe/sh_stripe.yy",},
{"name":"node_path_trim","order":6,"path":"scripts/node_path_trim/node_path_trim.yy",},
@@ -1108,6 +1111,7 @@
{"name":"fd_rectangle_replace_velocity","order":16,"path":"scripts/fd_rectangle_replace_velocity/fd_rectangle_replace_velocity.yy",},
{"name":"node_VFX_effector","order":6,"path":"scripts/node_VFX_effector/node_VFX_effector.yy",},
{"name":"node_path_shift","order":5,"path":"scripts/node_path_shift/node_path_shift.yy",},
+ {"name":"s_node_wav_file_write","order":20,"path":"sprites/s_node_wav_file_write/s_node_wav_file_write.yy",},
{"name":"s_node_3d_cylinder","order":1,"path":"sprites/s_node_3d_cylinder/s_node_3d_cylinder.yy",},
{"name":"node_strand_break","order":9,"path":"scripts/node_strand_break/node_strand_break.yy",},
{"name":"s_node_vec_split","order":6,"path":"sprites/s_node_vec_split/s_node_vec_split.yy",},
diff --git a/PixelComposer.yyp b/PixelComposer.yyp
index b86a2e1d1..3d0535bc6 100644
--- a/PixelComposer.yyp
+++ b/PixelComposer.yyp
@@ -134,7 +134,7 @@
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"filter","folderPath":"folders/nodes/icons/filter.yy",},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"fluidSim","folderPath":"folders/nodes/icons/fluidSim.yy",},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"generator","folderPath":"folders/nodes/icons/generator.yy",},
- {"resourceType":"GMFolder","resourceVersion":"1.0","name":"input","folderPath":"folders/nodes/icons/input.yy",},
+ {"resourceType":"GMFolder","resourceVersion":"1.0","name":"IO","folderPath":"folders/nodes/icons/IO.yy",},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"node","folderPath":"folders/nodes/icons/node.yy",},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"render","folderPath":"folders/nodes/icons/render.yy",},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"rigidSim","folderPath":"folders/nodes/icons/rigidSim.yy",},
@@ -812,6 +812,11 @@
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"tile_0044.png","CopyToMask":-1,"filePath":"datafiles/Getting started",},
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"tile_0067.png","CopyToMask":-1,"filePath":"datafiles/Getting started",},
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"tile_0126.png","CopyToMask":-1,"filePath":"datafiles/Getting started",},
+ {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"LICENSE","CopyToMask":-1,"filePath":"datafiles/gifski",},
+ {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"README.md","CopyToMask":-1,"filePath":"datafiles/gifski",},
+ {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"gifski.dll","CopyToMask":-1,"filePath":"datafiles/gifski/win/developer",},
+ {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"gifski.h","CopyToMask":-1,"filePath":"datafiles/gifski/win/developer",},
+ {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"gifski.exe","CopyToMask":-1,"filePath":"datafiles/gifski/win",},
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"convert.exe","CopyToMask":-1,"filePath":"datafiles/ImageMagick",},
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"LICENSE.txt","CopyToMask":-1,"filePath":"datafiles/ImageMagick",},
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"magick.exe","CopyToMask":-1,"filePath":"datafiles/ImageMagick",},
@@ -848,6 +853,7 @@
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Tree sway.pxc","CopyToMask":-1,"filePath":"datafiles/Sample Projects",},
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Steamworks_Extension_Documentation.html","CopyToMask":0,"filePath":"datafiles",},
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"ucrtbased.dll","ConfigValues":{},"CopyToMask":-1,"filePath":"datafiles",},
+ {"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Uninstall gifski.lnk","CopyToMask":-1,"filePath":"datafiles",},
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"webpmux.exe","CopyToMask":-1,"filePath":"datafiles/webp",},
{"resourceType":"GMIncludedFile","resourceVersion":"1.0","name":"Welcome files.zip","CopyToMask":-1,"filePath":"datafiles",},
],
@@ -1049,6 +1055,7 @@
{"id":{"name":"sh_level_selector","path":"shaders/sh_level_selector/sh_level_selector.yy",},},
{"id":{"name":"string_eval","path":"scripts/string_eval/string_eval.yy",},},
{"id":{"name":"s_node_struct","path":"sprites/s_node_struct/s_node_struct.yy",},},
+ {"id":{"name":"byte_writer","path":"scripts/byte_writer/byte_writer.yy",},},
{"id":{"name":"checkboxGroup","path":"scripts/checkboxGroup/checkboxGroup.yy",},},
{"id":{"name":"node_FXAA","path":"scripts/node_FXAA/node_FXAA.yy",},},
{"id":{"name":"s_node_pixel_find","path":"sprites/s_node_pixel_find/s_node_pixel_find.yy",},},
@@ -1468,6 +1475,7 @@
{"id":{"name":"preview_overlay_puppet","path":"scripts/preview_overlay_puppet/preview_overlay_puppet.yy",},},
{"id":{"name":"__mesh","path":"scripts/__mesh/__mesh.yy",},},
{"id":{"name":"s_icon_64","path":"sprites/s_icon_64/s_icon_64.yy",},},
+ {"id":{"name":"panel_graph_export_image","path":"scripts/panel_graph_export_image/panel_graph_export_image.yy",},},
{"id":{"name":"save_function","path":"scripts/save_function/save_function.yy",},},
{"id":{"name":"s_node_gradient_replace","path":"sprites/s_node_gradient_replace/s_node_gradient_replace.yy",},},
{"id":{"name":"node_perlin_smear","path":"scripts/node_perlin_smear/node_perlin_smear.yy",},},
@@ -1555,6 +1563,7 @@
{"id":{"name":"fd_rectangle_set_repeat","path":"scripts/fd_rectangle_set_repeat/fd_rectangle_set_repeat.yy",},},
{"id":{"name":"sh_fd_calculate_velocity_divergence_glsl","path":"shaders/sh_fd_calculate_velocity_divergence_glsl/sh_fd_calculate_velocity_divergence_glsl.yy",},},
{"id":{"name":"node_simple_shape","path":"scripts/node_simple_shape/node_simple_shape.yy",},},
+ {"id":{"name":"node_wav_file_write","path":"scripts/node_wav_file_write/node_wav_file_write.yy",},},
{"id":{"name":"fd_rectangle_get_acceleration_a","path":"scripts/fd_rectangle_get_acceleration_a/fd_rectangle_get_acceleration_a.yy",},},
{"id":{"name":"node_random","path":"scripts/node_random/node_random.yy",},},
{"id":{"name":"sh_channel_A","path":"shaders/sh_channel_A/sh_channel_A.yy",},},
@@ -1947,6 +1956,7 @@
{"id":{"name":"node_VFX_effector","path":"scripts/node_VFX_effector/node_VFX_effector.yy",},},
{"id":{"name":"sh_blur_gaussian","path":"shaders/sh_blur_gaussian/sh_blur_gaussian.yy",},},
{"id":{"name":"node_path_shift","path":"scripts/node_path_shift/node_path_shift.yy",},},
+ {"id":{"name":"s_node_wav_file_write","path":"sprites/s_node_wav_file_write/s_node_wav_file_write.yy",},},
{"id":{"name":"s_node_3d_cylinder","path":"sprites/s_node_3d_cylinder/s_node_3d_cylinder.yy",},},
{"id":{"name":"node_strand_break","path":"scripts/node_strand_break/node_strand_break.yy",},},
{"id":{"name":"s_node_vec_split","path":"sprites/s_node_vec_split/s_node_vec_split.yy",},},
diff --git a/datafiles/Uninstall gifski.lnk b/datafiles/Uninstall gifski.lnk
new file mode 100644
index 000000000..37087cc5c
Binary files /dev/null and b/datafiles/Uninstall gifski.lnk differ
diff --git a/datafiles/data/themes/default.zip b/datafiles/data/themes/default.zip
index 694fc5fb1..b6e617be7 100644
Binary files a/datafiles/data/themes/default.zip and b/datafiles/data/themes/default.zip differ
diff --git a/datafiles/gifski/LICENSE b/datafiles/gifski/LICENSE
new file mode 100644
index 000000000..24eda8a7b
--- /dev/null
+++ b/datafiles/gifski/LICENSE
@@ -0,0 +1,619 @@
+
+Let [me](https://kornel.ski/contact) know if you'd like to use it in a product incompatible with this license. I can offer alternative licensing options.
+
+----
+
+### GNU AFFERO GENERAL PUBLIC LICENSE
+
+Version 3, 19 November 2007
+
+© 2007 Free Software Foundation, Inc.
+
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+### Preamble
+
+The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains
+free software for all its users.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing
+under this license.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+### TERMS AND CONDITIONS
+
+#### 0. Definitions.
+
+"This License" refers to version 3 of the GNU Affero General Public
+License.
+
+"Copyright" also means copyright-like laws that apply to other kinds
+of works, such as semiconductor masks.
+
+"The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of
+an exact copy. The resulting work is called a "modified version" of
+the earlier work or a work "based on" the earlier work.
+
+A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user
+through a computer network, with no transfer of a copy, is not
+conveying.
+
+An interactive user interface displays "Appropriate Legal Notices" to
+the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+#### 1. Source Code.
+
+The "source code" for a work means the preferred form of the work for
+making modifications to it. "Object code" means any non-source form of
+a work.
+
+A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+The Corresponding Source need not include anything that users can
+regenerate automatically from other parts of the Corresponding Source.
+
+The Corresponding Source for a work in source code form is that same
+work.
+
+#### 2. Basic Permissions.
+
+All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+You may make, run and propagate covered works that you do not convey,
+without conditions so long as your license otherwise remains in force.
+You may convey covered works to others for the sole purpose of having
+them make modifications exclusively for you, or provide you with
+facilities for running those works, provided that you comply with the
+terms of this License in conveying all material for which you do not
+control copyright. Those thus making or running the covered works for
+you must do so exclusively on your behalf, under your direction and
+control, on terms that prohibit them from making any copies of your
+copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under the
+conditions stated below. Sublicensing is not allowed; section 10 makes
+it unnecessary.
+
+#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such
+circumvention is effected by exercising rights under this License with
+respect to the covered work, and you disclaim any intention to limit
+operation or modification of the work as a means of enforcing, against
+the work's users, your or third parties' legal rights to forbid
+circumvention of technological measures.
+
+#### 4. Conveying Verbatim Copies.
+
+You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+#### 5. Conveying Modified Source Versions.
+
+You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these
+conditions:
+
+- a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+- b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under
+ section 7. This requirement modifies the requirement in section 4
+ to "keep intact all notices".
+- c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+- d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+#### 6. Conveying Non-Source Forms.
+
+You may convey a covered work in object code form under the terms of
+sections 4 and 5, provided that you also convey the machine-readable
+Corresponding Source under the terms of this License, in one of these
+ways:
+
+- a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+- b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the Corresponding
+ Source from a network server at no charge.
+- c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+- d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+- e) Convey the object code using peer-to-peer transmission,
+ provided you inform other peers where the object code and
+ Corresponding Source of the work are being offered to the general
+ public at no charge under subsection 6d.
+
+A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal,
+family, or household purposes, or (2) anything designed or sold for
+incorporation into a dwelling. In determining whether a product is a
+consumer product, doubtful cases shall be resolved in favor of
+coverage. For a particular product received by a particular user,
+"normally used" refers to a typical or common use of that class of
+product, regardless of the status of the particular user or of the way
+in which the particular user actually uses, or expects or is expected
+to use, the product. A product is a consumer product regardless of
+whether the product has substantial commercial, industrial or
+non-consumer uses, unless such uses represent the only significant
+mode of use of the product.
+
+"Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to
+install and execute modified versions of a covered work in that User
+Product from a modified version of its Corresponding Source. The
+information must suffice to ensure that the continued functioning of
+the modified object code is in no case prevented or interfered with
+solely because modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or
+updates for a work that has been modified or installed by the
+recipient, or for the User Product in which it has been modified or
+installed. Access to a network may be denied when the modification
+itself materially and adversely affects the operation of the network
+or violates the rules and protocols for communication across the
+network.
+
+Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+#### 7. Additional Terms.
+
+"Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders
+of that material) supplement the terms of this License with terms:
+
+- a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+- b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+- c) Prohibiting misrepresentation of the origin of that material,
+ or requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+- d) Limiting the use for publicity purposes of names of licensors
+ or authors of the material; or
+- e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+- f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions
+ of it) with contractual assumptions of liability to the recipient,
+ for any liability that these contractual assumptions directly
+ impose on those licensors and authors.
+
+All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions; the
+above requirements apply either way.
+
+#### 8. Termination.
+
+You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+#### 9. Acceptance Not Required for Having Copies.
+
+You are not required to accept this License in order to receive or run
+a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+#### 10. Automatic Licensing of Downstream Recipients.
+
+Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+#### 11. Patents.
+
+A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+A contributor's "essential patent claims" are all patent claims owned
+or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+A patent license is "discriminatory" if it does not include within the
+scope of its coverage, prohibits the exercise of, or is conditioned on
+the non-exercise of one or more of the rights that are specifically
+granted under this License. You may not convey a covered work if you
+are a party to an arrangement with a third party that is in the
+business of distributing software, under which you make payment to the
+third party based on the extent of your activity of conveying the
+work, and under which the third party grants, to any of the parties
+who would receive the covered work from you, a discriminatory patent
+license (a) in connection with copies of the covered work conveyed by
+you (or copies made from those copies), or (b) primarily for and in
+connection with specific products or compilations that contain the
+covered work, unless you entered into that arrangement, or that patent
+license was granted, prior to 28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+#### 12. No Surrender of Others' Freedom.
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under
+this License and any other pertinent obligations, then as a
+consequence you may not convey it at all. For example, if you agree to
+terms that obligate you to collect a royalty for further conveying
+from those to whom you convey the Program, the only way you could
+satisfy both those terms and this License would be to refrain entirely
+from conveying the Program.
+
+#### 13. Remote Network Interaction; Use with the GNU General Public License.
+
+Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your
+version supports such interaction) an opportunity to receive the
+Corresponding Source of your version by providing access to the
+Corresponding Source from a network server at no charge, through some
+standard or customary means of facilitating copying of software. This
+Corresponding Source shall include the Corresponding Source for any
+work covered by version 3 of the GNU General Public License that is
+incorporated pursuant to the following paragraph.
+
+Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+#### 14. Revised Versions of this License.
+
+The Free Software Foundation may publish revised and/or new versions
+of the GNU Affero General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever
+published by the Free Software Foundation.
+
+If the Program specifies that a proxy can decide which future versions
+of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+#### 15. Disclaimer of Warranty.
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
+WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+#### 16. Limitation of Liability.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
+CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
+NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
+LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
+TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
+PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+#### 17. Interpretation of Sections 15 and 16.
+
+If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
diff --git a/datafiles/gifski/README.md b/datafiles/gifski/README.md
new file mode 100644
index 000000000..888668361
--- /dev/null
+++ b/datafiles/gifski/README.md
@@ -0,0 +1,100 @@
+# [](https://gif.ski)
+
+Highest-quality GIF encoder based on [pngquant](https://pngquant.org).
+
+**[gifski](https://gif.ski)** converts video frames to GIF animations using pngquant's fancy features for efficient cross-frame palettes and temporal dithering. It produces animated GIFs that use thousands of colors per frame.
+
+![(CC) Blender Foundation | gooseberry.blender.org](https://gif.ski/demo.gif)
+
+It's a CLI tool, but it can also be compiled [as a C library](https://docs.rs/gifski) for seamless use in other apps.
+
+## Download and install
+
+See [releases](https://github.com/ImageOptim/gifski/releases) page for executables.
+
+If you have [Homebrew](https://brew.sh/), you can also get it with `brew install gifski`.
+
+If you have [Rust from rustup](https://www.rust-lang.org/install.html) (1.63+), you can also build it from source with [`cargo install gifski`](https://lib.rs/crates/gifski).
+
+## Usage
+
+gifski is a command-line tool. There is no GUI for Windows or Linux (there is one for [macOS](https://sindresorhus.com/gifski)).
+
+The recommended way is to first export video as PNG frames. If you have `ffmpeg` installed, you can run in terminal:
+
+```sh
+ffmpeg -i video.webm frame%04d.png
+```
+
+and then make the GIF from the frames:
+
+```sh
+gifski -o anim.gif frame*.png
+```
+
+You can also resize frames (with `-W ` option). If the input was ever encoded using a lossy video codec it's recommended to at least halve size of the frames to hide compression artefacts and counter chroma subsampling that was done by the video codec.
+
+Adding `--quality=90` may reduce file sizes a bit, but expect to lose a lot of quality for little gain. GIF just isn't that good at compressing, no matter how much you compromise.
+
+See `gifski -h` for more options.
+
+## Building
+
+1. [Install Rust via rustup](https://www.rust-lang.org/en-US/install.html) or run `rustup update`. This project only supports up-to-date versions of Rust. You may get compile errors, warnings about "unstable edition", etc. if you don't run `rustup update` regularly.
+2. Clone the repository: `git clone https://github.com/ImageOptim/gifski`
+3. In the cloned directory, run: `cargo build --release`
+
+### Using from C
+
+[See `gifski.h`](https://github.com/ImageOptim/gifski/blob/main/gifski.h) for [the C API](https://docs.rs/gifski/latest/gifski/c_api/#functions). To build the library, run:
+
+```sh
+rustup update
+cargo build --release
+```
+
+and link with `target/release/libgifski.a`. Please observe the [LICENSE](LICENSE).
+
+## License
+
+AGPL 3 or later. I can offer alternative licensing options, including [commercial licenses](https://supso.org/projects/pngquant). Let [me](https://kornel.ski/contact) know if you'd like to use it in a product incompatible with this license.
+
+## With built-in video support
+
+The tool optionally supports decoding video directly, but unfortunately it relies on ffmpeg 4.x, which may be *very hard* to get working, so it's not enabled by default.
+
+You must have `ffmpeg` and `libclang` installed, both with their C headers installed in default system include paths. Details depend on the platform and version, but you usually need to install packages such as `libavformat-dev`, `libavfilter-dev`, `libavdevice-dev`, `libclang-dev`, `clang`. Please note that installation of these dependencies may be quite difficult. Especially on macOS and Windows it takes *expert knowledge* to just get them installed without wasting several hours on endless stupid installation and compilation errors, which I can't help with. If you're cross-compiling, try uncommenting `[patch.crates-io]` section at the end of `Cargo.toml`, which includes some experimental fixes for ffmpeg.
+
+Once you have dependencies installed, compile with `cargo build --release --features=video` or `cargo build --release --features=video-static`.
+
+When compiled with video support [ffmpeg licenses](https://www.ffmpeg.org/legal.html) apply. You may need to have a patent license to use H.264/H.265 video (I recommend using VP9/WebM instead).
+
+```sh
+gifski -o out.gif video.mp4
+```
+
+## Cross-compilation for iOS
+
+The easy option is to use the included `gifski.xcodeproj` file to build the library automatically for all Apple platforms. Add it as a [subproject](https://lib.rs/crates/cargo-xcode) to your Xcode project, and link with `gifski-staticlib` Xcode target. See [the GUI app](https://github.com/sindresorhus/Gifski) for an example how to integrate the library.
+
+### Cross-compilation for iOS manually
+
+Make sure you have Rust installed via [rustup](https://rustup.rs/). Run once:
+
+```sh
+rustup target add aarch64-apple-ios
+```
+
+and then to build the library:
+
+```sh
+rustup update
+cargo build --lib --release --target=aarch64-apple-ios
+```
+
+The build will print "dropping unsupported crate type `cdylib`" warning. This is normal and expected when building for iOS (the cdylib option exists for other platforms).
+
+This will create a static library in `./target/aarch64-apple-ios/release/libgifski.a`. You can add this library to your Xcode project. See [gifski.app](https://github.com/sindresorhus/Gifski) for an example how to use libgifski from Swift.
+
+
+
diff --git a/datafiles/gifski/win/developer/gifski.dll b/datafiles/gifski/win/developer/gifski.dll
new file mode 100644
index 000000000..449a27322
Binary files /dev/null and b/datafiles/gifski/win/developer/gifski.dll differ
diff --git a/datafiles/gifski/win/developer/gifski.h b/datafiles/gifski/win/developer/gifski.h
new file mode 100644
index 000000000..86a7ba75f
--- /dev/null
+++ b/datafiles/gifski/win/developer/gifski.h
@@ -0,0 +1,321 @@
+#include
+#include
+#include
+#include
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct gifski;
+typedef struct gifski gifski;
+
+/**
+How to use from C
+
+```c
+gifski *g = gifski_new(&(GifskiSettings){
+ .quality = 90,
+});
+gifski_set_file_output(g, "file.gif");
+
+for(int i=0; i < frames; i++) {
+ int res = gifski_add_frame_rgba(g, i, width, height, buffer, 5);
+ if (res != GIFSKI_OK) break;
+}
+int res = gifski_finish(g);
+if (res != GIFSKI_OK) return;
+```
+
+It's safe and efficient to call `gifski_add_frame_*` in a loop as fast as you can get frames,
+because it blocks and waits until previous frames are written.
+
+To cancel processing, make progress callback return 0 and call `gifski_finish()`. The write callback
+may still be called between the cancellation and `gifski_finish()` returning.
+
+To build as a library:
+
+```bash
+cargo build --release --lib
+```
+
+it will create `target/release/libgifski.a` (static library)
+and `target/release/libgifski.so`/`dylib` or `gifski.dll` (dynamic library)
+
+Static is recommended.
+
+To build for iOS:
+
+```bash
+rustup target add aarch64-apple-ios
+cargo build --release --lib --target aarch64-apple-ios
+```
+
+it will build `target/aarch64-apple-ios/release/libgifski.a` (ignore the warning about cdylib).
+
+*/
+
+/**
+ * Settings for creating a new encoder instance. See `gifski_new`
+ */
+typedef struct GifskiSettings {
+ /**
+ * Resize to max this width if non-0.
+ */
+ uint32_t width;
+ /**
+ * Resize to max this height if width is non-0. Note that aspect ratio is not preserved.
+ */
+ uint32_t height;
+ /**
+ * 1-100, but useful range is 50-100. Recommended to set to 90.
+ */
+ uint8_t quality;
+ /**
+ * Lower quality, but faster encode.
+ */
+ bool fast;
+ /**
+ * If negative, looping is disabled. The number of times the sequence is repeated. 0 to loop forever.
+ */
+ int16_t repeat;
+} GifskiSettings;
+
+enum GifskiError {
+ GIFSKI_OK = 0,
+ /** one of input arguments was NULL */
+ GIFSKI_NULL_ARG,
+ /** a one-time function was called twice, or functions were called in wrong order */
+ GIFSKI_INVALID_STATE,
+ /** internal error related to palette quantization */
+ GIFSKI_QUANT,
+ /** internal error related to gif composing */
+ GIFSKI_GIF,
+ /** internal error - unexpectedly aborted */
+ GIFSKI_THREAD_LOST,
+ /** I/O error: file or directory not found */
+ GIFSKI_NOT_FOUND,
+ /** I/O error: permission denied */
+ GIFSKI_PERMISSION_DENIED,
+ /** I/O error: file already exists */
+ GIFSKI_ALREADY_EXISTS,
+ /** invalid arguments passed to function */
+ GIFSKI_INVALID_INPUT,
+ /** misc I/O error */
+ GIFSKI_TIMED_OUT,
+ /** misc I/O error */
+ GIFSKI_WRITE_ZERO,
+ /** misc I/O error */
+ GIFSKI_INTERRUPTED,
+ /** misc I/O error */
+ GIFSKI_UNEXPECTED_EOF,
+ /** progress callback returned 0, writing aborted */
+ GIFSKI_ABORTED,
+ /** should not happen, file a bug */
+ GIFSKI_OTHER,
+};
+
+/* workaround for a wrong definition in an older version of this header. Please use GIFSKI_ABORTED directly */
+#ifndef ABORTED
+#define ABORTED GIFSKI_ABORTED
+#endif
+
+typedef enum GifskiError GifskiError;
+
+/**
+ * Call to start the process
+ *
+ * See `gifski_add_frame_png_file` and `gifski_end_adding_frames`
+ *
+ * Returns a handle for the other functions, or `NULL` on error (if the settings are invalid).
+ */
+gifski *gifski_new(const GifskiSettings *settings);
+
+
+/** Quality 1-100 of temporal denoising. Lower values reduce motion. Defaults to `settings.quality`.
+ *
+ * Only valid immediately after calling `gifski_new`, before any frames are added. */
+GifskiError gifski_set_motion_quality(gifski *handle, uint8_t quality);
+
+/** Quality 1-100 of gifsicle compression. Lower values add noise. Defaults to `settings.quality`.
+ * Has no effect if the `gifsicle` feature hasn't been enabled.
+ * Only valid immediately after calling `gifski_new`, before any frames are added. */
+GifskiError gifski_set_lossy_quality(gifski *handle, uint8_t quality);
+
+/** If `true`, encoding will be significantly slower, but may look a bit better.
+ *
+ * Only valid immediately after calling `gifski_new`, before any frames are added. */
+GifskiError gifski_set_extra_effort(gifski *handle, bool extra);
+
+/**
+ * Adds a frame to the animation. This function is asynchronous.
+ *
+ * File path must be valid UTF-8.
+ *
+ * `frame_number` orders frames (consecutive numbers starting from 0).
+ * You can add frames in any order, and they will be sorted by their `frame_number`.
+ *
+ * Presentation timestamp (PTS) is time in seconds, since start of the file, when this frame is to be displayed.
+ * For a 20fps video it could be `frame_number/20.0`.
+ * Frames with duplicate or out-of-order PTS will be skipped.
+ *
+ * The first frame should have PTS=0. If the first frame has PTS > 0, it'll be used as a delay after the last frame.
+ *
+ * Returns 0 (`GIFSKI_OK`) on success, and non-0 `GIFSKI_*` constant on error.
+ */
+GifskiError gifski_add_frame_png_file(gifski *handle,
+ uint32_t frame_number,
+ const char *file_path,
+ double presentation_timestamp);
+
+/**
+ * Adds a frame to the animation. This function is asynchronous.
+ *
+ * `pixels` is an array width×height×4 bytes large.
+ * The array is copied, so you can free/reuse it immediately after this function returns.
+ *
+ * `frame_number` orders frames (consecutive numbers starting from 0).
+ * You can add frames in any order, and they will be sorted by their `frame_number`.
+ *
+ * Presentation timestamp (PTS) is time in seconds, since start of the file, when this frame is to be displayed.
+ * For a 20fps video it could be `frame_number/20.0`. First frame must have PTS=0.
+ * Frames with duplicate or out-of-order PTS will be skipped.
+ *
+ * The first frame should have PTS=0. If the first frame has PTS > 0, it'll be used as a delay after the last frame.
+ *
+ * Colors are in sRGB, uncorrelated RGBA, with alpha byte last.
+ *
+ * Returns 0 (`GIFSKI_OK`) on success, and non-0 `GIFSKI_*` constant on error.
+ */
+GifskiError gifski_add_frame_rgba(gifski *handle,
+ uint32_t frame_number,
+ uint32_t width,
+ uint32_t height,
+ const unsigned char *pixels,
+ double presentation_timestamp);
+
+/** Same as `gifski_add_frame_rgba`, but with bytes per row arg */
+GifskiError gifski_add_frame_rgba_stride(gifski *handle,
+ uint32_t frame_number,
+ uint32_t width,
+ uint32_t height,
+ uint32_t bytes_per_row,
+ const unsigned char *pixels,
+ double presentation_timestamp);
+
+/** Same as `gifski_add_frame_rgba_stride`, except it expects components in ARGB order.
+
+Bytes per row must be multiple of 4, and greater or equal width×4.
+If the bytes per row value is invalid (e.g. an odd number), frames may look sheared/skewed.
+
+Colors are in sRGB, uncorrelated ARGB, with alpha byte first.
+
+`gifski_add_frame_rgba` is preferred over this function.
+*/
+GifskiError gifski_add_frame_argb(gifski *handle,
+ uint32_t frame_number,
+ uint32_t width,
+ uint32_t bytes_per_row,
+ uint32_t height,
+ const unsigned char *pixels,
+ double presentation_timestamp);
+
+/** Same as `gifski_add_frame_rgba_stride`, except it expects RGB components (3 bytes per pixel)
+
+Bytes per row must be multiple of 3, and greater or equal width×3.
+If the bytes per row value is invalid (not multiple of 3), frames may look sheared/skewed.
+
+Colors are in sRGB, red byte first.
+
+`gifski_add_frame_rgba` is preferred over this function.
+*/
+GifskiError gifski_add_frame_rgb(gifski *handle,
+ uint32_t frame_number,
+ uint32_t width,
+ uint32_t bytes_per_row,
+ uint32_t height,
+ const unsigned char *pixels,
+ double presentation_timestamp);
+
+/**
+ * Get a callback for frame processed, and abort processing if desired.
+ *
+ * The callback is called once per input frame,
+ * even if the encoder decides to skip some frames.
+ *
+ * It gets arbitrary pointer (`user_data`) as an argument. `user_data` can be `NULL`.
+ *
+ * The callback must return `1` to continue processing, or `0` to abort.
+ *
+ * The callback must be thread-safe (it will be called from another thread).
+ * It must remain valid at all times, until `gifski_finish` completes.
+ *
+ * This function must be called before `gifski_set_file_output()` to take effect.
+ */
+void gifski_set_progress_callback(gifski *handle, int (*progress_callback)(void *user_data), void *user_data);
+
+/**
+ * Get a callback when an error occurs.
+ * This is intended mostly for logging and debugging, not for user interface.
+ *
+ * The callback function has the following arguments:
+ * * A `\0`-terminated C string in UTF-8 encoding. The string is only valid for the duration of the call. Make a copy if you need to keep it.
+ * * An arbitrary pointer (`user_data`). `user_data` can be `NULL`.
+ *
+ * The callback must be thread-safe (it will be called from another thread).
+ * It must remain valid at all times, until `gifski_finish` completes.
+ *
+ * If the callback is not set, errors will be printed to stderr.
+ *
+ * This function must be called before `gifski_set_file_output()` to take effect.
+ */
+GifskiError gifski_set_error_message_callback(gifski *handle, void (*error_message_callback)(const char*, void*), void *user_data);
+
+/**
+ * Start writing to the file at `destination_path` (overwrites if needed).
+ * The file path must be ASCII or valid UTF-8.
+ *
+ * This function has to be called before any frames are added.
+ * This call will not block.
+ *
+ * Returns 0 (`GIFSKI_OK`) on success, and non-0 `GIFSKI_*` constant on error.
+ */
+GifskiError gifski_set_file_output(gifski *handle, const char *destination_path);
+
+/**
+ * Start writing via callback (any buffer, file, whatever you want). This has to be called before any frames are added.
+ * This call will not block.
+ *
+ * The callback function receives 3 arguments:
+ * - size of the buffer to write, in bytes. IT MAY BE ZERO (when it's zero, either do nothing, or flush internal buffers if necessary).
+ * - pointer to the buffer.
+ * - context pointer to arbitrary user data, same as passed in to this function.
+ *
+ * The callback should return 0 (`GIFSKI_OK`) on success, and non-zero on error.
+ *
+ * The callback function must be thread-safe. It must remain valid at all times, until `gifski_finish` completes.
+ *
+ * Returns 0 (`GIFSKI_OK`) on success, and non-0 `GIFSKI_*` constant on error.
+ */
+GifskiError gifski_set_write_callback(gifski *handle,
+ int (*write_callback)(size_t buffer_length, const uint8_t *buffer, void *user_data),
+ void *user_data);
+
+/**
+ * The last step:
+ * - stops accepting any more frames (gifski_add_frame_* calls are blocked)
+ * - blocks and waits until all already-added frames have finished writing
+ *
+ * Returns final status of write operations. Remember to check the return value!
+ *
+ * Must always be called, otherwise it will leak memory.
+ * After this call, the handle is freed and can't be used any more.
+ *
+ * Returns 0 (`GIFSKI_OK`) on success, and non-0 `GIFSKI_*` constant on error.
+ */
+GifskiError gifski_finish(gifski *g);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/datafiles/gifski/win/gifski.exe b/datafiles/gifski/win/gifski.exe
new file mode 100644
index 000000000..e538b708d
Binary files /dev/null and b/datafiles/gifski/win/gifski.exe differ
diff --git a/objects/o_dialog_about/Create_0.gml b/objects/o_dialog_about/Create_0.gml
index 8aef650b5..475b5fbf9 100644
--- a/objects/o_dialog_about/Create_0.gml
+++ b/objects/o_dialog_about/Create_0.gml
@@ -37,7 +37,7 @@ event_inherited();
draw_text(cx, yy, "Special Thanks");
for( var i = 0; i < array_length(credits); i++ ) {
- yy += line_height(, 8);
+ yy += line_get_height(, 8);
draw_set_font(f_p2);
draw_set_color(COLORS._main_text_sub);
draw_text(cx, yy, credits[i][0]);
diff --git a/objects/o_dialog_about/Draw_64.gml b/objects/o_dialog_about/Draw_64.gml
index 3d34168c8..63c20e406 100644
--- a/objects/o_dialog_about/Draw_64.gml
+++ b/objects/o_dialog_about/Draw_64.gml
@@ -15,7 +15,7 @@ if !ready exit;
draw_set_text(f_h3, fa_center, fa_top, COLORS._main_text_accent);
draw_text(cx, ly, "Pixel Composer");
- ly += line_height();
+ ly += line_get_height();
draw_set_text(f_p0, fa_center, fa_top, COLORS._main_text_sub);
draw_text(cx, ly, "2021, MakhamDev");
diff --git a/objects/o_dialog_arrayBox/Alarm_0.gml b/objects/o_dialog_arrayBox/Alarm_0.gml
index 0b4dc17a7..4ca552455 100644
--- a/objects/o_dialog_arrayBox/Alarm_0.gml
+++ b/objects/o_dialog_arrayBox/Alarm_0.gml
@@ -1,6 +1,6 @@
/// @description init
#region pos
- var hght = line_height(f_p0, 8);
+ var hght = line_get_height(f_p0, 8);
var hh = array_length(arrayBox.data) * hght;
dialog_h = min(max_h, hh);
diff --git a/objects/o_dialog_arrayBox/Create_0.gml b/objects/o_dialog_arrayBox/Create_0.gml
index aac9d2b67..d0632f19f 100644
--- a/objects/o_dialog_arrayBox/Create_0.gml
+++ b/objects/o_dialog_arrayBox/Create_0.gml
@@ -14,7 +14,7 @@ event_inherited();
sc_content = new scrollPane(0, 0, function(_y, _m) {
draw_clear_alpha(COLORS.panel_bg_clear, 0);
- var hght = line_height(f_p0, 8);
+ var hght = line_get_height(f_p0, 8);
var _h = array_length(arrayBox.data) * hght;
var _dw = sc_content.surface_w;
var array = arrayBox.arraySet;
diff --git a/objects/o_dialog_crashed/Draw_64.gml b/objects/o_dialog_crashed/Draw_64.gml
index 91caf0b7a..6088bb914 100644
--- a/objects/o_dialog_crashed/Draw_64.gml
+++ b/objects/o_dialog_crashed/Draw_64.gml
@@ -15,7 +15,7 @@ draw_set_alpha(1);
var py = dialog_y + ui(16);
draw_set_text(f_h5, fa_left, fa_top, COLORS._main_text_title);
draw_text(dialog_x + ui(24), py, get_text("crashed_title", "Restore project"));
- py += line_height(, 4);
+ py += line_get_height(, 4);
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text);
draw_text(dialog_x + ui(24), py, get_text("crashed_content", "Restore previous project before crash?"));
diff --git a/objects/o_dialog_exit/Draw_64.gml b/objects/o_dialog_exit/Draw_64.gml
index 0b87454a5..c86eb393e 100644
--- a/objects/o_dialog_exit/Draw_64.gml
+++ b/objects/o_dialog_exit/Draw_64.gml
@@ -16,7 +16,7 @@ draw_set_alpha(1);
var py = dialog_y + ui(16);
draw_set_text(f_h5, fa_left, fa_top, COLORS._main_text_title);
draw_text(dialog_x + ui(24), py, "Project modified");
- py += line_height(, 4);
+ py += line_get_height(, 4);
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text);
draw_text(dialog_x + ui(24), py, "Save progress before exit?");
diff --git a/objects/o_dialog_file_name_collection/Draw_64.gml b/objects/o_dialog_file_name_collection/Draw_64.gml
index 41389b6d1..d1ccc8168 100644
--- a/objects/o_dialog_file_name_collection/Draw_64.gml
+++ b/objects/o_dialog_file_name_collection/Draw_64.gml
@@ -99,8 +99,8 @@
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text_title);
draw_text(dialog_x + ui(16), yy, get_text("description", "Description"));
- yy += line_height() + ui(4);
- dialog_h += line_height() + ui(4);
+ yy += line_get_height() + ui(4);
+ dialog_h += line_get_height() + ui(4);
t_desc.setActiveFocus(sFOCUS, sHOVER);
t_desc.register();
@@ -110,8 +110,8 @@
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text_title);
draw_text(dialog_x + ui(16), yy, get_text("author", "Author"));
- yy += line_height() + ui(4);
- dialog_h += line_height() + ui(4);
+ yy += line_get_height() + ui(4);
+ dialog_h += line_get_height() + ui(4);
t_auth.setActiveFocus(sFOCUS, sHOVER);
t_auth.register();
@@ -121,8 +121,8 @@
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text_title);
draw_text(dialog_x + ui(16), yy, get_text("contact_info", "Contact info"));
- yy += line_height() + ui(4);
- dialog_h += line_height() + ui(4);
+ yy += line_get_height() + ui(4);
+ dialog_h += line_get_height() + ui(4);
t_cont.setActiveFocus(sFOCUS, sHOVER);
t_cont.register();
@@ -132,8 +132,8 @@
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text_title);
draw_text(dialog_x + ui(16), yy, get_text("alias", "Alias"));
- yy += line_height() + ui(4);
- dialog_h += line_height() + ui(4);
+ yy += line_get_height() + ui(4);
+ dialog_h += line_get_height() + ui(4);
t_alias.setActiveFocus(sFOCUS, sHOVER);
t_alias.register();
@@ -143,8 +143,8 @@
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text_title);
draw_text(dialog_x + ui(16), yy, get_text("tags", "Tags"));
- yy += line_height() + ui(4);
- dialog_h += line_height() + ui(4);
+ yy += line_get_height() + ui(4);
+ dialog_h += line_get_height() + ui(4);
t_tags.setActiveFocus(sFOCUS, sHOVER);
t_tags.register();
diff --git a/objects/o_dialog_fontscrollbox/Alarm_0.gml b/objects/o_dialog_fontscrollbox/Alarm_0.gml
index 1dbe25baa..716ab4b7b 100644
--- a/objects/o_dialog_fontscrollbox/Alarm_0.gml
+++ b/objects/o_dialog_fontscrollbox/Alarm_0.gml
@@ -1,6 +1,6 @@
/// @description init
#region pos
- var hght = line_height(f_p0, 8);
+ var hght = line_get_height(f_p0, 8);
var hh = array_length(FONT_INTERNAL) * hght;
dialog_h = min(max_h, hh);
diff --git a/objects/o_dialog_fontscrollbox/Create_0.gml b/objects/o_dialog_fontscrollbox/Create_0.gml
index 9785d5a6c..394b66aec 100644
--- a/objects/o_dialog_fontscrollbox/Create_0.gml
+++ b/objects/o_dialog_fontscrollbox/Create_0.gml
@@ -14,7 +14,7 @@ event_inherited();
sc_content = new scrollPane(0, 0, function(_y, _m) {
draw_clear_alpha(COLORS.panel_bg_clear, 0);
- var hght = line_height(f_p0, 8);
+ var hght = line_get_height(f_p0, 8);
var data = FONT_INTERNAL;
var _h = array_length(data) * hght;
var _dw = sc_content.surface_w;
diff --git a/objects/o_dialog_history/Create_0.gml b/objects/o_dialog_history/Create_0.gml
index e0555049b..ecd4f5fae 100644
--- a/objects/o_dialog_history/Create_0.gml
+++ b/objects/o_dialog_history/Create_0.gml
@@ -52,7 +52,7 @@ event_inherited();
draw_set_text(f_p1, fa_left, fa_center, COLORS._main_text);
- var lh = line_height() + ui(8);
+ var lh = line_get_height() + ui(8);
var _h = 0, hh;
var yy = _y + ui(8);
diff --git a/objects/o_dialog_l_system/Create_0.gml b/objects/o_dialog_l_system/Create_0.gml
index cd14a00e1..e7388af59 100644
--- a/objects/o_dialog_l_system/Create_0.gml
+++ b/objects/o_dialog_l_system/Create_0.gml
@@ -76,7 +76,7 @@ event_inherited();
}
if(is_array(_f)) {
- var hh = (line_height(f_p0b) + pad) * array_length(_f) + ui(16);
+ var hh = (line_get_height(f_p0b) + pad) * array_length(_f) + ui(16);
BLEND_OVERRIDE
draw_sprite_stretched_ext(THEME.ui_panel_bg, 0, ui(8), yy,
sp_note.surface_w - ui(16), hh, COLORS.dialog_lua_ref_bg, 1);
@@ -87,7 +87,7 @@ event_inherited();
for( var j = 0; j < array_length(_f); j++ ) {
var _t = _f[j][0];
var _c = _f[j][1];
- hh = line_height(f_p0b);
+ hh = line_get_height(f_p0b);
draw_set_text(f_p0b, fa_left, fa_top, COLORS._main_text_accent);
draw_text(ui(32), yy, _t);
diff --git a/objects/o_dialog_load/Draw_64.gml b/objects/o_dialog_load/Draw_64.gml
index 248ec86cd..5db52485d 100644
--- a/objects/o_dialog_load/Draw_64.gml
+++ b/objects/o_dialog_load/Draw_64.gml
@@ -16,7 +16,7 @@ draw_set_alpha(1);
var py = dialog_y + ui(16);
draw_set_text(f_h5, fa_left, fa_top, COLORS._main_text_title);
draw_text(dialog_x + ui(24), py, "Project modified");
- py += line_height(, 4);
+ py += line_get_height(, 4);
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text);
draw_text(dialog_x + ui(24), py, "Do you want to save progress?");
diff --git a/objects/o_dialog_lua_reference/Create_0.gml b/objects/o_dialog_lua_reference/Create_0.gml
index ef695666c..002d6be49 100644
--- a/objects/o_dialog_lua_reference/Create_0.gml
+++ b/objects/o_dialog_lua_reference/Create_0.gml
@@ -28,7 +28,7 @@ event_inherited();
if(is_string(_f)) {
draw_set_text(f_p0b, fa_left, fa_top, COLORS._main_text_accent);
yy += ui(8);
- var hh = line_height() + pad + ui(8);
+ var hh = line_get_height() + pad + ui(8);
draw_text_over(ui(24), yy, _f);
@@ -42,12 +42,12 @@ event_inherited();
var _func = array_length(_f) > 2? _f[2] : _f[0];
var _desp = array_safe_get(_f, 3, "");
var _args = array_safe_get(_f, 4, []);
- var hh = line_height();
+ var hh = line_get_height();
if(is_open[i]) {
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text);
hh += pad + string_height(_desp) + ui(8);
if(array_length(_args))
- hh += (line_height() + ui(4)) * (array_length(_args) + 1) + ui(20);
+ hh += (line_get_height() + ui(4)) * (array_length(_args) + 1) + ui(20);
}
hh += pad * 2;
@@ -63,12 +63,12 @@ event_inherited();
sp_note.surface_w, hh, COLORS.dialog_lua_ref_bg, 1);
BLEND_NORMAL;
- draw_sprite_ui(THEME.arrow, is_open[i]? 3 : 0, ui(16), yy + pad + line_height() / 2,,,, COLORS._main_icon);
+ draw_sprite_ui(THEME.arrow, is_open[i]? 3 : 0, ui(16), yy + pad + line_get_height() / 2,,,, COLORS._main_icon);
draw_set_text(f_code, fa_left, fa_top, COLORS._main_text);
draw_code(ui(28), yy + pad, _func);
if(is_open[i]) {
- var ty = yy + pad + line_height() + ui(4);
+ var ty = yy + pad + line_get_height() + ui(4);
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text_sub);
draw_text(ui(32), ty, _desp);
@@ -76,9 +76,9 @@ event_inherited();
var ax0 = ui(64 + 16);
var ax1 = ui(200);
var ax2 = ui(320);
- ty += line_height() + ui(12);
+ ty += line_get_height() + ui(12);
- var ah = (line_height() + ui(4)) * (array_length(_args) + 1) + ui(8);
+ var ah = (line_get_height() + ui(4)) * (array_length(_args) + 1) + ui(8);
draw_sprite_stretched_ext(THEME.ui_panel_bg, 0, ui(64), ty, sp_note.surface_w - ui(96), ah,
COLORS.dialog_lua_ref_bg_args, 1);
@@ -88,13 +88,13 @@ event_inherited();
draw_text(ax2, ty, "Description");
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text);
- ty += line_height() + ui(4);
+ ty += line_get_height() + ui(4);
for( var j = 0; j < array_length(_args); j++ ) {
draw_text(ax0, ty, _args[j][0]);
draw_text(ax1, ty, _args[j][1]);
draw_text(ax2, ty, _args[j][2]);
- ty += line_height() + ui(4);
+ ty += line_get_height() + ui(4);
}
}
}
diff --git a/objects/o_dialog_migration/Draw_64.gml b/objects/o_dialog_migration/Draw_64.gml
index 4fec3b92a..1fa4b07ac 100644
--- a/objects/o_dialog_migration/Draw_64.gml
+++ b/objects/o_dialog_migration/Draw_64.gml
@@ -15,7 +15,7 @@ draw_set_alpha(1);
var py = dialog_y + ui(16);
draw_set_text(f_h5, fa_left, fa_top, COLORS._main_text_title);
draw_text(dialog_x + ui(24), py, get_text("dialog_migration_title", "Program directory changed in 1.13"));
- py += line_height(, 4);
+ py += line_get_height(, 4);
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text);
draw_text_ext(dialog_x + ui(24), py, get_text("dialog_migration_content",
diff --git a/objects/o_dialog_output_visibility/Create_0.gml b/objects/o_dialog_output_visibility/Create_0.gml
index 76c7f4930..afcdd29fb 100644
--- a/objects/o_dialog_output_visibility/Create_0.gml
+++ b/objects/o_dialog_output_visibility/Create_0.gml
@@ -17,7 +17,7 @@ event_inherited();
draw_clear_alpha(COLORS._main_text, 0);
if(node == noone) return 0;
- var hh = line_height() + ui(8);
+ var hh = line_get_height() + ui(8);
var _h = ds_list_size(node.outputs) * hh;
for( var i = 0; i < ds_list_size(node.outputs); i++ ) {
diff --git a/objects/o_dialog_preference/Draw_64.gml b/objects/o_dialog_preference/Draw_64.gml
index 55f89fc9e..9551a80f2 100644
--- a/objects/o_dialog_preference/Draw_64.gml
+++ b/objects/o_dialog_preference/Draw_64.gml
@@ -19,7 +19,7 @@ if !ready exit;
#region page
var yy = dialog_y + ui(title_height);
var yl = yy - ui(8);
- var hg = line_height(f_p0, 16);
+ var hg = line_get_height(f_p0, 16);
for(var i = 0; i < array_length(page); i++) {
draw_set_text(f_p0, fa_left, fa_center, COLORS._main_text);
diff --git a/objects/o_dialog_preset/Create_0.gml b/objects/o_dialog_preset/Create_0.gml
index dda2057c0..24850259d 100644
--- a/objects/o_dialog_preset/Create_0.gml
+++ b/objects/o_dialog_preset/Create_0.gml
@@ -21,7 +21,7 @@ event_inherited();
var pres = global.PRESETS_MAP[? folder];
var amo = array_length(pres);
- var hh = line_height() + ui(8);
+ var hh = line_get_height() + ui(8);
var _h = amo * (hh + ui(4)) + ui(32);
for( var i = 0; i < amo; i++ ) {
diff --git a/objects/o_dialog_scrollbox/Create_0.gml b/objects/o_dialog_scrollbox/Create_0.gml
index 482c63548..8598a1dce 100644
--- a/objects/o_dialog_scrollbox/Create_0.gml
+++ b/objects/o_dialog_scrollbox/Create_0.gml
@@ -54,7 +54,7 @@ event_inherited();
}
function setSize() {
- var hght = line_height(f_p0, 8);
+ var hght = line_get_height(f_p0, 8);
var hh = ui(16 + 24);
for( var i = 0; i < array_length(data); i++ )
@@ -68,7 +68,7 @@ event_inherited();
sc_content = new scrollPane(0, 0, function(_y, _m) {
draw_clear_alpha(COLORS.panel_bg_clear, 0);
- var hght = line_height(f_p0, 8);
+ var hght = line_get_height(f_p0, 8);
var _dw = sc_content.surface_w;
var _h = 0;
var _ly = _y;
diff --git a/objects/o_dialog_splash/Create_0.gml b/objects/o_dialog_splash/Create_0.gml
index bf2c04881..4d171bf43 100644
--- a/objects/o_dialog_splash/Create_0.gml
+++ b/objects/o_dialog_splash/Create_0.gml
@@ -45,7 +45,7 @@ event_inherited();
var ww = ui(264);
var hh = 0;
var pad = ui(8);
- var hgt = ui(16) + line_height(f_p0b) + line_height(f_p1);
+ var hgt = ui(16) + line_get_height(f_p0b) + line_get_height(f_p1);
_y += pad;
var col = expand? 2 : 1;
@@ -92,11 +92,11 @@ event_inherited();
}
}
- var ly = recent_thumbnail? _y + hg - (line_height(f_p0b) + line_height(f_p1)) - ui(8) : _y + ui(8);
+ var ly = recent_thumbnail? _y + hg - (line_get_height(f_p0b) + line_get_height(f_p1)) - ui(8) : _y + ui(8);
draw_set_text(f_p0b, fa_left, fa_top, COLORS._main_text);
draw_text(fx + ui(12), ly, filename_name(_rec));
- ly += line_height();
+ ly += line_get_height();
draw_set_text(f_p1, fa_left, fa_top, COLORS._main_text_sub);
draw_text_cut(fx + ui(12), ly, _rec, ww - ui(24));
}
@@ -179,7 +179,7 @@ event_inherited();
draw_set_color(_project.tag == "Getting started"? COLORS._main_text_accent : COLORS._main_text_sub);
draw_text(tx, ty - ui(2), _project.tag);
- ty += line_height();
+ ty += line_get_height();
}
draw_set_text(f_p1, fa_center, fa_top, COLORS._main_text);
diff --git a/objects/o_dialog_splash/Draw_64.gml b/objects/o_dialog_splash/Draw_64.gml
index 4609f70a8..a38d16698 100644
--- a/objects/o_dialog_splash/Draw_64.gml
+++ b/objects/o_dialog_splash/Draw_64.gml
@@ -17,7 +17,7 @@ if !ready exit;
var txt = "v. " + VERSION_STRING;
draw_set_text(f_p0, fa_left, fa_center, COLORS._main_text_sub);
var ww = string_width(txt) + ui(16);
- var hh = line_height(, 16);
+ var hh = line_get_height(, 16);
if(buttonInstant(THEME.button_hide, bx, by - hh / 2, ww, hh, mouse_ui, sFOCUS, sHOVER) == 2) {
dialogCall(o_dialog_release_note, WIN_W / 2, WIN_H / 2);
}
diff --git a/objects/o_main/Draw_75.gml b/objects/o_main/Draw_75.gml
index 6cae73c77..98068d2bf 100644
--- a/objects/o_main/Draw_75.gml
+++ b/objects/o_main/Draw_75.gml
@@ -28,6 +28,9 @@ if(OS == os_windows && gameframe_is_minimized()) exit;
case VALUE_TYPE.color :
draw_tooltip_color(content);
break;
+ case VALUE_TYPE.gradient :
+ draw_tooltip_gradient(content);
+ break;
case VALUE_TYPE.d3object :
draw_tooltip_text("[" + get_text("tooltip_3d_object", "3D Object") + "]");
break;
@@ -70,6 +73,9 @@ if(OS == os_windows && gameframe_is_minimized()) exit;
txt += " (groups: " + string(array_length(content)) + ")";
draw_tooltip_text("[" + txt + "]");
break;
+ case VALUE_TYPE.atlas :
+ draw_tooltip_atlas(content);
+ break;
}
} else
draw_tooltip_text(TOOLTIP);
diff --git a/scripts/_node_VFX_spawner/_node_VFX_spawner.gml b/scripts/_node_VFX_spawner/_node_VFX_spawner.gml
index 0862f938e..e8cbf3b93 100644
--- a/scripts/_node_VFX_spawner/_node_VFX_spawner.gml
+++ b/scripts/_node_VFX_spawner/_node_VFX_spawner.gml
@@ -34,8 +34,7 @@ function Node_VFX_Spawner_Base(_x, _y, _group = noone) : Node(_x, _y, _group) co
inputs[| 11] = nodeValue("Scale over time", self, JUNCTION_CONNECT.input, VALUE_TYPE.curve, CURVE_DEF_11 );
- inputs[| 12] = nodeValue("Color over lifetime", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 12] = nodeValue("Color over lifetime", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 13] = nodeValue("Alpha", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 1, 1 ])
.setDisplay(VALUE_DISPLAY.range);
@@ -78,8 +77,7 @@ function Node_VFX_Spawner_Base(_x, _y, _group = noone) : Node(_x, _y, _group) co
inputs[| 27] = nodeValue("Spawn", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, true);
- inputs[| 28] = nodeValue("Random blend", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 28] = nodeValue("Random blend", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 29] = nodeValue("Directed from center", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false, "Make particle move away from the spawn center.");
diff --git a/scripts/buttonGradient/buttonGradient.gml b/scripts/buttonGradient/buttonGradient.gml
index 19cc36f81..587e6412e 100644
--- a/scripts/buttonGradient/buttonGradient.gml
+++ b/scripts/buttonGradient/buttonGradient.gml
@@ -24,6 +24,8 @@ function buttonGradient(_onApply, dialog = noone) : widget() constructor {
y = _y;
w = _w;
h = _h;
+ if(!is_instanceof(_gradient, gradientObject)) return;
+
current_gradient = _gradient;
var click = false;
diff --git a/scripts/byte_writer/byte_writer.gml b/scripts/byte_writer/byte_writer.gml
new file mode 100644
index 000000000..014f58d3c
--- /dev/null
+++ b/scripts/byte_writer/byte_writer.gml
@@ -0,0 +1,27 @@
+function buffer_write_int8_little(buffer, value) {
+ buffer_write(buffer, buffer_u8, clamp(value, -128, 127));
+}
+
+function buffer_write_int16_little(buffer, value) {
+ value = ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8);
+ buffer_write(buffer, buffer_s16, value);
+}
+
+function buffer_write_int32_little(buffer, value) {
+ value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24);
+ buffer_write(buffer, buffer_s32, value);
+}
+
+function buffer_write_uint8_little(buffer, value) {
+ buffer_write(buffer, buffer_u8, clamp(value, 0, 255));
+}
+
+function buffer_write_uint16_little(buffer, value) {
+ value = ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8);
+ buffer_write(buffer, buffer_u16, value);
+}
+
+function buffer_write_uint32_little(buffer, value) {
+ value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24);
+ buffer_write(buffer, buffer_u32, value);
+}
diff --git a/scripts/byte_writer/byte_writer.yy b/scripts/byte_writer/byte_writer.yy
new file mode 100644
index 000000000..bc1cd8578
--- /dev/null
+++ b/scripts/byte_writer/byte_writer.yy
@@ -0,0 +1,11 @@
+{
+ "resourceType": "GMScript",
+ "resourceVersion": "1.0",
+ "name": "byte_writer",
+ "isCompatibility": false,
+ "isDnD": false,
+ "parent": {
+ "name": "importers",
+ "path": "folders/functions/importers.yy",
+ },
+}
\ No newline at end of file
diff --git a/scripts/draw_UI_scale/draw_UI_scale.gml b/scripts/draw_UI_scale/draw_UI_scale.gml
index 5515cf733..790c5deb9 100644
--- a/scripts/draw_UI_scale/draw_UI_scale.gml
+++ b/scripts/draw_UI_scale/draw_UI_scale.gml
@@ -1,4 +1,4 @@
-function line_height(font = noone, offset = 0) {
+function line_get_height(font = noone, offset = 0) {
var ff = draw_get_font();
if(font != noone)
@@ -9,7 +9,7 @@ function line_height(font = noone, offset = 0) {
return hh;
}
-function line_width(txt, font = noone, offset = 0) {
+function line_get_width(txt, font = noone, offset = 0) {
var ff = draw_get_font();
if(font != noone)
@@ -21,7 +21,7 @@ function line_width(txt, font = noone, offset = 0) {
}
#region global
- #macro TEXTBOX_HEIGHT line_height(f_p0, 12)
+ #macro TEXTBOX_HEIGHT line_get_height(f_p0, 12)
function ui(val) {
gml_pragma("forceinline");
diff --git a/scripts/draw_tooltip/draw_tooltip.gml b/scripts/draw_tooltip/draw_tooltip.gml
index 31300738f..884726c71 100644
--- a/scripts/draw_tooltip/draw_tooltip.gml
+++ b/scripts/draw_tooltip/draw_tooltip.gml
@@ -18,11 +18,6 @@ function draw_tooltip_color(clr) {
return;
}
- if(is_struct(clr)) {
- draw_tooltip_gradient(clr);
- return;
- }
-
var ww = ui(32);
var hh = ui(32);
@@ -120,4 +115,49 @@ function draw_tooltip_surface(surf) {
draw_sprite_stretched(THEME.textbox, 0, mx, my, ww + ui(16), hh + ui(16));
draw_surface_ext_safe(surf, mx + ui(8), my + ui(8), ss, ss);
+}
+
+function draw_tooltip_atlas(atlas) {
+ if(!is_array(atlas)) atlas = [ atlas ];
+
+ var amo = array_length(atlas);
+ var ww = ui(160);
+ var hh = amo * ui(48 + 8) - ui(8);
+
+ var mx = min(mouse_mx + ui(16), WIN_W - (ww + ui(16)));
+ var my = min(mouse_my + ui(16), WIN_H - (hh + ui(16)));
+
+ draw_sprite_stretched(THEME.textbox, 3, mx, my, ww + ui(16), hh + ui(16));
+ draw_sprite_stretched(THEME.textbox, 0, mx, my, ww + ui(16), hh + ui(16));
+
+ var sx = mx + ui(8);
+ var sy = my + ui(8);
+
+ for( var i = 0; i < amo; i++ ) {
+ var _y = sy + i * ui(48 + 8);
+
+ var atl = atlas[i];
+ var surf = atl.surface.get();
+
+ if(!is_surface(surf)) continue;
+
+ var sw = surface_get_width(surf);
+ var sh = surface_get_height(surf);
+
+ var ss = min(ui(48) / sw, ui(48) / sh);
+ draw_surface_ext_safe(surf, sx, _y, ss, ss);
+
+ draw_set_color(COLORS._main_icon);
+ draw_rectangle(sx, _y, sx + ui(48), _y + ui(48), 1);
+
+ draw_set_text(f_p3, fa_left, fa_top, COLORS._main_text_sub);
+ draw_text_add(sx + ui( 56), _y + ui( 0), "Position");
+ draw_text_add(sx + ui( 56), _y + ui(16), "Rotation");
+ draw_text_add(sx + ui( 56), _y + ui(32), "Scale");
+
+ draw_set_text(f_p3, fa_right, fa_top, COLORS._main_text);
+ draw_text_add(sx + ui(160), _y + ui( 0), atl.position);
+ draw_text_add(sx + ui(160), _y + ui(16), atl.rotation);
+ draw_text_add(sx + ui(160), _y + ui(32), atl.scale);
+ }
}
\ No newline at end of file
diff --git a/scripts/globals/globals.gml b/scripts/globals/globals.gml
index 9d5bd7612..b05ab318c 100644
--- a/scripts/globals/globals.gml
+++ b/scripts/globals/globals.gml
@@ -32,10 +32,10 @@
globalvar VERSION, SAVEFILE_VERSION, VERSION_STRING, BUILD_NUMBER;
- VERSION = 1144;
+ VERSION = 1145;
SAVEFILE_VERSION = 1440;
- VERSION_STRING = "1.14.4.1";
- BUILD_NUMBER = 114400;
+ VERSION_STRING = "1.14.5";
+ BUILD_NUMBER = 114500;
globalvar NODES, NODE_MAP, APPEND_MAP, NODE_NAME_MAP;
globalvar HOTKEYS, HOTKEY_CONTEXT, NODE_INSTANCES;
diff --git a/scripts/globalvar_drawer/globalvar_drawer.gml b/scripts/globalvar_drawer/globalvar_drawer.gml
index c6b06322b..4beaf52ad 100644
--- a/scripts/globalvar_drawer/globalvar_drawer.gml
+++ b/scripts/globalvar_drawer/globalvar_drawer.gml
@@ -4,7 +4,7 @@ function globalvar_viewer_init() {
function globalvar_viewer_draw(xx, yy, ww, _m, focus, hover, _scrollPane, rx, ry) {
var hh = 0;
- var lb_h = line_height(f_p0) + ui(8);
+ var lb_h = line_get_height(f_p0) + ui(8);
var padd = ui(8);
if(var_editing) {
diff --git a/scripts/meta_data/meta_data.gml b/scripts/meta_data/meta_data.gml
index 29ce294a3..57393cef0 100644
--- a/scripts/meta_data/meta_data.gml
+++ b/scripts/meta_data/meta_data.gml
@@ -23,11 +23,11 @@ function MetaDataManager() constructor {
steam = false;
static displays = [
- [ get_text("description", "Description"), function(meta) { return meta.description; } , line_height() * 5],
- [ get_text("author", "Author"), function(meta) { return meta.author; } , line_height() ],
- [ get_text("contact_info", "Contact info"), function(meta) { return meta.contact; } , line_height() ],
- [ get_text("alias", "Alias"), function(meta) { return meta.alias; } , line_height() ],
- [ get_text("tags", "Tags"), function(meta) { return meta.tags; } , line_height() ],
+ [ get_text("description", "Description"), function(meta) { return meta.description; } , line_get_height() * 5],
+ [ get_text("author", "Author"), function(meta) { return meta.author; } , line_get_height() ],
+ [ get_text("contact_info", "Contact info"), function(meta) { return meta.contact; } , line_get_height() ],
+ [ get_text("alias", "Alias"), function(meta) { return meta.alias; } , line_get_height() ],
+ [ get_text("tags", "Tags"), function(meta) { return meta.tags; } , line_get_height() ],
];
static serialize = function() {
@@ -130,7 +130,7 @@ function MetaDataManager() constructor {
draw_set_font(f_p0);
_h += ui(8);
var tx = 0;
- var hh = line_height(f_p0, ui(4));
+ var hh = line_get_height(f_p0, ui(4));
var th = hh;
for( var i = 0; i < array_length(tags); i++ ) {
var ww = string_width(tags[i]) + ui(16);
@@ -191,7 +191,7 @@ function MetaDataManager() constructor {
draw_set_text(f_p0, fa_left, fa_center, COLORS._main_text);
ty += ui(8);
var tx = 0;
- var hh = line_height(f_p0, ui(4));
+ var hh = line_get_height(f_p0, ui(4));
for( var i = 0; i < array_length(tags); i++ ) {
var ww = string_width(tags[i]) + ui(16);
diff --git a/scripts/nodeValue_drawer/nodeValue_drawer.gml b/scripts/nodeValue_drawer/nodeValue_drawer.gml
index e69605831..220a2230a 100644
--- a/scripts/nodeValue_drawer/nodeValue_drawer.gml
+++ b/scripts/nodeValue_drawer/nodeValue_drawer.gml
@@ -11,7 +11,7 @@ function drawWidget(xx, yy, ww, _m, jun, global_var = true, _hover = false, _foc
var con_w = ww - ui(4);
var xc = xx + ww / 2;
- var lb_h = line_height(f_p0) + ui(8);
+ var lb_h = line_get_height(f_p0) + ui(8);
var lb_y = yy + lb_h / 2;
var butx = xx;
@@ -265,6 +265,9 @@ function drawWidget(xx, yy, ww, _m, jun, global_var = true, _hover = false, _foc
case VALUE_TYPE.color :
jun.editWidget.draw(editBoxX, editBoxY, editBoxW, editBoxH, jun.showValue(), _m);
break;
+ case VALUE_TYPE.gradient :
+ jun.editWidget.draw(editBoxX, editBoxY, editBoxW, editBoxH, jun.showValue(), _m);
+ break;
case VALUE_TYPE.path :
switch(jun.display_type) {
case VALUE_DISPLAY.path_load :
diff --git a/scripts/node_ase_file_read/node_ase_file_read.gml b/scripts/node_ase_file_read/node_ase_file_read.gml
index 4e14f5525..501292244 100644
--- a/scripts/node_ase_file_read/node_ase_file_read.gml
+++ b/scripts/node_ase_file_read/node_ase_file_read.gml
@@ -89,7 +89,7 @@ function Node_ASE_File_Read(_x, _y, _group = noone) : Node(_x, _y, _group) const
var current_tag = inputs[| 2].getValue();
var amo = array_length(tags);
var abx = ui(24);
- var lb_h = line_height(f_p0);
+ var lb_h = line_get_height(f_p0);
var lb_y = _y + lb_h / 2 + ui(6);
var by = _y;
diff --git a/scripts/node_colorize/node_colorize.gml b/scripts/node_colorize/node_colorize.gml
index 0edef932e..872af8077 100644
--- a/scripts/node_colorize/node_colorize.gml
+++ b/scripts/node_colorize/node_colorize.gml
@@ -11,8 +11,7 @@ function Node_Colorize(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
inputs[| 0] = nodeValue("Surface in", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, 0);
- inputs[| 1] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject([ c_black, c_white ]) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 1] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject([ c_black, c_white ]) );
inputs[| 2] = nodeValue("Gradient shift", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0)
.setDisplay(VALUE_DISPLAY.slider, [ -1, 1, .01 ]);
diff --git a/scripts/node_composite/node_composite.gml b/scripts/node_composite/node_composite.gml
index d06041bf4..47f4dafce 100644
--- a/scripts/node_composite/node_composite.gml
+++ b/scripts/node_composite/node_composite.gml
@@ -244,7 +244,8 @@ function Node_Composite(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
outputs[| 0] = nodeValue("Surface out", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone);
- outputs[| 1] = nodeValue("Atlas data", self, JUNCTION_CONNECT.output, VALUE_TYPE.atlas, []);
+ outputs[| 1] = nodeValue("Atlas data", self, JUNCTION_CONNECT.output, VALUE_TYPE.atlas, [])
+ .rejectArrayProcess();
temp_surface = [ surface_create(1, 1), surface_create(1, 1) ];
@@ -717,6 +718,7 @@ function Node_Composite(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
static process_data = function(_outSurf, _data, _output_index, _array_index) {
if(_output_index == 1) return atlas_data;
+ if(_output_index == 0 && _array_index == 0) atlas_data = [];
if(array_length(_data) < 4) return _outSurf;
var _pad = _data[0];
@@ -766,7 +768,6 @@ function Node_Composite(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
var res_index = 0, bg = 0;
var imageAmo = (ds_list_size(inputs) - input_fix_len) / data_length;
var _vis = attributes[? "layer_visible"];
- atlas_data = [];
surface_set_shader(_outSurf, sh_sample, true, BLEND.alphamulp);
diff --git a/scripts/node_display_text/node_display_text.gml b/scripts/node_display_text/node_display_text.gml
index dfafae6f9..20f9c8db2 100644
--- a/scripts/node_display_text/node_display_text.gml
+++ b/scripts/node_display_text/node_display_text.gml
@@ -237,7 +237,7 @@ function Node_Display_Text(_x, _y, _group = noone) : Node(_x, _y, _group) constr
draw_set_text(font, fa_left, fa_top, color);
for( var i = 0; i < array_length(_lines); i++ ) {
var _line = _lines[i];
- var _h = line_height(font);
+ var _h = line_get_height(font);
var _w = draw_text_style(tx, ty, _line, _s);
ww = max(ww, _w);
diff --git a/scripts/node_export/node_export.gml b/scripts/node_export/node_export.gml
index e7418a06b..7ca766b83 100644
--- a/scripts/node_export/node_export.gml
+++ b/scripts/node_export/node_export.gml
@@ -103,6 +103,8 @@ function Node_Export(_x, _y, _group = noone) : Node(_x, _y, _group) constructor
magick = working_directory + "ImageMagick/magick.exe";
webp = working_directory + "webp/webpmux.exe";
+ gifski = working_directory + "gifski\\win\\gifski.exe";
+
static onValueUpdate = function(_index) {
var form = inputs[| 3].getValue();
@@ -200,31 +202,33 @@ function Node_Export(_x, _y, _group = noone) : Node(_x, _y, _group) constructor
}
static renderGif = function(temp_path, target_path) {
- var loop = inputs[| 5].getValue();
- var opti = inputs[| 6].getValue();
- var fuzz = inputs[| 7].getValue();
- var rate = inputs[| 8].getValue();
+ var loop = inputs[| 5].getValue();
+ var opti = inputs[| 6].getValue();
+ var fuzz = inputs[| 7].getValue();
+ var rate = inputs[| 8].getValue();
+ var qual = inputs[| 10].getValue();
if(rate == 0) rate = 1;
- var framerate = 100 / rate;
- var loop_str = loop? 0 : 1;
+ target_path = string_replace_all(target_path, "/", "\\");
+ var framerate = 100 / rate;
+ var loop_str = loop? 0 : 1;
+ var use_gifski = false;
- var shell_cmd = "-delay " + string(framerate) +
- " -alpha set" +
- " -dispose previous" +
- " -loop " + string(loop_str);
-
- if(opti) {
- shell_cmd += " -fuzz " + string(fuzz * 100) + "%" +
- " -layers OptimizeFrame" +
- " -layers OptimizeTransparency";
+ if(use_gifski) {
+ var shell_cmd = $"-o {target_path} -r {rate} --repeat {loop_str} -Q {qual} ";
+ shell_cmd += temp_path;
+
+ //print($"{gifski} {shell_cmd}");
+ execute_shell(gifski, shell_cmd);
+ } else {
+ var shell_cmd = $"-delay {string(framerate)} -alpha set -dispose previous -loop {string(loop_str)}";
+ if(opti) shell_cmd += $" -fuzz {string(fuzz * 100)}% -layers OptimizeFrame -layers OptimizeTransparency";
+ shell_cmd += " " + temp_path + " " + target_path;
+
+ //print($"{converter} {shell_cmd}");
+ execute_shell(converter, shell_cmd);
}
- shell_cmd += " " + temp_path +
- " " + target_path;
-
- execute_shell(converter, shell_cmd);
-
var noti = log_message("EXPORT", "Export gif as " + target_path, THEME.noti_icon_tick, COLORS._main_value_positive, false);
noti.path = filename_dir(target_path);
noti.setOnClick(function() { shellOpenExplorer(self.path); }, "Open in explorer", THEME.explorer);
@@ -476,7 +480,7 @@ function Node_Export(_x, _y, _group = noone) : Node(_x, _y, _group) constructor
if(anim == NODE_EXPORT_FORMAT.gif) {
inputs[| 9].display_data = format_animation;
inputs[| 9].editWidget.data_list = format_animation;
- inputs[| 10].setVisible(extn == 1);
+ inputs[| 10].setVisible(true);
} else {
inputs[| 9].display_data = format_image;
inputs[| 9].editWidget.data_list = format_image;
diff --git a/scripts/node_global/node_global.gml b/scripts/node_global/node_global.gml
index 8819db71f..d2ac72367 100644
--- a/scripts/node_global/node_global.gml
+++ b/scripts/node_global/node_global.gml
@@ -1,8 +1,8 @@
function variable_editor(nodeVal) constructor {
value = nodeVal;
- val_type = [ VALUE_TYPE.integer, VALUE_TYPE.float, VALUE_TYPE.boolean, VALUE_TYPE.color, VALUE_TYPE.path, VALUE_TYPE.curve, VALUE_TYPE.text ];
- val_type_name = [ "Integer", "Float", "Boolean", "Color", "Path", "Curve", "Text" ];
+ val_type = [ VALUE_TYPE.integer, VALUE_TYPE.float, VALUE_TYPE.boolean, VALUE_TYPE.color, VALUE_TYPE.gradient, VALUE_TYPE.path, VALUE_TYPE.curve, VALUE_TYPE.text ];
+ val_type_name = [ "Integer", "Float", "Boolean", "Color", "Gradient", "Path", "Curve", "Text" ];
display_list = [
/*Integer*/ [ "Default", "Range", "Rotation", "Rotation range", "Slider", "Slider range", "Padding", "Vector2", "Vector3", "Vector4", "Vector range", "Vector2 range", "Area" ],
/*Float*/ [ "Default", "Range", "Rotation", "Rotation range", "Slider", "Slider range", "Padding", "Vector2", "Vector3", "Vector4", "Vector range", "Vector2 range", "Area" ],
@@ -93,9 +93,6 @@ function variable_editor(nodeVal) constructor {
break;
case VALUE_TYPE.color :
switch(sc_disp.data_list[disp_index]) {
- case "Gradient" :
- value.setValue(new gradientObject(c_black));
- break;
case "Palette" :
value.setValue([0]);
break;
@@ -104,6 +101,9 @@ function variable_editor(nodeVal) constructor {
break;
}
break;
+ case VALUE_TYPE.gradient :
+ value.setValue(new gradientObject(c_black));
+ break;
case VALUE_TYPE.boolean :
value.setValue(false);
break;
@@ -135,7 +135,6 @@ function variable_editor(nodeVal) constructor {
case "Vector range" : value.setDisplay(VALUE_DISPLAY.vector_range); break;
case "Vector2 range" : value.setDisplay(VALUE_DISPLAY.vector_range); break;
case "Area" : value.setDisplay(VALUE_DISPLAY.area); break;
- case "Gradient" : value.setDisplay(VALUE_DISPLAY.gradient); break;
case "Palette" : value.setDisplay(VALUE_DISPLAY.palette); break;
case "Import" : value.setDisplay(VALUE_DISPLAY.path_load, ["", ""]); break;
diff --git a/scripts/node_gradient/node_gradient.gml b/scripts/node_gradient/node_gradient.gml
index 6b564d5b8..6f5053fab 100644
--- a/scripts/node_gradient/node_gradient.gml
+++ b/scripts/node_gradient/node_gradient.gml
@@ -18,8 +18,7 @@ function Node_Gradient(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
inputs[| 0] = nodeValue("Dimension", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, def_surf_size2 )
.setDisplay(VALUE_DISPLAY.vector);
- inputs[| 1] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 1] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 2] = nodeValue("Type", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.enum_scroll, [ "Linear", "Circular", "Radial" ]);
diff --git a/scripts/node_gradient_extract/node_gradient_extract.gml b/scripts/node_gradient_extract/node_gradient_extract.gml
index bb13df56c..129dc29e8 100644
--- a/scripts/node_gradient_extract/node_gradient_extract.gml
+++ b/scripts/node_gradient_extract/node_gradient_extract.gml
@@ -4,8 +4,7 @@ function Node_Gradient_Extract(_x, _y, _group = noone) : Node_Processor(_x, _y,
w = 96;
- inputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient)
+ inputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) )
.setVisible(true, true);
outputs[| 0] = nodeValue("Colors", self, JUNCTION_CONNECT.output, VALUE_TYPE.color, [] )
diff --git a/scripts/node_gradient_output/node_gradient_output.gml b/scripts/node_gradient_output/node_gradient_output.gml
index fdc16e435..c810a5e37 100644
--- a/scripts/node_gradient_output/node_gradient_output.gml
+++ b/scripts/node_gradient_output/node_gradient_output.gml
@@ -4,15 +4,13 @@ function Node_Gradient_Out(_x, _y, _group = noone) : Node_Processor(_x, _y, _gro
w = 96;
- inputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 1] = nodeValue("Sample", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0, "Position to sample a color from the gradient.")
.setDisplay(VALUE_DISPLAY.slider, [0, 1, 0.01])
.rejectArray();
- outputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.output, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ outputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.output, VALUE_TYPE.gradient, new gradientObject(c_white) );
outputs[| 1] = nodeValue("Color", self, JUNCTION_CONNECT.output, VALUE_TYPE.color, c_white);
diff --git a/scripts/node_gradient_palette/node_gradient_palette.gml b/scripts/node_gradient_palette/node_gradient_palette.gml
index 9b2129aba..fedbe5d62 100644
--- a/scripts/node_gradient_palette/node_gradient_palette.gml
+++ b/scripts/node_gradient_palette/node_gradient_palette.gml
@@ -1,5 +1,5 @@
function Node_Gradient_Palette(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) constructor {
- name = "Gradient Palette";
+ name = "Palette to Gradient";
previewable = false;
w = 96;
@@ -17,8 +17,7 @@ function Node_Gradient_Palette(_x, _y, _group = noone) : Node_Processor(_x, _y,
inputs[| 3] = nodeValue("Blending", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.enum_button, [ "RGB", "HSV", "Hard" ]);
- outputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.output, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ outputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.output, VALUE_TYPE.gradient, new gradientObject(c_white) )
_pal = -1;
diff --git a/scripts/node_gradient_replace/node_gradient_replace.gml b/scripts/node_gradient_replace/node_gradient_replace.gml
index 29e43b9bd..139212d62 100644
--- a/scripts/node_gradient_replace/node_gradient_replace.gml
+++ b/scripts/node_gradient_replace/node_gradient_replace.gml
@@ -4,8 +4,7 @@ function Node_Gradient_Replace_Color(_x, _y, _group = noone) : Node_Processor(_x
w = 96;
- inputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient)
+ inputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) )
.setVisible(true, true);
inputs[| 1] = nodeValue("Color from", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, [ c_black ])
@@ -17,8 +16,7 @@ function Node_Gradient_Replace_Color(_x, _y, _group = noone) : Node_Processor(_x
inputs[| 3] = nodeValue("Threshold", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)
.setDisplay(VALUE_DISPLAY.slider, [0, 1, 0.01]);
- outputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.output, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ outputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.output, VALUE_TYPE.gradient, new gradientObject(c_white) );
static process_data = function(_outSurf, _data, _output_index, _array_index) {
var gra = _data[0];
diff --git a/scripts/node_gradient_shift/node_gradient_shift.gml b/scripts/node_gradient_shift/node_gradient_shift.gml
index 7bbf96ec2..a341d3dfe 100644
--- a/scripts/node_gradient_shift/node_gradient_shift.gml
+++ b/scripts/node_gradient_shift/node_gradient_shift.gml
@@ -4,8 +4,7 @@ function Node_Gradient_Shift(_x, _y, _group = noone) : Node_Processor(_x, _y, _g
w = 96;
- inputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient)
+ inputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) )
.setDisplay(true, true);
inputs[| 1] = nodeValue("Shift", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0)
@@ -16,8 +15,7 @@ function Node_Gradient_Shift(_x, _y, _group = noone) : Node_Processor(_x, _y, _g
inputs[| 3] = nodeValue("Scale", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 1)
.setDisplay(VALUE_DISPLAY.slider, [0, 2, 0.01]);
- outputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.output, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ outputs[| 0] = nodeValue("Gradient", self, JUNCTION_CONNECT.output, VALUE_TYPE.gradient, new gradientObject(c_white) );
_pal = -1;
diff --git a/scripts/node_grid/node_grid.gml b/scripts/node_grid/node_grid.gml
index 7aa3c788a..2a431b6a3 100644
--- a/scripts/node_grid/node_grid.gml
+++ b/scripts/node_grid/node_grid.gml
@@ -34,8 +34,7 @@ function Node_Grid(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) cons
inputs[| 4] = nodeValue("Angle", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0)
.setDisplay(VALUE_DISPLAY.rotation);
- inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 6] = nodeValue("Gap color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, c_black);
diff --git a/scripts/node_grid_hex/node_grid_hex.gml b/scripts/node_grid_hex/node_grid_hex.gml
index a29bebeec..f4aa1ce18 100644
--- a/scripts/node_grid_hex/node_grid_hex.gml
+++ b/scripts/node_grid_hex/node_grid_hex.gml
@@ -32,8 +32,7 @@ function Node_Grid_Hex(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
inputs[| 4] = nodeValue("Gap", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0.1)
.setDisplay(VALUE_DISPLAY.slider, [0, 0.5, 0.01]);
- inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 6] = nodeValue("Gap color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, c_black);
diff --git a/scripts/node_grid_tri/node_grid_tri.gml b/scripts/node_grid_tri/node_grid_tri.gml
index 7c1c75b6d..c0daaf2db 100644
--- a/scripts/node_grid_tri/node_grid_tri.gml
+++ b/scripts/node_grid_tri/node_grid_tri.gml
@@ -32,8 +32,7 @@ function Node_Grid_Tri(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
inputs[| 4] = nodeValue("Angle", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0)
.setDisplay(VALUE_DISPLAY.rotation);
- inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 5] = nodeValue("Tile color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 6] = nodeValue("Gap color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, c_black);
diff --git a/scripts/node_group_input/node_group_input.gml b/scripts/node_group_input/node_group_input.gml
index 604d47fff..b735807f1 100644
--- a/scripts/node_group_input/node_group_input.gml
+++ b/scripts/node_group_input/node_group_input.gml
@@ -107,7 +107,7 @@ function Node_Group_Input(_x, _y, _group = noone) : Node(_x, _y, _group) constru
_dtype = display_list[_val_type][_dtype];
- inParent.type = _val_type;
+ inParent.type = _val_type;
outputs[| 0].type = _val_type;
var _val = inParent.getValue();
@@ -177,8 +177,11 @@ function Node_Group_Input(_x, _y, _group = noone) : Node(_x, _y, _group) constru
break;
case "Gradient":
+ inParent.type = VALUE_TYPE.gradient;
+ outputs[| 0].type = inParent.type;
+
inParent.animator = new valueAnimator(new gradientObject(c_white), inParent);
- inParent.setDisplay(VALUE_DISPLAY.gradient);
+ inParent.setDisplay(VALUE_DISPLAY._default);
break;
default: inParent.setDisplay(VALUE_DISPLAY._default); break;
}
diff --git a/scripts/node_keyframe/node_keyframe.gml b/scripts/node_keyframe/node_keyframe.gml
index 35b1f2942..8d2c9e934 100644
--- a/scripts/node_keyframe/node_keyframe.gml
+++ b/scripts/node_keyframe/node_keyframe.gml
@@ -142,7 +142,7 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor {
if(ds_list_size(values) == 1)
return processType(values[| 0].value);
- if(prop.display_type == VALUE_DISPLAY.gradient)
+ if(prop.type == VALUE_TYPE.gradient)
return values[| 0].value;
if(prop.type == VALUE_TYPE.path)
@@ -389,7 +389,7 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor {
static deserialize = function(_list, scale = false) {
ds_list_clear(values);
- if(prop.type == VALUE_TYPE.color && prop.display_type == VALUE_DISPLAY.gradient && LOADING_VERSION < 1340 && !CLONING) { //backward compat: Gradient
+ if(prop.type == VALUE_TYPE.gradient && LOADING_VERSION < 1340 && !CLONING) { //backward compat: Gradient
var _val = [];
var value = _list[| 0][| 1];
@@ -430,7 +430,7 @@ function valueAnimator(_val, _prop, _sep_axis = false) constructor {
else if(prop.type == VALUE_TYPE.path && prop.display_type == VALUE_DISPLAY.path_array) {
for(var j = 0; j < ds_list_size(value); j++)
_val[j] = value[| j];
- } else if(prop.type == VALUE_TYPE.color && prop.display_type == VALUE_DISPLAY.gradient) {
+ } else if(prop.type == VALUE_TYPE.gradient) {
var grad = new gradientObject();
_val = grad.deserialize(value);
} else if(!sep_axis && typeArray(prop.display_type)) {
diff --git a/scripts/node_line/node_line.gml b/scripts/node_line/node_line.gml
index f1263a616..230350d54 100644
--- a/scripts/node_line/node_line.gml
+++ b/scripts/node_line/node_line.gml
@@ -30,8 +30,7 @@ function Node_Line(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) cons
inputs[| 9] = nodeValue("Shift", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0)
.setDisplay(VALUE_DISPLAY._default, 1 / 64);
- inputs[| 10] = nodeValue("Color over length", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 10] = nodeValue("Color over length", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 11] = nodeValue("Width over length", self, JUNCTION_CONNECT.input, VALUE_TYPE.curve, CURVE_DEF_11);
diff --git a/scripts/node_pixel_cloud/node_pixel_cloud.gml b/scripts/node_pixel_cloud/node_pixel_cloud.gml
index 0435e732a..7149ea196 100644
--- a/scripts/node_pixel_cloud/node_pixel_cloud.gml
+++ b/scripts/node_pixel_cloud/node_pixel_cloud.gml
@@ -27,8 +27,7 @@ function Node_Pixel_Cloud(_x, _y, _group = noone) : Node_Processor(_x, _y, _grou
inputs[| 3] = nodeValue("Strength map", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, 0);
- inputs[| 4] = nodeValue("Color over lifetime", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 4] = nodeValue("Color over lifetime", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 5] = nodeValue("Distance", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 1);
diff --git a/scripts/node_plot_linear/node_plot_linear.gml b/scripts/node_plot_linear/node_plot_linear.gml
index 6c1b2435c..fd0840bf2 100644
--- a/scripts/node_plot_linear/node_plot_linear.gml
+++ b/scripts/node_plot_linear/node_plot_linear.gml
@@ -34,8 +34,7 @@ function Node_Plot_Linear(_x, _y, _group = noone) : Node_Processor(_x, _y, _grou
inputs[| 12] = nodeValue("Value Offset", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0);
- inputs[| 13] = nodeValue("Color Over Sample", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white))
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 13] = nodeValue("Color Over Sample", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white));
inputs[| 14] = nodeValue("Trim mode", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
.setDisplay(VALUE_DISPLAY.enum_scroll, [ "Range", "Window" ]);
diff --git a/scripts/node_processor/node_processor.gml b/scripts/node_processor/node_processor.gml
index b80131eed..a28eee719 100644
--- a/scripts/node_processor/node_processor.gml
+++ b/scripts/node_processor/node_processor.gml
@@ -206,9 +206,13 @@ function Node_Processor(_x, _y, _group = noone) : Node(_x, _y, _group) construct
process_length[i][1] = amoMax;
}
+ var val;
for(var i = 0; i < ds_list_size(outputs); i++) {
- var val = preProcess(i);
- if(val == undefined) continue;
+ if(outputs[| i].process_array) {
+ val = preProcess(i);
+ if(val == undefined) continue;
+ } else
+ val = process_data(noone, noone, i);
outputs[| i].setValue(val);
}
}
diff --git a/scripts/node_registry/node_registry.gml b/scripts/node_registry/node_registry.gml
index 1dc407c33..f6916a882 100644
--- a/scripts/node_registry/node_registry.gml
+++ b/scripts/node_registry/node_registry.gml
@@ -230,6 +230,7 @@ function NodeObject(_name, _spr, _node, _create, tags = []) constructor {
addNodeObject(input, "ASE File In", s_node_ase_file, "Node_ASE_File_Read", [0, Node_create_ASE_File_Read],, "Load Aseprite file with support for layers, tags.").setVersion(1100);
addNodeObject(input, "ASE Layer", s_node_ase_layer, "Node_ASE_layer", [1, Node_ASE_layer]).setVersion(1100);
addNodeObject(input, "WAV File In", s_node_wav_file_read, "Node_WAV_File_Read", [0, Node_create_WAV_File_Read],, "Load wav audio file.").setVersion(1144);
+ addNodeObject(input, "WAV File Out", s_node_wav_file_write, "Node_WAV_File_Write", [1, Node_WAV_File_Write],, "Save wav audio file.").setVersion(1145);
var transform = ds_list_create();
addNodeCatagory("Transform", transform);
@@ -528,7 +529,7 @@ function NodeObject(_name, _spr, _node, _create, tags = []) constructor {
ds_list_add(color, "Gradient");
addNodeObject(color, "Gradient", s_node_gradient_out, "Node_Gradient_Out", [1, Node_Gradient_Out]);
- addNodeObject(color, "Gradient Palette", s_node_gradient_palette, "Node_Gradient_Palette", [1, Node_Gradient_Palette],, "Create gradient from palette.").setVersion(1135);
+ addNodeObject(color, "Palette to Gradient", s_node_gradient_palette, "Node_Gradient_Palette", [1, Node_Gradient_Palette],, "Create gradient from palette.").setVersion(1135);
addNodeObject(color, "Gradient Shift", s_node_gradient_shift, "Node_Gradient_Shift", [1, Node_Gradient_Shift],, "Move gradients keys.");
addNodeObject(color, "Gradient Replace", s_node_gradient_replace, "Node_Gradient_Replace_Color", [1, Node_Gradient_Replace_Color]).setVersion(1135);
addNodeObject(color, "Gradient Data", s_node_gradient_data, "Node_Gradient_Extract", [1, Node_Gradient_Extract],, "Get palatte and array of key positions from gradient.").setVersion(1135);
@@ -541,11 +542,12 @@ function NodeObject(_name, _spr, _node, _create, tags = []) constructor {
addNodeObject(animation, "Evaluate Curve", s_node_curve_eval, "Node_Anim_Curve", [1, Node_Anim_Curve],, "Evaluate value from an animation curve.");
ds_list_add(animation, "Audio");
- addNodeObject(animation, "WAV File In", s_node_wav_file_read, "Node_WAV_File_Read", [0, Node_create_WAV_File_Read],, "Load wav audio file.").setVersion(1144);
- addNodeObject(animation, "FFT", s_node_FFT, "Node_FFT", [1, Node_FFT], ["frequency analysis"], "Perform fourier transform on number array.").setVersion(1144);
- addNodeObject(animation, "Bar / Graph", s_node_bar_graph, "Node_Plot_Linear", [1, Node_Plot_Linear], ["graph", "waveform", "bar chart", "plot"], "Plot graph or bar chart from array of number.").setVersion(1144);
- addNodeObject(animation, "Audio Window",s_node_audio_trim, "Node_Audio_Window", [1, Node_Audio_Window],, "Take a slice of an audio array based on the current frame.").setVersion(1144);
-
+ addNodeObject(animation, "WAV File In", s_node_wav_file_read, "Node_WAV_File_Read", [0, Node_create_WAV_File_Read],, "Load wav audio file.").setVersion(1144);
+ addNodeObject(animation, "WAV File Out", s_node_wav_file_write, "Node_WAV_File_Write", [1, Node_WAV_File_Write],, "Save wav audio file.").setVersion(1145);
+ addNodeObject(animation, "FFT", s_node_FFT, "Node_FFT", [1, Node_FFT], ["frequency analysis"], "Perform fourier transform on number array.").setVersion(1144);
+ addNodeObject(animation, "Bar / Graph", s_node_bar_graph, "Node_Plot_Linear", [1, Node_Plot_Linear], ["graph", "waveform", "bar chart", "plot"], "Plot graph or bar chart from array of number.").setVersion(1144);
+ addNodeObject(animation, "Audio Window", s_node_audio_trim, "Node_Audio_Window", [1, Node_Audio_Window],, "Take a slice of an audio array based on the current frame.").setVersion(1144);
+
var node = ds_list_create();
addNodeCatagory("Node", node);
ds_list_add(node, "Logic");
diff --git a/scripts/node_repeat/node_repeat.gml b/scripts/node_repeat/node_repeat.gml
index eb43192a0..5df9c753c 100644
--- a/scripts/node_repeat/node_repeat.gml
+++ b/scripts/node_repeat/node_repeat.gml
@@ -39,8 +39,7 @@ function Node_Repeat(_x, _y, _group = noone) : Node(_x, _y, _group) constructor
inputs[| 13] = nodeValue("Path shift", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, 0);
- inputs[| 14] = nodeValue("Color over copy", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 14] = nodeValue("Color over copy", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 15] = nodeValue("Alpha over copy", self, JUNCTION_CONNECT.input, VALUE_TYPE.curve, CURVE_DEF_11 );
diff --git a/scripts/node_scatter/node_scatter.gml b/scripts/node_scatter/node_scatter.gml
index 572d5b2f8..012a4ee03 100644
--- a/scripts/node_scatter/node_scatter.gml
+++ b/scripts/node_scatter/node_scatter.gml
@@ -30,8 +30,7 @@ function Node_Scatter(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) c
inputs[| 10] = nodeValue("Seed", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, irandom(9999999));
- inputs[| 11] = nodeValue("Random blend", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 11] = nodeValue("Random blend", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 12] = nodeValue("Alpha", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 1, 1 ])
.setDisplay(VALUE_DISPLAY.slider_range, [0, 1, 0.01]);
@@ -59,7 +58,8 @@ function Node_Scatter(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) c
outputs[| 0] = nodeValue("Surface out", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone);
- outputs[| 1] = nodeValue("Atlas data", self, JUNCTION_CONNECT.output, VALUE_TYPE.atlas, []);
+ outputs[| 1] = nodeValue("Atlas data", self, JUNCTION_CONNECT.output, VALUE_TYPE.atlas, [])
+ .rejectArrayProcess();
input_display_list = [
["Output", false], 0, 1, 15, 10,
@@ -103,7 +103,7 @@ function Node_Scatter(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) c
static process_data = function(_outSurf, _data, _output_index, _array_index) {
if(_output_index == 1) return scatter_data;
- scatter_data = [];
+ if(_output_index == 0 && _array_index == 0) scatter_data = [];
var _inSurf = _data[0];
if(_inSurf == 0)
diff --git a/scripts/node_strand_render/node_strand_render.gml b/scripts/node_strand_render/node_strand_render.gml
index 11d89f352..6565710d1 100644
--- a/scripts/node_strand_render/node_strand_render.gml
+++ b/scripts/node_strand_render/node_strand_render.gml
@@ -15,11 +15,9 @@ function Node_Strand_Render(_x, _y, _group = noone) : Node(_x, _y, _group) const
inputs[| 3] = nodeValue("Thickness over length", self, JUNCTION_CONNECT.input, VALUE_TYPE.curve, CURVE_DEF_11);
- inputs[| 4] = nodeValue("Random color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white))
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 4] = nodeValue("Random color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white));
- inputs[| 5] = nodeValue("Color over length", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white))
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 5] = nodeValue("Color over length", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white));
inputs[| 6] = nodeValue("Seed", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, irandom_range(10000, 99999));
diff --git a/scripts/node_strand_render_texture/node_strand_render_texture.gml b/scripts/node_strand_render_texture/node_strand_render_texture.gml
index 63c4dc34b..93277039a 100644
--- a/scripts/node_strand_render_texture/node_strand_render_texture.gml
+++ b/scripts/node_strand_render_texture/node_strand_render_texture.gml
@@ -13,8 +13,7 @@ function Node_Strand_Render_Texture(_x, _y, _group = noone) : Node(_x, _y, _grou
inputs[| 2] = nodeValue("Thickness", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 8, 8 ])
.setDisplay(VALUE_DISPLAY.vector_range);
- inputs[| 3] = nodeValue("Random color", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white))
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 3] = nodeValue("Random color", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white));
inputs[| 4] = nodeValue("Texture", self, JUNCTION_CONNECT.input, VALUE_TYPE.surface, 0);
diff --git a/scripts/node_stripe/node_stripe.gml b/scripts/node_stripe/node_stripe.gml
index 7751a6b55..61726947b 100644
--- a/scripts/node_stripe/node_stripe.gml
+++ b/scripts/node_stripe/node_stripe.gml
@@ -38,8 +38,7 @@ function Node_Stripe(_x, _y, _group = noone) : Node_Processor(_x, _y, _group) co
inputs[| 6] = nodeValue("Random color", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false);
- inputs[| 7] = nodeValue("Colors", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, new gradientObject(c_white) )
- .setDisplay(VALUE_DISPLAY.gradient);
+ inputs[| 7] = nodeValue("Colors", self, JUNCTION_CONNECT.input, VALUE_TYPE.gradient, new gradientObject(c_white) );
inputs[| 8] = nodeValue("Color 1", self, JUNCTION_CONNECT.input, VALUE_TYPE.color, c_white);
diff --git a/scripts/node_value/node_value.gml b/scripts/node_value/node_value.gml
index 531b4821c..afdb64192 100644
--- a/scripts/node_value/node_value.gml
+++ b/scripts/node_value/node_value.gml
@@ -30,6 +30,7 @@ enum VALUE_TYPE {
atlas = 20,
d3vertex = 21,
+ gradient = 22,
action = 99,
}
@@ -47,7 +48,6 @@ enum VALUE_DISPLAY {
slider_range,
//Color
- gradient,
palette,
//Int array
@@ -107,6 +107,7 @@ function value_color(i) {
$5dde8f, //trigger
$976bff, //atlas
#c1007c, //d3vertex
+ $5dde8f, //gradient
];
if(i == 99) return $5dde8f;
@@ -119,6 +120,7 @@ function value_bit(i) {
case VALUE_TYPE.float : return 1 << 2 | 1 << 1;
case VALUE_TYPE.boolean : return 1 << 3 | 1 << 1;
case VALUE_TYPE.color : return 1 << 4;
+ case VALUE_TYPE.gradient : return 1 << 25;
case VALUE_TYPE.surface : return 1 << 5;
case VALUE_TYPE.path : return 1 << 10;
case VALUE_TYPE.text : return 1 << 10;
@@ -146,17 +148,18 @@ function value_bit(i) {
}
function value_type_directional(f, t) {
- if(f == VALUE_TYPE.surface && t == VALUE_TYPE.integer) return true;
- if(f == VALUE_TYPE.surface && t == VALUE_TYPE.float) return true;
+ if(f == VALUE_TYPE.surface && t == VALUE_TYPE.integer) return true;
+ if(f == VALUE_TYPE.surface && t == VALUE_TYPE.float) return true;
if(f == VALUE_TYPE.integer && t == VALUE_TYPE.text) return true;
if(f == VALUE_TYPE.float && t == VALUE_TYPE.text) return true;
if(f == VALUE_TYPE.boolean && t == VALUE_TYPE.text) return true;
- if(f == VALUE_TYPE.integer && t == VALUE_TYPE.color) return true;
- if(f == VALUE_TYPE.float && t == VALUE_TYPE.color) return true;
- if(f == VALUE_TYPE.color && t == VALUE_TYPE.integer) return true;
- if(f == VALUE_TYPE.color && t == VALUE_TYPE.float ) return true;
+ if(f == VALUE_TYPE.integer && t == VALUE_TYPE.color) return true;
+ if(f == VALUE_TYPE.float && t == VALUE_TYPE.color) return true;
+ if(f == VALUE_TYPE.color && t == VALUE_TYPE.integer) return true;
+ if(f == VALUE_TYPE.color && t == VALUE_TYPE.float ) return true;
+ if(f == VALUE_TYPE.color && t == VALUE_TYPE.gradient) return true;
if(f == VALUE_TYPE.strands && t == VALUE_TYPE.pathnode ) return true;
@@ -195,7 +198,6 @@ function typeArrayDynamic(_type) {
switch(_type) {
case VALUE_DISPLAY.curve :
case VALUE_DISPLAY.palette :
- case VALUE_DISPLAY.gradient :
return true;
}
return false;
@@ -426,6 +428,8 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
express_edit.boxColor = COLORS._main_value_positive;
express_edit.align = fa_left;
+ process_array = true;
+
static setDefault = function(vals) {
if(LOADING || APPENDING) return self;
@@ -480,8 +484,13 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
return self;
}
+ static rejectArrayProcess = function() {
+ process_array = false;
+ return self;
+ }
+
static isAnimable = function() {
- if(display_type == VALUE_DISPLAY.gradient) return false;
+ if(type == VALUE_TYPE.gradient) return false;
if(display_type == VALUE_DISPLAY.text_array) return false;
return true;
}
@@ -494,10 +503,10 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
case VALUE_TYPE.color :
switch(display_type) {
case VALUE_DISPLAY.palette : drop_key = "Palette"; break;
- case VALUE_DISPLAY.gradient : drop_key = "Gradient"; break;
default : drop_key = "Color";
}
break;
+ case VALUE_TYPE.gradient : drop_key = "Gradient"; break;
case VALUE_TYPE.path : drop_key = "Asset"; break;
case VALUE_TYPE.text : drop_key = "Text"; break;
case VALUE_TYPE.pathnode : drop_key = "Path"; break;
@@ -740,14 +749,6 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
graph_h = ui(16);
extract_node = "Node_Color";
break;
- case VALUE_DISPLAY.gradient :
- editWidget = new buttonGradient(function(gradient) {
- MODIFIED = true;
- return setValueDirect(gradient);
- } );
-
- extract_node = "Node_Gradient_Out";
- break;
case VALUE_DISPLAY.palette :
editWidget = new buttonPalette(function(color) {
MODIFIED = true;
@@ -758,6 +759,14 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
break;
}
break;
+ case VALUE_TYPE.gradient :
+ editWidget = new buttonGradient(function(gradient) {
+ MODIFIED = true;
+ return setValueDirect(gradient);
+ } );
+
+ extract_node = "Node_Gradient_Out";
+ break;
case VALUE_TYPE.path :
switch(display_type) {
case VALUE_DISPLAY.path_array :
@@ -907,8 +916,8 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
var typeFrom = nodeFrom.type;
var display = nodeFrom.display_type;
- if(display_type == VALUE_DISPLAY.gradient && typeFrom == VALUE_TYPE.color) {
- if(display == VALUE_DISPLAY.gradient || (is_struct(value) && instanceof(value) == "gradientObject"))
+ if(type == VALUE_TYPE.gradient && typeFrom == VALUE_TYPE.color) {
+ if(is_struct(value) && instanceof(value) == "gradientObject")
return value;
if(is_array(value)) {
var amo = array_length(value);
@@ -1197,7 +1206,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
updated = animator.setValue(val, connect_type == JUNCTION_CONNECT.input && record, time);
}
- if(display_type == VALUE_DISPLAY.gradient) updated = true;
+ if(type == VALUE_TYPE.gradient) updated = true;
if(display_type == VALUE_DISPLAY.palette) updated = true;
if(updated) {
@@ -1478,7 +1487,7 @@ function NodeValue(_name, _node, _connect, _type, _value, _tooltip = "") constru
if(type == VALUE_TYPE.action) {
var tx = x;
draw_set_text(f_p1, fa_center, fa_center, _draw_cc);
- draw_text(tx, y - (line_height() + 16) / 2, name);
+ draw_text(tx, y - (line_get_height() + 16) / 2, name);
} else if(connect_type == JUNCTION_CONNECT.input) {
var tx = x - 12 * _s;
draw_set_halign(fa_right);
diff --git a/scripts/node_wav_file_write/node_wav_file_write.gml b/scripts/node_wav_file_write/node_wav_file_write.gml
new file mode 100644
index 000000000..873885b5f
--- /dev/null
+++ b/scripts/node_wav_file_write/node_wav_file_write.gml
@@ -0,0 +1,119 @@
+function Node_WAV_File_Write(_x, _y, _group = noone) : Node(_x, _y, _group) constructor {
+ name = "WAV File Out";
+ color = COLORS.node_blend_input;
+ previewable = false;
+
+ h = 72;
+ min_h = h;
+
+ inputs[| 0] = nodeValue("Path", self, JUNCTION_CONNECT.input, VALUE_TYPE.path, "")
+ .setDisplay(VALUE_DISPLAY.path_save, ["*.wav", ""])
+ .rejectArray()
+ .setVisible(true);
+
+ inputs[| 1] = nodeValue("Audio Data", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [[]])
+ .setArrayDepth(1)
+ .setVisible(true, true);
+
+ inputs[| 2] = nodeValue("Sample", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 44100);
+
+ inputs[| 3] = nodeValue("Bit Depth", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0)
+ .setDisplay(VALUE_DISPLAY.enum_scroll, [ "8 bit positive", "16 bit integer" ]);
+
+ inputs[| 4] = nodeValue("Remap Data", self, JUNCTION_CONNECT.input, VALUE_TYPE.boolean, false)
+
+ inputs[| 5] = nodeValue("Data Range", self, JUNCTION_CONNECT.input, VALUE_TYPE.float, [ 0, 1 ])
+ .setDisplay(VALUE_DISPLAY.vector);
+
+ input_display_list = [
+ [ "Data", false], 1, 0, 4, 5,
+ [ "Format", false], 2, 3,
+ ]
+
+ insp1UpdateTooltip = "Export";
+ insp1UpdateIcon = [ THEME.sequence_control, 1, COLORS._main_value_positive ];
+
+ static onInspector1Update = function() {
+ export();
+ }
+
+ static step = function() {
+ var remap = inputs[| 4].getValue();
+
+ inputs[| 5].setVisible(remap);
+ }
+
+ static export = function() {
+ var path = inputs[| 0].getValue();
+ var data = inputs[| 1].getValue();
+ var samp = inputs[| 2].getValue();
+ var bitd = inputs[| 3].getValue() + 1;
+
+ var remp = inputs[| 4].getValue();
+ var rern = inputs[| 5].getValue();
+
+ if(!is_array(data)) return;
+
+ var _chn = array_length(data);
+ var _siz = 0;
+
+ for( var i = 0; i < _chn; i++ ) {
+ if(!is_array(data[i])) {
+ noti_warning("Audio Export: Malformed data. Expects 2D array [channel x number array].");
+ return;
+ }
+ var len = array_length(data[i]);
+
+ if(_siz && _siz != len) {
+ noti_warning("Audio Export: Uneven sample per channel.");
+ return;
+ }
+
+ _siz = len;
+ }
+
+ if(filename_ext(path) != ".wav") path += ".wav";
+
+ var buff = buffer_create(1, buffer_grow, 1);
+ var _pkg = _chn * _siz * bitd + 12 + 24 + 8;
+
+ buffer_write(buff, buffer_text, "RIFF");
+ buffer_write(buff, buffer_u32, _pkg); //package size
+ buffer_write(buff, buffer_text, "WAVE");
+
+ buffer_write(buff, buffer_text, "fmt ");
+ buffer_write(buff, buffer_u32, 16); //chunk size
+ buffer_write(buff, buffer_u16, 1); //format
+ buffer_write(buff, buffer_u16, _chn); //channels
+ buffer_write(buff, buffer_u32, samp); //sampling rate
+ buffer_write(buff, buffer_u32, samp * bitd); //data rate
+ buffer_write(buff, buffer_u16, bitd); //bitrate (byte)
+ buffer_write(buff, buffer_u16, bitd * 8); //bit per sample
+
+ buffer_write(buff, buffer_text, "data"); //bit per sample
+ buffer_write(buff, buffer_u32, _siz); //data length
+
+ var typ = bitd == 1? buffer_u8 : buffer_s16;
+ var rerng = rern[1] - rern[0];
+
+ for( var i = 0; i < _siz; i++ )
+ for( var j = 0; j < _chn; j++ ) {
+ var _dat = data[j][i];
+ if(remp) {
+ if(bitd == 1) _dat = (_dat - rern[0]) / rerng * 255;
+ else if(bitd == 2) _dat = (_dat - rern[0]) / rerng * 65535 - 32768;
+ _dat = round(_dat);
+ }
+
+ buffer_write(buff, typ, _dat);
+ }
+
+ buffer_save(buff, path);
+ buffer_delete(buff);
+ }
+
+ static onDrawNode = function(xx, yy, _mx, _my, _s, _hover, _focus) {
+ var bbox = drawGetBbox(xx, yy, _s);
+ draw_sprite_fit(s_node_wav_file_write, 0, bbox.xc, bbox.yc, bbox.w, bbox.h);
+ }
+}
\ No newline at end of file
diff --git a/scripts/node_wav_file_write/node_wav_file_write.yy b/scripts/node_wav_file_write/node_wav_file_write.yy
new file mode 100644
index 000000000..e97e57afd
--- /dev/null
+++ b/scripts/node_wav_file_write/node_wav_file_write.yy
@@ -0,0 +1,11 @@
+{
+ "resourceType": "GMScript",
+ "resourceVersion": "1.0",
+ "name": "node_wav_file_write",
+ "isCompatibility": false,
+ "isDnD": false,
+ "parent": {
+ "name": "audio",
+ "path": "folders/nodes/data/audio.yy",
+ },
+}
\ No newline at end of file
diff --git a/scripts/panel_collection/panel_collection.gml b/scripts/panel_collection/panel_collection.gml
index 4ca9ff218..833c5b4bd 100644
--- a/scripts/panel_collection/panel_collection.gml
+++ b/scripts/panel_collection/panel_collection.gml
@@ -168,6 +168,7 @@ function Panel_Collection() : PanelContent() constructor {
var _node = index < node_list? nodes[| index] : steamNode[index - node_list];
var _nx = grid_space + (grid_width + grid_space) * j;
var _boxx = _nx + (grid_width - grid_size) / 2;
+ _boxx = round(_boxx);
var gr_x1 = _boxx + grid_size;
var gr_y1 = yy + grid_size;
@@ -373,7 +374,7 @@ function Panel_Collection() : PanelContent() constructor {
var _x = ui(16);
var _y = ui(24);
- var bh = line_height(f_p0b, 8);
+ var bh = line_get_height(f_p0b, 8);
var rootx = 0;
for( var i = 0; i < array_length(roots); i++ ) {
diff --git a/scripts/panel_graph_export_image/panel_graph_export_image.gml b/scripts/panel_graph_export_image/panel_graph_export_image.gml
new file mode 100644
index 000000000..ea08bd78d
--- /dev/null
+++ b/scripts/panel_graph_export_image/panel_graph_export_image.gml
@@ -0,0 +1,3 @@
+function Script659(){
+
+}
\ No newline at end of file
diff --git a/scripts/panel_graph_export_image/panel_graph_export_image.yy b/scripts/panel_graph_export_image/panel_graph_export_image.yy
new file mode 100644
index 000000000..477ac405c
--- /dev/null
+++ b/scripts/panel_graph_export_image/panel_graph_export_image.yy
@@ -0,0 +1,11 @@
+{
+ "resourceType": "GMScript",
+ "resourceVersion": "1.0",
+ "name": "panel_graph_export_image",
+ "isCompatibility": false,
+ "isDnD": false,
+ "parent": {
+ "name": "graph",
+ "path": "folders/panels/graph.yy",
+ },
+}
\ No newline at end of file
diff --git a/scripts/panel_history/panel_history.gml b/scripts/panel_history/panel_history.gml
index 37e1e1f2e..8d21ea95d 100644
--- a/scripts/panel_history/panel_history.gml
+++ b/scripts/panel_history/panel_history.gml
@@ -51,7 +51,7 @@ function Panel_History() : PanelContent() constructor {
draw_set_text(f_p1, fa_left, fa_center, COLORS._main_text);
- var lh = line_height() + ui(8);
+ var lh = line_get_height() + ui(8);
var _h = 0, hh;
var yy = _y + ui(8);
diff --git a/scripts/panel_inspector/panel_inspector.gml b/scripts/panel_inspector/panel_inspector.gml
index 31dc93b89..50bae7fc2 100644
--- a/scripts/panel_inspector/panel_inspector.gml
+++ b/scripts/panel_inspector/panel_inspector.gml
@@ -156,8 +156,8 @@ function Panel_Inspector() : PanelContent() constructor {
draw_set_text(f_p0, fa_left, fa_top, COLORS._main_text);
draw_text_add(ui(16), yy, display[0]);
- yy += line_height() + ui(6);
- hh += line_height() + ui(6);
+ yy += line_get_height() + ui(6);
+ hh += line_get_height() + ui(6);
meta_tb[j].setActiveFocus(pFOCUS, _hover);
if(pFOCUS) meta_tb[j].register(contentPane);
@@ -416,8 +416,8 @@ function Panel_Inspector() : PanelContent() constructor {
if(pos == 0) continue;
}
- var lb_h = line_height(f_p0) + ui(8);
- var lb_w = line_width(jun.name, f_p0) + ui(16);
+ var lb_h = line_get_height(f_p0) + ui(8);
+ var lb_w = line_get_width(jun.name, f_p0) + ui(16);
var padd = ui(8);
var _selY = yy - ui(0);
diff --git a/scripts/panel_menu/panel_menu.gml b/scripts/panel_menu/panel_menu.gml
index 303774055..22afbfff1 100644
--- a/scripts/panel_menu/panel_menu.gml
+++ b/scripts/panel_menu/panel_menu.gml
@@ -348,7 +348,7 @@ function Panel_Menu() : PanelContent() constructor {
for(var i = 0; i < array_length(menus); i++) {
draw_set_text(f_p1, fa_center, fa_center, COLORS._main_text);
var ww = string_width(menus[i][0]) + ui(16);
- var hh = line_height() + ui(8);
+ var hh = line_get_height() + ui(8);
if(hori) {
xc = xx + ww / 2;
diff --git a/scripts/panel_notification/panel_notification.gml b/scripts/panel_notification/panel_notification.gml
index 77be1de54..e382f8efb 100644
--- a/scripts/panel_notification/panel_notification.gml
+++ b/scripts/panel_notification/panel_notification.gml
@@ -79,7 +79,7 @@ function Panel_Notification() : PanelContent() constructor {
menuItem(get_text("noti_delete_message", "Delete notification"), function() {
ds_list_remove(STATUSES, o_dialog_menubox.noti);
}),
- ],, o_dialog_menubox.noti);
+ ],, noti);
dia.noti = noti;
}
}
diff --git a/scripts/textArea/textArea.gml b/scripts/textArea/textArea.gml
index e221a9bea..aebbc5454 100644
--- a/scripts/textArea/textArea.gml
+++ b/scripts/textArea/textArea.gml
@@ -86,10 +86,11 @@ function textArea(_input, _onModify, _extras = noone) : textInput(_input, _onMod
var currW = 0;
var currL = "";
var cut = true;
+ var len = array_length(words);
- for( var j = 0; j < array_length(words); j++ ) {
+ for( var j = 0; j < len; j++ ) {
var word = words[j];
- if(j) word = " " + word;
+ if(j < len - 1) word = word + " ";
if(string_width(word) > line_width) { //the entire word is longer than a line
for( var k = 1; k <= string_length(word); k++ ) {
@@ -302,7 +303,7 @@ function textArea(_input, _onModify, _extras = noone) : textInput(_input, _onMod
else if(format == TEXT_AREA_FORMAT.delimiter)
draw_text_delimiter(ch_x, ch_y, _str);
- ch_y += line_height();
+ ch_y += line_get_height();
}
draw_set_alpha(1);
@@ -318,7 +319,7 @@ function textArea(_input, _onModify, _extras = noone) : textInput(_input, _onMod
for( var i = 0; i < array_length(_input_text_line); i++ ) {
_str = string_trim_end(_input_text_line[i]);
_l = string_length(_str);
- _ch_h = line_height();
+ _ch_h = line_get_height();
ch_cxo = sx;
ch_x = sx;
@@ -387,7 +388,7 @@ function textArea(_input, _onModify, _extras = noone) : textInput(_input, _onMod
}
draw_set_font(font);
- var c_h = line_height();
+ var c_h = line_get_height();
var line_count = max(min_lines, array_length(_input_text_line));
hh = max(_h, ui(14) + c_h * line_count);
@@ -565,7 +566,7 @@ function textArea(_input, _onModify, _extras = noone) : textInput(_input, _onMod
cursor_line = i;
}
char_run += _l;
- ch_y += line_height();
+ ch_y += line_get_height();
}
cursor_pos_x = cursor_pos_x == 0? cursor_pos_x_to : lerp_float(cursor_pos_x, cursor_pos_x_to, 4);
diff --git a/scripts/textArrayBox/textArrayBox.gml b/scripts/textArrayBox/textArrayBox.gml
index b0b5053ee..57f8474d2 100644
--- a/scripts/textArrayBox/textArrayBox.gml
+++ b/scripts/textArrayBox/textArrayBox.gml
@@ -17,7 +17,7 @@ function textArrayBox(arraySet, data, onModify = noone) : widget() constructor {
var tx = _x + ui(4);
var ty = _y + ui(4);
- var hh = line_height(f_p0, ui(4));
+ var hh = line_get_height(f_p0, ui(4));
var th = hh + ui(8);
draw_set_text(f_p0, fa_left, fa_center, COLORS._main_text);
@@ -52,7 +52,7 @@ function textArrayBox(arraySet, data, onModify = noone) : widget() constructor {
var tx = _x + ui(4);
var ty = _y + ui(4);
- var hh = line_height(f_p0, ui(4));
+ var hh = line_get_height(f_p0, ui(4));
draw_set_text(f_p0, fa_left, fa_center, COLORS._main_text);
for( var i = 0; i < array_length(arraySet); i++ ) {
diff --git a/scripts/wav_file_read/wav_file_read.gml b/scripts/wav_file_read/wav_file_read.gml
index dfa1739ef..31de86368 100644
--- a/scripts/wav_file_read/wav_file_read.gml
+++ b/scripts/wav_file_read/wav_file_read.gml
@@ -39,10 +39,16 @@ function file_read_wav(path) {
printIf(global.FLAG.wav_import, "-- FORMAT --")
var b = file_read_ASCII(wav_file_reader, 4); printIf(global.FLAG.wav_import, b);
var l = file_read_bytes(wav_file_reader, 4); printIf(global.FLAG.wav_import, $"Length: {l}");
+
+ if(l != 16) {
+ noti_waning("File format not supported, the audio file need to be 8, 16 bit PCM wav with no extension.");
+ return;
+ }
+
var l = file_read_bytes(wav_file_reader, 2); printIf(global.FLAG.wav_import, $"0x01: {l}");
var ch = file_read_bytes(wav_file_reader, 2); printIf(global.FLAG.wav_import, $"Channels: {ch}");
var sm = file_read_bytes(wav_file_reader, 4); printIf(global.FLAG.wav_import, $"Sample: {sm}");
- var l = file_read_bytes(wav_file_reader, 4); printIf(global.FLAG.wav_import, $"BPS: {l}");
+ var l = file_read_bytes(wav_file_reader, 4); printIf(global.FLAG.wav_import, $"BPS: {l}");
var br = file_read_bytes(wav_file_reader, 2); printIf(global.FLAG.wav_import, $"Bitrate: {br}");
var l = file_read_bytes(wav_file_reader, 2); printIf(global.FLAG.wav_import, $"Bit/Sam: {l}");
diff --git a/sprites/s_node_ase_file/s_node_ase_file.yy b/sprites/s_node_ase_file/s_node_ase_file.yy
index f2003868b..551b1838b 100644
--- a/sprites/s_node_ase_file/s_node_ase_file.yy
+++ b/sprites/s_node_ase_file/s_node_ase_file.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_ase_layer/s_node_ase_layer.yy b/sprites/s_node_ase_layer/s_node_ase_layer.yy
index 411e536f3..daba920c5 100644
--- a/sprites/s_node_ase_layer/s_node_ase_layer.yy
+++ b/sprites/s_node_ase_layer/s_node_ase_layer.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_canvas/s_node_canvas.yy b/sprites/s_node_canvas/s_node_canvas.yy
index b3ed23688..4ceb38b4e 100644
--- a/sprites/s_node_canvas/s_node_canvas.yy
+++ b/sprites/s_node_canvas/s_node_canvas.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_csv_file_read/s_node_csv_file_read.yy b/sprites/s_node_csv_file_read/s_node_csv_file_read.yy
index 0f46a7782..637135f9f 100644
--- a/sprites/s_node_csv_file_read/s_node_csv_file_read.yy
+++ b/sprites/s_node_csv_file_read/s_node_csv_file_read.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_csv_file_write/s_node_csv_file_write.yy b/sprites/s_node_csv_file_write/s_node_csv_file_write.yy
index acb99e25f..1abf947c9 100644
--- a/sprites/s_node_csv_file_write/s_node_csv_file_write.yy
+++ b/sprites/s_node_csv_file_write/s_node_csv_file_write.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_image/s_node_image.yy b/sprites/s_node_image/s_node_image.yy
index f1a370a90..7ae13d079 100644
--- a/sprites/s_node_image/s_node_image.yy
+++ b/sprites/s_node_image/s_node_image.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_image_animation/s_node_image_animation.yy b/sprites/s_node_image_animation/s_node_image_animation.yy
index 9d2836ec3..f71381cb0 100644
--- a/sprites/s_node_image_animation/s_node_image_animation.yy
+++ b/sprites/s_node_image_animation/s_node_image_animation.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_image_copy/s_node_image_copy.yy b/sprites/s_node_image_copy/s_node_image_copy.yy
index 12aa4ef57..912db281e 100644
--- a/sprites/s_node_image_copy/s_node_image_copy.yy
+++ b/sprites/s_node_image_copy/s_node_image_copy.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_image_gif/s_node_image_gif.yy b/sprites/s_node_image_gif/s_node_image_gif.yy
index 66657e6c8..41a834b9d 100644
--- a/sprites/s_node_image_gif/s_node_image_gif.yy
+++ b/sprites/s_node_image_gif/s_node_image_gif.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_image_sequence/s_node_image_sequence.yy b/sprites/s_node_image_sequence/s_node_image_sequence.yy
index f41350eb4..4c718903d 100644
--- a/sprites/s_node_image_sequence/s_node_image_sequence.yy
+++ b/sprites/s_node_image_sequence/s_node_image_sequence.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_image_sequence_to_anim/s_node_image_sequence_to_anim.yy b/sprites/s_node_image_sequence_to_anim/s_node_image_sequence_to_anim.yy
index c339e1078..9013ccbc3 100644
--- a/sprites/s_node_image_sequence_to_anim/s_node_image_sequence_to_anim.yy
+++ b/sprites/s_node_image_sequence_to_anim/s_node_image_sequence_to_anim.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_image_sheet/s_node_image_sheet.yy b/sprites/s_node_image_sheet/s_node_image_sheet.yy
index 203c6f2fe..9b0cdd394 100644
--- a/sprites/s_node_image_sheet/s_node_image_sheet.yy
+++ b/sprites/s_node_image_sheet/s_node_image_sheet.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_json_file_read/s_node_json_file_read.yy b/sprites/s_node_json_file_read/s_node_json_file_read.yy
index 567f6c592..62311b025 100644
--- a/sprites/s_node_json_file_read/s_node_json_file_read.yy
+++ b/sprites/s_node_json_file_read/s_node_json_file_read.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_json_file_write/s_node_json_file_write.yy b/sprites/s_node_json_file_write/s_node_json_file_write.yy
index b0bbab7ee..345dbc240 100644
--- a/sprites/s_node_json_file_write/s_node_json_file_write.yy
+++ b/sprites/s_node_json_file_write/s_node_json_file_write.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_text_file_read/s_node_text_file_read.yy b/sprites/s_node_text_file_read/s_node_text_file_read.yy
index 375966e4b..058d14c88 100644
--- a/sprites/s_node_text_file_read/s_node_text_file_read.yy
+++ b/sprites/s_node_text_file_read/s_node_text_file_read.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_text_file_write/s_node_text_file_write.yy b/sprites/s_node_text_file_write/s_node_text_file_write.yy
index 99421b26c..73559ab1c 100644
--- a/sprites/s_node_text_file_write/s_node_text_file_write.yy
+++ b/sprites/s_node_text_file_write/s_node_text_file_write.yy
@@ -25,8 +25,8 @@
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
diff --git a/sprites/s_node_wav_file_read/2c9b305d-d36e-476b-a921-16c2f79031b8.png b/sprites/s_node_wav_file_read/2c9b305d-d36e-476b-a921-16c2f79031b8.png
new file mode 100644
index 000000000..cd69bb658
Binary files /dev/null and b/sprites/s_node_wav_file_read/2c9b305d-d36e-476b-a921-16c2f79031b8.png differ
diff --git a/sprites/s_node_wav_file_read/ed7117f7-7897-476a-acf0-d7b1d10f562f.png b/sprites/s_node_wav_file_read/ed7117f7-7897-476a-acf0-d7b1d10f562f.png
deleted file mode 100644
index 04d6ec641..000000000
Binary files a/sprites/s_node_wav_file_read/ed7117f7-7897-476a-acf0-d7b1d10f562f.png and /dev/null differ
diff --git a/sprites/s_node_wav_file_read/layers/2c9b305d-d36e-476b-a921-16c2f79031b8/fa7c8971-1920-4811-911b-85dc4a0031ba.png b/sprites/s_node_wav_file_read/layers/2c9b305d-d36e-476b-a921-16c2f79031b8/fa7c8971-1920-4811-911b-85dc4a0031ba.png
new file mode 100644
index 000000000..cd69bb658
Binary files /dev/null and b/sprites/s_node_wav_file_read/layers/2c9b305d-d36e-476b-a921-16c2f79031b8/fa7c8971-1920-4811-911b-85dc4a0031ba.png differ
diff --git a/sprites/s_node_wav_file_read/layers/ed7117f7-7897-476a-acf0-d7b1d10f562f/d0ffea8e-e51f-4415-b383-c42802705804.png b/sprites/s_node_wav_file_read/layers/ed7117f7-7897-476a-acf0-d7b1d10f562f/d0ffea8e-e51f-4415-b383-c42802705804.png
deleted file mode 100644
index 04d6ec641..000000000
Binary files a/sprites/s_node_wav_file_read/layers/ed7117f7-7897-476a-acf0-d7b1d10f562f/d0ffea8e-e51f-4415-b383-c42802705804.png and /dev/null differ
diff --git a/sprites/s_node_wav_file_read/s_node_wav_file_read.yy b/sprites/s_node_wav_file_read/s_node_wav_file_read.yy
index 071adabb9..22886eba0 100644
--- a/sprites/s_node_wav_file_read/s_node_wav_file_read.yy
+++ b/sprites/s_node_wav_file_read/s_node_wav_file_read.yy
@@ -2,10 +2,10 @@
"resourceType": "GMSprite",
"resourceVersion": "1.0",
"name": "s_node_wav_file_read",
- "bbox_bottom": 62,
+ "bbox_bottom": 57,
"bbox_left": 0,
"bbox_right": 62,
- "bbox_top": 0,
+ "bbox_top": 6,
"bboxMode": 0,
"collisionKind": 1,
"collisionTolerance": 0,
@@ -13,20 +13,20 @@
"edgeFiltering": false,
"For3D": false,
"frames": [
- {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"ed7117f7-7897-476a-acf0-d7b1d10f562f",},
+ {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"2c9b305d-d36e-476b-a921-16c2f79031b8",},
],
"gridX": 0,
"gridY": 0,
"height": 64,
"HTile": false,
"layers": [
- {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"d0ffea8e-e51f-4415-b383-c42802705804","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,},
+ {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"fa7c8971-1920-4811-911b-85dc4a0031ba","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,},
],
"nineSlice": null,
"origin": 4,
"parent": {
- "name": "input",
- "path": "folders/nodes/icons/input.yy",
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
},
"preMultiplyAlpha": false,
"sequence": {
@@ -54,7 +54,7 @@
"timeUnits": 1,
"tracks": [
{"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[
- {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"ed7117f7-7897-476a-acf0-d7b1d10f562f","path":"sprites/s_node_wav_file_read/s_node_wav_file_read.yy",},},},"Disabled":false,"id":"97d549db-4e6f-4505-b6dd-aacd000eaa4c","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,},
+ {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"2c9b305d-d36e-476b-a921-16c2f79031b8","path":"sprites/s_node_wav_file_read/s_node_wav_file_read.yy",},},},"Disabled":false,"id":"f1114075-0273-45bf-9df3-d4c534342938","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,},
],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,},
],
"visibleRange": null,
diff --git a/sprites/s_node_wav_file_write/f600e086-97a1-4550-abca-174b3b243d48.png b/sprites/s_node_wav_file_write/f600e086-97a1-4550-abca-174b3b243d48.png
new file mode 100644
index 000000000..1708c88ef
Binary files /dev/null and b/sprites/s_node_wav_file_write/f600e086-97a1-4550-abca-174b3b243d48.png differ
diff --git a/sprites/s_node_wav_file_write/layers/f600e086-97a1-4550-abca-174b3b243d48/9875a4ef-d711-48b8-b516-a9d19283bd1a.png b/sprites/s_node_wav_file_write/layers/f600e086-97a1-4550-abca-174b3b243d48/9875a4ef-d711-48b8-b516-a9d19283bd1a.png
new file mode 100644
index 000000000..1708c88ef
Binary files /dev/null and b/sprites/s_node_wav_file_write/layers/f600e086-97a1-4550-abca-174b3b243d48/9875a4ef-d711-48b8-b516-a9d19283bd1a.png differ
diff --git a/sprites/s_node_wav_file_write/s_node_wav_file_write.yy b/sprites/s_node_wav_file_write/s_node_wav_file_write.yy
new file mode 100644
index 000000000..643c12b0a
--- /dev/null
+++ b/sprites/s_node_wav_file_write/s_node_wav_file_write.yy
@@ -0,0 +1,74 @@
+{
+ "resourceType": "GMSprite",
+ "resourceVersion": "1.0",
+ "name": "s_node_wav_file_write",
+ "bbox_bottom": 63,
+ "bbox_left": 0,
+ "bbox_right": 63,
+ "bbox_top": 0,
+ "bboxMode": 0,
+ "collisionKind": 1,
+ "collisionTolerance": 0,
+ "DynamicTexturePage": false,
+ "edgeFiltering": false,
+ "For3D": false,
+ "frames": [
+ {"resourceType":"GMSpriteFrame","resourceVersion":"1.1","name":"f600e086-97a1-4550-abca-174b3b243d48",},
+ ],
+ "gridX": 0,
+ "gridY": 0,
+ "height": 64,
+ "HTile": false,
+ "layers": [
+ {"resourceType":"GMImageLayer","resourceVersion":"1.0","name":"9875a4ef-d711-48b8-b516-a9d19283bd1a","blendMode":0,"displayName":"default","isLocked":false,"opacity":100.0,"visible":true,},
+ ],
+ "nineSlice": null,
+ "origin": 4,
+ "parent": {
+ "name": "IO",
+ "path": "folders/nodes/icons/IO.yy",
+ },
+ "preMultiplyAlpha": false,
+ "sequence": {
+ "resourceType": "GMSequence",
+ "resourceVersion": "1.4",
+ "name": "s_node_wav_file_write",
+ "autoRecord": true,
+ "backdropHeight": 768,
+ "backdropImageOpacity": 0.5,
+ "backdropImagePath": "",
+ "backdropWidth": 1366,
+ "backdropXOffset": 0.0,
+ "backdropYOffset": 0.0,
+ "events": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],},
+ "eventStubScript": null,
+ "eventToFunction": {},
+ "length": 1.0,
+ "lockOrigin": false,
+ "moments": {"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[],},
+ "playback": 1,
+ "playbackSpeed": 30.0,
+ "playbackSpeedType": 0,
+ "showBackdrop": true,
+ "showBackdropImage": false,
+ "timeUnits": 1,
+ "tracks": [
+ {"resourceType":"GMSpriteFramesTrack","resourceVersion":"1.0","name":"frames","builtinName":0,"events":[],"inheritsTrackColour":true,"interpolation":1,"isCreationTrack":false,"keyframes":{"resourceType":"KeyframeStore","resourceVersion":"1.0","Keyframes":[
+ {"resourceType":"Keyframe","resourceVersion":"1.0","Channels":{"0":{"resourceType":"SpriteFrameKeyframe","resourceVersion":"1.0","Id":{"name":"f600e086-97a1-4550-abca-174b3b243d48","path":"sprites/s_node_wav_file_write/s_node_wav_file_write.yy",},},},"Disabled":false,"id":"0a3f90fa-129f-4c03-a0d8-cac031cdfc05","IsCreationKey":false,"Key":0.0,"Length":1.0,"Stretch":false,},
+ ],},"modifiers":[],"spriteId":null,"trackColour":0,"tracks":[],"traits":0,},
+ ],
+ "visibleRange": null,
+ "volume": 1.0,
+ "xorigin": 32,
+ "yorigin": 32,
+ },
+ "swatchColours": null,
+ "swfPrecision": 2.525,
+ "textureGroupId": {
+ "name": "Default",
+ "path": "texturegroups/Default",
+ },
+ "type": 0,
+ "VTile": false,
+ "width": 64,
+}
\ No newline at end of file