diff options
307 files changed, 5182 insertions, 4057 deletions
diff --git a/.github/actions/godot-build/action.yml b/.github/actions/godot-build/action.yml index 0a0899db78..bf29b7e430 100644 --- a/.github/actions/godot-build/action.yml +++ b/.github/actions/godot-build/action.yml @@ -13,10 +13,10 @@ inputs: sconsflags: default: "" scons-cache: - description: The scons cache path. + description: The SCons cache path. default: "${{ github.workspace }}/.scons-cache/" scons-cache-limit: - description: The scons cache size limit. + description: The SCons cache size limit. # actions/cache has 10 GiB limit, and GitHub runners have a 14 GiB disk. # Limit to 7 GiB to avoid having the extracted cache fill the disk. default: 7168 diff --git a/.github/actions/godot-cache/action.yml b/.github/actions/godot-cache/action.yml index b7ca01bb47..13142e7ed1 100644 --- a/.github/actions/godot-cache/action.yml +++ b/.github/actions/godot-cache/action.yml @@ -5,13 +5,13 @@ inputs: description: The cache base name (job name by default). default: "${{github.job}}" scons-cache: - description: The scons cache path. + description: The SCons cache path. default: "${{github.workspace}}/.scons-cache/" runs: using: "composite" steps: - # Upload cache on completion and check it out now - - name: Load .scons_cache directory + # Upload cache on completion and check it out now. + - name: Load SCons cache directory uses: actions/cache@v4 with: path: ${{inputs.scons-cache}} diff --git a/.github/actions/godot-deps/action.yml b/.github/actions/godot-deps/action.yml index cac72c436e..07a364cd79 100644 --- a/.github/actions/godot-deps/action.yml +++ b/.github/actions/godot-deps/action.yml @@ -1,27 +1,30 @@ -name: Setup python and scons -description: Setup python, install the pip version of scons. +name: Setup Python and SCons +description: Setup Python, install the pip version of SCons. inputs: python-version: - description: The python version to use. + description: The Python version to use. default: "3.x" python-arch: - description: The python architecture. + description: The Python architecture. default: "x64" + scons-version: + description: The SCons version to use. + default: "4.7.0" runs: using: "composite" steps: - # Use python 3.x release (works cross platform) - name: Set up Python 3.x uses: actions/setup-python@v5 with: - # Semantic version range syntax or exact version of a Python version + # Semantic version range syntax or exact version of a Python version. python-version: ${{ inputs.python-version }} - # Optional - x64 or x86 architecture, defaults to x64 + # Optional - x64 or x86 architecture, defaults to x64. architecture: ${{ inputs.python-arch }} - - name: Setup scons + - name: Setup SCons shell: bash run: | python -c "import sys; print(sys.version)" - python -m pip install scons==4.7.0 + python -m pip install wheel + python -m pip install scons==${{ inputs.scons-version }} scons --version diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml index 5cb66a40ab..9a488bd095 100644 --- a/.github/workflows/android_builds.yml +++ b/.github/workflows/android_builds.yml @@ -32,7 +32,7 @@ jobs: uses: ./.github/actions/godot-cache continue-on-error: true - - name: Setup python and scons + - name: Setup Python and SCons uses: ./.github/actions/godot-deps - name: Compilation (arm32) diff --git a/.github/workflows/godot_cpp_test.yml b/.github/workflows/godot_cpp_test.yml index 7350920810..57114dacfc 100644 --- a/.github/workflows/godot_cpp_test.yml +++ b/.github/workflows/godot_cpp_test.yml @@ -22,7 +22,7 @@ jobs: with: submodules: recursive - - name: Setup python and scons + - name: Setup Python and SCons uses: ./.github/actions/godot-deps # Checkout godot-cpp @@ -47,7 +47,7 @@ jobs: cp -f godot-api/gdextension_interface.h godot-cpp/gdextension/ cp -f godot-api/extension_api.json godot-cpp/gdextension/ - # TODO: Add caching to the scons build and store it for CI via the godot-cache + # TODO: Add caching to the SCons build and store it for CI via the godot-cache # action. # Build godot-cpp test extension diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml index e205d551ed..0546f43acc 100644 --- a/.github/workflows/ios_builds.yml +++ b/.github/workflows/ios_builds.yml @@ -26,7 +26,7 @@ jobs: uses: ./.github/actions/godot-cache continue-on-error: true - - name: Setup python and scons + - name: Setup Python and SCons uses: ./.github/actions/godot-deps - name: Compilation (arm64) diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index 0420a02b1d..6b98256110 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -58,6 +58,8 @@ jobs: tests: true # Skip 2GiB artifact speeding up action. artifact: false + # Test our oldest supported SCons/Python versions on one arbitrary editor build. + legacy-scons: true - name: Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld) cache-name: linux-editor-thread-sanitizer @@ -115,9 +117,18 @@ jobs: cache-name: ${{ matrix.cache-name }} continue-on-error: true - - name: Setup python and scons + - name: Setup Python and SCons + if: ${{ ! matrix.legacy-scons }} uses: ./.github/actions/godot-deps + - name: Setup Python and SCons (legacy versions) + if: ${{ matrix.legacy-scons }} + uses: ./.github/actions/godot-deps + with: + # Sync with Ensure*Version in SConstruct. + python-version: 3.6 + scons-version: 3.1.2 + - name: Setup GCC problem matcher uses: ammaraskar/gcc-problem-matcher@master diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 70031ec4c3..badcb688d1 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -43,7 +43,7 @@ jobs: cache-name: ${{ matrix.cache-name }} continue-on-error: true - - name: Setup python and scons + - name: Setup Python and SCons uses: ./.github/actions/godot-deps - name: Setup Vulkan SDK diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml index cfa1571d1f..1eb7b901cd 100644 --- a/.github/workflows/web_builds.yml +++ b/.github/workflows/web_builds.yml @@ -58,7 +58,7 @@ jobs: cache-name: ${{ matrix.cache-name }} continue-on-error: true - - name: Setup python and scons + - name: Setup Python and SCons uses: ./.github/actions/godot-deps - name: Compilation diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index 5443ba20ab..f9513af5e3 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -48,7 +48,7 @@ jobs: cache-name: ${{ matrix.cache-name }} continue-on-error: true - - name: Setup python and scons + - name: Setup Python and SCons uses: ./.github/actions/godot-deps - name: Download Direct3D 12 SDK components diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5c0c0b6917..c9a7714023 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,12 +17,12 @@ repos: platform/android/java/lib/src/com/.* ) - - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.2.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.4 hooks: - - id: black - files: (\.py$|SConstruct|SCsub) - types_or: [text] + - id: ruff + args: [--fix] + - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy rev: v0.971 @@ -74,37 +74,6 @@ repos: # files: ^(doc/classes|.*/doc_classes)/.*\.xml$ # args: [--schema, doc/class.xsd] - - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.46.0 - hooks: - - id: eslint - name: eslint-engine - files: ^(platform/web/js/engine/|js/jsdoc2rst/).*\.js$ - args: [--fix, --no-eslintrc, --config, platform/web/.eslintrc.engine.js] - additional_dependencies: &eslint-deps - - eslint@8.46.0 - - eslint-config-airbnb-base@15.0.0 - - eslint-plugin-import@2.28.0 - - eslint-plugin-html@7.1.0 - - '@html-eslint/eslint-plugin@0.19.1' - - '@html-eslint/parser@0.19.1' - - id: eslint - name: eslint-libs - files: ^(platform/web/js/libs/|modules/).*\.js$ - args: [--fix, --no-eslintrc, --config, platform/web/.eslintrc.libs.js] - additional_dependencies: *eslint-deps - - id: eslint - name: eslint-sw - files: ^misc/dist/html/service-worker\.js$ - args: [--fix, --no-eslintrc, --config, platform/web/.eslintrc.sw.js] - additional_dependencies: *eslint-deps - - id: eslint - name: eslint-html - files: ^misc/dist/html/.*\.html$ - types: [html] - args: [--fix, --no-eslintrc, --config, platform/web/.eslintrc.html.js] - additional_dependencies: *eslint-deps - - repo: local hooks: - id: make-rst @@ -120,13 +89,39 @@ repos: entry: python3 doc/tools/doc_status.py files: ^(doc/classes|.*/doc_classes)/.*\.xml$ + - id: eslint + name: eslint + language: node + entry: eslint + files: ^(platform/web/js/|modules/|misc/dist/html/).*\.(js|html)$ + args: [--fix, --no-warn-ignored, --no-config-lookup, --config, platform/web/eslint.config.cjs] + additional_dependencies: + - '@eslint/js@^9.3.0' + - '@html-eslint/eslint-plugin@^0.24.1' + - '@html-eslint/parser@^0.24.1' + - '@stylistic/eslint-plugin@^2.1.0' + - 'eslint@^9.3.0' + - 'eslint-plugin-html@^8.1.1' + - 'globals@^15.3.0' + - 'espree@^10.0.1' + - id: jsdoc name: jsdoc language: node entry: jsdoc files: ^platform/web/js/engine/(engine|config|features)\.js$ - args: [--template, platform/web/js/jsdoc2rst/, --destination, '', -d, dry-run] - additional_dependencies: ["jsdoc@4.0.2"] + args: + - --template + - platform/web/js/jsdoc2rst/ + - platform/web/js/engine/engine.js + - platform/web/js/engine/config.js + - platform/web/js/engine/features.js + - --destination + - '' + - -d + - dry-run + pass_filenames: false + additional_dependencies: ['jsdoc@^4.0.3'] - id: copyright-headers name: copyright-headers @@ -136,6 +131,7 @@ repos: exclude: | (?x)^( core/math/bvh_.*\.inc$| + platform/(?!android|ios|linuxbsd|macos|web|windows)\w+/.*| platform/android/java/lib/src/com/.*| platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView\.java$| platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper\.java$| @@ -176,3 +172,15 @@ repos: language: python entry: python3 misc/scripts/dotnet_format.py types_or: [c#] + +# End of upstream Godot pre-commit hooks. +# +# Keep this separation to let downstream forks add their own hooks to this file, +# without running into merge conflicts when rebasing on latest upstream. +# +# Start of downstream pre-commit hooks. +# +# This is still the "repo: local" scope, so new local hooks can be defined directly at this indentation: +# - id: new-local-hook +# To add external repo hooks, bring the indentation back to: +# - repo: my-remote-hook diff --git a/SConstruct b/SConstruct index 07ec014cf9..6fa3e00325 100644 --- a/SConstruct +++ b/SConstruct @@ -1,6 +1,6 @@ #!/usr/bin/env python -EnsureSConsVersion(3, 0, 0) +EnsureSConsVersion(3, 1, 2) EnsurePythonVersion(3, 6) # System @@ -10,21 +10,11 @@ import os import pickle import sys import time -from types import ModuleType from collections import OrderedDict -from importlib.util import spec_from_file_location, module_from_spec -from SCons import __version__ as scons_raw_version - -# Enable ANSI escape code support on Windows 10 and later (for colored console output). -# <https://github.com/python/cpython/issues/73245> -if sys.platform == "win32": - from ctypes import windll, c_int, byref +from importlib.util import module_from_spec, spec_from_file_location +from types import ModuleType - stdout_handle = windll.kernel32.GetStdHandle(c_int(-11)) - mode = c_int(0) - windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode)) - mode = c_int(mode.value | 4) - windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode) +from SCons import __version__ as scons_raw_version # Explicitly resolve the helper modules, this is done to avoid clash with # modules of the same name that might be randomly added (e.g. someone adding @@ -63,17 +53,35 @@ _helper_module("core.core_builders", "core/core_builders.py") _helper_module("main.main_builders", "main/main_builders.py") # Local -import methods -import glsl_builders import gles3_builders +import glsl_builders +import methods import scu_builders -from methods import print_warning, print_error -from platform_methods import architectures, architecture_aliases +from methods import print_error, print_warning +from platform_methods import architecture_aliases, architectures if ARGUMENTS.get("target", "editor") == "editor": _helper_module("editor.editor_builders", "editor/editor_builders.py") _helper_module("editor.template_builders", "editor/template_builders.py") +# Enable ANSI escape code support on Windows 10 and later (for colored console output). +# <https://github.com/python/cpython/issues/73245> +if sys.stdout.isatty() and sys.platform == "win32": + try: + from ctypes import WinError, byref, windll # type: ignore + from ctypes.wintypes import DWORD # type: ignore + + stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) + mode = DWORD(0) + if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)): + raise WinError() + mode = DWORD(mode.value | 4) + if not windll.kernel32.SetConsoleMode(stdout_handle, mode): + raise WinError() + except Exception as e: + methods._colorize = False + print_error(f"Failed to enable ANSI escape code support, disabling color output.\n{e}") + # Scan possible build platforms platform_list = [] # list of platforms @@ -114,6 +122,8 @@ for x in sorted(glob.glob("platform/*")): platform_list += [x] platform_opts[x] = detect.get_opts() platform_flags[x] = detect.get_flags() + if isinstance(platform_flags[x], list): # backwards compatibility + platform_flags[x] = {flag[0]: flag[1] for flag in platform_flags[x]} sys.path.remove(tmppath) sys.modules.pop("detect") @@ -365,7 +375,7 @@ if env["platform"] in platform_opts: opts.Add(opt) # Update the environment to take platform-specific options into account. -opts.Update(env, {**ARGUMENTS, **env}) +opts.Update(env, {**ARGUMENTS, **env.Dictionary()}) # Detect modules. modules_detected = OrderedDict() @@ -425,7 +435,7 @@ for name, path in modules_detected.items(): env.modules_detected = modules_detected # Update the environment again after all the module options are added. -opts.Update(env, {**ARGUMENTS, **env}) +opts.Update(env, {**ARGUMENTS, **env.Dictionary()}) Help(opts.GenerateHelpText(env)) # add default include paths @@ -554,16 +564,16 @@ if env["build_profile"] != "": dbo = ft["disabled_build_options"] for c in dbo: env[c] = dbo[c] - except: + except json.JSONDecodeError: print_error('Failed to open feature build profile: "{}"'.format(env["build_profile"])) Exit(255) # Platform specific flags. # These can sometimes override default options. flag_list = platform_flags[env["platform"]] -for f in flag_list: - if not (f[0] in ARGUMENTS) or ARGUMENTS[f[0]] == "auto": # Allow command line to override platform flags - env[f[0]] = f[1] +for key, value in flag_list.items(): + if key not in ARGUMENTS or ARGUMENTS[key] == "auto": # Allow command line to override platform flags + env[key] = value # 'dev_mode' and 'production' are aliases to set default options if they haven't been # set manually by the user. @@ -583,7 +593,7 @@ if env["production"]: # Run SCU file generation script if in a SCU build. if env["scu_build"]: max_includes_per_scu = 8 - if env.dev_build == True: + if env.dev_build: max_includes_per_scu = 1024 read_scu_limit = int(env["scu_limit"]) @@ -976,7 +986,7 @@ GLSL_BUILDERS = { env.Append(BUILDERS=GLSL_BUILDERS) scons_cache_path = os.environ.get("SCONS_CACHE") -if scons_cache_path != None: +if scons_cache_path is not None: CacheDir(scons_cache_path) print("Scons cache enabled... (path: '" + scons_cache_path + "')") diff --git a/core/SCsub b/core/SCsub index a61c0b5fc2..52f3506416 100644 --- a/core/SCsub +++ b/core/SCsub @@ -2,9 +2,11 @@ Import("env") +import os + import core_builders + import methods -import os env.core_sources = [] @@ -188,9 +190,7 @@ def version_info_builder(target, source, env): #define VERSION_WEBSITE "{website}" #define VERSION_DOCS_BRANCH "{docs_branch}" #define VERSION_DOCS_URL "https://docs.godotengine.org/en/" VERSION_DOCS_BRANCH -""".format( - **env.version_info - ) +""".format(**env.version_info) ) @@ -206,9 +206,7 @@ def version_hash_builder(target, source, env): const char *const VERSION_HASH = "{git_hash}"; const uint64_t VERSION_TIMESTAMP = {git_timestamp}; -""".format( - **env.version_info - ) +""".format(**env.version_info) ) diff --git a/core/core_builders.py b/core/core_builders.py index a401f03693..a3dc935b79 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -180,7 +180,7 @@ def make_license_header(target, source, env): return line def next_tag(self): - if not ":" in self.current: + if ":" not in self.current: return ("", []) tag, line = self.current.split(":", 1) lines = [line.strip()] @@ -206,7 +206,7 @@ def make_license_header(target, source, env): if not tag or not reader.current: # end of a paragraph start a new part - if "License" in part and not "Files" in part: + if "License" in part and "Files" not in part: # no Files tag in this one, so assume standalone license license_list.append(part["License"]) part = {} @@ -298,13 +298,13 @@ def make_license_header(target, source, env): f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n") f.write("const char *const LICENSE_NAMES[] = {\n") - for l in license_list: - f.write('\t"' + escape_string(l[0]) + '",\n') + for license in license_list: + f.write('\t"' + escape_string(license[0]) + '",\n') f.write("};\n\n") f.write("const char *const LICENSE_BODIES[] = {\n\n") - for l in license_list: - for line in l[1:]: + for license in license_list: + for line in license[1:]: if line == ".": f.write('\t"\\n"\n') else: diff --git a/core/extension/SCsub b/core/extension/SCsub index 901ceec1e8..6ab2d2b0a6 100644 --- a/core/extension/SCsub +++ b/core/extension/SCsub @@ -2,8 +2,8 @@ Import("env") -import make_wrappers import make_interface_dumper +import make_wrappers env.CommandNoCache(["ext_wrappers.gen.inc"], "make_wrappers.py", env.Run(make_wrappers.run)) env.CommandNoCache( diff --git a/core/extension/make_wrappers.py b/core/extension/make_wrappers.py index 655b90d2b1..54f4fd5579 100644 --- a/core/extension/make_wrappers.py +++ b/core/extension/make_wrappers.py @@ -10,7 +10,6 @@ _FORCE_INLINE_ virtual $RETVAL m_name($FUNCARGS) $CONST override { \\ def generate_mod_version(argcount, const=False, returns=False): s = proto_mod sproto = str(argcount) - method_info = "" if returns: sproto += "R" s = s.replace("$RETTYPE", "m_ret, ") @@ -68,7 +67,6 @@ virtual $RETVAL m_name($FUNCARGS) $CONST override { \\ def generate_ex_version(argcount, const=False, returns=False): s = proto_ex sproto = str(argcount) - method_info = "" if returns: sproto += "R" s = s.replace("$RETTYPE", "m_ret, ") diff --git a/core/input/SCsub b/core/input/SCsub index da29637135..d8e6f33156 100644 --- a/core/input/SCsub +++ b/core/input/SCsub @@ -4,7 +4,6 @@ Import("env") import input_builders - # Order matters here. Higher index controller database files write on top of lower index database files. controller_databases = [ "gamecontrollerdb.txt", diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 573692f41e..1ecfd8366d 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -569,11 +569,18 @@ Resource::Resource() : remapped_list(this) {} Resource::~Resource() { - if (!path_cache.is_empty()) { - ResourceCache::lock.lock(); - ResourceCache::resources.erase(path_cache); - ResourceCache::lock.unlock(); + if (unlikely(path_cache.is_empty())) { + return; } + + ResourceCache::lock.lock(); + // Only unregister from the cache if this is the actual resource listed there. + // (Other resources can have the same value in `path_cache` if loaded with `CACHE_IGNORE`.) + HashMap<String, Resource *>::Iterator E = ResourceCache::resources.find(path_cache); + if (likely(E && E->value == this)) { + ResourceCache::resources.remove(E); + } + ResourceCache::lock.unlock(); } HashMap<String, Resource *> ResourceCache::resources; diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h index 1502b2807c..83ebdc5a84 100644 --- a/core/math/geometry_2d.h +++ b/core/math/geometry_2d.h @@ -350,6 +350,8 @@ public: return triangles; } + // Assumes cartesian coordinate system with +x to the right, +y up. + // If using screen coordinates (+x to the right, +y down) the result will need to be flipped. static bool is_polygon_clockwise(const Vector<Vector2> &p_polygon) { int c = p_polygon.size(); if (c < 3) { diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index e25703b93f..3d93ce8e73 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -522,7 +522,7 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require #ifdef TOOLS_ENABLED if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) { if (!ti->inherits_ptr || !ti->inherits_ptr->creation_func) { - ERR_PRINT_ONCE(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name)); + ERR_PRINT(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name)); } else { ObjectGDExtension *extension = get_placeholder_extension(ti->name); return (Object *)extension->create_instance(extension->class_userdata); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 4b32acaaa0..bcab80ea94 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1571,9 +1571,6 @@ <member name="Geometry3D" type="Geometry3D" setter="" getter=""> The [Geometry3D] singleton. </member> - <member name="GodotSharp" type="GodotSharp" setter="" getter=""> - The [GodotSharp] singleton. - </member> <member name="IP" type="IP" setter="" getter=""> The [IP] singleton. </member> diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index 30df4fd10d..d2af6179d9 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -122,7 +122,7 @@ The horizontal space between [Button]'s icon and text. Negative values will be treated as [code]0[/code] when used. </theme_item> <theme_item name="icon_max_width" data_type="constant" type="int" default="0"> - The maximum allowed width of the [Button]'s icon. This limit is applied on top of the default size of the icon, or its expanded size if [member expand_icon] is [code]true[/code]. The height is adjusted according to the icon's ratio. + The maximum allowed width of the [Button]'s icon. This limit is applied on top of the default size of the icon, or its expanded size if [member expand_icon] is [code]true[/code]. The height is adjusted according to the icon's ratio. If the button has additional icons (e.g. [CheckBox]), they will also be limited. </theme_item> <theme_item name="outline_size" data_type="constant" type="int" default="0"> The size of the text outline. diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml index 6b25be490e..cfdc172fd1 100644 --- a/doc/classes/EditorInspector.xml +++ b/doc/classes/EditorInspector.xml @@ -28,7 +28,6 @@ </method> </methods> <members> - <member name="follow_focus" type="bool" setter="set_follow_focus" getter="is_following_focus" overrides="ScrollContainer" default="true" /> <member name="horizontal_scroll_mode" type="int" setter="set_horizontal_scroll_mode" getter="get_horizontal_scroll_mode" overrides="ScrollContainer" enum="ScrollContainer.ScrollMode" default="0" /> </members> <signals> diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml index dfcf299fe6..f21696d02c 100644 --- a/doc/classes/Geometry2D.xml +++ b/doc/classes/Geometry2D.xml @@ -116,6 +116,7 @@ <param index="0" name="polygon" type="PackedVector2Array" /> <description> Returns [code]true[/code] if [param polygon]'s vertices are ordered in clockwise order, otherwise returns [code]false[/code]. + [b]Note:[/b] Assumes a Cartesian coordinate system where [code]+x[/code] is right and [code]+y[/code] is up. If using screen coordinates ([code]+y[/code] is down), the result will need to be flipped (i.e. a [code]true[/code] result will indicate counter-clockwise). </description> </method> <method name="line_intersects_line"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 926d981f68..5ac4c96d93 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -554,10 +554,10 @@ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when defining a local or subclass member variable that would shadow a variable that is inherited from a parent class. </member> <member name="debug/gdscript/warnings/standalone_expression" type="int" setter="" getter="" default="1"> - When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling an expression that has no effect on the surrounding code, such as writing [code]2 + 2[/code] as a statement. + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling an expression that may have no effect on the surrounding code, such as writing [code]2 + 2[/code] as a statement. </member> <member name="debug/gdscript/warnings/standalone_ternary" type="int" setter="" getter="" default="1"> - When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a ternary expression that has no effect on the surrounding code, such as writing [code]42 if active else 0[/code] as a statement. + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a ternary expression that may have no effect on the surrounding code, such as writing [code]42 if active else 0[/code] as a statement. </member> <member name="debug/gdscript/warnings/static_called_on_instance" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a static method from an instance of a class instead of from the class directly. diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 4dda1f8db8..75cad4d08b 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -1314,7 +1314,7 @@ [b]Note:[/b] This method is only implemented on Linux. </member> <member name="minimap_draw" type="bool" setter="set_draw_minimap" getter="is_drawing_minimap" default="false"> - If [code]true[/code], a minimap is shown, providing an outline of your source code. + If [code]true[/code], a minimap is shown, providing an outline of your source code. The minimap uses a fixed-width text size. </member> <member name="minimap_width" type="int" setter="set_minimap_width" getter="get_minimap_width" default="80"> The width, in pixels, of the minimap. diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 43cd1a8aaa..b0cb25fafd 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -655,13 +655,13 @@ [StyleBox] used for the cursor, when the [Tree] is not being focused. </theme_item> <theme_item name="custom_button" data_type="style" type="StyleBox"> - Default [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode cell. + Default [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode cell when button is enabled with [method TreeItem.set_custom_as_button]. </theme_item> <theme_item name="custom_button_hover" data_type="style" type="StyleBox"> - [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode cell when it's hovered. + [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode button cell when it's hovered. </theme_item> <theme_item name="custom_button_pressed" data_type="style" type="StyleBox"> - [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode cell when it's pressed. + [StyleBox] for a [constant TreeItem.CELL_MODE_CUSTOM] mode button cell when it's pressed. </theme_item> <theme_item name="focus" data_type="style" type="StyleBox"> The focused style for the [Tree], drawn on top of everything. diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index c679838ec5..78a703c213 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -318,12 +318,14 @@ <return type="int" enum="TextServer.StructuredTextParser" /> <param index="0" name="column" type="int" /> <description> + Returns the BiDi algorithm override set for this cell. </description> </method> <method name="get_structured_text_bidi_override_options" qualifiers="const"> <return type="Array" /> <param index="0" name="column" type="int" /> <description> + Returns the additional BiDi options set for this cell. </description> </method> <method name="get_suffix" qualifiers="const"> @@ -401,6 +403,7 @@ <return type="bool" /> <param index="0" name="column" type="int" /> <description> + Returns [code]true[/code] if the cell was made into a button with [method set_custom_as_button]. </description> </method> <method name="is_edit_multiline" qualifiers="const"> @@ -532,7 +535,7 @@ <param index="0" name="column" type="int" /> <param index="1" name="mode" type="int" enum="TreeItem.TreeCellMode" /> <description> - Sets the given column's cell mode to [param mode]. See [enum TreeCellMode] constants. + Sets the given column's cell mode to [param mode]. This determines how the cell is displayed and edited. See [enum TreeCellMode] constants for details. </description> </method> <method name="set_checked"> @@ -555,6 +558,7 @@ <param index="0" name="column" type="int" /> <param index="1" name="enable" type="bool" /> <description> + Makes a cell with [constant CELL_MODE_CUSTOM] display as a non-flat button with a [StyleBox]. </description> </method> <method name="set_custom_bg_color"> @@ -589,7 +593,7 @@ <param index="0" name="column" type="int" /> <param index="1" name="callback" type="Callable" /> <description> - Sets the given column's custom draw callback. Use an empty [Callable] ([code skip-lint]Callable()[/code]) to clear the custom callback. + Sets the given column's custom draw callback. Use an empty [Callable] ([code skip-lint]Callable()[/code]) to clear the custom callback. The cell has to be in [constant CELL_MODE_CUSTOM] to use this feature. The [param callback] should accept two arguments: the [TreeItem] that is drawn and its position and size as a [Rect2]. </description> </method> @@ -639,7 +643,7 @@ <param index="0" name="column" type="int" /> <param index="1" name="texture" type="Texture2D" /> <description> - Sets the given column's icon [Texture2D]. + Sets the given cell's icon [Texture2D]. The cell has to be in [constant CELL_MODE_ICON] mode. </description> </method> <method name="set_icon_max_width"> @@ -724,6 +728,7 @@ <param index="0" name="column" type="int" /> <param index="1" name="parser" type="int" enum="TextServer.StructuredTextParser" /> <description> + Set BiDi algorithm override for the structured text. Has effect for cells that display text. </description> </method> <method name="set_structured_text_bidi_override_options"> @@ -731,6 +736,7 @@ <param index="0" name="column" type="int" /> <param index="1" name="args" type="Array" /> <description> + Set additional options for BiDi override. Has effect for cells that display text. </description> </method> <method name="set_suffix"> @@ -805,18 +811,21 @@ </members> <constants> <constant name="CELL_MODE_STRING" value="0" enum="TreeCellMode"> - Cell contains a string. + Cell shows a string label. When editable, the text can be edited using a [LineEdit], or a [TextEdit] popup if [method set_edit_multiline] is used. </constant> <constant name="CELL_MODE_CHECK" value="1" enum="TreeCellMode"> - Cell contains a checkbox. + Cell shows a checkbox, optionally with text. The checkbox can be pressed, released, or indeterminate (via [method set_indeterminate]). The checkbox can't be clicked unless the cell is editable. </constant> <constant name="CELL_MODE_RANGE" value="2" enum="TreeCellMode"> - Cell contains a range. + Cell shows a numeric range. When editable, it can be edited using a range slider. Use [method set_range] to set the value and [method set_range_config] to configure the range. + This cell can also be used in a text dropdown mode when you assign a text with [method set_text]. Separate options with a comma, e.g. [code]"Option1,Option2,Option3"[/code]. </constant> <constant name="CELL_MODE_ICON" value="3" enum="TreeCellMode"> - Cell contains an icon. + Cell shows an icon. It can't be edited nor display text. </constant> <constant name="CELL_MODE_CUSTOM" value="4" enum="TreeCellMode"> + Cell shows as a clickable button. It will display an arrow similar to [OptionButton], but doesn't feature a dropdown (for that you can use [constant CELL_MODE_RANGE]). Clicking the button emits the [signal Tree.item_edited] signal. The button is flat by default, you can use [method set_custom_as_button] to display it with a [StyleBox]. + This mode also supports custom drawing using [method set_custom_draw_callback]. </constant> </constants> </class> diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py index a23c11b585..57c03bfdee 100755 --- a/doc/tools/doc_status.py +++ b/doc/tools/doc_status.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 import fnmatch -import os -import sys -import re import math +import os import platform +import re +import sys import xml.etree.ElementTree as ET from typing import Dict, List, Set @@ -286,7 +286,7 @@ class ClassStatus: status.progresses[tag.tag].increment(is_deprecated or is_experimental or has_descr) elif tag.tag in ["constants", "members", "theme_items"]: for sub_tag in list(tag): - if not sub_tag.text is None: + if sub_tag.text is not None: is_deprecated = "deprecated" in sub_tag.attrib is_experimental = "experimental" in sub_tag.attrib has_descr = len(sub_tag.text.strip()) > 0 diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 0b722757f9..bc379f8553 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -4,17 +4,16 @@ import argparse import os -import platform import re import sys import xml.etree.ElementTree as ET from collections import OrderedDict -from typing import List, Dict, TextIO, Tuple, Optional, Any, Union +from typing import Any, Dict, List, Optional, TextIO, Tuple, Union # Import hardcoded version information from version.py root_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../") sys.path.append(root_directory) # Include the root directory -import version +import version # noqa: E402 # $DOCS_URL/path/to/page.html(#fragment-tag) GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$") @@ -677,17 +676,6 @@ class ScriptLanguageParityCheck: # Entry point for the RST generator. def main() -> None: - # Enable ANSI escape code support on Windows 10 and later (for colored console output). - # <https://bugs.python.org/issue29059> - if platform.system().lower() == "windows": - from ctypes import windll, c_int, byref # type: ignore - - stdout_handle = windll.kernel32.GetStdHandle(c_int(-11)) - mode = c_int(0) - windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode)) - mode = c_int(mode.value | 4) - windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode) - parser = argparse.ArgumentParser() parser.add_argument("path", nargs="+", help="A path to an XML file or a directory containing XML files to parse.") parser.add_argument("--filter", default="", help="The filepath pattern for XML files to filter.") @@ -711,7 +699,25 @@ def main() -> None: ) args = parser.parse_args() - should_color = args.color or (hasattr(sys.stdout, "isatty") and sys.stdout.isatty()) + should_color = bool(args.color or sys.stdout.isatty() or os.environ.get("CI")) + + # Enable ANSI escape code support on Windows 10 and later (for colored console output). + # <https://github.com/python/cpython/issues/73245> + if should_color and sys.stdout.isatty() and sys.platform == "win32": + try: + from ctypes import WinError, byref, windll # type: ignore + from ctypes.wintypes import DWORD # type: ignore + + stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) + mode = DWORD(0) + if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)): + raise WinError() + mode = DWORD(mode.value | 4) + if not windll.kernel32.SetConsoleMode(stdout_handle, mode): + raise WinError() + except Exception: + should_color = False + STYLES["red"] = "\x1b[91m" if should_color else "" STYLES["green"] = "\x1b[92m" if should_color else "" STYLES["yellow"] = "\x1b[93m" if should_color else "" @@ -1406,7 +1412,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: operator_anchor = f".. _class_{class_name}_operator_{sanitize_operator_name(m.name, state)}" for parameter in m.parameters: operator_anchor += f"_{parameter.type_name.type_name}" - operator_anchor += f":\n\n" + operator_anchor += ":\n\n" f.write(operator_anchor) f.write(".. rst-class:: classref-operator\n\n") @@ -1546,7 +1552,7 @@ def make_method_signature( out += f":ref:`{op_name}<class_{class_def.name}_{ref_type}_{sanitize_operator_name(definition.name, state)}" for parameter in definition.parameters: out += f"_{parameter.type_name.type_name}" - out += f">`" + out += ">`" elif ref_type == "method": ref_type_qualifier = "" if definition.name.startswith("_"): diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index c656ee3cc7..5cde4ee9de 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -70,21 +70,28 @@ enum SceneUniformLocation { SCENE_GLOBALS_UNIFORM_LOCATION, SCENE_DATA_UNIFORM_LOCATION, SCENE_MATERIAL_UNIFORM_LOCATION, - SCENE_EMPTY, // Unused, put here to avoid conflicts with SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION. + SCENE_EMPTY1, // Unused, put here to avoid conflicts with SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION. SCENE_OMNILIGHT_UNIFORM_LOCATION, SCENE_SPOTLIGHT_UNIFORM_LOCATION, SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, SCENE_MULTIVIEW_UNIFORM_LOCATION, SCENE_POSITIONAL_SHADOW_UNIFORM_LOCATION, SCENE_DIRECTIONAL_SHADOW_UNIFORM_LOCATION, + SCENE_EMPTY2, // Unused, put here to avoid conflicts with SKY_MULTIVIEW_UNIFORM_LOCATION. }; enum SkyUniformLocation { SKY_TONEMAP_UNIFORM_LOCATION, SKY_GLOBALS_UNIFORM_LOCATION, - SKY_EMPTY, // Unused, put here to avoid conflicts with SCENE_DATA_UNIFORM_LOCATION. + SKY_EMPTY1, // Unused, put here to avoid conflicts with SCENE_DATA_UNIFORM_LOCATION. SKY_MATERIAL_UNIFORM_LOCATION, SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, + SKY_EMPTY2, // Unused, put here to avoid conflicts with SCENE_OMNILIGHT_UNIFORM_LOCATION. + SKY_EMPTY3, // Unused, put here to avoid conflicts with SCENE_SPOTLIGHT_UNIFORM_LOCATION. + SKY_EMPTY4, // Unused, put here to avoid conflicts with SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION. + SKY_EMPTY5, // Unused, put here to avoid conflicts with SCENE_MULTIVIEW_UNIFORM_LOCATION. + SKY_EMPTY6, // Unused, put here to avoid conflicts with SCENE_POSITIONAL_SHADOW_UNIFORM_LOCATION. + SKY_EMPTY7, // Unused, put here to avoid conflicts with SCENE_DIRECTIONAL_SHADOW_UNIFORM_LOCATION. SKY_MULTIVIEW_UNIFORM_LOCATION, }; diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index 26549901a6..9de65ba960 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -116,7 +116,7 @@ uniform float z_far; uniform uint directional_light_count; #ifdef USE_MULTIVIEW -layout(std140) uniform MultiviewData { // ubo:5 +layout(std140) uniform MultiviewData { // ubo:11 highp mat4 projection_matrix_view[MAX_VIEWS]; highp mat4 inv_projection_matrix_view[MAX_VIEWS]; highp vec4 eye_offset[MAX_VIEWS]; diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index a65347a5b1..2dcf623995 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1391,7 +1391,7 @@ void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) { tinfo.format = t->format; tinfo.width = t->alloc_width; tinfo.height = t->alloc_height; - tinfo.depth = 0; + tinfo.depth = t->depth; tinfo.bytes = t->total_data_size; r_info->push_back(tinfo); } @@ -1495,14 +1495,18 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref<Image> &p_image, for (int i = 0; i < mipmaps; i++) { int size, ofs; img->get_mipmap_offset_and_size(i, ofs, size); - if (compressed) { glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - - int bw = w; - int bh = h; - - glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); + if (texture->target == GL_TEXTURE_2D_ARRAY) { + if (p_initialize) { + glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0, + size * texture->layers, &read[ofs]); + } else { + glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, internal_format, size, &read[ofs]); + } + } else { + glCompressedTexImage2D(blit_target, i, internal_format, w, h, 0, size, &read[ofs]); + } } else { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); if (texture->target == GL_TEXTURE_2D_ARRAY) { diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 131e0e4a8a..896fc6ff91 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -3816,11 +3816,11 @@ bool RenderingDeviceDriverVulkan::pipeline_cache_create(const Vector<uint8_t> &p if (p_data.is_empty()) { // No pre-existing cache, just create it. } else if (p_data.size() <= (int)sizeof(PipelineCacheHeader)) { - WARN_PRINT("Invalid/corrupt pipelines cache."); + print_verbose("Invalid/corrupt Vulkan pipelines cache. Existing shader pipeline cache will be ignored, which may result in stuttering during gameplay."); } else { const PipelineCacheHeader *loaded_header = reinterpret_cast<const PipelineCacheHeader *>(p_data.ptr()); if (loaded_header->magic != 868 + VK_PIPELINE_CACHE_HEADER_VERSION_ONE) { - WARN_PRINT("Invalid pipelines cache magic number."); + print_verbose("Invalid Vulkan pipelines cache magic number. Existing shader pipeline cache will be ignored, which may result in stuttering during gameplay."); } else { const uint8_t *loaded_buffer_start = p_data.ptr() + sizeof(PipelineCacheHeader); uint32_t loaded_buffer_size = p_data.size() - sizeof(PipelineCacheHeader); @@ -3832,7 +3832,7 @@ bool RenderingDeviceDriverVulkan::pipeline_cache_create(const Vector<uint8_t> &p loaded_header->driver_version != current_header->driver_version || memcmp(loaded_header->uuid, current_header->uuid, VK_UUID_SIZE) != 0 || loaded_header->driver_abi != current_header->driver_abi) { - WARN_PRINT("Invalid pipelines cache header."); + print_verbose("Invalid Vulkan pipelines cache header. This may be due to an engine change, GPU change or graphics driver version change. Existing shader pipeline cache will be ignored, which may result in stuttering during gameplay."); } else { pipelines_cache.current_size = loaded_buffer_size; pipelines_cache.buffer = p_data; diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 8ea1f52d15..a349a66f75 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -107,12 +107,6 @@ const IID IID_IAudioClient3 = __uuidof(IAudioClient3); const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); -#define SAFE_RELEASE(memory) \ - if ((memory) != nullptr) { \ - (memory)->Release(); \ - (memory) = nullptr; \ - } - #define REFTIMES_PER_SEC 10000000 #define REFTIMES_PER_MILLISEC 10000 @@ -129,16 +123,10 @@ static bool default_input_device_changed = false; class CMMNotificationClient : public IMMNotificationClient { LONG _cRef = 1; - IMMDeviceEnumerator *_pEnumerator = nullptr; public: CMMNotificationClient() {} - virtual ~CMMNotificationClient() { - if ((_pEnumerator) != nullptr) { - (_pEnumerator)->Release(); - (_pEnumerator) = nullptr; - } - } + virtual ~CMMNotificationClient() {} ULONG STDMETHODCALLTYPE AddRef() { return InterlockedIncrement(&_cRef); @@ -203,8 +191,8 @@ static CMMNotificationClient notif_client; Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_input, bool p_reinit, bool p_no_audio_client_3) { WAVEFORMATEX *pwfex; - IMMDeviceEnumerator *enumerator = nullptr; - IMMDevice *output_device = nullptr; + ComPtr<IMMDeviceEnumerator> enumerator = nullptr; + ComPtr<IMMDevice> output_device = nullptr; HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); @@ -212,7 +200,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i if (p_device->device_name == "Default") { hr = enumerator->GetDefaultAudioEndpoint(p_input ? eCapture : eRender, eConsole, &output_device); } else { - IMMDeviceCollection *devices = nullptr; + ComPtr<IMMDeviceCollection> devices = nullptr; hr = enumerator->EnumAudioEndpoints(p_input ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); @@ -225,12 +213,12 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); for (ULONG i = 0; i < count && !found; i++) { - IMMDevice *tmp_device = nullptr; + ComPtr<IMMDevice> tmp_device = nullptr; hr = devices->Item(i, &tmp_device); ERR_BREAK(hr != S_OK); - IPropertyStore *props = nullptr; + ComPtr<IPropertyStore> props = nullptr; hr = tmp_device->OpenPropertyStore(STGM_READ, &props); ERR_BREAK(hr != S_OK); @@ -248,8 +236,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i } PropVariantClear(&propvar); - props->Release(); - tmp_device->Release(); } if (found) { @@ -276,7 +262,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i } hr = enumerator->RegisterEndpointNotificationCallback(¬if_client); - SAFE_RELEASE(enumerator) if (hr != S_OK) { ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); @@ -303,8 +288,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i hr = output_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); } - SAFE_RELEASE(output_device) - if (p_reinit) { if (hr != S_OK) { return ERR_CANT_OPEN; @@ -319,7 +302,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i audioProps.bIsOffload = FALSE; audioProps.eCategory = AudioCategory_GameEffects; - hr = ((IAudioClient3 *)p_device->audio_client)->SetClientProperties(&audioProps); + hr = ((IAudioClient3 *)p_device->audio_client.Get())->SetClientProperties(&audioProps); ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: SetClientProperties failed with error 0x" + String::num_uint64(hr, 16) + "."); } @@ -402,7 +385,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i } } else { - IAudioClient3 *device_audio_client_3 = (IAudioClient3 *)p_device->audio_client; + IAudioClient3 *device_audio_client_3 = (IAudioClient3 *)p_device->audio_client.Get(); // AUDCLNT_STREAMFLAGS_RATEADJUST is an invalid flag with IAudioClient3, therefore we have to use // the closest supported mix rate supported by the audio driver. @@ -419,7 +402,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i if (hr != S_OK) { print_verbose("WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); CoTaskMemFree(pwfex); - SAFE_RELEASE(output_device) return audio_device_init(p_device, p_input, p_reinit, true); } @@ -441,7 +423,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i if (hr != S_OK) { print_verbose("WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); CoTaskMemFree(pwfex); - SAFE_RELEASE(output_device); return audio_device_init(p_device, p_input, p_reinit, true); } else { uint32_t output_latency_in_frames; @@ -453,7 +434,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i } else { print_verbose("WASAPI: GetCurrentSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); CoTaskMemFree(pwfex); - SAFE_RELEASE(output_device); return audio_device_init(p_device, p_input, p_reinit, true); } } @@ -468,7 +448,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i // Free memory CoTaskMemFree(pwfex); - SAFE_RELEASE(output_device) return OK; } @@ -537,9 +516,9 @@ Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) { p_device->active.clear(); } - SAFE_RELEASE(p_device->audio_client) - SAFE_RELEASE(p_device->render_client) - SAFE_RELEASE(p_device->capture_client) + p_device->audio_client.Reset(); + p_device->render_client.Reset(); + p_device->capture_client.Reset(); return OK; } @@ -581,8 +560,8 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) { PackedStringArray list; - IMMDeviceCollection *devices = nullptr; - IMMDeviceEnumerator *enumerator = nullptr; + ComPtr<IMMDeviceCollection> devices = nullptr; + ComPtr<IMMDeviceEnumerator> enumerator = nullptr; list.push_back(String("Default")); @@ -597,12 +576,12 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) { ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); for (ULONG i = 0; i < count; i++) { - IMMDevice *output_device = nullptr; + ComPtr<IMMDevice> output_device = nullptr; hr = devices->Item(i, &output_device); ERR_BREAK(hr != S_OK); - IPropertyStore *props = nullptr; + ComPtr<IPropertyStore> props = nullptr; hr = output_device->OpenPropertyStore(STGM_READ, &props); ERR_BREAK(hr != S_OK); @@ -615,12 +594,8 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) { list.push_back(String(propvar.pwszVal)); PropVariantClear(&propvar); - props->Release(); - output_device->Release(); } - devices->Release(); - enumerator->Release(); return list; } diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index 367c30607a..d73cbf4a8a 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -40,15 +40,18 @@ #include <audioclient.h> #include <mmdeviceapi.h> +#include <wrl/client.h> #define WIN32_LEAN_AND_MEAN #include <windows.h> +using Microsoft::WRL::ComPtr; + class AudioDriverWASAPI : public AudioDriver { class AudioDeviceWASAPI { public: - IAudioClient *audio_client = nullptr; - IAudioRenderClient *render_client = nullptr; // Output - IAudioCaptureClient *capture_client = nullptr; // Input + ComPtr<IAudioClient> audio_client = nullptr; + ComPtr<IAudioRenderClient> render_client = nullptr; // Output + ComPtr<IAudioCaptureClient> capture_client = nullptr; // Input SafeFlag active; WORD format_tag = 0; diff --git a/editor/SCsub b/editor/SCsub index e613a71238..029048969a 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -4,11 +4,12 @@ Import("env") env.editor_sources = [] -import os import glob +import os + import editor_builders -import methods +import methods if env.editor_build: # Generate doc data paths diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 6ff0520ab6..3023c5907a 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -505,6 +505,9 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info event_item->set_button_color(2, 1, Color(1, 1, 1, 0.75)); } } + + // Update UI. + clear_all_search->set_disabled(action_list_search->get_text().is_empty() && action_list_search_by_event->get_event().is_null()); } void ActionMapEditor::show_message(const String &p_message) { @@ -550,8 +553,9 @@ ActionMapEditor::ActionMapEditor() { action_list_search_by_event->connect(SceneStringName(focus_exited), callable_mp(this, &ActionMapEditor::_on_filter_unfocused)); top_hbox->add_child(action_list_search_by_event); - Button *clear_all_search = memnew(Button); + clear_all_search = memnew(Button); clear_all_search->set_text(TTR("Clear All")); + clear_all_search->set_tooltip_text(TTR("Clear all search filters.")); clear_all_search->connect(SceneStringName(pressed), callable_mp(action_list_search_by_event, &EventListenerLineEdit::clear_event)); clear_all_search->connect(SceneStringName(pressed), callable_mp(action_list_search, &LineEdit::clear)); top_hbox->add_child(clear_all_search); diff --git a/editor/action_map_editor.h b/editor/action_map_editor.h index 2b329f2fca..017296bfaa 100644 --- a/editor/action_map_editor.h +++ b/editor/action_map_editor.h @@ -52,7 +52,7 @@ public: bool has_initial = false; Dictionary action_initial; - Ref<Texture2D> icon = Ref<Texture2D>(); + Ref<Texture2D> icon; bool editable = true; }; @@ -85,6 +85,7 @@ private: CheckButton *show_builtin_actions_checkbutton = nullptr; LineEdit *action_list_search = nullptr; EventListenerLineEdit *action_list_search_by_event = nullptr; + Button *clear_all_search = nullptr; HBoxContainer *add_hbox = nullptr; LineEdit *add_edit = nullptr; diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index ebb63dd57c..045774080c 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -7312,17 +7312,19 @@ AnimationTrackEditor::AnimationTrackEditor() { bottom_hb->add_child(zoom); timeline->set_zoom(zoom); + ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F); + auto_fit = memnew(Button); auto_fit->set_flat(true); auto_fit->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_auto_fit)); - auto_fit->set_shortcut(ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F)); + auto_fit->set_shortcut(ED_GET_SHORTCUT("animation_editor/auto_fit")); bottom_hb->add_child(auto_fit); auto_fit_bezier = memnew(Button); auto_fit_bezier->set_flat(true); auto_fit_bezier->set_visible(false); auto_fit_bezier->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_auto_fit_bezier)); - auto_fit_bezier->set_shortcut(ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F)); + auto_fit_bezier->set_shortcut(ED_GET_SHORTCUT("animation_editor/auto_fit")); bottom_hb->add_child(auto_fit_bezier); edit = memnew(MenuButton); diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index d3174ed215..2b36b9254c 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -85,7 +85,7 @@ void CreateDialog::_fill_type_list() { EditorData &ed = EditorNode::get_editor_data(); for (List<StringName>::Element *I = complete_type_list.front(); I; I = I->next()) { - String type = I->get(); + StringName type = I->get(); if (!_should_hide_type(type)) { type_list.push_back(type); @@ -118,12 +118,12 @@ bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_cla return !profile.is_null() && profile->is_class_disabled(p_class); } -bool CreateDialog::_should_hide_type(const String &p_type) const { +bool CreateDialog::_should_hide_type(const StringName &p_type) const { if (_is_class_disabled_by_feature_profile(p_type)) { return true; } - if (is_base_type_node && p_type.begins_with("Editor")) { + if (is_base_type_node && p_type.operator String().begins_with("Editor")) { return true; // Do not show editor nodes. } @@ -146,12 +146,12 @@ bool CreateDialog::_should_hide_type(const String &p_type) const { } } } else { - if (!EditorNode::get_editor_data().script_class_is_parent(p_type, base_type)) { - return true; // Wrong inheritance. - } if (!ScriptServer::is_global_class(p_type)) { return true; } + if (!EditorNode::get_editor_data().script_class_is_parent(p_type, base_type)) { + return true; // Wrong inheritance. + } StringName native_type = ScriptServer::get_global_class_native_base(p_type); if (ClassDB::class_exists(native_type) && !ClassDB::can_instantiate(native_type)) { @@ -182,24 +182,30 @@ void CreateDialog::_update_search() { const String search_text = search_box->get_text(); bool empty_search = search_text.is_empty(); - // Filter all candidate results. - Vector<String> candidates; + float highest_score = 0.0f; + StringName best_match; + for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) { - if (empty_search || search_text.is_subsequence_ofn(I->get())) { - candidates.push_back(I->get()); + StringName candidate = I->get(); + if (empty_search || search_text.is_subsequence_ofn(candidate)) { + _add_type(candidate, ClassDB::class_exists(candidate) ? TypeCategory::CPP_TYPE : TypeCategory::OTHER_TYPE); + + // Determine the best match for an non-empty search. + if (!empty_search) { + float score = _score_type(candidate.operator String().get_slicec(' ', 0), search_text); + if (score > highest_score) { + highest_score = score; + best_match = candidate; + } + } } } - // Build the type tree. - for (int i = 0; i < candidates.size(); i++) { - _add_type(candidates[i], ClassDB::class_exists(candidates[i]) ? TypeCategory::CPP_TYPE : TypeCategory::OTHER_TYPE); - } - // Select the best result. if (empty_search) { select_type(base_type); - } else if (candidates.size() > 0) { - select_type(_top_result(candidates, search_text)); + } else if (best_match != StringName()) { + select_type(best_match); } else { favorite->set_disabled(true); help_bit->set_custom_text(String(), String(), vformat(TTR("No results for \"%s\"."), search_text.replace("[", "[lb]"))); @@ -208,52 +214,43 @@ void CreateDialog::_update_search() { } } -void CreateDialog::_add_type(const String &p_type, const TypeCategory p_type_category) { +void CreateDialog::_add_type(const StringName &p_type, TypeCategory p_type_category) { if (search_options_types.has(p_type)) { return; } - String inherits; - TypeCategory inherited_type = TypeCategory::OTHER_TYPE; + StringName inherits; if (p_type_category == TypeCategory::CPP_TYPE) { inherits = ClassDB::get_parent_class(p_type); inherited_type = TypeCategory::CPP_TYPE; - } else if (p_type_category == TypeCategory::PATH_TYPE) { - ERR_FAIL_COND(!ResourceLoader::exists(p_type, "Script")); - Ref<Script> scr = ResourceLoader::load(p_type, "Script"); - ERR_FAIL_COND(scr.is_null()); - - Ref<Script> base = scr->get_base_script(); - if (base.is_null()) { - String extends; - scr->get_language()->get_global_class_name(scr->get_path(), &extends); - - inherits = extends; - inherited_type = TypeCategory::CPP_TYPE; - } else { - inherits = scr->get_language()->get_global_class_name(base->get_path()); - if (inherits.is_empty()) { - inherits = base->get_path(); - inherited_type = TypeCategory::PATH_TYPE; - } - } } else { - if (ScriptServer::is_global_class(p_type)) { - Ref<Script> scr = EditorNode::get_editor_data().script_class_load_script(p_type); + if (p_type_category == TypeCategory::PATH_TYPE || ScriptServer::is_global_class(p_type)) { + Ref<Script> scr; + if (p_type_category == TypeCategory::PATH_TYPE) { + ERR_FAIL_COND(!ResourceLoader::exists(p_type, "Script")); + scr = ResourceLoader::load(p_type, "Script"); + } else { + scr = EditorNode::get_editor_data().script_class_load_script(p_type); + } ERR_FAIL_COND(scr.is_null()); Ref<Script> base = scr->get_base_script(); if (base.is_null()) { - String extends; - scr->get_language()->get_global_class_name(scr->get_path(), &extends); + // Must be a native base type. + StringName extends = scr->get_instance_base_type(); + if (extends == StringName()) { + // Not a valid script (has compile errors), we therefore ignore it as it can not be instantiated anyway (when selected). + return; + } inherits = extends; inherited_type = TypeCategory::CPP_TYPE; } else { - inherits = scr->get_language()->get_global_class_name(base->get_path()); - if (inherits.is_empty()) { + inherits = base->get_global_name(); + + if (inherits == StringName()) { inherits = base->get_path(); inherited_type = TypeCategory::PATH_TYPE; } @@ -267,7 +264,7 @@ void CreateDialog::_add_type(const String &p_type, const TypeCategory p_type_cat } // Should never happen, but just in case... - ERR_FAIL_COND(inherits.is_empty()); + ERR_FAIL_COND(inherits == StringName()); _add_type(inherits, inherited_type); @@ -276,7 +273,7 @@ void CreateDialog::_add_type(const String &p_type, const TypeCategory p_type_cat _configure_search_option_item(item, p_type, p_type_category); } -void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String &p_type, const TypeCategory p_type_category) { +void CreateDialog::_configure_search_option_item(TreeItem *r_item, const StringName &p_type, TypeCategory p_type_category) { bool script_type = ScriptServer::is_global_class(p_type); bool is_abstract = false; if (p_type_category == TypeCategory::CPP_TYPE) { @@ -342,20 +339,6 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String } } -String CreateDialog::_top_result(const Vector<String> &p_candidates, const String &p_search_text) const { - float highest_score = 0; - int highest_index = 0; - for (int i = 0; i < p_candidates.size(); i++) { - float score = _score_type(p_candidates[i].get_slicec(' ', 0), p_search_text); - if (score > highest_score) { - highest_score = score; - highest_index = i; - } - } - - return p_candidates[highest_index]; -} - float CreateDialog::_score_type(const String &p_type, const String &p_search) const { if (p_type == p_search) { // Always favor an exact match (case-sensitive), since clicking a favorite will set the search text to the type. diff --git a/editor/create_dialog.h b/editor/create_dialog.h index 12385747c2..d2866e9f04 100644 --- a/editor/create_dialog.h +++ b/editor/create_dialog.h @@ -68,10 +68,9 @@ class CreateDialog : public ConfirmationDialog { HashSet<StringName> type_blacklist; void _update_search(); - bool _should_hide_type(const String &p_type) const; - void _add_type(const String &p_type, const TypeCategory p_type_category); - void _configure_search_option_item(TreeItem *r_item, const String &p_type, const TypeCategory p_type_category); - String _top_result(const Vector<String> &p_candidates, const String &p_search_text) const; + bool _should_hide_type(const StringName &p_type) const; + void _add_type(const StringName &p_type, TypeCategory p_type_category); + void _configure_search_option_item(TreeItem *r_item, const StringName &p_type, TypeCategory p_type_category); float _score_type(const String &p_type, const String &p_search) const; bool _is_type_preferred(const String &p_type) const; diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index b58e82b7e7..331dacf6ad 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -344,25 +344,6 @@ void DocTools::merge_from(const DocTools &p_data) { merge_theme_properties(c.theme_properties, cf.theme_properties); merge_operators(c.operators, cf.operators); - -#ifndef MODULE_MONO_ENABLED - // The Mono module defines some properties that we want to keep when - // re-generating docs with a non-Mono build, to prevent pointless diffs - // (and loss of descriptions) depending on the config of the doc writer. - // We use a horrible hack to force keeping the relevant properties, - // hardcoded below. At least it's an ad hoc hack... ¯\_(ツ)_/¯ - // Don't show this to your kids. - if (c.name == "@GlobalScope") { - // Retrieve GodotSharp singleton. - for (int j = 0; j < cf.properties.size(); j++) { - if (cf.properties[j].name == "GodotSharp") { - c.properties.push_back(cf.properties[j]); - c.properties.sort(); - break; - } - } - } -#endif } } diff --git a/editor/editor_builders.py b/editor/editor_builders.py index cfe6e56b49..625d570666 100644 --- a/editor/editor_builders.py +++ b/editor/editor_builders.py @@ -7,6 +7,7 @@ import subprocess import tempfile import uuid import zlib + from methods import print_warning diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp index 5a7e914c11..020706c714 100644 --- a/editor/editor_feature_profile.cpp +++ b/editor/editor_feature_profile.cpp @@ -497,8 +497,8 @@ void EditorFeatureProfileManager::_hide_requested() { _cancel_pressed(); // From AcceptDialog. } -void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) { - TreeItem *class_item = class_list->create_item(p_parent); +void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected, int p_class_insert_index) { + TreeItem *class_item = class_list->create_item(p_parent, p_class_insert_index); class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); class_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_class)); String text = p_class; @@ -647,7 +647,7 @@ void EditorFeatureProfileManager::_class_list_item_edited() { String class_selected = md; edited->set_disable_class(class_selected, !checked); _save_and_update(); - _update_selected_profile(); + _update_profile_tree_from(item); } else if (md.get_type() == Variant::INT) { int feature_selected = md; edited->set_disable_feature(EditorFeatureProfile::Feature(feature_selected), !checked); @@ -703,19 +703,29 @@ void EditorFeatureProfileManager::_property_item_edited() { String property_selected = md; edited->set_disable_class_property(class_name, property_selected, !checked); _save_and_update(); - _update_selected_profile(); + _update_profile_tree_from(class_list->get_selected()); } else if (md.get_type() == Variant::INT) { int feature_selected = md; switch (feature_selected) { case CLASS_OPTION_DISABLE_EDITOR: { edited->set_disable_class_editor(class_name, !checked); _save_and_update(); - _update_selected_profile(); + _update_profile_tree_from(class_list->get_selected()); } break; } } } +void EditorFeatureProfileManager::_update_profile_tree_from(TreeItem *p_edited) { + String edited_class = p_edited->get_metadata(0); + + TreeItem *edited_parent = p_edited->get_parent(); + int class_insert_index = p_edited->get_index(); + p_edited->get_parent()->remove_child(p_edited); + + _fill_classes_from(edited_parent, edited_class, edited_class, class_insert_index); +} + void EditorFeatureProfileManager::_update_selected_profile() { String class_selected; int feature_selected = -1; diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h index 2fa6ae9813..7458a04e19 100644 --- a/editor/editor_feature_profile.h +++ b/editor/editor_feature_profile.h @@ -147,7 +147,8 @@ class EditorFeatureProfileManager : public AcceptDialog { String current_profile; void _update_profile_list(const String &p_select_profile = String()); void _update_selected_profile(); - void _fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected); + void _update_profile_tree_from(TreeItem *p_edited); + void _fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected, int p_class_insert_index = -1); Ref<EditorFeatureProfile> current; Ref<EditorFeatureProfile> edited; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index bee1275d64..3adff84e40 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -297,20 +297,20 @@ void EditorFileSystem::_scan_filesystem() { } } - String update_cache = EditorPaths::get_singleton()->get_project_settings_dir().path_join("filesystem_update4"); - - if (FileAccess::exists(update_cache)) { + const String update_cache = EditorPaths::get_singleton()->get_project_settings_dir().path_join("filesystem_update4"); + if (first_scan && FileAccess::exists(update_cache)) { { Ref<FileAccess> f2 = FileAccess::open(update_cache, FileAccess::READ); String l = f2->get_line().strip_edges(); while (!l.is_empty()) { - file_cache.erase(l); //erase cache for this, so it gets updated + dep_update_list.insert(l); + file_cache.erase(l); // Erase cache for this, so it gets updated. l = f2->get_line().strip_edges(); } } - Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - d->remove(update_cache); //bye bye update cache + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES); + d->remove(update_cache); // Bye bye update cache. } EditorProgressBG scan_progress("efs", "ScanFS", 1000); @@ -326,6 +326,7 @@ void EditorFileSystem::_scan_filesystem() { Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES); d->change_dir("res://"); _scan_new_dir(new_filesystem, d, sp); + dep_update_list.clear(); file_cache.clear(); //clear caches, no longer needed @@ -946,11 +947,14 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAc fi->import_modified_time = 0; fi->import_valid = true; - if (ClassDB::is_parent_class(fi->type, SNAME("Script"))) { - _queue_update_script_class(path); - } - if (fi->type == SNAME("PackedScene")) { - _queue_update_scene_groups(path); + // Files in dep_update_list are forced for rescan to update dependencies. They don't need other updates. + if (!dep_update_list.has(path)) { + if (ClassDB::is_parent_class(fi->type, SNAME("Script"))) { + _queue_update_script_class(path); + } + if (fi->type == SNAME("PackedScene")) { + _queue_update_scene_groups(path); + } } } } diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index ad0e3f10ef..84ae1e182c 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -197,6 +197,7 @@ class EditorFileSystem : public Node { }; HashMap<String, FileCache> file_cache; + HashSet<String> dep_update_list; struct ScanProgress { float low = 0; diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index a8d978c66d..9884241708 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1967,7 +1967,7 @@ void EditorHelp::_update_doc() { class_desc->add_text(argument.name); class_desc->add_text(": "); - _add_type(argument.type); + _add_type(argument.type, argument.enumeration, argument.is_bitfield); if (!argument.default_value.is_empty()) { class_desc->push_color(theme_cache.symbol_color); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 8e608fb797..7c5e3877f2 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -2727,6 +2727,8 @@ void EditorInspector::update_tree() { // TODO: Can be useful to store more context for the focusable, such as the caret position in LineEdit. StringName current_selected = property_selected; int current_focusable = -1; + // Temporarily disable focus following to avoid jumping while the inspector is updating. + set_follow_focus(false); if (property_focusable != -1) { // Check that focusable is actually focusable. @@ -3482,6 +3484,7 @@ void EditorInspector::update_tree() { // Updating inspector might invalidate some editing owners. EditorNode::get_singleton()->hide_unused_editors(); } + set_follow_focus(true); } void EditorInspector::update_property(const String &p_prop) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 26110fbe51..949916c2a3 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3533,7 +3533,6 @@ void EditorNode::add_extension_editor_plugin(const StringName &p_class_name) { EditorPlugin *plugin = Object::cast_to<EditorPlugin>(ClassDB::instantiate(p_class_name)); singleton->editor_data.add_extension_editor_plugin(p_class_name, plugin); add_editor_plugin(plugin); - plugin->enable_plugin(); } void EditorNode::remove_extension_editor_plugin(const StringName &p_class_name) { @@ -6298,6 +6297,14 @@ EditorNode::EditorNode() { EditorSettings::create(); } + ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::L); + ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::L); + ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::G); + ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::G); + + // Used in the GPUParticles/CPUParticles 2D/3D editor plugins. + ED_SHORTCUT("particles/restart_emission", TTR("Restart Emission"), KeyModifierMask::CTRL | Key::R); + FileAccess::set_backup_save(EDITOR_GET("filesystem/on_save/safe_save_on_backup_then_rename")); _update_vsync_mode(); @@ -7273,9 +7280,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) { - EditorPlugin *plugin = EditorPlugins::create(i); - add_editor_plugin(plugin); - plugin->enable_plugin(); + add_editor_plugin(EditorPlugins::create(i)); } for (const StringName &extension_class_name : GDExtensionEditorPlugins::get_extension_classes()) { diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index 4eb3f07c47..5c50231623 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -35,7 +35,6 @@ #include "editor/export/editor_export.h" #include "editor/export/editor_export_platform.h" #include "editor/themes/editor_scale.h" -#include "scene/resources/image_texture.h" void EditorRunNative::_notification(int p_what) { switch (p_what) { @@ -49,17 +48,26 @@ void EditorRunNative::_notification(int p_what) { if (changed) { PopupMenu *popup = remote_debug->get_popup(); popup->clear(); - for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) { - Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(i); + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i); + Ref<EditorExportPlatform> eep = preset->get_platform(); if (eep.is_null()) { continue; } + int platform_idx = -1; + for (int j = 0; j < EditorExport::get_singleton()->get_export_platform_count(); j++) { + if (eep->get_name() == EditorExport::get_singleton()->get_export_platform(j)->get_name()) { + platform_idx = j; + } + } int dc = MIN(eep->get_options_count(), 9000); - if (dc > 0) { + bool needs_templates; + String error; + if (dc > 0 && preset->is_runnable() && eep->can_export(preset, error, needs_templates)) { popup->add_icon_item(eep->get_run_icon(), eep->get_name(), -1); popup->set_item_disabled(-1, true); for (int j = 0; j < dc; j++) { - popup->add_icon_item(eep->get_option_icon(j), eep->get_option_label(j), 10000 * i + j); + popup->add_icon_item(eep->get_option_icon(j), eep->get_option_label(j), 10000 * platform_idx + j); popup->set_item_tooltip(-1, eep->get_option_tooltip(j)); popup->set_item_indent(-1, 2); } diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index 2c6c13401f..b1f3875175 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -30,14 +30,13 @@ #include "editor_settings_dialog.h" -#include "core/config/project_settings.h" #include "core/input/input_map.h" #include "core/os/keyboard.h" #include "editor/debugger/editor_debugger_node.h" -#include "editor/editor_file_system.h" #include "editor/editor_log.h" #include "editor/editor_node.h" #include "editor/editor_property_name_processor.h" +#include "editor/editor_sectioned_inspector.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" @@ -45,7 +44,9 @@ #include "editor/input_event_configuration_dialog.h" #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" -#include "scene/gui/margin_container.h" +#include "scene/gui/panel_container.h" +#include "scene/gui/tab_container.h" +#include "scene/gui/texture_rect.h" void EditorSettingsDialog::ok_pressed() { if (!EditorSettings::get_singleton()) { @@ -499,6 +500,9 @@ void EditorSettingsDialog::_update_shortcuts() { memdelete(section); } } + + // Update UI. + clear_all_search->set_disabled(shortcut_search_box->get_text().is_empty() && shortcut_search_by_event->get_event().is_null()); } void EditorSettingsDialog::_shortcut_button_pressed(Object *p_item, int p_column, int p_idx, MouseButton p_button) { @@ -782,8 +786,9 @@ EditorSettingsDialog::EditorSettingsDialog() { shortcut_search_by_event->connect(SceneStringName(focus_exited), callable_mp((AcceptDialog *)this, &AcceptDialog::set_close_on_escape).bind(true)); top_hbox->add_child(shortcut_search_by_event); - Button *clear_all_search = memnew(Button); + clear_all_search = memnew(Button); clear_all_search->set_text(TTR("Clear All")); + clear_all_search->set_tooltip_text(TTR("Clear all search filters.")); clear_all_search->connect(SceneStringName(pressed), callable_mp(shortcut_search_box, &LineEdit::clear)); clear_all_search->connect(SceneStringName(pressed), callable_mp(shortcut_search_by_event, &EventListenerLineEdit::clear_event)); top_hbox->add_child(clear_all_search); diff --git a/editor/editor_settings_dialog.h b/editor/editor_settings_dialog.h index fdfc0a43ec..cab8fe9da1 100644 --- a/editor/editor_settings_dialog.h +++ b/editor/editor_settings_dialog.h @@ -32,13 +32,14 @@ #define EDITOR_SETTINGS_DIALOG_H #include "editor/action_map_editor.h" -#include "editor/editor_inspector.h" -#include "editor/editor_sectioned_inspector.h" #include "scene/gui/dialogs.h" -#include "scene/gui/panel_container.h" -#include "scene/gui/rich_text_label.h" -#include "scene/gui/tab_container.h" -#include "scene/gui/texture_rect.h" + +class PanelContainer; +class SectionedInspector; +class TabContainer; +class TextureRect; +class Tree; +class TreeItem; class EditorSettingsDialog : public AcceptDialog { GDCLASS(EditorSettingsDialog, AcceptDialog); @@ -53,6 +54,7 @@ class EditorSettingsDialog : public AcceptDialog { LineEdit *shortcut_search_box = nullptr; EventListenerLineEdit *shortcut_search_by_event = nullptr; SectionedInspector *inspector = nullptr; + Button *clear_all_search = nullptr; // Shortcuts enum ShortcutButton { diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index c6a671b7f9..dd283ebfd6 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -131,7 +131,7 @@ void ProjectExportDialog::popup_export() { if (saved_size != Rect2()) { popup(saved_size); } else { - popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8); + popup_centered_clamped(Size2(900, 500) * EDSCALE, 0.7); } } @@ -1311,9 +1311,15 @@ ProjectExportDialog::ProjectExportDialog() { // Resources export parameters. + ScrollContainer *resources_scroll_container = memnew(ScrollContainer); + resources_scroll_container->set_name(TTR("Resources")); + resources_scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); + sections->add_child(resources_scroll_container); + VBoxContainer *resources_vb = memnew(VBoxContainer); - sections->add_child(resources_vb); - resources_vb->set_name(TTR("Resources")); + resources_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + resources_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL); + resources_scroll_container->add_child(resources_vb); export_filter = memnew(OptionButton); export_filter->add_item(TTR("Export all resources in the project")); @@ -1332,6 +1338,7 @@ ProjectExportDialog::ProjectExportDialog() { resources_vb->add_child(include_margin); include_files = memnew(Tree); + include_files->set_custom_minimum_size(Size2(1, 75 * EDSCALE)); include_margin->add_child(include_files); include_files->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); include_files->connect("item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed)); @@ -1384,18 +1391,25 @@ ProjectExportDialog::ProjectExportDialog() { VBoxContainer *feature_vb = memnew(VBoxContainer); feature_vb->set_name(TTR("Features")); + feature_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); custom_features = memnew(LineEdit); custom_features->connect("text_changed", callable_mp(this, &ProjectExportDialog::_custom_features_changed)); feature_vb->add_margin_child(TTR("Custom (comma-separated):"), custom_features); custom_feature_display = memnew(RichTextLabel); + custom_feature_display->set_custom_minimum_size(Size2(1, 75 * EDSCALE)); custom_feature_display->set_v_size_flags(Control::SIZE_EXPAND_FILL); feature_vb->add_margin_child(TTR("Feature List:"), custom_feature_display, true); sections->add_child(feature_vb); // Encryption export parameters. + ScrollContainer *sec_scroll_container = memnew(ScrollContainer); + sec_scroll_container->set_name(TTR("Encryption")); + sec_scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); + VBoxContainer *sec_vb = memnew(VBoxContainer); - sec_vb->set_name(TTR("Encryption")); + sec_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + sec_scroll_container->add_child(sec_vb); enc_pck = memnew(CheckButton); enc_pck->connect("toggled", callable_mp(this, &ProjectExportDialog::_enc_pck_changed)); @@ -1426,7 +1440,7 @@ ProjectExportDialog::ProjectExportDialog() { script_key_error->add_theme_color_override("font_color", EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("error_color"), EditorStringName(Editor))); sec_vb->add_margin_child(TTR("Encryption Key (256-bits as hexadecimal):"), script_key); sec_vb->add_child(script_key_error); - sections->add_child(sec_vb); + sections->add_child(sec_scroll_container); Label *sec_info = memnew(Label); sec_info->set_text(TTR("Note: Encryption key needs to be stored in the binary,\nyou need to build the export templates from source.")); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index b05cafa694..5f311ae445 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -79,6 +79,15 @@ Control *FileSystemList::make_custom_tooltip(const String &p_text) const { } void FileSystemList::_line_editor_submit(const String &p_text) { + if (popup_edit_commited) { + return; // Already processed by _text_editor_popup_modal_close + } + + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { + return; // ESC pressed, app focus lost, or forced close from code. + } + + popup_edit_commited = true; // End edit popup processing. popup_editor->hide(); emit_signal(SNAME("item_edited")); @@ -127,6 +136,7 @@ bool FileSystemList::edit_selected() { line_editor->set_text(name); line_editor->select(0, name.rfind(".")); + popup_edit_commited = false; // Start edit popup processing. popup_editor->popup(); popup_editor->child_controls_changed(); line_editor->grab_focus(); @@ -138,8 +148,12 @@ String FileSystemList::get_edit_text() { } void FileSystemList::_text_editor_popup_modal_close() { + if (popup_edit_commited) { + return; // Already processed by _text_editor_popup_modal_close + } + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { - return; + return; // ESC pressed, app focus lost, or forced close from code. } _line_editor_submit(line_editor->get_text()); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 7449657c06..3fbff3ef19 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -59,6 +59,7 @@ class FileSystemTree : public Tree { class FileSystemList : public ItemList { GDCLASS(FileSystemList, ItemList); + bool popup_edit_commited = true; VBoxContainer *popup_editor_vb = nullptr; Popup *popup_editor = nullptr; LineEdit *line_editor = nullptr; diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index a06dd310ea..fa0dad41dc 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -1368,22 +1368,35 @@ bool SceneTreeEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_d } bool scene_drop = true; + bool audio_drop = true; for (int i = 0; i < files.size(); i++) { String ftype = EditorFileSystem::get_singleton()->get_file_type(files[i]); if (ftype != "PackedScene") { scene_drop = false; - break; + } + if (audio_drop && !ClassDB::is_parent_class(ftype, "AudioStream")) { + audio_drop = false; } } if (scene_drop) { tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM); - } else { + return true; + } + + if (audio_drop) { if (files.size() > 1) { - return false; + tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); + } else { + tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM); } - tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); + return true; + } + + if (files.size() > 1) { + return false; } + tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); return true; } diff --git a/editor/icons/AudioStreamPlayer.svg b/editor/icons/AudioStreamPlayer.svg index 317b25d3e9..ba84502d99 100644 --- a/editor/icons/AudioStreamPlayer.svg +++ b/editor/icons/AudioStreamPlayer.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientUnits="userSpaceOnUse" x2="0" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="M9 14a1 1 0 0 0 1.5.85l4-2.511a1 1 0 0 0 0-1.724l-4-2.511a1 1 0 0 0-1.5.85z" fill="#e0e0e0"/><path d="M13 2a1 1 0 0 0-1-1L4.754 3A1 1 0 0 0 4 4v5.55A2.5 2.5 0 1 0 6 12V4.756l5-1.428V6.5l2-1z" fill="url(#a)"/></svg> +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path d="M1.382.335.777.858H.204v.673h.564l.614.531Z" style="fill:#e0e0e0;stroke:#e0e0e0;stroke-width:.1764;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"/><path d="M1.718.572c.275.374.275.882 0 1.256M1.947.343c.402.5.402 1.213 0 1.714" style="fill:none;stroke:#e0e0e0;stroke-width:.176328;stroke-linecap:round;stroke-opacity:1"/></svg>
\ No newline at end of file diff --git a/editor/icons/AudioStreamPlayer2D.svg b/editor/icons/AudioStreamPlayer2D.svg index 87beccd19a..54ff128a4d 100644 --- a/editor/icons/AudioStreamPlayer2D.svg +++ b/editor/icons/AudioStreamPlayer2D.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientUnits="userSpaceOnUse" x2="0" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="M9 14a1 1 0 0 0 1.5.85l4-2.511a1 1 0 0 0 0-1.724l-4-2.511a1 1 0 0 0-1.5.85z" fill="#8da5f3"/><path d="M13 2a1 1 0 0 0-1-1L4.754 3A1 1 0 0 0 4 4v5.55A2.5 2.5 0 1 0 6 12V4.756l5-1.428V6.5l2-1z" fill="url(#a)"/></svg> +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path d="M1.382.335.777.858H.204v.673h.564l.614.531Z" style="fill:#8da5f3;stroke:#8da5f3;stroke-width:.1764;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"/><path d="M1.718.572c.275.374.275.882 0 1.256M1.947.343c.402.5.402 1.213 0 1.714" style="fill:none;stroke:#8da5f3;stroke-width:.176328;stroke-linecap:round;stroke-opacity:1"/></svg>
\ No newline at end of file diff --git a/editor/icons/AudioStreamPlayer3D.svg b/editor/icons/AudioStreamPlayer3D.svg index 440a5e52ef..fc0231a657 100644 --- a/editor/icons/AudioStreamPlayer3D.svg +++ b/editor/icons/AudioStreamPlayer3D.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" gradientUnits="userSpaceOnUse" x2="0" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="M9 14a1 1 0 0 0 1.5.85l4-2.511a1 1 0 0 0 0-1.724l-4-2.511a1 1 0 0 0-1.5.85z" fill="#fc7f7f"/><path d="M13 2a1 1 0 0 0-1-1L4.754 3A1 1 0 0 0 4 4v5.55A2.5 2.5 0 1 0 6 12V4.756l5-1.428V6.5l2-1z" fill="url(#a)"/></svg> +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path d="M1.382.335.777.858H.204v.673h.564l.614.531Z" style="fill:#fc7f7f;stroke:#fc7f7f;stroke-width:.1764;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"/><path d="M1.718.572c.275.374.275.882 0 1.256M1.947.343c.402.5.402 1.213 0 1.714" style="fill:none;stroke:#fc7f7f;stroke-width:.176328;stroke-linecap:round;stroke-opacity:1"/></svg>
\ No newline at end of file diff --git a/editor/icons/SCsub b/editor/icons/SCsub index d2d752cff4..0d9ac43c46 100644 --- a/editor/icons/SCsub +++ b/editor/icons/SCsub @@ -3,8 +3,8 @@ Import("env") import os -import editor_icons_builders +import editor_icons_builders env["BUILDERS"]["MakeEditorIconsBuilder"] = Builder( action=env.Run(editor_icons_builders.make_editor_icons_action), diff --git a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp index 3eabe46950..b69d38afa0 100644 --- a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp @@ -204,7 +204,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } else { for (int j = 0; j < key_len; j++) { Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j))); - anim->track_set_key_value(i, j, (global_transform.basis * sc).get_scale()); + anim->track_set_key_value(i, j, (global_transform.orthonormalized().basis * sc).get_scale()); } } } diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index 070c44419a..c0d38af26a 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -279,7 +279,7 @@ bool ResourceImporterScene::get_option_visibility(const String &p_path, const St } } - if (animation_importer && (p_option.begins_with("nodes/") || p_option.begins_with("meshes/") || p_option.begins_with("skins/"))) { + if (animation_importer && (p_option == "nodes/root_type" || p_option == "nodes/root_name" || p_option.begins_with("meshes/") || p_option.begins_with("skins/"))) { return false; // Nothing to do here for animations. } @@ -2329,6 +2329,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import } script_ext_hint += "*." + E; } + bool trimming_defaults_on = p_path.get_extension().to_lower() == "fbx"; r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0)); @@ -2342,7 +2343,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), trimming_defaults_on)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/remove_immutable_tracks"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import_rest_as_RESET"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), "")); diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp index c454ebc6eb..94e5dd0f12 100644 --- a/editor/import/resource_importer_imagefont.cpp +++ b/editor/import/resource_importer_imagefont.cpp @@ -158,17 +158,16 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin c++; // Skip "+". continue; } - } else if (range[c] == '0') { - if ((c <= range.length() - 2) && range[c + 1] == 'x') { - token = String(); - if (step == STEP_START_BEGIN) { - step = STEP_START_READ_HEX; - } else { - step = STEP_END_READ_HEX; - } - c++; // Skip "x". - continue; + } else if (range[c] == '0' && (c <= range.length() - 2) && range[c + 1] == 'x') { + // Read hexadecimal value, start. + token = String(); + if (step == STEP_START_BEGIN) { + step = STEP_START_READ_HEX; + } else { + step = STEP_END_READ_HEX; } + c++; // Skip "x". + continue; } else if (range[c] == '\'' || range[c] == '\"') { if ((c <= range.length() - 3) && (range[c + 2] == '\'' || range[c + 2] == '\"')) { token = String(); @@ -184,14 +183,13 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin } } else if (is_digit(range[c])) { // Read decimal value, start. - c++; token = String(); + token += range[c]; if (step == STEP_START_BEGIN) { step = STEP_START_READ_DEC; } else { step = STEP_END_READ_DEC; } - token += range[c]; continue; } [[fallthrough]]; @@ -254,9 +252,19 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin } break; } } + if (step == STEP_START_READ_HEX) { + start = token.hex_to_int(); + } else if (step == STEP_START_READ_DEC) { + start = token.to_int(); + } else if (step == STEP_END_READ_HEX) { + end = token.hex_to_int(); + } else if (step == STEP_END_READ_DEC) { + end = token.to_int(); + } if (end == -1) { end = start; } + if (start == -1) { WARN_PRINT(vformat("Invalid range: \"%s\"", range)); continue; diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 8cf104725a..487b8fc175 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -269,9 +269,9 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { Vector<uint8_t> data; if (use_webp) { - data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i)); + data = Image::webp_lossless_packer(i ? p_image->get_image_from_mipmap(i) : p_image); } else { - data = Image::png_packer(p_image->get_image_from_mipmap(i)); + data = Image::png_packer(i ? p_image->get_image_from_mipmap(i) : p_image); } int data_len = data.size(); f->store_32(data_len); @@ -289,7 +289,7 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I f->store_32(p_image->get_format()); for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { - Vector<uint8_t> data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality); + Vector<uint8_t> data = Image::webp_lossy_packer(i ? p_image->get_image_from_mipmap(i) : p_image, p_lossy_quality); int data_len = data.size(); f->store_32(data_len); diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index fbe76e1d5c..2d87e6592f 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -188,10 +188,20 @@ void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_ params->checked.clear(); params->base_options_path = p_path; + HashMap<StringName, Variant> import_options; + List<String> section_keys; + p_config->get_section_keys("params", §ion_keys); + for (const String §ion_key : section_keys) { + import_options[section_key] = p_config->get_value("params", section_key); + } + if (params->importer.is_valid()) { + params->importer->handle_compatibility_options(import_options); + } + for (const ResourceImporter::ImportOption &E : options) { params->properties.push_back(E.option); - if (p_config.is_valid() && p_config->has_section_key("params", E.option.name)) { - params->values[E.option.name] = p_config->get_value("params", E.option.name); + if (p_config.is_valid() && import_options.has(E.option.name)) { + params->values[E.option.name] = import_options[E.option.name]; } else { params->values[E.option.name] = E.default_value; } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 731c89250e..ee96de8f23 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -46,10 +46,12 @@ #include "editor/scene_tree_dock.h" #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" +#include "scene/2d/audio_stream_player_2d.h" #include "scene/2d/polygon_2d.h" #include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite_2d.h" #include "scene/2d/touch_screen_button.h" +#include "scene/gui/base_button.h" #include "scene/gui/flow_container.h" #include "scene/gui/grid_container.h" #include "scene/gui/separator.h" @@ -966,7 +968,7 @@ void CanvasItemEditor::_add_node_pressed(int p_result) { } } -void CanvasItemEditor::_node_created(Node *p_node) { +void CanvasItemEditor::_adjust_new_node_position(Node *p_node) { if (node_create_position == Point2()) { return; } @@ -5161,7 +5163,7 @@ CanvasItemEditor::CanvasItemEditor() { editor_selection->connect("selection_changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); editor_selection->connect("selection_changed", callable_mp(this, &CanvasItemEditor::_selection_changed)); - SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created)); + SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_adjust_new_node_position)); SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position)); // Add some margin to the sides for better esthetics. @@ -5417,7 +5419,7 @@ CanvasItemEditor::CanvasItemEditor() { lock_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(LOCK_SELECTED)); lock_button->set_tooltip_text(TTR("Lock selected node, preventing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - lock_button->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::L)); + lock_button->set_shortcut(ED_GET_SHORTCUT("editor/lock_selected_nodes")); unlock_button = memnew(Button); unlock_button->set_theme_type_variation("FlatButton"); @@ -5425,7 +5427,7 @@ CanvasItemEditor::CanvasItemEditor() { unlock_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(UNLOCK_SELECTED)); unlock_button->set_tooltip_text(TTR("Unlock selected node, allowing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - unlock_button->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::L)); + unlock_button->set_shortcut(ED_GET_SHORTCUT("editor/unlock_selected_nodes")); group_button = memnew(Button); group_button->set_theme_type_variation("FlatButton"); @@ -5433,7 +5435,7 @@ CanvasItemEditor::CanvasItemEditor() { group_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(GROUP_SELECTED)); group_button->set_tooltip_text(TTR("Groups the selected node with its children. This causes the parent to be selected when any child node is clicked in 2D and 3D view.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - group_button->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::G)); + group_button->set_shortcut(ED_GET_SHORTCUT("editor/group_selected_nodes")); ungroup_button = memnew(Button); ungroup_button->set_theme_type_variation("FlatButton"); @@ -5441,7 +5443,7 @@ CanvasItemEditor::CanvasItemEditor() { ungroup_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_popup_callback).bind(UNGROUP_SELECTED)); ungroup_button->set_tooltip_text(TTR("Ungroups the selected node from its children. Child nodes will be individual items in 2D and 3D view.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - ungroup_button->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::G)); + ungroup_button->set_shortcut(ED_GET_SHORTCUT("editor/ungroup_selected_nodes")); main_menu_hbox->add_child(memnew(VSeparator)); @@ -5697,15 +5699,15 @@ CanvasItemEditorPlugin::~CanvasItemEditorPlugin() { } void CanvasItemEditorViewport::_on_mouse_exit() { - if (!selector->is_visible()) { + if (!texture_node_type_selector->is_visible()) { _remove_preview(); } } -void CanvasItemEditorViewport::_on_select_type(Object *selected) { +void CanvasItemEditorViewport::_on_select_texture_node_type(Object *selected) { CheckBox *check = Object::cast_to<CheckBox>(selected); String type = check->get_text(); - selector->set_title(vformat(TTR("Add %s"), type)); + texture_node_type_selector->set_title(vformat(TTR("Add %s"), type)); label->set_text(vformat(TTR("Adding %s..."), type)); } @@ -5717,7 +5719,7 @@ void CanvasItemEditorViewport::_on_change_type_confirmed() { CheckBox *check = Object::cast_to<CheckBox>(button_group->get_pressed_button()); default_texture_node_type = check->get_text(); _perform_drop_data(); - selector->hide(); + texture_node_type_selector->hide(); } void CanvasItemEditorViewport::_on_change_type_closed() { @@ -5728,36 +5730,35 @@ void CanvasItemEditorViewport::_create_preview(const Vector<String> &files) cons bool add_preview = false; for (int i = 0; i < files.size(); i++) { Ref<Resource> res = ResourceLoader::load(files[i]); - ERR_FAIL_COND(res.is_null()); - Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res)); - Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res)); - if (texture != nullptr || scene != nullptr) { - bool root_node_selected = EditorNode::get_singleton()->get_editor_selection()->is_selected(EditorNode::get_singleton()->get_edited_scene()); - String desc = TTR("Drag and drop to add as child of selected node.") + "\n" + TTR("Hold Alt when dropping to add as child of root node."); - if (!root_node_selected) { - desc += "\n" + TTR("Hold Shift when dropping to add as sibling of selected node."); - } - if (texture != nullptr) { - Sprite2D *sprite = memnew(Sprite2D); - sprite->set_texture(texture); - sprite->set_modulate(Color(1, 1, 1, 0.7f)); - preview_node->add_child(sprite); - label->show(); - label_desc->show(); - desc += "\n" + TTR("Hold Alt + Shift when dropping to add as a different node type."); - label_desc->set_text(desc); - } else { - if (scene.is_valid()) { - Node *instance = scene->instantiate(); - if (instance) { - preview_node->add_child(instance); - label_desc->show(); - label_desc->set_text(desc); - } - } + ERR_CONTINUE(res.is_null()); + + Ref<Texture2D> texture = res; + if (texture.is_valid()) { + Sprite2D *sprite = memnew(Sprite2D); + sprite->set_texture(texture); + sprite->set_modulate(Color(1, 1, 1, 0.7f)); + preview_node->add_child(sprite); + add_preview = true; + } + + Ref<PackedScene> scene = res; + if (scene.is_valid()) { + Node *instance = scene->instantiate(); + if (instance) { + preview_node->add_child(instance); } add_preview = true; } + + Ref<AudioStream> audio = res; + if (audio.is_valid()) { + Sprite2D *sprite = memnew(Sprite2D); + sprite->set_texture(get_editor_theme_icon(SNAME("AudioStreamPlayer2D"))); + sprite->set_modulate(Color(1, 1, 1, 0.7f)); + sprite->set_position(Vector2(0, -sprite->get_texture()->get_size().height) * EDSCALE); + preview_node->add_child(sprite); + add_preview = true; + } } if (add_preview) { @@ -5798,44 +5799,46 @@ bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String &p_targe return false; } -void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point) { +void CanvasItemEditorViewport::_create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point) { // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others. - String name = path.get_file().get_basename(); - child->set_name(Node::adjust_name_casing(name)); + const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename()); + if (!node_name.is_empty()) { + p_child->set_name(node_name); + } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - Ref<Texture2D> texture = ResourceCache::get_ref(path); + Ref<Texture2D> texture = ResourceCache::get_ref(p_path); - if (parent) { - undo_redo->add_do_method(parent, "add_child", child, true); - undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); - undo_redo->add_do_reference(child); - undo_redo->add_undo_method(parent, "remove_child", child); + if (p_parent) { + undo_redo->add_do_method(p_parent, "add_child", p_child, true); + undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_reference(p_child); + undo_redo->add_undo_method(p_parent, "remove_child", p_child); } else { // If no parent is selected, set as root node of the scene. - undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child); - undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); - undo_redo->add_do_reference(child); + undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_child); + undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_reference(p_child); undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); } - if (parent) { - String new_name = parent->validate_child_name(child); + if (p_parent) { + String new_name = p_parent->validate_child_name(p_child); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), child->get_class(), new_name); - undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_child->get_class(), new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name)); } - if (Object::cast_to<TouchScreenButton>(child) || Object::cast_to<TextureButton>(child)) { - undo_redo->add_do_property(child, "texture_normal", texture); + if (Object::cast_to<TouchScreenButton>(p_child) || Object::cast_to<TextureButton>(p_child)) { + undo_redo->add_do_property(p_child, "texture_normal", texture); } else { - undo_redo->add_do_property(child, "texture", texture); + undo_redo->add_do_property(p_child, "texture", texture); } // make visible for certain node type - if (Object::cast_to<Control>(child)) { + if (Object::cast_to<Control>(p_child)) { Size2 texture_size = texture->get_size(); - undo_redo->add_do_property(child, "size", texture_size); - } else if (Object::cast_to<Polygon2D>(child)) { + undo_redo->add_do_property(p_child, "size", texture_size); + } else if (Object::cast_to<Polygon2D>(p_child)) { Size2 texture_size = texture->get_size(); Vector<Vector2> list = { Vector2(0, 0), @@ -5843,7 +5846,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & Vector2(texture_size.width, texture_size.height), Vector2(0, texture_size.height) }; - undo_redo->add_do_property(child, "polygon", list); + undo_redo->add_do_property(p_child, "polygon", list); } // Compute the global position @@ -5851,21 +5854,68 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & Point2 target_position = xform.affine_inverse().xform(p_point); // Adjust position for Control and TouchScreenButton - if (Object::cast_to<Control>(child) || Object::cast_to<TouchScreenButton>(child)) { + if (Object::cast_to<Control>(p_child) || Object::cast_to<TouchScreenButton>(p_child)) { target_position -= texture->get_size() / 2; } - // there's nothing to be used as source position so snapping will work as absolute if enabled + // There's nothing to be used as source position, so snapping will work as absolute if enabled. + target_position = canvas_item_editor->snap_point(target_position); + + CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent); + Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position; + + undo_redo->add_do_method(p_child, "set_position", local_target_pos); +} + +void CanvasItemEditorViewport::_create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point) { + AudioStreamPlayer2D *child = memnew(AudioStreamPlayer2D); + child->set_stream(ResourceCache::get_ref(p_path)); + + // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others. + const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename()); + if (!node_name.is_empty()) { + child->set_name(node_name); + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + + if (p_parent) { + undo_redo->add_do_method(p_parent, "add_child", child, true); + undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_reference(child); + undo_redo->add_undo_method(p_parent, "remove_child", child); + } else { // If no parent is selected, set as root node of the scene. + undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child); + undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_reference(child); + undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); + } + + if (p_parent) { + String new_name = p_parent->validate_child_name(child); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), child->get_class(), new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name)); + } + + // Compute the global position + Transform2D xform = canvas_item_editor->get_canvas_transform(); + Point2 target_position = xform.affine_inverse().xform(p_point); + + // There's nothing to be used as source position, so snapping will work as absolute if enabled. target_position = canvas_item_editor->snap_point(target_position); - CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent); + CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent); Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position; undo_redo->add_do_method(child, "set_position", local_target_pos); + + EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); + undo_redo->add_do_method(editor_selection, "add_node", child); } -bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) { - Ref<PackedScene> sdata = ResourceLoader::load(path); +bool CanvasItemEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) { + Ref<PackedScene> sdata = ResourceLoader::load(p_path); if (!sdata.is_valid()) { // invalid scene return false; } @@ -5884,27 +5934,27 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons } } - instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path)); + instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_path)); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); - undo_redo->add_do_method(parent, "add_child", instantiated_scene, true); + undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true); undo_redo->add_do_method(instantiated_scene, "set_owner", edited_scene); undo_redo->add_do_reference(instantiated_scene); - undo_redo->add_undo_method(parent, "remove_child", instantiated_scene); + undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene); undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene); - String new_name = parent->validate_child_name(instantiated_scene); + String new_name = p_parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(parent), path, new_name); - undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name)); + undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(p_parent), p_path, new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)) + "/" + new_name)); CanvasItem *instance_ci = Object::cast_to<CanvasItem>(instantiated_scene); if (instance_ci) { Vector2 target_pos = canvas_item_editor->get_canvas_transform().affine_inverse().xform(p_point); target_pos = canvas_item_editor->snap_point(target_pos); - CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent); + CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_parent); if (parent_ci) { target_pos = parent_ci->get_global_transform_with_canvas().affine_inverse().xform(target_pos); } @@ -5923,12 +5973,8 @@ void CanvasItemEditorViewport::_perform_drop_data() { _remove_preview(); if (!target_node) { - // Without root dropping multiple files is not allowed - if (selected_files.size() > 1) { - accept->set_text(TTR("Cannot instantiate multiple nodes without root.")); - accept->popup_centered(); - return; - } + // Should already be handled by `can_drop_data`. + ERR_FAIL_COND_MSG(selected_files.size() > 1, "Can't instantiate multiple nodes without root."); const String &path = selected_files[0]; Ref<Resource> res = ResourceLoader::load(path); @@ -5974,9 +6020,14 @@ void CanvasItemEditorViewport::_perform_drop_data() { Ref<Texture2D> texture = res; if (texture.is_valid()) { Node *child = Object::cast_to<Node>(ClassDB::instantiate(default_texture_node_type)); - _create_nodes(target_node, child, path, drop_pos); + _create_texture_node(target_node, child, path, drop_pos); undo_redo->add_do_method(editor_selection, "add_node", child); } + + Ref<AudioStream> audio = res; + if (audio.is_valid()) { + _create_audio_node(target_node, path, drop_pos); + } } undo_redo->commit_action(); @@ -5995,71 +6046,108 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian } Vector<String> files = d["files"]; - bool can_instantiate = false; - bool is_cyclical_dep = false; - String error_file; - // Check if at least one of the dragged files is a texture or scene. - for (int i = 0; i < files.size(); i++) { - bool is_scene = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "PackedScene"); - bool is_texture = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Texture2D"); + const Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + if (!edited_scene && files.size() > 1) { + canvas_item_editor->message = TTR("Can't instantiate multiple nodes without root."); + canvas_item_editor->update_viewport(); + return false; + } + + enum { + SCENE = 1 << 0, + TEXTURE = 1 << 1, + AUDIO = 1 << 2, + }; + int instantiate_type = 0; + + for (const String &path : files) { + const String &res_type = ResourceLoader::get_resource_type(path); + String error_message; + + if (ClassDB::is_parent_class(res_type, "PackedScene")) { + Ref<PackedScene> scn = ResourceLoader::load(path); + ERR_CONTINUE(scn.is_null()); - if (is_scene || is_texture) { - Ref<Resource> res = ResourceLoader::load(files[i]); - if (res.is_null()) { + Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); + if (!instantiated_scene) { continue; } - Ref<PackedScene> scn = res; - if (scn.is_valid()) { - Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); - if (!instantiated_scene) { - continue; - } - - Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); - if (edited_scene && !edited_scene->get_scene_file_path().is_empty() && _cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) { - memdelete(instantiated_scene); - can_instantiate = false; - is_cyclical_dep = true; - error_file = files[i].get_file(); - break; - } - memdelete(instantiated_scene); + if (edited_scene && !edited_scene->get_scene_file_path().is_empty() && _cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) { + error_message = vformat(TTR("Circular dependency found at %s."), path.get_file()); } - can_instantiate = true; + memdelete(instantiated_scene); + instantiate_type |= SCENE; + } + if (ClassDB::is_parent_class(res_type, "Texture2D")) { + instantiate_type |= TEXTURE; + } + if (ClassDB::is_parent_class(res_type, "AudioStream")) { + instantiate_type |= AUDIO; + } + + if (!error_message.is_empty()) { + // TRANSLATORS: The placeholder is the error message. + canvas_item_editor->message = vformat(TTR("Can't instantiate: %s"), error_message); + canvas_item_editor->update_viewport(); + return false; } } - if (is_cyclical_dep) { - canvas_item_editor->message = vformat(TTR("Can't instantiate: %s."), vformat(TTR("Circular dependency found at %s"), error_file)); - canvas_item_editor->update_viewport(); + if (instantiate_type == 0) { return false; } - if (can_instantiate) { - if (!preview_node->get_parent()) { // create preview only once - _create_preview(files); - } - ERR_FAIL_COND_V(preview_node->get_child_count() == 0, false); - Transform2D trans = canvas_item_editor->get_canvas_transform(); - preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x); + if (!preview_node->get_parent()) { // create preview only once + _create_preview(files); + } + ERR_FAIL_COND_V(preview_node->get_child_count() == 0, false); + + const Transform2D trans = canvas_item_editor->get_canvas_transform(); + preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x); + + if (!edited_scene && instantiate_type & SCENE) { String scene_file_path = preview_node->get_child(0)->get_scene_file_path(); - if (scene_file_path.is_empty() || preview_node->get_tree()->get_edited_scene_root()) { - double snap = EDITOR_GET("interface/inspector/default_float_step"); - int snap_step_decimals = Math::range_step_decimals(snap); + // TRANSLATORS: The placeholder is the file path of the scene being instantiated. + canvas_item_editor->message = vformat(TTR("Creating inherited scene from: %s"), scene_file_path); + } else { + double snap = EDITOR_GET("interface/inspector/default_float_step"); + int snap_step_decimals = Math::range_step_decimals(snap); #define FORMAT(value) (TS->format_number(String::num(value, snap_step_decimals))) - Vector2 preview_node_pos = preview_node->get_global_position(); - canvas_item_editor->message = TTR("Instantiating:") + " (" + FORMAT(preview_node_pos.x) + ", " + FORMAT(preview_node_pos.y) + ") px"; - label->set_text(vformat(TTR("Adding %s..."), default_texture_node_type)); - } else { - canvas_item_editor->message = TTR("Creating inherited scene from: ") + scene_file_path; + Vector2 preview_node_pos = preview_node->get_global_position(); + canvas_item_editor->message = TTR("Instantiating: ") + "(" + FORMAT(preview_node_pos.x) + ", " + FORMAT(preview_node_pos.y) + ") px"; + } + canvas_item_editor->update_viewport(); + + if (instantiate_type & TEXTURE && instantiate_type & AUDIO) { + // TRANSLATORS: The placeholders are the types of nodes being instantiated. + label->set_text(vformat(TTR("Adding %s and %s..."), default_texture_node_type, "AudioStreamPlayer2D")); + } else { + String node_type; + if (instantiate_type & TEXTURE) { + node_type = default_texture_node_type; + } else if (instantiate_type & AUDIO) { + node_type = "AudioStreamPlayer2D"; + } + if (!node_type.is_empty()) { + // TRANSLATORS: The placeholder is the type of node being instantiated. + label->set_text(vformat(TTR("Adding %s..."), node_type)); } + } + label->set_visible(instantiate_type & ~SCENE); - canvas_item_editor->update_viewport(); + String desc = TTR("Drag and drop to add as sibling of selected node (except when root is selected).") + + "\n" + TTR("Hold Shift when dropping to add as child of selected node.") + + "\n" + TTR("Hold Alt when dropping to add as child of root node."); + if (instantiate_type & TEXTURE) { + desc += "\n" + TTR("Hold Alt + Shift when dropping to add as different node type."); } - return can_instantiate; + label_desc->set_text(desc); + label_desc->show(); + + return true; } -void CanvasItemEditorViewport::_show_resource_type_selector() { +void CanvasItemEditorViewport::_show_texture_node_type_selector() { _remove_preview(); List<BaseButton *> btn_list; button_group->get_buttons(&btn_list); @@ -6068,18 +6156,17 @@ void CanvasItemEditorViewport::_show_resource_type_selector() { CheckBox *check = Object::cast_to<CheckBox>(btn); check->set_pressed(check->get_text() == default_texture_node_type); } - selector->set_title(vformat(TTR("Add %s"), default_texture_node_type)); - selector->popup_centered(); + texture_node_type_selector->set_title(vformat(TTR("Add %s"), default_texture_node_type)); + texture_node_type_selector->popup_centered(); } -bool CanvasItemEditorViewport::_only_packed_scenes_selected() const { +bool CanvasItemEditorViewport::_is_any_texture_selected() const { for (int i = 0; i < selected_files.size(); ++i) { - if (ResourceLoader::load(selected_files[i])->get_class() != "PackedScene") { - return false; + if (ClassDB::is_parent_class(ResourceLoader::get_resource_type(selected_files[i]), "Texture2D")) { + return true; } } - - return true; + return false; } void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p_data) { @@ -6099,11 +6186,12 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p Node *root_node = EditorNode::get_singleton()->get_edited_scene(); if (selected_nodes.size() > 0) { Node *selected_node = selected_nodes.front()->get(); - target_node = selected_node; if (is_alt) { target_node = root_node; - } else if (is_shift && selected_node != root_node) { - target_node = selected_node->get_parent(); + } else if (is_shift) { + target_node = selected_node; + } else { // Default behavior. + target_node = (selected_node != root_node) ? selected_node->get_parent() : root_node; } } else { if (root_node) { @@ -6115,8 +6203,8 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p drop_pos = p_point; - if (is_alt && is_shift && !_only_packed_scenes_selected()) { - _show_resource_type_selector(); + if (is_alt && is_shift && _is_any_texture_selected()) { + _show_texture_node_type_selector(); } else { _perform_drop_data(); } @@ -6176,19 +6264,19 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it accept = memnew(AcceptDialog); EditorNode::get_singleton()->get_gui_base()->add_child(accept); - selector = memnew(AcceptDialog); - EditorNode::get_singleton()->get_gui_base()->add_child(selector); - selector->set_title(TTR("Change Default Type")); - selector->connect("confirmed", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_confirmed)); - selector->connect("canceled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed)); + texture_node_type_selector = memnew(AcceptDialog); + EditorNode::get_singleton()->get_gui_base()->add_child(texture_node_type_selector); + texture_node_type_selector->set_title(TTR("Change Default Type")); + texture_node_type_selector->connect("confirmed", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_confirmed)); + texture_node_type_selector->connect("canceled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed)); VBoxContainer *vbc = memnew(VBoxContainer); - selector->add_child(vbc); + texture_node_type_selector->add_child(vbc); vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); vbc->set_v_size_flags(Control::SIZE_EXPAND_FILL); vbc->set_custom_minimum_size(Size2(240, 260) * EDSCALE); - btn_group = memnew(VBoxContainer); + VBoxContainer *btn_group = memnew(VBoxContainer); vbc->add_child(btn_group); btn_group->set_h_size_flags(SIZE_EXPAND_FILL); @@ -6197,7 +6285,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it CheckBox *check = memnew(CheckBox); btn_group->add_child(check); check->set_text(texture_node_types[i]); - check->connect("button_down", callable_mp(this, &CanvasItemEditorViewport::_on_select_type).bind(check)); + check->connect("button_down", callable_mp(this, &CanvasItemEditorViewport::_on_select_texture_node_type).bind(check)); check->set_button_group(button_group); } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 6d951a77ec..a9de5e9a0b 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -32,10 +32,11 @@ #define CANVAS_ITEM_EDITOR_PLUGIN_H #include "editor/plugins/editor_plugin.h" -#include "scene/gui/base_button.h" #include "scene/gui/box_container.h" class AcceptDialog; +class Button; +class ButtonGroup; class CanvasItemEditorViewport; class ConfirmationDialog; class EditorData; @@ -409,7 +410,7 @@ private: void _selection_result_pressed(int); void _selection_menu_hide(); void _add_node_pressed(int p_result); - void _node_created(Node *p_node); + void _adjust_new_node_position(Node *p_node); void _reset_create_position(); void _update_editor_settings(); bool _is_grid_visible() const; @@ -634,15 +635,13 @@ class CanvasItemEditorViewport : public Control { CanvasItemEditor *canvas_item_editor = nullptr; Control *preview_node = nullptr; AcceptDialog *accept = nullptr; - AcceptDialog *selector = nullptr; - Label *selector_label = nullptr; + AcceptDialog *texture_node_type_selector = nullptr; Label *label = nullptr; Label *label_desc = nullptr; - VBoxContainer *btn_group = nullptr; Ref<ButtonGroup> button_group; void _on_mouse_exit(); - void _on_select_type(Object *selected); + void _on_select_texture_node_type(Object *selected); void _on_change_type_confirmed(); void _on_change_type_closed(); @@ -650,11 +649,12 @@ class CanvasItemEditorViewport : public Control { void _remove_preview(); bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const; - bool _only_packed_scenes_selected() const; - void _create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point); - bool _create_instance(Node *parent, String &path, const Point2 &p_point); + bool _is_any_texture_selected() const; + void _create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point); + void _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point); + bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point); void _perform_drop_data(); - void _show_resource_type_selector(); + void _show_texture_node_type_selector(); void _update_theme(); protected: diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp index dfc8323fc0..1d53a1b4d4 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -33,6 +33,7 @@ #include "canvas_item_editor_plugin.h" #include "core/io/image_loader.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_file_dialog.h" #include "editor/scene_tree_dock.h" @@ -268,7 +269,7 @@ CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() { toolbar->hide(); menu = memnew(MenuButton); - menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART); + menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_RESTART); menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); menu->get_popup()->add_item(TTR("Convert to GPUParticles2D"), MENU_CONVERT_TO_GPU_PARTICLES); menu->set_text(TTR("CPUParticles2D")); diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp index b5e3f102cf..baf70e45f0 100644 --- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "cpu_particles_3d_editor_plugin.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" #include "editor/gui/scene_tree_editor.h" #include "editor/plugins/node_3d_editor_plugin.h" @@ -168,7 +169,7 @@ CPUParticles3DEditor::CPUParticles3DEditor() { particles_editor_hb->hide(); options->set_text(TTR("CPUParticles3D")); - options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_OPTION_RESTART); options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB); options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); options->get_popup()->add_item(TTR("Convert to GPUParticles3D"), MENU_OPTION_CONVERT_TO_GPU_PARTICLES); diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp index e9f1b07c34..328b272562 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -33,6 +33,7 @@ #include "canvas_item_editor_plugin.h" #include "core/io/image_loader.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_file_dialog.h" #include "editor/scene_tree_dock.h" @@ -370,7 +371,7 @@ GPUParticles2DEditorPlugin::GPUParticles2DEditorPlugin() { toolbar->hide(); menu = memnew(MenuButton); - menu->get_popup()->add_item(TTR("Restart"), MENU_RESTART); + menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_RESTART); menu->get_popup()->add_item(TTR("Generate Visibility Rect"), MENU_GENERATE_VISIBILITY_RECT); menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); // menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK); diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp index 04b0a8aa26..9063109ece 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "core/io/resource_loader.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "editor/scene_tree_dock.h" @@ -414,7 +415,7 @@ GPUParticles3DEditor::GPUParticles3DEditor() { particles_editor_hb->hide(); options->set_text(TTR("GPUParticles3D")); - options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART); + options->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_OPTION_RESTART); options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB); options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE); options->get_popup()->add_item(TTR("Convert to CPUParticles3D"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 5873d10e76..e9e5b2294f 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -78,12 +78,14 @@ #include "editor/plugins/gizmos/voxel_gi_gizmo_plugin.h" #include "editor/plugins/node_3d_editor_gizmos.h" #include "editor/scene_tree_dock.h" +#include "scene/3d/audio_stream_player_3d.h" #include "scene/3d/camera_3d.h" #include "scene/3d/decal.h" #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/physics/collision_shape_3d.h" #include "scene/3d/physics/physics_body_3d.h" +#include "scene/3d/sprite_3d.h" #include "scene/3d/visual_instance_3d.h" #include "scene/3d/world_environment.h" #include "scene/gui/center_container.h" @@ -4192,27 +4194,37 @@ Node *Node3DEditorViewport::_sanitize_preview_node(Node *p_node) const { void Node3DEditorViewport::_create_preview_node(const Vector<String> &files) const { bool add_preview = false; - for (int i = 0; i < files.size(); i++) { - Ref<Resource> res = ResourceLoader::load(files[i]); + for (const String &path : files) { + Ref<Resource> res = ResourceLoader::load(path); ERR_CONTINUE(res.is_null()); - Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res)); - Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res)); - if (mesh != nullptr || scene != nullptr) { - if (mesh != nullptr) { - MeshInstance3D *mesh_instance = memnew(MeshInstance3D); - mesh_instance->set_mesh(mesh); - preview_node->add_child(mesh_instance); - } else { - if (scene.is_valid()) { - Node *instance = scene->instantiate(); - if (instance) { - instance = _sanitize_preview_node(instance); - preview_node->add_child(instance); - } - } + + Ref<PackedScene> scene = res; + if (scene.is_valid()) { + Node *instance = scene->instantiate(); + if (instance) { + instance = _sanitize_preview_node(instance); + preview_node->add_child(instance); } add_preview = true; } + + Ref<Mesh> mesh = res; + if (mesh.is_valid()) { + MeshInstance3D *mesh_instance = memnew(MeshInstance3D); + mesh_instance->set_mesh(mesh); + preview_node->add_child(mesh_instance); + add_preview = true; + } + + Ref<AudioStream> audio = res; + if (audio.is_valid()) { + Sprite3D *sprite = memnew(Sprite3D); + sprite->set_texture(get_editor_theme_icon(SNAME("Gizmo3DSamplePlayer"))); + sprite->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); + sprite->set_pixel_size(0.005); + preview_node->add_child(sprite); + add_preview = true; + } } if (add_preview) { EditorNode::get_singleton()->get_scene_root()->add_child(preview_node); @@ -4346,12 +4358,12 @@ bool Node3DEditorViewport::_cyclical_dependency_exists(const String &p_target_sc return false; } -bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) { - Ref<Resource> res = ResourceLoader::load(path); +bool Node3DEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) { + Ref<Resource> res = ResourceLoader::load(p_path); ERR_FAIL_COND_V(res.is_null(), false); - Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res)); - Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res)); + Ref<PackedScene> scene = res; + Ref<Mesh> mesh = res; Node *instantiated_scene = nullptr; @@ -4361,8 +4373,10 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po mesh_instance->set_mesh(mesh); // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others. - String name = path.get_file().get_basename(); - mesh_instance->set_name(Node::adjust_name_casing(name)); + const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename()); + if (!node_name.is_empty()) { + mesh_instance->set_name(node_name); + } instantiated_scene = mesh_instance; } else { @@ -4386,25 +4400,25 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po } if (scene != nullptr) { - instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path)); + instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_path)); } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->add_do_method(parent, "add_child", instantiated_scene, true); + undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true); undo_redo->add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene()); undo_redo->add_do_reference(instantiated_scene); - undo_redo->add_undo_method(parent, "remove_child", instantiated_scene); + undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene); undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene); - String new_name = parent->validate_child_name(instantiated_scene); + String new_name = p_parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - undo_redo->add_do_method(ed, "live_debug_instantiate_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), path, new_name); - undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + undo_redo->add_do_method(ed, "live_debug_instantiate_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_path, new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name)); Node3D *node3d = Object::cast_to<Node3D>(instantiated_scene); if (node3d) { Transform3D parent_tf; - Node3D *parent_node3d = Object::cast_to<Node3D>(parent); + Node3D *parent_node3d = Object::cast_to<Node3D>(p_parent); if (parent_node3d) { parent_tf = parent_node3d->get_global_gizmo_transform(); } @@ -4419,6 +4433,46 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po return true; } +bool Node3DEditorViewport::_create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point) { + Ref<AudioStream> audio = ResourceLoader::load(p_path); + ERR_FAIL_COND_V(audio.is_null(), false); + + AudioStreamPlayer3D *audio_player = memnew(AudioStreamPlayer3D); + audio_player->set_stream(audio); + + // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others. + const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename()); + if (!node_name.is_empty()) { + audio_player->set_name(node_name); + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->add_do_method(p_parent, "add_child", audio_player, true); + undo_redo->add_do_method(audio_player, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_reference(audio_player); + undo_redo->add_undo_method(p_parent, "remove_child", audio_player); + undo_redo->add_do_method(editor_selection, "add_node", audio_player); + + const String new_name = p_parent->validate_child_name(audio_player); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), audio_player->get_class(), new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name)); + + Transform3D parent_tf; + Node3D *parent_node3d = Object::cast_to<Node3D>(p_parent); + if (parent_node3d) { + parent_tf = parent_node3d->get_global_gizmo_transform(); + } + + Transform3D new_tf = audio_player->get_transform(); + new_tf.origin = parent_tf.affine_inverse().xform(preview_node_pos + audio_player->get_position()); + new_tf.basis = parent_tf.affine_inverse().basis * new_tf.basis; + + undo_redo->add_do_method(audio_player, "set_transform", new_tf); + + return true; +} + void Node3DEditorViewport::_perform_drop_data() { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); if (spatial_editor->get_preview_material_target().is_valid()) { @@ -4453,11 +4507,18 @@ void Node3DEditorViewport::_perform_drop_data() { if (res.is_null()) { continue; } - Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res)); - Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res)); - if (mesh != nullptr || scene != nullptr) { - bool success = _create_instance(target_node, path, drop_pos); - if (!success) { + + Ref<PackedScene> scene = res; + Ref<Mesh> mesh = res; + if (mesh.is_valid() || scene.is_valid()) { + if (!_create_instance(target_node, path, drop_pos)) { + error_files.push_back(path.get_file()); + } + } + + Ref<AudioStream> audio = res; + if (audio.is_valid()) { + if (!_create_audio_node(target_node, path, drop_pos)) { error_files.push_back(path.get_file()); } } @@ -4488,12 +4549,14 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant bool is_other_valid = false; // Check if at least one of the dragged files is a mesh, material, texture or scene. for (int i = 0; i < files.size(); i++) { - bool is_scene = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "PackedScene"); - bool is_mesh = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Mesh"); - bool is_material = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Material"); - bool is_texture = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Texture"); - - if (is_mesh || is_scene || is_material || is_texture) { + const String &res_type = ResourceLoader::get_resource_type(files[i]); + bool is_scene = ClassDB::is_parent_class(res_type, "PackedScene"); + bool is_mesh = ClassDB::is_parent_class(res_type, "Mesh"); + bool is_material = ClassDB::is_parent_class(res_type, "Material"); + bool is_texture = ClassDB::is_parent_class(res_type, "Texture"); + bool is_audio = ClassDB::is_parent_class(res_type, "AudioStream"); + + if (is_mesh || is_scene || is_material || is_texture || is_audio) { Ref<Resource> res = ResourceLoader::load(files[i]); if (res.is_null()) { continue; @@ -4502,6 +4565,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant Ref<Mesh> mesh = res; Ref<Material> mat = res; Ref<Texture2D> tex = res; + Ref<AudioStream> audio = res; if (scn.is_valid()) { Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); if (!instantiated_scene) { @@ -4537,6 +4601,8 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant spatial_editor->set_preview_material(new_mat); is_other_valid = true; continue; + } else if (!is_other_valid && audio.is_valid()) { + is_other_valid = true; } else { continue; } @@ -4593,11 +4659,12 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_ Node *root_node = EditorNode::get_singleton()->get_edited_scene(); if (selected_nodes.size() > 0) { Node *selected_node = selected_nodes.front()->get(); - target_node = selected_node; if (is_alt) { target_node = root_node; - } else if (is_shift && selected_node != root_node) { - target_node = selected_node->get_parent(); + } else if (is_shift) { + target_node = selected_node; + } else { // Default behavior. + target_node = (selected_node != root_node) ? selected_node->get_parent() : root_node; } } else { if (root_node) { @@ -8409,7 +8476,7 @@ Node3DEditor::Node3DEditor() { tool_button[TOOL_LOCK_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_LOCK_SELECTED)); tool_button[TOOL_LOCK_SELECTED]->set_tooltip_text(TTR("Lock selected node, preventing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::L)); + tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/lock_selected_nodes")); tool_button[TOOL_UNLOCK_SELECTED] = memnew(Button); main_menu_hbox->add_child(tool_button[TOOL_UNLOCK_SELECTED]); @@ -8417,7 +8484,7 @@ Node3DEditor::Node3DEditor() { tool_button[TOOL_UNLOCK_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_UNLOCK_SELECTED)); tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip_text(TTR("Unlock selected node, allowing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::L)); + tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/unlock_selected_nodes")); tool_button[TOOL_GROUP_SELECTED] = memnew(Button); main_menu_hbox->add_child(tool_button[TOOL_GROUP_SELECTED]); @@ -8425,7 +8492,7 @@ Node3DEditor::Node3DEditor() { tool_button[TOOL_GROUP_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_GROUP_SELECTED)); tool_button[TOOL_GROUP_SELECTED]->set_tooltip_text(TTR("Groups the selected node with its children. This selects the parent when any child node is clicked in 2D and 3D view.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::G)); + tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/group_selected_nodes")); tool_button[TOOL_UNGROUP_SELECTED] = memnew(Button); main_menu_hbox->add_child(tool_button[TOOL_UNGROUP_SELECTED]); @@ -8433,7 +8500,7 @@ Node3DEditor::Node3DEditor() { tool_button[TOOL_UNGROUP_SELECTED]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_UNGROUP_SELECTED)); tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip_text(TTR("Ungroups the selected node from its children. Child nodes will be individual items in 2D and 3D view.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. - tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::G)); + tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_GET_SHORTCUT("editor/ungroup_selected_nodes")); main_menu_hbox->add_child(memnew(VSeparator)); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index a3e1224cb8..4990b11a47 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -443,7 +443,8 @@ private: void _reset_preview_material() const; void _remove_preview_material(); bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const; - bool _create_instance(Node *parent, String &path, const Point2 &p_point); + bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point); + bool _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point); void _perform_drop_data(); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 6cb49bbfa6..22dbb6e9f2 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -731,6 +731,7 @@ void ScriptEditor::_go_to_tab(int p_idx) { _update_members_overview(); _update_help_overview(); _update_selected_editor_menu(); + _update_online_doc(); _update_members_overview_visibility(); _update_help_overview_visibility(); } @@ -764,7 +765,7 @@ void ScriptEditor::_update_recent_scripts() { } recent_scripts->add_separator(); - recent_scripts->add_shortcut(ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Files"))); + recent_scripts->add_shortcut(ED_GET_SHORTCUT("script_editor/clear_recent")); recent_scripts->set_item_disabled(recent_scripts->get_item_id(recent_scripts->get_item_count() - 1), rc.is_empty()); recent_scripts->reset_size(); @@ -903,6 +904,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { _go_to_tab(idx); } else { _update_selected_editor_menu(); + _update_online_doc(); } _update_history_arrows(); @@ -1350,7 +1352,21 @@ void ScriptEditor::_menu_option(int p_option) { help_search_dialog->popup_dialog(); } break; case SEARCH_WEBSITE: { - OS::get_singleton()->shell_open(VERSION_DOCS_URL "/"); + Control *tab = tab_container->get_current_tab_control(); + + EditorHelp *eh = Object::cast_to<EditorHelp>(tab); + bool native_class_doc = false; + if (eh) { + const HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(eh->get_class()); + native_class_doc = E && !E->value.is_script_doc; + } + if (native_class_doc) { + String name = eh->get_class().to_lower(); + String doc_url = vformat(VERSION_DOCS_URL "/classes/class_%s.html", name); + OS::get_singleton()->shell_open(doc_url); + } else { + OS::get_singleton()->shell_open(VERSION_DOCS_URL "/"); + } } break; case WINDOW_NEXT: { _history_forward(); @@ -2029,6 +2045,26 @@ void ScriptEditor::_update_help_overview() { } } +void ScriptEditor::_update_online_doc() { + Node *current = tab_container->get_tab_control(tab_container->get_current_tab()); + + EditorHelp *eh = Object::cast_to<EditorHelp>(current); + bool native_class_doc = false; + if (eh) { + const HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(eh->get_class()); + native_class_doc = E && !E->value.is_script_doc; + } + if (native_class_doc) { + String name = eh->get_class(); + String tooltip = vformat(TTR("Open '%s' in Godot online documentation."), name); + site_search->set_text(TTR("Open in Online Docs")); + site_search->set_tooltip_text(tooltip); + } else { + site_search->set_text(TTR("Online Docs")); + site_search->set_tooltip_text(TTR("Open Godot online documentation.")); + } +} + void ScriptEditor::_update_script_colors() { bool script_temperature_enabled = EDITOR_GET("text_editor/script_list/script_temperature_enabled"); @@ -3555,13 +3591,13 @@ void ScriptEditor::_update_selected_editor_menu() { script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), Key::F3), HELP_SEARCH_FIND_NEXT); script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KeyModifierMask::SHIFT | Key::F3), HELP_SEARCH_FIND_PREVIOUS); script_search_menu->get_popup()->add_separator(); - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F), SEARCH_IN_FILES); - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R), REPLACE_IN_FILES); + script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/find_in_files"), SEARCH_IN_FILES); + script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/replace_in_files"), REPLACE_IN_FILES); script_search_menu->show(); } else { if (tab_container->get_tab_count() == 0) { - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F), SEARCH_IN_FILES); - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R), REPLACE_IN_FILES); + script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/find_in_files"), SEARCH_IN_FILES); + script_search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/replace_in_files"), REPLACE_IN_FILES); script_search_menu->show(); } else { script_search_menu->hide(); @@ -4065,7 +4101,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script..."), KeyModifierMask::CMD_OR_CTRL | Key::N), FILE_NEW); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New Text File..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::N), FILE_NEW_TEXTFILE); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open...")), FILE_OPEN); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::T), FILE_REOPEN_CLOSED); + file_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_editor/reopen_closed_script"), FILE_REOPEN_CLOSED); recent_scripts = memnew(PopupMenu); file_menu->get_popup()->add_submenu_node_item(TTR("Open Recent"), recent_scripts, FILE_OPEN_RECENT); @@ -4086,6 +4122,9 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KeyModifierMask::ALT | Key::LEFT), WINDOW_PREV); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KeyModifierMask::ALT | Key::RIGHT), WINDOW_NEXT); + ED_SHORTCUT_OVERRIDE("script_editor/history_previous", "macos", KeyModifierMask::ALT | KeyModifierMask::META | Key::LEFT); + ED_SHORTCUT_OVERRIDE("script_editor/history_next", "macos", KeyModifierMask::ALT | KeyModifierMask::META | Key::RIGHT); + file_menu->get_popup()->add_separator(); theme_submenu = memnew(PopupMenu); @@ -4147,10 +4186,8 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { site_search = memnew(Button); site_search->set_flat(true); - site_search->set_text(TTR("Online Docs")); site_search->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditor::_menu_option).bind(SEARCH_WEBSITE)); menu_hb->add_child(site_search); - site_search->set_tooltip_text(TTR("Open Godot online documentation.")); help_search = memnew(Button); help_search->set_flat(true); @@ -4271,6 +4308,8 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { Ref<EditorJSONSyntaxHighlighter> json_syntax_highlighter; json_syntax_highlighter.instantiate(); register_syntax_highlighter(json_syntax_highlighter); + + _update_online_doc(); } ScriptEditor::~ScriptEditor() { @@ -4463,6 +4502,15 @@ void ScriptEditorPlugin::edited_scene_changed() { } ScriptEditorPlugin::ScriptEditorPlugin() { + ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::T); + ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Scripts")); + ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F); + ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R); + + ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KeyModifierMask::SHIFT | Key::F4); + ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KeyModifierMask::SHIFT | Key::F5); + ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KeyModifierMask::SHIFT | Key::F6); + window_wrapper = memnew(WindowWrapper); window_wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), TTR("Script Editor"))); window_wrapper->set_margins_enabled(true); @@ -4491,9 +4539,6 @@ ScriptEditorPlugin::ScriptEditorPlugin() { EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_path", PROPERTY_HINT_GLOBAL_FILE)); EDITOR_DEF("text_editor/external/exec_flags", "{file}"); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_flags", PROPERTY_HINT_PLACEHOLDER_TEXT, "Call flags with placeholders: {project}, {file}, {col}, {line}.")); - - ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::T); - ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Scripts")); } ScriptEditorPlugin::~ScriptEditorPlugin() { diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index e6bb8f14a9..6f8e71ce75 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -450,6 +450,8 @@ class ScriptEditor : public PanelContainer { void _update_help_overview(); void _help_overview_selected(int p_idx); + void _update_online_doc(); + void _find_scripts(Node *p_base, Node *p_current, HashSet<Ref<Script>> &used); void _tree_changed(); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 191d58228b..a5d89ff54c 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -2133,10 +2133,11 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { void ScriptTextEditor::_color_changed(const Color &p_color) { String new_args; + const int decimals = 3; if (p_color.a == 1.0f) { - new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ")"); + new_args = String("(" + String::num(p_color.r, decimals) + ", " + String::num(p_color.g, decimals) + ", " + String::num(p_color.b, decimals) + ")"); } else { - new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ", " + rtos(p_color.a) + ")"); + new_args = String("(" + String::num(p_color.r, decimals) + ", " + String::num(p_color.g, decimals) + ", " + String::num(p_color.b, decimals) + ", " + String::num(p_color.a, decimals) + ")"); } String line = code_editor->get_text_editor()->get_line(color_position.x); @@ -2311,9 +2312,9 @@ void ScriptTextEditor::_enable_code_editor() { edit_menu->get_popup()->add_separator(); { PopupMenu *sub_menu = memnew(PopupMenu); - sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KeyModifierMask::SHIFT | Key::F4), EDIT_TO_UPPERCASE); - sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KeyModifierMask::SHIFT | Key::F5), EDIT_TO_LOWERCASE); - sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KeyModifierMask::SHIFT | Key::F6), EDIT_CAPITALIZE); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/capitalize"), EDIT_CAPITALIZE); sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); edit_menu->get_popup()->add_submenu_node_item(TTR("Convert Case"), sub_menu); } diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 2aa04d7874..5bc3cafe19 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -647,9 +647,9 @@ TextEditor::TextEditor() { edit_menu->get_popup()->add_separator(); PopupMenu *convert_case = memnew(PopupMenu); edit_menu->get_popup()->add_submenu_node_item(TTR("Convert Case"), convert_case); - convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase")), EDIT_TO_UPPERCASE); - convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase")), EDIT_TO_LOWERCASE); - convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize")), EDIT_CAPITALIZE); + convert_case->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE); + convert_case->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE); + convert_case->add_shortcut(ED_GET_SHORTCUT("script_text_editor/capitalize"), EDIT_CAPITALIZE); convert_case->connect("id_pressed", callable_mp(this, &TextEditor::_edit_option)); highlighter_menu = memnew(PopupMenu); diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp index 637547062c..bb74bf8d1f 100644 --- a/editor/plugins/text_shader_editor.cpp +++ b/editor/plugins/text_shader_editor.cpp @@ -318,6 +318,8 @@ void ShaderTextEditor::_load_theme_settings() { const Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color"); syntax_highlighter->add_color_region("/**", "*/", doc_comment_color, false); + // "/**/" will be treated as the start of the "/**" region, this line is guaranteed to end the color_region. + syntax_highlighter->add_color_region("/**/", "", comment_color, true); // Disabled preprocessor branches use translucent text color to be easier to distinguish from comments. syntax_highlighter->set_disabled_branch_color(Color(EDITOR_GET("text_editor/theme/highlighting/text_color")) * Color(1, 1, 1, 0.5)); diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 2ee0dc0316..2b86268414 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -250,7 +250,7 @@ void TileAtlasView::_draw_base_tiles() { Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin()); // Draw the tile. - TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame); + TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame); } } @@ -411,7 +411,7 @@ void TileAtlasView::_draw_alternatives() { } // Draw the tile. - TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id); + TileMapLayer::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id); // Increment the x position. current_pos.x += transposed ? texture_region_size.y : texture_region_size.x; diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 2d0e5214db..a94080e253 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -144,7 +144,8 @@ void GenericTilePolygonEditor::_base_control_draw() { } // Draw tile-related things. - Size2 tile_size = tile_set->get_tile_size(); + const Size2 base_tile_size = tile_set->get_tile_size(); + const Size2 tile_size = background_region.size; Transform2D xform; xform.set_origin(base_control->get_size() / 2 + panning); @@ -170,12 +171,16 @@ void GenericTilePolygonEditor::_base_control_draw() { // Draw grid. if (current_snap_option == SNAP_GRID) { - Vector2 spacing = tile_size / snap_subdivision->get_value(); + Vector2 spacing = base_tile_size / snap_subdivision->get_value(); Vector2 offset = -tile_size / 2; + int w = snap_subdivision->get_value() * (tile_size / base_tile_size).x; + int h = snap_subdivision->get_value() * (tile_size / base_tile_size).y; - for (int i = 1; i < snap_subdivision->get_value(); i++) { - base_control->draw_line(Vector2(spacing.x * i, 0) + offset, Vector2(spacing.x * i, tile_size.y) + offset, Color(1, 1, 1, 0.33)); - base_control->draw_line(Vector2(0, spacing.y * i) + offset, Vector2(tile_size.x, spacing.y * i) + offset, Color(1, 1, 1, 0.33)); + for (int y = 1; y < h; y++) { + for (int x = 1; x < w; x++) { + base_control->draw_line(Vector2(spacing.x * x, 0) + offset, Vector2(spacing.x * x, tile_size.y) + offset, Color(1, 1, 1, 0.33)); + base_control->draw_line(Vector2(0, spacing.y * y) + offset, Vector2(tile_size.x, spacing.y * y) + offset, Color(1, 1, 1, 0.33)); + } } } @@ -1320,7 +1325,7 @@ TileDataDefaultEditor::TileDataDefaultEditor() { picker_button = memnew(Button); picker_button->set_theme_type_variation("FlatButton"); picker_button->set_toggle_mode(true); - picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P)); + picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker")); toolbar->add_child(picker_button); } @@ -2807,7 +2812,7 @@ TileDataTerrainsEditor::TileDataTerrainsEditor() { picker_button = memnew(Button); picker_button->set_theme_type_variation("FlatButton"); picker_button->set_toggle_mode(true); - picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P)); + picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker")); toolbar->add_child(picker_button); // Setup diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp index 98c05e9b15..5fd4fb3602 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -2183,13 +2183,6 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { ->get_viewport_control() ->connect(SceneStringName(mouse_exited), callable_mp(this, &TileMapLayerEditorTilesPlugin::_mouse_exited_viewport)); - // --- Shortcuts --- - ED_SHORTCUT("tiles_editor/cut", TTR("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X); - ED_SHORTCUT("tiles_editor/copy", TTR("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C); - ED_SHORTCUT("tiles_editor/paste", TTR("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V); - ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), Key::ESCAPE); - ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE); - // --- Initialize references --- tile_map_clipboard.instantiate(); selection_pattern.instantiate(); @@ -2217,7 +2210,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { paint_tool_button->set_theme_type_variation("FlatButton"); paint_tool_button->set_toggle_mode(true); paint_tool_button->set_button_group(tool_buttons_group); - paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D)); + paint_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/paint_tool")); paint_tool_button->set_tooltip_text(TTR("Shift: Draw line.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Shift: Draw rectangle.")); paint_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(paint_tool_button); @@ -2228,7 +2221,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { line_tool_button->set_toggle_mode(true); line_tool_button->set_button_group(tool_buttons_group); // TRANSLATORS: This refers to the line tool in the tilemap editor. - line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line", "Tool"), Key::L)); + line_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/line_tool")); line_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(line_tool_button); viewport_shortcut_buttons.push_back(line_tool_button); @@ -2237,7 +2230,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { rect_tool_button->set_theme_type_variation("FlatButton"); rect_tool_button->set_toggle_mode(true); rect_tool_button->set_button_group(tool_buttons_group); - rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R)); + rect_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/rect_tool")); rect_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(rect_tool_button); viewport_shortcut_buttons.push_back(rect_tool_button); @@ -2246,7 +2239,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { bucket_tool_button->set_theme_type_variation("FlatButton"); bucket_tool_button->set_toggle_mode(true); bucket_tool_button->set_button_group(tool_buttons_group); - bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B)); + bucket_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/bucket_tool")); bucket_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(bucket_tool_button); toolbar->add_child(tilemap_tiles_tools_buttons); @@ -2263,7 +2256,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { picker_button = memnew(Button); picker_button->set_theme_type_variation("FlatButton"); picker_button->set_toggle_mode(true); - picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P)); + picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker")); Key key = (OS::get_singleton()->has_feature("macos") || OS::get_singleton()->has_feature("web_macos") || OS::get_singleton()->has_feature("web_ios")) ? Key::META : Key::CTRL; picker_button->set_tooltip_text(vformat(TTR("Alternatively hold %s with other tools to pick tile."), find_keycode_name(key))); picker_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport)); @@ -2274,7 +2267,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { erase_button = memnew(Button); erase_button->set_theme_type_variation("FlatButton"); erase_button->set_toggle_mode(true); - erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E)); + erase_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/eraser")); erase_button->set_tooltip_text(TTR("Alternatively use RMB to erase tiles.")); erase_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport)); tools_settings->add_child(erase_button); @@ -3552,7 +3545,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { paint_tool_button->set_toggle_mode(true); paint_tool_button->set_button_group(tool_buttons_group); paint_tool_button->set_pressed(true); - paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D)); + paint_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/paint_tool")); paint_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(paint_tool_button); viewport_shortcut_buttons.push_back(paint_tool_button); @@ -3561,7 +3554,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { line_tool_button->set_theme_type_variation("FlatButton"); line_tool_button->set_toggle_mode(true); line_tool_button->set_button_group(tool_buttons_group); - line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line"), Key::L)); + line_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/line_tool")); line_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(line_tool_button); viewport_shortcut_buttons.push_back(line_tool_button); @@ -3570,7 +3563,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { rect_tool_button->set_theme_type_variation("FlatButton"); rect_tool_button->set_toggle_mode(true); rect_tool_button->set_button_group(tool_buttons_group); - rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R)); + rect_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/rect_tool")); rect_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(rect_tool_button); viewport_shortcut_buttons.push_back(rect_tool_button); @@ -3579,7 +3572,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { bucket_tool_button->set_theme_type_variation("FlatButton"); bucket_tool_button->set_toggle_mode(true); bucket_tool_button->set_button_group(tool_buttons_group); - bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B)); + bucket_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/bucket_tool")); bucket_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(bucket_tool_button); viewport_shortcut_buttons.push_back(bucket_tool_button); @@ -3597,7 +3590,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { picker_button = memnew(Button); picker_button->set_theme_type_variation("FlatButton"); picker_button->set_toggle_mode(true); - picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P)); + picker_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/picker")); picker_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport)); tools_settings->add_child(picker_button); viewport_shortcut_buttons.push_back(picker_button); @@ -3606,7 +3599,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { erase_button = memnew(Button); erase_button->set_theme_type_variation("FlatButton"); erase_button->set_toggle_mode(true); - erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E)); + erase_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/eraser")); erase_button->connect(SceneStringName(pressed), callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport)); tools_settings->add_child(erase_button); viewport_shortcut_buttons.push_back(erase_button); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index e4fd32a637..1daba1f665 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2627,7 +2627,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tools_settings_erase_button = memnew(Button); tools_settings_erase_button->set_theme_type_variation("FlatButton"); tools_settings_erase_button->set_toggle_mode(true); - tools_settings_erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E)); + tools_settings_erase_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/eraser")); tools_settings_erase_button->set_shortcut_context(this); tool_settings->add_child(tools_settings_erase_button); @@ -2687,7 +2687,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_create_help->set_grow_direction_preset(Control::PRESET_BOTTOM_LEFT); base_tile_popup_menu = memnew(PopupMenu); - base_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE), TILE_DELETE); + base_tile_popup_menu->add_shortcut(ED_GET_SHORTCUT("tiles_editor/delete"), TILE_DELETE); base_tile_popup_menu->add_item(TTR("Create an Alternative Tile"), TILE_CREATE_ALTERNATIVE); base_tile_popup_menu->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option)); tile_atlas_view->add_child(base_tile_popup_menu); diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 6833a36138..f1cc69ce18 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -303,6 +303,19 @@ TilesEditorUtils::TilesEditorUtils() { singleton = this; // Pattern preview generation thread. pattern_preview_thread.start(_thread_func, this); + + ED_SHORTCUT("tiles_editor/cut", TTR("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X); + ED_SHORTCUT("tiles_editor/copy", TTR("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C); + ED_SHORTCUT("tiles_editor/paste", TTR("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V); + ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), Key::ESCAPE); + ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE); + + ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D); + ED_SHORTCUT("tiles_editor/line_tool", TTR("Line", "Tool"), Key::L); + ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R); + ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B); + ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E); + ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P); } TilesEditorUtils::~TilesEditorUtils() { diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 01551a52c2..438d798120 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -60,7 +60,6 @@ #include "scene/gui/view_panner.h" #include "scene/main/window.h" #include "scene/resources/curve_texture.h" -#include "scene/resources/image_texture.h" #include "scene/resources/style_box_flat.h" #include "scene/resources/visual_shader_nodes.h" #include "scene/resources/visual_shader_particle_nodes.h" @@ -5135,7 +5134,7 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, List<CopyItem> &r_items, c } int new_node_id = connection_remap[item.id]; - int new_frame_id = connection_remap[node->get_frame()]; + int new_frame_id = node->get_frame(); undo_redo->add_do_method(visual_shader.ptr(), "attach_node_to_frame", type, new_node_id, new_frame_id); undo_redo->add_do_method(graph_plugin.ptr(), "attach_node_to_frame", type, new_node_id, new_frame_id); } diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp index 32f088ec60..f474464f6c 100644 --- a/editor/project_manager/project_list.cpp +++ b/editor/project_manager/project_list.cpp @@ -32,6 +32,8 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" +#include "core/os/time.h" +#include "core/version.h" #include "editor/editor_paths.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -130,12 +132,29 @@ void ProjectListItemControl::set_project_icon(const Ref<Texture2D> &p_icon) { project_icon->set_texture(p_icon); } +void ProjectListItemControl::set_last_edited_info(const String &p_info) { + last_edited_info->set_text(p_info); +} + +void ProjectListItemControl::set_project_version(const String &p_info) { + project_version->set_text(p_info); +} + void ProjectListItemControl::set_unsupported_features(PackedStringArray p_features) { if (p_features.size() > 0) { String tooltip_text = ""; for (int i = 0; i < p_features.size(); i++) { if (ProjectList::project_feature_looks_like_version(p_features[i])) { - tooltip_text += TTR("This project was last edited in a different Godot version: ") + p_features[i] + "\n"; + PackedStringArray project_version_split = p_features[i].split("."); + int project_version_major = 0, project_version_minor = 0; + if (project_version_split.size() >= 2) { + project_version_major = project_version_split[0].to_int(); + project_version_minor = project_version_split[1].to_int(); + } + if (VERSION_MAJOR != project_version_major || VERSION_MINOR <= project_version_minor) { + // Don't show a warning if the project was last edited in a previous minor version. + tooltip_text += TTR("This project was last edited in a different Godot version: ") + p_features[i] + "\n"; + } p_features.remove_at(i); i--; } @@ -144,6 +163,10 @@ void ProjectListItemControl::set_unsupported_features(PackedStringArray p_featur String unsupported_features_str = String(", ").join(p_features); tooltip_text += TTR("This project uses features unsupported by the current build:") + "\n" + unsupported_features_str; } + if (tooltip_text.is_empty()) { + return; + } + project_version->set_tooltip_text(tooltip_text); project_unsupported_features->set_tooltip_text(tooltip_text); project_unsupported_features->show(); } else { @@ -272,12 +295,24 @@ ProjectListItemControl::ProjectListItemControl() { project_path->set_modulate(Color(1, 1, 1, 0.5)); path_hb->add_child(project_path); + last_edited_info = memnew(Label); + last_edited_info->set_name("LastEditedInfo"); + last_edited_info->set_mouse_filter(Control::MOUSE_FILTER_PASS); + last_edited_info->set_tooltip_text("Last edited timestamp"); + last_edited_info->set_modulate(Color(1, 1, 1, 0.5)); + path_hb->add_child(last_edited_info); + project_unsupported_features = memnew(TextureRect); project_unsupported_features->set_name("ProjectUnsupportedFeatures"); project_unsupported_features->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); path_hb->add_child(project_unsupported_features); project_unsupported_features->hide(); + project_version = memnew(Label); + project_version->set_name("ProjectVersion"); + project_version->set_mouse_filter(Control::MOUSE_FILTER_PASS); + path_hb->add_child(project_version); + Control *spacer = memnew(Control); spacer->set_custom_minimum_size(Size2(10, 10)); path_hb->add_child(spacer); @@ -409,6 +444,24 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa PackedStringArray project_features = cf->get_value("application", "config/features", PackedStringArray()); PackedStringArray unsupported_features = ProjectSettings::get_unsupported_features(project_features); + String project_version = "?"; + for (int i = 0; i < project_features.size(); i++) { + if (ProjectList::project_feature_looks_like_version(project_features[i])) { + project_version = project_features[i]; + break; + } + } + + if (config_version < ProjectSettings::CONFIG_VERSION) { + // Previous versions may not have unsupported features. + if (config_version == 4) { + unsupported_features.push_back("3.x"); + project_version = "3.x"; + } else { + unsupported_features.push_back("Unknown version"); + } + } + uint64_t last_edited = 0; if (cf_err == OK) { // The modification date marks the date the project was last edited. @@ -433,7 +486,7 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa ProjectManager::get_singleton()->add_new_tag(tag); } - return Item(project_name, description, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version); + return Item(project_name, description, project_version, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version); } void ProjectList::_update_icons_async() { @@ -706,6 +759,8 @@ void ProjectList::_create_project_item_control(int p_index) { hb->set_tooltip_text(item.description); hb->set_tags(item.tags, this); hb->set_unsupported_features(item.unsupported_features.duplicate()); + hb->set_project_version(item.project_version); + hb->set_last_edited_info(Time::get_singleton()->get_datetime_string_from_unix_time(item.last_edited, true)); hb->set_is_favorite(item.favorite); hb->set_is_missing(item.missing); diff --git a/editor/project_manager/project_list.h b/editor/project_manager/project_list.h index 981df0f3a0..6e0f5830ac 100644 --- a/editor/project_manager/project_list.h +++ b/editor/project_manager/project_list.h @@ -51,6 +51,8 @@ class ProjectListItemControl : public HBoxContainer { TextureRect *project_icon = nullptr; Label *project_title = nullptr; Label *project_path = nullptr; + Label *last_edited_info = nullptr; + Label *project_version = nullptr; TextureRect *project_unsupported_features = nullptr; HBoxContainer *tag_container = nullptr; @@ -71,6 +73,8 @@ public: void set_project_path(const String &p_path); void set_tags(const PackedStringArray &p_tags, ProjectList *p_parent_list); void set_project_icon(const Ref<Texture2D> &p_icon); + void set_last_edited_info(const String &p_info); + void set_project_version(const String &p_version); void set_unsupported_features(PackedStringArray p_features); bool should_load_project_icon() const; @@ -100,6 +104,7 @@ public: struct Item { String project_name; String description; + String project_version; PackedStringArray tags; String tag_sort_string; String path; @@ -118,6 +123,7 @@ public: Item(const String &p_name, const String &p_description, + const String &p_project_version, const PackedStringArray &p_tags, const String &p_path, const String &p_icon, @@ -130,6 +136,7 @@ public: int p_version) { project_name = p_name; description = p_description; + project_version = p_project_version; tags = p_tags; path = p_path; icon = p_icon; diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index cff95cadc8..dc9d1df4e8 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -54,7 +54,7 @@ void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) { if (saved_size != Rect2()) { popup(saved_size); } else { - popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8); + popup_centered_clamped(Size2(1200, 700) * EDSCALE, 0.8); } _add_feature_overrides(); diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index 51f9d319a6..edefaa61a8 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -289,6 +289,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor) { vbc->add_child(lbl_preview_title); lbl_preview = memnew(Label); + lbl_preview->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); lbl_preview->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); vbc->add_child(lbl_preview); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 53145c3450..ec0380f964 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -56,6 +56,7 @@ #include "editor/shader_create_dialog.h" #include "editor/themes/editor_scale.h" #include "scene/animation/animation_tree.h" +#include "scene/audio/audio_stream_player.h" #include "scene/gui/check_box.h" #include "scene/property_utils.h" #include "scene/resources/packed_scene.h" @@ -254,8 +255,8 @@ void SceneTreeDock::instantiate_scenes(const Vector<String> &p_files, Node *p_pa _perform_instantiate_scenes(p_files, parent, -1); } -void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, Node *parent, int p_pos) { - ERR_FAIL_NULL(parent); +void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, Node *p_parent, int p_pos) { + ERR_FAIL_NULL(p_parent); Vector<Node *> instances; @@ -302,25 +303,25 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Instantiate Scene(s)")); + undo_redo->create_action_for_history(TTRN("Instantiate Scene", "Instantiate Scenes", instances.size()), editor_data->get_current_edited_scene_history_id()); + undo_redo->add_do_method(editor_selection, "clear"); for (int i = 0; i < instances.size(); i++) { Node *instantiated_scene = instances[i]; - undo_redo->add_do_method(parent, "add_child", instantiated_scene, true); + undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true); if (p_pos >= 0) { - undo_redo->add_do_method(parent, "move_child", instantiated_scene, p_pos + i); + undo_redo->add_do_method(p_parent, "move_child", instantiated_scene, p_pos + i); } undo_redo->add_do_method(instantiated_scene, "set_owner", edited_scene); - undo_redo->add_do_method(editor_selection, "clear"); undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene); undo_redo->add_do_reference(instantiated_scene); - undo_redo->add_undo_method(parent, "remove_child", instantiated_scene); + undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene); - String new_name = parent->validate_child_name(instantiated_scene); + String new_name = p_parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(parent), p_files[i], new_name); - undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).path_join(new_name))); + undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(p_parent), p_files[i], new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).path_join(new_name))); } undo_redo->commit_action(); @@ -330,6 +331,75 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N } } +void SceneTreeDock::_perform_create_audio_stream_players(const Vector<String> &p_files, Node *p_parent, int p_pos) { + ERR_FAIL_NULL(p_parent); + + StringName node_type = "AudioStreamPlayer"; + if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) { + if (Object::cast_to<Node2D>(p_parent)) { + node_type = "AudioStreamPlayer2D"; + } else if (Object::cast_to<Node3D>(p_parent)) { + node_type = "AudioStreamPlayer3D"; + } + } + + Vector<Node *> nodes; + bool error = false; + + for (const String &path : p_files) { + Ref<AudioStream> stream = ResourceLoader::load(path); + if (stream.is_null()) { + current_option = -1; + accept->set_text(vformat(TTR("Error loading audio stream from %s"), path)); + accept->popup_centered(); + error = true; + break; + } + + Node *player = Object::cast_to<Node>(ClassDB::instantiate(node_type)); + player->set("stream", stream); + + // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others. + const String &node_name = Node::adjust_name_casing(path.get_file().get_basename()); + if (!node_name.is_empty()) { + player->set_name(node_name); + } + + nodes.push_back(player); + } + + if (error) { + for (Node *node : nodes) { + memdelete(node); + } + return; + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action_for_history(TTRN("Create AudioStreamPlayer", "Create AudioStreamPlayers", nodes.size()), editor_data->get_current_edited_scene_history_id()); + undo_redo->add_do_method(editor_selection, "clear"); + + for (int i = 0; i < nodes.size(); i++) { + Node *node = nodes[i]; + + undo_redo->add_do_method(p_parent, "add_child", node, true); + if (p_pos >= 0) { + undo_redo->add_do_method(p_parent, "move_child", node, p_pos + i); + } + undo_redo->add_do_method(node, "set_owner", edited_scene); + undo_redo->add_do_method(editor_selection, "add_node", node); + undo_redo->add_do_reference(node); + undo_redo->add_undo_method(p_parent, "remove_child", node); + + String new_name = p_parent->validate_child_name(node); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + undo_redo->add_do_method(ed, "live_debug_create_node", edited_scene->get_path_to(p_parent), node->get_class(), new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).path_join(new_name))); + } + + undo_redo->commit_action(); +} + void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) { // `move_child` + `get_index` doesn't really work for internal nodes. ERR_FAIL_COND_MSG(base->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to replace internal node, this is not supported."); @@ -3196,15 +3266,13 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) { void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to, int p_type) { Node *node = get_node(p_to); ERR_FAIL_NULL(node); + ERR_FAIL_COND(p_files.is_empty()); - if (scene_tree->get_scene_tree()->get_drop_mode_flags() & Tree::DROP_MODE_INBETWEEN) { - // Dropped PackedScene, instance it. - int to_pos = -1; - _normalize_drop(node, to_pos, p_type); - _perform_instantiate_scenes(p_files, node, to_pos); - } else { - const String &res_path = p_files[0]; - StringName res_type = EditorFileSystem::get_singleton()->get_file_type(res_path); + const String &res_path = p_files[0]; + const StringName res_type = EditorFileSystem::get_singleton()->get_file_type(res_path); + + // Dropping as property when possible. + if (p_type == 0 && p_files.size() == 1) { List<String> valid_properties; List<PropertyInfo> pinfo; @@ -3238,10 +3306,22 @@ void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to, menu_properties->reset_size(); menu_properties->set_position(get_screen_position() + get_local_mouse_position()); menu_properties->popup(); - } else if (!valid_properties.is_empty()) { + return; + } + if (!valid_properties.is_empty()) { _perform_property_drop(node, valid_properties.front()->get(), ResourceLoader::load(res_path)); + return; } } + + // Either instantiate scenes or create AudioStreamPlayers. + int to_pos = -1; + _normalize_drop(node, to_pos, p_type); + if (res_type == "PackedScene") { + _perform_instantiate_scenes(p_files, node, to_pos); + } else { + _perform_create_audio_stream_players(p_files, node, to_pos); + } } void SceneTreeDock::_script_dropped(const String &p_file, NodePath p_to) { @@ -3571,7 +3651,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (profile_allow_editing) { menu->add_separator(); - menu->add_icon_shortcut(get_editor_theme_icon(SNAME("Remove")), ED_SHORTCUT("scene_tree/delete", TTR("Delete Node(s)"), Key::KEY_DELETE), TOOL_ERASE); + menu->add_icon_shortcut(get_editor_theme_icon(SNAME("Remove")), ED_GET_SHORTCUT("scene_tree/delete"), TOOL_ERASE); } menu->reset_size(); menu->set_position(p_menu_pos); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 21e1b00f93..abef990995 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -272,7 +272,8 @@ class SceneTreeDock : public VBoxContainer { void _filter_option_selected(int option); void _append_filter_options_to(PopupMenu *p_menu, bool p_include_separator = true); - void _perform_instantiate_scenes(const Vector<String> &p_files, Node *parent, int p_pos); + void _perform_instantiate_scenes(const Vector<String> &p_files, Node *p_parent, int p_pos); + void _perform_create_audio_stream_players(const Vector<String> &p_files, Node *p_parent, int p_pos); void _replace_with_branch_scene(const String &p_file, Node *base); void _remote_tree_selected(); diff --git a/editor/themes/SCsub b/editor/themes/SCsub index 65cfb6a8be..e8f96e4299 100644 --- a/editor/themes/SCsub +++ b/editor/themes/SCsub @@ -3,8 +3,8 @@ Import("env") import glob -import editor_theme_builders +import editor_theme_builders # Fonts flist = glob.glob(env.Dir("#thirdparty").abspath + "/fonts/*.ttf") diff --git a/gles3_builders.py b/gles3_builders.py index 1491927feb..a4928c81c5 100644 --- a/gles3_builders.py +++ b/gles3_builders.py @@ -1,9 +1,10 @@ """Functions used to generate source files during build time""" import os.path -from methods import print_error from typing import Optional +from methods import print_error + class GLES3HeaderStruct: def __init__(self): @@ -91,11 +92,11 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct, includeline = line.replace("#include ", "").strip()[1:-1] included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline) - if not included_file in header_data.vertex_included_files and header_data.reading == "vertex": + if included_file not in header_data.vertex_included_files and header_data.reading == "vertex": header_data.vertex_included_files += [included_file] if include_file_in_gles3_header(included_file, header_data, depth + 1) is None: print_error(f'In file "{filename}": #include "{includeline}" could not be found!"') - elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment": + elif included_file not in header_data.fragment_included_files and header_data.reading == "fragment": header_data.fragment_included_files += [included_file] if include_file_in_gles3_header(included_file, header_data, depth + 1) is None: print_error(f'In file "{filename}": #include "{includeline}" could not be found!"') @@ -121,7 +122,7 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct, # unfiorm array x = x[: x.find("[")] - if not x in header_data.texunit_names: + if x not in header_data.texunit_names: header_data.texunits += [(x, texunit)] header_data.texunit_names += [x] @@ -142,7 +143,7 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct, # unfiorm array x = x[: x.find("[")] - if not x in header_data.ubo_names: + if x not in header_data.ubo_names: header_data.ubos += [(x, ubo)] header_data.ubo_names += [x] @@ -157,7 +158,7 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct, # unfiorm array x = x[: x.find("[")] - if not x in header_data.uniforms: + if x not in header_data.uniforms: header_data.uniforms += [x] if (line.strip().find("out ") == 0 or line.strip().find("flat ") == 0) and line.find("tfb:") != -1: diff --git a/glsl_builders.py b/glsl_builders.py index 22f4de74b1..05aab3acbb 100644 --- a/glsl_builders.py +++ b/glsl_builders.py @@ -1,8 +1,9 @@ """Functions used to generate source files during build time""" import os.path +from typing import Iterable, Optional + from methods import print_error -from typing import Optional, Iterable def generate_inline_code(input_lines: Iterable[str], insert_newline: bool = True): @@ -77,15 +78,15 @@ def include_file_in_rd_header(filename: str, header_data: RDHeaderStruct, depth: else: included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline) - if not included_file in header_data.vertex_included_files and header_data.reading == "vertex": + if included_file not in header_data.vertex_included_files and header_data.reading == "vertex": header_data.vertex_included_files += [included_file] if include_file_in_rd_header(included_file, header_data, depth + 1) is None: print_error(f'In file "{filename}": #include "{includeline}" could not be found!"') - elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment": + elif included_file not in header_data.fragment_included_files and header_data.reading == "fragment": header_data.fragment_included_files += [included_file] if include_file_in_rd_header(included_file, header_data, depth + 1) is None: print_error(f'In file "{filename}": #include "{includeline}" could not be found!"') - elif not included_file in header_data.compute_included_files and header_data.reading == "compute": + elif included_file not in header_data.compute_included_files and header_data.reading == "compute": header_data.compute_included_files += [included_file] if include_file_in_rd_header(included_file, header_data, depth + 1) is None: print_error(f'In file "{filename}": #include "{includeline}" could not be found!"') diff --git a/main/main.cpp b/main/main.cpp index 66706be745..65f637d778 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2196,11 +2196,11 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (globals->has_setting("display/window/size/window_width_override") && globals->has_setting("display/window/size/window_height_override")) { - int desired_width = globals->get("display/window/size/window_width_override"); + int desired_width = GLOBAL_GET("display/window/size/window_width_override"); if (desired_width > 0) { window_size.width = desired_width; } - int desired_height = globals->get("display/window/size/window_height_override"); + int desired_height = GLOBAL_GET("display/window/size/window_height_override"); if (desired_height > 0) { window_size.height = desired_height; } @@ -4295,9 +4295,6 @@ void Main::cleanup(bool p_force) { if (globals) { memdelete(globals); } - if (engine) { - memdelete(engine); - } if (OS::get_singleton()->is_restart_on_exit_set()) { //attempt to restart with arguments @@ -4315,6 +4312,10 @@ void Main::cleanup(bool p_force) { uninitialize_modules(MODULE_INITIALIZATION_LEVEL_CORE); unregister_core_types(); + if (engine) { + memdelete(engine); + } + OS::get_singleton()->benchmark_end_measure("Shutdown", "Total"); OS::get_singleton()->benchmark_dump(); diff --git a/methods.py b/methods.py index da221cc0ea..7600e6d445 100644 --- a/methods.py +++ b/methods.py @@ -1,17 +1,14 @@ +import contextlib +import glob import os -import sys import re -import glob import subprocess -import contextlib +import sys from collections import OrderedDict -from collections.abc import Mapping from enum import Enum -from typing import Generator, Optional -from io import TextIOWrapper, StringIO +from io import StringIO, TextIOWrapper from pathlib import Path -from os.path import normpath, basename - +from typing import Generator, Optional # Get the "Godot" folder name ahead of time base_folder_path = str(os.path.abspath(Path(__file__).parent)) + "/" @@ -199,7 +196,7 @@ def add_module_version_string(self, s): def get_version_info(module_version_string="", silent=False): build_name = "custom_build" - if os.getenv("BUILD_NAME") != None: + if os.getenv("BUILD_NAME") is not None: build_name = str(os.getenv("BUILD_NAME")) if not silent: print(f"Using custom build name: '{build_name}'.") @@ -221,7 +218,7 @@ def get_version_info(module_version_string="", silent=False): # For dev snapshots (alpha, beta, RC, etc.) we do not commit status change to Git, # so this define provides a way to override it without having to modify the source. - if os.getenv("GODOT_VERSION_STATUS") != None: + if os.getenv("GODOT_VERSION_STATUS") is not None: version_info["status"] = str(os.getenv("GODOT_VERSION_STATUS")) if not silent: print(f"Using version status '{version_info['status']}', overriding the original '{version.status}'.") @@ -435,7 +432,7 @@ def module_check_dependencies(self, module): required_deps = self.module_dependencies[module][0] if module in self.module_dependencies else [] for dep in required_deps: opt = "module_{}_enabled".format(dep) - if not opt in self or not self[opt]: + if opt not in self or not self[opt]: missing_deps.append(dep) if missing_deps != []: @@ -450,7 +447,6 @@ def module_check_dependencies(self, module): def sort_module_list(env): - out = OrderedDict() deps = {k: v[0] + list(filter(lambda x: x in env.module_list, v[1])) for k, v in env.module_dependencies.items()} frontier = list(env.module_list.keys()) @@ -650,7 +646,7 @@ def detect_visual_c_compiler_version(tools_env): def find_visual_c_batch_file(env): - from SCons.Tool.MSCommon.vc import get_default_version, get_host_target, find_batch_file, find_vc_pdir + from SCons.Tool.MSCommon.vc import find_batch_file, find_vc_pdir, get_default_version, get_host_target msvc_version = get_default_version(env) @@ -696,10 +692,7 @@ def glob_recursive(pattern, node="."): def add_to_vs_project(env, sources): for x in sources: - if type(x) == type(""): - fname = env.File(x).path - else: - fname = env.File(x)[0].path + fname = env.File(x).path if isinstance(x, str) else env.File(x)[0].path pieces = fname.split(".") if len(pieces) > 0: basename = pieces[0] @@ -884,7 +877,8 @@ def show_progress(env): return import sys - from SCons.Script import Progress, Command, AlwaysBuild + + from SCons.Script import AlwaysBuild, Command, Progress screen = sys.stdout # Progress reporting is not available in non-TTY environments since it @@ -895,7 +889,8 @@ def show_progress(env): node_count_interval = 1 node_count_fname = str(env.Dir("#")) + "/.scons_node_count" - import time, math + import math + import time class cache_progress: # The default is 1 GB cache and 12 hours half life @@ -903,7 +898,7 @@ def show_progress(env): self.path = path self.limit = limit self.exponent_scale = math.log(2) / half_life - if env["verbose"] and path != None: + if env["verbose"] and path is not None: screen.write( "Current cache limit is {} (used: {})\n".format( self.convert_size(limit), self.convert_size(self.get_size(path)) @@ -1049,11 +1044,11 @@ def generate_vs_project(env, original_args, project_name="godot"): if type(f) is Node.FS.Dir: results += glob_recursive_2(pattern, dirs, f) r = Glob(str(node) + "/" + pattern, source=True) - if len(r) > 0 and not str(node) in dirs: + if len(r) > 0 and str(node) not in dirs: d = "" for part in str(node).split("\\"): d += part - if not d in dirs: + if d not in dirs: dirs.append(d) d += "\\" results += r @@ -1066,7 +1061,7 @@ def generate_vs_project(env, original_args, project_name="godot"): if val is not None: try: return _text2bool(val) - except: + except (ValueError, AttributeError): return default else: return default @@ -1239,13 +1234,13 @@ def generate_vs_project(env, original_args, project_name="godot"): others_active = [] for x in envsources: fname = "" - if type(x) == type(""): + if isinstance(x, str): fname = env.File(x).path else: # Some object files might get added directly as a File object and not a list. try: fname = env.File(x)[0].path - except: + except Exception: fname = x.path pass @@ -1324,7 +1319,7 @@ def generate_vs_project(env, original_args, project_name="godot"): itemlist = {} for item in activeItems: key = os.path.dirname(item).replace("\\", "_") - if not key in itemlist: + if key not in itemlist: itemlist[key] = [item] else: itemlist[key] += [item] @@ -1465,14 +1460,14 @@ def generate_vs_project(env, original_args, project_name="godot"): if godot_platform != "windows": configurations += [ f'<ProjectConfiguration Include="editor|{proj_plat}">', - f" <Configuration>editor</Configuration>", + " <Configuration>editor</Configuration>", f" <Platform>{proj_plat}</Platform>", "</ProjectConfiguration>", ] properties += [ f"<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='editor|{proj_plat}'\">", - f" <GodotConfiguration>editor</GodotConfiguration>", + " <GodotConfiguration>editor</GodotConfiguration>", f" <GodotPlatform>{proj_plat}</GodotPlatform>", "</PropertyGroup>", ] diff --git a/misc/dist/html/service-worker.js b/misc/dist/html/service-worker.js index a5da7482f4..5211873e0b 100644 --- a/misc/dist/html/service-worker.js +++ b/misc/dist/html/service-worker.js @@ -160,8 +160,6 @@ self.addEventListener('message', (event) => { caches.delete(CACHE_NAME); } else if (msg === 'update') { self.skipWaiting().then(() => self.clients.claim()).then(() => self.clients.matchAll()).then((all) => all.forEach((c) => c.navigate(c.url))); - } else { - onClientMessage(event); } }); }); diff --git a/misc/scripts/dotnet_format.py b/misc/scripts/dotnet_format.py index 51fd7a1223..51fd7a1223 100644..100755 --- a/misc/scripts/dotnet_format.py +++ b/misc/scripts/dotnet_format.py diff --git a/misc/scripts/file_format.py b/misc/scripts/file_format.py index a4ea544a45..a4ea544a45 100644..100755 --- a/misc/scripts/file_format.py +++ b/misc/scripts/file_format.py diff --git a/misc/scripts/gitignore_check.sh b/misc/scripts/gitignore_check.sh index f162e25391..f162e25391 100644..100755 --- a/misc/scripts/gitignore_check.sh +++ b/misc/scripts/gitignore_check.sh diff --git a/misc/scripts/header_guards.py b/misc/scripts/header_guards.py index b554be5159..b554be5159 100644..100755 --- a/misc/scripts/header_guards.py +++ b/misc/scripts/header_guards.py diff --git a/misc/scripts/install_d3d12_sdk_windows.py b/misc/scripts/install_d3d12_sdk_windows.py index aed80d81a4..d7574e6222 100755 --- a/misc/scripts/install_d3d12_sdk_windows.py +++ b/misc/scripts/install_d3d12_sdk_windows.py @@ -1,15 +1,15 @@ #!/usr/bin/env python import os -import urllib.request import shutil import subprocess import sys +import urllib.request # Enable ANSI escape code support on Windows 10 and later (for colored console output). # <https://github.com/python/cpython/issues/73245> if sys.platform == "win32": - from ctypes import windll, c_int, byref + from ctypes import byref, c_int, windll stdout_handle = windll.kernel32.GetStdHandle(c_int(-11)) mode = c_int(0) diff --git a/modules/SCsub b/modules/SCsub index 739c5de0b5..db0e563dc4 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -1,8 +1,9 @@ #!/usr/bin/env python -import methods import os +import methods + Import("env") env_modules = env.Clone() diff --git a/modules/fbx/editor/editor_scene_importer_ufbx.cpp b/modules/fbx/editor/editor_scene_importer_ufbx.cpp index 3cc919fae2..e0f60fe998 100644 --- a/modules/fbx/editor/editor_scene_importer_ufbx.cpp +++ b/modules/fbx/editor/editor_scene_importer_ufbx.cpp @@ -111,7 +111,7 @@ void EditorSceneFormatImporterUFBX::get_import_options(const String &p_path, void EditorSceneFormatImporterUFBX::handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const { if (!p_import_params.has("fbx/importer")) { - p_import_params["fbx/importer"] = EditorSceneFormatImporterUFBX::FBX_IMPORTER_UFBX; + p_import_params["fbx/importer"] = EditorSceneFormatImporterUFBX::FBX_IMPORTER_FBX2GLTF; } } diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 07d917ea04..1909ca5ab5 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -349,10 +349,11 @@ <param index="1" name="hint_string" type="String" /> <param index="2" name="usage" type="int" enum="PropertyUsageFlags" is_bitfield="true" default="6" /> <description> - Allows you to set a custom hint, hint string, and usage flags for the exported property. Note that there's no validation done in GDScript, it will just pass the hint along to the editor. + Allows you to set a custom hint, hint string, and usage flags for the exported property. Note that there's no validation done in GDScript, it will just pass the parameters to the editor. [codeblock] @export_custom(PROPERTY_HINT_NONE, "suffix:m") var suffix: Vector3 [/codeblock] + [b]Note:[/b] Regardless of the [param usage] value, the [constant PROPERTY_USAGE_SCRIPT_VARIABLE] flag is always added, as with any explicitly declared script variable. </description> </annotation> <annotation name="@export_dir"> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index de56dd5ece..0c58b41fcb 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -396,6 +396,8 @@ bool GDScript::get_property_default_value(const StringName &p_property, Variant } ScriptInstance *GDScript::instance_create(Object *p_this) { + ERR_FAIL_COND_V_MSG(!valid, nullptr, "Script is invalid!"); + GDScript *top = this; while (top->_base) { top = top->_base; @@ -902,6 +904,11 @@ void GDScript::unload_static() const { } Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (unlikely(!valid)) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + GDScript *top = this; while (top) { HashMap<StringName, GDScriptFunction *>::Iterator E = top->member_functions.find(p_method); @@ -924,6 +931,10 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { return true; } + if (unlikely(!valid)) { + return false; + } + const GDScript *top = this; while (top) { { @@ -980,6 +991,10 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) { return true; } + if (unlikely(!valid)) { + return false; + } + GDScript *top = this; while (top) { HashMap<StringName, MemberInfo>::ConstIterator E = top->static_variables_indices.find(p_name); @@ -1014,6 +1029,10 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) { void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const { p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + if (unlikely(!valid)) { + return; + } + List<const GDScript *> classes; const GDScript *top = this; while (top) { @@ -1630,6 +1649,10 @@ GDScript::~GDScript() { ////////////////////////////// bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { + if (unlikely(!script->valid)) { + return false; + } + { HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name); if (E) { @@ -1703,6 +1726,10 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { } bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { + if (unlikely(!script->valid)) { + return false; + } + { HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = script->member_indices.find(p_name); if (E) { @@ -1823,6 +1850,10 @@ void GDScriptInstance::validate_property(PropertyInfo &p_property) const { } void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { + if (unlikely(!script->valid)) { + return; + } + // exported members, not done yet! const GDScript *sptr = script.ptr(); @@ -1901,6 +1932,10 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const } bool GDScriptInstance::property_can_revert(const StringName &p_name) const { + if (unlikely(!script->valid)) { + return false; + } + Variant name = p_name; const Variant *args[1] = { &name }; @@ -1921,6 +1956,10 @@ bool GDScriptInstance::property_can_revert(const StringName &p_name) const { } bool GDScriptInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const { + if (unlikely(!script->valid)) { + return false; + } + Variant name = p_name; const Variant *args[1] = { &name }; @@ -1995,6 +2034,11 @@ void GDScriptInstance::_call_implicit_ready_recursively(GDScript *p_script) { } Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (unlikely(!script->valid)) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + GDScript *sptr = script.ptr(); if (unlikely(p_method == SceneStringName(_ready))) { // Call implicit ready first, including for the super classes recursively. @@ -2012,6 +2056,10 @@ Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_ar } void GDScriptInstance::notification(int p_notification, bool p_reversed) { + if (unlikely(!script->valid)) { + return; + } + //notification is not virtual, it gets called at ALL levels just like in C. Variant value = p_notification; const Variant *args[1] = { &value }; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 38d5c59e0e..a2680c932f 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3394,6 +3394,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } #ifdef DEBUG_ENABLED + // FIXME: No warning for built-in constructors and utilities due to early return. if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL && !(p_call->is_super && p_call->function_name == GDScriptLanguage::get_singleton()->strings._init)) { parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index e62972b949..eeffc13a10 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -3228,7 +3228,11 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri GDScriptCache::add_static_script(p_script); } - return GDScriptCache::finish_compiling(main_script->path); + err = GDScriptCache::finish_compiling(main_script->path); + if (err) { + main_script->valid = false; + } + return err; } String GDScriptCompiler::get_error() const { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index cae85d7c70..4ae39d80cd 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -911,6 +911,29 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a option.insert_text = option.display.quote(p_quote_style); r_result.insert(option.display, option); } + } else if (p_annotation->name == SNAME("@export_custom")) { + switch (p_argument) { + case 0: { + static HashMap<StringName, int64_t> items; + if (unlikely(items.is_empty())) { + CoreConstants::get_enum_values(SNAME("PropertyHint"), &items); + } + for (const KeyValue<StringName, int64_t> &item : items) { + ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT); + r_result.insert(option.display, option); + } + } break; + case 2: { + static HashMap<StringName, int64_t> items; + if (unlikely(items.is_empty())) { + CoreConstants::get_enum_values(SNAME("PropertyUsageFlags"), &items); + } + for (const KeyValue<StringName, int64_t> &item : items) { + ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT); + r_result.insert(option.display, option); + } + } break; + } } else if (p_annotation->name == SNAME("@warning_ignore")) { for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) { ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); @@ -2065,6 +2088,12 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, found = false; } + // If the found type was not fully analyzed we analyze it now. + if (found && r_type.type.kind == GDScriptParser::DataType::CLASS && !r_type.type.class_type->resolved_body) { + Error err; + Ref<GDScriptParserRef> r = GDScriptCache::get_parser(r_type.type.script_path, GDScriptParserRef::FULLY_SOLVED, err); + } + // Check type hint last. For collections we want chance to get the actual value first // This way we can detect types from the content of dictionaries and arrays if (!found && p_expression->get_datatype().is_hard_type()) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 9be9307b8a..9a4c92f601 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -101,7 +101,6 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation); // Export annotations. register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>); - register_annotation(MethodInfo("@export_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>); register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true); register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true); register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>); @@ -121,6 +120,7 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>); register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>); register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>); + register_annotation(MethodInfo("@export_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_storage_annotation); register_annotation(MethodInfo("@export_custom", PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_ENUM, "PropertyHint"), PropertyInfo(Variant::STRING, "hint_string"), PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_BITFIELD, "PropertyUsageFlags")), AnnotationInfo::VARIABLE, &GDScriptParser::export_custom_annotation, varray(PROPERTY_USAGE_DEFAULT)); // Export grouping annotations. register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>); @@ -1877,6 +1877,10 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { case Node::CALL: // Fine. break; + case Node::PRELOAD: + // `preload` is a function-like keyword. + push_warning(expression, GDScriptWarning::RETURN_VALUE_DISCARDED, "preload"); + break; case Node::LAMBDA: // Standalone lambdas can't be used, so make this an error. push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression); @@ -4295,7 +4299,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node case GDScriptParser::DataType::BUILTIN: variable->export_info.type = export_type.builtin_type; variable->export_info.hint = PROPERTY_HINT_NONE; - variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type); + variable->export_info.hint_string = String(); break; case GDScriptParser::DataType::NATIVE: if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) { @@ -4396,12 +4400,6 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node push_error(_get_annotation_error_string(p_annotation->name, expected_types, variable->get_datatype()), p_annotation); return false; } - } else if (p_annotation->name == SNAME("@export_storage")) { - use_default_variable_type_check = false; // Can be applied to a variable of any type. - - // Save the info because the compiler uses export info for overwriting member info. - variable->export_info = export_type.to_property_info(variable->identifier->name); - variable->export_info.usage |= PROPERTY_USAGE_STORAGE; } if (use_default_variable_type_check) { @@ -4421,19 +4419,50 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node if (variable->export_info.hint) { hint_prefix += "/" + itos(variable->export_info.hint); } + variable->export_info.type = original_export_type_builtin; variable->export_info.hint = PROPERTY_HINT_TYPE_STRING; variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string; - variable->export_info.type = original_export_type_builtin; + variable->export_info.usage = PROPERTY_USAGE_DEFAULT; + variable->export_info.class_name = StringName(); } return true; } +// For `@export_storage` and `@export_custom`, there is no need to check the variable type, argument values, +// or handle array exports in a special way, so they are implemented as separate methods. + +bool GDScriptParser::export_storage_annotation(const AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) { + ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name)); + + VariableNode *variable = static_cast<VariableNode *>(p_node); + if (variable->is_static) { + push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation); + return false; + } + if (variable->exported) { + push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation); + return false; + } + + variable->exported = true; + + // Save the info because the compiler uses export info for overwriting member info. + variable->export_info = variable->get_datatype().to_property_info(variable->identifier->name); + variable->export_info.usage |= PROPERTY_USAGE_STORAGE; + + return true; +} + bool GDScriptParser::export_custom_annotation(const AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) { ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name)); ERR_FAIL_COND_V_MSG(p_annotation->resolved_arguments.size() < 2, false, R"(Annotation "@export_custom" requires 2 arguments.)"); VariableNode *variable = static_cast<VariableNode *>(p_node); + if (variable->is_static) { + push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation); + return false; + } if (variable->exported) { push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation); return false; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 4c11fa7f8b..96358165c0 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1497,6 +1497,7 @@ private: bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); template <PropertyHint t_hint, Variant::Type t_type> bool export_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); + bool export_storage_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); bool export_custom_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); template <PropertyUsageFlags t_usage> bool export_group_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 48a0abe617..611a9ad2d9 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -74,7 +74,7 @@ String GDScriptWarning::get_message() const { case UNREACHABLE_PATTERN: return "Unreachable pattern (pattern after wildcard or bind)."; case STANDALONE_EXPRESSION: - return "Standalone expression (the line has no effect)."; + return "Standalone expression (the line may have no effect)."; case STANDALONE_TERNARY: return "Standalone ternary operator: the return value is being discarded."; case INCOMPATIBLE_TERNARY: diff --git a/modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.out b/modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.out index 505af5f1f3..0d96f8c021 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.out +++ b/modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.out @@ -1,11 +1,11 @@ GDTEST_OK var test_1: Dictionary - hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_2: TestExportEnumAsDictionary.MyEnum - hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM + hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"TestExportEnumAsDictionary.MyEnum" var test_3: Dictionary - hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_4: TestExportEnumAsDictionary.MyEnum - hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM + hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"TestExportEnumAsDictionary.MyEnum" var test_5: TestExportEnumAsDictionary.MyEnum - hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM + hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"TestExportEnumAsDictionary.MyEnum" diff --git a/modules/gdscript/tests/scripts/parser/features/annotations.out b/modules/gdscript/tests/scripts/parser/features/annotations.out index 2ba9dd7496..6516672820 100644 --- a/modules/gdscript/tests/scripts/parser/features/annotations.out +++ b/modules/gdscript/tests/scripts/parser/features/annotations.out @@ -1,25 +1,25 @@ GDTEST_OK var test_1: int = null - hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_2: int = null - hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_3: int = null - hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_4: int = null - hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_5: int = 0 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_6: int = 0 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_7: int = 42 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_8: int = 0 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_9: int = 0 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_10: int = 0 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_11: int = 0 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_12: int = 0 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" diff --git a/modules/gdscript/tests/scripts/parser/features/export_arrays.out b/modules/gdscript/tests/scripts/parser/features/export_arrays.out index acbf389645..f1522d096f 100644 --- a/modules/gdscript/tests/scripts/parser/features/export_arrays.out +++ b/modules/gdscript/tests/scripts/parser/features/export_arrays.out @@ -1,139 +1,139 @@ GDTEST_OK var test_dir: Array - hint=TYPE_STRING hint_string="String/DIR:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<DIR>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_dir_packed: PackedStringArray - hint=TYPE_STRING hint_string="String/DIR:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<DIR>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_file: Array - hint=TYPE_STRING hint_string="String/FILE:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<FILE>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_file_packed: PackedStringArray - hint=TYPE_STRING hint_string="String/FILE:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<FILE>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_global_dir: Array - hint=TYPE_STRING hint_string="String/GLOBAL_DIR:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<GLOBAL_DIR>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_global_dir_packed: PackedStringArray - hint=TYPE_STRING hint_string="String/GLOBAL_DIR:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<GLOBAL_DIR>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_global_file: Array - hint=TYPE_STRING hint_string="String/GLOBAL_FILE:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<GLOBAL_FILE>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_global_file_packed: PackedStringArray - hint=TYPE_STRING hint_string="String/GLOBAL_FILE:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<GLOBAL_FILE>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag: Array - hint=TYPE_STRING hint_string="int/FLAGS:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<FLAGS>:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_packed_byte: PackedByteArray - hint=TYPE_STRING hint_string="int/FLAGS:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<FLAGS>:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_packed32: PackedInt32Array - hint=TYPE_STRING hint_string="int/FLAGS:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<FLAGS>:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_packed64: PackedInt64Array - hint=TYPE_STRING hint_string="int/FLAGS:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<FLAGS>:A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_nav: Array - hint=TYPE_STRING hint_string="int/LAYERS_2D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_nav_packed_byte: PackedByteArray - hint=TYPE_STRING hint_string="int/LAYERS_2D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_nav_packed32: PackedInt32Array - hint=TYPE_STRING hint_string="int/LAYERS_2D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_nav_packed64: PackedInt64Array - hint=TYPE_STRING hint_string="int/LAYERS_2D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_phys: Array - hint=TYPE_STRING hint_string="int/LAYERS_2D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_phys_packed_byte: PackedByteArray - hint=TYPE_STRING hint_string="int/LAYERS_2D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_phys_packed32: PackedInt32Array - hint=TYPE_STRING hint_string="int/LAYERS_2D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_phys_packed64: PackedInt64Array - hint=TYPE_STRING hint_string="int/LAYERS_2D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_render: Array - hint=TYPE_STRING hint_string="int/LAYERS_2D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_render_packed_byte: PackedByteArray - hint=TYPE_STRING hint_string="int/LAYERS_2D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_render_packed32: PackedInt32Array - hint=TYPE_STRING hint_string="int/LAYERS_2D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_2d_render_packed64: PackedInt64Array - hint=TYPE_STRING hint_string="int/LAYERS_2D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_2D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_nav: Array - hint=TYPE_STRING hint_string="int/LAYERS_3D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_nav_packed_byte: PackedByteArray - hint=TYPE_STRING hint_string="int/LAYERS_3D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_nav_packed32: PackedInt32Array - hint=TYPE_STRING hint_string="int/LAYERS_3D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_nav_packed64: PackedInt64Array - hint=TYPE_STRING hint_string="int/LAYERS_3D_NAVIGATION:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_NAVIGATION>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_phys: Array - hint=TYPE_STRING hint_string="int/LAYERS_3D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_phys_packed_byte: PackedByteArray - hint=TYPE_STRING hint_string="int/LAYERS_3D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_phys_packed32: PackedInt32Array - hint=TYPE_STRING hint_string="int/LAYERS_3D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_phys_packed64: PackedInt64Array - hint=TYPE_STRING hint_string="int/LAYERS_3D_PHYSICS:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_PHYSICS>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_render: Array - hint=TYPE_STRING hint_string="int/LAYERS_3D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_render_packed_byte: PackedByteArray - hint=TYPE_STRING hint_string="int/LAYERS_3D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_render_packed32: PackedInt32Array - hint=TYPE_STRING hint_string="int/LAYERS_3D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_bit_flag_3d_render_packed64: PackedInt64Array - hint=TYPE_STRING hint_string="int/LAYERS_3D_RENDER:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<LAYERS_3D_RENDER>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_multiline: Array - hint=TYPE_STRING hint_string="String/MULTILINE_TEXT:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<MULTILINE_TEXT>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_multiline_packed: PackedStringArray - hint=TYPE_STRING hint_string="String/MULTILINE_TEXT:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<MULTILINE_TEXT>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_placeholder: Array - hint=TYPE_STRING hint_string="String/PLACEHOLDER_TEXT:Placeholder" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<PLACEHOLDER_TEXT>:Placeholder" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_placeholder_packed: PackedStringArray - hint=TYPE_STRING hint_string="String/PLACEHOLDER_TEXT:Placeholder" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<PLACEHOLDER_TEXT>:Placeholder" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_int: Array - hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_int_packed_byte: PackedByteArray - hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_int_packed32: PackedInt32Array - hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_int_packed64: PackedInt64Array - hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_int_float_step: Array - hint=TYPE_STRING hint_string="int/RANGE:1,10,0.01" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10,0.01" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_float: Array - hint=TYPE_STRING hint_string="float/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<float>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_float_packed32: PackedFloat32Array - hint=TYPE_STRING hint_string="float/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<float>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_float_packed64: PackedFloat64Array - hint=TYPE_STRING hint_string="float/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<float>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_exp_easing: Array - hint=TYPE_STRING hint_string="float/EXP_EASING:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<float>/<EXP_EASING>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_exp_easing_packed32: PackedFloat32Array - hint=TYPE_STRING hint_string="float/EXP_EASING:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<float>/<EXP_EASING>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_exp_easing_packed64: PackedFloat64Array - hint=TYPE_STRING hint_string="float/EXP_EASING:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<float>/<EXP_EASING>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_node_path: Array - hint=TYPE_STRING hint_string="NodePath/NODE_PATH_VALID_TYPES:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<NodePath>/<NODE_PATH_VALID_TYPES>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_color: Array - hint=TYPE_STRING hint_string="Color/COLOR_NO_ALPHA:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<Color>/<COLOR_NO_ALPHA>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_color_packed: PackedColorArray - hint=TYPE_STRING hint_string="Color/COLOR_NO_ALPHA:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<Color>/<COLOR_NO_ALPHA>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_byte_array: PackedByteArray - hint=TYPE_STRING hint_string="int:int" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_int32_array: PackedInt32Array - hint=TYPE_STRING hint_string="int:int" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_int64_array: PackedInt64Array - hint=TYPE_STRING hint_string="int:int" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_float32_array: PackedFloat32Array - hint=TYPE_STRING hint_string="float:float" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<float>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_float64_array: PackedFloat64Array - hint=TYPE_STRING hint_string="float:float" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<float>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_color_array: PackedColorArray - hint=TYPE_STRING hint_string="Color:Color" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<Color>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_vector2_array: PackedVector2Array - hint=TYPE_STRING hint_string="Vector2:Vector2" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<Vector2>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_vector3_array: PackedVector3Array - hint=TYPE_STRING hint_string="Vector3:Vector3" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<Vector3>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_vector4_array: PackedVector4Array - hint=TYPE_STRING hint_string="Vector4:Vector4" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<Vector4>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_weak_packed_byte_array: PackedByteArray - hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_weak_packed_int32_array: PackedInt32Array - hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_weak_packed_int64_array: PackedInt64Array - hint=TYPE_STRING hint_string="int/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_weak_packed_float32_array: PackedFloat32Array - hint=TYPE_STRING hint_string="float/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<float>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_weak_packed_float64_array: PackedFloat64Array - hint=TYPE_STRING hint_string="float/RANGE:1,10" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<float>/<RANGE>:1,10" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_noalpha_weak_packed_color_array: PackedColorArray - hint=TYPE_STRING hint_string="Color/COLOR_NO_ALPHA:" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<Color>/<COLOR_NO_ALPHA>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" diff --git a/modules/gdscript/tests/scripts/parser/features/export_enum.out b/modules/gdscript/tests/scripts/parser/features/export_enum.out index c87f9b17f0..31d3fa8902 100644 --- a/modules/gdscript/tests/scripts/parser/features/export_enum.out +++ b/modules/gdscript/tests/scripts/parser/features/export_enum.out @@ -1,41 +1,41 @@ GDTEST_OK var test_untyped: int = null - hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_with_values: int = null - hint=ENUM hint_string="Red:10,Green:20,Blue:30" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="Red:10,Green:20,Blue:30" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_variant: int = null - hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_int: int = 0 - hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_string: String = "" - hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_array_int: Array = Array[int]([]) - hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_array_string: Array = Array[String]([]) - hint=TYPE_STRING hint_string="String/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_byte_array: PackedByteArray = PackedByteArray() - hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_int32_array: PackedInt32Array = PackedInt32Array() - hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_int64_array: PackedInt64Array = PackedInt64Array() - hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_weak_packed_string_array: PackedStringArray = PackedStringArray() - hint=TYPE_STRING hint_string="String/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_hard_variant: int = null - hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_hard_int: int = 0 - hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_hard_string: String = "" - hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=ENUM hint_string="Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_hard_array_int: Array = Array[int]([]) - hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_hard_array_string: Array = Array[String]([]) - hint=TYPE_STRING hint_string="String/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_variant_array_int: Array = Array[int]([]) - hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_variant_packed_int32_array: PackedInt32Array = PackedInt32Array() - hint=TYPE_STRING hint_string="int/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<int>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_variant_array_string: Array = Array[String]([]) - hint=TYPE_STRING hint_string="String/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_variant_packed_string_array: PackedStringArray = PackedStringArray() - hint=TYPE_STRING hint_string="String/ENUM:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE + hint=TYPE_STRING hint_string="<String>/<ENUM>:Red,Green,Blue" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.gd b/modules/gdscript/tests/scripts/parser/features/export_variable.gd index 2a218774de..8b343de5ef 100644 --- a/modules/gdscript/tests/scripts/parser/features/export_variable.gd +++ b/modules/gdscript/tests/scripts/parser/features/export_variable.gd @@ -2,19 +2,43 @@ extends Node const Utils = preload("../../utils.notest.gd") +# Built-in types. @export var test_weak_int = 1 @export var test_hard_int: int = 2 -@export_storage var test_storage_untyped -@export_storage var test_storage_weak_int = 3 # Property info still `Variant`, unlike `@export`. -@export_storage var test_storage_hard_int: int = 4 @export_range(0, 100) var test_range = 100 @export_range(0, 100, 1) var test_range_step = 101 @export_range(0, 100, 1, "or_greater") var test_range_step_or_greater = 102 @export var test_color: Color @export_color_no_alpha var test_color_no_alpha: Color @export_node_path("Sprite2D", "Sprite3D", "Control", "Node") var test_node_path := ^"hello" -@export var test_node: Node -@export var test_node_array: Array[Node] + +# Enums. +@export var test_side: Side +@export var test_atm: AutoTranslateMode + +# Resources and nodes. +@export var test_image: Image +@export var test_timer: Timer + +# Arrays. +@export var test_array: Array +@export var test_array_bool: Array[bool] +@export var test_array_array: Array[Array] +@export var test_array_side: Array[Side] +@export var test_array_atm: Array[AutoTranslateMode] +@export var test_array_image: Array[Image] +@export var test_array_timer: Array[Timer] + +# `@export_storage`. +@export_storage var test_storage_untyped +@export_storage var test_storage_weak_int = 3 # Property info still `Variant`, unlike `@export`. +@export_storage var test_storage_hard_int: int = 4 + +# `@export_custom`. +# NOTE: `PROPERTY_USAGE_NIL_IS_VARIANT` flag will be removed. +@export_custom(PROPERTY_HINT_ENUM, "A,B,C") var test_export_custom_untyped +@export_custom(PROPERTY_HINT_ENUM, "A,B,C") var test_export_custom_weak_int = 5 +@export_custom(PROPERTY_HINT_ENUM, "A,B,C") var test_export_custom_hard_int: int = 6 func test(): for property in get_property_list(): diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.out b/modules/gdscript/tests/scripts/parser/features/export_variable.out index b3f9d0ca9c..99d7b27130 100644 --- a/modules/gdscript/tests/scripts/parser/features/export_variable.out +++ b/modules/gdscript/tests/scripts/parser/features/export_variable.out @@ -1,27 +1,51 @@ GDTEST_OK var test_weak_int: int = 1 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_hard_int: int = 2 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE -var test_storage_untyped: Variant = null - hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE|NIL_IS_VARIANT -var test_storage_weak_int: Variant = 3 - hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE|NIL_IS_VARIANT -var test_storage_hard_int: int = 4 - hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range: int = 100 - hint=RANGE hint_string="0,100" usage=DEFAULT|SCRIPT_VARIABLE + hint=RANGE hint_string="0,100" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_step: int = 101 - hint=RANGE hint_string="0,100,1" usage=DEFAULT|SCRIPT_VARIABLE + hint=RANGE hint_string="0,100,1" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_range_step_or_greater: int = 102 - hint=RANGE hint_string="0,100,1,or_greater" usage=DEFAULT|SCRIPT_VARIABLE + hint=RANGE hint_string="0,100,1,or_greater" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_color: Color = Color(0, 0, 0, 1) - hint=NONE hint_string="Color" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_color_no_alpha: Color = Color(0, 0, 0, 1) - hint=COLOR_NO_ALPHA hint_string="" usage=DEFAULT|SCRIPT_VARIABLE + hint=COLOR_NO_ALPHA hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" var test_node_path: NodePath = NodePath("hello") - hint=NODE_PATH_VALID_TYPES hint_string="Sprite2D,Sprite3D,Control,Node" usage=DEFAULT|SCRIPT_VARIABLE -var test_node: Node = null - hint=NODE_TYPE hint_string="Node" usage=DEFAULT|SCRIPT_VARIABLE -var test_node_array: Array = Array[Node]([]) - hint=TYPE_STRING hint_string="Object/NODE_TYPE:Node" usage=DEFAULT|SCRIPT_VARIABLE + hint=NODE_PATH_VALID_TYPES hint_string="Sprite2D,Sprite3D,Control,Node" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" +var test_side: Side = 0 + hint=ENUM hint_string="Side Left:0,Side Top:1,Side Right:2,Side Bottom:3" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"Side" +var test_atm: Node.AutoTranslateMode = 0 + hint=ENUM hint_string="Auto Translate Mode Inherit:0,Auto Translate Mode Always:1,Auto Translate Mode Disabled:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"Node.AutoTranslateMode" +var test_image: Image = null + hint=RESOURCE_TYPE hint_string="Image" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"Image" +var test_timer: Timer = null + hint=NODE_TYPE hint_string="Timer" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"Timer" +var test_array: Array = [] + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" +var test_array_bool: Array = Array[bool]([]) + hint=TYPE_STRING hint_string="<bool>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" +var test_array_array: Array = Array[Array]([]) + hint=TYPE_STRING hint_string="<Array>:" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" +var test_array_side: Array = Array[int]([]) + hint=TYPE_STRING hint_string="<int>/<ENUM>:Side Left:0,Side Top:1,Side Right:2,Side Bottom:3" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" +var test_array_atm: Array = Array[int]([]) + hint=TYPE_STRING hint_string="<int>/<ENUM>:Auto Translate Mode Inherit:0,Auto Translate Mode Always:1,Auto Translate Mode Disabled:2" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" +var test_array_image: Array = Array[Image]([]) + hint=TYPE_STRING hint_string="<Object>/<RESOURCE_TYPE>:Image" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" +var test_array_timer: Array = Array[Timer]([]) + hint=TYPE_STRING hint_string="<Object>/<NODE_TYPE>:Timer" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" +var test_storage_untyped: Variant = null + hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE|NIL_IS_VARIANT class_name=&"" +var test_storage_weak_int: Variant = 3 + hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE|NIL_IS_VARIANT class_name=&"" +var test_storage_hard_int: int = 4 + hint=NONE hint_string="" usage=STORAGE|SCRIPT_VARIABLE class_name=&"" +var test_export_custom_untyped: null = null + hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" +var test_export_custom_weak_int: int = 5 + hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" +var test_export_custom_hard_int: int = 6 + hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd index 00598e4d50..f8f70b8cc3 100644 --- a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd +++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd @@ -4,3 +4,4 @@ func i_return_int() -> int: func test(): i_return_int() + preload("../../utils.notest.gd") # `preload` is a function-like keyword. diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out index f2db4e9307..107051df6c 100644 --- a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out +++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out @@ -3,3 +3,7 @@ GDTEST_OK >> Line: 6 >> RETURN_VALUE_DISCARDED >> The function "i_return_int()" returns a value that will be discarded if not used. +>> WARNING +>> Line: 7 +>> RETURN_VALUE_DISCARDED +>> The function "preload()" returns a value that will be discarded if not used. diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd index dc4223ec2d..74f42b012b 100644 --- a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd +++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd @@ -6,3 +6,16 @@ func test(): Vector3.ZERO [true, false] float(125) + # The following statements should not produce `STANDALONE_EXPRESSION`: + var _a = 1 + _a = 2 # Assignment is a local (or global) side effect. + @warning_ignore("redundant_await") + await 3 # The `await` operand is usually a coroutine or a signal. + absi(4) # A call (in general) can have side effects. + @warning_ignore("return_value_discarded") + preload("../../utils.notest.gd") # A static initializer may have side effects. + """ + Python-like "comment". + """ + @warning_ignore("standalone_ternary") + 1 if 2 else 3 # Produces `STANDALONE_TERNARY` instead. diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out index a2c67a6e51..72c659c952 100644 --- a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out +++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out @@ -2,16 +2,16 @@ GDTEST_OK >> WARNING >> Line: 3 >> STANDALONE_EXPRESSION ->> Standalone expression (the line has no effect). +>> Standalone expression (the line may have no effect). >> WARNING >> Line: 4 >> STANDALONE_EXPRESSION ->> Standalone expression (the line has no effect). +>> Standalone expression (the line may have no effect). >> WARNING >> Line: 6 >> STANDALONE_EXPRESSION ->> Standalone expression (the line has no effect). +>> Standalone expression (the line may have no effect). >> WARNING >> Line: 7 >> STANDALONE_EXPRESSION ->> Standalone expression (the line has no effect). +>> Standalone expression (the line may have no effect). diff --git a/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out index 9387ec50d7..a1e7233078 100644 --- a/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out +++ b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out @@ -1,8 +1,8 @@ GDTEST_OK Not shadowed: Resource var test_1: int = 0 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" @export_category("test_1") - hint=NONE hint_string="" usage=CATEGORY + hint=NONE hint_string="" usage=CATEGORY class_name=&"" var test_2: int = 0 - hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE + hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&"" diff --git a/modules/gdscript/tests/scripts/utils.notest.gd b/modules/gdscript/tests/scripts/utils.notest.gd index 1cf46c179e..7fdd6556ec 100644 --- a/modules/gdscript/tests/scripts/utils.notest.gd +++ b/modules/gdscript/tests/scripts/utils.notest.gd @@ -55,18 +55,18 @@ static func get_human_readable_hint_string(property: Dictionary) -> String: if elem_type_hint.is_valid_int(): elem_type = elem_type_hint.to_int() - type_hint_prefixes += type_string(elem_type) + ":" + type_hint_prefixes += "<%s>:" % type_string(elem_type) else: if elem_type_hint.count("/") != 1: push_error("Invalid PROPERTY_HINT_TYPE_STRING format.") elem_type = elem_type_hint.get_slice("/", 0).to_int() elem_hint = elem_type_hint.get_slice("/", 1).to_int() - type_hint_prefixes += "%s/%s:" % [ - type_string(elem_type), - get_property_hint_name(elem_hint).trim_prefix("PROPERTY_HINT_"), + type_hint_prefixes += "<%s>/<%s>:" % [ + type_string(elem_type), + get_property_hint_name(elem_hint).trim_prefix("PROPERTY_HINT_"), ] - if elem_type < TYPE_ARRAY: + if elem_type < TYPE_ARRAY or hint_string.is_empty(): break return type_hint_prefixes + hint_string @@ -76,10 +76,11 @@ static func get_human_readable_hint_string(property: Dictionary) -> String: static func print_property_extended_info(property: Dictionary, base: Object = null, is_static: bool = false) -> void: print(get_property_signature(property, base, is_static)) - print(' hint=%s hint_string="%s" usage=%s' % [ + print(' hint=%s hint_string="%s" usage=%s class_name=&"%s"' % [ get_property_hint_name(property.hint).trim_prefix("PROPERTY_HINT_"), - get_human_readable_hint_string(property), + get_human_readable_hint_string(property).c_escape(), get_property_usage_string(property.usage).replace("PROPERTY_USAGE_", ""), + property.class_name.c_escape(), ]) diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp index e4ed79d15e..af9b2f2363 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp @@ -59,6 +59,9 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t int32_t enum_option = p_options["gltf/embedded_image_handling"]; state->set_handle_binary_image(enum_option); } + if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) { + state->set_import_as_skeleton_bones(true); + } Error err = gltf->append_from_file(p_path, state, p_flags); if (err != OK) { if (r_err) { diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index cc2fa18449..7ac7bd8088 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -428,6 +428,7 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i SWAP(edge.a, edge.b); SWAP(edge.na, edge.nb); SWAP(uv2.a, uv2.b); + SWAP(uv2.indices.x, uv2.indices.y); SWAP(edge_indices.x, edge_indices.y); } diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py index f9709362eb..efbf298f99 100755 --- a/modules/mono/build_scripts/build_assemblies.py +++ b/modules/mono/build_scripts/build_assemblies.py @@ -5,7 +5,7 @@ import os.path import shlex import subprocess from dataclasses import dataclass -from typing import Optional, List +from typing import List, Optional def find_dotnet_cli(): @@ -304,9 +304,7 @@ def generate_sdk_package_versions(): <GodotVersionConstants>{1}</GodotVersionConstants> </PropertyGroup> </Project> -""".format( - version_str, ";".join(version_defines) - ) +""".format(version_str, ";".join(version_defines)) # We write in ../SdkPackageVersions.props. with open(os.path.join(dirname(script_path), "SdkPackageVersions.props"), "w", encoding="utf-8", newline="\n") as f: @@ -323,9 +321,7 @@ def generate_sdk_package_versions(): public const string VersionDocsUrl = "https://docs.godotengine.org/en/{docs_branch}"; }} }} -""".format( - **version_info - ) +""".format(**version_info) generators_dir = os.path.join( dirname(script_path), diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index e5469c4980..98c50dcc22 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -1,7 +1,3 @@ -import os -import os.path - - def is_desktop(platform): return platform in ["windows", "macos", "linuxbsd"] diff --git a/modules/mono/config.py b/modules/mono/config.py index 3d087c9e27..5ebdb83b36 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -12,7 +12,7 @@ def configure(env): # Check if the platform has marked mono as supported. supported = env.get("supported", []) - if not "mono" in supported: + if "mono" not in supported: raise RuntimeError("This module does not currently support building for this platform") env.add_module_version_string("mono") diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index b559ca20b2..7e14259d5a 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -5,7 +5,6 @@ </brief_description> <description> This class represents a C# script. It is the C# equivalent of the [GDScript] class and is only available in Mono-enabled Godot builds. - See also [GodotSharp]. </description> <tutorials> <link title="C# documentation index">$DOCS_URL/tutorials/scripting/c_sharp/index.html</link> diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml deleted file mode 100644 index 969ca14350..0000000000 --- a/modules/mono/doc_classes/GodotSharp.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="GodotSharp" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> - <brief_description> - Bridge between Godot and the Mono runtime (Mono-enabled builds only). - </brief_description> - <description> - This class is a bridge between Godot and the Mono runtime. It exposes several low-level operations and is only available in Mono-enabled Godot builds. - See also [CSharpScript]. - </description> - <tutorials> - </tutorials> - <methods> - <method name="is_runtime_initialized"> - <return type="bool" /> - <description> - Returns [code]true[/code] if the .NET runtime is initialized, [code]false[/code] otherwise. - </description> - </method> - </methods> -</class> diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsView.cs index b6d6d9ebf8..35a62a0eab 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsView.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsView.cs @@ -280,7 +280,7 @@ namespace GodotTools.Build if (_problemsContextMenu.ItemCount > 0) { - _problemsContextMenu.Position = (Vector2I)(_problemsTree.GlobalPosition + position); + _problemsContextMenu.Position = (Vector2I)(GetScreenPosition() + position); _problemsContextMenu.Popup(); } } diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 05dacd28fb..03d8b4eab6 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -143,7 +143,7 @@ bool godot_icall_Internal_IsAssembliesReloadingNeeded() { void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) { #ifdef GD_MONO_HOT_RELOAD - mono_bind::GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload); + callable_mp(mono_bind::GodotSharp::get_singleton(), &mono_bind::GodotSharp::reload_assemblies).call_deferred(p_soft_reload); #endif } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs index ab7f8ede44..33f0850a8d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs @@ -318,9 +318,9 @@ namespace Godot Vector3 ofs = _position + halfExtents; return ofs + new Vector3( - dir.X > 0f ? -halfExtents.X : halfExtents.X, - dir.Y > 0f ? -halfExtents.Y : halfExtents.Y, - dir.Z > 0f ? -halfExtents.Z : halfExtents.Z); + dir.X > 0f ? halfExtents.X : -halfExtents.X, + dir.Y > 0f ? halfExtents.Y : -halfExtents.Y, + dir.Z > 0f ? halfExtents.Z : -halfExtents.Z); } /// <summary> diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 0e34616951..48caae8523 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -564,11 +564,7 @@ namespace mono_bind { GodotSharp *GodotSharp::singleton = nullptr; -bool GodotSharp::_is_runtime_initialized() { - return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized(); -} - -void GodotSharp::_reload_assemblies(bool p_soft_reload) { +void GodotSharp::reload_assemblies(bool p_soft_reload) { #ifdef GD_MONO_HOT_RELOAD CRASH_COND(CSharpLanguage::get_singleton() == nullptr); // This method may be called more than once with `call_deferred`, so we need to check @@ -579,11 +575,6 @@ void GodotSharp::_reload_assemblies(bool p_soft_reload) { #endif } -void GodotSharp::_bind_methods() { - ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::_is_runtime_initialized); - ClassDB::bind_method(D_METHOD("_reload_assemblies"), &GodotSharp::_reload_assemblies); -} - GodotSharp::GodotSharp() { singleton = this; } diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 0cb087db57..614bfc63fb 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -167,18 +167,14 @@ namespace mono_bind { class GodotSharp : public Object { GDCLASS(GodotSharp, Object); - friend class GDMono; - - void _reload_assemblies(bool p_soft_reload); - bool _is_runtime_initialized(); - protected: static GodotSharp *singleton; - static void _bind_methods(); public: static GodotSharp *get_singleton() { return singleton; } + void reload_assemblies(bool p_soft_reload); + GodotSharp(); ~GodotSharp(); }; diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp index beaa50ecb2..4d5426d96f 100644 --- a/modules/mono/register_types.cpp +++ b/modules/mono/register_types.cpp @@ -49,9 +49,6 @@ void initialize_mono_module(ModuleInitializationLevel p_level) { _godotsharp = memnew(mono_bind::GodotSharp); - GDREGISTER_CLASS(mono_bind::GodotSharp); - Engine::get_singleton()->add_singleton(Engine::Singleton("GodotSharp", mono_bind::GodotSharp::get_singleton())); - script_language_cs = memnew(CSharpLanguage); script_language_cs->set_language_index(ScriptServer::get_language_count()); ScriptServer::register_language(script_language_cs); diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp index cc3bbdbf01..df0bdc9537 100644 --- a/modules/navigation/3d/nav_mesh_generator_3d.cpp +++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp @@ -895,9 +895,22 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation Vector<Vector3> nav_vertices; + HashMap<Vector3, int> recast_vertex_to_native_index; + LocalVector<int> recast_index_to_native_index; + recast_index_to_native_index.resize(detail_mesh->nverts); + for (int i = 0; i < detail_mesh->nverts; i++) { const float *v = &detail_mesh->verts[i * 3]; - nav_vertices.push_back(Vector3(v[0], v[1], v[2])); + const Vector3 vertex = Vector3(v[0], v[1], v[2]); + int *existing_index_ptr = recast_vertex_to_native_index.getptr(vertex); + if (!existing_index_ptr) { + int new_index = recast_vertex_to_native_index.size(); + recast_index_to_native_index[i] = new_index; + recast_vertex_to_native_index[vertex] = new_index; + nav_vertices.push_back(vertex); + } else { + recast_index_to_native_index[i] = *existing_index_ptr; + } } p_navigation_mesh->set_vertices(nav_vertices); p_navigation_mesh->clear_polygons(); @@ -912,9 +925,14 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation Vector<int> nav_indices; nav_indices.resize(3); // Polygon order in recast is opposite than godot's - nav_indices.write[0] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0])); - nav_indices.write[1] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2])); - nav_indices.write[2] = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1])); + int index1 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 0])); + int index2 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 2])); + int index3 = ((int)(detail_mesh_bverts + detail_mesh_tris[j * 4 + 1])); + + nav_indices.write[0] = recast_index_to_native_index[index1]; + nav_indices.write[1] = recast_index_to_native_index[index2]; + nav_indices.write[2] = recast_index_to_native_index[index3]; + p_navigation_mesh->add_polygon(nav_indices); } } diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index 0443fec4a0..0960b2ad36 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -119,6 +119,7 @@ void NoiseTexture2D::_set_texture_image(const Ref<Image> &p_image) { } else { texture = RS::get_singleton()->texture_2d_create(p_image); } + RS::get_singleton()->texture_set_path(texture, get_path()); } emit_changed(); } diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml index 79aa547c52..338d632524 100644 --- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml +++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml @@ -84,6 +84,12 @@ Called right before the OpenXR instance is destroyed. </description> </method> + <method name="_on_main_swapchains_created" qualifiers="virtual"> + <return type="void" /> + <description> + Called right after the main swapchains are (re)created. + </description> + </method> <method name="_on_pre_render" qualifiers="virtual"> <return type="void" /> <description> diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index ce03df0b30..8d05657afc 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_extension_wrapper.h @@ -84,6 +84,7 @@ public: // This is when controller data is queried and made available to game logic. virtual void on_process() {} virtual void on_pre_render() {} // `on_pre_render` is called right before we start rendering our XR viewports. + virtual void on_main_swapchains_created() {} // `on_main_swapchains_created` is called right after our main swapchains are (re)created. virtual void on_pre_draw_viewport(RID p_render_target) {} // `on_pre_draw_viewport` is called right before we start rendering this viewport virtual void on_post_draw_viewport(RID p_render_target) {} // `on_port_draw_viewport` is called right after we start rendering this viewport (note that on Vulkan draw commands may only be queued) diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp index 3b31e1b1f6..e09ca484d5 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp @@ -50,6 +50,7 @@ void OpenXRExtensionWrapperExtension::_bind_methods() { GDVIRTUAL_BIND(_on_session_created, "session"); GDVIRTUAL_BIND(_on_process); GDVIRTUAL_BIND(_on_pre_render); + GDVIRTUAL_BIND(_on_main_swapchains_created); GDVIRTUAL_BIND(_on_session_destroyed); GDVIRTUAL_BIND(_on_state_idle); GDVIRTUAL_BIND(_on_state_ready); @@ -198,6 +199,10 @@ void OpenXRExtensionWrapperExtension::on_pre_render() { GDVIRTUAL_CALL(_on_pre_render); } +void OpenXRExtensionWrapperExtension::on_main_swapchains_created() { + GDVIRTUAL_CALL(_on_main_swapchains_created); +} + void OpenXRExtensionWrapperExtension::on_session_destroyed() { GDVIRTUAL_CALL(_on_session_destroyed); } diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h index 71d2a57ff8..e37853903b 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h +++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h @@ -86,6 +86,7 @@ public: virtual void on_session_created(const XrSession p_session) override; virtual void on_process() override; virtual void on_pre_render() override; + virtual void on_main_swapchains_created() override; virtual void on_session_destroyed() override; GDVIRTUAL0(_on_register_metadata); @@ -95,6 +96,7 @@ public: GDVIRTUAL1(_on_session_created, uint64_t); GDVIRTUAL0(_on_process); GDVIRTUAL0(_on_pre_render); + GDVIRTUAL0(_on_main_swapchains_created); GDVIRTUAL0(_on_session_destroyed); virtual void on_state_idle() override; diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp index bbdd2e3c8a..8ce808dd3c 100644 --- a/modules/openxr/extensions/openxr_fb_foveation_extension.cpp +++ b/modules/openxr/extensions/openxr_fb_foveation_extension.cpp @@ -101,7 +101,7 @@ void *OpenXRFBFoveationExtension::set_swapchain_create_info_and_get_next_pointer } } -void OpenXRFBFoveationExtension::on_state_ready() { +void OpenXRFBFoveationExtension::on_main_swapchains_created() { update_profile(); } @@ -127,26 +127,41 @@ void OpenXRFBFoveationExtension::set_foveation_dynamic(XrFoveationDynamicFB p_fo update_profile(); } -void OpenXRFBFoveationExtension::update_profile() { - if (!is_enabled()) { +void OpenXRFBFoveationExtension::_update_profile() { + // Must be called from rendering thread! + ERR_NOT_ON_RENDER_THREAD; + + OpenXRFBFoveationExtension *fov_ext = OpenXRFBFoveationExtension::get_singleton(); + ERR_FAIL_NULL(fov_ext); + + if (!fov_ext->is_enabled()) { + return; + } + + OpenXRAPI *openxr_api = OpenXRAPI::get_singleton(); + ERR_FAIL_NULL(openxr_api); + + XrSwapchain main_color_swapchain = openxr_api->get_color_swapchain(); + if (main_color_swapchain == XR_NULL_HANDLE) { + // Our swapchain hasn't been created yet, we'll call this again once it has. return; } XrFoveationLevelProfileCreateInfoFB level_profile_create_info; level_profile_create_info.type = XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB; level_profile_create_info.next = nullptr; - level_profile_create_info.level = foveation_level; + level_profile_create_info.level = fov_ext->foveation_level; level_profile_create_info.verticalOffset = 0.0f; - level_profile_create_info.dynamic = foveation_dynamic; + level_profile_create_info.dynamic = fov_ext->foveation_dynamic; XrFoveationProfileCreateInfoFB profile_create_info; profile_create_info.type = XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB; profile_create_info.next = &level_profile_create_info; XrFoveationProfileFB foveation_profile; - XrResult result = xrCreateFoveationProfileFB(OpenXRAPI::get_singleton()->get_session(), &profile_create_info, &foveation_profile); + XrResult result = fov_ext->xrCreateFoveationProfileFB(openxr_api->get_session(), &profile_create_info, &foveation_profile); if (XR_FAILED(result)) { - print_line("OpenXR: Unable to create the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + print_line("OpenXR: Unable to create the foveation profile [", openxr_api->get_error_string(result), "]"); return; } @@ -154,15 +169,15 @@ void OpenXRFBFoveationExtension::update_profile() { foveation_update_state.type = XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB; foveation_update_state.profile = foveation_profile; - result = swapchain_update_state_ext->xrUpdateSwapchainFB(OpenXRAPI::get_singleton()->get_color_swapchain(), (XrSwapchainStateBaseHeaderFB *)&foveation_update_state); + result = fov_ext->swapchain_update_state_ext->xrUpdateSwapchainFB(main_color_swapchain, (XrSwapchainStateBaseHeaderFB *)&foveation_update_state); if (XR_FAILED(result)) { - print_line("OpenXR: Unable to update the swapchain [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + print_line("OpenXR: Unable to update the swapchain [", openxr_api->get_error_string(result), "]"); // We still want to destroy our profile so keep going... } - result = xrDestroyFoveationProfileFB(foveation_profile); + result = fov_ext->xrDestroyFoveationProfileFB(foveation_profile); if (XR_FAILED(result)) { - print_line("OpenXR: Unable to destroy the foveation profile [", OpenXRAPI::get_singleton()->get_error_string(result), "]"); + print_line("OpenXR: Unable to destroy the foveation profile [", openxr_api->get_error_string(result), "]"); } } diff --git a/modules/openxr/extensions/openxr_fb_foveation_extension.h b/modules/openxr/extensions/openxr_fb_foveation_extension.h index 1c5e722731..84bd7011b5 100644 --- a/modules/openxr/extensions/openxr_fb_foveation_extension.h +++ b/modules/openxr/extensions/openxr_fb_foveation_extension.h @@ -60,7 +60,7 @@ public: virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override; - virtual void on_state_ready() override; + virtual void on_main_swapchains_created() override; bool is_enabled() const; @@ -82,7 +82,15 @@ private: XrFoveationLevelFB foveation_level = XR_FOVEATION_LEVEL_NONE_FB; XrFoveationDynamicFB foveation_dynamic = XR_FOVEATION_DYNAMIC_DISABLED_FB; - void update_profile(); + static void _update_profile(); + + void update_profile() { + // If we're rendering on a separate thread, we may still be processing the last frame, don't communicate this till we're ready... + RenderingServer *rendering_server = RenderingServer::get_singleton(); + ERR_FAIL_NULL(rendering_server); + + rendering_server->call_on_render_thread(callable_mp_static(&OpenXRFBFoveationExtension::_update_profile)); + } // Enable foveation on this swapchain XrSwapchainCreateInfoFoveationFB swapchain_create_info_foveation_fb; diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 32512070d6..541e369925 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -1220,6 +1220,10 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) { } }; + for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) { + wrapper->on_main_swapchains_created(); + } + return true; }; diff --git a/modules/openxr/scene/openxr_composition_layer.cpp b/modules/openxr/scene/openxr_composition_layer.cpp index 50cc7b9e7b..b02f3082ab 100644 --- a/modules/openxr/scene/openxr_composition_layer.cpp +++ b/modules/openxr/scene/openxr_composition_layer.cpp @@ -165,7 +165,9 @@ void OpenXRCompositionLayer::set_layer_viewport(SubViewport *p_viewport) { return; } - ERR_FAIL_COND_EDMSG(is_viewport_in_use(p_viewport), RTR("Cannot use the same SubViewport with multiple OpenXR composition layers. Clear it from its current layer first.")); + if (p_viewport != nullptr) { + ERR_FAIL_COND_EDMSG(is_viewport_in_use(p_viewport), RTR("Cannot use the same SubViewport with multiple OpenXR composition layers. Clear it from its current layer first.")); + } layer_viewport = p_viewport; diff --git a/modules/svg/SCsub b/modules/svg/SCsub index 0e21c1e6d7..a32be0e41a 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -26,7 +26,6 @@ thirdparty_sources = [ "src/loaders/raw/tvgRawLoader.cpp", # image loaders "src/loaders/external_png/tvgPngLoader.cpp", - "src/loaders/external_webp/tvgWebpLoader.cpp", "src/loaders/jpg/tvgJpgd.cpp", "src/loaders/jpg/tvgJpgLoader.cpp", # renderer common @@ -59,6 +58,10 @@ thirdparty_sources = [ "src/renderer/sw_engine/tvgSwStroke.cpp", ] +if env["module_webp_enabled"]: + thirdparty_sources += ["src/loaders/external_webp/tvgWebpLoader.cpp"] + env_svg.Append(CPPDEFINES=["THORVG_WEBP_LOADER_SUPPORT"]) + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env_svg.Prepend(CPPPATH=[thirdparty_dir + "inc"]) @@ -76,12 +79,15 @@ env_thirdparty.Prepend( thirdparty_dir + "src/renderer/sw_engine", thirdparty_dir + "src/loaders/raw", thirdparty_dir + "src/loaders/external_png", - thirdparty_dir + "src/loaders/external_webp", thirdparty_dir + "src/loaders/jpg", - "#thirdparty/libpng", - "#thirdparty/libwebp/src", ] ) +if env["builtin_libpng"]: + env_thirdparty.Prepend(CPPPATH=["#thirdparty/libpng"]) +if env["module_webp_enabled"]: + env_thirdparty.Prepend(CPPPATH=[thirdparty_dir + "src/loaders/external_webp"]) + if env["builtin_libwebp"]: + env_thirdparty.Prepend(CPPPATH=["#thirdparty/libwebp/src"]) env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) env.modules_sources += thirdparty_obj diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index 018984a52f..2f7a175d91 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -1,19 +1,27 @@ #!/usr/bin/env python import atexit import sys -import methods import time +import methods + # Enable ANSI escape code support on Windows 10 and later (for colored console output). # <https://github.com/python/cpython/issues/73245> -if sys.platform == "win32": - from ctypes import windll, c_int, byref - - stdout_handle = windll.kernel32.GetStdHandle(c_int(-11)) - mode = c_int(0) - windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode)) - mode = c_int(mode.value | 4) - windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode) +if sys.stdout.isatty() and sys.platform == "win32": + try: + from ctypes import WinError, byref, windll # type: ignore + from ctypes.wintypes import DWORD # type: ignore + + stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) + mode = DWORD(0) + if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)): + raise WinError() + mode = DWORD(mode.value | 4) + if not windll.kernel32.SetConsoleMode(stdout_handle, mode): + raise WinError() + except Exception as e: + methods._colorize = False + methods.print_error(f"Failed to enable ANSI escape code support, disabling color output.\n{e}") # For the reference: # - CCFLAGS are compilation flags shared between C and C++ @@ -713,7 +721,7 @@ if env["static_icu_data"]: env.Append(CXXFLAGS=["-DICU_STATIC_DATA"]) env.Append(CPPPATH=["../../../thirdparty/icu4c/"]) else: - thirdparty_sources += ["../icu_data/icudata_stub.cpp"] + thirdparty_icu_sources += ["../icu_data/icudata_stub.cpp"] env_icu.Append(CPPPATH=["../../../thirdparty/icu4c/common/", "../../../thirdparty/icu4c/i18n/"]) env_icu.Append( diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py index 3453c3e8f0..43bf56abfe 100644 --- a/modules/text_server_adv/gdextension_build/methods.py +++ b/modules/text_server_adv/gdextension_build/methods.py @@ -81,9 +81,9 @@ def disable_warnings(self): self.Append(CCFLAGS=["/w"]) self.Append(CFLAGS=["/w"]) self.Append(CXXFLAGS=["/w"]) - self["CCFLAGS"] = [x for x in self["CCFLAGS"] if not x in warn_flags] - self["CFLAGS"] = [x for x in self["CFLAGS"] if not x in warn_flags] - self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if not x in warn_flags] + self["CCFLAGS"] = [x for x in self["CCFLAGS"] if x not in warn_flags] + self["CFLAGS"] = [x for x in self["CFLAGS"] if x not in warn_flags] + self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if x not in warn_flags] else: self.Append(CCFLAGS=["-w"]) self.Append(CFLAGS=["-w"]) @@ -117,31 +117,31 @@ def make_icu_data(target, source, env): def write_macos_plist(target, binary_name, identifier, name): os.makedirs(f"{target}/Resource/", exist_ok=True) with open(f"{target}/Resource/Info.plist", "w", encoding="utf-8", newline="\n") as f: - f.write(f'<?xml version="1.0" encoding="UTF-8"?>\n') - f.write( - f'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' - ) - f.write(f'<plist version="1.0">\n') - f.write(f"<dict>\n") - f.write(f"\t<key>CFBundleExecutable</key>\n") - f.write(f"\t<string>{binary_name}</string>\n") - f.write(f"\t<key>CFBundleIdentifier</key>\n") - f.write(f"\t<string>{identifier}</string>\n") - f.write(f"\t<key>CFBundleInfoDictionaryVersion</key>\n") - f.write(f"\t<string>6.0</string>\n") - f.write(f"\t<key>CFBundleName</key>\n") - f.write(f"\t<string>{name}</string>\n") - f.write(f"\t<key>CFBundlePackageType</key>\n") - f.write(f"\t<string>FMWK</string>\n") - f.write(f"\t<key>CFBundleShortVersionString</key>\n") - f.write(f"\t<string>1.0.0</string>\n") - f.write(f"\t<key>CFBundleSupportedPlatforms</key>\n") - f.write(f"\t<array>\n") - f.write(f"\t\t<string>MacOSX</string>\n") - f.write(f"\t</array>\n") - f.write(f"\t<key>CFBundleVersion</key>\n") - f.write(f"\t<string>1.0.0</string>\n") - f.write(f"\t<key>LSMinimumSystemVersion</key>\n") - f.write(f"\t<string>10.14</string>\n") - f.write(f"</dict>\n") - f.write(f"</plist>\n") + f.write(f"""\ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleExecutable</key> + <string>{binary_name}</string> + <key>CFBundleIdentifier</key> + <string>{identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>{name}</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.0.0</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>CFBundleVersion</key> + <string>1.0.0</string> + <key>LSMinimumSystemVersion</key> + <string>10.14</string> +</dict> +</plist> +""") diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct index 07940719eb..f6ae7149be 100644 --- a/modules/text_server_fb/gdextension_build/SConstruct +++ b/modules/text_server_fb/gdextension_build/SConstruct @@ -1,19 +1,27 @@ #!/usr/bin/env python import atexit import sys -import methods import time +import methods + # Enable ANSI escape code support on Windows 10 and later (for colored console output). # <https://github.com/python/cpython/issues/73245> -if sys.platform == "win32": - from ctypes import windll, c_int, byref - - stdout_handle = windll.kernel32.GetStdHandle(c_int(-11)) - mode = c_int(0) - windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode)) - mode = c_int(mode.value | 4) - windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode) +if sys.stdout.isatty() and sys.platform == "win32": + try: + from ctypes import WinError, byref, windll # type: ignore + from ctypes.wintypes import DWORD # type: ignore + + stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) + mode = DWORD(0) + if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)): + raise WinError() + mode = DWORD(mode.value | 4) + if not windll.kernel32.SetConsoleMode(stdout_handle, mode): + raise WinError() + except Exception as e: + methods._colorize = False + methods.print_error(f"Failed to enable ANSI escape code support, disabling color output.\n{e}") # For the reference: # - CCFLAGS are compilation flags shared between C and C++ diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py index 3453c3e8f0..43bf56abfe 100644 --- a/modules/text_server_fb/gdextension_build/methods.py +++ b/modules/text_server_fb/gdextension_build/methods.py @@ -81,9 +81,9 @@ def disable_warnings(self): self.Append(CCFLAGS=["/w"]) self.Append(CFLAGS=["/w"]) self.Append(CXXFLAGS=["/w"]) - self["CCFLAGS"] = [x for x in self["CCFLAGS"] if not x in warn_flags] - self["CFLAGS"] = [x for x in self["CFLAGS"] if not x in warn_flags] - self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if not x in warn_flags] + self["CCFLAGS"] = [x for x in self["CCFLAGS"] if x not in warn_flags] + self["CFLAGS"] = [x for x in self["CFLAGS"] if x not in warn_flags] + self["CXXFLAGS"] = [x for x in self["CXXFLAGS"] if x not in warn_flags] else: self.Append(CCFLAGS=["-w"]) self.Append(CFLAGS=["-w"]) @@ -117,31 +117,31 @@ def make_icu_data(target, source, env): def write_macos_plist(target, binary_name, identifier, name): os.makedirs(f"{target}/Resource/", exist_ok=True) with open(f"{target}/Resource/Info.plist", "w", encoding="utf-8", newline="\n") as f: - f.write(f'<?xml version="1.0" encoding="UTF-8"?>\n') - f.write( - f'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' - ) - f.write(f'<plist version="1.0">\n') - f.write(f"<dict>\n") - f.write(f"\t<key>CFBundleExecutable</key>\n") - f.write(f"\t<string>{binary_name}</string>\n") - f.write(f"\t<key>CFBundleIdentifier</key>\n") - f.write(f"\t<string>{identifier}</string>\n") - f.write(f"\t<key>CFBundleInfoDictionaryVersion</key>\n") - f.write(f"\t<string>6.0</string>\n") - f.write(f"\t<key>CFBundleName</key>\n") - f.write(f"\t<string>{name}</string>\n") - f.write(f"\t<key>CFBundlePackageType</key>\n") - f.write(f"\t<string>FMWK</string>\n") - f.write(f"\t<key>CFBundleShortVersionString</key>\n") - f.write(f"\t<string>1.0.0</string>\n") - f.write(f"\t<key>CFBundleSupportedPlatforms</key>\n") - f.write(f"\t<array>\n") - f.write(f"\t\t<string>MacOSX</string>\n") - f.write(f"\t</array>\n") - f.write(f"\t<key>CFBundleVersion</key>\n") - f.write(f"\t<string>1.0.0</string>\n") - f.write(f"\t<key>LSMinimumSystemVersion</key>\n") - f.write(f"\t<string>10.14</string>\n") - f.write(f"</dict>\n") - f.write(f"</plist>\n") + f.write(f"""\ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleExecutable</key> + <string>{binary_name}</string> + <key>CFBundleIdentifier</key> + <string>{identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>{name}</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>1.0.0</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>CFBundleVersion</key> + <string>1.0.0</string> + <key>LSMinimumSystemVersion</key> + <string>10.14</string> +</dict> +</plist> +""") diff --git a/modules/webp/SCsub b/modules/webp/SCsub index e78236a60b..dde4450c23 100644 --- a/modules/webp/SCsub +++ b/modules/webp/SCsub @@ -128,6 +128,7 @@ if env["builtin_libwebp"]: "src/utils/filters_utils.c", "src/utils/huffman_encode_utils.c", "src/utils/huffman_utils.c", + "src/utils/palette.c", "src/utils/quant_levels_dec_utils.c", "src/utils/quant_levels_utils.c", "src/utils/random_utils.c", diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js index 722b448fd5..031530a047 100644 --- a/modules/webxr/native/library_godot_webxr.js +++ b/modules/webxr/native/library_godot_webxr.js @@ -67,7 +67,8 @@ const GodotWebXR = { GodotWebXR.orig_requestAnimationFrame = Browser.requestAnimationFrame; } Browser.requestAnimationFrame = enable - ? GodotWebXR.requestAnimationFrame : GodotWebXR.orig_requestAnimationFrame; + ? GodotWebXR.requestAnimationFrame + : GodotWebXR.orig_requestAnimationFrame; }, pauseResumeMainLoop: () => { // Once both GodotWebXR.session and GodotWebXR.space are set or @@ -76,9 +77,9 @@ const GodotWebXR = { // gets picked up automatically, however, in the Oculus Browser // on the Quest, we need to pause and resume the main loop. Browser.mainLoop.pause(); - runtimeKeepalivePush(); // eslint-disable-line no-undef + runtimeKeepalivePush(); window.setTimeout(function () { - runtimeKeepalivePop(); // eslint-disable-line no-undef + runtimeKeepalivePop(); Browser.mainLoop.resume(); }, 0); }, @@ -287,12 +288,12 @@ const GodotWebXR = { // Store onsimpleevent so we can use it later. GodotWebXR.onsimpleevent = onsimpleevent; - const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef + const gl_context_handle = _emscripten_webgl_get_current_context(); const gl = GL.getContext(gl_context_handle).GLctx; GodotWebXR.gl = gl; gl.makeXRCompatible().then(function () { - GodotWebXR.gl_binding = new XRWebGLBinding(session, gl); // eslint-disable-line no-undef + GodotWebXR.gl_binding = new XRWebGLBinding(session, gl); // This will trigger the layer to get created. GodotWebXR.getLayer(); diff --git a/platform/SCsub b/platform/SCsub index b24c189848..b07023efed 100644 --- a/platform/SCsub +++ b/platform/SCsub @@ -1,9 +1,10 @@ #!/usr/bin/env python -import methods from glob import glob from pathlib import Path +import methods + Import("env") env.platform_sources = [] diff --git a/platform/android/SCsub b/platform/android/SCsub index 4d76ffb180..bc1b5e9200 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -1,7 +1,8 @@ #!/usr/bin/env python -import sys import subprocess +import sys + from methods import print_warning Import("env") diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h index 5b30c392e7..01da0c7935 100644 --- a/platform/android/api/jni_singleton.h +++ b/platform/android/api/jni_singleton.h @@ -244,6 +244,7 @@ public: ~JNISingleton() { #ifdef ANDROID_ENABLED + method_map.clear(); if (instance) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL(env); diff --git a/platform/android/detect.py b/platform/android/detect.py index 6a8c4ed86d..ed0ceb5862 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -1,10 +1,11 @@ import os -import sys import platform import subprocess -from methods import print_warning, print_error +import sys from typing import TYPE_CHECKING +from methods import print_error, print_warning + if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -66,11 +67,11 @@ def get_min_target_api(): def get_flags(): - return [ - ("arch", "arm64"), # Default for convenience. - ("target", "template_debug"), - ("supported", ["mono"]), - ] + return { + "arch": "arm64", # Default for convenience. + "target": "template_debug", + "supported": ["mono"], + } # Check if Android NDK version is installed diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index d27e75b07a..c404af34d8 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -330,8 +330,7 @@ ext.getReleaseKeyAlias = { -> } ext.isAndroidStudio = { -> - def sysProps = System.getProperties() - return sysProps != null && sysProps['idea.platform.prefix'] != null + return project.hasProperty('android.injected.invoked.from.ide') } ext.shouldZipAlign = { -> diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index c609b33ef4..b91b023ce6 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -232,11 +232,6 @@ def generateBuildTasks(String flavor = "template") { return tasks } -def isAndroidStudio() { - def sysProps = System.getProperties() - return sysProps != null && sysProps['idea.platform.prefix'] != null -} - task copyEditorReleaseApkToBin(type: Copy) { dependsOn ':editor:assembleRelease' from('editor/build/outputs/apk/release') diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt index 89fbb9f580..cfbbcf7d0e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt @@ -61,6 +61,9 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi private var contextClickInProgress = false private var pointerCaptureInProgress = false + private var lastDragX: Float = 0.0f + private var lastDragY: Float = 0.0f + override fun onDown(event: MotionEvent): Boolean { GodotInputHandler.handleMotionEvent(event.source, MotionEvent.ACTION_DOWN, event.buttonState, event.x, event.y, nextDownIsDoubleTap) nextDownIsDoubleTap = false @@ -165,6 +168,8 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi pointerCaptureInProgress = false dragInProgress = false contextClickInProgress = false + lastDragX = 0.0f + lastDragY = 0.0f return true } @@ -189,6 +194,17 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi sourceMouseRelative ) return true + } else if (!scaleInProgress) { + // The 'onScroll' event is triggered with a long delay. + // Force the 'InputEventScreenDrag' event earlier here. + // We don't toggle 'dragInProgress' here so that the scaling logic can override the drag operation if needed. + // Once the 'onScroll' event kicks-in, 'dragInProgress' will be properly set. + if (lastDragX != event.getX(0) || lastDragY != event.getY(0)) { + lastDragX = event.getX(0) + lastDragY = event.getY(0) + GodotInputHandler.handleMotionEvent(event) + return true + } } return false } @@ -216,7 +232,7 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi distanceY: Float ): Boolean { if (scaleInProgress) { - if (dragInProgress) { + if (dragInProgress || lastDragX != 0.0f || lastDragY != 0.0f) { if (originEvent != null) { // Cancel the drag GodotInputHandler.handleMotionEvent( @@ -228,6 +244,8 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi ) } dragInProgress = false + lastDragX = 0.0f + lastDragY = 0.0f } } @@ -235,8 +253,10 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi val y = terminusEvent.y if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress && !dragInProgress) { GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f) - } else if (!scaleInProgress){ + } else if (!scaleInProgress) { dragInProgress = true + lastDragX = terminusEvent.getX(0) + lastDragY = terminusEvent.getY(0) GodotInputHandler.handleMotionEvent(terminusEvent) } return true diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java index c0912ca4dc..c975c29e96 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java @@ -112,19 +112,18 @@ public abstract class GodotPlugin { /** * Register the plugin with Godot native code. * <p> - * This method is invoked by the Godot Engine on the render thread. + * This method is invoked on the render thread to register the plugin on engine startup. */ public final void onRegisterPluginWithGodotNative() { - registeredSignals.putAll( - registerPluginWithGodotNative(this, getPluginName(), getPluginMethods(), getPluginSignals())); - } + final String pluginName = getPluginName(); + if (!nativeRegisterSingleton(pluginName, this)) { + return; + } - private static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject, - String pluginName, List<String> pluginMethods, Set<SignalInfo> pluginSignals) { - nativeRegisterSingleton(pluginName, pluginObject); + List<String> pluginMethods = getPluginMethods(); Set<Method> filteredMethods = new HashSet<>(); - Class<?> clazz = pluginObject.getClass(); + Class<?> clazz = getClass(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { @@ -156,15 +155,14 @@ public abstract class GodotPlugin { nativeRegisterMethod(pluginName, method.getName(), method.getReturnType().getName(), pt); } + Set<SignalInfo> pluginSignals = getPluginSignals(); + // Register the signals for this plugin. - Map<String, SignalInfo> registeredSignals = new HashMap<>(); for (SignalInfo signalInfo : pluginSignals) { String signalName = signalInfo.getName(); nativeRegisterSignal(pluginName, signalName, signalInfo.getParamTypesNames()); registeredSignals.put(signalName, signalInfo); } - - return registeredSignals; } /** @@ -408,7 +406,7 @@ public abstract class GodotPlugin { * Used to setup a {@link GodotPlugin} instance. * @param p_name Name of the instance. */ - private static native void nativeRegisterSingleton(String p_name, Object object); + private static native boolean nativeRegisterSingleton(String p_name, Object object); /** * Used to complete registration of the {@link GodotPlugin} instance's methods. diff --git a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt index e1534c7685..96b6dfc9f3 100644 --- a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt +++ b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt @@ -7,6 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(GODOT_ROOT_DIR ../../../..) +set(ANDROID_ROOT_DIR "${GODOT_ROOT_DIR}/platform/android" CACHE STRING "") # Get sources file(GLOB_RECURSE SOURCES ${GODOT_ROOT_DIR}/*.c**) @@ -15,6 +16,7 @@ file(GLOB_RECURSE HEADERS ${GODOT_ROOT_DIR}/*.h**) add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS}) target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC - ${GODOT_ROOT_DIR}) + ${GODOT_ROOT_DIR} + ${ANDROID_ROOT_DIR}) add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED -DGLES3_ENABLED -DTOOLS_ENABLED) diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 6cab7e74fd..93743c4e35 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -42,6 +42,7 @@ #include "jni_utils.h" #include "net_socket_android.h" #include "os_android.h" +#include "plugin/godot_plugin_jni.h" #include "string_android.h" #include "thread_jandroid.h" #include "tts_android.h" @@ -78,6 +79,9 @@ static void _terminate(JNIEnv *env, bool p_restart = false) { step.set(-1); // Ensure no further steps are attempted and no further events are sent // lets cleanup + // Unregister android plugins + unregister_plugins_singletons(); + if (java_class_wrapper) { memdelete(java_class_wrapper); } diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp index fd60ba4ae7..e3cde145cb 100644 --- a/platform/android/plugin/godot_plugin_jni.cpp +++ b/platform/android/plugin/godot_plugin_jni.cpp @@ -40,16 +40,32 @@ static HashMap<String, JNISingleton *> jni_singletons; +void unregister_plugins_singletons() { + for (const KeyValue<String, JNISingleton *> &E : jni_singletons) { + Engine::get_singleton()->remove_singleton(E.key); + ProjectSettings::get_singleton()->set(E.key, Variant()); + + if (E.value) { + memdelete(E.value); + } + } + jni_singletons.clear(); +} + extern "C" { -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj) { +JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj) { String singname = jstring_to_string(name, env); + + ERR_FAIL_COND_V(jni_singletons.has(singname), false); + JNISingleton *s = (JNISingleton *)ClassDB::instantiate("JNISingleton"); s->set_instance(env->NewGlobalRef(obj)); jni_singletons[singname] = s; Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s)); ProjectSettings::get_singleton()->set(singname, s); + return true; } JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args) { diff --git a/platform/android/plugin/godot_plugin_jni.h b/platform/android/plugin/godot_plugin_jni.h index baa29a79ea..8c62fb0f68 100644 --- a/platform/android/plugin/godot_plugin_jni.h +++ b/platform/android/plugin/godot_plugin_jni.h @@ -34,8 +34,10 @@ #include <android/log.h> #include <jni.h> +void unregister_plugins_singletons(); + extern "C" { -JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj); +JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj); JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args); JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types); JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params); diff --git a/platform/ios/SCsub b/platform/ios/SCsub index f38914434b..cff7dcc1fd 100644 --- a/platform/ios/SCsub +++ b/platform/ios/SCsub @@ -2,11 +2,11 @@ Import("env") -import os, json -from platform_methods import architectures, lipo, get_build_version, detect_mvk -import subprocess +import os import shutil +from platform_methods import detect_mvk, lipo + def generate_bundle(target, source, env): bin_dir = Dir("#bin").abspath diff --git a/platform/ios/detect.py b/platform/ios/detect.py index e3bac4ec5c..35e1b9cd6d 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -1,9 +1,9 @@ import os import sys -from methods import print_error, detect_darwin_sdk_path - from typing import TYPE_CHECKING +from methods import detect_darwin_sdk_path, print_error + if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -47,13 +47,13 @@ def get_doc_path(): def get_flags(): - return [ - ("arch", "arm64"), # Default for convenience. - ("target", "template_debug"), - ("use_volk", False), - ("supported", ["mono"]), - ("builtin_pcre2_with_jit", False), - ] + return { + "arch": "arm64", # Default for convenience. + "target": "template_debug", + "use_volk": False, + "supported": ["mono"], + "builtin_pcre2_with_jit": False, + } def configure(env: "SConsEnvironment"): diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 47f3bcadc9..303a88ab26 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -1,11 +1,11 @@ import os import platform import sys -from methods import print_warning, print_error, get_compiler_version, using_gcc -from platform_methods import detect_arch - from typing import TYPE_CHECKING +from methods import get_compiler_version, print_error, print_warning, using_gcc +from platform_methods import detect_arch + if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -65,10 +65,10 @@ def get_doc_path(): def get_flags(): - return [ - ("arch", detect_arch()), - ("supported", ["mono"]), - ] + return { + "arch": detect_arch(), + "supported": ["mono"], + } def configure(env: "SConsEnvironment"): diff --git a/platform/macos/SCsub b/platform/macos/SCsub index 59ef4ee85c..eb8524826f 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -2,11 +2,13 @@ Import("env") -import os, json -from platform_methods import architectures, lipo, get_build_version -import platform_macos_builders -import subprocess +import os import shutil +import subprocess + +import platform_macos_builders + +from platform_methods import get_build_version, lipo def generate_bundle(target, source, env): diff --git a/platform/macos/detect.py b/platform/macos/detect.py index a5ef29e34f..70cb00c6ff 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -1,10 +1,10 @@ import os import sys -from methods import print_error, detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang -from platform_methods import detect_arch, detect_mvk - from typing import TYPE_CHECKING +from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error +from platform_methods import detect_arch, detect_mvk + if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -53,11 +53,11 @@ def get_doc_path(): def get_flags(): - return [ - ("arch", detect_arch()), - ("use_volk", False), - ("supported", ["mono"]), - ] + return { + "arch": detect_arch(), + "use_volk": False, + "supported": ["mono"], + } def configure(env: "SConsEnvironment"): @@ -107,7 +107,7 @@ def configure(env: "SConsEnvironment"): env.Append(CCFLAGS=["-fobjc-arc"]) - if not "osxcross" in env: # regular native build + if "osxcross" not in env: # regular native build if env["macports_clang"] != "no": mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local") mpclangver = env["macports_clang"] diff --git a/platform/web/.eslintrc.engine.js b/platform/web/.eslintrc.engine.js deleted file mode 100644 index a76bd46b9e..0000000000 --- a/platform/web/.eslintrc.engine.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - "extends": [ - "./.eslintrc.js", - ], - "globals": { - "InternalConfig": true, - "Godot": true, - "Features": true, - "Preloader": true, - }, -}; diff --git a/platform/web/.eslintrc.html.js b/platform/web/.eslintrc.html.js deleted file mode 100644 index 8c9a3d83da..0000000000 --- a/platform/web/.eslintrc.html.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - "plugins": [ - "html", - "@html-eslint", - ], - "parser": "@html-eslint/parser", - "extends": ["plugin:@html-eslint/recommended", "./.eslintrc.js"], - "rules": { - "no-alert": "off", - "no-console": "off", - "@html-eslint/require-closing-tags": ["error", { "selfClosing": "never" }], - "@html-eslint/indent": ["error", "tab"], - }, - "globals": { - "Godot": true, - "Engine": true, - "$GODOT_CONFIG": true, - "$GODOT_THREADS_ENABLED": true, - "___GODOT_THREADS_ENABLED___": true, - }, -}; diff --git a/platform/web/.eslintrc.js b/platform/web/.eslintrc.js deleted file mode 100644 index 2c81f1f02d..0000000000 --- a/platform/web/.eslintrc.js +++ /dev/null @@ -1,51 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "es2021": true, - }, - "extends": [ - "airbnb-base", - ], - "parserOptions": { - "ecmaVersion": 12, - }, - "ignorePatterns": "*.externs.js", - "rules": { - "func-names": "off", - // Use tabs for consistency with the C++ codebase. - "indent": ["error", "tab"], - "max-len": "off", - "no-else-return": ["error", {allowElseIf: true}], - "curly": ["error", "all"], - "brace-style": ["error", "1tbs", { "allowSingleLine": false }], - "no-bitwise": "off", - "no-continue": "off", - "no-self-assign": "off", - "no-tabs": "off", - "no-param-reassign": ["error", { "props": false }], - "no-plusplus": "off", - "no-unused-vars": ["error", { "args": "none" }], - "prefer-destructuring": "off", - "prefer-rest-params": "off", - "prefer-spread": "off", - "camelcase": "off", - "no-underscore-dangle": "off", - "max-classes-per-file": "off", - "prefer-arrow-callback": "off", - // Messes up with copyright headers in source files. - "spaced-comment": "off", - // Completely breaks emscripten libraries. - "object-shorthand": "off", - // Closure compiler (exported properties) - "quote-props": ["error", "consistent"], - "dot-notation": "off", - // No comma dangle for functions (it's madness, and ES2017) - "comma-dangle": ["error", { - "arrays": "always-multiline", - "objects": "always-multiline", - "imports": "always-multiline", - "exports": "always-multiline", - "functions": "never" - }], - } -}; diff --git a/platform/web/.eslintrc.libs.js b/platform/web/.eslintrc.libs.js deleted file mode 100644 index 8e579fd462..0000000000 --- a/platform/web/.eslintrc.libs.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = { - "extends": [ - "./.eslintrc.js", - ], - "globals": { - "LibraryManager": true, - "mergeInto": true, - "autoAddDeps": true, - "HEAP8": true, - "HEAPU8": true, - "HEAP32": true, - "HEAPF32": true, - "ERRNO_CODES": true, - "FS": true, - "IDBFS": true, - "GodotOS": true, - "GodotConfig": true, - "GodotEventListeners": true, - "GodotRuntime": true, - "GodotFS": true, - "IDHandler": true, - "Browser": true, - "GL": true, - "XRWebGLLayer": true, - }, -}; diff --git a/platform/web/.eslintrc.sw.js b/platform/web/.eslintrc.sw.js deleted file mode 100644 index cba9ed8001..0000000000 --- a/platform/web/.eslintrc.sw.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - "extends": [ - "./.eslintrc.js", - ], - "rules": { - "no-restricted-globals": 0, - }, - "globals": { - "onClientMessage": true, - "___GODOT_ENSURE_CROSSORIGIN_ISOLATION_HEADERS___": true, - "___GODOT_CACHE___": true, - "___GODOT_OPT_CACHE___": true, - }, -}; diff --git a/platform/web/SCsub b/platform/web/SCsub index bc5893ab3a..fea2fa7df9 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -6,9 +6,10 @@ Import("env") # The HTTP server "targets". Run with "scons p=web serve", or "scons p=web run" if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS: - from serve import serve import os + from serve import serve + port = os.environ.get("GODOT_WEB_TEST_PORT", 8060) try: port = int(port) diff --git a/platform/web/detect.py b/platform/web/detect.py index ccd884b225..3df8cbad7c 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -1,18 +1,19 @@ import os import sys +from typing import TYPE_CHECKING from emscripten_helpers import ( - run_closure_compiler, - create_engine_file, + add_js_externs, add_js_libraries, add_js_pre, - add_js_externs, + create_engine_file, create_template_zip, get_template_zip_path, + run_closure_compiler, ) -from methods import print_warning, print_error, get_compiler_version from SCons.Util import WhereIs -from typing import TYPE_CHECKING + +from methods import get_compiler_version, print_error, print_warning if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -64,21 +65,21 @@ def get_doc_path(): def get_flags(): - return [ - ("arch", "wasm32"), - ("target", "template_debug"), - ("builtin_pcre2_with_jit", False), - ("vulkan", False), + return { + "arch": "wasm32", + "target": "template_debug", + "builtin_pcre2_with_jit": False, + "vulkan": False, # Embree is heavy and requires too much memory (GH-70621). - ("module_raycast_enabled", False), + "module_raycast_enabled": False, # Use -Os to prioritize optimizing for reduced file size. This is # particularly valuable for the web platform because it directly # decreases download time. # -Os reduces file size by around 5 MiB over -O3. -Oz only saves about # 100 KiB over -Os, which does not justify the negative impact on # run-time performance. - ("optimize", "size"), - ] + "optimize": "size", + } def configure(env: "SConsEnvironment"): diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py index 3ba133c9a1..745b2457fa 100644 --- a/platform/web/emscripten_helpers.py +++ b/platform/web/emscripten_helpers.py @@ -1,4 +1,5 @@ -import os, json +import json +import os from SCons.Util import WhereIs @@ -24,13 +25,13 @@ def get_build_version(): import version name = "custom_build" - if os.getenv("BUILD_NAME") != None: + if os.getenv("BUILD_NAME") is not None: name = os.getenv("BUILD_NAME") v = "%d.%d" % (version.major, version.minor) if version.patch > 0: v += ".%d" % version.patch status = version.status - if os.getenv("GODOT_VERSION_STATUS") != None: + if os.getenv("GODOT_VERSION_STATUS") is not None: status = str(os.getenv("GODOT_VERSION_STATUS")) v += ".%s.%s" % (status, name) return v diff --git a/platform/web/eslint.config.cjs b/platform/web/eslint.config.cjs new file mode 100644 index 0000000000..65ade096a7 --- /dev/null +++ b/platform/web/eslint.config.cjs @@ -0,0 +1,204 @@ +const fs = require('fs'); +const globals = require('globals'); +const htmlParser = require('@html-eslint/parser'); +const htmlPlugin = require('@html-eslint/eslint-plugin'); +const pluginJs = require('@eslint/js'); +const pluginReference = require('eslint-plugin-html'); +const stylistic = require('@stylistic/eslint-plugin'); + +if (process && process.env && process.env.npm_command && !fs.existsSync('./platform/web/eslint.config.cjs')) { + throw Error('eslint must be run from the Godot project root folder'); +} + +const emscriptenGlobals = { + 'Browser': true, + 'ERRNO_CODES': true, + 'FS': true, + 'GL': true, + 'HEAP32': true, + 'HEAP8': true, + 'HEAPF32': true, + 'HEAPU8': true, + 'IDBFS': true, + 'LibraryManager': true, + 'Module': true, + 'UTF8ToString': true, + '_emscripten_webgl_get_current_context': true, + '_free': true, + '_malloc': true, + 'autoAddDeps': true, + 'getValue': true, + 'lengthBytesUTF8': true, + 'mergeInto': true, + 'runtimeKeepalivePop': true, + 'runtimeKeepalivePush': true, + 'setValue': true, + 'stringToUTF8': true, + 'stringToUTF8Array': true, + 'wasmTable': true, +}; + +module.exports = [ + pluginJs.configs.all, + stylistic.configs.customize({ jsx: false }), + + { + rules: { + 'consistent-this': ['error', 'me'], // enforce consistent naming when capturing the current execution context + 'curly': ['error', 'all'], // enforce consistent brace style for all control statements + 'no-else-return': ['error', { 'allowElseIf': true }], // disallow else blocks after return statements in if statements + 'no-param-reassign': ['error', { 'props': false }], // disallow reassigning function parameters + 'no-unused-vars': ['error', { 'args': 'none', 'caughtErrors': 'none' }], // disallow unused variables + + '@stylistic/arrow-parens': ['error', 'always'], // enforces the consistent use of parentheses in arrow functions + '@stylistic/brace-style': ['error', '1tbs', { 'allowSingleLine': false }], // describes the placement of braces relative to their control statement and body + '@stylistic/comma-dangle': ['error', { + 'arrays': 'always-multiline', + 'objects': 'always-multiline', + 'imports': 'always-multiline', + 'exports': 'always-multiline', + 'functions': 'never', + }], // enforces consistent use of trailing commas in object and array literals + '@stylistic/indent': ['error', 'tab', { 'SwitchCase': 0 }], // enforces a consistent indentation style + '@stylistic/indent-binary-ops': ['error', 'tab'], // indentation for binary operators in multiline expressions + '@stylistic/multiline-ternary': ['error', 'always-multiline'], // enforces or disallows newlines between operands of a ternary expression + '@stylistic/no-tabs': ['error', { 'allowIndentationTabs': true }], // looks for tabs anywhere inside a file: code, comments or anything else + '@stylistic/quote-props': ['error', 'consistent'], // requires quotes around object literal property names + '@stylistic/quotes': ['error', 'single'], // enforces the consistent use of either backticks, double, or single quotes + '@stylistic/semi': ['error', 'always'], // enforces consistent use of semicolons + '@stylistic/spaced-comment': ['error', 'always', { 'block': { 'exceptions': ['*'] } }], // enforce consistency of spacing after the start of a comment + + 'camelcase': 'off', // disable: camelcase naming convention + 'capitalized-comments': 'off', // disable: enforce or disallow capitalization of the first letter of a comment + 'complexity': 'off', // disable: enforce a maximum cyclomatic complexity allowed in a program + 'dot-notation': 'off', // disable: enforce dot notation whenever possible + 'eqeqeq': 'off', // disable: require the use of === and !== + 'func-name-matching': 'off', // disable: require function names to match the name of the variable or property to which they are assigned + 'func-names': 'off', // disable: checking named function expressions + 'func-style': 'off', // disable: consistent use of either function declarations or expressions + 'id-length': 'off', // disable: enforce minimum and maximum identifier lengths + 'init-declarations': 'off', // disable: require or disallow initialization in variable declarations + 'line-comment-position': 'off', // disable: enforce position of line comments + 'max-classes-per-file': 'off', // disable: maximum number of classes per file + 'max-lines': 'off', // disable: maximum number of lines per file + 'max-lines-per-function': 'off', // disable: maximum number of lines of code in a function + 'max-params': 'off', // disable: enforce a maximum number of parameters in function definitions + 'max-statements': 'off', // disable: maximum number of statements allowed in function blocks + 'multiline-comment-style': 'off', // disable: enforce a particular style for multiline comments + 'new-cap': 'off', // disable: require constructor names to begin with a capital letter + 'no-bitwise': 'off', // disable: disallow bitwise operators + 'no-continue': 'off', // disable: disallow continue statements + 'no-empty-function': 'off', // disable: disallow empty functions + 'no-eq-null': 'off', // disable: disallow null comparisons without type-checking operators + 'no-implicit-coercion': 'off', // disable: disallow shorthand type conversions + 'no-inline-comments': 'off', // disable: disallow inline comments after code + 'no-magic-numbers': 'off', // disable: disallow magic numbers + 'no-negated-condition': 'off', // disable: disallow negated conditions + 'no-plusplus': 'off', // disable: disallow the unary operators ++ and -- + 'no-self-assign': 'off', // disable: disallow assignments where both sides are exactly the same + 'no-ternary': 'off', // disable: disallow ternary operators + 'no-undefined': 'off', // disable: disallow the use of undefined as an identifier + 'no-underscore-dangle': 'off', // disable: disallow dangling underscores in identifiers + 'no-useless-assignment': 'off', // disable: disallow variable assignments when the value is not used + 'no-warning-comments': 'off', // disable: disallow specified warning terms in comments + 'object-shorthand': 'off', // disable: require or disallow method and property shorthand syntax for object literals + 'one-var': 'off', // disable: enforce variables to be declared either together or separately in functions + 'prefer-arrow-callback': 'off', // disable: require using arrow functions for callbacks + 'prefer-destructuring': 'off', // disable: require destructuring from arrays and/or objects + 'prefer-named-capture-group': 'off', // disable: enforce using named capture group in regular expression + 'prefer-promise-reject-errors': 'off', // disable: require using Error objects as Promise rejection reasons + 'prefer-rest-params': 'off', // disable: require rest parameters instead of arguments + 'prefer-spread': 'off', // disable: require spread operators instead of .apply() + 'require-unicode-regexp': 'off', // disable: enforce the use of u or v flag on RegExp + 'sort-keys': 'off', // disable: require object keys to be sorted + }, + }, + + // jsdoc2rst (node) + { + files: ['js/jsdoc2rst/**/*.js', 'platform/web/js/jsdoc2rst/**/*.js'], + languageOptions: { + globals: globals.node, + }, + }, + + // engine files (browser) + { + files: ['js/engine/**/*.js', 'platform/web/js/engine/**/*.js'], + languageOptions: { + globals: { + ...globals.browser, + 'Features': true, + 'Godot': true, + 'InternalConfig': true, + 'Preloader': true, + }, + }, + }, + + // libraries and modules (browser) + { + files: ['js/libs/**/*.js', 'platform/web/js/libs/**/*.js', 'modules/**/*.js'], + languageOptions: { + globals: { + ...globals.browser, + ...emscriptenGlobals, + 'GodotConfig': true, + 'GodotEventListeners': true, + 'GodotFS': true, + 'GodotOS': true, + 'GodotRuntime': true, + 'IDHandler': true, + 'XRWebGLLayer': true, + }, + }, + }, + + // javascript templates (service workers) + { + files: ['misc/dist/html/**/*.js'], + languageOptions: { + globals: { + ...globals.browser, + '___GODOT_CACHE___': true, + '___GODOT_ENSURE_CROSSORIGIN_ISOLATION_HEADERS___': true, + '___GODOT_OPT_CACHE___': true, + }, + }, + }, + + // html templates + { + files: ['misc/dist/html/**/*.html'], + plugins: { + '@html-eslint': htmlPlugin, + 'eslint-plugin-html': pluginReference, + }, + languageOptions: { + parser: htmlParser, + globals: { + ...globals.browser, + 'Engine': true, + '$GODOT_CONFIG': true, + '$GODOT_PROJECT_NAME': true, + '$GODOT_THREADS_ENABLED': true, + '___GODOT_THREADS_ENABLED___': true, + }, + }, + rules: { + ...htmlPlugin.configs.recommended.rules, + '@html-eslint/indent': ['error', 'tab'], + '@html-eslint/require-closing-tags': ['error', { 'selfClosing': 'never' }], + 'no-alert': 'off', + 'no-console': 'off', + }, + }, + + { + ignores: [ + '**/eslint.config.cjs', + '**/.eslintrc*.js', + '**/*.externs.js', + ], + }, +]; diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 0bf3927e14..06dfe7d646 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -585,17 +585,20 @@ bool EditorExportPlatformWeb::poll_export() { } } + int prev = menu_options; + menu_options = preset.is_valid(); HTTPServerState prev_server_state = server_state; server_state = HTTP_SERVER_STATE_OFF; if (server->is_listening()) { - if (preset.is_null()) { + if (preset.is_null() || menu_options == 0) { server->stop(); } else { server_state = HTTP_SERVER_STATE_ON; + menu_options += 1; } } - return server_state != prev_server_state; + return server_state != prev_server_state || menu_options != prev; } Ref<ImageTexture> EditorExportPlatformWeb::get_option_icon(int p_index) const { diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h index 9d3a1a7861..d3d2083a23 100644 --- a/platform/web/export/export_plugin.h +++ b/platform/web/export/export_plugin.h @@ -58,6 +58,7 @@ class EditorExportPlatformWeb : public EditorExportPlatform { HTTPServerState server_state = HTTP_SERVER_STATE_OFF; Ref<EditorHTTPServer> server; + int menu_options = 0; String _get_template_name(bool p_extension, bool p_thread_support, bool p_debug) const { String name = "web"; diff --git a/platform/web/js/engine/features.js b/platform/web/js/engine/features.js index 263ea6ac88..601fa4a117 100644 --- a/platform/web/js/engine/features.js +++ b/platform/web/js/engine/features.js @@ -1,4 +1,4 @@ -const Features = { // eslint-disable-line no-unused-vars +const Features = { /** * Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for. * diff --git a/platform/web/js/libs/library_godot_display.js b/platform/web/js/libs/library_godot_display.js index 99fc429d8f..9445ec7ae5 100644 --- a/platform/web/js/libs/library_godot_display.js +++ b/platform/web/js/libs/library_godot_display.js @@ -279,7 +279,7 @@ const GodotDisplayScreen = { return 0; }, _updateGL: function () { - const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef + const gl_context_handle = _emscripten_webgl_get_current_context(); const gl = GL.getContext(gl_context_handle); if (gl) { GL.resizeOffscreenFramebuffer(gl); @@ -392,19 +392,19 @@ const GodotDisplay = { const func = GodotRuntime.get_func(p_callback); function listener_end(evt) { - evt.currentTarget.cb(1 /*TTS_UTTERANCE_ENDED*/, evt.currentTarget.id, 0); + evt.currentTarget.cb(1 /* TTS_UTTERANCE_ENDED */, evt.currentTarget.id, 0); } function listener_start(evt) { - evt.currentTarget.cb(0 /*TTS_UTTERANCE_STARTED*/, evt.currentTarget.id, 0); + evt.currentTarget.cb(0 /* TTS_UTTERANCE_STARTED */, evt.currentTarget.id, 0); } function listener_error(evt) { - evt.currentTarget.cb(2 /*TTS_UTTERANCE_CANCELED*/, evt.currentTarget.id, 0); + evt.currentTarget.cb(2 /* TTS_UTTERANCE_CANCELED */, evt.currentTarget.id, 0); } function listener_bound(evt) { - evt.currentTarget.cb(3 /*TTS_UTTERANCE_BOUNDARY*/, evt.currentTarget.id, evt.charIndex); + evt.currentTarget.cb(3 /* TTS_UTTERANCE_BOUNDARY */, evt.currentTarget.id, evt.charIndex); } const utterance = new SpeechSynthesisUtterance(GodotRuntime.parseString(p_text)); diff --git a/platform/web/js/libs/library_godot_os.js b/platform/web/js/libs/library_godot_os.js index 92635cb6ae..568212275b 100644 --- a/platform/web/js/libs/library_godot_os.js +++ b/platform/web/js/libs/library_godot_os.js @@ -78,7 +78,7 @@ const GodotConfig = { }, locate_file: function (file) { - return Module['locateFile'](file); // eslint-disable-line no-undef + return Module['locateFile'](file); }, clear: function () { GodotConfig.canvas = null; diff --git a/platform/web/js/libs/library_godot_runtime.js b/platform/web/js/libs/library_godot_runtime.js index 73581759b0..4114aa566e 100644 --- a/platform/web/js/libs/library_godot_runtime.js +++ b/platform/web/js/libs/library_godot_runtime.js @@ -34,7 +34,7 @@ const GodotRuntime = { * Functions */ get_func: function (ptr) { - return wasmTable.get(ptr); // eslint-disable-line no-undef + return wasmTable.get(ptr); }, /* @@ -52,19 +52,19 @@ const GodotRuntime = { * Memory */ malloc: function (p_size) { - return _malloc(p_size); // eslint-disable-line no-undef + return _malloc(p_size); }, free: function (p_ptr) { - _free(p_ptr); // eslint-disable-line no-undef + _free(p_ptr); }, getHeapValue: function (p_ptr, p_type) { - return getValue(p_ptr, p_type); // eslint-disable-line no-undef + return getValue(p_ptr, p_type); }, setHeapValue: function (p_ptr, p_value, p_type) { - setValue(p_ptr, p_value, p_type); // eslint-disable-line no-undef + setValue(p_ptr, p_value, p_type); }, heapSub: function (p_heap, p_ptr, p_len) { @@ -86,7 +86,7 @@ const GodotRuntime = { * Strings */ parseString: function (p_ptr) { - return UTF8ToString(p_ptr); // eslint-disable-line no-undef + return UTF8ToString(p_ptr); }, parseStringArray: function (p_ptr, p_size) { @@ -99,13 +99,13 @@ const GodotRuntime = { }, strlen: function (p_str) { - return lengthBytesUTF8(p_str); // eslint-disable-line no-undef + return lengthBytesUTF8(p_str); }, allocString: function (p_str) { const length = GodotRuntime.strlen(p_str) + 1; const c_str = GodotRuntime.malloc(length); - stringToUTF8(p_str, c_str, length); // eslint-disable-line no-undef + stringToUTF8(p_str, c_str, length); return c_str; }, @@ -126,7 +126,7 @@ const GodotRuntime = { }, stringToHeap: function (p_str, p_ptr, p_len) { - return stringToUTF8Array(p_str, HEAP8, p_ptr, p_len); // eslint-disable-line no-undef + return stringToUTF8Array(p_str, HEAP8, p_ptr, p_len); }, }, }; diff --git a/platform/web/js/libs/library_godot_webgl2.js b/platform/web/js/libs/library_godot_webgl2.js index 005b4b7917..4621b2e3c2 100644 --- a/platform/web/js/libs/library_godot_webgl2.js +++ b/platform/web/js/libs/library_godot_webgl2.js @@ -38,7 +38,7 @@ const GodotWebGL2 = { godot_webgl2_glGetBufferSubData__sig: 'vippp', godot_webgl2_glGetBufferSubData__deps: ['$GL', 'emscripten_webgl_get_current_context'], godot_webgl2_glGetBufferSubData: function (target, offset, size, data) { - const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef + const gl_context_handle = _emscripten_webgl_get_current_context(); const gl = GL.getContext(gl_context_handle); if (gl) { gl.GLctx['getBufferSubData'](target, offset, HEAPU8, data, size); diff --git a/platform/web/package-lock.json b/platform/web/package-lock.json index a1615fe591..a2e0fd3b27 100644 --- a/platform/web/package-lock.json +++ b/platform/web/package-lock.json @@ -9,28 +9,21 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@html-eslint/eslint-plugin": "^0.19.1", - "@html-eslint/parser": "^0.19.1", - "eslint": "^8.46.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-html": "^7.1.0", - "eslint-plugin-import": "^2.28.0", - "jsdoc": "^4.0.2" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "@eslint/js": "^9.3.0", + "@html-eslint/eslint-plugin": "^0.24.1", + "@html-eslint/parser": "^0.24.1", + "@stylistic/eslint-plugin": "^2.1.0", + "eslint": "^9.3.0", + "eslint-plugin-html": "^8.1.1", + "espree": "^10.0.1", + "globals": "^15.3.0", + "jsdoc": "^4.0.3" } }, "node_modules/@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -54,25 +47,37 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", - "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -80,34 +85,46 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", - "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz", + "integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@html-eslint/eslint-plugin": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.19.1.tgz", - "integrity": "sha512-Tn+/GWLtNM6NiZFLbfM+vTK0d7gKaDgnw4Pp+DsZi09lFimi4bOPOgy8dSVnLeyFIfP6LkeuwVm4pfBZZM2qbA==", + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.24.1.tgz", + "integrity": "sha512-JwNDQBrNIWEPcxgSpla/2jaUXyQCqL7Xp8CmON4Bk5qg8MwiDLXOgjylfVC+tN52i8JeHWMca34I9DqBGRj9Qg==", "dev": true, "engines": { - "node": ">=8.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@html-eslint/parser": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.19.1.tgz", - "integrity": "sha512-dpAw6UX0ZSVNnsAzl9ULHZX7CvAGKF5uta4iebbhSDvGE1o9NX6BoOofD/6WucTvs/qnoKojc3Y2LG6vy4afiQ==", + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.24.1.tgz", + "integrity": "sha512-O13xX/+Ldh0P7VZMpDDYc3XtWiE1cYm5QhVJ0VB5i7D8Q69HrrGN+5BjS17vkCoLTz+3zWWIiJv4oFmyS5LReA==", "dev": true, "dependencies": { "es-html-parser": "^0.0.9" @@ -117,13 +134,13 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -144,15 +161,28 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@jsdoc/salty": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.5.tgz", - "integrity": "sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.8.tgz", + "integrity": "sha512-5e+SFVavj1ORKlKaKr2BmTOekmXbelU7dC0cDkQLqag7xfuTPuGMUFx7KWJuv4bYZrTsoL2Z18VVCOKYxzoHcg==", "dev": true, "dependencies": { "lodash": "^4.17.21" @@ -196,218 +226,364 @@ "node": ">= 8" } }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true - }, - "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "node_modules/@stylistic/eslint-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.1.0.tgz", + "integrity": "sha512-cBBowKP2u/+uE5CzgH5w8pE9VKqcM7BXdIDPIbGt2rmLJGnA6MJPr9vYGaqgMoJFs7R/FzsMQerMvvEP40g2uw==", "dev": true, "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@stylistic/eslint-plugin-js": "2.1.0", + "@stylistic/eslint-plugin-jsx": "2.1.0", + "@stylistic/eslint-plugin-plus": "2.1.0", + "@stylistic/eslint-plugin-ts": "2.1.0", + "@types/eslint": "^8.56.10" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" } }, - "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true + "node_modules/@stylistic/eslint-plugin-js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.1.0.tgz", + "integrity": "sha512-gdXUjGNSsnY6nPyqxu6lmDTtVrwCOjun4x8PUn0x04d5ucLI74N3MT1Q0UhdcOR9No3bo5PGDyBgXK+KmD787A==", + "dev": true, + "dependencies": { + "@types/eslint": "^8.56.10", + "acorn": "^8.11.3", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "node_modules/@stylistic/eslint-plugin-jsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-2.1.0.tgz", + "integrity": "sha512-mMD7S+IndZo2vxmwpHVTCwx2O1VdtE5tmpeNwgaEcXODzWV1WTWpnsc/PECQKIr/mkLPFWiSIqcuYNhQ/3l6AQ==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@stylistic/eslint-plugin-js": "^2.1.0", + "@types/eslint": "^8.56.10", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" }, "engines": { - "node": ">=0.4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@stylistic/eslint-plugin-plus": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-2.1.0.tgz", + "integrity": "sha512-S5QAlgYXESJaSBFhBSBLZy9o36gXrXQwWSt6QkO+F0SrT9vpV5JF/VKoh+ojO7tHzd8Ckmyouq02TT9Sv2B0zQ==", "dev": true, + "dependencies": { + "@types/eslint": "^8.56.10", + "@typescript-eslint/utils": "^7.8.0" + }, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "*" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/utils": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@stylistic/eslint-plugin-ts": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.1.0.tgz", + "integrity": "sha512-2ioFibufHYBALx2TBrU4KXovCkN8qCqcb9yIHc0fyOfTaO5jw4d56WW7YRcF3Zgde6qFyXwAN6z/+w4pnmos1g==", "dev": true, + "dependencies": { + "@stylistic/eslint-plugin-js": "2.1.0", + "@types/eslint": "^8.56.10", + "@typescript-eslint/utils": "^7.8.0" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/utils": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/@types/eslint": { + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/linkify-it": "^5", + "@types/mdurl": "^2" } }, - "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz", + "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz", - "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==", + "node_modules/@typescript-eslint/types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz", + "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz", + "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz", + "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" + "@typescript-eslint/types": "7.10.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">= 0.4" + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/balanced-match": { @@ -432,17 +608,16 @@ "concat-map": "0.0.1" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "fill-range": "^7.1.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, "node_modules/callsites": { @@ -506,12 +681,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -549,32 +718,16 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "path-type": "^4.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, "node_modules/dom-serializer": { @@ -644,105 +797,12 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.1", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-html-parser": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/es-html-parser/-/es-html-parser-0.0.9.tgz", "integrity": "sha512-oniQMi+466VFsDzcdron9Ry/sqUJpDJg1bbDn0jFJKDdxXhwIOYDr4DgBnO5/yPLGj2xv+n5yy4L1Q0vAC5TYQ==", "dev": true }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -756,40 +816,37 @@ } }, "node_modules/eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", - "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.3.0.tgz", + "integrity": "sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.1", - "@eslint/js": "^8.46.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.3.0", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.2", - "espree": "^9.6.1", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", @@ -803,179 +860,64 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", - "dev": true, - "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/eslint-plugin-html": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-7.1.0.tgz", - "integrity": "sha512-fNLRraV/e6j8e3XYOC9xgND4j+U7b1Rq+OygMlLcMg+wI/IpVbF+ubQa3R78EjKB9njT6TQOlcK5rFKBVVtdfg==", - "dev": true, - "dependencies": { - "htmlparser2": "^8.0.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz", - "integrity": "sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.findlastindex": "^1.2.2", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.8.0", - "has": "^1.0.3", - "is-core-module": "^2.12.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.6", - "object.groupby": "^1.0.0", - "object.values": "^1.1.6", - "resolve": "^1.22.3", - "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-8.1.1.tgz", + "integrity": "sha512-6qmlJsc40D2m3Dn9oEH+0PAOkJhxVu0f5sVItqpCE0YWgYnyP4xCjBc3UWTHaJcY9ARkWOLIIuXLq0ndRnQOHw==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "htmlparser2": "^9.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.11.3", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -1029,6 +971,34 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1042,24 +1012,36 @@ "dev": true }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "to-regex-range": "^5.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=8" } }, "node_modules/find-up": { @@ -1079,123 +1061,24 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1209,45 +1092,35 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.3.0.tgz", + "integrity": "sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/graceful-fs": { @@ -1256,33 +1129,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1292,61 +1138,10 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", @@ -1358,14 +1153,14 @@ "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "domutils": "^3.1.0", + "entities": "^4.5.0" } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -1396,117 +1191,6 @@ "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1528,31 +1212,13 @@ "node": ">=0.10.0" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.12.0" } }, "node_modules/is-path-inside": { @@ -1564,97 +1230,6 @@ "node": ">=8" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1683,21 +1258,21 @@ } }, "node_modules/jsdoc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", - "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.3.tgz", + "integrity": "sha512-Nu7Sf35kXJ1MWDZIMAuATRQTg1iIPdzh7tqJ6jjvaU/GfDf+qi5UV8zJR3Mo+/pYFvm8mzay4+6O5EWigaQBQw==", "dev": true, "dependencies": { "@babel/parser": "^7.20.15", "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", + "@types/markdown-it": "^14.1.1", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", @@ -1720,6 +1295,12 @@ "node": ">=8" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1732,16 +1313,13 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "json-buffer": "3.0.1" } }, "node_modules/klaw": { @@ -1767,12 +1345,12 @@ } }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/locate-path": { @@ -1803,19 +1381,20 @@ "dev": true }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/markdown-it-anchor": { @@ -1828,15 +1407,6 @@ "markdown-it": "*" } }, - "node_modules/markdown-it/node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/marked": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", @@ -1850,11 +1420,45 @@ } }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1867,15 +1471,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -1900,123 +1495,18 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz", - "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.21.2", - "get-intrinsic": "^1.2.1" - } - }, - "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -2073,15 +1563,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2091,11 +1572,26 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -2107,9 +1603,18 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "dev": true, "engines": { "node": ">=6" @@ -2135,23 +1640,6 @@ } ] }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/requizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", @@ -2161,23 +1649,6 @@ "lodash": "^4.17.21" } }, - "node_modules/resolve": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz", - "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.12.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2197,21 +1668,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2235,45 +1691,16 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/shebang-command": { @@ -2297,63 +1724,13 @@ "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, "node_modules/strip-ansi": { @@ -2368,15 +1745,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2401,146 +1769,68 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1" + "is-number": "^7.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "node": ">=16" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "typescript": ">=4.2.0" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14.17" } }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", "dev": true }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/underscore": { "version": "1.13.6", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", @@ -2571,47 +1861,15 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, "node_modules/xmlcreate": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", diff --git a/platform/web/package.json b/platform/web/package.json index 4e17cd530b..588af2ff3b 100644 --- a/platform/web/package.json +++ b/platform/web/package.json @@ -3,32 +3,22 @@ "private": true, "version": "1.0.0", "description": "Development and linting setup for Godot's Web platform code", + "author": "Godot Engine contributors", + "license": "MIT", "scripts": { "docs": "jsdoc --template js/jsdoc2rst/ js/engine/engine.js js/engine/config.js js/engine/features.js --destination ''", - "lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules && npm run lint:tools && npm run lint:sw && npm run lint:html", - "lint:engine": "eslint \"js/engine/*.js\" --no-eslintrc -c .eslintrc.engine.js", - "lint:sw": "eslint \"../../misc/dist/html/service-worker.js\" --no-eslintrc -c .eslintrc.sw.js", - "lint:libs": "eslint \"js/libs/*.js\" --no-eslintrc -c .eslintrc.libs.js", - "lint:modules": "eslint \"../../modules/**/*.js\" --no-eslintrc -c .eslintrc.libs.js", - "lint:tools": "eslint \"js/jsdoc2rst/**/*.js\" --no-eslintrc -c .eslintrc.engine.js", - "lint:html": "eslint \"../../misc/dist/html/*.html\" --no-eslintrc -c .eslintrc.html.js", - "format": "npm run format:engine && npm run format:libs && npm run format:modules && npm run format:tools && format:sw && npm run format:html", - "format:engine": "npm run lint:engine -- --fix", - "format:libs": "npm run lint:libs -- --fix", - "format:modules": "npm run lint:modules -- --fix", - "format:tools": "npm run lint:tools -- --fix", - "format:html": "npm run lint:html -- --fix", - "format:sw": "npm run lint:sw -- --fix" + "lint": "cd ../.. && eslint --no-config-lookup --config ./platform/web/eslint.config.cjs ./platform/web/js ./modules ./misc/dist/html", + "format": "npm run lint -- --fix" }, - "author": "Godot Engine contributors", - "license": "MIT", "devDependencies": { - "@html-eslint/eslint-plugin": "^0.19.1", - "@html-eslint/parser": "^0.19.1", - "eslint": "^8.46.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.28.0", - "jsdoc": "^4.0.2", - "eslint-plugin-html": "^7.1.0" + "@eslint/js": "^9.3.0", + "@html-eslint/eslint-plugin": "^0.24.1", + "@html-eslint/parser": "^0.24.1", + "@stylistic/eslint-plugin": "^2.1.0", + "eslint": "^9.3.0", + "eslint-plugin-html": "^8.1.1", + "espree": "^10.0.1", + "globals": "^15.3.0", + "jsdoc": "^4.0.3" } } diff --git a/platform/web/serve.py b/platform/web/serve.py index 89dff63ca3..f0b0ec9622 100755 --- a/platform/web/serve.py +++ b/platform/web/serve.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 -from http.server import HTTPServer, SimpleHTTPRequestHandler, test # type: ignore -from pathlib import Path -import os -import sys import argparse import contextlib +import os import socket import subprocess +import sys +from http.server import HTTPServer, SimpleHTTPRequestHandler, test # type: ignore +from pathlib import Path # See cpython GH-17851 and GH-17864. diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 435c501956..1c2bfb9b75 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -4,6 +4,7 @@ Import("env") import os from pathlib import Path + import platform_windows_builders sources = [] diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 93eb34001e..0d3e9c606e 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -1,12 +1,12 @@ -import methods import os import subprocess import sys -from methods import print_warning, print_error -from platform_methods import detect_arch - from typing import TYPE_CHECKING +import methods +from methods import print_error, print_warning +from platform_methods import detect_arch + if TYPE_CHECKING: from SCons.Script.SConscript import SConsEnvironment @@ -178,7 +178,7 @@ def get_opts(): caller_frame = inspect.stack()[1] caller_script_dir = os.path.dirname(os.path.abspath(caller_frame[1])) d3d12_deps_folder = os.path.join(caller_script_dir, "bin", "build_deps") - except: # Give up. + except Exception: # Give up. d3d12_deps_folder = "" return [ @@ -248,10 +248,10 @@ def get_doc_path(): def get_flags(): arch = detect_build_env_arch() or detect_arch() - return [ - ("arch", arch), - ("supported", ["mono"]), - ] + return { + "arch": arch, + "supported": ["mono"], + } def build_res_file(target, source, env: "SConsEnvironment"): @@ -523,7 +523,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): env.Append(CXXFLAGS=["/bigobj"]) # PIX - if not env["arch"] in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]): + if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]): env["use_pix"] = False if env["use_pix"]: @@ -750,7 +750,7 @@ def configure_mingw(env: "SConsEnvironment"): env.Append(LIBS=["dxgi", "dxguid"]) # PIX - if not env["arch"] in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]): + if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]): env["use_pix"] = False if env["use_pix"]: diff --git a/platform/windows/platform_windows_builders.py b/platform/windows/platform_windows_builders.py index 729d55cea6..3fd9e1a581 100644 --- a/platform/windows/platform_windows_builders.py +++ b/platform/windows/platform_windows_builders.py @@ -1,8 +1,8 @@ """Functions used to generate source files during build time""" import os -from detect import get_mingw_bin_prefix -from detect import try_cmd + +from detect import get_mingw_bin_prefix, try_cmd def make_debug_mingw(target, source, env): diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp index 6fc33afe99..6c54faa13a 100644 --- a/platform/windows/windows_terminal_logger.cpp +++ b/platform/windows/windows_terminal_logger.cpp @@ -42,20 +42,30 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er return; } - const unsigned int BUFFER_SIZE = 16384; - char buf[BUFFER_SIZE + 1]; // +1 for the terminating character - int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list); - if (len <= 0) { - return; + const int static_buffer_size = 1024; + char static_buf[static_buffer_size]; + char *buf = static_buf; + va_list list_copy; + va_copy(list_copy, p_list); + int len = vsnprintf(buf, static_buffer_size, p_format, p_list); + if (len >= static_buffer_size) { + buf = (char *)memalloc(len + 1); + len = vsnprintf(buf, len + 1, p_format, list_copy); + } + va_end(list_copy); + + String str_buf = String::utf8(buf, len).replace("\r\n", "\n").replace("\n", "\r\n"); + if (len >= static_buffer_size) { + memfree(buf); } - if ((unsigned int)len >= BUFFER_SIZE) { - len = BUFFER_SIZE; // Output is too big, will be truncated + CharString cstr_buf = str_buf.utf8(); + if (cstr_buf.length() == 0) { + return; } - buf[len] = 0; DWORD written = 0; HANDLE h = p_err ? GetStdHandle(STD_ERROR_HANDLE) : GetStdHandle(STD_OUTPUT_HANDLE); - WriteFile(h, &buf[0], len, &written, nullptr); + WriteFile(h, cstr_buf.ptr(), cstr_buf.length(), &written, nullptr); #ifdef DEBUG_ENABLED FlushFileBuffers(h); diff --git a/platform_methods.py b/platform_methods.py index 5326e36077..2b157da22b 100644 --- a/platform_methods.py +++ b/platform_methods.py @@ -1,10 +1,7 @@ import os -import sys -import json import platform -import uuid -import functools import subprocess + import methods # NOTE: The multiprocessing module is not compatible with SCons due to conflict on cPickle @@ -47,14 +44,14 @@ def get_build_version(short): import version name = "custom_build" - if os.getenv("BUILD_NAME") != None: + if os.getenv("BUILD_NAME") is not None: name = os.getenv("BUILD_NAME") v = "%d.%d" % (version.major, version.minor) if version.patch > 0: v += ".%d" % version.patch status = version.status if not short: - if os.getenv("GODOT_VERSION_STATUS") != None: + if os.getenv("GODOT_VERSION_STATUS") is not None: status = str(os.getenv("GODOT_VERSION_STATUS")) v += ".%s.%s" % (status, name) return v @@ -86,7 +83,7 @@ def get_mvk_sdk_path(osname): def int_or_zero(i): try: return int(i) - except: + except (TypeError, ValueError): return 0 def ver_parse(a): @@ -140,9 +137,8 @@ def detect_mvk(env, osname): ) for mvk_path in mvk_list: - if mvk_path and os.path.isfile(os.path.join(mvk_path, osname + "/libMoltenVK.a")): - mvk_found = True - print("MoltenVK found at: " + mvk_path) + if mvk_path and os.path.isfile(os.path.join(mvk_path, f"{osname}/libMoltenVK.a")): + print(f"MoltenVK found at: {mvk_path}") return mvk_path return "" diff --git a/pyproject.toml b/pyproject.toml index f1ea10fbae..34ae075f2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,19 @@ namespace_packages = true explicit_package_bases = true exclude = ["thirdparty/"] -[tool.black] +[tool.ruff] +extend-exclude = ["thirdparty"] +extend-include = ["SConstruct", "SCsub"] line-length = 120 -extend-exclude = ".*thirdparty/.*" +target-version = "py37" + +[tool.ruff.lint] +extend-select = [ + "I", # isort +] + +[tool.ruff.lint.per-file-ignores] +"{SConstruct,SCsub}" = [ + "E402", # Module level import not at top of file + "F821", # Undefined name +] diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 523d81e874..f7d672620d 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -225,91 +225,6 @@ int TileMap::get_rendering_quadrant_size() const { return rendering_quadrant_size; } -void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_normalized_animation_offset) { - ERR_FAIL_COND(!p_tile_set.is_valid()); - ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); - TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Check for the frame. - if (p_frame >= 0) { - ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords)); - } - - // Get the texture. - Ref<Texture2D> tex = atlas_source->get_runtime_texture(); - if (!tex.is_valid()) { - return; - } - - // Check if we are in the texture, return otherwise. - Vector2i grid_size = atlas_source->get_atlas_grid_size(); - if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { - return; - } - - // Get tile data. - const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile); - - // Get the tile modulation. - Color modulate = tile_data->get_modulate() * p_modulation; - - // Compute the offset. - Vector2 tile_offset = tile_data->get_texture_origin(); - - // Get destination rect. - Rect2 dest_rect; - dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size; - dest_rect.size.x += FP_ADJUST; - dest_rect.size.y += FP_ADJUST; - - bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); - if (transpose) { - dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); - } else { - dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); - } - - if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) { - dest_rect.size.x = -dest_rect.size.x; - } - - if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) { - dest_rect.size.y = -dest_rect.size.y; - } - - // Draw the tile. - if (p_frame >= 0) { - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) { - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - } else { - real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords); - real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed; - real_t animation_offset = p_normalized_animation_offset * animation_duration; - // Accumulate durations unaffected by the speed to avoid accumulating floating point division errors. - // Aka do `sum(duration[i]) / speed` instead of `sum(duration[i] / speed)`. - real_t time_unscaled = 0.0; - for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) { - real_t frame_duration_unscaled = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame); - real_t slice_start = time_unscaled / speed; - real_t slice_end = (time_unscaled + frame_duration_unscaled) / speed; - RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, slice_start, slice_end, animation_offset); - - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - - time_unscaled += frame_duration_unscaled; - } - RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0); - } - } -} - void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { if (p_tileset == tile_set) { return; diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 45604bfb8a..690102f730 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -63,8 +63,6 @@ private: // A compatibility enum to specify how is the data if formatted. mutable TileMapDataFormat format = TileMapDataFormat::TILE_MAP_DATA_FORMAT_3; - static constexpr float FP_ADJUST = 0.00001; - // Properties. Ref<TileSet> tile_set; int rendering_quadrant_size = 16; @@ -123,8 +121,6 @@ public: void set_rendering_quadrant_size(int p_size); int get_rendering_quadrant_size() const; - static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0); - // Accessors. void set_tileset(const Ref<TileSet> &p_tileset); Ref<TileSet> get_tileset() const; diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index 03fd7364c0..0ac236eaa7 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -340,7 +340,7 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) { } // Drawing the tile in the canvas item. - TileMap::draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, get_self_modulate(), tile_data, random_animation_offset); + draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, get_self_modulate(), tile_data, random_animation_offset); } // Reset physics interpolation for any recreated canvas items. @@ -603,6 +603,7 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder(occlusion_layer_index, flip_h, flip_v, transpose)->get_rid()); rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas()); rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index)); + rs->canvas_light_occluder_set_as_sdf_collision(occluder, tile_set->get_occlusion_layer_sdf_collision(occlusion_layer_index)); } else { // Clear occluder. if (occluder.is_valid()) { @@ -1695,18 +1696,18 @@ void TileMapLayer::_notification(int p_what) { _internal_update(true); } break; - case TileMap::NOTIFICATION_ENTER_CANVAS: { + case NOTIFICATION_ENTER_CANVAS: { dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true; _queue_internal_update(); } break; - case TileMap::NOTIFICATION_EXIT_CANVAS: { + case NOTIFICATION_EXIT_CANVAS: { dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true; // Update immediately on exiting, and force cleanup. _internal_update(true); } break; - case TileMap::NOTIFICATION_VISIBILITY_CHANGED: { + case NOTIFICATION_VISIBILITY_CHANGED: { dirty.flags[DIRTY_FLAGS_LAYER_VISIBILITY] = true; _queue_internal_update(); } break; @@ -2161,6 +2162,91 @@ TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords) const { } } +void TileMapLayer::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_normalized_animation_offset) { + ERR_FAIL_COND(p_tile_set.is_null()); + ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); + TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Check for the frame. + if (p_frame >= 0) { + ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords)); + } + + // Get the texture. + Ref<Texture2D> tex = atlas_source->get_runtime_texture(); + if (tex.is_null()) { + return; + } + + // Check if we are in the texture, return otherwise. + Vector2i grid_size = atlas_source->get_atlas_grid_size(); + if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { + return; + } + + // Get tile data. + const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile); + + // Get the tile modulation. + Color modulate = tile_data->get_modulate() * p_modulation; + + // Compute the offset. + Vector2 tile_offset = tile_data->get_texture_origin(); + + // Get destination rect. + Rect2 dest_rect; + dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size; + dest_rect.size.x += FP_ADJUST; + dest_rect.size.y += FP_ADJUST; + + bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + if (transpose) { + dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); + } else { + dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); + } + + if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) { + dest_rect.size.x = -dest_rect.size.x; + } + + if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) { + dest_rect.size.y = -dest_rect.size.y; + } + + // Draw the tile. + if (p_frame >= 0) { + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) { + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else { + real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords); + real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed; + real_t animation_offset = p_normalized_animation_offset * animation_duration; + // Accumulate durations unaffected by the speed to avoid accumulating floating point division errors. + // Aka do `sum(duration[i]) / speed` instead of `sum(duration[i] / speed)`. + real_t time_unscaled = 0.0; + for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) { + real_t frame_duration_unscaled = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame); + real_t slice_start = time_unscaled / speed; + real_t slice_end = (time_unscaled + frame_duration_unscaled) / speed; + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, slice_start, slice_end, animation_offset); + + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + + time_unscaled += frame_duration_unscaled; + } + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0); + } + } +} + void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile) { // Set the current cell tile (using integer position). Vector2i pk(p_coords); @@ -2211,7 +2297,7 @@ void TileMapLayer::erase_cell(const Vector2i &p_coords) { } void TileMapLayer::fix_invalid_tiles() { - ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot call fix_invalid_tiles() on a TileMap without a valid TileSet."); + ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot call fix_invalid_tiles() on a TileMapLayer without a valid TileSet."); RBSet<Vector2i> coords; for (const KeyValue<Vector2i, CellData> &E : tile_map_layer_data) { diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h index 5861433c8a..57c83d7c4c 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -269,6 +269,8 @@ public: }; private: + static constexpr float FP_ADJUST = 0.00001; + // Properties. HashMap<Vector2i, CellData> tile_map_layer_data; @@ -407,6 +409,8 @@ public: // Not exposed to users. TileMapCell get_cell(const Vector2i &p_coords) const; + static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0); + ////////////// Exposed functions ////////////// // --- Cells manipulation --- diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 498e101b3c..a4804e928a 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -847,12 +847,13 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { ERR_FAIL_INDEX(p_bone_idx, bone_size); Bone *bonesptr = bones.ptrw(); - List<int> bones_to_process = List<int>(); + thread_local LocalVector<int> bones_to_process; + bones_to_process.clear(); bones_to_process.push_back(p_bone_idx); - while (bones_to_process.size() > 0) { - int current_bone_idx = bones_to_process.front()->get(); - bones_to_process.erase(current_bone_idx); + uint32_t index = 0; + while (index < bones_to_process.size()) { + int current_bone_idx = bones_to_process[index]; Bone &b = bonesptr[current_bone_idx]; bool bone_enabled = b.enabled && !show_rest_only; @@ -905,6 +906,8 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { for (int i = 0; i < child_bone_size; i++) { bones_to_process.push_back(b.child_bones[i]); } + + index++; } } diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 15b7f62036..3bf9d79c7f 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -50,6 +50,83 @@ void Button::_set_internal_margin(Side p_side, float p_value) { void Button::_queue_update_size_cache() { } +void Button::_update_theme_item_cache() { + Control::_update_theme_item_cache(); + + const bool rtl = is_layout_rtl(); + if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) { + theme_cache.max_style_size = theme_cache.normal_mirrored->get_minimum_size(); + theme_cache.style_margin_left = theme_cache.normal_mirrored->get_margin(SIDE_LEFT); + theme_cache.style_margin_right = theme_cache.normal_mirrored->get_margin(SIDE_RIGHT); + theme_cache.style_margin_top = theme_cache.normal_mirrored->get_margin(SIDE_TOP); + theme_cache.style_margin_bottom = theme_cache.normal_mirrored->get_margin(SIDE_BOTTOM); + } else { + theme_cache.max_style_size = theme_cache.normal->get_minimum_size(); + theme_cache.style_margin_left = theme_cache.normal->get_margin(SIDE_LEFT); + theme_cache.style_margin_right = theme_cache.normal->get_margin(SIDE_RIGHT); + theme_cache.style_margin_top = theme_cache.normal->get_margin(SIDE_TOP); + theme_cache.style_margin_bottom = theme_cache.normal->get_margin(SIDE_BOTTOM); + } + if (has_theme_stylebox("hover_pressed")) { + if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover_pressed_mirrored->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover_pressed_mirrored->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover_pressed_mirrored->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover_pressed_mirrored->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover_pressed_mirrored->get_margin(SIDE_BOTTOM)); + } else { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover_pressed->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover_pressed->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover_pressed->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover_pressed->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover_pressed->get_margin(SIDE_BOTTOM)); + } + } + if (rtl && has_theme_stylebox(SNAME("pressed_mirrored"))) { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.pressed_mirrored->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.pressed_mirrored->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.pressed_mirrored->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.pressed_mirrored->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.pressed_mirrored->get_margin(SIDE_BOTTOM)); + } else { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.pressed->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.pressed->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.pressed->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.pressed->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.pressed->get_margin(SIDE_BOTTOM)); + } + if (rtl && has_theme_stylebox(SNAME("hover_mirrored"))) { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover_mirrored->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover_mirrored->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover_mirrored->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover_mirrored->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover_mirrored->get_margin(SIDE_BOTTOM)); + } else { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover->get_margin(SIDE_BOTTOM)); + } + if (rtl && has_theme_stylebox(SNAME("disabled_mirrored"))) { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.disabled_mirrored->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.disabled_mirrored->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.disabled_mirrored->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.disabled_mirrored->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.disabled_mirrored->get_margin(SIDE_BOTTOM)); + } else { + theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.disabled->get_minimum_size()); + theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.disabled->get_margin(SIDE_LEFT)); + theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.disabled->get_margin(SIDE_RIGHT)); + theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.disabled->get_margin(SIDE_TOP)); + theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.disabled->get_margin(SIDE_BOTTOM)); + } +} + +Size2 Button::_get_largest_stylebox_size() const { + return theme_cache.max_style_size; +} + Ref<StyleBox> Button::_get_current_stylebox() const { Ref<StyleBox> stylebox = theme_cache.normal; const bool rtl = is_layout_rtl(); @@ -137,16 +214,13 @@ void Button::_notification(int p_what) { const RID ci = get_canvas_item(); const Size2 size = get_size(); - const Ref<StyleBox> style = _get_current_stylebox(); - { // Draws the stylebox in the current state. - if (!flat) { - style->draw(ci, Rect2(Point2(), size)); - } + // Draws the stylebox in the current state. + if (!flat) { + _get_current_stylebox()->draw(ci, Rect2(Point2(), size)); + } - if (has_focus()) { - Ref<StyleBox> style2 = theme_cache.focus; - style2->draw(ci, Rect2(Point2(), size)); - } + if (has_focus()) { + theme_cache.focus->draw(ci, Rect2(Point2(), size)); } Ref<Texture2D> _icon = icon; @@ -158,16 +232,11 @@ void Button::_notification(int p_what) { break; } - const float style_margin_left = style->get_margin(SIDE_LEFT); - const float style_margin_right = style->get_margin(SIDE_RIGHT); - const float style_margin_top = style->get_margin(SIDE_TOP); - const float style_margin_bottom = style->get_margin(SIDE_BOTTOM); - Size2 drawable_size_remained = size; { // The size after the stelybox is stripped. - drawable_size_remained.width -= style_margin_left + style_margin_right; - drawable_size_remained.height -= style_margin_top + style_margin_bottom; + drawable_size_remained.width -= theme_cache.style_margin_left + theme_cache.style_margin_right; + drawable_size_remained.height -= theme_cache.style_margin_top + theme_cache.style_margin_bottom; } const int h_separation = MAX(0, theme_cache.h_separation); @@ -312,12 +381,12 @@ void Button::_notification(int p_what) { [[fallthrough]]; case HORIZONTAL_ALIGNMENT_FILL: case HORIZONTAL_ALIGNMENT_LEFT: { - icon_ofs.x += style_margin_left; + icon_ofs.x += theme_cache.style_margin_left; icon_ofs.x += left_internal_margin_with_h_separation; } break; case HORIZONTAL_ALIGNMENT_RIGHT: { - icon_ofs.x = size.x - style_margin_right; + icon_ofs.x = size.x - theme_cache.style_margin_right; icon_ofs.x -= right_internal_margin_with_h_separation; icon_ofs.x -= icon_size.width; } break; @@ -330,11 +399,11 @@ void Button::_notification(int p_what) { [[fallthrough]]; case VERTICAL_ALIGNMENT_FILL: case VERTICAL_ALIGNMENT_TOP: { - icon_ofs.y += style_margin_top; + icon_ofs.y += theme_cache.style_margin_top; } break; case VERTICAL_ALIGNMENT_BOTTOM: { - icon_ofs.y = size.y - style_margin_bottom - icon_size.height; + icon_ofs.y = size.y - theme_cache.style_margin_bottom - icon_size.height; } break; } icon_ofs = icon_ofs.floor(); @@ -373,7 +442,7 @@ void Button::_notification(int p_what) { case HORIZONTAL_ALIGNMENT_FILL: case HORIZONTAL_ALIGNMENT_LEFT: case HORIZONTAL_ALIGNMENT_RIGHT: { - text_ofs.x += style_margin_left; + text_ofs.x += theme_cache.style_margin_left; text_ofs.x += left_internal_margin_with_h_separation; if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) { // Offset by the space's width that occupied by icon and h_separation together. @@ -382,7 +451,7 @@ void Button::_notification(int p_what) { } break; } - text_ofs.y = (drawable_size_remained.height - text_buf->get_size().height) / 2.0f + style_margin_top; + text_ofs.y = (drawable_size_remained.height - text_buf->get_size().height) / 2.0f + theme_cache.style_margin_top; if (vertical_icon_alignment == VERTICAL_ALIGNMENT_TOP) { text_ofs.y += custom_element_size.height - drawable_size_remained.height; // Offset by the icon's height. } @@ -452,7 +521,7 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu } } - return _get_current_stylebox()->get_minimum_size() + minsize; + return _get_largest_stylebox_size() + minsize; } void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) { diff --git a/scene/gui/button.h b/scene/gui/button.h index 86fdbd35d5..eefb690913 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -69,6 +69,12 @@ private: Ref<StyleBox> disabled_mirrored; Ref<StyleBox> focus; + Size2 max_style_size; + float style_margin_left = 0; + float style_margin_right = 0; + float style_margin_top = 0; + float style_margin_bottom = 0; + Color font_color; Color font_focus_color; Color font_pressed_color; @@ -94,16 +100,18 @@ private: int icon_max_width = 0; } theme_cache; - Size2 _fit_icon_size(const Size2 &p_size) const; - void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = ""); void _texture_changed(); protected: + virtual void _update_theme_item_cache() override; + void _set_internal_margin(Side p_side, float p_value); virtual void _queue_update_size_cache(); + Size2 _fit_icon_size(const Size2 &p_size) const; Ref<StyleBox> _get_current_stylebox() const; + Size2 _get_largest_stylebox_size() const; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp index af6696834e..99937aaf41 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -59,14 +59,14 @@ Size2 CheckBox::get_icon_size() const { if (!theme_cache.radio_unchecked_disabled.is_null()) { tex_size = tex_size.max(theme_cache.radio_unchecked_disabled->get_size()); } - return tex_size; + return _fit_icon_size(tex_size); } Size2 CheckBox::get_minimum_size() const { Size2 minsize = Button::get_minimum_size(); const Size2 tex_size = get_icon_size(); if (tex_size.width > 0 || tex_size.height > 0) { - const Size2 padding = _get_current_stylebox()->get_minimum_size(); + const Size2 padding = _get_largest_stylebox_size(); Size2 content_size = minsize - padding; if (content_size.width > 0 && tex_size.width > 0) { content_size.width += MAX(0, theme_cache.h_separation); @@ -127,9 +127,9 @@ void CheckBox::_notification(int p_what) { ofs.y = int((get_size().height - get_icon_size().height) / 2) + theme_cache.check_v_offset; if (is_pressed()) { - on_tex->draw(ci, ofs); + on_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(on_tex->get_size()))); } else { - off_tex->draw(ci, ofs); + off_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(off_tex->get_size()))); } } break; } diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index ab3b74a3c3..29b9504776 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -63,14 +63,14 @@ Size2 CheckButton::get_icon_size() const { tex_size = tex_size.max(off_tex->get_size()); } - return tex_size; + return _fit_icon_size(tex_size); } Size2 CheckButton::get_minimum_size() const { Size2 minsize = Button::get_minimum_size(); const Size2 tex_size = get_icon_size(); if (tex_size.width > 0 || tex_size.height > 0) { - const Size2 padding = _get_current_stylebox()->get_minimum_size(); + const Size2 padding = _get_largest_stylebox_size(); Size2 content_size = minsize - padding; if (content_size.width > 0 && tex_size.width > 0) { content_size.width += MAX(0, theme_cache.h_separation); @@ -134,9 +134,9 @@ void CheckButton::_notification(int p_what) { ofs.y = (get_size().height - tex_size.height) / 2 + theme_cache.check_v_offset; if (is_pressed()) { - on_tex->draw(ci, ofs); + on_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(on_tex->get_size()))); } else { - off_tex->draw(ci, ofs); + off_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(off_tex->get_size()))); } } break; } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index d5ad258df8..c9f3fc1dfe 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -898,6 +898,12 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { return true; } } + + // This prevents interactions with a port hotzone that is behind another node. + Rect2 graph_node_rect = Rect2(graph_node->get_position(), graph_node->get_size() * zoom); + if (graph_node_rect.has_point(click_pos * zoom)) { + break; + } } return false; @@ -1027,6 +1033,12 @@ void GraphEdit::_top_connection_layer_input(const Ref<InputEvent> &p_ev) { return; } } + + // This prevents interactions with a port hotzone that is behind another node. + Rect2 graph_node_rect = Rect2(graph_node->get_position(), graph_node->get_size() * zoom); + if (graph_node_rect.has_point(click_pos * zoom)) { + break; + } } } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 0f161a014a..68e72ea996 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -60,7 +60,7 @@ Size2 OptionButton::get_minimum_size() const { } if (has_theme_icon(SNAME("arrow"))) { - const Size2 padding = _get_current_stylebox()->get_minimum_size(); + const Size2 padding = _get_largest_stylebox_size(); const Size2 arrow_size = theme_cache.arrow_icon->get_size(); Size2 content_size = minsize - padding; diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 1d45a10d2a..38204af6d5 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -37,6 +37,7 @@ void Popup::_input_from_window(const Ref<InputEvent> &p_event) { if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) { + hide_reason = HIDE_REASON_CANCELED; // ESC pressed, mark as canceled unconditionally. _close_pressed(); } Window::_input_from_window(p_event); @@ -104,13 +105,18 @@ void Popup::_notification(int p_what) { case NOTIFICATION_WM_CLOSE_REQUEST: { if (!is_in_edited_scene_root()) { - hide_reason = HIDE_REASON_UNFOCUSED; + if (hide_reason == HIDE_REASON_NONE) { + hide_reason = HIDE_REASON_UNFOCUSED; + } _close_pressed(); } } break; case NOTIFICATION_APPLICATION_FOCUS_OUT: { if (!is_in_edited_scene_root() && get_flag(FLAG_POPUP)) { + if (hide_reason == HIDE_REASON_NONE) { + hide_reason = HIDE_REASON_UNFOCUSED; + } _close_pressed(); } } break; @@ -119,7 +125,9 @@ void Popup::_notification(int p_what) { void Popup::_parent_focused() { if (popped_up && get_flag(FLAG_POPUP)) { - hide_reason = HIDE_REASON_UNFOCUSED; + if (hide_reason == HIDE_REASON_NONE) { + hide_reason = HIDE_REASON_UNFOCUSED; + } _close_pressed(); } } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 49cfa8a030..69b84da23d 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -558,8 +558,6 @@ void TextEdit::_notification(int p_what) { int visible_rows = get_visible_line_count() + 1; - Color color = !editable ? theme_cache.font_readonly_color : theme_cache.font_color; - if (theme_cache.background_color.a > 0.01) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), theme_cache.background_color); } @@ -734,7 +732,7 @@ void TextEdit::_notification(int p_what) { if (draw_minimap) { int minimap_visible_lines = get_minimap_visible_lines(); int minimap_line_height = (minimap_char_size.y + minimap_line_spacing); - int minimap_tab_size = minimap_char_size.x * text.get_tab_size(); + int tab_size = text.get_tab_size(); // Calculate viewport size and y offset. int viewport_height = (draw_amount - 1) * minimap_line_height; @@ -839,68 +837,74 @@ void TextEdit::_notification(int p_what) { } } - Color previous_color; + Color next_color = current_color; int characters = 0; - int tabs = 0; + int tab_alignment = 0; + int xpos = xmargin_end + 2 + indent_px; for (int j = 0; j < str.length(); j++) { - const Variant *color_data = color_map.getptr(last_wrap_column + j); - if (color_data != nullptr) { - current_color = (color_data->operator Dictionary()).get("color", theme_cache.font_color); - if (!editable) { - current_color.a = theme_cache.font_readonly_color.a; + bool next_is_whitespace = false; + bool next_is_tab = false; + // Get the number of characters to draw together. + for (characters = 0; j + characters < str.length(); characters++) { + int next_char_index = j + characters; + const Variant *color_data = color_map.getptr(last_wrap_column + next_char_index); + if (color_data != nullptr) { + next_color = (color_data->operator Dictionary()).get("color", theme_cache.font_color); + if (!editable) { + next_color.a = theme_cache.font_readonly_color.a; + } + next_color.a *= 0.6; } - } - color = current_color; - - if (j == 0) { - previous_color = color; - } - - int xpos = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * j)) + tabs; - bool out_of_bounds = (xpos >= xmargin_end + minimap_width); - - bool whitespace = is_whitespace(str[j]); - if (!whitespace) { - characters++; - - if (j < str.length() - 1 && color == previous_color && !out_of_bounds) { - continue; + if (characters == 0) { + current_color = next_color; } - - // If we've changed color we are at the start of a new section, therefore we need to go back to the end - // of the previous section to draw it, we'll also add the character back on. - if (color != previous_color) { - characters--; - j--; - - if (str[j] == '\t') { - tabs -= minimap_tab_size; + if (next_color != current_color) { + break; + } + next_is_whitespace = is_whitespace(str[next_char_index]); + if (next_is_whitespace) { + if (str[next_char_index] == '\t') { + next_is_tab = true; } + break; + } + bool out_of_bounds = xpos + minimap_char_size.x * characters >= xmargin_end + minimap_width; + if (out_of_bounds) { + break; } } + if (!next_is_whitespace && characters == 0) { + break; + } if (characters > 0) { - previous_color.a *= 0.6; - // Take one for zero indexing, and if we hit whitespace / the end of a word. - int chars = MAX(0, (j - (characters - 1)) - (whitespace ? 1 : 0)) + 1; - int char_x_ofs = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * chars)) + tabs; if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - char_x_ofs - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - xpos - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), current_color); } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_x_ofs, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(xpos, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), current_color); } } - if (out_of_bounds) { - break; - } + j += characters - 1; + xpos += minimap_char_size.x * characters; + tab_alignment += characters; - if (str[j] == '\t') { - tabs += minimap_tab_size; + if (next_is_whitespace) { + if (next_is_tab) { + tab_alignment %= tab_size; + xpos += minimap_char_size.x * (tab_size - tab_alignment); + tab_alignment = 0; + } else { + xpos += minimap_char_size.x; + tab_alignment += 1; + } + j += 1; } - previous_color = color; - characters = 0; + if (xpos >= xmargin_end + minimap_width) { + // Out of bounds. + break; + } } } } @@ -1188,6 +1192,7 @@ void TextEdit::_notification(int p_what) { if (!clipped && lookup_symbol_word.length() != 0) { // Highlight word if (is_ascii_alphabet_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '_' || lookup_symbol_word[0] == '.') { + Color highlight_underline_color = !editable ? theme_cache.font_readonly_color : theme_cache.font_color; int lookup_symbol_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); int lookup_symbol_word_len = lookup_symbol_word.length(); while (lookup_symbol_word_col != -1) { @@ -1205,7 +1210,7 @@ void TextEdit::_notification(int p_what) { } rect.position.y += ceil(TS->shaped_text_get_ascent(rid)) + ceil(theme_cache.font->get_underline_position(theme_cache.font_size)); rect.size.y = MAX(1, theme_cache.font->get_underline_thickness(theme_cache.font_size)); - draw_rect(rect, color); + draw_rect(rect, highlight_underline_color); } lookup_symbol_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, lookup_symbol_word_col + lookup_symbol_word_len); @@ -7973,7 +7978,7 @@ void TextEdit::_update_minimap_click() { Point2 mp = get_local_mouse_pos(); int xmargin_end = get_size().width - theme_cache.style_normal->get_margin(SIDE_RIGHT); - if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.y > xmargin_end)) { + if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.x > xmargin_end)) { minimap_clicked = false; return; } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 133515754b..fc5b942918 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -3152,8 +3152,12 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int } void Tree::_text_editor_popup_modal_close() { + if (popup_edit_commited) { + return; // Already processed by LineEdit/TextEdit commit. + } + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { - return; + return; // ESC pressed, app focus lost, or forced close from code. } if (value_editor->has_point(value_editor->get_local_mouse_position())) { @@ -3172,9 +3176,18 @@ void Tree::_text_editor_popup_modal_close() { } void Tree::_text_editor_gui_input(const Ref<InputEvent> &p_event) { + if (popup_edit_commited) { + return; // Already processed by _text_editor_popup_modal_close + } + + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { + return; // ESC pressed, app focus lost, or forced close from code. + } + if (p_event->is_action_pressed("ui_text_newline_blank", true)) { accept_event(); } else if (p_event->is_action_pressed("ui_text_newline")) { + popup_edit_commited = true; // End edit popup processing. popup_editor->hide(); _apply_multiline_edit(); accept_event(); @@ -3205,6 +3218,15 @@ void Tree::_apply_multiline_edit() { } void Tree::_line_editor_submit(String p_text) { + if (popup_edit_commited) { + return; // Already processed by _text_editor_popup_modal_close + } + + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { + return; // ESC pressed, app focus lost, or forced close from code. + } + + popup_edit_commited = true; // End edit popup processing. popup_editor->hide(); if (!popup_edited_item) { @@ -4072,6 +4094,7 @@ bool Tree::edit_selected(bool p_force_edit) { if (!popup_editor->is_embedded()) { popup_editor->set_content_scale_factor(popup_scale); } + popup_edit_commited = false; // Start edit popup processing. popup_editor->popup(); popup_editor->child_controls_changed(); @@ -4091,6 +4114,7 @@ bool Tree::edit_selected(bool p_force_edit) { if (!popup_editor->is_embedded()) { popup_editor->set_content_scale_factor(popup_scale); } + popup_edit_commited = false; // Start edit popup processing. popup_editor->popup(); popup_editor->child_controls_changed(); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 311055a2f8..e9c93c6e03 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -479,6 +479,7 @@ private: VBoxContainer *popup_editor_vb = nullptr; + bool popup_edit_commited = true; Popup *popup_editor = nullptr; LineEdit *line_editor = nullptr; TextEdit *text_editor = nullptr; diff --git a/scene/resources/gradient_texture.cpp b/scene/resources/gradient_texture.cpp index 7df439a799..6ec9422d2d 100644 --- a/scene/resources/gradient_texture.cpp +++ b/scene/resources/gradient_texture.cpp @@ -136,6 +136,7 @@ void GradientTexture1D::_update() { texture = RS::get_singleton()->texture_2d_create(image); } } + RS::get_singleton()->texture_set_path(texture, get_path()); } void GradientTexture1D::set_width(int p_width) { @@ -275,6 +276,7 @@ void GradientTexture2D::_update() { } else { texture = RS::get_singleton()->texture_2d_create(image); } + RS::get_singleton()->texture_set_path(texture, get_path()); } float GradientTexture2D::_get_gradient_offset_at(int x, int y) const { diff --git a/scene/resources/portable_compressed_texture.cpp b/scene/resources/portable_compressed_texture.cpp index 918b5c0b41..002db30379 100644 --- a/scene/resources/portable_compressed_texture.cpp +++ b/scene/resources/portable_compressed_texture.cpp @@ -153,14 +153,14 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { Vector<uint8_t> data; if (p_compression_mode == COMPRESSION_MODE_LOSSY) { - data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality); + data = Image::webp_lossy_packer(i ? p_image->get_image_from_mipmap(i) : p_image, p_lossy_quality); encode_uint16(DATA_FORMAT_WEBP, buffer.ptrw() + 2); } else { if (use_webp) { - data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i)); + data = Image::webp_lossless_packer(i ? p_image->get_image_from_mipmap(i) : p_image); encode_uint16(DATA_FORMAT_WEBP, buffer.ptrw() + 2); } else { - data = Image::png_packer(p_image->get_image_from_mipmap(i)); + data = Image::png_packer(i ? p_image->get_image_from_mipmap(i) : p_image); encode_uint16(DATA_FORMAT_PNG, buffer.ptrw() + 2); } } diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 5e9510c1ac..dfe5bd4a47 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -155,7 +155,7 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr DocData::PropertyDoc prop_doc; prop_doc.name = "shader_parameter/" + pi.name; #ifdef MODULE_REGEX_ENABLED - const RegEx pattern("/\\*\\*([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/\\s*uniform\\s+\\w+\\s+" + pi.name + "(?=[\\s:;=])"); + const RegEx pattern("/\\*\\*\\s([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/\\s*uniform\\s+\\w+\\s+" + pi.name + "(?=[\\s:;=])"); Ref<RegExMatch> pattern_ref = pattern.search(code); if (pattern_ref != nullptr) { RegExMatch *match = pattern_ref.ptr(); @@ -173,7 +173,7 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr } } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && !class_doc.name.is_empty() && p_params) { + if (EditorHelp::get_doc_data() != nullptr && Engine::get_singleton()->is_editor_hint() && !class_doc.name.is_empty() && p_params) { EditorHelp::get_doc_data()->add_doc(class_doc); } #endif diff --git a/scene/theme/SCsub b/scene/theme/SCsub index 5f62ae4b05..2372d1820a 100644 --- a/scene/theme/SCsub +++ b/scene/theme/SCsub @@ -4,7 +4,6 @@ Import("env") import default_theme_builders - env.add_source_files(env.scene_sources, "*.cpp") SConscript("icons/SCsub") diff --git a/scene/theme/icons/SCsub b/scene/theme/icons/SCsub index 46133ccceb..1f3b7f6d17 100644 --- a/scene/theme/icons/SCsub +++ b/scene/theme/icons/SCsub @@ -4,7 +4,6 @@ Import("env") import default_theme_icons_builders - env["BUILDERS"]["MakeDefaultThemeIconsBuilder"] = Builder( action=env.Run(default_theme_icons_builders.make_default_theme_icons_action), suffix=".h", diff --git a/scu_builders.py b/scu_builders.py index a9ae428222..fc5461196f 100644 --- a/scu_builders.py +++ b/scu_builders.py @@ -1,11 +1,11 @@ -"""Functions used to generate scu build source files during build time -""" +"""Functions used to generate scu build source files during build time""" -import glob, os +import glob import math -from methods import print_error +import os from pathlib import Path -from os.path import normpath, basename + +from methods import print_error base_folder_path = str(Path(__file__).parent) + "/" base_folder_only = os.path.basename(os.path.normpath(base_folder_path)) @@ -25,7 +25,7 @@ def clear_out_stale_files(output_folder, extension, fresh_files): for file in glob.glob(output_folder + "/*." + extension): file = Path(file) - if not file in fresh_files: + if file not in fresh_files: # print("removed stale file: " + str(file)) os.remove(file) @@ -56,7 +56,7 @@ def find_files_in_folder(folder, sub_folder, include_list, extension, sought_exc li = '#include "' + folder + "/" + sub_folder_slashed + file + '"' - if not simple_name in sought_exceptions: + if simple_name not in sought_exceptions: include_list.append(li) else: found_exceptions.append(li) @@ -78,9 +78,9 @@ def write_output_file(file_count, include_list, start_line, end_line, output_fol file_text = "" - for l in range(start_line, end_line): - if l < len(include_list): - line = include_list[l] + for i in range(start_line, end_line): + if i < len(include_list): + line = include_list[i] li = line + "\n" file_text += li @@ -221,7 +221,6 @@ def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension= lines_per_file = max(lines_per_file, 1) start_line = 0 - file_number = 0 # These do not vary throughout the loop output_folder = abs_main_folder + "/scu/" diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 878ea7bcfb..bd45dbfbc9 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -1879,25 +1879,27 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const // LOD if (p_render_data->scene_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) { - // Get the LOD support points on the mesh AABB. - Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - - // Get the distances to those points on the AABB from the camera origin. - float distance_min = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_min); - float distance_max = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_max); - float distance = 0.0; - if (distance_min * distance_max < 0.0) { - //crossing plane - distance = 0.0; - } else if (distance_min >= 0.0) { - distance = distance_min; - } else if (distance_max <= 0.0) { - distance = -distance_max; + // Check if camera is NOT inside the mesh AABB. + if (!inst->transformed_aabb.has_point(p_render_data->scene_data->cam_transform.origin)) { + // Get the LOD support points on the mesh AABB. + Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); + Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); + + // Get the distances to those points on the AABB from the camera origin. + float distance_min = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_min); + float distance_max = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_max); + + if (distance_min * distance_max < 0.0) { + //crossing plane + distance = 0.0; + } else if (distance_min >= 0.0) { + distance = distance_min; + } else if (distance_max <= 0.0) { + distance = -distance_max; + } } - if (p_render_data->scene_data->cam_orthogonal) { distance = 1.0; } diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index 012451a5cd..14d138181f 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -169,7 +169,11 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color return; } - RD::get_singleton()->screen_prepare_for_drawing(DisplayServer::MAIN_WINDOW_ID); + Error err = RD::get_singleton()->screen_prepare_for_drawing(DisplayServer::MAIN_WINDOW_ID); + if (err != OK) { + // Window is minimized and does not have valid swapchain, skip drawing without printing errors. + return; + } RID texture = texture_storage->texture_allocate(); texture_storage->texture_2d_initialize(texture, p_image); diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index f844919df1..6e5e8f63e0 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1457,6 +1457,23 @@ void TextureStorage::texture_set_detect_roughness_callback(RID p_texture, RS::Te } void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) { + List<RID> textures; + texture_owner.get_owned_list(&textures); + + for (List<RID>::Element *E = textures.front(); E; E = E->next()) { + Texture *t = texture_owner.get_or_null(E->get()); + if (!t) { + continue; + } + RS::TextureInfo tinfo; + tinfo.path = t->path; + tinfo.format = t->format; + tinfo.width = t->width; + tinfo.height = t->height; + tinfo.depth = t->depth; + tinfo.bytes = Image::get_image_data_size(t->width, t->height, t->format, t->mipmaps); + r_info->push_back(tinfo); + } } void TextureStorage::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) { @@ -3042,6 +3059,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { texture_2d_placeholder_initialize(rt->texture); Texture *tex = get_texture(rt->texture); tex->is_render_target = true; + tex->path = "Render Target (Internal)"; } _clear_render_target(rt); diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 6746a1dde1..15e1731823 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -3154,9 +3154,6 @@ Error RenderingDevice::screen_create(DisplayServer::WindowID p_screen) { RDD::SwapChainID swap_chain = driver->swap_chain_create(surface); ERR_FAIL_COND_V_MSG(swap_chain.id == 0, ERR_CANT_CREATE, "Unable to create swap chain."); - Error err = driver->swap_chain_resize(main_queue, swap_chain, _get_swap_chain_desired_count()); - ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Unable to resize the new swap chain."); - screen_swap_chains[p_screen] = swap_chain; return OK; @@ -3774,13 +3771,13 @@ void RenderingDevice::draw_list_draw(DrawListID p_list, bool p_use_indices, uint #endif - // Bind descriptor sets. - +#ifdef DEBUG_ENABLED for (uint32_t i = 0; i < dl->state.set_count; i++) { if (dl->state.sets[i].pipeline_expected_format == 0) { - continue; // Nothing expected by this pipeline. + // Nothing expected by this pipeline. + continue; } -#ifdef DEBUG_ENABLED + if (dl->state.sets[i].pipeline_expected_format != dl->state.sets[i].uniform_set_format) { if (dl->state.sets[i].uniform_set_format == 0) { ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline."); @@ -3791,9 +3788,22 @@ void RenderingDevice::draw_list_draw(DrawListID p_list, bool p_use_indices, uint ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader)); } } + } #endif - draw_graph.add_draw_list_uniform_set_prepare_for_use(dl->state.pipeline_shader_driver_id, dl->state.sets[i].uniform_set_driver_id, i); + + // Prepare descriptor sets if the API doesn't use pipeline barriers. + if (!driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + for (uint32_t i = 0; i < dl->state.set_count; i++) { + if (dl->state.sets[i].pipeline_expected_format == 0) { + // Nothing expected by this pipeline. + continue; + } + + draw_graph.add_draw_list_uniform_set_prepare_for_use(dl->state.pipeline_shader_driver_id, dl->state.sets[i].uniform_set_driver_id, i); + } } + + // Bind descriptor sets. for (uint32_t i = 0; i < dl->state.set_count; i++) { if (dl->state.sets[i].pipeline_expected_format == 0) { continue; // Nothing expected by this pipeline. @@ -4167,13 +4177,13 @@ void RenderingDevice::compute_list_dispatch(ComputeListID p_list, uint32_t p_x_g #endif - // Bind descriptor sets. - +#ifdef DEBUG_ENABLED for (uint32_t i = 0; i < cl->state.set_count; i++) { if (cl->state.sets[i].pipeline_expected_format == 0) { - continue; // Nothing expected by this pipeline. + // Nothing expected by this pipeline. + continue; } -#ifdef DEBUG_ENABLED + if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { if (cl->state.sets[i].uniform_set_format == 0) { ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline."); @@ -4184,9 +4194,22 @@ void RenderingDevice::compute_list_dispatch(ComputeListID p_list, uint32_t p_x_g ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); } } + } #endif - draw_graph.add_compute_list_uniform_set_prepare_for_use(cl->state.pipeline_shader_driver_id, cl->state.sets[i].uniform_set_driver_id, i); + + // Prepare descriptor sets if the API doesn't use pipeline barriers. + if (!driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + for (uint32_t i = 0; i < cl->state.set_count; i++) { + if (cl->state.sets[i].pipeline_expected_format == 0) { + // Nothing expected by this pipeline. + continue; + } + + draw_graph.add_compute_list_uniform_set_prepare_for_use(cl->state.pipeline_shader_driver_id, cl->state.sets[i].uniform_set_driver_id, i); + } } + + // Bind descriptor sets. for (uint32_t i = 0; i < cl->state.set_count; i++) { if (cl->state.sets[i].pipeline_expected_format == 0) { continue; // Nothing expected by this pipeline. @@ -4261,16 +4284,16 @@ void RenderingDevice::compute_list_dispatch_indirect(ComputeListID p_list, RID p #endif - // Bind descriptor sets. - +#ifdef DEBUG_ENABLED for (uint32_t i = 0; i < cl->state.set_count; i++) { if (cl->state.sets[i].pipeline_expected_format == 0) { - continue; // Nothing expected by this pipeline. + // Nothing expected by this pipeline. + continue; } -#ifdef DEBUG_ENABLED + if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { if (cl->state.sets[i].uniform_set_format == 0) { - ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); + ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline."); } else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) { UniformSet *us = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); @@ -4278,9 +4301,22 @@ void RenderingDevice::compute_list_dispatch_indirect(ComputeListID p_list, RID p ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); } } + } #endif - draw_graph.add_compute_list_uniform_set_prepare_for_use(cl->state.pipeline_shader_driver_id, cl->state.sets[i].uniform_set_driver_id, i); + + // Prepare descriptor sets if the API doesn't use pipeline barriers. + if (!driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + for (uint32_t i = 0; i < cl->state.set_count; i++) { + if (cl->state.sets[i].pipeline_expected_format == 0) { + // Nothing expected by this pipeline. + continue; + } + + draw_graph.add_compute_list_uniform_set_prepare_for_use(cl->state.pipeline_shader_driver_id, cl->state.sets[i].uniform_set_driver_id, i); + } } + + // Bind descriptor sets. for (uint32_t i = 0; i < cl->state.set_count; i++) { if (cl->state.sets[i].pipeline_expected_format == 0) { continue; // Nothing expected by this pipeline. diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 020be6be18..42773fc347 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -797,6 +797,8 @@ private: #endif public: + RenderingContextDriver *get_context_driver() const { return context; } + const RDD::Capabilities &get_device_capabilities() const { return driver->get_capabilities(); } bool has_feature(const Features p_feature) const; diff --git a/servers/rendering/rendering_light_culler.h b/servers/rendering/rendering_light_culler.h index 0bf975430b..b0437d2310 100644 --- a/servers/rendering/rendering_light_culler.h +++ b/servers/rendering/rendering_light_culler.h @@ -181,14 +181,14 @@ private: } // Prevent divide by zero. - if (lc > 0.00001f) { + if (lc > 0.001f) { // If the summed length of the smaller two // sides is close to the length of the longest side, // the points are colinear, and the triangle is near degenerate. float ld = ((la + lb) - lc) / lc; // ld will be close to zero for colinear tris. - return ld < 0.00001f; + return ld < 0.001f; } // Don't create planes from tiny triangles, diff --git a/tests/create_test.py b/tests/create_test.py index deb53aca20..deb53aca20 100644..100755 --- a/tests/create_test.py +++ b/tests/create_test.py diff --git a/tests/python_build/test_gles3_builder.py b/tests/python_build/test_gles3_builder.py index 6f16139eb9..b34d33bde7 100644 --- a/tests/python_build/test_gles3_builder.py +++ b/tests/python_build/test_gles3_builder.py @@ -2,7 +2,7 @@ import json import pytest -from gles3_builders import build_gles3_header, GLES3HeaderStruct +from gles3_builders import GLES3HeaderStruct, build_gles3_header @pytest.mark.parametrize( diff --git a/tests/python_build/test_glsl_builder.py b/tests/python_build/test_glsl_builder.py index 348ef8441c..9f548855ff 100644 --- a/tests/python_build/test_glsl_builder.py +++ b/tests/python_build/test_glsl_builder.py @@ -2,7 +2,7 @@ import json import pytest -from glsl_builders import build_raw_header, RAWHeaderStruct, build_rd_header, RDHeaderStruct +from glsl_builders import RAWHeaderStruct, RDHeaderStruct, build_raw_header, build_rd_header @pytest.mark.parametrize( diff --git a/thirdparty/README.md b/thirdparty/README.md index b404dafc03..665c188272 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -517,7 +517,7 @@ Files extracted from upstream source: ## libwebp - Upstream: https://chromium.googlesource.com/webm/libwebp/ -- Version: 1.3.2 (ca332209cb5567c9b249c86788cb2dbf8847e760, 2023) +- Version: 1.4.0 (845d5476a866141ba35ac133f856fa62f0b7445f, 2024) - License: BSD-3-Clause Files extracted from upstream source: diff --git a/thirdparty/glslang/SPIRV/GlslangToSpv.cpp b/thirdparty/glslang/SPIRV/GlslangToSpv.cpp index ec40f663a7..ec40f663a7 100755..100644 --- a/thirdparty/glslang/SPIRV/GlslangToSpv.cpp +++ b/thirdparty/glslang/SPIRV/GlslangToSpv.cpp diff --git a/thirdparty/glslang/SPIRV/doc.cpp b/thirdparty/glslang/SPIRV/doc.cpp index 1a05c67360..1a05c67360 100755..100644 --- a/thirdparty/glslang/SPIRV/doc.cpp +++ b/thirdparty/glslang/SPIRV/doc.cpp diff --git a/thirdparty/glslang/glslang/Include/BaseTypes.h b/thirdparty/glslang/glslang/Include/BaseTypes.h index 64bffa8926..64bffa8926 100755..100644 --- a/thirdparty/glslang/glslang/Include/BaseTypes.h +++ b/thirdparty/glslang/glslang/Include/BaseTypes.h diff --git a/thirdparty/glslang/glslang/MachineIndependent/Initialize.cpp b/thirdparty/glslang/glslang/MachineIndependent/Initialize.cpp index af333f3f16..af333f3f16 100755..100644 --- a/thirdparty/glslang/glslang/MachineIndependent/Initialize.cpp +++ b/thirdparty/glslang/glslang/MachineIndependent/Initialize.cpp diff --git a/thirdparty/glslang/glslang/MachineIndependent/Versions.h b/thirdparty/glslang/glslang/MachineIndependent/Versions.h index 475cb89341..475cb89341 100755..100644 --- a/thirdparty/glslang/glslang/MachineIndependent/Versions.h +++ b/thirdparty/glslang/glslang/MachineIndependent/Versions.h diff --git a/thirdparty/libwebp/AUTHORS b/thirdparty/libwebp/AUTHORS index 8359b20da9..4cbe976608 100644 --- a/thirdparty/libwebp/AUTHORS +++ b/thirdparty/libwebp/AUTHORS @@ -2,6 +2,8 @@ Contributors: - Aidan O'Loan (aidanol at gmail dot com) - Alan Browning (browning at google dot com) - Alexandru Ardelean (ardeleanalex at gmail dot com) +- Anuraag Agrawal (anuraaga at gmail dot com) +- Arthur Eubanks (aeubanks at google dot com) - Brian Ledger (brianpl at google dot com) - Charles Munger (clm at google dot com) - Cheng Yi (cyi at google dot com) @@ -19,6 +21,8 @@ Contributors: - Jehan (jehan at girinstud dot io) - Jeremy Maitin-Shepard (jbms at google dot com) - Johann Koenig (johann dot koenig at duck dot com) +- Jonathan Grant (jgrantinfotech at gmail dot com) +- Jonliu1993 (13720414433 at 163 dot com) - Jovan Zelincevic (jovan dot zelincevic at imgtec dot com) - Jyrki Alakuijala (jyrki at google dot com) - Konstantin Ivlev (tomskside at gmail dot com) @@ -28,13 +32,16 @@ Contributors: - Marcin Kowalczyk (qrczak at google dot com) - Martin Olsson (mnemo at minimum dot se) - Maryla Ustarroz-Calonge (maryla at google dot com) +- Masahiro Hanada (hanada at atmark-techno dot com) - Mikołaj Zalewski (mikolajz at google dot com) - Mislav Bradac (mislavm at google dot com) +- natewood (natewood at fb dot com) - Nico Weber (thakis at chromium dot org) - Noel Chromium (noel at chromium dot org) - Nozomi Isozaki (nontan at pixiv dot co dot jp) - Oliver Wolff (oliver dot wolff at qt dot io) - Owen Rodley (orodley at google dot com) +- Ozkan Sezer (sezeroz at gmail dot com) - Parag Salasakar (img dot mips1 at gmail dot com) - Pascal Massimino (pascal dot massimino at gmail dot com) - Paweł Hajdan, Jr (phajdan dot jr at chromium dot org) diff --git a/thirdparty/libwebp/godot-node-debug-fix.patch b/thirdparty/libwebp/patches/godot-node-debug-fix.patch index 848664dccf..848664dccf 100644 --- a/thirdparty/libwebp/godot-node-debug-fix.patch +++ b/thirdparty/libwebp/patches/godot-node-debug-fix.patch diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv.c b/thirdparty/libwebp/sharpyuv/sharpyuv.c index a074564888..7cbf668fbb 100644 --- a/thirdparty/libwebp/sharpyuv/sharpyuv.c +++ b/thirdparty/libwebp/sharpyuv/sharpyuv.c @@ -75,41 +75,48 @@ static int RGBToGray(int64_t r, int64_t g, int64_t b) { } static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d, - int rgb_bit_depth) { + int rgb_bit_depth, + SharpYuvTransferFunctionType transfer_type) { const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth); - const uint32_t A = SharpYuvGammaToLinear(a, bit_depth); - const uint32_t B = SharpYuvGammaToLinear(b, bit_depth); - const uint32_t C = SharpYuvGammaToLinear(c, bit_depth); - const uint32_t D = SharpYuvGammaToLinear(d, bit_depth); - return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth); + const uint32_t A = SharpYuvGammaToLinear(a, bit_depth, transfer_type); + const uint32_t B = SharpYuvGammaToLinear(b, bit_depth, transfer_type); + const uint32_t C = SharpYuvGammaToLinear(c, bit_depth, transfer_type); + const uint32_t D = SharpYuvGammaToLinear(d, bit_depth, transfer_type); + return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth, + transfer_type); } static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w, - int rgb_bit_depth) { + int rgb_bit_depth, + SharpYuvTransferFunctionType transfer_type) { const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth); - int i; - for (i = 0; i < w; ++i) { - const uint32_t R = SharpYuvGammaToLinear(src[0 * w + i], bit_depth); - const uint32_t G = SharpYuvGammaToLinear(src[1 * w + i], bit_depth); - const uint32_t B = SharpYuvGammaToLinear(src[2 * w + i], bit_depth); + int i = 0; + do { + const uint32_t R = + SharpYuvGammaToLinear(src[0 * w + i], bit_depth, transfer_type); + const uint32_t G = + SharpYuvGammaToLinear(src[1 * w + i], bit_depth, transfer_type); + const uint32_t B = + SharpYuvGammaToLinear(src[2 * w + i], bit_depth, transfer_type); const uint32_t Y = RGBToGray(R, G, B); - dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth); - } + dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth, transfer_type); + } while (++i < w); } static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2, - fixed_t* dst, int uv_w, int rgb_bit_depth) { - int i; - for (i = 0; i < uv_w; ++i) { + fixed_t* dst, int uv_w, int rgb_bit_depth, + SharpYuvTransferFunctionType transfer_type) { + int i = 0; + do { const int r = ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1], src2[0 * uv_w + 0], - src2[0 * uv_w + 1], rgb_bit_depth); + src2[0 * uv_w + 1], rgb_bit_depth, transfer_type); const int g = ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], src2[2 * uv_w + 0], - src2[2 * uv_w + 1], rgb_bit_depth); + src2[2 * uv_w + 1], rgb_bit_depth, transfer_type); const int b = ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], src2[4 * uv_w + 0], - src2[4 * uv_w + 1], rgb_bit_depth); + src2[4 * uv_w + 1], rgb_bit_depth, transfer_type); const int W = RGBToGray(r, g, b); dst[0 * uv_w] = (fixed_t)(r - W); dst[1 * uv_w] = (fixed_t)(g - W); @@ -117,15 +124,15 @@ static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2, dst += 1; src1 += 2; src2 += 2; - } + } while (++i < uv_w); } static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) { - int i; + int i = 0; assert(w > 0); - for (i = 0; i < w; ++i) { + do { y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]); - } + } while (++i < w); } //------------------------------------------------------------------------------ @@ -151,9 +158,9 @@ static void ImportOneRow(const uint8_t* const r_ptr, // Convert the rgb_step from a number of bytes to a number of uint8_t or // uint16_t values depending the bit depth. const int step = (rgb_bit_depth > 8) ? rgb_step / 2 : rgb_step; - int i; + int i = 0; const int w = (pic_width + 1) & ~1; - for (i = 0; i < pic_width; ++i) { + do { const int off = i * step; const int shift = GetPrecisionShift(rgb_bit_depth); if (rgb_bit_depth == 8) { @@ -165,7 +172,7 @@ static void ImportOneRow(const uint8_t* const r_ptr, dst[i + 1 * w] = Shift(((uint16_t*)g_ptr)[off], shift); dst[i + 2 * w] = Shift(((uint16_t*)b_ptr)[off], shift); } - } + } while (++i < pic_width); if (pic_width & 1) { // replicate rightmost pixel dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1]; dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1]; @@ -233,8 +240,11 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv, const int sfix = GetPrecisionShift(rgb_bit_depth); const int yuv_max = (1 << yuv_bit_depth) - 1; - for (best_uv = best_uv_base, j = 0; j < height; ++j) { - for (i = 0; i < width; ++i) { + best_uv = best_uv_base; + j = 0; + do { + i = 0; + do { const int off = (i >> 1); const int W = best_y[i]; const int r = best_uv[off + 0 * uv_w] + W; @@ -246,19 +256,22 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv, } else { ((uint16_t*)y_ptr)[i] = clip(y, yuv_max); } - } + } while (++i < width); best_y += w; best_uv += (j & 1) * 3 * uv_w; y_ptr += y_stride; - } - for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) { - for (i = 0; i < uv_w; ++i) { - const int off = i; + } while (++j < height); + + best_uv = best_uv_base; + j = 0; + do { + i = 0; + do { // Note r, g and b values here are off by W, but a constant offset on all // 3 components doesn't change the value of u and v with a YCbCr matrix. - const int r = best_uv[off + 0 * uv_w]; - const int g = best_uv[off + 1 * uv_w]; - const int b = best_uv[off + 2 * uv_w]; + const int r = best_uv[i + 0 * uv_w]; + const int g = best_uv[i + 1 * uv_w]; + const int b = best_uv[i + 2 * uv_w]; const int u = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_u, sfix); const int v = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_v, sfix); if (yuv_bit_depth <= 8) { @@ -268,11 +281,11 @@ static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv, ((uint16_t*)u_ptr)[i] = clip(u, yuv_max); ((uint16_t*)v_ptr)[i] = clip(v, yuv_max); } - } + } while (++i < uv_w); best_uv += 3 * uv_w; u_ptr += u_stride; v_ptr += v_stride; - } + } while (++j < uv_h); return 1; } @@ -285,7 +298,7 @@ static void* SafeMalloc(uint64_t nmemb, size_t size) { return malloc((size_t)total_size); } -#define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((W) * (H), sizeof(T))) +#define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((uint64_t)(W) * (H), sizeof(T))) static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr, const uint8_t* b_ptr, int rgb_step, int rgb_stride, @@ -293,12 +306,14 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr, uint8_t* u_ptr, int u_stride, uint8_t* v_ptr, int v_stride, int yuv_bit_depth, int width, int height, - const SharpYuvConversionMatrix* yuv_matrix) { + const SharpYuvConversionMatrix* yuv_matrix, + SharpYuvTransferFunctionType transfer_type) { // we expand the right/bottom border if needed const int w = (width + 1) & ~1; const int h = (height + 1) & ~1; const int uv_w = w >> 1; const int uv_h = h >> 1; + const int y_bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth); uint64_t prev_diff_y_sum = ~0; int j, iter; @@ -346,9 +361,9 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr, StoreGray(src1, best_y + 0, w); StoreGray(src2, best_y + w, w); - UpdateW(src1, target_y, w, rgb_bit_depth); - UpdateW(src2, target_y + w, w, rgb_bit_depth); - UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth); + UpdateW(src1, target_y, w, rgb_bit_depth, transfer_type); + UpdateW(src2, target_y + w, w, rgb_bit_depth, transfer_type); + UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth, transfer_type); memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv)); best_y += 2 * w; best_uv += 3 * uv_w; @@ -369,7 +384,8 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr, best_uv = best_uv_base; target_y = target_y_base; target_uv = target_uv_base; - for (j = 0; j < h; j += 2) { + j = 0; + do { fixed_y_t* const src1 = tmp_buffer + 0 * w; fixed_y_t* const src2 = tmp_buffer + 3 * w; { @@ -380,21 +396,21 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr, cur_uv = next_uv; } - UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth); - UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth); - UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth); + UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth, transfer_type); + UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth, transfer_type); + UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth, transfer_type); // update two rows of Y and one row of RGB diff_y_sum += - SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w, - rgb_bit_depth + GetPrecisionShift(rgb_bit_depth)); + SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w, y_bit_depth); SharpYuvUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w); best_y += 2 * w; best_uv += 3 * uv_w; target_y += 2 * w; target_uv += 3 * uv_w; - } + j += 2; + } while (j < h); // test exit condition if (iter > 0) { if (diff_y_sum < diff_y_threshold) break; @@ -418,6 +434,7 @@ static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr, free(tmp_buffer); return ok; } + #undef SAFE_ALLOC #if defined(WEBP_USE_THREAD) && !defined(_WIN32) @@ -462,12 +479,42 @@ void SharpYuvInit(VP8CPUInfo cpu_info_func) { UNLOCK_ACCESS_AND_RETURN; } -int SharpYuvConvert(const void* r_ptr, const void* g_ptr, - const void* b_ptr, int rgb_step, int rgb_stride, - int rgb_bit_depth, void* y_ptr, int y_stride, - void* u_ptr, int u_stride, void* v_ptr, - int v_stride, int yuv_bit_depth, int width, +int SharpYuvConvert(const void* r_ptr, const void* g_ptr, const void* b_ptr, + int rgb_step, int rgb_stride, int rgb_bit_depth, + void* y_ptr, int y_stride, void* u_ptr, int u_stride, + void* v_ptr, int v_stride, int yuv_bit_depth, int width, int height, const SharpYuvConversionMatrix* yuv_matrix) { + SharpYuvOptions options; + options.yuv_matrix = yuv_matrix; + options.transfer_type = kSharpYuvTransferFunctionSrgb; + return SharpYuvConvertWithOptions( + r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride, rgb_bit_depth, y_ptr, y_stride, + u_ptr, u_stride, v_ptr, v_stride, yuv_bit_depth, width, height, &options); +} + +int SharpYuvOptionsInitInternal(const SharpYuvConversionMatrix* yuv_matrix, + SharpYuvOptions* options, int version) { + const int major = (version >> 24); + const int minor = (version >> 16) & 0xff; + if (options == NULL || yuv_matrix == NULL || + (major == SHARPYUV_VERSION_MAJOR && major == 0 && + minor != SHARPYUV_VERSION_MINOR) || + (major != SHARPYUV_VERSION_MAJOR)) { + return 0; + } + options->yuv_matrix = yuv_matrix; + options->transfer_type = kSharpYuvTransferFunctionSrgb; + return 1; +} + +int SharpYuvConvertWithOptions(const void* r_ptr, const void* g_ptr, + const void* b_ptr, int rgb_step, int rgb_stride, + int rgb_bit_depth, void* y_ptr, int y_stride, + void* u_ptr, int u_stride, void* v_ptr, + int v_stride, int yuv_bit_depth, int width, + int height, const SharpYuvOptions* options) { + const SharpYuvConversionMatrix* yuv_matrix = options->yuv_matrix; + SharpYuvTransferFunctionType transfer_type = options->transfer_type; SharpYuvConversionMatrix scaled_matrix; const int rgb_max = (1 << rgb_bit_depth) - 1; const int rgb_round = 1 << (rgb_bit_depth - 1); @@ -486,7 +533,7 @@ int SharpYuvConvert(const void* r_ptr, const void* g_ptr, if (yuv_bit_depth != 8 && yuv_bit_depth != 10 && yuv_bit_depth != 12) { return 0; } - if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride %2 != 0)) { + if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride % 2 != 0)) { // Step/stride should be even for uint16_t buffers. return 0; } @@ -521,7 +568,7 @@ int SharpYuvConvert(const void* r_ptr, const void* g_ptr, return DoSharpArgbToYuv(r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride, rgb_bit_depth, y_ptr, y_stride, u_ptr, u_stride, v_ptr, v_stride, yuv_bit_depth, width, height, - &scaled_matrix); + &scaled_matrix, transfer_type); } //------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv.h b/thirdparty/libwebp/sharpyuv/sharpyuv.h index 7b9904d6f9..fe95891599 100644 --- a/thirdparty/libwebp/sharpyuv/sharpyuv.h +++ b/thirdparty/libwebp/sharpyuv/sharpyuv.h @@ -22,22 +22,37 @@ extern "C" { #else // This explicitly marks library functions and allows for changing the // signature for e.g., Windows DLL builds. -#if defined(__GNUC__) && __GNUC__ >= 4 -#define SHARPYUV_EXTERN extern __attribute__((visibility("default"))) -#else -#if defined(_MSC_VER) && defined(WEBP_DLL) +#if defined(_WIN32) && defined(WEBP_DLL) #define SHARPYUV_EXTERN __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define SHARPYUV_EXTERN extern __attribute__((visibility("default"))) #else #define SHARPYUV_EXTERN extern -#endif /* _MSC_VER && WEBP_DLL */ -#endif /* __GNUC__ >= 4 */ +#endif /* defined(_WIN32) && defined(WEBP_DLL) */ #endif /* WEBP_EXTERN */ #endif /* SHARPYUV_EXTERN */ +#ifndef SHARPYUV_INLINE +#ifdef WEBP_INLINE +#define SHARPYUV_INLINE WEBP_INLINE +#else +#ifndef _MSC_VER +#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \ + (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) +#define SHARPYUV_INLINE inline +#else +#define SHARPYUV_INLINE +#endif +#else +#define SHARPYUV_INLINE __forceinline +#endif /* _MSC_VER */ +#endif /* WEBP_INLINE */ +#endif /* SHARPYUV_INLINE */ + // SharpYUV API version following the convention from semver.org #define SHARPYUV_VERSION_MAJOR 0 -#define SHARPYUV_VERSION_MINOR 2 -#define SHARPYUV_VERSION_PATCH 1 +#define SHARPYUV_VERSION_MINOR 4 +#define SHARPYUV_VERSION_PATCH 0 // Version as a uint32_t. The major number is the high 8 bits. // The minor number is the middle 8 bits. The patch number is the low 16 bits. #define SHARPYUV_MAKE_VERSION(MAJOR, MINOR, PATCH) \ @@ -61,6 +76,33 @@ typedef struct { int rgb_to_v[4]; } SharpYuvConversionMatrix; +typedef struct SharpYuvOptions SharpYuvOptions; + +// Enums for transfer functions, as defined in H.273, +// https://www.itu.int/rec/T-REC-H.273-202107-I/en +typedef enum SharpYuvTransferFunctionType { + // 0 is reserved + kSharpYuvTransferFunctionBt709 = 1, + // 2 is unspecified + // 3 is reserved + kSharpYuvTransferFunctionBt470M = 4, + kSharpYuvTransferFunctionBt470Bg = 5, + kSharpYuvTransferFunctionBt601 = 6, + kSharpYuvTransferFunctionSmpte240 = 7, + kSharpYuvTransferFunctionLinear = 8, + kSharpYuvTransferFunctionLog100 = 9, + kSharpYuvTransferFunctionLog100_Sqrt10 = 10, + kSharpYuvTransferFunctionIec61966 = 11, + kSharpYuvTransferFunctionBt1361 = 12, + kSharpYuvTransferFunctionSrgb = 13, + kSharpYuvTransferFunctionBt2020_10Bit = 14, + kSharpYuvTransferFunctionBt2020_12Bit = 15, + kSharpYuvTransferFunctionSmpte2084 = 16, // PQ + kSharpYuvTransferFunctionSmpte428 = 17, + kSharpYuvTransferFunctionHlg = 18, + kSharpYuvTransferFunctionNum +} SharpYuvTransferFunctionType; + // Converts RGB to YUV420 using a downsampling algorithm that minimizes // artefacts caused by chroma subsampling. // This is slower than standard downsampling (averaging of 4 UV values). @@ -85,6 +127,8 @@ typedef struct { // adjacent pixels on the y, u and v channels. If yuv_bit_depth > 8, they // should be multiples of 2. // width, height: width and height of the image in pixels +// This function calls SharpYuvConvertWithOptions with a default transfer +// function of kSharpYuvTransferFunctionSrgb. SHARPYUV_EXTERN int SharpYuvConvert(const void* r_ptr, const void* g_ptr, const void* b_ptr, int rgb_step, int rgb_stride, int rgb_bit_depth, @@ -93,6 +137,31 @@ SHARPYUV_EXTERN int SharpYuvConvert(const void* r_ptr, const void* g_ptr, int yuv_bit_depth, int width, int height, const SharpYuvConversionMatrix* yuv_matrix); +struct SharpYuvOptions { + // This matrix cannot be NULL and can be initialized by + // SharpYuvComputeConversionMatrix. + const SharpYuvConversionMatrix* yuv_matrix; + SharpYuvTransferFunctionType transfer_type; +}; + +// Internal, version-checked, entry point +SHARPYUV_EXTERN int SharpYuvOptionsInitInternal(const SharpYuvConversionMatrix*, + SharpYuvOptions*, int); + +// Should always be called, to initialize a fresh SharpYuvOptions +// structure before modification. SharpYuvOptionsInit() must have succeeded +// before using the 'options' object. +static SHARPYUV_INLINE int SharpYuvOptionsInit( + const SharpYuvConversionMatrix* yuv_matrix, SharpYuvOptions* options) { + return SharpYuvOptionsInitInternal(yuv_matrix, options, SHARPYUV_VERSION); +} + +SHARPYUV_EXTERN int SharpYuvConvertWithOptions( + const void* r_ptr, const void* g_ptr, const void* b_ptr, int rgb_step, + int rgb_stride, int rgb_bit_depth, void* y_ptr, int y_stride, void* u_ptr, + int u_stride, void* v_ptr, int v_stride, int yuv_bit_depth, int width, + int height, const SharpYuvOptions* options); + // TODO(b/194336375): Add YUV444 to YUV420 conversion. Maybe also add 422 // support (it's rarely used in practice, especially for images). diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c b/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c index 0da3efc0b8..94a40ec686 100644 --- a/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c +++ b/thirdparty/libwebp/sharpyuv/sharpyuv_dsp.c @@ -17,6 +17,7 @@ #include <stdlib.h> #include "sharpyuv/sharpyuv_cpu.h" +#include "src/webp/types.h" //----------------------------------------------------------------------------- @@ -69,8 +70,7 @@ uint64_t (*SharpYuvUpdateY)(const uint16_t* src, const uint16_t* ref, void (*SharpYuvUpdateRGB)(const int16_t* src, const int16_t* ref, int16_t* dst, int len); void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len, - const uint16_t* best_y, uint16_t* out, - int bit_depth); + const uint16_t* best_y, uint16_t* out, int bit_depth); extern VP8CPUInfo SharpYuvGetCPUInfo; extern void InitSharpYuvSSE2(void); diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv_gamma.c b/thirdparty/libwebp/sharpyuv/sharpyuv_gamma.c index 20ab2da6bc..09028428ac 100644 --- a/thirdparty/libwebp/sharpyuv/sharpyuv_gamma.c +++ b/thirdparty/libwebp/sharpyuv/sharpyuv_gamma.c @@ -12,6 +12,7 @@ #include "sharpyuv/sharpyuv_gamma.h" #include <assert.h> +#include <float.h> #include <math.h> #include "src/webp/types.h" @@ -97,7 +98,7 @@ static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab, return result; } -uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth) { +static uint32_t ToLinearSrgb(uint16_t v, int bit_depth) { const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth; if (shift > 0) { return kGammaToLinearTabS[v << shift]; @@ -105,9 +106,314 @@ uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth) { return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0); } -uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth) { +static uint16_t FromLinearSrgb(uint32_t value, int bit_depth) { return FixedPointInterpolation( value, kLinearToGammaTabS, (GAMMA_TO_LINEAR_BITS - LINEAR_TO_GAMMA_TAB_BITS), bit_depth - GAMMA_TO_LINEAR_BITS); } + +//////////////////////////////////////////////////////////////////////////////// + +#define CLAMP(x, low, high) \ + (((x) < (low)) ? (low) : (((high) < (x)) ? (high) : (x))) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +static WEBP_INLINE float Roundf(float x) { + if (x < 0) + return (float)ceil((double)(x - 0.5f)); + else + return (float)floor((double)(x + 0.5f)); +} + +static WEBP_INLINE float Powf(float base, float exp) { + return (float)pow((double)base, (double)exp); +} + +static WEBP_INLINE float Log10f(float x) { return (float)log10((double)x); } + +static float ToLinear709(float gamma) { + if (gamma < 0.f) { + return 0.f; + } else if (gamma < 4.5f * 0.018053968510807f) { + return gamma / 4.5f; + } else if (gamma < 1.f) { + return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f); + } + return 1.f; +} + +static float FromLinear709(float linear) { + if (linear < 0.f) { + return 0.f; + } else if (linear < 0.018053968510807f) { + return linear * 4.5f; + } else if (linear < 1.f) { + return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f; + } + return 1.f; +} + +static float ToLinear470M(float gamma) { + return Powf(CLAMP(gamma, 0.f, 1.f), 2.2f); +} + +static float FromLinear470M(float linear) { + return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.2f); +} + +static float ToLinear470Bg(float gamma) { + return Powf(CLAMP(gamma, 0.f, 1.f), 2.8f); +} + +static float FromLinear470Bg(float linear) { + return Powf(CLAMP(linear, 0.f, 1.f), 1.f / 2.8f); +} + +static float ToLinearSmpte240(float gamma) { + if (gamma < 0.f) { + return 0.f; + } else if (gamma < 4.f * 0.022821585529445f) { + return gamma / 4.f; + } else if (gamma < 1.f) { + return Powf((gamma + 0.111572195921731f) / 1.111572195921731f, 1.f / 0.45f); + } + return 1.f; +} + +static float FromLinearSmpte240(float linear) { + if (linear < 0.f) { + return 0.f; + } else if (linear < 0.022821585529445f) { + return linear * 4.f; + } else if (linear < 1.f) { + return 1.111572195921731f * Powf(linear, 0.45f) - 0.111572195921731f; + } + return 1.f; +} + +static float ToLinearLog100(float gamma) { + // The function is non-bijective so choose the middle of [0, 0.01]. + const float mid_interval = 0.01f / 2.f; + return (gamma <= 0.0f) ? mid_interval + : Powf(10.0f, 2.f * (MIN(gamma, 1.f) - 1.0f)); +} + +static float FromLinearLog100(float linear) { + return (linear < 0.01f) ? 0.0f : 1.0f + Log10f(MIN(linear, 1.f)) / 2.0f; +} + +static float ToLinearLog100Sqrt10(float gamma) { + // The function is non-bijective so choose the middle of [0, 0.00316227766f[. + const float mid_interval = 0.00316227766f / 2.f; + return (gamma <= 0.0f) ? mid_interval + : Powf(10.0f, 2.5f * (MIN(gamma, 1.f) - 1.0f)); +} + +static float FromLinearLog100Sqrt10(float linear) { + return (linear < 0.00316227766f) ? 0.0f + : 1.0f + Log10f(MIN(linear, 1.f)) / 2.5f; +} + +static float ToLinearIec61966(float gamma) { + if (gamma <= -4.5f * 0.018053968510807f) { + return Powf((-gamma + 0.09929682680944f) / -1.09929682680944f, 1.f / 0.45f); + } else if (gamma < 4.5f * 0.018053968510807f) { + return gamma / 4.5f; + } + return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f); +} + +static float FromLinearIec61966(float linear) { + if (linear <= -0.018053968510807f) { + return -1.09929682680944f * Powf(-linear, 0.45f) + 0.09929682680944f; + } else if (linear < 0.018053968510807f) { + return linear * 4.5f; + } + return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f; +} + +static float ToLinearBt1361(float gamma) { + if (gamma < -0.25f) { + return -0.25f; + } else if (gamma < 0.f) { + return Powf((gamma - 0.02482420670236f) / -0.27482420670236f, 1.f / 0.45f) / + -4.f; + } else if (gamma < 4.5f * 0.018053968510807f) { + return gamma / 4.5f; + } else if (gamma < 1.f) { + return Powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.f / 0.45f); + } + return 1.f; +} + +static float FromLinearBt1361(float linear) { + if (linear < -0.25f) { + return -0.25f; + } else if (linear < 0.f) { + return -0.27482420670236f * Powf(-4.f * linear, 0.45f) + 0.02482420670236f; + } else if (linear < 0.018053968510807f) { + return linear * 4.5f; + } else if (linear < 1.f) { + return 1.09929682680944f * Powf(linear, 0.45f) - 0.09929682680944f; + } + return 1.f; +} + +static float ToLinearPq(float gamma) { + if (gamma > 0.f) { + const float pow_gamma = Powf(gamma, 32.f / 2523.f); + const float num = MAX(pow_gamma - 107.f / 128.f, 0.0f); + const float den = MAX(2413.f / 128.f - 2392.f / 128.f * pow_gamma, FLT_MIN); + return Powf(num / den, 4096.f / 653.f); + } + return 0.f; +} + +static float FromLinearPq(float linear) { + if (linear > 0.f) { + const float pow_linear = Powf(linear, 653.f / 4096.f); + const float num = 107.f / 128.f + 2413.f / 128.f * pow_linear; + const float den = 1.0f + 2392.f / 128.f * pow_linear; + return Powf(num / den, 2523.f / 32.f); + } + return 0.f; +} + +static float ToLinearSmpte428(float gamma) { + return Powf(MAX(gamma, 0.f), 2.6f) / 0.91655527974030934f; +} + +static float FromLinearSmpte428(float linear) { + return Powf(0.91655527974030934f * MAX(linear, 0.f), 1.f / 2.6f); +} + +// Conversion in BT.2100 requires RGB info. Simplify to gamma correction here. +static float ToLinearHlg(float gamma) { + if (gamma < 0.f) { + return 0.f; + } else if (gamma <= 0.5f) { + return Powf((gamma * gamma) * (1.f / 3.f), 1.2f); + } + return Powf((expf((gamma - 0.55991073f) / 0.17883277f) + 0.28466892f) / 12.0f, + 1.2f); +} + +static float FromLinearHlg(float linear) { + linear = Powf(linear, 1.f / 1.2f); + if (linear < 0.f) { + return 0.f; + } else if (linear <= (1.f / 12.f)) { + return sqrtf(3.f * linear); + } + return 0.17883277f * logf(12.f * linear - 0.28466892f) + 0.55991073f; +} + +uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth, + SharpYuvTransferFunctionType transfer_type) { + float v_float, linear; + if (transfer_type == kSharpYuvTransferFunctionSrgb) { + return ToLinearSrgb(v, bit_depth); + } + v_float = (float)v / ((1 << bit_depth) - 1); + switch (transfer_type) { + case kSharpYuvTransferFunctionBt709: + case kSharpYuvTransferFunctionBt601: + case kSharpYuvTransferFunctionBt2020_10Bit: + case kSharpYuvTransferFunctionBt2020_12Bit: + linear = ToLinear709(v_float); + break; + case kSharpYuvTransferFunctionBt470M: + linear = ToLinear470M(v_float); + break; + case kSharpYuvTransferFunctionBt470Bg: + linear = ToLinear470Bg(v_float); + break; + case kSharpYuvTransferFunctionSmpte240: + linear = ToLinearSmpte240(v_float); + break; + case kSharpYuvTransferFunctionLinear: + return v; + case kSharpYuvTransferFunctionLog100: + linear = ToLinearLog100(v_float); + break; + case kSharpYuvTransferFunctionLog100_Sqrt10: + linear = ToLinearLog100Sqrt10(v_float); + break; + case kSharpYuvTransferFunctionIec61966: + linear = ToLinearIec61966(v_float); + break; + case kSharpYuvTransferFunctionBt1361: + linear = ToLinearBt1361(v_float); + break; + case kSharpYuvTransferFunctionSmpte2084: + linear = ToLinearPq(v_float); + break; + case kSharpYuvTransferFunctionSmpte428: + linear = ToLinearSmpte428(v_float); + break; + case kSharpYuvTransferFunctionHlg: + linear = ToLinearHlg(v_float); + break; + default: + assert(0); + linear = 0; + break; + } + return (uint32_t)Roundf(linear * ((1 << 16) - 1)); +} + +uint16_t SharpYuvLinearToGamma(uint32_t v, int bit_depth, + SharpYuvTransferFunctionType transfer_type) { + float v_float, linear; + if (transfer_type == kSharpYuvTransferFunctionSrgb) { + return FromLinearSrgb(v, bit_depth); + } + v_float = (float)v / ((1 << 16) - 1); + switch (transfer_type) { + case kSharpYuvTransferFunctionBt709: + case kSharpYuvTransferFunctionBt601: + case kSharpYuvTransferFunctionBt2020_10Bit: + case kSharpYuvTransferFunctionBt2020_12Bit: + linear = FromLinear709(v_float); + break; + case kSharpYuvTransferFunctionBt470M: + linear = FromLinear470M(v_float); + break; + case kSharpYuvTransferFunctionBt470Bg: + linear = FromLinear470Bg(v_float); + break; + case kSharpYuvTransferFunctionSmpte240: + linear = FromLinearSmpte240(v_float); + break; + case kSharpYuvTransferFunctionLinear: + return v; + case kSharpYuvTransferFunctionLog100: + linear = FromLinearLog100(v_float); + break; + case kSharpYuvTransferFunctionLog100_Sqrt10: + linear = FromLinearLog100Sqrt10(v_float); + break; + case kSharpYuvTransferFunctionIec61966: + linear = FromLinearIec61966(v_float); + break; + case kSharpYuvTransferFunctionBt1361: + linear = FromLinearBt1361(v_float); + break; + case kSharpYuvTransferFunctionSmpte2084: + linear = FromLinearPq(v_float); + break; + case kSharpYuvTransferFunctionSmpte428: + linear = FromLinearSmpte428(v_float); + break; + case kSharpYuvTransferFunctionHlg: + linear = FromLinearHlg(v_float); + break; + default: + assert(0); + linear = 0; + break; + } + return (uint16_t)Roundf(linear * ((1 << bit_depth) - 1)); +} diff --git a/thirdparty/libwebp/sharpyuv/sharpyuv_gamma.h b/thirdparty/libwebp/sharpyuv/sharpyuv_gamma.h index d13aff59e1..b8ba7e9870 100644 --- a/thirdparty/libwebp/sharpyuv/sharpyuv_gamma.h +++ b/thirdparty/libwebp/sharpyuv/sharpyuv_gamma.h @@ -12,6 +12,7 @@ #ifndef WEBP_SHARPYUV_SHARPYUV_GAMMA_H_ #define WEBP_SHARPYUV_SHARPYUV_GAMMA_H_ +#include "sharpyuv/sharpyuv.h" #include "src/webp/types.h" #ifdef __cplusplus @@ -22,11 +23,13 @@ extern "C" { // SharpYuvGammaToLinear or SharpYuvLinearToGamma. void SharpYuvInitGammaTables(void); -// Converts a gamma color value on 'bit_depth' bits to a 16 bit linear value. -uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth); +// Converts a 'bit_depth'-bit gamma color value to a 16-bit linear value. +uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth, + SharpYuvTransferFunctionType transfer_type); -// Converts a 16 bit linear color value to a gamma value on 'bit_depth' bits. -uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth); +// Converts a 16-bit linear color value to a 'bit_depth'-bit gamma value. +uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth, + SharpYuvTransferFunctionType transfer_type); #ifdef __cplusplus } // extern "C" diff --git a/thirdparty/libwebp/src/dec/alpha_dec.c b/thirdparty/libwebp/src/dec/alpha_dec.c index 0b93a30b32..b6c874fb84 100644 --- a/thirdparty/libwebp/src/dec/alpha_dec.c +++ b/thirdparty/libwebp/src/dec/alpha_dec.c @@ -13,18 +13,20 @@ #include <stdlib.h> #include "src/dec/alphai_dec.h" +#include "src/dec/vp8_dec.h" #include "src/dec/vp8i_dec.h" #include "src/dec/vp8li_dec.h" #include "src/dsp/dsp.h" #include "src/utils/quant_levels_dec_utils.h" #include "src/utils/utils.h" #include "src/webp/format_constants.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // ALPHDecoder object. // Allocates a new alpha decoder instance. -static ALPHDecoder* ALPHNew(void) { +WEBP_NODISCARD static ALPHDecoder* ALPHNew(void) { ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); return dec; } @@ -45,9 +47,9 @@ static void ALPHDelete(ALPHDecoder* const dec) { // header for alpha data stored using lossless compression. // Returns false in case of error in alpha header (data too short, invalid // compression method or filter, error in lossless header data etc). -static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, - size_t data_size, const VP8Io* const src_io, - uint8_t* output) { +WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, + size_t data_size, const VP8Io* const src_io, + uint8_t* output) { int ok = 0; const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN; const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN; @@ -79,7 +81,9 @@ static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, } // Copy the necessary parameters from src_io to io - VP8InitIo(io); + if (!VP8InitIo(io)) { + return 0; + } WebPInitCustomIo(NULL, io); io->opaque = dec; io->width = src_io->width; @@ -107,7 +111,8 @@ static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, // starting from row number 'row'. It assumes that rows up to (row - 1) have // already been decoded. // Returns false in case of bitstream error. -static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) { +WEBP_NODISCARD static int ALPHDecode(VP8Decoder* const dec, int row, + int num_rows) { ALPHDecoder* const alph_dec = dec->alph_dec_; const int width = alph_dec->width_; const int height = alph_dec->io_.crop_bottom; @@ -117,21 +122,12 @@ static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) { const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width; uint8_t* dst = dec->alpha_plane_ + row * width; assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]); - if (alph_dec->filter_ != WEBP_FILTER_NONE) { - assert(WebPUnfilters[alph_dec->filter_] != NULL); - for (y = 0; y < num_rows; ++y) { - WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width); - prev_line = dst; - dst += width; - deltas += width; - } - } else { - for (y = 0; y < num_rows; ++y) { - memcpy(dst, deltas, width * sizeof(*dst)); - prev_line = dst; - dst += width; - deltas += width; - } + assert(WebPUnfilters[alph_dec->filter_] != NULL); + for (y = 0; y < num_rows; ++y) { + WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width); + prev_line = dst; + dst += width; + deltas += width; } dec->alpha_prev_line_ = prev_line; } else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION @@ -147,7 +143,8 @@ static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) { return 1; } -static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) { +WEBP_NODISCARD static int AllocateAlphaPlane(VP8Decoder* const dec, + const VP8Io* const io) { const int stride = io->width; const int height = io->crop_bottom; const uint64_t alpha_size = (uint64_t)stride * height; @@ -155,7 +152,8 @@ static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) { dec->alpha_plane_mem_ = (uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane_)); if (dec->alpha_plane_mem_ == NULL) { - return 0; + return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, + "Alpha decoder initialization failed."); } dec->alpha_plane_ = dec->alpha_plane_mem_; dec->alpha_prev_line_ = NULL; @@ -174,9 +172,9 @@ void WebPDeallocateAlphaMemory(VP8Decoder* const dec) { //------------------------------------------------------------------------------ // Main entry point. -const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, - const VP8Io* const io, - int row, int num_rows) { +WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, + const VP8Io* const io, + int row, int num_rows) { const int width = io->width; const int height = io->crop_bottom; @@ -189,10 +187,19 @@ const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, if (!dec->is_alpha_decoded_) { if (dec->alph_dec_ == NULL) { // Initialize decoder. dec->alph_dec_ = ALPHNew(); - if (dec->alph_dec_ == NULL) return NULL; + if (dec->alph_dec_ == NULL) { + VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, + "Alpha decoder initialization failed."); + return NULL; + } if (!AllocateAlphaPlane(dec, io)) goto Error; if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_, io, dec->alpha_plane_)) { + VP8LDecoder* const vp8l_dec = dec->alph_dec_->vp8l_dec_; + VP8SetError(dec, + (vp8l_dec == NULL) ? VP8_STATUS_OUT_OF_MEMORY + : vp8l_dec->status_, + "Alpha decoder initialization failed."); goto Error; } // if we allowed use of alpha dithering, check whether it's needed at all diff --git a/thirdparty/libwebp/src/dec/buffer_dec.c b/thirdparty/libwebp/src/dec/buffer_dec.c index 4786cf0ddb..11ce76f19e 100644 --- a/thirdparty/libwebp/src/dec/buffer_dec.c +++ b/thirdparty/libwebp/src/dec/buffer_dec.c @@ -75,7 +75,7 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) { const WebPRGBABuffer* const buf = &buffer->u.RGBA; const int stride = abs(buf->stride); const uint64_t size = - MIN_BUFFER_SIZE(width * kModeBpp[mode], height, stride); + MIN_BUFFER_SIZE((uint64_t)width * kModeBpp[mode], height, stride); ok &= (size <= buf->size); ok &= (stride >= width * kModeBpp[mode]); ok &= (buf->rgba != NULL); diff --git a/thirdparty/libwebp/src/dec/idec_dec.c b/thirdparty/libwebp/src/dec/idec_dec.c index 9035df5659..ad042a1ffc 100644 --- a/thirdparty/libwebp/src/dec/idec_dec.c +++ b/thirdparty/libwebp/src/dec/idec_dec.c @@ -17,8 +17,10 @@ #include "src/dec/alphai_dec.h" #include "src/dec/webpi_dec.h" +#include "src/dec/vp8_dec.h" #include "src/dec/vp8i_dec.h" #include "src/utils/utils.h" +#include "src/webp/decode.h" // In append mode, buffer allocations increase as multiples of this value. // Needs to be a power of 2. @@ -161,8 +163,9 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) { // Appends data to the end of MemBuffer->buf_. It expands the allocated memory // size if required and also updates VP8BitReader's if new memory is allocated. -static int AppendToMemBuffer(WebPIDecoder* const idec, - const uint8_t* const data, size_t data_size) { +WEBP_NODISCARD static int AppendToMemBuffer(WebPIDecoder* const idec, + const uint8_t* const data, + size_t data_size) { VP8Decoder* const dec = (VP8Decoder*)idec->dec_; MemBuffer* const mem = &idec->mem_; const int need_compressed_alpha = NeedCompressedAlpha(idec); @@ -203,8 +206,9 @@ static int AppendToMemBuffer(WebPIDecoder* const idec, return 1; } -static int RemapMemBuffer(WebPIDecoder* const idec, - const uint8_t* const data, size_t data_size) { +WEBP_NODISCARD static int RemapMemBuffer(WebPIDecoder* const idec, + const uint8_t* const data, + size_t data_size) { MemBuffer* const mem = &idec->mem_; const uint8_t* const old_buf = mem->buf_; const uint8_t* const old_start = @@ -237,7 +241,8 @@ static void ClearMemBuffer(MemBuffer* const mem) { } } -static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) { +WEBP_NODISCARD static int CheckMemBufferMode(MemBuffer* const mem, + MemBufferMode expected) { if (mem->mode_ == MEM_MODE_NONE) { mem->mode_ = expected; // switch to the expected mode } else if (mem->mode_ != expected) { @@ -248,7 +253,7 @@ static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) { } // To be called last. -static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { +WEBP_NODISCARD static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { const WebPDecoderOptions* const options = idec->params_.options; WebPDecBuffer* const output = idec->params_.output; @@ -258,8 +263,10 @@ static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { if (status != VP8_STATUS_OK) return status; } if (idec->final_output_ != NULL) { - WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy + const VP8StatusCode status = WebPCopyDecBufferPixels( + output, idec->final_output_); // do the slow-copy WebPFreeDecBuffer(&idec->output_); + if (status != VP8_STATUS_OK) return status; *output = *idec->final_output_; idec->final_output_ = NULL; } @@ -288,7 +295,7 @@ static void RestoreContext(const MBContext* context, VP8Decoder* const dec, static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) { if (idec->state_ == STATE_VP8_DATA) { // Synchronize the thread, clean-up and check for errors. - VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_); + (void)VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_); } idec->state_ = STATE_ERROR; return error; @@ -329,6 +336,7 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) { if (dec == NULL) { return VP8_STATUS_OUT_OF_MEMORY; } + dec->incremental_ = 1; idec->dec_ = dec; dec->alpha_data_ = headers.alpha_data; dec->alpha_data_size_ = headers.alpha_data_size; @@ -601,8 +609,9 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) { //------------------------------------------------------------------------------ // Internal constructor -static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer, - const WebPBitstreamFeatures* const features) { +WEBP_NODISCARD static WebPIDecoder* NewDecoder( + WebPDecBuffer* const output_buffer, + const WebPBitstreamFeatures* const features) { WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec)); if (idec == NULL) { return NULL; @@ -614,8 +623,10 @@ static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer, idec->last_mb_y_ = -1; InitMemBuffer(&idec->mem_); - WebPInitDecBuffer(&idec->output_); - VP8InitIo(&idec->io_); + if (!WebPInitDecBuffer(&idec->output_) || !VP8InitIo(&idec->io_)) { + WebPSafeFree(idec); + return NULL; + } WebPResetDecParams(&idec->params_); if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) { @@ -674,7 +685,8 @@ void WebPIDelete(WebPIDecoder* idec) { if (!idec->is_lossless_) { if (idec->state_ == STATE_VP8_DATA) { // Synchronize the thread, clean-up and check for errors. - VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_); + // TODO(vrabaud) do we care about the return result? + (void)VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_); } VP8Delete((VP8Decoder*)idec->dec_); } else { @@ -851,8 +863,8 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, return src; } -uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y, - int* width, int* height, int* stride) { +WEBP_NODISCARD uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y, + int* width, int* height, int* stride) { const WebPDecBuffer* const src = GetOutputBuffer(idec); if (src == NULL) return NULL; if (src->colorspace >= MODE_YUV) { @@ -867,10 +879,10 @@ uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y, return src->u.RGBA.rgba; } -uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y, - uint8_t** u, uint8_t** v, uint8_t** a, - int* width, int* height, - int* stride, int* uv_stride, int* a_stride) { +WEBP_NODISCARD uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y, + uint8_t** u, uint8_t** v, uint8_t** a, + int* width, int* height, int* stride, + int* uv_stride, int* a_stride) { const WebPDecBuffer* const src = GetOutputBuffer(idec); if (src == NULL) return NULL; if (src->colorspace < MODE_YUV) { diff --git a/thirdparty/libwebp/src/dec/vp8_dec.c b/thirdparty/libwebp/src/dec/vp8_dec.c index 20b92e84c4..2ee8900605 100644 --- a/thirdparty/libwebp/src/dec/vp8_dec.c +++ b/thirdparty/libwebp/src/dec/vp8_dec.c @@ -86,6 +86,8 @@ void VP8Delete(VP8Decoder* const dec) { int VP8SetError(VP8Decoder* const dec, VP8StatusCode error, const char* const msg) { + // VP8_STATUS_SUSPENDED is only meaningful in incremental decoding. + assert(dec->incremental_ || error != VP8_STATUS_SUSPENDED); // The oldest error reported takes precedence over the new one. if (dec->status_ == VP8_STATUS_OK) { dec->status_ = error; @@ -190,12 +192,12 @@ static int ParseSegmentHeader(VP8BitReader* br, } // Paragraph 9.5 -// This function returns VP8_STATUS_SUSPENDED if we don't have all the -// necessary data in 'buf'. -// This case is not necessarily an error (for incremental decoding). -// Still, no bitreader is ever initialized to make it possible to read -// unavailable memory. -// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA +// If we don't have all the necessary data in 'buf', this function returns +// VP8_STATUS_SUSPENDED in incremental decoding, VP8_STATUS_NOT_ENOUGH_DATA +// otherwise. +// In incremental decoding, this case is not necessarily an error. Still, no +// bitreader is ever initialized to make it possible to read unavailable memory. +// If we don't even have the partitions' sizes, then VP8_STATUS_NOT_ENOUGH_DATA // is returned, and this is an unrecoverable error. // If the partitions were positioned ok, VP8_STATUS_OK is returned. static VP8StatusCode ParsePartitions(VP8Decoder* const dec, @@ -225,8 +227,10 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec, sz += 3; } VP8InitBitReader(dec->parts_ + last_part, part_start, size_left); - return (part_start < buf_end) ? VP8_STATUS_OK : - VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data + if (part_start < buf_end) return VP8_STATUS_OK; + return dec->incremental_ + ? VP8_STATUS_SUSPENDED // Init is ok, but there's not enough data + : VP8_STATUS_NOT_ENOUGH_DATA; } // Paragraph 9.4 diff --git a/thirdparty/libwebp/src/dec/vp8_dec.h b/thirdparty/libwebp/src/dec/vp8_dec.h index a05405df72..91fe104093 100644 --- a/thirdparty/libwebp/src/dec/vp8_dec.h +++ b/thirdparty/libwebp/src/dec/vp8_dec.h @@ -15,6 +15,7 @@ #define WEBP_DEC_VP8_DEC_H_ #include "src/webp/decode.h" +#include "src/webp/types.h" #ifdef __cplusplus extern "C" { @@ -108,16 +109,14 @@ struct VP8Io { }; // Internal, version-checked, entry point -int VP8InitIoInternal(VP8Io* const, int); +WEBP_NODISCARD int VP8InitIoInternal(VP8Io* const, int); // Set the custom IO function pointers and user-data. The setter for IO hooks // should be called before initiating incremental decoding. Returns true if // WebPIDecoder object is successfully modified, false otherwise. -int WebPISetIOHooks(WebPIDecoder* const idec, - VP8IoPutHook put, - VP8IoSetupHook setup, - VP8IoTeardownHook teardown, - void* user_data); +WEBP_NODISCARD int WebPISetIOHooks(WebPIDecoder* const idec, VP8IoPutHook put, + VP8IoSetupHook setup, + VP8IoTeardownHook teardown, void* user_data); // Main decoding object. This is an opaque structure. typedef struct VP8Decoder VP8Decoder; @@ -128,17 +127,17 @@ VP8Decoder* VP8New(void); // Must be called to make sure 'io' is initialized properly. // Returns false in case of version mismatch. Upon such failure, no other // decoding function should be called (VP8Decode, VP8GetHeaders, ...) -static WEBP_INLINE int VP8InitIo(VP8Io* const io) { +WEBP_NODISCARD static WEBP_INLINE int VP8InitIo(VP8Io* const io) { return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION); } // Decode the VP8 frame header. Returns true if ok. // Note: 'io->data' must be pointing to the start of the VP8 frame header. -int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io); +WEBP_NODISCARD int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io); // Decode a picture. Will call VP8GetHeaders() if it wasn't done already. // Returns false in case of error. -int VP8Decode(VP8Decoder* const dec, VP8Io* const io); +WEBP_NODISCARD int VP8Decode(VP8Decoder* const dec, VP8Io* const io); // Return current status of the decoder: VP8StatusCode VP8Status(VP8Decoder* const dec); diff --git a/thirdparty/libwebp/src/dec/vp8i_dec.h b/thirdparty/libwebp/src/dec/vp8i_dec.h index 7929fd7506..cb21d475ae 100644 --- a/thirdparty/libwebp/src/dec/vp8i_dec.h +++ b/thirdparty/libwebp/src/dec/vp8i_dec.h @@ -21,6 +21,7 @@ #include "src/utils/random_utils.h" #include "src/utils/thread_utils.h" #include "src/dsp/dsp.h" +#include "src/webp/types.h" #ifdef __cplusplus extern "C" { @@ -31,8 +32,8 @@ extern "C" { // version numbers #define DEC_MAJ_VERSION 1 -#define DEC_MIN_VERSION 3 -#define DEC_REV_VERSION 2 +#define DEC_MIN_VERSION 4 +#define DEC_REV_VERSION 0 // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). // Constraints are: We need to store one 16x16 block of luma samples (y), @@ -186,6 +187,7 @@ struct VP8Decoder { // Main data source VP8BitReader br_; + int incremental_; // if true, incremental decoding is expected // headers VP8FrameHeader frm_hdr_; @@ -281,7 +283,7 @@ int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec); void VP8ParseQuant(VP8Decoder* const dec); // in frame.c -int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io); +WEBP_NODISCARD int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io); // Call io->setup() and finish setting up scan parameters. // After this call returns, one must always call VP8ExitCritical() with the // same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK @@ -289,7 +291,7 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io); VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io); // Must always be called in pair with VP8EnterCritical(). // Returns false in case of error. -int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io); +WEBP_NODISCARD int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io); // Return the multi-threading method to use (0=off), depending // on options and bitstream size. Only for lossy decoding. int VP8GetThreadMethod(const WebPDecoderOptions* const options, @@ -299,11 +301,12 @@ int VP8GetThreadMethod(const WebPDecoderOptions* const options, void VP8InitDithering(const WebPDecoderOptions* const options, VP8Decoder* const dec); // Process the last decoded row (filtering + output). -int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io); +WEBP_NODISCARD int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io); // To be called at the start of a new scanline, to initialize predictors. void VP8InitScanline(VP8Decoder* const dec); // Decode one macroblock. Returns false if there is not enough data. -int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br); +WEBP_NODISCARD int VP8DecodeMB(VP8Decoder* const dec, + VP8BitReader* const token_br); // in alpha.c const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, diff --git a/thirdparty/libwebp/src/dec/vp8l_dec.c b/thirdparty/libwebp/src/dec/vp8l_dec.c index 7995313fa1..11c00ea964 100644 --- a/thirdparty/libwebp/src/dec/vp8l_dec.c +++ b/thirdparty/libwebp/src/dec/vp8l_dec.c @@ -12,6 +12,7 @@ // Authors: Vikas Arora (vikaas.arora@gmail.com) // Jyrki Alakuijala (jyrki@google.com) +#include <assert.h> #include <stdlib.h> #include "src/dec/alphai_dec.h" @@ -101,6 +102,14 @@ static const uint16_t kTableSize[12] = { FIXED_TABLE_SIZE + 2704 }; +static int VP8LSetError(VP8LDecoder* const dec, VP8StatusCode error) { + // The oldest error reported takes precedence over the new one. + if (dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED) { + dec->status_ = error; + } + return 0; +} + static int DecodeImageStream(int xsize, int ysize, int is_level0, VP8LDecoder* const dec, @@ -301,7 +310,7 @@ static int ReadHuffmanCodeLengths( End: VP8LHuffmanTablesDeallocate(&tables); - if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + if (!ok) return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR); return ok; } @@ -333,10 +342,7 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, int i; int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 }; const int num_codes = VP8LReadBits(br, 4) + 4; - if (num_codes > NUM_CODE_LENGTH_CODES) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; - } + assert(num_codes <= NUM_CODE_LENGTH_CODES); for (i = 0; i < num_codes; ++i) { code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3); @@ -351,15 +357,14 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, code_lengths, alphabet_size); } if (!ok || size == 0) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; + return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR); } return size; } static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, int color_cache_bits, int allow_recursion) { - int i, j; + int i; VP8LBitReader* const br = &dec->br_; VP8LMetadata* const hdr = &dec->hdr_; uint32_t* huffman_image = NULL; @@ -367,9 +372,6 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, HuffmanTables* huffman_tables = &hdr->huffman_tables_; int num_htree_groups = 1; int num_htree_groups_max = 1; - int max_alphabet_size = 0; - int* code_lengths = NULL; - const int table_size = kTableSize[color_cache_bits]; int* mapping = NULL; int ok = 0; @@ -383,7 +385,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision); const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision); const int huffman_pixs = huffman_xsize * huffman_ysize; - if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec, + if (!DecodeImageStream(huffman_xsize, huffman_ysize, /*is_level0=*/0, dec, &huffman_image)) { goto Error; } @@ -407,7 +409,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, // values [0, num_htree_groups) mapping = (int*)WebPSafeMalloc(num_htree_groups_max, sizeof(*mapping)); if (mapping == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); goto Error; } // -1 means a value is unmapped, and therefore unused in the Huffman @@ -426,25 +428,52 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, if (br->eos_) goto Error; - // Find maximum alphabet size for the htree group. - for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { - int alphabet_size = kAlphabetSize[j]; - if (j == 0 && color_cache_bits > 0) { - alphabet_size += 1 << color_cache_bits; - } - if (max_alphabet_size < alphabet_size) { - max_alphabet_size = alphabet_size; - } + if (!ReadHuffmanCodesHelper(color_cache_bits, num_htree_groups, + num_htree_groups_max, mapping, dec, + huffman_tables, &htree_groups)) { + goto Error; } + ok = 1; - code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size, - sizeof(*code_lengths)); - htree_groups = VP8LHtreeGroupsNew(num_htree_groups); + // All OK. Finalize pointers. + hdr->huffman_image_ = huffman_image; + hdr->num_htree_groups_ = num_htree_groups; + hdr->htree_groups_ = htree_groups; - if (htree_groups == NULL || code_lengths == NULL || + Error: + WebPSafeFree(mapping); + if (!ok) { + WebPSafeFree(huffman_image); + VP8LHuffmanTablesDeallocate(huffman_tables); + VP8LHtreeGroupsFree(htree_groups); + } + return ok; +} + +int ReadHuffmanCodesHelper(int color_cache_bits, int num_htree_groups, + int num_htree_groups_max, const int* const mapping, + VP8LDecoder* const dec, + HuffmanTables* const huffman_tables, + HTreeGroup** const htree_groups) { + int i, j, ok = 0; + const int max_alphabet_size = + kAlphabetSize[0] + ((color_cache_bits > 0) ? 1 << color_cache_bits : 0); + const int table_size = kTableSize[color_cache_bits]; + int* code_lengths = NULL; + + if ((mapping == NULL && num_htree_groups != num_htree_groups_max) || + num_htree_groups > num_htree_groups_max) { + goto Error; + } + + code_lengths = + (int*)WebPSafeCalloc((uint64_t)max_alphabet_size, sizeof(*code_lengths)); + *htree_groups = VP8LHtreeGroupsNew(num_htree_groups); + + if (*htree_groups == NULL || code_lengths == NULL || !VP8LHuffmanTablesAllocate(num_htree_groups * table_size, huffman_tables)) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); goto Error; } @@ -464,7 +493,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, } } else { HTreeGroup* const htree_group = - &htree_groups[(mapping == NULL) ? i : mapping[i]]; + &(*htree_groups)[(mapping == NULL) ? i : mapping[i]]; HuffmanCode** const htrees = htree_group->htrees; int size; int total_size = 0; @@ -516,18 +545,12 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, } ok = 1; - // All OK. Finalize pointers. - hdr->huffman_image_ = huffman_image; - hdr->num_htree_groups_ = num_htree_groups; - hdr->htree_groups_ = htree_groups; - Error: WebPSafeFree(code_lengths); - WebPSafeFree(mapping); if (!ok) { - WebPSafeFree(huffman_image); VP8LHuffmanTablesDeallocate(huffman_tables); - VP8LHtreeGroupsFree(htree_groups); + VP8LHtreeGroupsFree(*htree_groups); + *htree_groups = NULL; } return ok; } @@ -551,8 +574,7 @@ static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) { scaled_data_size * sizeof(*scaled_data); uint8_t* memory = (uint8_t*)WebPSafeMalloc(memory_size, sizeof(*memory)); if (memory == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - return 0; + return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); } assert(dec->rescaler_memory == NULL); dec->rescaler_memory = memory; @@ -1086,12 +1108,10 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, End: br->eos_ = VP8LIsEndOfStream(br); if (!ok || (br->eos_ && pos < end)) { - ok = 0; - dec->status_ = br->eos_ ? VP8_STATUS_SUSPENDED - : VP8_STATUS_BITSTREAM_ERROR; - } else { - dec->last_pixel_ = pos; + return VP8LSetError( + dec, br->eos_ ? VP8_STATUS_SUSPENDED : VP8_STATUS_BITSTREAM_ERROR); } + dec->last_pixel_ = pos; return ok; } @@ -1241,9 +1261,20 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, } br->eos_ = VP8LIsEndOfStream(br); - if (dec->incremental_ && br->eos_ && src < src_end) { + // In incremental decoding: + // br->eos_ && src < src_last: if 'br' reached the end of the buffer and + // 'src_last' has not been reached yet, there is not enough data. 'dec' has to + // be reset until there is more data. + // !br->eos_ && src < src_last: this cannot happen as either the buffer is + // fully read, either enough has been read to reach 'src_last'. + // src >= src_last: 'src_last' is reached, all is fine. 'src' can actually go + // beyond 'src_last' in case the image is cropped and an LZ77 goes further. + // The buffer might have been enough or there is some left. 'br->eos_' does + // not matter. + assert(!dec->incremental_ || (br->eos_ && src < src_last) || src >= src_last); + if (dec->incremental_ && br->eos_ && src < src_last) { RestoreState(dec); - } else if (!br->eos_) { + } else if ((dec->incremental_ && src >= src_last) || !br->eos_) { // Process the remaining rows corresponding to last row-block. if (process_func != NULL) { process_func(dec, row > last_row ? last_row : row); @@ -1258,8 +1289,7 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, return 1; Error: - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; + return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR); } // ----------------------------------------------------------------------------- @@ -1326,7 +1356,7 @@ static int ReadTransform(int* const xsize, int const* ysize, transform->bits_), VP8LSubSampleSize(transform->ysize_, transform->bits_), - 0, dec, &transform->data_); + /*is_level0=*/0, dec, &transform->data_); break; case COLOR_INDEXING_TRANSFORM: { const int num_colors = VP8LReadBits(br, 8) + 1; @@ -1336,8 +1366,11 @@ static int ReadTransform(int* const xsize, int const* ysize, : 3; *xsize = VP8LSubSampleSize(transform->xsize_, bits); transform->bits_ = bits; - ok = DecodeImageStream(num_colors, 1, 0, dec, &transform->data_); - ok = ok && ExpandColorMap(num_colors, transform); + ok = DecodeImageStream(num_colors, /*ysize=*/1, /*is_level0=*/0, dec, + &transform->data_); + if (ok && !ExpandColorMap(num_colors, transform)) { + return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); + } break; } case SUBTRACT_GREEN_TRANSFORM: @@ -1443,7 +1476,7 @@ static int DecodeImageStream(int xsize, int ysize, color_cache_bits = VP8LReadBits(br, 4); ok = (color_cache_bits >= 1 && color_cache_bits <= MAX_CACHE_BITS); if (!ok) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR); goto End; } } @@ -1452,7 +1485,7 @@ static int DecodeImageStream(int xsize, int ysize, ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize, color_cache_bits, is_level0); if (!ok) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR); goto End; } @@ -1460,8 +1493,7 @@ static int DecodeImageStream(int xsize, int ysize, if (color_cache_bits > 0) { hdr->color_cache_size_ = 1 << color_cache_bits; if (!VP8LColorCacheInit(&hdr->color_cache_, color_cache_bits)) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - ok = 0; + ok = VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); goto End; } } else { @@ -1478,8 +1510,7 @@ static int DecodeImageStream(int xsize, int ysize, const uint64_t total_size = (uint64_t)transform_xsize * transform_ysize; data = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*data)); if (data == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - ok = 0; + ok = VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); goto End; } } @@ -1524,8 +1555,7 @@ static int AllocateInternalBuffers32b(VP8LDecoder* const dec, int final_width) { dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint32_t)); if (dec->pixels_ == NULL) { dec->argb_cache_ = NULL; // for soundness - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - return 0; + return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); } dec->argb_cache_ = dec->pixels_ + num_pixels + cache_top_pixels; return 1; @@ -1536,8 +1566,7 @@ static int AllocateInternalBuffers8b(VP8LDecoder* const dec) { dec->argb_cache_ = NULL; // for soundness dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint8_t)); if (dec->pixels_ == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - return 0; + return VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); } return 1; } @@ -1592,7 +1621,8 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, dec->status_ = VP8_STATUS_OK; VP8LInitBitReader(&dec->br_, data, data_size); - if (!DecodeImageStream(alph_dec->width_, alph_dec->height_, 1, dec, NULL)) { + if (!DecodeImageStream(alph_dec->width_, alph_dec->height_, /*is_level0=*/1, + dec, /*decoded_data=*/NULL)) { goto Err; } @@ -1647,22 +1677,24 @@ int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { if (dec == NULL) return 0; if (io == NULL) { - dec->status_ = VP8_STATUS_INVALID_PARAM; - return 0; + return VP8LSetError(dec, VP8_STATUS_INVALID_PARAM); } dec->io_ = io; dec->status_ = VP8_STATUS_OK; VP8LInitBitReader(&dec->br_, io->data, io->data_size); if (!ReadImageInfo(&dec->br_, &width, &height, &has_alpha)) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; + VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR); goto Error; } dec->state_ = READ_DIM; io->width = width; io->height = height; - if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error; + if (!DecodeImageStream(width, height, /*is_level0=*/1, dec, + /*decoded_data=*/NULL)) { + goto Error; + } return 1; Error: @@ -1692,7 +1724,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) { assert(dec->output_ != NULL); if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) { - dec->status_ = VP8_STATUS_INVALID_PARAM; + VP8LSetError(dec, VP8_STATUS_INVALID_PARAM); goto Err; } @@ -1702,7 +1734,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) { if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err; #else if (io->use_scaling) { - dec->status_ = VP8_STATUS_INVALID_PARAM; + VP8LSetError(dec, VP8_STATUS_INVALID_PARAM); goto Err; } #endif @@ -1720,7 +1752,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) { dec->hdr_.saved_color_cache_.colors_ == NULL) { if (!VP8LColorCacheInit(&dec->hdr_.saved_color_cache_, dec->hdr_.color_cache_.hash_bits_)) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; + VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY); goto Err; } } diff --git a/thirdparty/libwebp/src/dec/vp8li_dec.h b/thirdparty/libwebp/src/dec/vp8li_dec.h index 32540a4b88..9a13bcc98d 100644 --- a/thirdparty/libwebp/src/dec/vp8li_dec.h +++ b/thirdparty/libwebp/src/dec/vp8li_dec.h @@ -20,6 +20,7 @@ #include "src/utils/bit_reader_utils.h" #include "src/utils/color_cache_utils.h" #include "src/utils/huffman_utils.h" +#include "src/webp/types.h" #ifdef __cplusplus extern "C" { @@ -99,25 +100,26 @@ struct ALPHDecoder; // Defined in dec/alphai.h. // Decodes image header for alpha data stored using lossless compression. // Returns false in case of error. -int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec, - const uint8_t* const data, size_t data_size); +WEBP_NODISCARD int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec, + const uint8_t* const data, + size_t data_size); // Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are // already decoded in previous call(s), it will resume decoding from where it // was paused. // Returns false in case of bitstream error. -int VP8LDecodeAlphaImageStream(struct ALPHDecoder* const alph_dec, - int last_row); +WEBP_NODISCARD int VP8LDecodeAlphaImageStream( + struct ALPHDecoder* const alph_dec, int last_row); // Allocates and initialize a new lossless decoder instance. -VP8LDecoder* VP8LNew(void); +WEBP_NODISCARD VP8LDecoder* VP8LNew(void); // Decodes the image header. Returns false in case of error. -int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io); +WEBP_NODISCARD int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io); // Decodes an image. It's required to decode the lossless header before calling // this function. Returns false in case of error, with updated dec->status_. -int VP8LDecodeImage(VP8LDecoder* const dec); +WEBP_NODISCARD int VP8LDecodeImage(VP8LDecoder* const dec); // Resets the decoder in its initial state, reclaiming memory. // Preserves the dec->status_ value. @@ -126,6 +128,18 @@ void VP8LClear(VP8LDecoder* const dec); // Clears and deallocate a lossless decoder instance. void VP8LDelete(VP8LDecoder* const dec); +// Helper function for reading the different Huffman codes and storing them in +// 'huffman_tables' and 'htree_groups'. +// If mapping is NULL 'num_htree_groups_max' must equal 'num_htree_groups'. +// If it is not NULL, it maps 'num_htree_groups_max' indices to the +// 'num_htree_groups' groups. If 'num_htree_groups_max' > 'num_htree_groups', +// some of those indices map to -1. This is used for non-balanced codes to +// limit memory usage. +WEBP_NODISCARD int ReadHuffmanCodesHelper( + int color_cache_bits, int num_htree_groups, int num_htree_groups_max, + const int* const mapping, VP8LDecoder* const dec, + HuffmanTables* const huffman_tables, HTreeGroup** const htree_groups); + //------------------------------------------------------------------------------ #ifdef __cplusplus diff --git a/thirdparty/libwebp/src/dec/webp_dec.c b/thirdparty/libwebp/src/dec/webp_dec.c index f557868b99..49ef205c8b 100644 --- a/thirdparty/libwebp/src/dec/webp_dec.c +++ b/thirdparty/libwebp/src/dec/webp_dec.c @@ -13,11 +13,14 @@ #include <stdlib.h> +#include "src/dec/vp8_dec.h" #include "src/dec/vp8i_dec.h" #include "src/dec/vp8li_dec.h" #include "src/dec/webpi_dec.h" #include "src/utils/utils.h" #include "src/webp/mux_types.h" // ALPHA_FLAG +#include "src/webp/decode.h" +#include "src/webp/types.h" //------------------------------------------------------------------------------ // RIFF layout is: @@ -444,8 +447,9 @@ void WebPResetDecParams(WebPDecParams* const params) { // "Into" decoding variants // Main flow -static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, - WebPDecParams* const params) { +WEBP_NODISCARD static VP8StatusCode DecodeInto(const uint8_t* const data, + size_t data_size, + WebPDecParams* const params) { VP8StatusCode status; VP8Io io; WebPHeaderStructure headers; @@ -459,7 +463,9 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, } assert(params != NULL); - VP8InitIo(&io); + if (!VP8InitIo(&io)) { + return VP8_STATUS_INVALID_PARAM; + } io.data = headers.data + headers.offset; io.data_size = headers.data_size - headers.offset; WebPInitCustomIo(params, &io); // Plug the I/O functions. @@ -523,17 +529,16 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, } // Helpers -static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace, - const uint8_t* const data, - size_t data_size, - uint8_t* const rgba, - int stride, size_t size) { +WEBP_NODISCARD static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace, + const uint8_t* const data, + size_t data_size, + uint8_t* const rgba, + int stride, size_t size) { WebPDecParams params; WebPDecBuffer buf; - if (rgba == NULL) { + if (rgba == NULL || !WebPInitDecBuffer(&buf)) { return NULL; } - WebPInitDecBuffer(&buf); WebPResetDecParams(¶ms); params.output = &buf; buf.colorspace = colorspace; @@ -578,8 +583,7 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size, uint8_t* v, size_t v_size, int v_stride) { WebPDecParams params; WebPDecBuffer output; - if (luma == NULL) return NULL; - WebPInitDecBuffer(&output); + if (luma == NULL || !WebPInitDecBuffer(&output)) return NULL; WebPResetDecParams(¶ms); params.output = &output; output.colorspace = MODE_YUV; @@ -601,13 +605,17 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size, //------------------------------------------------------------------------------ -static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data, - size_t data_size, int* const width, int* const height, - WebPDecBuffer* const keep_info) { +WEBP_NODISCARD static uint8_t* Decode(WEBP_CSP_MODE mode, + const uint8_t* const data, + size_t data_size, int* const width, + int* const height, + WebPDecBuffer* const keep_info) { WebPDecParams params; WebPDecBuffer output; - WebPInitDecBuffer(&output); + if (!WebPInitDecBuffer(&output)) { + return NULL; + } WebPResetDecParams(¶ms); params.output = &output; output.colorspace = mode; @@ -733,7 +741,9 @@ int WebPInitDecoderConfigInternal(WebPDecoderConfig* config, } memset(config, 0, sizeof(*config)); DefaultFeatures(&config->input); - WebPInitDecBuffer(&config->output); + if (!WebPInitDecBuffer(&config->output)) { + return 0; + } return 1; } @@ -772,7 +782,9 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, if (WebPAvoidSlowMemory(params.output, &config->input)) { // decoding to slow memory: use a temporary in-mem buffer to decode into. WebPDecBuffer in_mem_buffer; - WebPInitDecBuffer(&in_mem_buffer); + if (!WebPInitDecBuffer(&in_mem_buffer)) { + return VP8_STATUS_INVALID_PARAM; + } in_mem_buffer.colorspace = config->output.colorspace; in_mem_buffer.width = config->input.width; in_mem_buffer.height = config->input.height; diff --git a/thirdparty/libwebp/src/dec/webpi_dec.h b/thirdparty/libwebp/src/dec/webpi_dec.h index 3b97388c71..77bf5264b7 100644 --- a/thirdparty/libwebp/src/dec/webpi_dec.h +++ b/thirdparty/libwebp/src/dec/webpi_dec.h @@ -20,6 +20,7 @@ extern "C" { #include "src/utils/rescaler_utils.h" #include "src/dec/vp8_dec.h" +#include "src/webp/decode.h" //------------------------------------------------------------------------------ // WebPDecParams: Decoding output parameters. Transient internal object. @@ -87,8 +88,9 @@ void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io); // Setup crop_xxx fields, mb_w and mb_h in io. 'src_colorspace' refers // to the *compressed* format, not the output one. -int WebPIoInitFromOptions(const WebPDecoderOptions* const options, - VP8Io* const io, WEBP_CSP_MODE src_colorspace); +WEBP_NODISCARD int WebPIoInitFromOptions( + const WebPDecoderOptions* const options, VP8Io* const io, + WEBP_CSP_MODE src_colorspace); //------------------------------------------------------------------------------ // Internal functions regarding WebPDecBuffer memory (in buffer.c). diff --git a/thirdparty/libwebp/src/demux/anim_decode.c b/thirdparty/libwebp/src/demux/anim_decode.c index e077ffb536..27f0e2b0bb 100644 --- a/thirdparty/libwebp/src/demux/anim_decode.c +++ b/thirdparty/libwebp/src/demux/anim_decode.c @@ -20,6 +20,7 @@ #include "src/utils/utils.h" #include "src/webp/decode.h" #include "src/webp/demux.h" +#include "src/webp/types.h" #define NUM_CHANNELS 4 @@ -68,8 +69,9 @@ int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions* dec_options, return 1; } -static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options, - WebPAnimDecoder* const dec) { +WEBP_NODISCARD static int ApplyDecoderOptions( + const WebPAnimDecoderOptions* const dec_options, + WebPAnimDecoder* const dec) { WEBP_CSP_MODE mode; WebPDecoderConfig* config = &dec->config_; assert(dec_options != NULL); @@ -82,7 +84,9 @@ static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options, dec->blend_func_ = (mode == MODE_RGBA || mode == MODE_BGRA) ? &BlendPixelRowNonPremult : &BlendPixelRowPremult; - WebPInitDecoderConfig(config); + if (!WebPInitDecoderConfig(config)) { + return 0; + } config->output.colorspace = mode; config->output.is_external_memory = 1; config->options.use_threads = dec_options->use_threads; @@ -157,8 +161,8 @@ static int IsFullFrame(int width, int height, int canvas_width, } // Clear the canvas to transparent. -static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width, - uint32_t canvas_height) { +WEBP_NODISCARD static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width, + uint32_t canvas_height) { const uint64_t size = (uint64_t)canvas_width * canvas_height * NUM_CHANNELS * sizeof(*buf); if (!CheckSizeOverflow(size)) return 0; @@ -179,8 +183,8 @@ static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset, } // Copy width * height pixels from 'src' to 'dst'. -static int CopyCanvas(const uint8_t* src, uint8_t* dst, - uint32_t width, uint32_t height) { +WEBP_NODISCARD static int CopyCanvas(const uint8_t* src, uint8_t* dst, + uint32_t width, uint32_t height) { const uint64_t size = (uint64_t)width * height * NUM_CHANNELS; if (!CheckSizeOverflow(size)) return 0; assert(src != NULL && dst != NULL); @@ -424,7 +428,9 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, WebPDemuxReleaseIterator(&dec->prev_iter_); dec->prev_iter_ = iter; dec->prev_frame_was_keyframe_ = is_key_frame; - CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height); + if (!CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height)) { + goto Error; + } if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS, dec->prev_iter_.x_offset, dec->prev_iter_.y_offset, diff --git a/thirdparty/libwebp/src/demux/demux.c b/thirdparty/libwebp/src/demux/demux.c index 4b0d3f59e9..d01c6a7464 100644 --- a/thirdparty/libwebp/src/demux/demux.c +++ b/thirdparty/libwebp/src/demux/demux.c @@ -24,8 +24,8 @@ #include "src/webp/format_constants.h" #define DMUX_MAJ_VERSION 1 -#define DMUX_MIN_VERSION 3 -#define DMUX_REV_VERSION 2 +#define DMUX_MIN_VERSION 4 +#define DMUX_REV_VERSION 0 typedef struct { size_t start_; // start location of the data diff --git a/thirdparty/libwebp/src/dsp/alpha_processing_sse2.c b/thirdparty/libwebp/src/dsp/alpha_processing_sse2.c index f0843d0feb..aa0cc2848a 100644 --- a/thirdparty/libwebp/src/dsp/alpha_processing_sse2.c +++ b/thirdparty/libwebp/src/dsp/alpha_processing_sse2.c @@ -144,6 +144,46 @@ static int ExtractAlpha_SSE2(const uint8_t* WEBP_RESTRICT argb, int argb_stride, return (alpha_and == 0xff); } +static void ExtractGreen_SSE2(const uint32_t* WEBP_RESTRICT argb, + uint8_t* WEBP_RESTRICT alpha, int size) { + int i; + const __m128i mask = _mm_set1_epi32(0xff); + const __m128i* src = (const __m128i*)argb; + + for (i = 0; i + 16 <= size; i += 16, src += 4) { + const __m128i a0 = _mm_loadu_si128(src + 0); + const __m128i a1 = _mm_loadu_si128(src + 1); + const __m128i a2 = _mm_loadu_si128(src + 2); + const __m128i a3 = _mm_loadu_si128(src + 3); + const __m128i b0 = _mm_srli_epi32(a0, 8); + const __m128i b1 = _mm_srli_epi32(a1, 8); + const __m128i b2 = _mm_srli_epi32(a2, 8); + const __m128i b3 = _mm_srli_epi32(a3, 8); + const __m128i c0 = _mm_and_si128(b0, mask); + const __m128i c1 = _mm_and_si128(b1, mask); + const __m128i c2 = _mm_and_si128(b2, mask); + const __m128i c3 = _mm_and_si128(b3, mask); + const __m128i d0 = _mm_packs_epi32(c0, c1); + const __m128i d1 = _mm_packs_epi32(c2, c3); + const __m128i e = _mm_packus_epi16(d0, d1); + // store + _mm_storeu_si128((__m128i*)&alpha[i], e); + } + if (i + 8 <= size) { + const __m128i a0 = _mm_loadu_si128(src + 0); + const __m128i a1 = _mm_loadu_si128(src + 1); + const __m128i b0 = _mm_srli_epi32(a0, 8); + const __m128i b1 = _mm_srli_epi32(a1, 8); + const __m128i c0 = _mm_and_si128(b0, mask); + const __m128i c1 = _mm_and_si128(b1, mask); + const __m128i d = _mm_packs_epi32(c0, c1); + const __m128i e = _mm_packus_epi16(d, d); + _mm_storel_epi64((__m128i*)&alpha[i], e); + i += 8; + } + for (; i < size; ++i) alpha[i] = argb[i] >> 8; +} + //------------------------------------------------------------------------------ // Non-dither premultiplied modes @@ -354,6 +394,7 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE2(void) { WebPDispatchAlpha = DispatchAlpha_SSE2; WebPDispatchAlphaToGreen = DispatchAlphaToGreen_SSE2; WebPExtractAlpha = ExtractAlpha_SSE2; + WebPExtractGreen = ExtractGreen_SSE2; WebPHasAlpha8b = HasAlpha8b_SSE2; WebPHasAlpha32b = HasAlpha32b_SSE2; diff --git a/thirdparty/libwebp/src/dsp/cpu.c b/thirdparty/libwebp/src/dsp/cpu.c index 2234c77b35..8ba8f68335 100644 --- a/thirdparty/libwebp/src/dsp/cpu.c +++ b/thirdparty/libwebp/src/dsp/cpu.c @@ -36,18 +36,6 @@ static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) { : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) : "a"(info_type), "c"(0)); } -#elif defined(__x86_64__) && \ - (defined(__code_model_medium__) || defined(__code_model_large__)) && \ - defined(__PIC__) -static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) { - __asm__ volatile ( - "xchg{q}\t{%%rbx}, %q1\n" - "cpuid\n" - "xchg{q}\t{%%rbx}, %q1\n" - : "=a"(cpu_info[0]), "=&r"(cpu_info[1]), "=c"(cpu_info[2]), - "=d"(cpu_info[3]) - : "a"(info_type), "c"(0)); -} #elif defined(__i386__) || defined(__x86_64__) static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) { __asm__ volatile ( diff --git a/thirdparty/libwebp/src/dsp/dec.c b/thirdparty/libwebp/src/dsp/dec.c index 33d8df8a62..451d649d58 100644 --- a/thirdparty/libwebp/src/dsp/dec.c +++ b/thirdparty/libwebp/src/dsp/dec.c @@ -37,9 +37,6 @@ static WEBP_INLINE uint8_t clip_8b(int v) { STORE(3, y, DC - (d)); \ } while (0) -#define MUL1(a) ((((a) * 20091) >> 16) + (a)) -#define MUL2(a) (((a) * 35468) >> 16) - #if !WEBP_NEON_OMIT_C_CODE static void TransformOne_C(const int16_t* in, uint8_t* dst) { int C[4 * 4], *tmp; @@ -48,8 +45,10 @@ static void TransformOne_C(const int16_t* in, uint8_t* dst) { for (i = 0; i < 4; ++i) { // vertical pass const int a = in[0] + in[8]; // [-4096, 4094] const int b = in[0] - in[8]; // [-4095, 4095] - const int c = MUL2(in[4]) - MUL1(in[12]); // [-3783, 3783] - const int d = MUL1(in[4]) + MUL2(in[12]); // [-3785, 3781] + const int c = WEBP_TRANSFORM_AC3_MUL2(in[4]) - + WEBP_TRANSFORM_AC3_MUL1(in[12]); // [-3783, 3783] + const int d = WEBP_TRANSFORM_AC3_MUL1(in[4]) + + WEBP_TRANSFORM_AC3_MUL2(in[12]); // [-3785, 3781] tmp[0] = a + d; // [-7881, 7875] tmp[1] = b + c; // [-7878, 7878] tmp[2] = b - c; // [-7878, 7878] @@ -69,8 +68,10 @@ static void TransformOne_C(const int16_t* in, uint8_t* dst) { const int dc = tmp[0] + 4; const int a = dc + tmp[8]; const int b = dc - tmp[8]; - const int c = MUL2(tmp[4]) - MUL1(tmp[12]); - const int d = MUL1(tmp[4]) + MUL2(tmp[12]); + const int c = + WEBP_TRANSFORM_AC3_MUL2(tmp[4]) - WEBP_TRANSFORM_AC3_MUL1(tmp[12]); + const int d = + WEBP_TRANSFORM_AC3_MUL1(tmp[4]) + WEBP_TRANSFORM_AC3_MUL2(tmp[12]); STORE(0, 0, a + d); STORE(1, 0, b + c); STORE(2, 0, b - c); @@ -83,17 +84,15 @@ static void TransformOne_C(const int16_t* in, uint8_t* dst) { // Simplified transform when only in[0], in[1] and in[4] are non-zero static void TransformAC3_C(const int16_t* in, uint8_t* dst) { const int a = in[0] + 4; - const int c4 = MUL2(in[4]); - const int d4 = MUL1(in[4]); - const int c1 = MUL2(in[1]); - const int d1 = MUL1(in[1]); + const int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]); + const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]); + const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]); + const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]); STORE2(0, a + d4, d1, c1); STORE2(1, a + c4, d1, c1); STORE2(2, a - c4, d1, c1); STORE2(3, a - d4, d1, c1); } -#undef MUL1 -#undef MUL2 #undef STORE2 static void TransformTwo_C(const int16_t* in, uint8_t* dst, int do_two) { diff --git a/thirdparty/libwebp/src/dsp/dec_mips32.c b/thirdparty/libwebp/src/dsp/dec_mips32.c index e4e70966d2..f0e7de4ac4 100644 --- a/thirdparty/libwebp/src/dsp/dec_mips32.c +++ b/thirdparty/libwebp/src/dsp/dec_mips32.c @@ -18,8 +18,8 @@ #include "src/dsp/mips_macro.h" -static const int kC1 = 20091 + (1 << 16); -static const int kC2 = 35468; +static const int kC1 = WEBP_TRANSFORM_AC3_C1; +static const int kC2 = WEBP_TRANSFORM_AC3_C2; static WEBP_INLINE int abs_mips32(int x) { const int sign = x >> 31; @@ -219,7 +219,7 @@ static void TransformOne(const int16_t* in, uint8_t* dst) { int temp0, temp1, temp2, temp3, temp4; int temp5, temp6, temp7, temp8, temp9; int temp10, temp11, temp12, temp13, temp14; - int temp15, temp16, temp17, temp18; + int temp15, temp16, temp17, temp18, temp19; int16_t* p_in = (int16_t*)in; // loops unrolled and merged to avoid usage of tmp buffer @@ -233,16 +233,14 @@ static void TransformOne(const int16_t* in, uint8_t* dst) { "addu %[temp16], %[temp0], %[temp8] \n\t" "subu %[temp0], %[temp0], %[temp8] \n\t" "mul %[temp8], %[temp4], %[kC2] \n\t" - "mul %[temp17], %[temp12], %[kC1] \n\t" - "mul %[temp4], %[temp4], %[kC1] \n\t" + MUL_SHIFT_C1(temp17, temp12) + MUL_SHIFT_C1_IO(temp4, temp19) "mul %[temp12], %[temp12], %[kC2] \n\t" "lh %[temp1], 2(%[in]) \n\t" "lh %[temp5], 10(%[in]) \n\t" "lh %[temp9], 18(%[in]) \n\t" "lh %[temp13], 26(%[in]) \n\t" "sra %[temp8], %[temp8], 16 \n\t" - "sra %[temp17], %[temp17], 16 \n\t" - "sra %[temp4], %[temp4], 16 \n\t" "sra %[temp12], %[temp12], 16 \n\t" "lh %[temp2], 4(%[in]) \n\t" "lh %[temp6], 12(%[in]) \n\t" @@ -261,49 +259,43 @@ static void TransformOne(const int16_t* in, uint8_t* dst) { "addu %[temp12], %[temp0], %[temp17] \n\t" "subu %[temp0], %[temp0], %[temp17] \n\t" "mul %[temp9], %[temp5], %[kC2] \n\t" - "mul %[temp17], %[temp13], %[kC1] \n\t" - "mul %[temp5], %[temp5], %[kC1] \n\t" + MUL_SHIFT_C1(temp17, temp13) + MUL_SHIFT_C1_IO(temp5, temp19) "mul %[temp13], %[temp13], %[kC2] \n\t" "sra %[temp9], %[temp9], 16 \n\t" - "sra %[temp17], %[temp17], 16 \n\t" "subu %[temp17], %[temp9], %[temp17] \n\t" - "sra %[temp5], %[temp5], 16 \n\t" "sra %[temp13], %[temp13], 16 \n\t" "addu %[temp5], %[temp5], %[temp13] \n\t" "addu %[temp13], %[temp1], %[temp17] \n\t" "subu %[temp1], %[temp1], %[temp17] \n\t" - "mul %[temp17], %[temp14], %[kC1] \n\t" + MUL_SHIFT_C1(temp17, temp14) "mul %[temp14], %[temp14], %[kC2] \n\t" "addu %[temp9], %[temp16], %[temp5] \n\t" "subu %[temp5], %[temp16], %[temp5] \n\t" "addu %[temp16], %[temp2], %[temp10] \n\t" "subu %[temp2], %[temp2], %[temp10] \n\t" "mul %[temp10], %[temp6], %[kC2] \n\t" - "mul %[temp6], %[temp6], %[kC1] \n\t" - "sra %[temp17], %[temp17], 16 \n\t" + MUL_SHIFT_C1_IO(temp6, temp19) "sra %[temp14], %[temp14], 16 \n\t" "sra %[temp10], %[temp10], 16 \n\t" - "sra %[temp6], %[temp6], 16 \n\t" "subu %[temp17], %[temp10], %[temp17] \n\t" "addu %[temp6], %[temp6], %[temp14] \n\t" "addu %[temp10], %[temp16], %[temp6] \n\t" "subu %[temp6], %[temp16], %[temp6] \n\t" "addu %[temp14], %[temp2], %[temp17] \n\t" "subu %[temp2], %[temp2], %[temp17] \n\t" - "mul %[temp17], %[temp15], %[kC1] \n\t" + MUL_SHIFT_C1(temp17, temp15) "mul %[temp15], %[temp15], %[kC2] \n\t" "addu %[temp16], %[temp3], %[temp11] \n\t" "subu %[temp3], %[temp3], %[temp11] \n\t" "mul %[temp11], %[temp7], %[kC2] \n\t" - "mul %[temp7], %[temp7], %[kC1] \n\t" + MUL_SHIFT_C1_IO(temp7, temp19) "addiu %[temp8], %[temp8], 4 \n\t" "addiu %[temp12], %[temp12], 4 \n\t" "addiu %[temp0], %[temp0], 4 \n\t" "addiu %[temp4], %[temp4], 4 \n\t" - "sra %[temp17], %[temp17], 16 \n\t" "sra %[temp15], %[temp15], 16 \n\t" "sra %[temp11], %[temp11], 16 \n\t" - "sra %[temp7], %[temp7], 16 \n\t" "subu %[temp17], %[temp11], %[temp17] \n\t" "addu %[temp7], %[temp7], %[temp15] \n\t" "addu %[temp15], %[temp3], %[temp17] \n\t" @@ -313,48 +305,40 @@ static void TransformOne(const int16_t* in, uint8_t* dst) { "addu %[temp16], %[temp8], %[temp10] \n\t" "subu %[temp8], %[temp8], %[temp10] \n\t" "mul %[temp10], %[temp9], %[kC2] \n\t" - "mul %[temp17], %[temp11], %[kC1] \n\t" - "mul %[temp9], %[temp9], %[kC1] \n\t" + MUL_SHIFT_C1(temp17, temp11) + MUL_SHIFT_C1_IO(temp9, temp19) "mul %[temp11], %[temp11], %[kC2] \n\t" "sra %[temp10], %[temp10], 16 \n\t" - "sra %[temp17], %[temp17], 16 \n\t" - "sra %[temp9], %[temp9], 16 \n\t" "sra %[temp11], %[temp11], 16 \n\t" "subu %[temp17], %[temp10], %[temp17] \n\t" "addu %[temp11], %[temp9], %[temp11] \n\t" "addu %[temp10], %[temp12], %[temp14] \n\t" "subu %[temp12], %[temp12], %[temp14] \n\t" "mul %[temp14], %[temp13], %[kC2] \n\t" - "mul %[temp9], %[temp15], %[kC1] \n\t" - "mul %[temp13], %[temp13], %[kC1] \n\t" + MUL_SHIFT_C1(temp9, temp15) + MUL_SHIFT_C1_IO(temp13, temp19) "mul %[temp15], %[temp15], %[kC2] \n\t" "sra %[temp14], %[temp14], 16 \n\t" - "sra %[temp9], %[temp9], 16 \n\t" - "sra %[temp13], %[temp13], 16 \n\t" "sra %[temp15], %[temp15], 16 \n\t" "subu %[temp9], %[temp14], %[temp9] \n\t" "addu %[temp15], %[temp13], %[temp15] \n\t" "addu %[temp14], %[temp0], %[temp2] \n\t" "subu %[temp0], %[temp0], %[temp2] \n\t" "mul %[temp2], %[temp1], %[kC2] \n\t" - "mul %[temp13], %[temp3], %[kC1] \n\t" - "mul %[temp1], %[temp1], %[kC1] \n\t" + MUL_SHIFT_C1(temp13, temp3) + MUL_SHIFT_C1_IO(temp1, temp19) "mul %[temp3], %[temp3], %[kC2] \n\t" "sra %[temp2], %[temp2], 16 \n\t" - "sra %[temp13], %[temp13], 16 \n\t" - "sra %[temp1], %[temp1], 16 \n\t" "sra %[temp3], %[temp3], 16 \n\t" "subu %[temp13], %[temp2], %[temp13] \n\t" "addu %[temp3], %[temp1], %[temp3] \n\t" "addu %[temp2], %[temp4], %[temp6] \n\t" "subu %[temp4], %[temp4], %[temp6] \n\t" "mul %[temp6], %[temp5], %[kC2] \n\t" - "mul %[temp1], %[temp7], %[kC1] \n\t" - "mul %[temp5], %[temp5], %[kC1] \n\t" + MUL_SHIFT_C1(temp1, temp7) + MUL_SHIFT_C1_IO(temp5, temp19) "mul %[temp7], %[temp7], %[kC2] \n\t" "sra %[temp6], %[temp6], 16 \n\t" - "sra %[temp1], %[temp1], 16 \n\t" - "sra %[temp5], %[temp5], 16 \n\t" "sra %[temp7], %[temp7], 16 \n\t" "subu %[temp1], %[temp6], %[temp1] \n\t" "addu %[temp7], %[temp5], %[temp7] \n\t" @@ -542,7 +526,7 @@ static void TransformOne(const int16_t* in, uint8_t* dst) { [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11), [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), [temp14]"=&r"(temp14), [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), [temp17]"=&r"(temp17), - [temp18]"=&r"(temp18) + [temp18]"=&r"(temp18), [temp19]"=&r"(temp19) : [in]"r"(p_in), [kC1]"r"(kC1), [kC2]"r"(kC2), [dst]"r"(dst) : "memory", "hi", "lo" ); diff --git a/thirdparty/libwebp/src/dsp/dec_mips_dsp_r2.c b/thirdparty/libwebp/src/dsp/dec_mips_dsp_r2.c index b0936bc46e..0ba706a2ef 100644 --- a/thirdparty/libwebp/src/dsp/dec_mips_dsp_r2.c +++ b/thirdparty/libwebp/src/dsp/dec_mips_dsp_r2.c @@ -18,10 +18,8 @@ #include "src/dsp/mips_macro.h" -static const int kC1 = 20091 + (1 << 16); -static const int kC2 = 35468; - -#define MUL(a, b) (((a) * (b)) >> 16) +static const int kC1 = WEBP_TRANSFORM_AC3_C1; +static const int kC2 = WEBP_TRANSFORM_AC3_C2; static void TransformDC(const int16_t* in, uint8_t* dst) { int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9, temp10; @@ -49,10 +47,10 @@ static void TransformDC(const int16_t* in, uint8_t* dst) { static void TransformAC3(const int16_t* in, uint8_t* dst) { const int a = in[0] + 4; - int c4 = MUL(in[4], kC2); - const int d4 = MUL(in[4], kC1); - const int c1 = MUL(in[1], kC2); - const int d1 = MUL(in[1], kC1); + int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]); + const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]); + const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]); + const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]); int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9; int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18; @@ -479,8 +477,6 @@ static void HFilter8i(uint8_t* u, uint8_t* v, int stride, FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh); } -#undef MUL - //------------------------------------------------------------------------------ // Simple In-loop filtering (Paragraph 15.2) diff --git a/thirdparty/libwebp/src/dsp/dec_msa.c b/thirdparty/libwebp/src/dsp/dec_msa.c index 8090622b7b..58d1730192 100644 --- a/thirdparty/libwebp/src/dsp/dec_msa.c +++ b/thirdparty/libwebp/src/dsp/dec_msa.c @@ -37,8 +37,6 @@ d1_m = d_tmp1_m + d_tmp2_m; \ BUTTERFLY_4(a1_m, b1_m, c1_m, d1_m, out0, out1, out2, out3); \ } -#define MULT1(a) ((((a) * 20091) >> 16) + (a)) -#define MULT2(a) (((a) * 35468) >> 16) static void TransformOne(const int16_t* in, uint8_t* dst) { v8i16 input0, input1; @@ -124,10 +122,10 @@ static void TransformDC(const int16_t* in, uint8_t* dst) { static void TransformAC3(const int16_t* in, uint8_t* dst) { const int a = in[0] + 4; - const int c4 = MULT2(in[4]); - const int d4 = MULT1(in[4]); - const int in2 = MULT2(in[1]); - const int in3 = MULT1(in[1]); + const int c4 = WEBP_TRANSFORM_AC3_MUL2(in[4]); + const int d4 = WEBP_TRANSFORM_AC3_MUL1(in[4]); + const int in2 = WEBP_TRANSFORM_AC3_MUL2(in[1]); + const int in3 = WEBP_TRANSFORM_AC3_MUL1(in[1]); v4i32 tmp0 = { 0 }; v4i32 out0 = __msa_fill_w(a + d4); v4i32 out1 = __msa_fill_w(a + c4); diff --git a/thirdparty/libwebp/src/dsp/dec_neon.c b/thirdparty/libwebp/src/dsp/dec_neon.c index 22784cf15a..83b3a1f970 100644 --- a/thirdparty/libwebp/src/dsp/dec_neon.c +++ b/thirdparty/libwebp/src/dsp/dec_neon.c @@ -1000,8 +1000,9 @@ static void HFilter8i_NEON(uint8_t* u, uint8_t* v, int stride, // libwebp adds 1 << 16 to cospi8sqrt2minus1 (kC1). However, this causes the // same issue with kC1 and vqdmulh that we work around by down shifting kC2 -static const int16_t kC1 = 20091; -static const int16_t kC2 = 17734; // half of kC2, actually. See comment above. +static const int16_t kC1 = WEBP_TRANSFORM_AC3_C1; +static const int16_t kC2 = + WEBP_TRANSFORM_AC3_C2 / 2; // half of kC2, actually. See comment above. #if defined(WEBP_USE_INTRINSICS) static WEBP_INLINE void Transpose8x2_NEON(const int16x8_t in0, @@ -1255,15 +1256,12 @@ static void TransformWHT_NEON(const int16_t* in, int16_t* out) { //------------------------------------------------------------------------------ -#define MUL(a, b) (((a) * (b)) >> 16) static void TransformAC3_NEON(const int16_t* in, uint8_t* dst) { - static const int kC1_full = 20091 + (1 << 16); - static const int kC2_full = 35468; const int16x4_t A = vld1_dup_s16(in); - const int16x4_t c4 = vdup_n_s16(MUL(in[4], kC2_full)); - const int16x4_t d4 = vdup_n_s16(MUL(in[4], kC1_full)); - const int c1 = MUL(in[1], kC2_full); - const int d1 = MUL(in[1], kC1_full); + const int16x4_t c4 = vdup_n_s16(WEBP_TRANSFORM_AC3_MUL2(in[4])); + const int16x4_t d4 = vdup_n_s16(WEBP_TRANSFORM_AC3_MUL1(in[4])); + const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]); + const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]); const uint64_t cd = (uint64_t)( d1 & 0xffff) << 0 | (uint64_t)( c1 & 0xffff) << 16 | (uint64_t)(-c1 & 0xffff) << 32 | @@ -1274,7 +1272,6 @@ static void TransformAC3_NEON(const int16_t* in, uint8_t* dst) { const int16x8_t m2_m3 = vcombine_s16(vqsub_s16(B, c4), vqsub_s16(B, d4)); Add4x4_NEON(m0_m1, m2_m3, dst); } -#undef MUL //------------------------------------------------------------------------------ // 4x4 diff --git a/thirdparty/libwebp/src/dsp/dec_sse2.c b/thirdparty/libwebp/src/dsp/dec_sse2.c index 01e6bcb636..ff3a28555b 100644 --- a/thirdparty/libwebp/src/dsp/dec_sse2.c +++ b/thirdparty/libwebp/src/dsp/dec_sse2.c @@ -196,15 +196,13 @@ static void Transform_SSE2(const int16_t* in, uint8_t* dst, int do_two) { } #if (USE_TRANSFORM_AC3 == 1) -#define MUL(a, b) (((a) * (b)) >> 16) + static void TransformAC3(const int16_t* in, uint8_t* dst) { - static const int kC1 = 20091 + (1 << 16); - static const int kC2 = 35468; const __m128i A = _mm_set1_epi16(in[0] + 4); - const __m128i c4 = _mm_set1_epi16(MUL(in[4], kC2)); - const __m128i d4 = _mm_set1_epi16(MUL(in[4], kC1)); - const int c1 = MUL(in[1], kC2); - const int d1 = MUL(in[1], kC1); + const __m128i c4 = _mm_set1_epi16(WEBP_TRANSFORM_AC3_MUL2(in[4])); + const __m128i d4 = _mm_set1_epi16(WEBP_TRANSFORM_AC3_MUL1(in[4])); + const int c1 = WEBP_TRANSFORM_AC3_MUL2(in[1]); + const int d1 = WEBP_TRANSFORM_AC3_MUL1(in[1]); const __m128i CD = _mm_set_epi16(0, 0, 0, 0, -d1, -c1, c1, d1); const __m128i B = _mm_adds_epi16(A, CD); const __m128i m0 = _mm_adds_epi16(B, d4); @@ -238,7 +236,7 @@ static void TransformAC3(const int16_t* in, uint8_t* dst) { WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2)); WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3)); } -#undef MUL + #endif // USE_TRANSFORM_AC3 //------------------------------------------------------------------------------ @@ -259,15 +257,15 @@ static WEBP_INLINE void SignedShift8b_SSE2(__m128i* const x) { *x = _mm_packs_epi16(lo_1, hi_1); } -#define FLIP_SIGN_BIT2(a, b) { \ +#define FLIP_SIGN_BIT2(a, b) do { \ (a) = _mm_xor_si128(a, sign_bit); \ (b) = _mm_xor_si128(b, sign_bit); \ -} +} while (0) -#define FLIP_SIGN_BIT4(a, b, c, d) { \ +#define FLIP_SIGN_BIT4(a, b, c, d) do { \ FLIP_SIGN_BIT2(a, b); \ FLIP_SIGN_BIT2(c, d); \ -} +} while (0) // input/output is uint8_t static WEBP_INLINE void GetNotHEV_SSE2(const __m128i* const p1, @@ -645,12 +643,12 @@ static void SimpleHFilter16i_SSE2(uint8_t* p, int stride, int thresh) { (m) = _mm_max_epu8(m, MM_ABS(p2, p1)); \ } while (0) -#define LOAD_H_EDGES4(p, stride, e1, e2, e3, e4) { \ +#define LOAD_H_EDGES4(p, stride, e1, e2, e3, e4) do { \ (e1) = _mm_loadu_si128((__m128i*)&(p)[0 * (stride)]); \ (e2) = _mm_loadu_si128((__m128i*)&(p)[1 * (stride)]); \ (e3) = _mm_loadu_si128((__m128i*)&(p)[2 * (stride)]); \ (e4) = _mm_loadu_si128((__m128i*)&(p)[3 * (stride)]); \ -} +} while (0) #define LOADUV_H_EDGE(p, u, v, stride) do { \ const __m128i U = _mm_loadl_epi64((__m128i*)&(u)[(stride)]); \ @@ -658,18 +656,18 @@ static void SimpleHFilter16i_SSE2(uint8_t* p, int stride, int thresh) { (p) = _mm_unpacklo_epi64(U, V); \ } while (0) -#define LOADUV_H_EDGES4(u, v, stride, e1, e2, e3, e4) { \ +#define LOADUV_H_EDGES4(u, v, stride, e1, e2, e3, e4) do { \ LOADUV_H_EDGE(e1, u, v, 0 * (stride)); \ LOADUV_H_EDGE(e2, u, v, 1 * (stride)); \ LOADUV_H_EDGE(e3, u, v, 2 * (stride)); \ LOADUV_H_EDGE(e4, u, v, 3 * (stride)); \ -} +} while (0) -#define STOREUV(p, u, v, stride) { \ +#define STOREUV(p, u, v, stride) do { \ _mm_storel_epi64((__m128i*)&(u)[(stride)], p); \ (p) = _mm_srli_si128(p, 8); \ _mm_storel_epi64((__m128i*)&(v)[(stride)], p); \ -} +} while (0) static WEBP_INLINE void ComplexMask_SSE2(const __m128i* const p1, const __m128i* const p0, diff --git a/thirdparty/libwebp/src/dsp/dsp.h b/thirdparty/libwebp/src/dsp/dsp.h index d2000b8efc..23bc296514 100644 --- a/thirdparty/libwebp/src/dsp/dsp.h +++ b/thirdparty/libwebp/src/dsp/dsp.h @@ -203,6 +203,11 @@ extern VP8DecIdct VP8TransformDC; extern VP8DecIdct VP8TransformDCUV; extern VP8WHT VP8TransformWHT; +#define WEBP_TRANSFORM_AC3_C1 20091 +#define WEBP_TRANSFORM_AC3_C2 35468 +#define WEBP_TRANSFORM_AC3_MUL1(a) ((((a) * WEBP_TRANSFORM_AC3_C1) >> 16) + (a)) +#define WEBP_TRANSFORM_AC3_MUL2(a) (((a) * WEBP_TRANSFORM_AC3_C2) >> 16) + // *dst is the destination block, with stride BPS. Boundary samples are // assumed accessible when needed. typedef void (*VP8PredFunc)(uint8_t* dst); diff --git a/thirdparty/libwebp/src/dsp/enc.c b/thirdparty/libwebp/src/dsp/enc.c index 2ba97ba8d6..395ad05b0b 100644 --- a/thirdparty/libwebp/src/dsp/enc.c +++ b/thirdparty/libwebp/src/dsp/enc.c @@ -109,10 +109,6 @@ static WEBP_TSAN_IGNORE_FUNCTION void InitTables(void) { #define STORE(x, y, v) \ dst[(x) + (y) * BPS] = clip_8b(ref[(x) + (y) * BPS] + ((v) >> 3)) -static const int kC1 = 20091 + (1 << 16); -static const int kC2 = 35468; -#define MUL(a, b) (((a) * (b)) >> 16) - static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, uint8_t* dst) { int C[4 * 4], *tmp; @@ -121,8 +117,10 @@ static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, for (i = 0; i < 4; ++i) { // vertical pass const int a = in[0] + in[8]; const int b = in[0] - in[8]; - const int c = MUL(in[4], kC2) - MUL(in[12], kC1); - const int d = MUL(in[4], kC1) + MUL(in[12], kC2); + const int c = + WEBP_TRANSFORM_AC3_MUL2(in[4]) - WEBP_TRANSFORM_AC3_MUL1(in[12]); + const int d = + WEBP_TRANSFORM_AC3_MUL1(in[4]) + WEBP_TRANSFORM_AC3_MUL2(in[12]); tmp[0] = a + d; tmp[1] = b + c; tmp[2] = b - c; @@ -134,10 +132,12 @@ static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in, tmp = C; for (i = 0; i < 4; ++i) { // horizontal pass const int dc = tmp[0] + 4; - const int a = dc + tmp[8]; - const int b = dc - tmp[8]; - const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1); - const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2); + const int a = dc + tmp[8]; + const int b = dc - tmp[8]; + const int c = + WEBP_TRANSFORM_AC3_MUL2(tmp[4]) - WEBP_TRANSFORM_AC3_MUL1(tmp[12]); + const int d = + WEBP_TRANSFORM_AC3_MUL1(tmp[4]) + WEBP_TRANSFORM_AC3_MUL2(tmp[12]); STORE(0, i, a + d); STORE(1, i, b + c); STORE(2, i, b - c); @@ -222,7 +222,6 @@ static void FTransformWHT_C(const int16_t* in, int16_t* out) { } #endif // !WEBP_NEON_OMIT_C_CODE -#undef MUL #undef STORE //------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/src/dsp/enc_mips32.c b/thirdparty/libwebp/src/dsp/enc_mips32.c index 618f0fc0ee..50518a5f1a 100644 --- a/thirdparty/libwebp/src/dsp/enc_mips32.c +++ b/thirdparty/libwebp/src/dsp/enc_mips32.c @@ -21,8 +21,8 @@ #include "src/enc/vp8i_enc.h" #include "src/enc/cost_enc.h" -static const int kC1 = 20091 + (1 << 16); -static const int kC2 = 35468; +static const int kC1 = WEBP_TRANSFORM_AC3_C1; +static const int kC2 = WEBP_TRANSFORM_AC3_C2; // macro for one vertical pass in ITransformOne // MUL macro inlined @@ -30,7 +30,7 @@ static const int kC2 = 35468; // A..D - offsets in bytes to load from in buffer // TEMP0..TEMP3 - registers for corresponding tmp elements // TEMP4..TEMP5 - temporary registers -#define VERTICAL_PASS(A, B, C, D, TEMP4, TEMP0, TEMP1, TEMP2, TEMP3) \ +#define VERTICAL_PASS(A, B, C, D, TEMP4, TEMP0, TEMP1, TEMP2, TEMP3) \ "lh %[temp16], " #A "(%[temp20]) \n\t" \ "lh %[temp18], " #B "(%[temp20]) \n\t" \ "lh %[temp17], " #C "(%[temp20]) \n\t" \ @@ -38,12 +38,10 @@ static const int kC2 = 35468; "addu %[" #TEMP4 "], %[temp16], %[temp18] \n\t" \ "subu %[temp16], %[temp16], %[temp18] \n\t" \ "mul %[" #TEMP0 "], %[temp17], %[kC2] \n\t" \ - "mul %[temp18], %[temp19], %[kC1] \n\t" \ - "mul %[temp17], %[temp17], %[kC1] \n\t" \ + MUL_SHIFT_C1_IO(temp17, temp18) \ + MUL_SHIFT_C1(temp18, temp19) \ "mul %[temp19], %[temp19], %[kC2] \n\t" \ "sra %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\n" \ - "sra %[temp18], %[temp18], 16 \n\n" \ - "sra %[temp17], %[temp17], 16 \n\n" \ "sra %[temp19], %[temp19], 16 \n\n" \ "subu %[" #TEMP2 "], %[" #TEMP0 "], %[temp18] \n\t" \ "addu %[" #TEMP3 "], %[temp17], %[temp19] \n\t" \ @@ -58,17 +56,15 @@ static const int kC2 = 35468; // temp0..temp15 holds tmp[0]..tmp[15] // A - offset in bytes to load from ref and store to dst buffer // TEMP0, TEMP4, TEMP8 and TEMP12 - registers for corresponding tmp elements -#define HORIZONTAL_PASS(A, TEMP0, TEMP4, TEMP8, TEMP12) \ +#define HORIZONTAL_PASS(A, TEMP0, TEMP4, TEMP8, TEMP12) \ "addiu %[" #TEMP0 "], %[" #TEMP0 "], 4 \n\t" \ "addu %[temp16], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \ "subu %[temp17], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \ "mul %[" #TEMP0 "], %[" #TEMP4 "], %[kC2] \n\t" \ - "mul %[" #TEMP8 "], %[" #TEMP12 "], %[kC1] \n\t" \ - "mul %[" #TEMP4 "], %[" #TEMP4 "], %[kC1] \n\t" \ + MUL_SHIFT_C1_IO(TEMP4, TEMP8) \ + MUL_SHIFT_C1(TEMP8, TEMP12) \ "mul %[" #TEMP12 "], %[" #TEMP12 "], %[kC2] \n\t" \ "sra %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\t" \ - "sra %[" #TEMP8 "], %[" #TEMP8 "], 16 \n\t" \ - "sra %[" #TEMP4 "], %[" #TEMP4 "], 16 \n\t" \ "sra %[" #TEMP12 "], %[" #TEMP12 "], 16 \n\t" \ "subu %[temp18], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \ "addu %[temp19], %[" #TEMP4 "], %[" #TEMP12 "] \n\t" \ diff --git a/thirdparty/libwebp/src/dsp/enc_mips_dsp_r2.c b/thirdparty/libwebp/src/dsp/enc_mips_dsp_r2.c index 9ddd895086..e1431f3bef 100644 --- a/thirdparty/libwebp/src/dsp/enc_mips_dsp_r2.c +++ b/thirdparty/libwebp/src/dsp/enc_mips_dsp_r2.c @@ -20,8 +20,8 @@ #include "src/enc/cost_enc.h" #include "src/enc/vp8i_enc.h" -static const int kC1 = 20091 + (1 << 16); -static const int kC2 = 35468; +static const int kC1 = WEBP_TRANSFORM_AC3_C1; +static const int kC2 = WEBP_TRANSFORM_AC3_C2; // O - output // I - input (macro doesn't change it) diff --git a/thirdparty/libwebp/src/dsp/enc_neon.c b/thirdparty/libwebp/src/dsp/enc_neon.c index 714800367b..6f641c9a76 100644 --- a/thirdparty/libwebp/src/dsp/enc_neon.c +++ b/thirdparty/libwebp/src/dsp/enc_neon.c @@ -27,8 +27,9 @@ // This code is pretty much the same as TransformOne in the dec_neon.c, except // for subtraction to *ref. See the comments there for algorithmic explanations. -static const int16_t kC1 = 20091; -static const int16_t kC2 = 17734; // half of kC2, actually. See comment above. +static const int16_t kC1 = WEBP_TRANSFORM_AC3_C1; +static const int16_t kC2 = + WEBP_TRANSFORM_AC3_C2 / 2; // half of kC2, actually. See comment above. // This code works but is *slower* than the inlined-asm version below // (with gcc-4.6). So we disable it for now. Later, it'll be conditional to diff --git a/thirdparty/libwebp/src/dsp/filters.c b/thirdparty/libwebp/src/dsp/filters.c index 85eee5098f..c9232ff16a 100644 --- a/thirdparty/libwebp/src/dsp/filters.c +++ b/thirdparty/libwebp/src/dsp/filters.c @@ -19,14 +19,16 @@ //------------------------------------------------------------------------------ // Helpful macro. -# define SANITY_CHECK(in, out) \ - assert((in) != NULL); \ - assert((out) != NULL); \ - assert(width > 0); \ - assert(height > 0); \ - assert(stride >= width); \ - assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ - (void)height; // Silence unused warning. +#define DCHECK(in, out) \ + do { \ + assert((in) != NULL); \ + assert((out) != NULL); \ + assert(width > 0); \ + assert(height > 0); \ + assert(stride >= width); \ + assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ + (void)height; /* Silence unused warning. */ \ + } while (0) #if !WEBP_NEON_OMIT_C_CODE static WEBP_INLINE void PredictLine_C(const uint8_t* src, const uint8_t* pred, @@ -49,7 +51,7 @@ static WEBP_INLINE void DoHorizontalFilter_C(const uint8_t* in, const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; preds = inverse ? out : in; @@ -86,7 +88,7 @@ static WEBP_INLINE void DoVerticalFilter_C(const uint8_t* in, const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; preds = inverse ? out : in; @@ -131,7 +133,7 @@ static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in, const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; preds = inverse ? out : in; @@ -165,7 +167,7 @@ static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in, } #endif // !WEBP_NEON_OMIT_C_CODE -#undef SANITY_CHECK +#undef DCHECK //------------------------------------------------------------------------------ @@ -189,6 +191,12 @@ static void GradientFilter_C(const uint8_t* data, int width, int height, //------------------------------------------------------------------------------ +static void NoneUnfilter_C(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + (void)prev; + if (out != in) memcpy(out, in, width * sizeof(*out)); +} + static void HorizontalUnfilter_C(const uint8_t* prev, const uint8_t* in, uint8_t* out, int width) { uint8_t pred = (prev == NULL) ? 0 : prev[0]; @@ -240,7 +248,7 @@ extern void VP8FiltersInitNEON(void); extern void VP8FiltersInitSSE2(void); WEBP_DSP_INIT_FUNC(VP8FiltersInit) { - WebPUnfilters[WEBP_FILTER_NONE] = NULL; + WebPUnfilters[WEBP_FILTER_NONE] = NoneUnfilter_C; #if !WEBP_NEON_OMIT_C_CODE WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_C; WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_C; @@ -279,6 +287,7 @@ WEBP_DSP_INIT_FUNC(VP8FiltersInit) { } #endif + assert(WebPUnfilters[WEBP_FILTER_NONE] != NULL); assert(WebPUnfilters[WEBP_FILTER_HORIZONTAL] != NULL); assert(WebPUnfilters[WEBP_FILTER_VERTICAL] != NULL); assert(WebPUnfilters[WEBP_FILTER_GRADIENT] != NULL); diff --git a/thirdparty/libwebp/src/dsp/filters_mips_dsp_r2.c b/thirdparty/libwebp/src/dsp/filters_mips_dsp_r2.c index 9382b12823..eca866f578 100644 --- a/thirdparty/libwebp/src/dsp/filters_mips_dsp_r2.c +++ b/thirdparty/libwebp/src/dsp/filters_mips_dsp_r2.c @@ -24,14 +24,16 @@ //------------------------------------------------------------------------------ // Helpful macro. -# define SANITY_CHECK(in, out) \ - assert(in != NULL); \ - assert(out != NULL); \ - assert(width > 0); \ - assert(height > 0); \ - assert(stride >= width); \ - assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ - (void)height; // Silence unused warning. +#define DCHECK(in, out) \ + do { \ + assert(in != NULL); \ + assert(out != NULL); \ + assert(width > 0); \ + assert(height > 0); \ + assert(stride >= width); \ + assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ + (void)height; /* Silence unused warning. */ \ + } while (0) #define DO_PREDICT_LINE(SRC, DST, LENGTH, INVERSE) do { \ const uint8_t* psrc = (uint8_t*)(SRC); \ @@ -200,7 +202,7 @@ static WEBP_INLINE void DoHorizontalFilter_MIPSdspR2(const uint8_t* in, const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; preds = in; @@ -248,7 +250,7 @@ static WEBP_INLINE void DoVerticalFilter_MIPSdspR2(const uint8_t* in, const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; preds = in; @@ -316,7 +318,7 @@ static void DoGradientFilter_MIPSdspR2(const uint8_t* in, const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; preds = in; @@ -378,7 +380,7 @@ static void GradientUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in, #undef DO_PREDICT_LINE_VERTICAL #undef PREDICT_LINE_ONE_PASS #undef DO_PREDICT_LINE -#undef SANITY_CHECK +#undef DCHECK //------------------------------------------------------------------------------ // Entry point diff --git a/thirdparty/libwebp/src/dsp/filters_msa.c b/thirdparty/libwebp/src/dsp/filters_msa.c index 14c437d141..33a1b20b70 100644 --- a/thirdparty/libwebp/src/dsp/filters_msa.c +++ b/thirdparty/libwebp/src/dsp/filters_msa.c @@ -56,12 +56,14 @@ static WEBP_INLINE void PredictLineInverse0(const uint8_t* src, //------------------------------------------------------------------------------ // Helpful macro. -#define SANITY_CHECK(in, out) \ - assert(in != NULL); \ - assert(out != NULL); \ - assert(width > 0); \ - assert(height > 0); \ - assert(stride >= width); +#define DCHECK(in, out) \ + do { \ + assert(in != NULL); \ + assert(out != NULL); \ + assert(width > 0); \ + assert(height > 0); \ + assert(stride >= width); \ + } while (0) //------------------------------------------------------------------------------ // Horrizontal filter @@ -72,7 +74,7 @@ static void HorizontalFilter_MSA(const uint8_t* data, int width, int height, const uint8_t* in = data; uint8_t* out = filtered_data; int row = 1; - SANITY_CHECK(in, out); + DCHECK(in, out); // Leftmost pixel is the same as input for topmost scanline. out[0] = in[0]; @@ -135,7 +137,7 @@ static void GradientFilter_MSA(const uint8_t* data, int width, int height, const uint8_t* preds = data; uint8_t* out = filtered_data; int row = 1; - SANITY_CHECK(in, out); + DCHECK(in, out); // left prediction for top scan-line out[0] = in[0]; @@ -163,7 +165,7 @@ static void VerticalFilter_MSA(const uint8_t* data, int width, int height, const uint8_t* preds = data; uint8_t* out = filtered_data; int row = 1; - SANITY_CHECK(in, out); + DCHECK(in, out); // Very first top-left pixel is copied. out[0] = in[0]; @@ -182,7 +184,7 @@ static void VerticalFilter_MSA(const uint8_t* data, int width, int height, } } -#undef SANITY_CHECK +#undef DCHECK //------------------------------------------------------------------------------ // Entry point diff --git a/thirdparty/libwebp/src/dsp/filters_neon.c b/thirdparty/libwebp/src/dsp/filters_neon.c index 3e6a578ea7..b49e515af1 100644 --- a/thirdparty/libwebp/src/dsp/filters_neon.c +++ b/thirdparty/libwebp/src/dsp/filters_neon.c @@ -21,14 +21,16 @@ //------------------------------------------------------------------------------ // Helpful macros. -# define SANITY_CHECK(in, out) \ - assert(in != NULL); \ - assert(out != NULL); \ - assert(width > 0); \ - assert(height > 0); \ - assert(stride >= width); \ - assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ - (void)height; // Silence unused warning. +#define DCHECK(in, out) \ + do { \ + assert(in != NULL); \ + assert(out != NULL); \ + assert(width > 0); \ + assert(height > 0); \ + assert(stride >= width); \ + assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ + (void)height; /* Silence unused warning. */ \ + } while (0) // load eight u8 and widen to s16 #define U8_TO_S16(A) vreinterpretq_s16_u16(vmovl_u8(A)) @@ -71,7 +73,7 @@ static WEBP_INLINE void DoHorizontalFilter_NEON(const uint8_t* in, uint8_t* out) { const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; @@ -110,7 +112,7 @@ static WEBP_INLINE void DoVerticalFilter_NEON(const uint8_t* in, uint8_t* out) { const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; @@ -172,7 +174,7 @@ static WEBP_INLINE void DoGradientFilter_NEON(const uint8_t* in, uint8_t* out) { const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; @@ -201,7 +203,7 @@ static void GradientFilter_NEON(const uint8_t* data, int width, int height, filtered_data); } -#undef SANITY_CHECK +#undef DCHECK //------------------------------------------------------------------------------ // Inverse transforms diff --git a/thirdparty/libwebp/src/dsp/filters_sse2.c b/thirdparty/libwebp/src/dsp/filters_sse2.c index 5c33ec15e2..bb4b5d5874 100644 --- a/thirdparty/libwebp/src/dsp/filters_sse2.c +++ b/thirdparty/libwebp/src/dsp/filters_sse2.c @@ -23,14 +23,16 @@ //------------------------------------------------------------------------------ // Helpful macro. -# define SANITY_CHECK(in, out) \ - assert((in) != NULL); \ - assert((out) != NULL); \ - assert(width > 0); \ - assert(height > 0); \ - assert(stride >= width); \ - assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ - (void)height; // Silence unused warning. +#define DCHECK(in, out) \ + do { \ + assert((in) != NULL); \ + assert((out) != NULL); \ + assert(width > 0); \ + assert(height > 0); \ + assert(stride >= width); \ + assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ + (void)height; /* Silence unused warning. */ \ + } while (0) static void PredictLineTop_SSE2(const uint8_t* src, const uint8_t* pred, uint8_t* dst, int length) { @@ -78,7 +80,7 @@ static WEBP_INLINE void DoHorizontalFilter_SSE2(const uint8_t* in, uint8_t* out) { const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; @@ -111,7 +113,7 @@ static WEBP_INLINE void DoVerticalFilter_SSE2(const uint8_t* in, uint8_t* out) { const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; @@ -174,7 +176,7 @@ static WEBP_INLINE void DoGradientFilter_SSE2(const uint8_t* in, uint8_t* out) { const size_t start_offset = row * stride; const int last_row = row + num_rows; - SANITY_CHECK(in, out); + DCHECK(in, out); in += start_offset; out += start_offset; @@ -197,7 +199,7 @@ static WEBP_INLINE void DoGradientFilter_SSE2(const uint8_t* in, } } -#undef SANITY_CHECK +#undef DCHECK //------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/src/dsp/lossless.h b/thirdparty/libwebp/src/dsp/lossless.h index de60d95d0b..0bf10a1a3d 100644 --- a/thirdparty/libwebp/src/dsp/lossless.h +++ b/thirdparty/libwebp/src/dsp/lossless.h @@ -182,9 +182,9 @@ extern VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16]; // ----------------------------------------------------------------------------- // Huffman-cost related functions. -typedef float (*VP8LCostFunc)(const uint32_t* population, int length); -typedef float (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y, - int length); +typedef uint32_t (*VP8LCostFunc)(const uint32_t* population, int length); +typedef uint32_t (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y, + int length); typedef float (*VP8LCombinedShannonEntropyFunc)(const int X[256], const int Y[256]); diff --git a/thirdparty/libwebp/src/dsp/lossless_common.h b/thirdparty/libwebp/src/dsp/lossless_common.h index 6a2f736b5e..d6139b2b57 100644 --- a/thirdparty/libwebp/src/dsp/lossless_common.h +++ b/thirdparty/libwebp/src/dsp/lossless_common.h @@ -16,9 +16,9 @@ #ifndef WEBP_DSP_LOSSLESS_COMMON_H_ #define WEBP_DSP_LOSSLESS_COMMON_H_ -#include "src/webp/types.h" - +#include "src/dsp/cpu.h" #include "src/utils/utils.h" +#include "src/webp/types.h" #ifdef __cplusplus extern "C" { @@ -166,7 +166,7 @@ uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { } //------------------------------------------------------------------------------ -// Transform-related functions use din both encoding and decoding. +// Transform-related functions used in both encoding and decoding. // Macros used to create a batch predictor that iteratively uses a // one-pixel predictor. diff --git a/thirdparty/libwebp/src/dsp/lossless_enc.c b/thirdparty/libwebp/src/dsp/lossless_enc.c index cde1280617..997d56c2ad 100644 --- a/thirdparty/libwebp/src/dsp/lossless_enc.c +++ b/thirdparty/libwebp/src/dsp/lossless_enc.c @@ -636,20 +636,25 @@ void VP8LBundleColorMap_C(const uint8_t* const row, int width, int xbits, //------------------------------------------------------------------------------ -static float ExtraCost_C(const uint32_t* population, int length) { +static uint32_t ExtraCost_C(const uint32_t* population, int length) { int i; - float cost = 0.f; - for (i = 2; i < length - 2; ++i) cost += (i >> 1) * population[i + 2]; + uint32_t cost = population[4] + population[5]; + assert(length % 2 == 0); + for (i = 2; i < length / 2 - 1; ++i) { + cost += i * (population[2 * i + 2] + population[2 * i + 3]); + } return cost; } -static float ExtraCostCombined_C(const uint32_t* X, const uint32_t* Y, - int length) { +static uint32_t ExtraCostCombined_C(const uint32_t* X, const uint32_t* Y, + int length) { int i; - float cost = 0.f; - for (i = 2; i < length - 2; ++i) { - const int xy = X[i + 2] + Y[i + 2]; - cost += (i >> 1) * xy; + uint32_t cost = X[4] + Y[4] + X[5] + Y[5]; + assert(length % 2 == 0); + for (i = 2; i < length / 2 - 1; ++i) { + const int xy0 = X[2 * i + 2] + Y[2 * i + 2]; + const int xy1 = X[2 * i + 3] + Y[2 * i + 3]; + cost += i * (xy0 + xy1); } return cost; } diff --git a/thirdparty/libwebp/src/dsp/lossless_enc_mips32.c b/thirdparty/libwebp/src/dsp/lossless_enc_mips32.c index 639f786631..e10f12da9d 100644 --- a/thirdparty/libwebp/src/dsp/lossless_enc_mips32.c +++ b/thirdparty/libwebp/src/dsp/lossless_enc_mips32.c @@ -103,8 +103,8 @@ static float FastLog2Slow_MIPS32(uint32_t v) { // cost += i * *(pop + 1); // pop += 2; // } -// return (float)cost; -static float ExtraCost_MIPS32(const uint32_t* const population, int length) { +// return cost; +static uint32_t ExtraCost_MIPS32(const uint32_t* const population, int length) { int i, temp0, temp1; const uint32_t* pop = &population[4]; const uint32_t* const LoopEnd = &population[length]; @@ -130,7 +130,7 @@ static float ExtraCost_MIPS32(const uint32_t* const population, int length) { : "memory", "hi", "lo" ); - return (float)((int64_t)temp0 << 32 | temp1); + return ((int64_t)temp0 << 32 | temp1); } // C version of this function: @@ -148,9 +148,9 @@ static float ExtraCost_MIPS32(const uint32_t* const population, int length) { // pX += 2; // pY += 2; // } -// return (float)cost; -static float ExtraCostCombined_MIPS32(const uint32_t* const X, - const uint32_t* const Y, int length) { +// return cost; +static uint32_t ExtraCostCombined_MIPS32(const uint32_t* const X, + const uint32_t* const Y, int length) { int i, temp0, temp1, temp2, temp3; const uint32_t* pX = &X[4]; const uint32_t* pY = &Y[4]; @@ -183,7 +183,7 @@ static float ExtraCostCombined_MIPS32(const uint32_t* const X, : "memory", "hi", "lo" ); - return (float)((int64_t)temp0 << 32 | temp1); + return ((int64_t)temp0 << 32 | temp1); } #define HUFFMAN_COST_PASS \ diff --git a/thirdparty/libwebp/src/dsp/lossless_enc_sse41.c b/thirdparty/libwebp/src/dsp/lossless_enc_sse41.c index ad358a6f25..7ab83c2604 100644 --- a/thirdparty/libwebp/src/dsp/lossless_enc_sse41.c +++ b/thirdparty/libwebp/src/dsp/lossless_enc_sse41.c @@ -18,8 +18,53 @@ #include <smmintrin.h> #include "src/dsp/lossless.h" -// For sign-extended multiplying constants, pre-shifted by 5: -#define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5) +//------------------------------------------------------------------------------ +// Cost operations. + +static WEBP_INLINE uint32_t HorizontalSum_SSE41(__m128i cost) { + cost = _mm_add_epi32(cost, _mm_srli_si128(cost, 8)); + cost = _mm_add_epi32(cost, _mm_srli_si128(cost, 4)); + return _mm_cvtsi128_si32(cost); +} + +static uint32_t ExtraCost_SSE41(const uint32_t* const a, int length) { + int i; + __m128i cost = _mm_set_epi32(2 * a[7], 2 * a[6], a[5], a[4]); + assert(length % 8 == 0); + + for (i = 8; i + 8 <= length; i += 8) { + const int j = (i - 2) >> 1; + const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i]); + const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]); + const __m128i w = _mm_set_epi32(j + 3, j + 2, j + 1, j); + const __m128i a2 = _mm_hadd_epi32(a0, a1); + const __m128i mul = _mm_mullo_epi32(a2, w); + cost = _mm_add_epi32(mul, cost); + } + return HorizontalSum_SSE41(cost); +} + +static uint32_t ExtraCostCombined_SSE41(const uint32_t* const a, + const uint32_t* const b, int length) { + int i; + __m128i cost = _mm_add_epi32(_mm_set_epi32(2 * a[7], 2 * a[6], a[5], a[4]), + _mm_set_epi32(2 * b[7], 2 * b[6], b[5], b[4])); + assert(length % 8 == 0); + + for (i = 8; i + 8 <= length; i += 8) { + const int j = (i - 2) >> 1; + const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i]); + const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]); + const __m128i b0 = _mm_loadu_si128((const __m128i*)&b[i]); + const __m128i b1 = _mm_loadu_si128((const __m128i*)&b[i + 4]); + const __m128i w = _mm_set_epi32(j + 3, j + 2, j + 1, j); + const __m128i a2 = _mm_hadd_epi32(a0, a1); + const __m128i b2 = _mm_hadd_epi32(b0, b1); + const __m128i mul = _mm_mullo_epi32(_mm_add_epi32(a2, b2), w); + cost = _mm_add_epi32(mul, cost); + } + return HorizontalSum_SSE41(cost); +} //------------------------------------------------------------------------------ // Subtract-Green Transform @@ -44,6 +89,9 @@ static void SubtractGreenFromBlueAndRed_SSE41(uint32_t* argb_data, //------------------------------------------------------------------------------ // Color Transform +// For sign-extended multiplying constants, pre-shifted by 5: +#define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5) + #define MK_CST_16(HI, LO) \ _mm_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff))) @@ -143,6 +191,8 @@ static void CollectColorRedTransforms_SSE41(const uint32_t* argb, int stride, extern void VP8LEncDspInitSSE41(void); WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE41(void) { + VP8LExtraCost = ExtraCost_SSE41; + VP8LExtraCostCombined = ExtraCostCombined_SSE41; VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_SSE41; VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_SSE41; VP8LCollectColorRedTransforms = CollectColorRedTransforms_SSE41; diff --git a/thirdparty/libwebp/src/dsp/lossless_neon.c b/thirdparty/libwebp/src/dsp/lossless_neon.c index ddc9b61711..e9960db38a 100644 --- a/thirdparty/libwebp/src/dsp/lossless_neon.c +++ b/thirdparty/libwebp/src/dsp/lossless_neon.c @@ -146,9 +146,9 @@ static void ConvertBGRAToRGB_NEON(const uint32_t* src, #define LOAD_U32P_AS_U8(IN) vreinterpret_u8_u32(vld1_u32((IN))) #define LOADQ_U32_AS_U8(IN) vreinterpretq_u8_u32(vdupq_n_u32((IN))) #define LOADQ_U32P_AS_U8(IN) vreinterpretq_u8_u32(vld1q_u32((IN))) -#define GET_U8_AS_U32(IN) vget_lane_u32(vreinterpret_u32_u8((IN)), 0); -#define GETQ_U8_AS_U32(IN) vgetq_lane_u32(vreinterpretq_u32_u8((IN)), 0); -#define STOREQ_U8_AS_U32P(OUT, IN) vst1q_u32((OUT), vreinterpretq_u32_u8((IN))); +#define GET_U8_AS_U32(IN) vget_lane_u32(vreinterpret_u32_u8((IN)), 0) +#define GETQ_U8_AS_U32(IN) vgetq_lane_u32(vreinterpretq_u32_u8((IN)), 0) +#define STOREQ_U8_AS_U32P(OUT, IN) vst1q_u32((OUT), vreinterpretq_u32_u8((IN))) #define ROTATE32_LEFT(L) vextq_u8((L), (L), 12) // D|C|B|A -> C|B|A|D static WEBP_INLINE uint8x8_t Average2_u8_NEON(uint32_t a0, uint32_t a1) { diff --git a/thirdparty/libwebp/src/dsp/mips_macro.h b/thirdparty/libwebp/src/dsp/mips_macro.h index 44aba9b71d..e810d3d382 100644 --- a/thirdparty/libwebp/src/dsp/mips_macro.h +++ b/thirdparty/libwebp/src/dsp/mips_macro.h @@ -45,28 +45,38 @@ "ulw %[" #O2 "], " #I3 "+" XSTR(I9) "*" #I7 "(%[" #I0 "]) \n\t" \ "ulw %[" #O3 "], " #I4 "+" XSTR(I9) "*" #I8 "(%[" #I0 "]) \n\t" + +// O - output +// I - input (macro doesn't change it so it should be different from I) +#define MUL_SHIFT_C1(O, I) \ + "mul %[" #O "], %[" #I "], %[kC1] \n\t" \ + "sra %[" #O "], %[" #O "], 16 \n\t" \ + "addu %[" #O "], %[" #O "], %[" #I "] \n\t" +#define MUL_SHIFT_C2(O, I) \ + "mul %[" #O "], %[" #I "], %[kC2] \n\t" \ + "sra %[" #O "], %[" #O "], 16 \n\t" + +// Same as #define MUL_SHIFT_C1 but I and O are the same. It stores the +// intermediary result in TMP. +#define MUL_SHIFT_C1_IO(IO, TMP) \ + "mul %[" #TMP "], %[" #IO "], %[kC1] \n\t" \ + "sra %[" #TMP "], %[" #TMP "], 16 \n\t" \ + "addu %[" #IO "], %[" #TMP "], %[" #IO "] \n\t" + // O - output // IO - input/output // I - input (macro doesn't change it) #define MUL_SHIFT_SUM(O0, O1, O2, O3, O4, O5, O6, O7, \ IO0, IO1, IO2, IO3, \ I0, I1, I2, I3, I4, I5, I6, I7) \ - "mul %[" #O0 "], %[" #I0 "], %[kC2] \n\t" \ - "mul %[" #O1 "], %[" #I0 "], %[kC1] \n\t" \ - "mul %[" #O2 "], %[" #I1 "], %[kC2] \n\t" \ - "mul %[" #O3 "], %[" #I1 "], %[kC1] \n\t" \ - "mul %[" #O4 "], %[" #I2 "], %[kC2] \n\t" \ - "mul %[" #O5 "], %[" #I2 "], %[kC1] \n\t" \ - "mul %[" #O6 "], %[" #I3 "], %[kC2] \n\t" \ - "mul %[" #O7 "], %[" #I3 "], %[kC1] \n\t" \ - "sra %[" #O0 "], %[" #O0 "], 16 \n\t" \ - "sra %[" #O1 "], %[" #O1 "], 16 \n\t" \ - "sra %[" #O2 "], %[" #O2 "], 16 \n\t" \ - "sra %[" #O3 "], %[" #O3 "], 16 \n\t" \ - "sra %[" #O4 "], %[" #O4 "], 16 \n\t" \ - "sra %[" #O5 "], %[" #O5 "], 16 \n\t" \ - "sra %[" #O6 "], %[" #O6 "], 16 \n\t" \ - "sra %[" #O7 "], %[" #O7 "], 16 \n\t" \ + MUL_SHIFT_C2(O0, I0) \ + MUL_SHIFT_C1(O1, I0) \ + MUL_SHIFT_C2(O2, I1) \ + MUL_SHIFT_C1(O3, I1) \ + MUL_SHIFT_C2(O4, I2) \ + MUL_SHIFT_C1(O5, I2) \ + MUL_SHIFT_C2(O6, I3) \ + MUL_SHIFT_C1(O7, I3) \ "addu %[" #IO0 "], %[" #IO0 "], %[" #I4 "] \n\t" \ "addu %[" #IO1 "], %[" #IO1 "], %[" #I5 "] \n\t" \ "subu %[" #IO2 "], %[" #IO2 "], %[" #I6 "] \n\t" \ diff --git a/thirdparty/libwebp/src/dsp/msa_macro.h b/thirdparty/libwebp/src/dsp/msa_macro.h index 51f6c643ab..90adbbc319 100644 --- a/thirdparty/libwebp/src/dsp/msa_macro.h +++ b/thirdparty/libwebp/src/dsp/msa_macro.h @@ -73,27 +73,25 @@ #define ST_UW(...) ST_W(v4u32, __VA_ARGS__) #define ST_SW(...) ST_W(v4i32, __VA_ARGS__) -#define MSA_LOAD_FUNC(TYPE, INSTR, FUNC_NAME) \ - static inline TYPE FUNC_NAME(const void* const psrc) { \ - const uint8_t* const psrc_m = (const uint8_t*)psrc; \ - TYPE val_m; \ - asm volatile ( \ - "" #INSTR " %[val_m], %[psrc_m] \n\t" \ - : [val_m] "=r" (val_m) \ - : [psrc_m] "m" (*psrc_m)); \ - return val_m; \ +#define MSA_LOAD_FUNC(TYPE, INSTR, FUNC_NAME) \ + static inline TYPE FUNC_NAME(const void* const psrc) { \ + const uint8_t* const psrc_m = (const uint8_t*)psrc; \ + TYPE val_m; \ + __asm__ volatile("" #INSTR " %[val_m], %[psrc_m] \n\t" \ + : [val_m] "=r"(val_m) \ + : [psrc_m] "m"(*psrc_m)); \ + return val_m; \ } #define MSA_LOAD(psrc, FUNC_NAME) FUNC_NAME(psrc) -#define MSA_STORE_FUNC(TYPE, INSTR, FUNC_NAME) \ - static inline void FUNC_NAME(TYPE val, void* const pdst) { \ - uint8_t* const pdst_m = (uint8_t*)pdst; \ - TYPE val_m = val; \ - asm volatile ( \ - " " #INSTR " %[val_m], %[pdst_m] \n\t" \ - : [pdst_m] "=m" (*pdst_m) \ - : [val_m] "r" (val_m)); \ +#define MSA_STORE_FUNC(TYPE, INSTR, FUNC_NAME) \ + static inline void FUNC_NAME(TYPE val, void* const pdst) { \ + uint8_t* const pdst_m = (uint8_t*)pdst; \ + TYPE val_m = val; \ + __asm__ volatile(" " #INSTR " %[val_m], %[pdst_m] \n\t" \ + : [pdst_m] "=m"(*pdst_m) \ + : [val_m] "r"(val_m)); \ } #define MSA_STORE(val, pdst, FUNC_NAME) FUNC_NAME(val, pdst) diff --git a/thirdparty/libwebp/src/dsp/quant.h b/thirdparty/libwebp/src/dsp/quant.h index bf7734cb11..dcbc11c77c 100644 --- a/thirdparty/libwebp/src/dsp/quant.h +++ b/thirdparty/libwebp/src/dsp/quant.h @@ -36,8 +36,9 @@ static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks, int thresh) { const int16x8_t tst_ones = vdupq_n_s16(-1); uint32x4_t sum = vdupq_n_u32(0); + int i; - for (int i = 0; i < num_blocks; ++i) { + for (i = 0; i < num_blocks; ++i) { // Set DC to zero. const int16x8_t a_0 = vsetq_lane_s16(0, vld1q_s16(levels), 0); const int16x8_t a_1 = vld1q_s16(levels + 8); diff --git a/thirdparty/libwebp/src/dsp/rescaler_neon.c b/thirdparty/libwebp/src/dsp/rescaler_neon.c index b976a852cf..957a92dbc9 100644 --- a/thirdparty/libwebp/src/dsp/rescaler_neon.c +++ b/thirdparty/libwebp/src/dsp/rescaler_neon.c @@ -32,7 +32,7 @@ #define STORE_32x8(SRC0, SRC1, DST) do { \ vst1q_u32((DST) + 0, SRC0); \ vst1q_u32((DST) + 4, SRC1); \ -} while (0); +} while (0) #if (WEBP_RESCALER_RFIX == 32) #define MAKE_HALF_CST(C) vdupq_n_s32((int32_t)((C) >> 1)) diff --git a/thirdparty/libwebp/src/dsp/upsampling_sse2.c b/thirdparty/libwebp/src/dsp/upsampling_sse2.c index 08b6d0b1cf..77b4f7221e 100644 --- a/thirdparty/libwebp/src/dsp/upsampling_sse2.c +++ b/thirdparty/libwebp/src/dsp/upsampling_sse2.c @@ -58,7 +58,7 @@ } while (0) // Loads 17 pixels each from rows r1 and r2 and generates 32 pixels. -#define UPSAMPLE_32PIXELS(r1, r2, out) { \ +#define UPSAMPLE_32PIXELS(r1, r2, out) do { \ const __m128i one = _mm_set1_epi8(1); \ const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \ const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \ @@ -85,7 +85,7 @@ /* pack the alternate pixels */ \ PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \ PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \ -} +} while (0) // Turn the macro into a function for reducing code-size when non-critical static void Upsample32Pixels_SSE2(const uint8_t r1[], const uint8_t r2[], @@ -229,11 +229,11 @@ static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \ } \ } -YUV444_FUNC(Yuv444ToRgba_SSE2, VP8YuvToRgba32_SSE2, WebPYuv444ToRgba_C, 4); -YUV444_FUNC(Yuv444ToBgra_SSE2, VP8YuvToBgra32_SSE2, WebPYuv444ToBgra_C, 4); +YUV444_FUNC(Yuv444ToRgba_SSE2, VP8YuvToRgba32_SSE2, WebPYuv444ToRgba_C, 4) +YUV444_FUNC(Yuv444ToBgra_SSE2, VP8YuvToBgra32_SSE2, WebPYuv444ToBgra_C, 4) #if !defined(WEBP_REDUCE_CSP) -YUV444_FUNC(Yuv444ToRgb_SSE2, VP8YuvToRgb32_SSE2, WebPYuv444ToRgb_C, 3); -YUV444_FUNC(Yuv444ToBgr_SSE2, VP8YuvToBgr32_SSE2, WebPYuv444ToBgr_C, 3); +YUV444_FUNC(Yuv444ToRgb_SSE2, VP8YuvToRgb32_SSE2, WebPYuv444ToRgb_C, 3) +YUV444_FUNC(Yuv444ToBgr_SSE2, VP8YuvToBgr32_SSE2, WebPYuv444ToBgr_C, 3) YUV444_FUNC(Yuv444ToArgb_SSE2, VP8YuvToArgb32_SSE2, WebPYuv444ToArgb_C, 4) YUV444_FUNC(Yuv444ToRgba4444_SSE2, VP8YuvToRgba444432_SSE2, \ WebPYuv444ToRgba4444_C, 2) diff --git a/thirdparty/libwebp/src/dsp/upsampling_sse41.c b/thirdparty/libwebp/src/dsp/upsampling_sse41.c index 648d456027..e38c88d5e6 100644 --- a/thirdparty/libwebp/src/dsp/upsampling_sse41.c +++ b/thirdparty/libwebp/src/dsp/upsampling_sse41.c @@ -60,7 +60,7 @@ } while (0) // Loads 17 pixels each from rows r1 and r2 and generates 32 pixels. -#define UPSAMPLE_32PIXELS(r1, r2, out) { \ +#define UPSAMPLE_32PIXELS(r1, r2, out) do { \ const __m128i one = _mm_set1_epi8(1); \ const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \ const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \ @@ -87,7 +87,7 @@ /* pack the alternate pixels */ \ PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \ PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \ -} +} while (0) // Turn the macro into a function for reducing code-size when non-critical static void Upsample32Pixels_SSE41(const uint8_t r1[], const uint8_t r2[], @@ -217,8 +217,8 @@ static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \ } #if !defined(WEBP_REDUCE_CSP) -YUV444_FUNC(Yuv444ToRgb_SSE41, VP8YuvToRgb32_SSE41, WebPYuv444ToRgb_C, 3); -YUV444_FUNC(Yuv444ToBgr_SSE41, VP8YuvToBgr32_SSE41, WebPYuv444ToBgr_C, 3); +YUV444_FUNC(Yuv444ToRgb_SSE41, VP8YuvToRgb32_SSE41, WebPYuv444ToRgb_C, 3) +YUV444_FUNC(Yuv444ToBgr_SSE41, VP8YuvToBgr32_SSE41, WebPYuv444ToBgr_C, 3) #endif // WEBP_REDUCE_CSP WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersSSE41(void) { diff --git a/thirdparty/libwebp/src/enc/alpha_enc.c b/thirdparty/libwebp/src/enc/alpha_enc.c index 26f003485a..c11a261c8a 100644 --- a/thirdparty/libwebp/src/enc/alpha_enc.c +++ b/thirdparty/libwebp/src/enc/alpha_enc.c @@ -20,6 +20,7 @@ #include "src/utils/filters_utils.h" #include "src/utils/quant_levels_utils.h" #include "src/utils/utils.h" +#include "src/webp/encode.h" #include "src/webp/format_constants.h" // ----------------------------------------------------------------------------- @@ -55,7 +56,7 @@ static int EncodeLossless(const uint8_t* const data, int width, int height, WebPConfig config; WebPPicture picture; - WebPPictureInit(&picture); + if (!WebPPictureInit(&picture)) return 0; picture.width = width; picture.height = height; picture.use_argb = 1; @@ -66,7 +67,7 @@ static int EncodeLossless(const uint8_t* const data, int width, int height, WebPDispatchAlphaToGreen(data, width, picture.width, picture.height, picture.argb, picture.argb_stride); - WebPConfigInit(&config); + if (!WebPConfigInit(&config)) return 0; config.lossless = 1; // Enable exact, or it would alter RGB values of transparent alpha, which is // normally OK but not here since we are not encoding the input image but an @@ -83,11 +84,7 @@ static int EncodeLossless(const uint8_t* const data, int width, int height, (use_quality_100 && effort_level == 6) ? 100 : 8.f * effort_level; assert(config.quality >= 0 && config.quality <= 100.f); - // TODO(urvang): Temporary fix to avoid generating images that trigger - // a decoder bug related to alpha with color cache. - // See: https://code.google.com/p/webp/issues/detail?id=239 - // Need to re-enable this later. - ok = VP8LEncodeStream(&config, &picture, bw, /*use_cache=*/0); + ok = VP8LEncodeStream(&config, &picture, bw); WebPPictureFree(&picture); ok = ok && !bw->error_; if (!ok) { diff --git a/thirdparty/libwebp/src/enc/frame_enc.c b/thirdparty/libwebp/src/enc/frame_enc.c index 9a98dc1f3e..01860ca757 100644 --- a/thirdparty/libwebp/src/enc/frame_enc.c +++ b/thirdparty/libwebp/src/enc/frame_enc.c @@ -578,7 +578,7 @@ static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt, uint64_t size = 0; uint64_t size_p0 = 0; uint64_t distortion = 0; - const uint64_t pixel_count = nb_mbs * 384; + const uint64_t pixel_count = (uint64_t)nb_mbs * 384; VP8IteratorInit(enc, &it); SetLoopParams(enc, s->q); @@ -789,7 +789,7 @@ int VP8EncTokenLoop(VP8Encoder* const enc) { VP8EncIterator it; VP8EncProba* const proba = &enc->proba_; const VP8RDLevel rd_opt = enc->rd_opt_level_; - const uint64_t pixel_count = enc->mb_w_ * enc->mb_h_ * 384; + const uint64_t pixel_count = (uint64_t)enc->mb_w_ * enc->mb_h_ * 384; PassStats stats; int ok; diff --git a/thirdparty/libwebp/src/enc/histogram_enc.c b/thirdparty/libwebp/src/enc/histogram_enc.c index 8418def2e1..3ca67b3ad0 100644 --- a/thirdparty/libwebp/src/enc/histogram_enc.c +++ b/thirdparty/libwebp/src/enc/histogram_enc.c @@ -358,15 +358,17 @@ static WEBP_INLINE float GetCombinedEntropy(const uint32_t* const X, // Estimates the Entropy + Huffman + other block overhead size cost. float VP8LHistogramEstimateBits(VP8LHistogram* const p) { - return - PopulationCost(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), - NULL, &p->is_used_[0]) - + PopulationCost(p->red_, NUM_LITERAL_CODES, NULL, &p->is_used_[1]) - + PopulationCost(p->blue_, NUM_LITERAL_CODES, NULL, &p->is_used_[2]) - + PopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL, &p->is_used_[3]) - + PopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL, &p->is_used_[4]) - + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES) - + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); + return PopulationCost(p->literal_, + VP8LHistogramNumCodes(p->palette_code_bits_), NULL, + &p->is_used_[0]) + + PopulationCost(p->red_, NUM_LITERAL_CODES, NULL, &p->is_used_[1]) + + PopulationCost(p->blue_, NUM_LITERAL_CODES, NULL, &p->is_used_[2]) + + PopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL, &p->is_used_[3]) + + PopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL, + &p->is_used_[4]) + + (float)VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, + NUM_LENGTH_CODES) + + (float)VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); } // ----------------------------------------------------------------------------- @@ -381,9 +383,9 @@ static int GetCombinedHistogramEntropy(const VP8LHistogram* const a, *cost += GetCombinedEntropy(a->literal_, b->literal_, VP8LHistogramNumCodes(palette_code_bits), a->is_used_[0], b->is_used_[0], 0); - *cost += VP8LExtraCostCombined(a->literal_ + NUM_LITERAL_CODES, - b->literal_ + NUM_LITERAL_CODES, - NUM_LENGTH_CODES); + *cost += (float)VP8LExtraCostCombined(a->literal_ + NUM_LITERAL_CODES, + b->literal_ + NUM_LITERAL_CODES, + NUM_LENGTH_CODES); if (*cost > cost_threshold) return 0; if (a->trivial_symbol_ != VP8L_NON_TRIVIAL_SYM && @@ -417,8 +419,8 @@ static int GetCombinedHistogramEntropy(const VP8LHistogram* const a, *cost += GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES, a->is_used_[4], b->is_used_[4], 0); - *cost += - VP8LExtraCostCombined(a->distance_, b->distance_, NUM_DISTANCE_CODES); + *cost += (float)VP8LExtraCostCombined(a->distance_, b->distance_, + NUM_DISTANCE_CODES); if (*cost > cost_threshold) return 0; return 1; @@ -506,11 +508,11 @@ static void UpdateHistogramCost(VP8LHistogram* const h) { PopulationCost(h->alpha_, NUM_LITERAL_CODES, &alpha_sym, &h->is_used_[3]); const float distance_cost = PopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL, &h->is_used_[4]) + - VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES); + (float)VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES); const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_); h->literal_cost_ = PopulationCost(h->literal_, num_codes, NULL, &h->is_used_[0]) + - VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES); + (float)VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES); h->red_cost_ = PopulationCost(h->red_, NUM_LITERAL_CODES, &red_sym, &h->is_used_[1]); h->blue_cost_ = @@ -1179,7 +1181,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize, const int entropy_combine_num_bins = low_effort ? NUM_PARTITIONS : BIN_SIZE; int entropy_combine; uint16_t* const map_tmp = - WebPSafeMalloc(2 * image_histo_raw_size, sizeof(map_tmp)); + WebPSafeMalloc(2 * image_histo_raw_size, sizeof(*map_tmp)); uint16_t* const cluster_mappings = map_tmp + image_histo_raw_size; int num_used = image_histo_raw_size; if (orig_histo == NULL || map_tmp == NULL) { diff --git a/thirdparty/libwebp/src/enc/picture_enc.c b/thirdparty/libwebp/src/enc/picture_enc.c index 3af6383d38..5a2703541f 100644 --- a/thirdparty/libwebp/src/enc/picture_enc.c +++ b/thirdparty/libwebp/src/enc/picture_enc.c @@ -12,10 +12,10 @@ // Author: Skal (pascal.massimino@gmail.com) #include <assert.h> +#include <limits.h> #include <stdlib.h> #include "src/enc/vp8i_enc.h" -#include "src/dsp/dsp.h" #include "src/utils/utils.h" //------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/src/enc/vp8i_enc.h b/thirdparty/libwebp/src/enc/vp8i_enc.h index 0864fbf1f5..00ff1be795 100644 --- a/thirdparty/libwebp/src/enc/vp8i_enc.h +++ b/thirdparty/libwebp/src/enc/vp8i_enc.h @@ -31,8 +31,8 @@ extern "C" { // version numbers #define ENC_MAJ_VERSION 1 -#define ENC_MIN_VERSION 3 -#define ENC_REV_VERSION 2 +#define ENC_MIN_VERSION 4 +#define ENC_REV_VERSION 0 enum { MAX_LF_LEVELS = 64, // Maximum loop filter level MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost diff --git a/thirdparty/libwebp/src/enc/vp8l_enc.c b/thirdparty/libwebp/src/enc/vp8l_enc.c index 3a8ec3dd1e..40eafa4169 100644 --- a/thirdparty/libwebp/src/enc/vp8l_enc.c +++ b/thirdparty/libwebp/src/enc/vp8l_enc.c @@ -23,6 +23,7 @@ #include "src/enc/vp8li_enc.h" #include "src/utils/bit_writer_utils.h" #include "src/utils/huffman_encode_utils.h" +#include "src/utils/palette.h" #include "src/utils/utils.h" #include "src/webp/encode.h" #include "src/webp/format_constants.h" @@ -30,298 +31,6 @@ // Maximum number of histogram images (sub-blocks). #define MAX_HUFF_IMAGE_SIZE 2600 -// Palette reordering for smaller sum of deltas (and for smaller storage). - -static int PaletteCompareColorsForQsort(const void* p1, const void* p2) { - const uint32_t a = WebPMemToUint32((uint8_t*)p1); - const uint32_t b = WebPMemToUint32((uint8_t*)p2); - assert(a != b); - return (a < b) ? -1 : 1; -} - -static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) { - return (v <= 128) ? v : (256 - v); -} - -// Computes a value that is related to the entropy created by the -// palette entry diff. -// -// Note that the last & 0xff is a no-operation in the next statement, but -// removed by most compilers and is here only for regularity of the code. -static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) { - const uint32_t diff = VP8LSubPixels(col1, col2); - const int kMoreWeightForRGBThanForAlpha = 9; - uint32_t score; - score = PaletteComponentDistance((diff >> 0) & 0xff); - score += PaletteComponentDistance((diff >> 8) & 0xff); - score += PaletteComponentDistance((diff >> 16) & 0xff); - score *= kMoreWeightForRGBThanForAlpha; - score += PaletteComponentDistance((diff >> 24) & 0xff); - return score; -} - -static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) { - const uint32_t tmp = *col1; - *col1 = *col2; - *col2 = tmp; -} - -static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color, - int num_colors) { - int low = 0, hi = num_colors; - if (sorted[low] == color) return low; // loop invariant: sorted[low] != color - while (1) { - const int mid = (low + hi) >> 1; - if (sorted[mid] == color) { - return mid; - } else if (sorted[mid] < color) { - low = mid; - } else { - hi = mid; - } - } - assert(0); - return 0; -} - -// The palette has been sorted by alpha. This function checks if the other -// components of the palette have a monotonic development with regards to -// position in the palette. If all have monotonic development, there is -// no benefit to re-organize them greedily. A monotonic development -// would be spotted in green-only situations (like lossy alpha) or gray-scale -// images. -static int PaletteHasNonMonotonousDeltas(const uint32_t* const palette, - int num_colors) { - uint32_t predict = 0x000000; - int i; - uint8_t sign_found = 0x00; - for (i = 0; i < num_colors; ++i) { - const uint32_t diff = VP8LSubPixels(palette[i], predict); - const uint8_t rd = (diff >> 16) & 0xff; - const uint8_t gd = (diff >> 8) & 0xff; - const uint8_t bd = (diff >> 0) & 0xff; - if (rd != 0x00) { - sign_found |= (rd < 0x80) ? 1 : 2; - } - if (gd != 0x00) { - sign_found |= (gd < 0x80) ? 8 : 16; - } - if (bd != 0x00) { - sign_found |= (bd < 0x80) ? 64 : 128; - } - predict = palette[i]; - } - return (sign_found & (sign_found << 1)) != 0; // two consequent signs. -} - -static void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted, - int num_colors, uint32_t* const palette) { - uint32_t predict = 0x00000000; - int i, k; - memcpy(palette, palette_sorted, num_colors * sizeof(*palette)); - if (!PaletteHasNonMonotonousDeltas(palette_sorted, num_colors)) return; - // Find greedily always the closest color of the predicted color to minimize - // deltas in the palette. This reduces storage needs since the - // palette is stored with delta encoding. - for (i = 0; i < num_colors; ++i) { - int best_ix = i; - uint32_t best_score = ~0U; - for (k = i; k < num_colors; ++k) { - const uint32_t cur_score = PaletteColorDistance(palette[k], predict); - if (best_score > cur_score) { - best_score = cur_score; - best_ix = k; - } - } - SwapColor(&palette[best_ix], &palette[i]); - predict = palette[i]; - } -} - -// Sort palette in increasing order and prepare an inverse mapping array. -static void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors, - uint32_t sorted[], uint32_t idx_map[]) { - uint32_t i; - memcpy(sorted, palette, num_colors * sizeof(*sorted)); - qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort); - for (i = 0; i < num_colors; ++i) { - idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i; - } -} - -// ----------------------------------------------------------------------------- -// Modified Zeng method from "A Survey on Palette Reordering -// Methods for Improving the Compression of Color-Indexed Images" by Armando J. -// Pinho and Antonio J. R. Neves. - -// Finds the biggest cooccurrence in the matrix. -static void CoOccurrenceFindMax(const uint32_t* const cooccurrence, - uint32_t num_colors, uint8_t* const c1, - uint8_t* const c2) { - // Find the index that is most frequently located adjacent to other - // (different) indexes. - uint32_t best_sum = 0u; - uint32_t i, j, best_cooccurrence; - *c1 = 0u; - for (i = 0; i < num_colors; ++i) { - uint32_t sum = 0; - for (j = 0; j < num_colors; ++j) sum += cooccurrence[i * num_colors + j]; - if (sum > best_sum) { - best_sum = sum; - *c1 = i; - } - } - // Find the index that is most frequently found adjacent to *c1. - *c2 = 0u; - best_cooccurrence = 0u; - for (i = 0; i < num_colors; ++i) { - if (cooccurrence[*c1 * num_colors + i] > best_cooccurrence) { - best_cooccurrence = cooccurrence[*c1 * num_colors + i]; - *c2 = i; - } - } - assert(*c1 != *c2); -} - -// Builds the cooccurrence matrix -static int CoOccurrenceBuild(const WebPPicture* const pic, - const uint32_t* const palette, uint32_t num_colors, - uint32_t* cooccurrence) { - uint32_t *lines, *line_top, *line_current, *line_tmp; - int x, y; - const uint32_t* src = pic->argb; - uint32_t prev_pix = ~src[0]; - uint32_t prev_idx = 0u; - uint32_t idx_map[MAX_PALETTE_SIZE] = {0}; - uint32_t palette_sorted[MAX_PALETTE_SIZE]; - lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines)); - if (lines == NULL) { - return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); - } - line_top = &lines[0]; - line_current = &lines[pic->width]; - PrepareMapToPalette(palette, num_colors, palette_sorted, idx_map); - for (y = 0; y < pic->height; ++y) { - for (x = 0; x < pic->width; ++x) { - const uint32_t pix = src[x]; - if (pix != prev_pix) { - prev_idx = idx_map[SearchColorNoIdx(palette_sorted, pix, num_colors)]; - prev_pix = pix; - } - line_current[x] = prev_idx; - // 4-connectivity is what works best as mentioned in "On the relation - // between Memon's and the modified Zeng's palette reordering methods". - if (x > 0 && prev_idx != line_current[x - 1]) { - const uint32_t left_idx = line_current[x - 1]; - ++cooccurrence[prev_idx * num_colors + left_idx]; - ++cooccurrence[left_idx * num_colors + prev_idx]; - } - if (y > 0 && prev_idx != line_top[x]) { - const uint32_t top_idx = line_top[x]; - ++cooccurrence[prev_idx * num_colors + top_idx]; - ++cooccurrence[top_idx * num_colors + prev_idx]; - } - } - line_tmp = line_top; - line_top = line_current; - line_current = line_tmp; - src += pic->argb_stride; - } - WebPSafeFree(lines); - return 1; -} - -struct Sum { - uint8_t index; - uint32_t sum; -}; - -// Implements the modified Zeng method from "A Survey on Palette Reordering -// Methods for Improving the Compression of Color-Indexed Images" by Armando J. -// Pinho and Antonio J. R. Neves. -static int PaletteSortModifiedZeng( - const WebPPicture* const pic, const uint32_t* const palette_sorted, - uint32_t num_colors, uint32_t* const palette) { - uint32_t i, j, ind; - uint8_t remapping[MAX_PALETTE_SIZE]; - uint32_t* cooccurrence; - struct Sum sums[MAX_PALETTE_SIZE]; - uint32_t first, last; - uint32_t num_sums; - // TODO(vrabaud) check whether one color images should use palette or not. - if (num_colors <= 1) return 1; - // Build the co-occurrence matrix. - cooccurrence = - (uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence)); - if (cooccurrence == NULL) { - return WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY); - } - if (!CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence)) { - WebPSafeFree(cooccurrence); - return 0; - } - - // Initialize the mapping list with the two best indices. - CoOccurrenceFindMax(cooccurrence, num_colors, &remapping[0], &remapping[1]); - - // We need to append and prepend to the list of remapping. To this end, we - // actually define the next start/end of the list as indices in a vector (with - // a wrap around when the end is reached). - first = 0; - last = 1; - num_sums = num_colors - 2; // -2 because we know the first two values - if (num_sums > 0) { - // Initialize the sums with the first two remappings and find the best one - struct Sum* best_sum = &sums[0]; - best_sum->index = 0u; - best_sum->sum = 0u; - for (i = 0, j = 0; i < num_colors; ++i) { - if (i == remapping[0] || i == remapping[1]) continue; - sums[j].index = i; - sums[j].sum = cooccurrence[i * num_colors + remapping[0]] + - cooccurrence[i * num_colors + remapping[1]]; - if (sums[j].sum > best_sum->sum) best_sum = &sums[j]; - ++j; - } - - while (num_sums > 0) { - const uint8_t best_index = best_sum->index; - // Compute delta to know if we need to prepend or append the best index. - int32_t delta = 0; - const int32_t n = num_colors - num_sums; - for (ind = first, j = 0; (ind + j) % num_colors != last + 1; ++j) { - const uint16_t l_j = remapping[(ind + j) % num_colors]; - delta += (n - 1 - 2 * (int32_t)j) * - (int32_t)cooccurrence[best_index * num_colors + l_j]; - } - if (delta > 0) { - first = (first == 0) ? num_colors - 1 : first - 1; - remapping[first] = best_index; - } else { - ++last; - remapping[last] = best_index; - } - // Remove best_sum from sums. - *best_sum = sums[num_sums - 1]; - --num_sums; - // Update all the sums and find the best one. - best_sum = &sums[0]; - for (i = 0; i < num_sums; ++i) { - sums[i].sum += cooccurrence[best_index * num_colors + sums[i].index]; - if (sums[i].sum > best_sum->sum) best_sum = &sums[i]; - } - } - } - assert((last + 1) % num_colors == first); - WebPSafeFree(cooccurrence); - - // Re-map the palette. - for (i = 0; i < num_colors; ++i) { - palette[i] = palette_sorted[remapping[(first + i) % num_colors]]; - } - return 1; -} - // ----------------------------------------------------------------------------- // Palette @@ -337,13 +46,6 @@ typedef enum { } EntropyIx; typedef enum { - kSortedDefault = 0, - kMinimizeDelta = 1, - kModifiedZeng = 2, - kUnusedPalette = 3, -} PaletteSorting; - -typedef enum { kHistoAlpha = 0, kHistoAlphaPred, kHistoGreen, @@ -565,7 +267,7 @@ typedef struct { // +2 because we add a palette sorting configuration for kPalette and // kPaletteAndSpatial. -#define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2) +#define CRUNCH_CONFIGS_MAX (kNumEntropyIx + 2 * kPaletteSortingNum) static int EncoderAnalyze(VP8LEncoder* const enc, CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX], @@ -586,13 +288,10 @@ static int EncoderAnalyze(VP8LEncoder* const enc, assert(pic != NULL && pic->argb != NULL); // Check whether a palette is possible. - enc->palette_size_ = WebPGetColorPalette(pic, enc->palette_sorted_); + enc->palette_size_ = GetColorPalette(pic, enc->palette_sorted_); use_palette = (enc->palette_size_ <= MAX_PALETTE_SIZE); if (!use_palette) { enc->palette_size_ = 0; - } else { - qsort(enc->palette_sorted_, enc->palette_size_, - sizeof(*enc->palette_sorted_), PaletteCompareColorsForQsort); } // Empirical bit sizes. @@ -625,20 +324,29 @@ static int EncoderAnalyze(VP8LEncoder* const enc, // a palette. if ((i != kPalette && i != kPaletteAndSpatial) || use_palette) { assert(*crunch_configs_size < CRUNCH_CONFIGS_MAX); - crunch_configs[(*crunch_configs_size)].entropy_idx_ = i; if (use_palette && (i == kPalette || i == kPaletteAndSpatial)) { - crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = - kMinimizeDelta; - ++*crunch_configs_size; - // Also add modified Zeng's method. - crunch_configs[(*crunch_configs_size)].entropy_idx_ = i; - crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = - kModifiedZeng; + int sorting_method; + for (sorting_method = 0; sorting_method < kPaletteSortingNum; + ++sorting_method) { + const PaletteSorting typed_sorting_method = + (PaletteSorting)sorting_method; + // TODO(vrabaud) kSortedDefault should be tested. It is omitted + // for now for backward compatibility. + if (typed_sorting_method == kUnusedPalette || + typed_sorting_method == kSortedDefault) { + continue; + } + crunch_configs[(*crunch_configs_size)].entropy_idx_ = i; + crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = + typed_sorting_method; + ++*crunch_configs_size; + } } else { + crunch_configs[(*crunch_configs_size)].entropy_idx_ = i; crunch_configs[(*crunch_configs_size)].palette_sorting_type_ = kUnusedPalette; + ++*crunch_configs_size; } - ++*crunch_configs_size; } } } else { @@ -1112,10 +820,10 @@ static int EncodeImageNoHuffman(VP8LBitWriter* const bw, static int EncodeImageInternal( VP8LBitWriter* const bw, const uint32_t* const argb, VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[4], int width, - int height, int quality, int low_effort, int use_cache, - const CrunchConfig* const config, int* cache_bits, int histogram_bits, - size_t init_byte_position, int* const hdr_size, int* const data_size, - const WebPPicture* const pic, int percent_range, int* const percent) { + int height, int quality, int low_effort, const CrunchConfig* const config, + int* cache_bits, int histogram_bits, size_t init_byte_position, + int* const hdr_size, int* const data_size, const WebPPicture* const pic, + int percent_range, int* const percent) { const uint32_t histogram_image_xysize = VP8LSubSampleSize(width, histogram_bits) * VP8LSubSampleSize(height, histogram_bits); @@ -1163,13 +871,9 @@ static int EncodeImageInternal( percent_start += percent_range; remaining_percent -= percent_range; - if (use_cache) { - // If the value is different from zero, it has been set during the - // palette analysis. - cache_bits_init = (*cache_bits == 0) ? MAX_COLOR_CACHE_BITS : *cache_bits; - } else { - cache_bits_init = 0; - } + // If the value is different from zero, it has been set during the palette + // analysis. + cache_bits_init = (*cache_bits == 0) ? MAX_COLOR_CACHE_BITS : *cache_bits; // If several iterations will happen, clone into bw_best. if ((config->sub_configs_size_ > 1 || config->sub_configs_[0].do_no_cache_) && !VP8LBitWriterClone(bw, &bw_best)) { @@ -1485,7 +1189,7 @@ static void ClearTransformBuffer(VP8LEncoder* const enc) { // enc->use_predict_, enc->use_cross_color_ static int AllocateTransformBuffer(VP8LEncoder* const enc, int width, int height) { - const uint64_t image_size = width * height; + const uint64_t image_size = (uint64_t)width * height; // VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra // pixel in each, plus 2 regular scanlines of bytes. // TODO(skal): Clean up by using arithmetic in bytes instead of words. @@ -1495,7 +1199,7 @@ static int AllocateTransformBuffer(VP8LEncoder* const enc, int width, : 0; const uint64_t transform_data_size = (enc->use_predict_ || enc->use_cross_color_) - ? VP8LSubSampleSize(width, enc->transform_bits_) * + ? (uint64_t)VP8LSubSampleSize(width, enc->transform_bits_) * VP8LSubSampleSize(height, enc->transform_bits_) : 0; const uint64_t max_alignment_in_words = @@ -1758,7 +1462,6 @@ typedef struct { const WebPPicture* picture_; VP8LBitWriter* bw_; VP8LEncoder* enc_; - int use_cache_; CrunchConfig crunch_configs_[CRUNCH_CONFIGS_MAX]; int num_crunch_configs_; int red_and_blue_always_zero_; @@ -1771,7 +1474,6 @@ static int EncodeStreamHook(void* input, void* data2) { const WebPPicture* const picture = params->picture_; VP8LBitWriter* const bw = params->bw_; VP8LEncoder* const enc = params->enc_; - const int use_cache = params->use_cache_; const CrunchConfig* const crunch_configs = params->crunch_configs_; const int num_crunch_configs = params->num_crunch_configs_; const int red_and_blue_always_zero = params->red_and_blue_always_zero_; @@ -1845,19 +1547,11 @@ static int EncodeStreamHook(void* input, void* data2) { // Encode palette if (enc->use_palette_) { - if (crunch_configs[idx].palette_sorting_type_ == kSortedDefault) { - // Nothing to do, we have already sorted the palette. - memcpy(enc->palette_, enc->palette_sorted_, - enc->palette_size_ * sizeof(*enc->palette_)); - } else if (crunch_configs[idx].palette_sorting_type_ == kMinimizeDelta) { - PaletteSortMinimizeDeltas(enc->palette_sorted_, enc->palette_size_, - enc->palette_); - } else { - assert(crunch_configs[idx].palette_sorting_type_ == kModifiedZeng); - if (!PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_, - enc->palette_size_, enc->palette_)) { - goto Error; - } + if (!PaletteSort(crunch_configs[idx].palette_sorting_type_, enc->pic_, + enc->palette_sorted_, enc->palette_size_, + enc->palette_)) { + WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY); + goto Error; } percent_range = remaining_percent / 4; if (!EncodePalette(bw, low_effort, enc, percent_range, &percent)) { @@ -1867,7 +1561,7 @@ static int EncodeStreamHook(void* input, void* data2) { if (!MapImageFromPalette(enc, use_delta_palette)) goto Error; // If using a color cache, do not have it bigger than the number of // colors. - if (use_cache && enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) { + if (enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) { enc->cache_bits_ = BitsLog2Floor(enc->palette_size_) + 1; } } @@ -1911,7 +1605,7 @@ static int EncodeStreamHook(void* input, void* data2) { // Encode and write the transformed image. if (!EncodeImageInternal( bw, enc->argb_, &enc->hash_chain_, enc->refs_, enc->current_width_, - height, quality, low_effort, use_cache, &crunch_configs[idx], + height, quality, low_effort, &crunch_configs[idx], &enc->cache_bits_, enc->histo_bits_, byte_position, &hdr_size, &data_size, picture, remaining_percent, &percent)) { goto Error; @@ -1953,7 +1647,7 @@ static int EncodeStreamHook(void* input, void* data2) { int VP8LEncodeStream(const WebPConfig* const config, const WebPPicture* const picture, - VP8LBitWriter* const bw_main, int use_cache) { + VP8LBitWriter* const bw_main) { VP8LEncoder* const enc_main = VP8LEncoderNew(config, picture); VP8LEncoder* enc_side = NULL; CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX]; @@ -1975,7 +1669,9 @@ int VP8LEncodeStream(const WebPConfig* const config, } // Avoid "garbage value" error from Clang's static analysis tool. - WebPPictureInit(&picture_side); + if (!WebPPictureInit(&picture_side)) { + goto Error; + } // Analyze image (entropy, num_palettes etc) if (!EncoderAnalyze(enc_main, crunch_configs, &num_crunch_configs_main, @@ -2010,7 +1706,6 @@ int VP8LEncodeStream(const WebPConfig* const config, StreamEncodeContext* const param = (idx == 0) ? ¶ms_main : ¶ms_side; param->config_ = config; - param->use_cache_ = use_cache; param->red_and_blue_always_zero_ = red_and_blue_always_zero; if (idx == 0) { param->picture_ = picture; @@ -2164,7 +1859,7 @@ int VP8LEncodeImage(const WebPConfig* const config, if (!WebPReportProgress(picture, 2, &percent)) goto UserAbort; // Encode main image stream. - if (!VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/)) goto Error; + if (!VP8LEncodeStream(config, picture, &bw)) goto Error; if (!WebPReportProgress(picture, 99, &percent)) goto UserAbort; diff --git a/thirdparty/libwebp/src/enc/vp8li_enc.h b/thirdparty/libwebp/src/enc/vp8li_enc.h index 3d35e1612d..c5b60dcb39 100644 --- a/thirdparty/libwebp/src/enc/vp8li_enc.h +++ b/thirdparty/libwebp/src/enc/vp8li_enc.h @@ -88,11 +88,9 @@ int VP8LEncodeImage(const WebPConfig* const config, const WebPPicture* const picture); // Encodes the main image stream using the supplied bit writer. -// If 'use_cache' is false, disables the use of color cache. // Returns false in case of error (stored in picture->error_code). int VP8LEncodeStream(const WebPConfig* const config, - const WebPPicture* const picture, VP8LBitWriter* const bw, - int use_cache); + const WebPPicture* const picture, VP8LBitWriter* const bw); #if (WEBP_NEAR_LOSSLESS == 1) // in near_lossless.c diff --git a/thirdparty/libwebp/src/mux/anim_encode.c b/thirdparty/libwebp/src/mux/anim_encode.c index 7078d9ae6b..31bd0457bf 100644 --- a/thirdparty/libwebp/src/mux/anim_encode.c +++ b/thirdparty/libwebp/src/mux/anim_encode.c @@ -22,6 +22,7 @@ #include "src/webp/encode.h" #include "src/webp/format_constants.h" #include "src/webp/mux.h" +#include "src/webp/types.h" #if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf _snprintf @@ -593,16 +594,17 @@ int WebPAnimEncoderRefineRect( int is_lossless, float quality, int* const x_offset, int* const y_offset, int* const width, int* const height) { FrameRectangle rect; - const int right = clip(*x_offset + *width, 0, curr_canvas->width); - const int left = clip(*x_offset, 0, curr_canvas->width - 1); - const int bottom = clip(*y_offset + *height, 0, curr_canvas->height); - const int top = clip(*y_offset, 0, curr_canvas->height - 1); + int right, left, bottom, top; if (prev_canvas == NULL || curr_canvas == NULL || prev_canvas->width != curr_canvas->width || prev_canvas->height != curr_canvas->height || !prev_canvas->use_argb || !curr_canvas->use_argb) { return 0; } + right = clip(*x_offset + *width, 0, curr_canvas->width); + left = clip(*x_offset, 0, curr_canvas->width - 1); + bottom = clip(*y_offset + *height, 0, curr_canvas->height); + top = clip(*y_offset, 0, curr_canvas->height - 1); rect.x_offset_ = left; rect.y_offset_ = top; rect.width_ = clip(right - left, 0, curr_canvas->width - rect.x_offset_); @@ -1397,7 +1399,10 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp, } config = *encoder_config; } else { - WebPConfigInit(&config); + if (!WebPConfigInit(&config)) { + MarkError(enc, "Cannot Init config"); + return 0; + } config.lossless = 1; } assert(enc->curr_canvas_ == NULL); @@ -1418,12 +1423,14 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp, // ----------------------------------------------------------------------------- // Bitstream assembly. -static int DecodeFrameOntoCanvas(const WebPMuxFrameInfo* const frame, - WebPPicture* const canvas) { +WEBP_NODISCARD static int DecodeFrameOntoCanvas( + const WebPMuxFrameInfo* const frame, WebPPicture* const canvas) { const WebPData* const image = &frame->bitstream; WebPPicture sub_image; WebPDecoderConfig config; - WebPInitDecoderConfig(&config); + if (!WebPInitDecoderConfig(&config)) { + return 0; + } WebPUtilClearPic(canvas, NULL); if (WebPGetFeatures(image->bytes, image->size, &config.input) != VP8_STATUS_OK) { @@ -1582,4 +1589,23 @@ const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc) { return enc->error_str_; } +WebPMuxError WebPAnimEncoderSetChunk( + WebPAnimEncoder* enc, const char fourcc[4], const WebPData* chunk_data, + int copy_data) { + if (enc == NULL) return WEBP_MUX_INVALID_ARGUMENT; + return WebPMuxSetChunk(enc->mux_, fourcc, chunk_data, copy_data); +} + +WebPMuxError WebPAnimEncoderGetChunk( + const WebPAnimEncoder* enc, const char fourcc[4], WebPData* chunk_data) { + if (enc == NULL) return WEBP_MUX_INVALID_ARGUMENT; + return WebPMuxGetChunk(enc->mux_, fourcc, chunk_data); +} + +WebPMuxError WebPAnimEncoderDeleteChunk( + WebPAnimEncoder* enc, const char fourcc[4]) { + if (enc == NULL) return WEBP_MUX_INVALID_ARGUMENT; + return WebPMuxDeleteChunk(enc->mux_, fourcc); +} + // ----------------------------------------------------------------------------- diff --git a/thirdparty/libwebp/src/mux/muxedit.c b/thirdparty/libwebp/src/mux/muxedit.c index 63e71a0aba..48c6834a4d 100644 --- a/thirdparty/libwebp/src/mux/muxedit.c +++ b/thirdparty/libwebp/src/mux/muxedit.c @@ -66,14 +66,16 @@ void WebPMuxDelete(WebPMux* mux) { // Handy MACRO, makes MuxSet() very symmetric to MuxGet(). #define SWITCH_ID_LIST(INDEX, LIST) \ - if (idx == (INDEX)) { \ - err = ChunkAssignData(&chunk, data, copy_data, tag); \ - if (err == WEBP_MUX_OK) { \ - err = ChunkSetHead(&chunk, (LIST)); \ - if (err != WEBP_MUX_OK) ChunkRelease(&chunk); \ + do { \ + if (idx == (INDEX)) { \ + err = ChunkAssignData(&chunk, data, copy_data, tag); \ + if (err == WEBP_MUX_OK) { \ + err = ChunkSetHead(&chunk, (LIST)); \ + if (err != WEBP_MUX_OK) ChunkRelease(&chunk); \ + } \ + return err; \ } \ - return err; \ - } + } while (0) static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, const WebPData* const data, int copy_data) { @@ -555,7 +557,8 @@ static WebPMuxError MuxCleanup(WebPMux* const mux) { if (num_frames == 1) { WebPMuxImage* frame = NULL; err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame); - assert(err == WEBP_MUX_OK); // We know that one frame does exist. + if (err != WEBP_MUX_OK) return err; + // We know that one frame does exist. assert(frame != NULL); if (frame->header_ != NULL && ((mux->canvas_width_ == 0 && mux->canvas_height_ == 0) || diff --git a/thirdparty/libwebp/src/mux/muxi.h b/thirdparty/libwebp/src/mux/muxi.h index afc5954353..74ae3fac12 100644 --- a/thirdparty/libwebp/src/mux/muxi.h +++ b/thirdparty/libwebp/src/mux/muxi.h @@ -28,8 +28,8 @@ extern "C" { // Defines and constants. #define MUX_MAJ_VERSION 1 -#define MUX_MIN_VERSION 3 -#define MUX_REV_VERSION 2 +#define MUX_MIN_VERSION 4 +#define MUX_REV_VERSION 0 // Chunk object. typedef struct WebPChunk WebPChunk; diff --git a/thirdparty/libwebp/src/mux/muxread.c b/thirdparty/libwebp/src/mux/muxread.c index 9862ec68ee..afd3542e12 100644 --- a/thirdparty/libwebp/src/mux/muxread.c +++ b/thirdparty/libwebp/src/mux/muxread.c @@ -21,20 +21,23 @@ // Handy MACRO. #define SWITCH_ID_LIST(INDEX, LIST) \ - if (idx == (INDEX)) { \ - const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \ - kChunks[(INDEX)].tag); \ - if (chunk) { \ - *data = chunk->data_; \ - return WEBP_MUX_OK; \ - } else { \ - return WEBP_MUX_NOT_FOUND; \ + do { \ + if (idx == (INDEX)) { \ + const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \ + kChunks[(INDEX)].tag); \ + if (chunk) { \ + *data = chunk->data_; \ + return WEBP_MUX_OK; \ + } else { \ + return WEBP_MUX_NOT_FOUND; \ + } \ } \ - } + } while (0) static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx, uint32_t nth, WebPData* const data) { assert(mux != NULL); + assert(idx != IDX_LAST_CHUNK); assert(!IsWPI(kChunks[idx].id)); WebPDataInit(data); @@ -429,6 +432,7 @@ WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4], return WEBP_MUX_INVALID_ARGUMENT; } idx = ChunkGetIndexFromFourCC(fourcc); + assert(idx != IDX_LAST_CHUNK); if (IsWPI(kChunks[idx].id)) { // An image chunk. return WEBP_MUX_INVALID_ARGUMENT; } else if (idx != IDX_UNKNOWN) { // A known chunk type. diff --git a/thirdparty/libwebp/src/utils/huffman_utils.c b/thirdparty/libwebp/src/utils/huffman_utils.c index cf73abd437..16f9faaa9a 100644 --- a/thirdparty/libwebp/src/utils/huffman_utils.c +++ b/thirdparty/libwebp/src/utils/huffman_utils.c @@ -122,6 +122,9 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits, const int symbol_code_length = code_lengths[symbol]; if (code_lengths[symbol] > 0) { if (sorted != NULL) { + if(offset[symbol_code_length] >= code_lengths_size) { + return 0; + } sorted[offset[symbol_code_length]++] = symbol; } else { offset[symbol_code_length]++; @@ -267,11 +270,11 @@ int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables) { // Have 'segment' point to the first segment for now, 'root'. HuffmanTablesSegment* const root = &huffman_tables->root; huffman_tables->curr_segment = root; + root->next = NULL; // Allocate root. root->start = (HuffmanCode*)WebPSafeMalloc(size, sizeof(*root->start)); if (root->start == NULL) return 0; root->curr_table = root->start; - root->next = NULL; root->size = size; return 1; } diff --git a/thirdparty/libwebp/src/utils/huffman_utils.h b/thirdparty/libwebp/src/utils/huffman_utils.h index 98415c5328..d511dc052c 100644 --- a/thirdparty/libwebp/src/utils/huffman_utils.h +++ b/thirdparty/libwebp/src/utils/huffman_utils.h @@ -63,7 +63,8 @@ typedef struct HuffmanTables { // Allocates a HuffmanTables with 'size' contiguous HuffmanCodes. Returns 0 on // memory allocation error, 1 otherwise. -int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables); +WEBP_NODISCARD int VP8LHuffmanTablesAllocate(int size, + HuffmanTables* huffman_tables); void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables); #define HUFFMAN_PACKED_BITS 6 @@ -91,7 +92,7 @@ struct HTreeGroup { }; // Creates the instance of HTreeGroup with specified number of tree-groups. -HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups); +WEBP_NODISCARD HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups); // Releases the memory allocated for HTreeGroup. void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups); @@ -101,8 +102,10 @@ void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups); // the huffman table. // Returns built table size or 0 in case of error (invalid tree or // memory error). -int VP8LBuildHuffmanTable(HuffmanTables* const root_table, int root_bits, - const int code_lengths[], int code_lengths_size); +WEBP_NODISCARD int VP8LBuildHuffmanTable(HuffmanTables* const root_table, + int root_bits, + const int code_lengths[], + int code_lengths_size); #ifdef __cplusplus } // extern "C" diff --git a/thirdparty/libwebp/src/utils/palette.c b/thirdparty/libwebp/src/utils/palette.c new file mode 100644 index 0000000000..515da21019 --- /dev/null +++ b/thirdparty/libwebp/src/utils/palette.c @@ -0,0 +1,402 @@ +// Copyright 2023 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Utilities for palette analysis. +// +// Author: Vincent Rabaud (vrabaud@google.com) + +#include "src/utils/palette.h" + +#include <assert.h> +#include <stdlib.h> + +#include "src/dsp/lossless_common.h" +#include "src/utils/color_cache_utils.h" +#include "src/utils/utils.h" +#include "src/webp/encode.h" +#include "src/webp/format_constants.h" + +// ----------------------------------------------------------------------------- + +// Palette reordering for smaller sum of deltas (and for smaller storage). + +static int PaletteCompareColorsForQsort(const void* p1, const void* p2) { + const uint32_t a = WebPMemToUint32((uint8_t*)p1); + const uint32_t b = WebPMemToUint32((uint8_t*)p2); + assert(a != b); + return (a < b) ? -1 : 1; +} + +static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) { + return (v <= 128) ? v : (256 - v); +} + +// Computes a value that is related to the entropy created by the +// palette entry diff. +// +// Note that the last & 0xff is a no-operation in the next statement, but +// removed by most compilers and is here only for regularity of the code. +static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) { + const uint32_t diff = VP8LSubPixels(col1, col2); + const int kMoreWeightForRGBThanForAlpha = 9; + uint32_t score; + score = PaletteComponentDistance((diff >> 0) & 0xff); + score += PaletteComponentDistance((diff >> 8) & 0xff); + score += PaletteComponentDistance((diff >> 16) & 0xff); + score *= kMoreWeightForRGBThanForAlpha; + score += PaletteComponentDistance((diff >> 24) & 0xff); + return score; +} + +static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) { + const uint32_t tmp = *col1; + *col1 = *col2; + *col2 = tmp; +} + +int SearchColorNoIdx(const uint32_t sorted[], uint32_t color, int num_colors) { + int low = 0, hi = num_colors; + if (sorted[low] == color) return low; // loop invariant: sorted[low] != color + while (1) { + const int mid = (low + hi) >> 1; + if (sorted[mid] == color) { + return mid; + } else if (sorted[mid] < color) { + low = mid; + } else { + hi = mid; + } + } + assert(0); + return 0; +} + +void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors, + uint32_t sorted[], uint32_t idx_map[]) { + uint32_t i; + memcpy(sorted, palette, num_colors * sizeof(*sorted)); + qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort); + for (i = 0; i < num_colors; ++i) { + idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i; + } +} + +//------------------------------------------------------------------------------ + +#define COLOR_HASH_SIZE (MAX_PALETTE_SIZE * 4) +#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE). + +int GetColorPalette(const WebPPicture* const pic, uint32_t* const palette) { + int i; + int x, y; + int num_colors = 0; + uint8_t in_use[COLOR_HASH_SIZE] = {0}; + uint32_t colors[COLOR_HASH_SIZE] = {0}; + const uint32_t* argb = pic->argb; + const int width = pic->width; + const int height = pic->height; + uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] + assert(pic != NULL); + assert(pic->use_argb); + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + int key; + if (argb[x] == last_pix) { + continue; + } + last_pix = argb[x]; + key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT); + while (1) { + if (!in_use[key]) { + colors[key] = last_pix; + in_use[key] = 1; + ++num_colors; + if (num_colors > MAX_PALETTE_SIZE) { + return MAX_PALETTE_SIZE + 1; // Exact count not needed. + } + break; + } else if (colors[key] == last_pix) { + break; // The color is already there. + } else { + // Some other color sits here, so do linear conflict resolution. + ++key; + key &= (COLOR_HASH_SIZE - 1); // Key mask. + } + } + } + argb += pic->argb_stride; + } + + if (palette != NULL) { // Fill the colors into palette. + num_colors = 0; + for (i = 0; i < COLOR_HASH_SIZE; ++i) { + if (in_use[i]) { + palette[num_colors] = colors[i]; + ++num_colors; + } + } + qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort); + } + return num_colors; +} + +#undef COLOR_HASH_SIZE +#undef COLOR_HASH_RIGHT_SHIFT + +// ----------------------------------------------------------------------------- + +// The palette has been sorted by alpha. This function checks if the other +// components of the palette have a monotonic development with regards to +// position in the palette. If all have monotonic development, there is +// no benefit to re-organize them greedily. A monotonic development +// would be spotted in green-only situations (like lossy alpha) or gray-scale +// images. +static int PaletteHasNonMonotonousDeltas(const uint32_t* const palette, + int num_colors) { + uint32_t predict = 0x000000; + int i; + uint8_t sign_found = 0x00; + for (i = 0; i < num_colors; ++i) { + const uint32_t diff = VP8LSubPixels(palette[i], predict); + const uint8_t rd = (diff >> 16) & 0xff; + const uint8_t gd = (diff >> 8) & 0xff; + const uint8_t bd = (diff >> 0) & 0xff; + if (rd != 0x00) { + sign_found |= (rd < 0x80) ? 1 : 2; + } + if (gd != 0x00) { + sign_found |= (gd < 0x80) ? 8 : 16; + } + if (bd != 0x00) { + sign_found |= (bd < 0x80) ? 64 : 128; + } + predict = palette[i]; + } + return (sign_found & (sign_found << 1)) != 0; // two consequent signs. +} + +static void PaletteSortMinimizeDeltas(const uint32_t* const palette_sorted, + int num_colors, uint32_t* const palette) { + uint32_t predict = 0x00000000; + int i, k; + memcpy(palette, palette_sorted, num_colors * sizeof(*palette)); + if (!PaletteHasNonMonotonousDeltas(palette_sorted, num_colors)) return; + // Find greedily always the closest color of the predicted color to minimize + // deltas in the palette. This reduces storage needs since the + // palette is stored with delta encoding. + for (i = 0; i < num_colors; ++i) { + int best_ix = i; + uint32_t best_score = ~0U; + for (k = i; k < num_colors; ++k) { + const uint32_t cur_score = PaletteColorDistance(palette[k], predict); + if (best_score > cur_score) { + best_score = cur_score; + best_ix = k; + } + } + SwapColor(&palette[best_ix], &palette[i]); + predict = palette[i]; + } +} + +// ----------------------------------------------------------------------------- +// Modified Zeng method from "A Survey on Palette Reordering +// Methods for Improving the Compression of Color-Indexed Images" by Armando J. +// Pinho and Antonio J. R. Neves. + +// Finds the biggest cooccurrence in the matrix. +static void CoOccurrenceFindMax(const uint32_t* const cooccurrence, + uint32_t num_colors, uint8_t* const c1, + uint8_t* const c2) { + // Find the index that is most frequently located adjacent to other + // (different) indexes. + uint32_t best_sum = 0u; + uint32_t i, j, best_cooccurrence; + *c1 = 0u; + for (i = 0; i < num_colors; ++i) { + uint32_t sum = 0; + for (j = 0; j < num_colors; ++j) sum += cooccurrence[i * num_colors + j]; + if (sum > best_sum) { + best_sum = sum; + *c1 = i; + } + } + // Find the index that is most frequently found adjacent to *c1. + *c2 = 0u; + best_cooccurrence = 0u; + for (i = 0; i < num_colors; ++i) { + if (cooccurrence[*c1 * num_colors + i] > best_cooccurrence) { + best_cooccurrence = cooccurrence[*c1 * num_colors + i]; + *c2 = i; + } + } + assert(*c1 != *c2); +} + +// Builds the cooccurrence matrix +static int CoOccurrenceBuild(const WebPPicture* const pic, + const uint32_t* const palette, uint32_t num_colors, + uint32_t* cooccurrence) { + uint32_t *lines, *line_top, *line_current, *line_tmp; + int x, y; + const uint32_t* src = pic->argb; + uint32_t prev_pix = ~src[0]; + uint32_t prev_idx = 0u; + uint32_t idx_map[MAX_PALETTE_SIZE] = {0}; + uint32_t palette_sorted[MAX_PALETTE_SIZE]; + lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines)); + if (lines == NULL) { + return 0; + } + line_top = &lines[0]; + line_current = &lines[pic->width]; + PrepareMapToPalette(palette, num_colors, palette_sorted, idx_map); + for (y = 0; y < pic->height; ++y) { + for (x = 0; x < pic->width; ++x) { + const uint32_t pix = src[x]; + if (pix != prev_pix) { + prev_idx = idx_map[SearchColorNoIdx(palette_sorted, pix, num_colors)]; + prev_pix = pix; + } + line_current[x] = prev_idx; + // 4-connectivity is what works best as mentioned in "On the relation + // between Memon's and the modified Zeng's palette reordering methods". + if (x > 0 && prev_idx != line_current[x - 1]) { + const uint32_t left_idx = line_current[x - 1]; + ++cooccurrence[prev_idx * num_colors + left_idx]; + ++cooccurrence[left_idx * num_colors + prev_idx]; + } + if (y > 0 && prev_idx != line_top[x]) { + const uint32_t top_idx = line_top[x]; + ++cooccurrence[prev_idx * num_colors + top_idx]; + ++cooccurrence[top_idx * num_colors + prev_idx]; + } + } + line_tmp = line_top; + line_top = line_current; + line_current = line_tmp; + src += pic->argb_stride; + } + WebPSafeFree(lines); + return 1; +} + +struct Sum { + uint8_t index; + uint32_t sum; +}; + +static int PaletteSortModifiedZeng(const WebPPicture* const pic, + const uint32_t* const palette_in, + uint32_t num_colors, + uint32_t* const palette) { + uint32_t i, j, ind; + uint8_t remapping[MAX_PALETTE_SIZE]; + uint32_t* cooccurrence; + struct Sum sums[MAX_PALETTE_SIZE]; + uint32_t first, last; + uint32_t num_sums; + // TODO(vrabaud) check whether one color images should use palette or not. + if (num_colors <= 1) return 1; + // Build the co-occurrence matrix. + cooccurrence = + (uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence)); + if (cooccurrence == NULL) { + return 0; + } + if (!CoOccurrenceBuild(pic, palette_in, num_colors, cooccurrence)) { + WebPSafeFree(cooccurrence); + return 0; + } + + // Initialize the mapping list with the two best indices. + CoOccurrenceFindMax(cooccurrence, num_colors, &remapping[0], &remapping[1]); + + // We need to append and prepend to the list of remapping. To this end, we + // actually define the next start/end of the list as indices in a vector (with + // a wrap around when the end is reached). + first = 0; + last = 1; + num_sums = num_colors - 2; // -2 because we know the first two values + if (num_sums > 0) { + // Initialize the sums with the first two remappings and find the best one + struct Sum* best_sum = &sums[0]; + best_sum->index = 0u; + best_sum->sum = 0u; + for (i = 0, j = 0; i < num_colors; ++i) { + if (i == remapping[0] || i == remapping[1]) continue; + sums[j].index = i; + sums[j].sum = cooccurrence[i * num_colors + remapping[0]] + + cooccurrence[i * num_colors + remapping[1]]; + if (sums[j].sum > best_sum->sum) best_sum = &sums[j]; + ++j; + } + + while (num_sums > 0) { + const uint8_t best_index = best_sum->index; + // Compute delta to know if we need to prepend or append the best index. + int32_t delta = 0; + const int32_t n = num_colors - num_sums; + for (ind = first, j = 0; (ind + j) % num_colors != last + 1; ++j) { + const uint16_t l_j = remapping[(ind + j) % num_colors]; + delta += (n - 1 - 2 * (int32_t)j) * + (int32_t)cooccurrence[best_index * num_colors + l_j]; + } + if (delta > 0) { + first = (first == 0) ? num_colors - 1 : first - 1; + remapping[first] = best_index; + } else { + ++last; + remapping[last] = best_index; + } + // Remove best_sum from sums. + *best_sum = sums[num_sums - 1]; + --num_sums; + // Update all the sums and find the best one. + best_sum = &sums[0]; + for (i = 0; i < num_sums; ++i) { + sums[i].sum += cooccurrence[best_index * num_colors + sums[i].index]; + if (sums[i].sum > best_sum->sum) best_sum = &sums[i]; + } + } + } + assert((last + 1) % num_colors == first); + WebPSafeFree(cooccurrence); + + // Re-map the palette. + for (i = 0; i < num_colors; ++i) { + palette[i] = palette_in[remapping[(first + i) % num_colors]]; + } + return 1; +} + +// ----------------------------------------------------------------------------- + +int PaletteSort(PaletteSorting method, const struct WebPPicture* const pic, + const uint32_t* const palette_sorted, uint32_t num_colors, + uint32_t* const palette) { + switch (method) { + case kSortedDefault: + // Nothing to do, we have already sorted the palette. + memcpy(palette, palette_sorted, num_colors * sizeof(*palette)); + return 1; + case kMinimizeDelta: + PaletteSortMinimizeDeltas(palette_sorted, num_colors, palette); + return 1; + case kModifiedZeng: + return PaletteSortModifiedZeng(pic, palette_sorted, num_colors, palette); + case kUnusedPalette: + case kPaletteSortingNum: + break; + } + + assert(0); + return 0; +} diff --git a/thirdparty/libwebp/src/utils/palette.h b/thirdparty/libwebp/src/utils/palette.h new file mode 100644 index 0000000000..34479e463f --- /dev/null +++ b/thirdparty/libwebp/src/utils/palette.h @@ -0,0 +1,60 @@ +// Copyright 2023 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Utilities for palette analysis. +// +// Author: Vincent Rabaud (vrabaud@google.com) + +#ifndef WEBP_UTILS_PALETTE_H_ +#define WEBP_UTILS_PALETTE_H_ + +#include "src/webp/types.h" + +struct WebPPicture; + +// The different ways a palette can be sorted. +typedef enum PaletteSorting { + kSortedDefault = 0, + // Sorts by minimizing L1 deltas between consecutive colors, giving more + // weight to RGB colors. + kMinimizeDelta = 1, + // Implements the modified Zeng method from "A Survey on Palette Reordering + // Methods for Improving the Compression of Color-Indexed Images" by Armando + // J. Pinho and Antonio J. R. Neves. + kModifiedZeng = 2, + kUnusedPalette = 3, + kPaletteSortingNum = 4 +} PaletteSorting; + +// Returns the index of 'color' in the sorted palette 'sorted' of size +// 'num_colors'. +int SearchColorNoIdx(const uint32_t sorted[], uint32_t color, int num_colors); + +// Sort palette in increasing order and prepare an inverse mapping array. +void PrepareMapToPalette(const uint32_t palette[], uint32_t num_colors, + uint32_t sorted[], uint32_t idx_map[]); + +// Returns count of unique colors in 'pic', assuming pic->use_argb is true. +// If the unique color count is more than MAX_PALETTE_SIZE, returns +// MAX_PALETTE_SIZE+1. +// If 'palette' is not NULL and the number of unique colors is less than or +// equal to MAX_PALETTE_SIZE, also outputs the actual unique colors into +// 'palette' in a sorted order. Note: 'palette' is assumed to be an array +// already allocated with at least MAX_PALETTE_SIZE elements. +int GetColorPalette(const struct WebPPicture* const pic, + uint32_t* const palette); + +// Sorts the palette according to the criterion defined by 'method'. +// 'palette_sorted' is the input palette sorted lexicographically, as done in +// PrepareMapToPalette. Returns 0 on memory allocation error. +int PaletteSort(PaletteSorting method, const struct WebPPicture* const pic, + const uint32_t* const palette_sorted, uint32_t num_colors, + uint32_t* const palette); + +#endif // WEBP_UTILS_PALETTE_H_ diff --git a/thirdparty/libwebp/src/utils/utils.c b/thirdparty/libwebp/src/utils/utils.c index a7c3a70fef..408ce88f67 100644 --- a/thirdparty/libwebp/src/utils/utils.c +++ b/thirdparty/libwebp/src/utils/utils.c @@ -11,13 +11,13 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include "src/utils/utils.h" + #include <stdlib.h> #include <string.h> // for memcpy() -#include "src/webp/decode.h" + +#include "src/utils/palette.h" #include "src/webp/encode.h" -#include "src/webp/format_constants.h" // for MAX_PALETTE_SIZE -#include "src/utils/color_cache_utils.h" -#include "src/utils/utils.h" // If PRINT_MEM_INFO is defined, extra info (like total memory used, number of // alloc/free etc) is printed. For debugging/tuning purpose only (it's slow, @@ -252,66 +252,10 @@ void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) { //------------------------------------------------------------------------------ -#define COLOR_HASH_SIZE (MAX_PALETTE_SIZE * 4) -#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE). - int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) { - int i; - int x, y; - int num_colors = 0; - uint8_t in_use[COLOR_HASH_SIZE] = { 0 }; - uint32_t colors[COLOR_HASH_SIZE]; - const uint32_t* argb = pic->argb; - const int width = pic->width; - const int height = pic->height; - uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] - assert(pic != NULL); - assert(pic->use_argb); - - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - int key; - if (argb[x] == last_pix) { - continue; - } - last_pix = argb[x]; - key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT); - while (1) { - if (!in_use[key]) { - colors[key] = last_pix; - in_use[key] = 1; - ++num_colors; - if (num_colors > MAX_PALETTE_SIZE) { - return MAX_PALETTE_SIZE + 1; // Exact count not needed. - } - break; - } else if (colors[key] == last_pix) { - break; // The color is already there. - } else { - // Some other color sits here, so do linear conflict resolution. - ++key; - key &= (COLOR_HASH_SIZE - 1); // Key mask. - } - } - } - argb += pic->argb_stride; - } - - if (palette != NULL) { // Fill the colors into palette. - num_colors = 0; - for (i = 0; i < COLOR_HASH_SIZE; ++i) { - if (in_use[i]) { - palette[num_colors] = colors[i]; - ++num_colors; - } - } - } - return num_colors; + return GetColorPalette(pic, palette); } -#undef COLOR_HASH_SIZE -#undef COLOR_HASH_RIGHT_SHIFT - //------------------------------------------------------------------------------ #if defined(WEBP_NEED_LOG_TABLE_8BIT) diff --git a/thirdparty/libwebp/src/utils/utils.h b/thirdparty/libwebp/src/utils/utils.h index c5ee873357..b2241fbf9b 100644 --- a/thirdparty/libwebp/src/utils/utils.h +++ b/thirdparty/libwebp/src/utils/utils.h @@ -20,9 +20,7 @@ #endif #include <assert.h> -#include <limits.h> -#include "src/dsp/dsp.h" #include "src/webp/types.h" #ifdef __cplusplus @@ -198,6 +196,7 @@ WEBP_EXTERN void WebPCopyPixels(const struct WebPPicture* const src, // MAX_PALETTE_SIZE, also outputs the actual unique colors into 'palette'. // Note: 'palette' is assumed to be an array already allocated with at least // MAX_PALETTE_SIZE elements. +// TODO(vrabaud) remove whenever we can break the ABI. WEBP_EXTERN int WebPGetColorPalette(const struct WebPPicture* const pic, uint32_t* const palette); diff --git a/thirdparty/libwebp/src/webp/decode.h b/thirdparty/libwebp/src/webp/decode.h index 0177b12089..d6895f5c55 100644 --- a/thirdparty/libwebp/src/webp/decode.h +++ b/thirdparty/libwebp/src/webp/decode.h @@ -48,34 +48,33 @@ WEBP_EXTERN int WebPGetDecoderVersion(void); // RIFF + VP8X + (optional chunks) + VP8(L) // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. -WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size, - int* width, int* height); +WEBP_NODISCARD WEBP_EXTERN int WebPGetInfo( + const uint8_t* data, size_t data_size, int* width, int* height); // Decodes WebP images pointed to by 'data' and returns RGBA samples, along // with the dimensions in *width and *height. The ordering of samples in // memory is R, G, B, A, R, G, B, A... in scan order (endian-independent). // The returned pointer should be deleted calling WebPFree(). // Returns NULL in case of error. -WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size, - int* width, int* height); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBA( + const uint8_t* data, size_t data_size, int* width, int* height); // Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data. -WEBP_EXTERN uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size, - int* width, int* height); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGB( + const uint8_t* data, size_t data_size, int* width, int* height); // Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data. -WEBP_EXTERN uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size, - int* width, int* height); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRA( + const uint8_t* data, size_t data_size, int* width, int* height); // Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data. // If the bitstream contains transparency, it is ignored. -WEBP_EXTERN uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size, - int* width, int* height); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGB( + const uint8_t* data, size_t data_size, int* width, int* height); // Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data. -WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, - int* width, int* height); - +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGR( + const uint8_t* data, size_t data_size, int* width, int* height); // Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer // returned is the Y samples buffer. Upon return, *u and *v will point to @@ -87,10 +86,9 @@ WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size, // 'width' and 'height' may be NULL, the other pointers must not be. // Returns NULL in case of error. // (*) Also named Y'CbCr. See: https://en.wikipedia.org/wiki/YCbCr -WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size, - int* width, int* height, - uint8_t** u, uint8_t** v, - int* stride, int* uv_stride); +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUV( + const uint8_t* data, size_t data_size, int* width, int* height, + uint8_t** u, uint8_t** v, int* stride, int* uv_stride); // These five functions are variants of the above ones, that decode the image // directly into a pre-allocated buffer 'output_buffer'. The maximum storage @@ -100,22 +98,22 @@ WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size, // The parameter 'output_stride' specifies the distance (in bytes) // between scanlines. Hence, output_buffer_size is expected to be at least // output_stride x picture-height. -WEBP_EXTERN uint8_t* WebPDecodeRGBAInto( +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBAInto( const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride); -WEBP_EXTERN uint8_t* WebPDecodeARGBInto( +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeARGBInto( const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride); -WEBP_EXTERN uint8_t* WebPDecodeBGRAInto( +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRAInto( const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride); // RGB and BGR variants. Here too the transparency information, if present, // will be dropped and ignored. -WEBP_EXTERN uint8_t* WebPDecodeRGBInto( +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeRGBInto( const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride); -WEBP_EXTERN uint8_t* WebPDecodeBGRInto( +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeBGRInto( const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride); @@ -126,7 +124,7 @@ WEBP_EXTERN uint8_t* WebPDecodeBGRInto( // 'u_size' and 'v_size' respectively. // Pointer to the luma plane ('*luma') is returned or NULL if an error occurred // during decoding (or because some buffers were found to be too small). -WEBP_EXTERN uint8_t* WebPDecodeYUVInto( +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPDecodeYUVInto( const uint8_t* data, size_t data_size, uint8_t* luma, size_t luma_size, int luma_stride, uint8_t* u, size_t u_size, int u_stride, @@ -217,11 +215,11 @@ struct WebPDecBuffer { }; // Internal, version-checked, entry point -WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int); +WEBP_NODISCARD WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int); // Initialize the structure as empty. Must be called before any other use. // Returns false in case of version mismatch -static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) { +WEBP_NODISCARD static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) { return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION); } @@ -232,7 +230,7 @@ WEBP_EXTERN void WebPFreeDecBuffer(WebPDecBuffer* buffer); //------------------------------------------------------------------------------ // Enumeration of the status codes -typedef enum VP8StatusCode { +typedef enum WEBP_NODISCARD VP8StatusCode { VP8_STATUS_OK = 0, VP8_STATUS_OUT_OF_MEMORY, VP8_STATUS_INVALID_PARAM, @@ -251,23 +249,24 @@ typedef enum VP8StatusCode { // WebPIDecoder object. This object can be left in a SUSPENDED state if the // picture is only partially decoded, pending additional input. // Code example: -// -// WebPInitDecBuffer(&output_buffer); -// output_buffer.colorspace = mode; -// ... -// WebPIDecoder* idec = WebPINewDecoder(&output_buffer); -// while (additional_data_is_available) { -// // ... (get additional data in some new_data[] buffer) -// status = WebPIAppend(idec, new_data, new_data_size); -// if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) { -// break; // an error occurred. -// } -// -// // The above call decodes the current available buffer. -// // Part of the image can now be refreshed by calling -// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc. -// } -// WebPIDelete(idec); +/* + WebPInitDecBuffer(&output_buffer); + output_buffer.colorspace = mode; + ... + WebPIDecoder* idec = WebPINewDecoder(&output_buffer); + while (additional_data_is_available) { + // ... (get additional data in some new_data[] buffer) + status = WebPIAppend(idec, new_data, new_data_size); + if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) { + break; // an error occurred. + } + + // The above call decodes the current available buffer. + // Part of the image can now be refreshed by calling + // WebPIDecGetRGB()/WebPIDecGetYUVA() etc. + } + WebPIDelete(idec); +*/ // Creates a new incremental decoder with the supplied buffer parameter. // This output_buffer can be passed NULL, in which case a default output buffer @@ -281,7 +280,8 @@ typedef enum VP8StatusCode { // within valid bounds. // All other fields of WebPDecBuffer MUST remain constant between calls. // Returns NULL if the allocation failed. -WEBP_EXTERN WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer); +WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewDecoder( + WebPDecBuffer* output_buffer); // This function allocates and initializes an incremental-decoder object, which // will output the RGB/A samples specified by 'csp' into a preallocated @@ -293,7 +293,7 @@ WEBP_EXTERN WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer); // colorspace 'csp' is taken into account for allocating this buffer. All other // parameters are ignored. // Returns NULL if the allocation failed, or if some parameters are invalid. -WEBP_EXTERN WebPIDecoder* WebPINewRGB( +WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewRGB( WEBP_CSP_MODE csp, uint8_t* output_buffer, size_t output_buffer_size, int output_stride); @@ -308,7 +308,7 @@ WEBP_EXTERN WebPIDecoder* WebPINewRGB( // In this case, the output buffer will be automatically allocated (using // MODE_YUVA) when decoding starts. All parameters are then ignored. // Returns NULL if the allocation failed or if a parameter is invalid. -WEBP_EXTERN WebPIDecoder* WebPINewYUVA( +WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewYUVA( uint8_t* luma, size_t luma_size, int luma_stride, uint8_t* u, size_t u_size, int u_stride, uint8_t* v, size_t v_size, int v_stride, @@ -316,7 +316,7 @@ WEBP_EXTERN WebPIDecoder* WebPINewYUVA( // Deprecated version of the above, without the alpha plane. // Kept for backward compatibility. -WEBP_EXTERN WebPIDecoder* WebPINewYUV( +WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPINewYUV( uint8_t* luma, size_t luma_size, int luma_stride, uint8_t* u, size_t u_size, int u_stride, uint8_t* v, size_t v_size, int v_stride); @@ -346,21 +346,21 @@ WEBP_EXTERN VP8StatusCode WebPIUpdate( // (*last_y, *width etc.) can be NULL if corresponding information is not // needed. The values in these pointers are only valid on successful (non-NULL) // return. -WEBP_EXTERN uint8_t* WebPIDecGetRGB( +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPIDecGetRGB( const WebPIDecoder* idec, int* last_y, int* width, int* height, int* stride); // Same as above function to get a YUVA image. Returns pointer to the luma // plane or NULL in case of error. If there is no alpha information // the alpha pointer '*a' will be returned NULL. -WEBP_EXTERN uint8_t* WebPIDecGetYUVA( +WEBP_NODISCARD WEBP_EXTERN uint8_t* WebPIDecGetYUVA( const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v, uint8_t** a, int* width, int* height, int* stride, int* uv_stride, int* a_stride); // Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the // alpha information (if present). Kept for backward compatibility. -static WEBP_INLINE uint8_t* WebPIDecGetYUV( +WEBP_NODISCARD static WEBP_INLINE uint8_t* WebPIDecGetYUV( const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v, int* width, int* height, int* stride, int* uv_stride) { return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height, @@ -373,7 +373,7 @@ static WEBP_INLINE uint8_t* WebPIDecGetYUV( // Returns NULL in case the incremental decoder object is in an invalid state. // Otherwise returns the pointer to the internal representation. This structure // is read-only, tied to WebPIDecoder's lifespan and should not be modified. -WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea( +WEBP_NODISCARD WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea( const WebPIDecoder* idec, int* left, int* top, int* width, int* height); //------------------------------------------------------------------------------ @@ -389,7 +389,7 @@ WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea( CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK); // C) Adjust 'config', if needed - config.no_fancy_upsampling = 1; + config.options.no_fancy_upsampling = 1; config.output.colorspace = MODE_BGRA; // etc. @@ -468,12 +468,14 @@ struct WebPDecoderConfig { }; // Internal, version-checked, entry point -WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*, int); +WEBP_NODISCARD WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*, + int); // Initialize the configuration as empty. This function must always be // called first, unless WebPGetFeatures() is to be called. // Returns false in case of mismatched version. -static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) { +WEBP_NODISCARD static WEBP_INLINE int WebPInitDecoderConfig( + WebPDecoderConfig* config) { return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION); } @@ -488,8 +490,8 @@ static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) { // The return WebPIDecoder object must always be deleted calling WebPIDelete(). // Returns NULL in case of error (and config->status will then reflect // the error condition, if available). -WEBP_EXTERN WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, - WebPDecoderConfig* config); +WEBP_NODISCARD WEBP_EXTERN WebPIDecoder* WebPIDecode( + const uint8_t* data, size_t data_size, WebPDecoderConfig* config); // Non-incremental version. This version decodes the full data at once, taking // 'config' into account. Returns decoding status (which should be VP8_STATUS_OK diff --git a/thirdparty/libwebp/src/webp/demux.h b/thirdparty/libwebp/src/webp/demux.h index 846eeb15a9..8d246550ca 100644 --- a/thirdparty/libwebp/src/webp/demux.h +++ b/thirdparty/libwebp/src/webp/demux.h @@ -50,6 +50,7 @@ #include "./decode.h" // for WEBP_CSP_MODE #include "./mux_types.h" +#include "./types.h" #ifdef __cplusplus extern "C" { @@ -85,13 +86,13 @@ typedef enum WebPDemuxState { } WebPDemuxState; // Internal, version-checked, entry point -WEBP_EXTERN WebPDemuxer* WebPDemuxInternal( +WEBP_NODISCARD WEBP_EXTERN WebPDemuxer* WebPDemuxInternal( const WebPData*, int, WebPDemuxState*, int); // Parses the full WebP file given by 'data'. For single images the WebP file // header alone or the file header and the chunk header may be absent. // Returns a WebPDemuxer object on successful parse, NULL otherwise. -static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) { +WEBP_NODISCARD static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) { return WebPDemuxInternal(data, 0, NULL, WEBP_DEMUX_ABI_VERSION); } @@ -103,7 +104,7 @@ static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) { // If this data is volatile, the demuxer object should be deleted (by calling // WebPDemuxDelete()) and WebPDemuxPartial() called again on the new data. // This is usually an inexpensive operation. -static WEBP_INLINE WebPDemuxer* WebPDemuxPartial( +WEBP_NODISCARD static WEBP_INLINE WebPDemuxer* WebPDemuxPartial( const WebPData* data, WebPDemuxState* state) { return WebPDemuxInternal(data, 1, state, WEBP_DEMUX_ABI_VERSION); } @@ -164,14 +165,14 @@ struct WebPIterator { // Returns false if 'dmux' is NULL or frame 'frame_number' is not present. // Call WebPDemuxReleaseIterator() when use of the iterator is complete. // NOTE: 'dmux' must persist for the lifetime of 'iter'. -WEBP_EXTERN int WebPDemuxGetFrame( +WEBP_NODISCARD WEBP_EXTERN int WebPDemuxGetFrame( const WebPDemuxer* dmux, int frame_number, WebPIterator* iter); // Sets 'iter->fragment' to point to the next ('iter->frame_num' + 1) or // previous ('iter->frame_num' - 1) frame. These functions do not loop. // Returns true on success, false otherwise. -WEBP_EXTERN int WebPDemuxNextFrame(WebPIterator* iter); -WEBP_EXTERN int WebPDemuxPrevFrame(WebPIterator* iter); +WEBP_NODISCARD WEBP_EXTERN int WebPDemuxNextFrame(WebPIterator* iter); +WEBP_NODISCARD WEBP_EXTERN int WebPDemuxPrevFrame(WebPIterator* iter); // Releases any memory associated with 'iter'. // Must be called before any subsequent calls to WebPDemuxGetChunk() on the same @@ -202,15 +203,16 @@ struct WebPChunkIterator { // payloads are accessed through WebPDemuxGetFrame() and related functions. // Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete. // NOTE: 'dmux' must persist for the lifetime of the iterator. -WEBP_EXTERN int WebPDemuxGetChunk(const WebPDemuxer* dmux, - const char fourcc[4], int chunk_number, - WebPChunkIterator* iter); +WEBP_NODISCARD WEBP_EXTERN int WebPDemuxGetChunk(const WebPDemuxer* dmux, + const char fourcc[4], + int chunk_number, + WebPChunkIterator* iter); // Sets 'iter->chunk' to point to the next ('iter->chunk_num' + 1) or previous // ('iter->chunk_num' - 1) chunk. These functions do not loop. // Returns true on success, false otherwise. -WEBP_EXTERN int WebPDemuxNextChunk(WebPChunkIterator* iter); -WEBP_EXTERN int WebPDemuxPrevChunk(WebPChunkIterator* iter); +WEBP_NODISCARD WEBP_EXTERN int WebPDemuxNextChunk(WebPChunkIterator* iter); +WEBP_NODISCARD WEBP_EXTERN int WebPDemuxPrevChunk(WebPChunkIterator* iter); // Releases any memory associated with 'iter'. // Must be called before destroying the associated WebPDemuxer with @@ -257,21 +259,21 @@ struct WebPAnimDecoderOptions { }; // Internal, version-checked, entry point. -WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal( +WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal( WebPAnimDecoderOptions*, int); // Should always be called, to initialize a fresh WebPAnimDecoderOptions // structure before modification. Returns false in case of version mismatch. // WebPAnimDecoderOptionsInit() must have succeeded before using the // 'dec_options' object. -static WEBP_INLINE int WebPAnimDecoderOptionsInit( +WEBP_NODISCARD static WEBP_INLINE int WebPAnimDecoderOptionsInit( WebPAnimDecoderOptions* dec_options) { return WebPAnimDecoderOptionsInitInternal(dec_options, WEBP_DEMUX_ABI_VERSION); } // Internal, version-checked, entry point. -WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal( +WEBP_NODISCARD WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal( const WebPData*, const WebPAnimDecoderOptions*, int); // Creates and initializes a WebPAnimDecoder object. @@ -284,7 +286,7 @@ WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal( // Returns: // A pointer to the newly created WebPAnimDecoder object, or NULL in case of // parsing error, invalid option or memory error. -static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew( +WEBP_NODISCARD static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew( const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) { return WebPAnimDecoderNewInternal(webp_data, dec_options, WEBP_DEMUX_ABI_VERSION); @@ -306,8 +308,8 @@ struct WebPAnimInfo { // info - (out) global information fetched from the animation. // Returns: // True on success. -WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, - WebPAnimInfo* info); +WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderGetInfo( + const WebPAnimDecoder* dec, WebPAnimInfo* info); // Fetch the next frame from 'dec' based on options supplied to // WebPAnimDecoderNew(). This will be a fully reconstructed canvas of size @@ -321,8 +323,9 @@ WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, // Returns: // False if any of the arguments are NULL, or if there is a parsing or // decoding error, or if there are no more frames. Otherwise, returns true. -WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, - uint8_t** buf, int* timestamp); +WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, + uint8_t** buf, + int* timestamp); // Check if there are more frames left to decode. // Parameters: @@ -330,7 +333,8 @@ WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, // Returns: // True if 'dec' is not NULL and some frames are yet to be decoded. // Otherwise, returns false. -WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec); +WEBP_NODISCARD WEBP_EXTERN int WebPAnimDecoderHasMoreFrames( + const WebPAnimDecoder* dec); // Resets the WebPAnimDecoder object, so that next call to // WebPAnimDecoderGetNext() will restart decoding from 1st frame. This would be @@ -348,7 +352,7 @@ WEBP_EXTERN void WebPAnimDecoderReset(WebPAnimDecoder* dec); // // Parameters: // dec - (in) decoder instance from which the demuxer object is to be fetched. -WEBP_EXTERN const WebPDemuxer* WebPAnimDecoderGetDemuxer( +WEBP_NODISCARD WEBP_EXTERN const WebPDemuxer* WebPAnimDecoderGetDemuxer( const WebPAnimDecoder* dec); // Deletes the WebPAnimDecoder object. diff --git a/thirdparty/libwebp/src/webp/encode.h b/thirdparty/libwebp/src/webp/encode.h index 56b68e2f10..f3d59297c8 100644 --- a/thirdparty/libwebp/src/webp/encode.h +++ b/thirdparty/libwebp/src/webp/encode.h @@ -164,13 +164,14 @@ typedef enum WebPPreset { } WebPPreset; // Internal, version-checked, entry point -WEBP_EXTERN int WebPConfigInitInternal(WebPConfig*, WebPPreset, float, int); +WEBP_NODISCARD WEBP_EXTERN int WebPConfigInitInternal(WebPConfig*, WebPPreset, + float, int); // Should always be called, to initialize a fresh WebPConfig structure before // modification. Returns false in case of version mismatch. WebPConfigInit() // must have succeeded before using the 'config' object. // Note that the default values are lossless=0 and quality=75. -static WEBP_INLINE int WebPConfigInit(WebPConfig* config) { +WEBP_NODISCARD static WEBP_INLINE int WebPConfigInit(WebPConfig* config) { return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f, WEBP_ENCODER_ABI_VERSION); } @@ -179,8 +180,9 @@ static WEBP_INLINE int WebPConfigInit(WebPConfig* config) { // set of parameters (referred to by 'preset') and a given quality factor. // This function can be called as a replacement to WebPConfigInit(). Will // return false in case of error. -static WEBP_INLINE int WebPConfigPreset(WebPConfig* config, - WebPPreset preset, float quality) { +WEBP_NODISCARD static WEBP_INLINE int WebPConfigPreset(WebPConfig* config, + WebPPreset preset, + float quality) { return WebPConfigInitInternal(config, preset, quality, WEBP_ENCODER_ABI_VERSION); } @@ -191,11 +193,12 @@ static WEBP_INLINE int WebPConfigPreset(WebPConfig* config, // speed and final compressed size. // This function will overwrite several fields from config: 'method', 'quality' // and 'lossless'. Returns false in case of parameter error. -WEBP_EXTERN int WebPConfigLosslessPreset(WebPConfig* config, int level); +WEBP_NODISCARD WEBP_EXTERN int WebPConfigLosslessPreset(WebPConfig* config, + int level); // Returns true if 'config' is non-NULL and all configuration parameters are // within their valid ranges. -WEBP_EXTERN int WebPValidateConfig(const WebPConfig* config); +WEBP_NODISCARD WEBP_EXTERN int WebPValidateConfig(const WebPConfig* config); //------------------------------------------------------------------------------ // Input / Output @@ -255,8 +258,8 @@ WEBP_EXTERN void WebPMemoryWriterClear(WebPMemoryWriter* writer); // The custom writer to be used with WebPMemoryWriter as custom_ptr. Upon // completion, writer.mem and writer.size will hold the coded data. // writer.mem must be freed by calling WebPMemoryWriterClear. -WEBP_EXTERN int WebPMemoryWrite(const uint8_t* data, size_t data_size, - const WebPPicture* picture); +WEBP_NODISCARD WEBP_EXTERN int WebPMemoryWrite( + const uint8_t* data, size_t data_size, const WebPPicture* picture); // Progress hook, called from time to time to report progress. It can return // false to request an abort of the encoding process, or true otherwise if @@ -364,13 +367,13 @@ struct WebPPicture { }; // Internal, version-checked, entry point -WEBP_EXTERN int WebPPictureInitInternal(WebPPicture*, int); +WEBP_NODISCARD WEBP_EXTERN int WebPPictureInitInternal(WebPPicture*, int); // Should always be called, to initialize the structure. Returns false in case // of version mismatch. WebPPictureInit() must have succeeded before using the // 'picture' object. // Note that, by default, use_argb is false and colorspace is WEBP_YUV420. -static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) { +WEBP_NODISCARD static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) { return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION); } @@ -381,7 +384,7 @@ static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) { // Allocate y/u/v buffers as per colorspace/width/height specification. // Note! This function will free the previous buffer if needed. // Returns false in case of memory error. -WEBP_EXTERN int WebPPictureAlloc(WebPPicture* picture); +WEBP_NODISCARD WEBP_EXTERN int WebPPictureAlloc(WebPPicture* picture); // Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*(). // Note that this function does _not_ free the memory used by the 'picture' @@ -394,7 +397,8 @@ WEBP_EXTERN void WebPPictureFree(WebPPicture* picture); // will fully own the copied pixels (this is not a view). The 'dst' picture need // not be initialized as its content is overwritten. // Returns false in case of memory allocation error. -WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst); +WEBP_NODISCARD WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src, + WebPPicture* dst); // Compute the single distortion for packed planes of samples. // 'src' will be compared to 'ref', and the raw distortion stored into @@ -403,19 +407,18 @@ WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst); // 'x_step' is the horizontal stride (in bytes) between samples. // 'src/ref_stride' is the byte distance between rows. // Returns false in case of error (bad parameter, memory allocation error, ...). -WEBP_EXTERN int WebPPlaneDistortion(const uint8_t* src, size_t src_stride, - const uint8_t* ref, size_t ref_stride, - int width, int height, - size_t x_step, - int type, // 0 = PSNR, 1 = SSIM, 2 = LSIM - float* distortion, float* result); +WEBP_NODISCARD WEBP_EXTERN int WebPPlaneDistortion( + const uint8_t* src, size_t src_stride, + const uint8_t* ref, size_t ref_stride, int width, int height, size_t x_step, + int type, // 0 = PSNR, 1 = SSIM, 2 = LSIM + float* distortion, float* result); // Compute PSNR, SSIM or LSIM distortion metric between two pictures. Results // are in dB, stored in result[] in the B/G/R/A/All order. The distortion is // always performed using ARGB samples. Hence if the input is YUV(A), the // picture will be internally converted to ARGB (just for the measurement). // Warning: this function is rather CPU-intensive. -WEBP_EXTERN int WebPPictureDistortion( +WEBP_NODISCARD WEBP_EXTERN int WebPPictureDistortion( const WebPPicture* src, const WebPPicture* ref, int metric_type, // 0 = PSNR, 1 = SSIM, 2 = LSIM float result[5]); @@ -428,8 +431,8 @@ WEBP_EXTERN int WebPPictureDistortion( // must be fully be comprised inside the 'src' source picture. If the source // picture uses the YUV420 colorspace, the top and left coordinates will be // snapped to even values. -WEBP_EXTERN int WebPPictureCrop(WebPPicture* picture, - int left, int top, int width, int height); +WEBP_NODISCARD WEBP_EXTERN int WebPPictureCrop( + WebPPicture* picture, int left, int top, int width, int height); // Extracts a view from 'src' picture into 'dst'. The rectangle for the view // is defined by the top-left corner pixel coordinates (left, top) as well @@ -442,9 +445,9 @@ WEBP_EXTERN int WebPPictureCrop(WebPPicture* picture, // with WebPPictureInit() if it is different from 'src', since its content will // be overwritten. // Returns false in case of invalid parameters. -WEBP_EXTERN int WebPPictureView(const WebPPicture* src, - int left, int top, int width, int height, - WebPPicture* dst); +WEBP_NODISCARD WEBP_EXTERN int WebPPictureView( + const WebPPicture* src, int left, int top, int width, int height, + WebPPicture* dst); // Returns true if the 'picture' is actually a view and therefore does // not own the memory for pixels. @@ -455,29 +458,30 @@ WEBP_EXTERN int WebPPictureIsView(const WebPPicture* picture); // dimension will be calculated preserving the aspect ratio. // No gamma correction is applied. // Returns false in case of error (invalid parameter or insufficient memory). -WEBP_EXTERN int WebPPictureRescale(WebPPicture* picture, int width, int height); +WEBP_NODISCARD WEBP_EXTERN int WebPPictureRescale(WebPPicture* picture, + int width, int height); // Colorspace conversion function to import RGB samples. // Previous buffer will be free'd, if any. // *rgb buffer should have a size of at least height * rgb_stride. // Returns false in case of memory error. -WEBP_EXTERN int WebPPictureImportRGB( +WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportRGB( WebPPicture* picture, const uint8_t* rgb, int rgb_stride); // Same, but for RGBA buffer. -WEBP_EXTERN int WebPPictureImportRGBA( +WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportRGBA( WebPPicture* picture, const uint8_t* rgba, int rgba_stride); // Same, but for RGBA buffer. Imports the RGB direct from the 32-bit format // input buffer ignoring the alpha channel. Avoids needing to copy the data // to a temporary 24-bit RGB buffer to import the RGB only. -WEBP_EXTERN int WebPPictureImportRGBX( +WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportRGBX( WebPPicture* picture, const uint8_t* rgbx, int rgbx_stride); // Variants of the above, but taking BGR(A|X) input. -WEBP_EXTERN int WebPPictureImportBGR( +WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportBGR( WebPPicture* picture, const uint8_t* bgr, int bgr_stride); -WEBP_EXTERN int WebPPictureImportBGRA( +WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportBGRA( WebPPicture* picture, const uint8_t* bgra, int bgra_stride); -WEBP_EXTERN int WebPPictureImportBGRX( +WEBP_NODISCARD WEBP_EXTERN int WebPPictureImportBGRX( WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride); // Converts picture->argb data to the YUV420A format. The 'colorspace' @@ -486,24 +490,24 @@ WEBP_EXTERN int WebPPictureImportBGRX( // non-opaque transparent values is detected, and 'colorspace' will be // adjusted accordingly. Note that this method is lossy. // Returns false in case of error. -WEBP_EXTERN int WebPPictureARGBToYUVA(WebPPicture* picture, - WebPEncCSP /*colorspace = WEBP_YUV420*/); +WEBP_NODISCARD WEBP_EXTERN int WebPPictureARGBToYUVA( + WebPPicture* picture, WebPEncCSP /*colorspace = WEBP_YUV420*/); // Same as WebPPictureARGBToYUVA(), but the conversion is done using // pseudo-random dithering with a strength 'dithering' between // 0.0 (no dithering) and 1.0 (maximum dithering). This is useful // for photographic picture. -WEBP_EXTERN int WebPPictureARGBToYUVADithered( +WEBP_NODISCARD WEBP_EXTERN int WebPPictureARGBToYUVADithered( WebPPicture* picture, WebPEncCSP colorspace, float dithering); -// Performs 'sharp' RGBA->YUVA420 downsampling and colorspace conversion. +// Performs 'sharp' RGBA->YUVA420 downsampling and colorspace conversion // Downsampling is handled with extra care in case of color clipping. This // method is roughly 2x slower than WebPPictureARGBToYUVA() but produces better // and sharper YUV representation. // Returns false in case of error. -WEBP_EXTERN int WebPPictureSharpARGBToYUVA(WebPPicture* picture); +WEBP_NODISCARD WEBP_EXTERN int WebPPictureSharpARGBToYUVA(WebPPicture* picture); // kept for backward compatibility: -WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture); +WEBP_NODISCARD WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture); // Converts picture->yuv to picture->argb and sets picture->use_argb to true. // The input format must be YUV_420 or YUV_420A. The conversion from YUV420 to @@ -511,7 +515,7 @@ WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture); // Note that the use of this colorspace is discouraged if one has access to the // raw ARGB samples, since using YUV420 is comparatively lossy. // Returns false in case of error. -WEBP_EXTERN int WebPPictureYUVAToARGB(WebPPicture* picture); +WEBP_NODISCARD WEBP_EXTERN int WebPPictureYUVAToARGB(WebPPicture* picture); // Helper function: given a width x height plane of RGBA or YUV(A) samples // clean-up or smoothen the YUV or RGB samples under fully transparent area, @@ -541,7 +545,8 @@ WEBP_EXTERN void WebPBlendAlpha(WebPPicture* picture, uint32_t background_rgb); // the former for lossy encoding, and the latter for lossless encoding // (when config.lossless is true). Automatic conversion from one format to // another is provided but they both incur some loss. -WEBP_EXTERN int WebPEncode(const WebPConfig* config, WebPPicture* picture); +WEBP_NODISCARD WEBP_EXTERN int WebPEncode(const WebPConfig* config, + WebPPicture* picture); //------------------------------------------------------------------------------ diff --git a/thirdparty/libwebp/src/webp/mux.h b/thirdparty/libwebp/src/webp/mux.h index 7d27489a40..8fb067e435 100644 --- a/thirdparty/libwebp/src/webp/mux.h +++ b/thirdparty/libwebp/src/webp/mux.h @@ -16,12 +16,13 @@ #define WEBP_WEBP_MUX_H_ #include "./mux_types.h" +#include "./types.h" #ifdef __cplusplus extern "C" { #endif -#define WEBP_MUX_ABI_VERSION 0x0108 // MAJOR(8b) + MINOR(8b) +#define WEBP_MUX_ABI_VERSION 0x0109 // MAJOR(8b) + MINOR(8b) //------------------------------------------------------------------------------ // Mux API @@ -70,7 +71,7 @@ typedef struct WebPMuxAnimParams WebPMuxAnimParams; typedef struct WebPAnimEncoderOptions WebPAnimEncoderOptions; // Error codes -typedef enum WebPMuxError { +typedef enum WEBP_NODISCARD WebPMuxError { WEBP_MUX_OK = 1, WEBP_MUX_NOT_FOUND = 0, WEBP_MUX_INVALID_ARGUMENT = -1, @@ -104,13 +105,13 @@ WEBP_EXTERN int WebPGetMuxVersion(void); // Life of a Mux object // Internal, version-checked, entry point -WEBP_EXTERN WebPMux* WebPNewInternal(int); +WEBP_NODISCARD WEBP_EXTERN WebPMux* WebPNewInternal(int); // Creates an empty mux object. // Returns: // A pointer to the newly created empty mux object. // Or NULL in case of memory error. -static WEBP_INLINE WebPMux* WebPMuxNew(void) { +WEBP_NODISCARD static WEBP_INLINE WebPMux* WebPMuxNew(void) { return WebPNewInternal(WEBP_MUX_ABI_VERSION); } @@ -123,18 +124,21 @@ WEBP_EXTERN void WebPMuxDelete(WebPMux* mux); // Mux creation. // Internal, version-checked, entry point -WEBP_EXTERN WebPMux* WebPMuxCreateInternal(const WebPData*, int, int); +WEBP_NODISCARD WEBP_EXTERN WebPMux* WebPMuxCreateInternal(const WebPData*, int, + int); // Creates a mux object from raw data given in WebP RIFF format. // Parameters: // bitstream - (in) the bitstream data in WebP RIFF format // copy_data - (in) value 1 indicates given data WILL be copied to the mux -// object and value 0 indicates data will NOT be copied. +// object and value 0 indicates data will NOT be copied. If the +// data is not copied, it must exist for the lifetime of the +// mux object. // Returns: // A pointer to the mux object created from given data - on success. // NULL - In case of invalid data or memory error. -static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream, - int copy_data) { +WEBP_NODISCARD static WEBP_INLINE WebPMux* WebPMuxCreate( + const WebPData* bitstream, int copy_data) { return WebPMuxCreateInternal(bitstream, copy_data, WEBP_MUX_ABI_VERSION); } @@ -154,7 +158,9 @@ static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream, // e.g., "ICCP", "XMP ", "EXIF" etc. // chunk_data - (in) the chunk data to be added // copy_data - (in) value 1 indicates given data WILL be copied to the mux -// object and value 0 indicates data will NOT be copied. +// object and value 0 indicates data will NOT be copied. If the +// data is not copied, it must exist until a call to +// WebPMuxAssemble() is made. // Returns: // WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL // or if fourcc corresponds to an image chunk. @@ -217,7 +223,9 @@ struct WebPMuxFrameInfo { // bitstream - (in) can be a raw VP8/VP8L bitstream or a single-image // WebP file (non-animated) // copy_data - (in) value 1 indicates given data WILL be copied to the mux -// object and value 0 indicates data will NOT be copied. +// object and value 0 indicates data will NOT be copied. If the +// data is not copied, it must exist until a call to +// WebPMuxAssemble() is made. // Returns: // WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL. // WEBP_MUX_MEMORY_ERROR - on memory allocation error. @@ -235,7 +243,9 @@ WEBP_EXTERN WebPMuxError WebPMuxSetImage( // mux - (in/out) object to which the frame is to be added // frame - (in) frame data. // copy_data - (in) value 1 indicates given data WILL be copied to the mux -// object and value 0 indicates data will NOT be copied. +// object and value 0 indicates data will NOT be copied. If the +// data is not copied, it must exist until a call to +// WebPMuxAssemble() is made. // Returns: // WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL // or if content of 'frame' is invalid. @@ -449,7 +459,7 @@ WEBP_EXTERN int WebPAnimEncoderOptionsInitInternal( // structure before modification. Returns false in case of version mismatch. // WebPAnimEncoderOptionsInit() must have succeeded before using the // 'enc_options' object. -static WEBP_INLINE int WebPAnimEncoderOptionsInit( +WEBP_NODISCARD static WEBP_INLINE int WebPAnimEncoderOptionsInit( WebPAnimEncoderOptions* enc_options) { return WebPAnimEncoderOptionsInitInternal(enc_options, WEBP_MUX_ABI_VERSION); } @@ -490,7 +500,7 @@ static WEBP_INLINE WebPAnimEncoder* WebPAnimEncoderNew( // Returns: // On error, returns false and frame->error_code is set appropriately. // Otherwise, returns true. -WEBP_EXTERN int WebPAnimEncoderAdd( +WEBP_NODISCARD WEBP_EXTERN int WebPAnimEncoderAdd( WebPAnimEncoder* enc, struct WebPPicture* frame, int timestamp_ms, const struct WebPConfig* config); @@ -503,8 +513,8 @@ WEBP_EXTERN int WebPAnimEncoderAdd( // webp_data - (out) generated WebP bitstream. // Returns: // True on success. -WEBP_EXTERN int WebPAnimEncoderAssemble(WebPAnimEncoder* enc, - WebPData* webp_data); +WEBP_NODISCARD WEBP_EXTERN int WebPAnimEncoderAssemble(WebPAnimEncoder* enc, + WebPData* webp_data); // Get error string corresponding to the most recent call using 'enc'. The // returned string is owned by 'enc' and is valid only until the next call to @@ -522,6 +532,57 @@ WEBP_EXTERN const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc); WEBP_EXTERN void WebPAnimEncoderDelete(WebPAnimEncoder* enc); //------------------------------------------------------------------------------ +// Non-image chunks. + +// Note: Only non-image related chunks should be managed through chunk APIs. +// (Image related chunks are: "ANMF", "VP8 ", "VP8L" and "ALPH"). + +// Adds a chunk with id 'fourcc' and data 'chunk_data' in the enc object. +// Any existing chunk(s) with the same id will be removed. +// Parameters: +// enc - (in/out) object to which the chunk is to be added +// fourcc - (in) a character array containing the fourcc of the given chunk; +// e.g., "ICCP", "XMP ", "EXIF", etc. +// chunk_data - (in) the chunk data to be added +// copy_data - (in) value 1 indicates given data WILL be copied to the enc +// object and value 0 indicates data will NOT be copied. If the +// data is not copied, it must exist until a call to +// WebPAnimEncoderAssemble() is made. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if enc, fourcc or chunk_data is NULL. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPAnimEncoderSetChunk( + WebPAnimEncoder* enc, const char fourcc[4], const WebPData* chunk_data, + int copy_data); + +// Gets a reference to the data of the chunk with id 'fourcc' in the enc object. +// The caller should NOT free the returned data. +// Parameters: +// enc - (in) object from which the chunk data is to be fetched +// fourcc - (in) a character array containing the fourcc of the chunk; +// e.g., "ICCP", "XMP ", "EXIF", etc. +// chunk_data - (out) returned chunk data +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if enc, fourcc or chunk_data is NULL. +// WEBP_MUX_NOT_FOUND - If enc does not contain a chunk with the given id. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPAnimEncoderGetChunk( + const WebPAnimEncoder* enc, const char fourcc[4], WebPData* chunk_data); + +// Deletes the chunk with the given 'fourcc' from the enc object. +// Parameters: +// enc - (in/out) object from which the chunk is to be deleted +// fourcc - (in) a character array containing the fourcc of the chunk; +// e.g., "ICCP", "XMP ", "EXIF", etc. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if enc or fourcc is NULL. +// WEBP_MUX_NOT_FOUND - If enc does not contain a chunk with the given fourcc. +// WEBP_MUX_OK - on success. +WEBP_EXTERN WebPMuxError WebPAnimEncoderDeleteChunk( + WebPAnimEncoder* enc, const char fourcc[4]); + +//------------------------------------------------------------------------------ #ifdef __cplusplus } // extern "C" diff --git a/thirdparty/libwebp/src/webp/mux_types.h b/thirdparty/libwebp/src/webp/mux_types.h index 2fe8195839..c585d2082f 100644 --- a/thirdparty/libwebp/src/webp/mux_types.h +++ b/thirdparty/libwebp/src/webp/mux_types.h @@ -79,7 +79,8 @@ static WEBP_INLINE void WebPDataClear(WebPData* webp_data) { // Allocates necessary storage for 'dst' and copies the contents of 'src'. // Returns true on success. -static WEBP_INLINE int WebPDataCopy(const WebPData* src, WebPData* dst) { +WEBP_NODISCARD static WEBP_INLINE int WebPDataCopy(const WebPData* src, + WebPData* dst) { if (src == NULL || dst == NULL) return 0; WebPDataInit(dst); if (src->bytes != NULL && src->size != 0) { diff --git a/thirdparty/libwebp/src/webp/types.h b/thirdparty/libwebp/src/webp/types.h index f255432e41..9c17edec45 100644 --- a/thirdparty/libwebp/src/webp/types.h +++ b/thirdparty/libwebp/src/webp/types.h @@ -36,18 +36,39 @@ typedef long long int int64_t; #define WEBP_INLINE __forceinline #endif /* _MSC_VER */ +#ifndef WEBP_NODISCARD +#if defined(WEBP_ENABLE_NODISCARD) && WEBP_ENABLE_NODISCARD +#if (defined(__cplusplus) && __cplusplus >= 201700L) || \ + (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) +#define WEBP_NODISCARD [[nodiscard]] +#else +// gcc's __has_attribute does not work for enums. +#if defined(__clang__) && defined(__has_attribute) +#if __has_attribute(warn_unused_result) +#define WEBP_NODISCARD __attribute__((warn_unused_result)) +#else +#define WEBP_NODISCARD +#endif /* __has_attribute(warn_unused_result) */ +#else +#define WEBP_NODISCARD +#endif /* defined(__clang__) && defined(__has_attribute) */ +#endif /* (defined(__cplusplus) && __cplusplus >= 201700L) || + (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) */ +#else +#define WEBP_NODISCARD +#endif /* defined(WEBP_ENABLE_NODISCARD) && WEBP_ENABLE_NODISCARD */ +#endif /* WEBP_NODISCARD */ + #ifndef WEBP_EXTERN // This explicitly marks library functions and allows for changing the // signature for e.g., Windows DLL builds. -# if defined(__GNUC__) && __GNUC__ >= 4 +# if defined(_WIN32) && defined(WEBP_DLL) +# define WEBP_EXTERN __declspec(dllexport) +# elif defined(__GNUC__) && __GNUC__ >= 4 # define WEBP_EXTERN extern __attribute__ ((visibility ("default"))) # else -# if defined(_MSC_VER) && defined(WEBP_DLL) -# define WEBP_EXTERN __declspec(dllexport) -# else -# define WEBP_EXTERN extern -# endif -# endif /* __GNUC__ >= 4 */ +# define WEBP_EXTERN extern +# endif /* defined(_WIN32) && defined(WEBP_DLL) */ #endif /* WEBP_EXTERN */ // Macro to check ABI compatibility (same major revision number) @@ -60,7 +81,7 @@ extern "C" { // Allocates 'size' bytes of memory. Returns NULL upon error. Memory // must be deallocated by calling WebPFree(). This function is made available // by the core 'libwebp' library. -WEBP_EXTERN void* WebPMalloc(size_t size); +WEBP_NODISCARD WEBP_EXTERN void* WebPMalloc(size_t size); // Releases memory returned by the WebPDecode*() functions (from decode.h). WEBP_EXTERN void WebPFree(void* ptr); diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index f271b52faf..3ae166cf22 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -5,9 +5,11 @@ #define THORVG_SVG_LOADER_SUPPORT #define THORVG_PNG_LOADER_SUPPORT #define THORVG_JPG_LOADER_SUPPORT -#define THORVG_WEBP_LOADER_SUPPORT #define THORVG_THREAD_SUPPORT +// Added conditionally if webp module is enabled. +//#define THORVG_WEBP_LOADER_SUPPORT + // For internal debugging: //#define THORVG_LOG_ENABLED diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index 23604afbcd..796060d3fb 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -38,9 +38,11 @@ cat << EOF > ../inc/config.h #define THORVG_SVG_LOADER_SUPPORT #define THORVG_PNG_LOADER_SUPPORT #define THORVG_JPG_LOADER_SUPPORT -#define THORVG_WEBP_LOADER_SUPPORT #define THORVG_THREAD_SUPPORT +// Added conditionally if webp module is enabled. +//#define THORVG_WEBP_LOADER_SUPPORT + // For internal debugging: //#define THORVG_LOG_ENABLED |
