summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy7
-rw-r--r--AUTHORS.md4
-rw-r--r--COPYRIGHT.txt25
-rw-r--r--DONORS.md71
-rw-r--r--SConstruct16
-rw-r--r--core/config/engine.cpp4
-rw-r--r--core/config/engine.h2
-rw-r--r--core/config/project_settings.cpp4
-rw-r--r--core/core_bind.cpp13
-rw-r--r--core/core_bind.h4
-rw-r--r--core/core_constants.cpp1
-rw-r--r--core/debugger/remote_debugger_peer.cpp7
-rw-r--r--core/debugger/script_debugger.cpp2
-rw-r--r--core/input/input.cpp63
-rw-r--r--core/input/input.h8
-rw-r--r--core/input/input_event.cpp109
-rw-r--r--core/input/input_event.h12
-rw-r--r--core/input/input_map.cpp42
-rw-r--r--core/input/input_map.h4
-rw-r--r--core/io/compression.cpp6
-rw-r--r--core/io/dir_access.cpp8
-rw-r--r--core/io/http_client.cpp11
-rw-r--r--core/io/http_client.h3
-rw-r--r--core/io/http_client_tcp.cpp92
-rw-r--r--core/io/http_client_tcp.h13
-rw-r--r--core/io/image.cpp56
-rw-r--r--core/io/image.h7
-rw-r--r--core/io/ip.cpp42
-rw-r--r--core/io/marshalls.cpp5
-rw-r--r--core/io/packet_peer.cpp2
-rw-r--r--core/io/resource.h2
-rw-r--r--core/io/resource_loader.cpp26
-rw-r--r--core/io/stream_peer.cpp4
-rw-r--r--core/math/aabb.h2
-rw-r--r--core/math/basis.h2
-rw-r--r--core/math/color.h2
-rw-r--r--core/math/convex_hull.cpp8
-rw-r--r--core/math/expression.cpp43
-rw-r--r--core/math/face3.cpp8
-rw-r--r--core/math/face3.h2
-rw-r--r--core/math/geometry_3d.cpp51
-rw-r--r--core/math/plane.h2
-rw-r--r--core/math/quaternion.h2
-rw-r--r--core/math/rect2.h36
-rw-r--r--core/math/transform_2d.h2
-rw-r--r--core/math/transform_3d.h2
-rw-r--r--core/math/vector2.h4
-rw-r--r--core/math/vector3.h2
-rw-r--r--core/math/vector3i.h2
-rw-r--r--core/object/object.h4
-rw-r--r--core/os/os.h7
-rw-r--r--core/string/locales.h1197
-rw-r--r--core/string/translation.cpp1235
-rw-r--r--core/string/translation.h36
-rw-r--r--core/string/ustring.cpp179
-rw-r--r--core/string/ustring.h3
-rw-r--r--core/templates/vector.h27
-rw-r--r--core/typedefs.h11
-rw-r--r--core/variant/array.cpp24
-rw-r--r--core/variant/array.h4
-rw-r--r--core/variant/binder_common.h50
-rw-r--r--core/variant/dictionary.cpp2
-rw-r--r--core/variant/variant.cpp24
-rw-r--r--core/variant/variant.h6
-rw-r--r--core/variant/variant_call.cpp23
-rw-r--r--core/variant/variant_op.h1
-rw-r--r--core/variant/variant_parser.cpp53
-rw-r--r--core/variant/variant_utility.cpp2
-rw-r--r--doc/classes/@GlobalScope.xml5
-rw-r--r--doc/classes/AnimationNodeOneShot.xml15
-rw-r--r--doc/classes/Area2D.xml6
-rw-r--r--doc/classes/Area3D.xml6
-rw-r--r--doc/classes/Array.xml13
-rw-r--r--doc/classes/ArrayMesh.xml3
-rw-r--r--doc/classes/AudioEffectSpectrumAnalyzer.xml4
-rw-r--r--doc/classes/AudioStreamGenerator.xml2
-rw-r--r--doc/classes/AudioStreamGeneratorPlayback.xml2
-rw-r--r--doc/classes/BaseButton.xml10
-rw-r--r--doc/classes/BitMap.xml13
-rw-r--r--doc/classes/BoxMesh.xml2
-rw-r--r--doc/classes/BoxShape3D.xml2
-rw-r--r--doc/classes/Button.xml8
-rw-r--r--doc/classes/ButtonGroup.xml2
-rw-r--r--doc/classes/CapsuleMesh.xml4
-rw-r--r--doc/classes/CapsuleShape3D.xml4
-rw-r--r--doc/classes/CharacterBody2D.xml8
-rw-r--r--doc/classes/CharacterBody3D.xml4
-rw-r--r--doc/classes/CheckBox.xml8
-rw-r--r--doc/classes/CheckButton.xml8
-rw-r--r--doc/classes/CodeEdit.xml17
-rw-r--r--doc/classes/CollisionObject2D.xml2
-rw-r--r--doc/classes/CollisionObject3D.xml2
-rw-r--r--doc/classes/Control.xml6
-rw-r--r--doc/classes/CurveTexture.xml2
-rw-r--r--doc/classes/CurveXYZTexture.xml2
-rw-r--r--doc/classes/CylinderMesh.xml4
-rw-r--r--doc/classes/CylinderShape3D.xml2
-rw-r--r--doc/classes/DisplayServer.xml66
-rw-r--r--doc/classes/EditorInspector.xml9
-rw-r--r--doc/classes/EditorPlugin.xml4
-rw-r--r--doc/classes/Environment.xml18
-rw-r--r--doc/classes/FlowContainer.xml20
-rw-r--r--doc/classes/FontData.xml16
-rw-r--r--doc/classes/GPUParticles2D.xml29
-rw-r--r--doc/classes/GPUParticles3D.xml6
-rw-r--r--doc/classes/GeometryInstance3D.xml9
-rw-r--r--doc/classes/GradientTexture1D.xml2
-rw-r--r--doc/classes/GraphNode.xml18
-rw-r--r--doc/classes/HFlowContainer.xml19
-rw-r--r--doc/classes/HSplitContainer.xml2
-rw-r--r--doc/classes/Image.xml49
-rw-r--r--doc/classes/ImporterMesh.xml5
-rw-r--r--doc/classes/Input.xml9
-rw-r--r--doc/classes/ItemList.xml2
-rw-r--r--doc/classes/JSON.xml42
-rw-r--r--doc/classes/Light3D.xml15
-rw-r--r--doc/classes/LineEdit.xml18
-rw-r--r--doc/classes/LinkButton.xml6
-rw-r--r--doc/classes/MenuButton.xml6
-rw-r--r--doc/classes/OS.xml9
-rw-r--r--doc/classes/Object.xml2
-rw-r--r--doc/classes/OptionButton.xml13
-rw-r--r--doc/classes/PackedByteArray.xml5
-rw-r--r--doc/classes/PackedColorArray.xml5
-rw-r--r--doc/classes/PackedFloat32Array.xml5
-rw-r--r--doc/classes/PackedFloat64Array.xml5
-rw-r--r--doc/classes/PackedInt32Array.xml5
-rw-r--r--doc/classes/PackedInt64Array.xml5
-rw-r--r--doc/classes/PackedScene.xml2
-rw-r--r--doc/classes/PackedStringArray.xml5
-rw-r--r--doc/classes/PackedVector2Array.xml5
-rw-r--r--doc/classes/PackedVector3Array.xml5
-rw-r--r--doc/classes/PanoramaSkyMaterial.xml3
-rw-r--r--doc/classes/PhysicsBody2D.xml12
-rw-r--r--doc/classes/PhysicsBody3D.xml10
-rw-r--r--doc/classes/PopupMenu.xml13
-rw-r--r--doc/classes/ProgressBar.xml2
-rw-r--r--doc/classes/ProjectSettings.xml41
-rw-r--r--doc/classes/RDTextureView.xml2
-rw-r--r--doc/classes/RDVertexAttribute.xml2
-rw-r--r--doc/classes/RayCast2D.xml8
-rw-r--r--doc/classes/RayCast3D.xml8
-rw-r--r--doc/classes/Rect2.xml5
-rw-r--r--doc/classes/Rect2i.xml7
-rw-r--r--doc/classes/ReflectionProbe.xml1
-rw-r--r--doc/classes/RenderingDevice.xml20
-rw-r--r--doc/classes/RenderingServer.xml19
-rw-r--r--doc/classes/RichTextLabel.xml17
-rw-r--r--doc/classes/ShapeCast2D.xml8
-rw-r--r--doc/classes/Skin.xml7
-rw-r--r--doc/classes/SoftDynamicBody3D.xml1
-rw-r--r--doc/classes/StreamPeerBuffer.xml10
-rw-r--r--doc/classes/String.xml17
-rw-r--r--doc/classes/TabBar.xml68
-rw-r--r--doc/classes/TabContainer.xml6
-rw-r--r--doc/classes/TextEdit.xml27
-rw-r--r--doc/classes/TextLine.xml4
-rw-r--r--doc/classes/TextParagraph.xml7
-rw-r--r--doc/classes/TextServer.xml63
-rw-r--r--doc/classes/TextServerExtension.xml43
-rw-r--r--doc/classes/TileMap.xml5
-rw-r--r--doc/classes/TileSetAtlasSource.xml2
-rw-r--r--doc/classes/Transform3D.xml8
-rw-r--r--doc/classes/TranslationServer.xml54
-rw-r--r--doc/classes/Tree.xml33
-rw-r--r--doc/classes/TreeItem.xml14
-rw-r--r--doc/classes/Tween.xml2
-rw-r--r--doc/classes/VFlowContainer.xml19
-rw-r--r--doc/classes/VSplitContainer.xml2
-rw-r--r--doc/classes/Viewport.xml12
-rw-r--r--doc/classes/VisualShaderNode.xml14
-rw-r--r--doc/classes/VisualShaderNodeClamp.xml9
-rw-r--r--doc/classes/VisualShaderNodeCompare.xml13
-rw-r--r--doc/classes/VisualShaderNodeDerivativeFunc.xml45
-rw-r--r--doc/classes/VisualShaderNodeFaceForward.xml2
-rw-r--r--doc/classes/VisualShaderNodeMix.xml18
-rw-r--r--doc/classes/VisualShaderNodeMultiplyAdd.xml11
-rw-r--r--doc/classes/VisualShaderNodeScalarDerivativeFunc.xml30
-rw-r--r--doc/classes/VisualShaderNodeSmoothStep.xml18
-rw-r--r--doc/classes/VisualShaderNodeStep.xml18
-rw-r--r--doc/classes/VisualShaderNodeSwitch.xml13
-rw-r--r--doc/classes/VisualShaderNodeVec2Constant.xml16
-rw-r--r--doc/classes/VisualShaderNodeVec2Uniform.xml19
-rw-r--r--doc/classes/VisualShaderNodeVectorBase.xml26
-rw-r--r--doc/classes/VisualShaderNodeVectorCompose.xml2
-rw-r--r--doc/classes/VisualShaderNodeVectorDecompose.xml2
-rw-r--r--doc/classes/VisualShaderNodeVectorDerivativeFunc.xml30
-rw-r--r--doc/classes/VisualShaderNodeVectorDistance.xml2
-rw-r--r--doc/classes/VisualShaderNodeVectorFunc.xml2
-rw-r--r--doc/classes/VisualShaderNodeVectorLen.xml2
-rw-r--r--doc/classes/VisualShaderNodeVectorOp.xml2
-rw-r--r--doc/classes/VoxelGI.xml4
-rw-r--r--doc/classes/Window.xml10
-rw-r--r--doc/classes/XRInterfaceExtension.xml50
-rw-r--r--doc/classes/XRPose.xml14
-rw-r--r--doc/classes/XRPositionalTracker.xml3
-rw-r--r--doc/classes/XRServer.xml18
-rwxr-xr-xdoc/tools/make_rst.py2
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp19
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp56
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp4
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h4
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp140
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h119
-rw-r--r--drivers/gles3/texture_loader_gles3.cpp5
-rw-r--r--drivers/unix/ip_unix.cpp2
-rw-r--r--drivers/unix/os_unix.cpp4
-rw-r--r--drivers/unix/os_unix.h4
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp77
-rw-r--r--drivers/vulkan/rendering_device_vulkan.h2
-rw-r--r--drivers/vulkan/vulkan_context.cpp306
-rw-r--r--drivers/vulkan/vulkan_context.h4
-rw-r--r--drivers/windows/dir_access_windows.cpp11
-rw-r--r--editor/animation_bezier_editor.cpp96
-rw-r--r--editor/animation_bezier_editor.h9
-rw-r--r--editor/animation_track_editor.cpp160
-rw-r--r--editor/animation_track_editor.h17
-rw-r--r--editor/code_editor.cpp39
-rw-r--r--editor/connections_dialog.cpp49
-rw-r--r--editor/create_dialog.cpp102
-rw-r--r--editor/create_dialog.h12
-rw-r--r--editor/debugger/editor_debugger_inspector.cpp7
-rw-r--r--editor/debugger/editor_debugger_node.cpp15
-rw-r--r--editor/debugger/editor_debugger_tree.cpp4
-rw-r--r--editor/debugger/editor_profiler.cpp3
-rw-r--r--editor/debugger/script_editor_debugger.cpp136
-rw-r--r--editor/debugger/script_editor_debugger.h10
-rw-r--r--editor/doc_tools.cpp25
-rw-r--r--editor/editor_asset_installer.cpp67
-rw-r--r--editor/editor_asset_installer.h3
-rw-r--r--editor/editor_audio_buses.cpp4
-rw-r--r--editor/editor_command_palette.cpp2
-rw-r--r--editor/editor_export.cpp34
-rw-r--r--editor/editor_export.h3
-rw-r--r--editor/editor_file_dialog.cpp45
-rw-r--r--editor/editor_file_dialog.h4
-rw-r--r--editor/editor_file_system.cpp6
-rw-r--r--editor/editor_file_system.h1
-rw-r--r--editor/editor_fonts.cpp5
-rw-r--r--editor/editor_help.cpp109
-rw-r--r--editor/editor_help.h15
-rw-r--r--editor/editor_inspector.cpp82
-rw-r--r--editor/editor_locale_dialog.cpp563
-rw-r--r--editor/editor_locale_dialog.h (renamed from modules/pvr/register_types.h)68
-rw-r--r--editor/editor_log.cpp2
-rw-r--r--editor/editor_node.cpp269
-rw-r--r--editor/editor_node.h31
-rw-r--r--editor/editor_paths.cpp5
-rw-r--r--editor/editor_plugin.cpp52
-rw-r--r--editor/editor_plugin.h3
-rw-r--r--editor/editor_properties.cpp534
-rw-r--r--editor/editor_properties.h65
-rw-r--r--editor/editor_properties_array_dict.cpp2
-rw-r--r--editor/editor_resource_picker.cpp9
-rw-r--r--editor/editor_run.cpp18
-rw-r--r--editor/editor_settings.cpp32
-rw-r--r--editor/editor_settings_dialog.cpp (renamed from editor/settings_config_dialog.cpp)8
-rw-r--r--editor/editor_settings_dialog.h (renamed from editor/settings_config_dialog.h)8
-rw-r--r--editor/editor_themes.cpp125
-rw-r--r--editor/editor_toaster.cpp8
-rw-r--r--editor/editor_toaster.h1
-rw-r--r--editor/filesystem_dock.cpp28
-rw-r--r--editor/filesystem_dock.h6
-rw-r--r--editor/groups_editor.cpp46
-rw-r--r--editor/groups_editor.h3
-rw-r--r--editor/icons/AudioBusLayout.svg2
-rw-r--r--editor/icons/AudioListener2D.svg2
-rw-r--r--editor/icons/AudioStreamMP3.svg2
-rw-r--r--editor/icons/AudioStreamOGGVorbis.svg2
-rw-r--r--editor/icons/AudioStreamPlayer.svg2
-rw-r--r--editor/icons/AudioStreamPlayer2D.svg2
-rw-r--r--editor/icons/AudioStreamPlayer3D.svg2
-rw-r--r--editor/icons/AudioStreamSample.svg2
-rw-r--r--editor/icons/BoxMesh.svg2
-rw-r--r--editor/icons/BoxShape3D.svg2
-rw-r--r--editor/icons/Breakpoint.svg2
-rw-r--r--editor/icons/BusVuEmpty.svg2
-rw-r--r--editor/icons/BusVuFull.svg2
-rw-r--r--editor/icons/CPUParticles2D.svg2
-rw-r--r--editor/icons/Callable.svg2
-rw-r--r--editor/icons/CanvasGroup.svg2
-rw-r--r--editor/icons/CanvasModulate.svg2
-rw-r--r--editor/icons/CapsuleShape2D.svg2
-rw-r--r--editor/icons/CapsuleShape3D.svg2
-rw-r--r--editor/icons/CircleShape2D.svg2
-rw-r--r--editor/icons/CodeEdit.svg2
-rw-r--r--editor/icons/ColorRect.svg2
-rw-r--r--editor/icons/ConcavePolygonShape2D.svg2
-rw-r--r--editor/icons/ConcavePolygonShape3D.svg2
-rw-r--r--editor/icons/ConvexPolygonShape2D.svg2
-rw-r--r--editor/icons/ConvexPolygonShape3D.svg2
-rw-r--r--editor/icons/CurveClose.svg2
-rw-r--r--editor/icons/CurveCreate.svg2
-rw-r--r--editor/icons/CurveCurve.svg2
-rw-r--r--editor/icons/CurveDelete.svg2
-rw-r--r--editor/icons/CurveEdit.svg2
-rw-r--r--editor/icons/CylinderShape3D.svg2
-rw-r--r--editor/icons/DebugSkipBreakpointsOff.svg2
-rw-r--r--editor/icons/DebugSkipBreakpointsOn.svg2
-rw-r--r--editor/icons/Decal.svg2
-rw-r--r--editor/icons/DirectionalLight2D.svg2
-rw-r--r--editor/icons/EditorBoneHandle.svg2
-rw-r--r--editor/icons/EditorControlAnchor.svg2
-rw-r--r--editor/icons/EditorCurveHandle.svg2
-rw-r--r--editor/icons/EditorPathSharpHandle.svg2
-rw-r--r--editor/icons/EditorPathSmoothHandle.svg2
-rw-r--r--editor/icons/EditorPositionPrevious.svg2
-rw-r--r--editor/icons/EditorPositionUnselected.svg2
-rw-r--r--editor/icons/Error.svg2
-rw-r--r--editor/icons/ErrorWarning.svg2
-rw-r--r--editor/icons/FileBroken.svg2
-rw-r--r--editor/icons/FileBrokenBigThumb.svg2
-rw-r--r--editor/icons/FileDead.svg2
-rw-r--r--editor/icons/FileDeadBigThumb.svg2
-rw-r--r--editor/icons/FileDeadMediumThumb.svg2
-rw-r--r--editor/icons/GizmoCPUParticles3D.svg2
-rw-r--r--editor/icons/GizmoDirectionalLight.svg2
-rw-r--r--editor/icons/GizmoLight.svg2
-rw-r--r--editor/icons/GizmoSpotLight.svg2
-rw-r--r--editor/icons/GizmoVoxelGI.svg (renamed from editor/icons/GizmoGIProbe.svg)0
-rw-r--r--editor/icons/GraphEdit.svg2
-rw-r--r--editor/icons/GraphNode.svg2
-rw-r--r--editor/icons/GuiDropdown.svg2
-rw-r--r--editor/icons/GuiScrollGrabber.svg2
-rw-r--r--editor/icons/GuiScrollGrabberHl.svg2
-rw-r--r--editor/icons/GuiScrollGrabberPressed.svg2
-rw-r--r--editor/icons/GuiSliderGrabber.svg2
-rw-r--r--editor/icons/GuiSliderGrabberHl.svg2
-rw-r--r--editor/icons/GuiToggleOn.svg2
-rw-r--r--editor/icons/GuiToggleOnMirrored.svg2
-rw-r--r--editor/icons/GuiTreeArrowDown.svg2
-rw-r--r--editor/icons/GuiTreeArrowLeft.svg2
-rw-r--r--editor/icons/GuiTreeArrowRight.svg2
-rw-r--r--editor/icons/GuiTreeUpdown.svg2
-rw-r--r--editor/icons/HFlowContainer.svg1
-rw-r--r--editor/icons/Heart.svg2
-rw-r--r--editor/icons/HeightMapShape3D.svg2
-rw-r--r--editor/icons/Help.svg2
-rw-r--r--editor/icons/ImmediateMesh.svg2
-rw-r--r--editor/icons/ImportCheck.svg2
-rw-r--r--editor/icons/ImportFail.svg2
-rw-r--r--editor/icons/KeyBlendShape.svg45
-rw-r--r--editor/icons/KeyInvalid.svg2
-rw-r--r--editor/icons/KeyTrackBlendShape.svg46
-rw-r--r--editor/icons/KeyTrackPosition.svg48
-rw-r--r--editor/icons/KeyTrackRotation.svg48
-rw-r--r--editor/icons/KeyTrackScale.svg48
-rw-r--r--editor/icons/KeyXPosition.svg44
-rw-r--r--editor/icons/KeyXRotation.svg45
-rw-r--r--editor/icons/KeyXScale.svg45
-rw-r--r--editor/icons/MaterialPreviewCube.svg2
-rw-r--r--editor/icons/MaterialPreviewCubeOff.svg2
-rw-r--r--editor/icons/MaterialPreviewLight1Off.svg2
-rw-r--r--editor/icons/MaterialPreviewSphereOff.svg2
-rw-r--r--editor/icons/NavigationAgent2D.svg2
-rw-r--r--editor/icons/NavigationAgent3D.svg2
-rw-r--r--editor/icons/NavigationObstacle2D.svg2
-rw-r--r--editor/icons/NavigationObstacle3D.svg2
-rw-r--r--editor/icons/NodeDisabled.svg2
-rw-r--r--editor/icons/Notification.svg2
-rw-r--r--editor/icons/NotificationDisabled.svg2
-rw-r--r--editor/icons/OccluderPolygon2D.svg2
-rw-r--r--editor/icons/OverbrightIndicator.svg2
-rw-r--r--editor/icons/PackedByteArray.svg2
-rw-r--r--editor/icons/PackedColorArray.svg2
-rw-r--r--editor/icons/PageFirst.svg48
-rw-r--r--editor/icons/PageLast.svg48
-rw-r--r--editor/icons/PageNext.svg43
-rw-r--r--editor/icons/PagePrevious.svg43
-rw-r--r--editor/icons/ParallaxBackground.svg2
-rw-r--r--editor/icons/ParallaxLayer.svg2
-rw-r--r--editor/icons/PlayOverlay.svg2
-rw-r--r--editor/icons/RectangleShape2D.svg2
-rw-r--r--editor/icons/ReverseGradient.svg2
-rw-r--r--editor/icons/Ruler.svg2
-rw-r--r--editor/icons/Script.svg2
-rw-r--r--editor/icons/ScriptCreate.svg2
-rw-r--r--editor/icons/ScriptExtend.svg2
-rw-r--r--editor/icons/ScriptRemove.svg2
-rw-r--r--editor/icons/SegmentShape2D.svg2
-rw-r--r--editor/icons/SeparationRayShape2D.svg2
-rw-r--r--editor/icons/ShapeCast2D.svg2
-rw-r--r--editor/icons/SnapGrid.svg2
-rw-r--r--editor/icons/SphereShape3D.svg2
-rw-r--r--editor/icons/StaticBody2D.svg2
-rw-r--r--editor/icons/StatusError.svg2
-rw-r--r--editor/icons/StatusSuccess.svg2
-rw-r--r--editor/icons/TerrainMatchCorners.svg2
-rw-r--r--editor/icons/TerrainMatchCornersAndSides.svg2
-rw-r--r--editor/icons/TerrainMatchSides.svg2
-rw-r--r--editor/icons/TextEdit.svg2
-rw-r--r--editor/icons/Texture3D.svg2
-rw-r--r--editor/icons/TimelineIndicator.svg2
-rw-r--r--editor/icons/ToolTriangle.svg2
-rw-r--r--editor/icons/TransitionEndAutoBig.svg2
-rw-r--r--editor/icons/TransitionEndBig.svg2
-rw-r--r--editor/icons/TransitionImmediateAutoBig.svg2
-rw-r--r--editor/icons/TransitionImmediateBig.svg2
-rw-r--r--editor/icons/TransitionSyncAutoBig.svg2
-rw-r--r--editor/icons/TransitionSyncBig.svg2
-rw-r--r--editor/icons/VFlowContainer.svg1
-rw-r--r--editor/icons/VisualShaderGraphTextureUniform.svg2
-rw-r--r--editor/icons/VisualShaderNodeColorConstant.svg2
-rw-r--r--editor/icons/VisualShaderNodeColorOp.svg2
-rw-r--r--editor/icons/VisualShaderNodeColorUniform.svg2
-rw-r--r--editor/icons/VisualShaderNodeCurveTexture.svg2
-rw-r--r--editor/icons/VisualShaderNodeCurveXYZTexture.svg2
-rw-r--r--editor/icons/VisualShaderNodeExpression.svg2
-rw-r--r--editor/icons/VisualShaderNodeInput.svg2
-rw-r--r--editor/icons/VisualShaderNodeTexture2DArrayUniform.svg2
-rw-r--r--editor/icons/VisualShaderNodeTexture3DUniform.svg2
-rw-r--r--editor/icons/VisualShaderNodeTextureUniform.svg2
-rw-r--r--editor/icons/VisualShaderNodeTextureUniformTriplanar.svg2
-rw-r--r--editor/icons/VisualShaderNodeTransformCompose.svg2
-rw-r--r--editor/icons/VisualShaderNodeTransformDecompose.svg2
-rw-r--r--editor/icons/VisualShaderNodeTransformVecMult.svg2
-rw-r--r--editor/icons/VisualShaderNodeVec3Uniform.svg2
-rw-r--r--editor/icons/VisualShaderNodeVectorCompose.svg2
-rw-r--r--editor/icons/VisualShaderNodeVectorDecompose.svg2
-rw-r--r--editor/icons/VisualShaderNodeVectorDistance.svg2
-rw-r--r--editor/icons/VisualShaderNodeVectorFunc.svg2
-rw-r--r--editor/icons/VisualShaderNodeVectorLen.svg2
-rw-r--r--editor/icons/WorldBoundaryShape2D.svg2
-rw-r--r--editor/icons/editor_icons_builders.py2
-rw-r--r--editor/import/collada.cpp3
-rw-r--r--editor/import/dynamicfont_import_settings.cpp455
-rw-r--r--editor/import/dynamicfont_import_settings.h7
-rw-r--r--editor/import/editor_import_collada.cpp2
-rw-r--r--editor/import/resource_importer_bmfont.cpp742
-rw-r--r--editor/import/resource_importer_csv_translation.cpp3
-rw-r--r--editor/import/resource_importer_layered_texture.cpp9
-rw-r--r--editor/import/resource_importer_scene.cpp87
-rw-r--r--editor/import/resource_importer_scene.h6
-rw-r--r--editor/import/resource_importer_texture.cpp21
-rw-r--r--editor/import_dock.cpp6
-rw-r--r--editor/import_dock.h6
-rw-r--r--editor/inspector_dock.cpp68
-rw-r--r--editor/inspector_dock.h9
-rw-r--r--editor/localization_editor.cpp251
-rw-r--r--editor/localization_editor.h17
-rw-r--r--editor/multi_node_edit.cpp4
-rw-r--r--editor/node_dock.cpp4
-rw-r--r--editor/node_dock.h9
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp9
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp2
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp11
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.h2
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp113
-rw-r--r--editor/plugins/animation_player_editor_plugin.h7
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp2
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp126
-rw-r--r--editor/plugins/asset_library_editor_plugin.h18
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp292
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h22
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp4
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp12
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp75
-rw-r--r--editor/plugins/node_3d_editor_plugin.h13
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp25
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp35
-rw-r--r--editor/plugins/path_3d_editor_plugin.h2
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp55
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.h9
-rw-r--r--editor/plugins/script_editor_plugin.cpp113
-rw-r--r--editor/plugins/script_editor_plugin.h7
-rw-r--r--editor/plugins/script_text_editor.cpp50
-rw-r--r--editor/plugins/script_text_editor.h7
-rw-r--r--editor/plugins/shader_editor_plugin.cpp22
-rw-r--r--editor/plugins/shader_editor_plugin.h2
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp5
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp8
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp43
-rw-r--r--editor/plugins/text_editor.cpp6
-rw-r--r--editor/plugins/text_editor.h2
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp46
-rw-r--r--editor/plugins/texture_region_editor_plugin.h7
-rw-r--r--editor/plugins/theme_editor_plugin.cpp83
-rw-r--r--editor/plugins/theme_editor_plugin.h2
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.cpp2
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp21
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h4
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp81
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp98
-rw-r--r--editor/plugins/tiles/tile_map_editor.h2
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp12
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp54
-rw-r--r--editor/plugins/tiles/tile_set_editor.h2
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp103
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h23
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp1193
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h29
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.cpp4
-rw-r--r--editor/project_export.cpp59
-rw-r--r--editor/project_export.h3
-rw-r--r--editor/project_settings_editor.cpp121
-rw-r--r--editor/project_settings_editor.h7
-rw-r--r--editor/property_editor.cpp33
-rw-r--r--editor/property_editor.h3
-rw-r--r--editor/property_selector.cpp2
-rw-r--r--editor/quick_open.cpp2
-rw-r--r--editor/rename_dialog.cpp9
-rw-r--r--editor/scene_tree_dock.cpp245
-rw-r--r--editor/scene_tree_dock.h15
-rw-r--r--editor/scene_tree_editor.cpp21
-rw-r--r--editor/scene_tree_editor.h1
-rw-r--r--editor/script_create_dialog.cpp8
-rw-r--r--gles3_builders.py2
-rw-r--r--main/main.cpp101
-rw-r--r--methods.py55
-rw-r--r--misc/dist/html/editor.html4
-rw-r--r--misc/dist/html/manifest.json2
-rw-r--r--misc/dist/html/offline.html2
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj2
-rw-r--r--misc/dist/osx_template.app/Contents/Info.plist7
-rwxr-xr-xmisc/scripts/black_format.sh16
-rwxr-xr-xmisc/scripts/clang_format.sh53
-rwxr-xr-xmisc/scripts/clang_tidy.sh31
-rwxr-xr-xmisc/scripts/file_format.sh7
-rw-r--r--modules/csg/csg_shape.cpp10
-rw-r--r--modules/csg/csg_shape.h2
-rw-r--r--modules/csg/doc_classes/CSGBox3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGCylinder3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGMesh3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGPolygon3D.xml5
-rw-r--r--modules/csg/doc_classes/CSGTorus3D.xml4
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp67
-rw-r--r--modules/fbx/fbx_parser/FBXAnimation.cpp2
-rw-r--r--modules/fbx/fbx_parser/FBXDeformer.cpp4
-rw-r--r--modules/fbx/fbx_parser/FBXParser.cpp2
-rw-r--r--modules/fbx/fbx_parser/FBXProperties.cpp3
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h1
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp96
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h1
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp5
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp1
-rw-r--r--modules/gdscript/gdscript_cache.cpp5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp2
-rw-r--r--modules/gdscript/gdscript_function.cpp2
-rw-r--r--modules/gdscript/gdscript_parser.cpp13
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp66
-rw-r--r--modules/gdscript/gdscript_vm.cpp4
-rw-r--r--modules/gdscript/icons/GDScriptInternal.svg1
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/params_default_values.gd35
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/params_default_values.out6
-rw-r--r--modules/glslang/glslang_resource_limits.h18
-rw-r--r--modules/gltf/gltf_document.cpp1
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml2
-rw-r--r--modules/gridmap/grid_map.cpp23
-rw-r--r--modules/gridmap/grid_map.h4
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp23
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h1
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp6
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h3
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py8
-rw-r--r--modules/mono/csharp_script.cpp16
-rw-r--r--modules/mono/csharp_script.h1
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs4
-rw-r--r--modules/mono/godotsharp_dirs.cpp7
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp121
-rw-r--r--modules/navigation/navigation_mesh_generator.h2
-rw-r--r--modules/pvr/SCsub38
-rw-r--r--modules/pvr/config.py6
-rw-r--r--modules/pvr/image_compress_pvrtc.h36
-rw-r--r--modules/pvr/texture_loader_pvr.cpp608
-rw-r--r--modules/svg/SCsub56
-rw-r--r--modules/svg/image_loader_svg.cpp190
-rw-r--r--modules/svg/image_loader_svg.h34
-rw-r--r--modules/svg/register_types.cpp10
-rw-r--r--modules/text_server_adv/SCsub1
-rw-r--r--modules/text_server_adv/text_server_adv.cpp528
-rw-r--r--modules/text_server_adv/text_server_adv.h32
-rw-r--r--modules/text_server_fb/text_server_fb.cpp323
-rw-r--r--modules/text_server_fb/text_server_fb.h36
-rw-r--r--modules/theora/video_stream_theora.cpp1
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp413
-rw-r--r--modules/visual_script/editor/visual_script_editor.h60
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.cpp1630
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.h166
-rw-r--r--modules/visual_script/icons/VisualScript.svg2
-rw-r--r--modules/visual_script/icons/VisualScriptInternal.svg1
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp2
-rw-r--r--modules/visual_script/visual_script_expression.cpp43
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp2
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml8
-rw-r--r--modules/webrtc/register_types.cpp8
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp2
-rw-r--r--modules/websocket/wsl_client.cpp14
-rw-r--r--modules/websocket/wsl_server.cpp16
-rw-r--r--modules/webxr/webxr_interface_js.cpp2
-rw-r--r--modules/webxr/webxr_interface_js.h2
-rw-r--r--platform/android/SCsub1
-rw-r--r--platform/android/android_input_handler.cpp5
-rw-r--r--platform/android/display_server_android.cpp30
-rw-r--r--platform/android/display_server_android.h4
-rw-r--r--platform/android/export/export_plugin.cpp16
-rw-r--r--platform/android/export/gradle_export_util.cpp5
-rw-r--r--platform/android/java/app/AndroidManifest.xml11
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java6
-rw-r--r--platform/android/java_godot_wrapper.cpp16
-rw-r--r--platform/android/java_godot_wrapper.h3
-rw-r--r--platform/android/plugin/godot_plugin_jni.cpp2
-rw-r--r--platform/iphone/display_server_iphone.h2
-rw-r--r--platform/iphone/display_server_iphone.mm19
-rw-r--r--platform/iphone/export/export_plugin.cpp193
-rw-r--r--platform/iphone/joypad_iphone.mm26
-rw-r--r--platform/javascript/detect.py6
-rw-r--r--platform/javascript/display_server_javascript.cpp18
-rw-r--r--platform/javascript/export/export_plugin.cpp6
-rw-r--r--platform/javascript/http_client_javascript.cpp7
-rw-r--r--platform/javascript/http_client_javascript.h2
-rw-r--r--platform/javascript/js/engine/config.js41
-rw-r--r--platform/javascript/js/libs/library_godot_input.js2
-rw-r--r--platform/javascript/os_javascript.cpp4
-rw-r--r--platform/javascript/os_javascript.h4
-rw-r--r--platform/javascript/package-lock.json3544
-rw-r--r--platform/javascript/package.json9
-rw-r--r--platform/javascript/serve.json21
-rw-r--r--platform/linuxbsd/display_server_x11.cpp65
-rw-r--r--platform/linuxbsd/display_server_x11.h7
-rw-r--r--platform/linuxbsd/gl_manager_x11.cpp44
-rw-r--r--platform/linuxbsd/joypad_linux.cpp26
-rw-r--r--platform/linuxbsd/joypad_linux.h4
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp8
-rw-r--r--platform/osx/display_server_osx.h10
-rw-r--r--platform/osx/display_server_osx.mm105
-rw-r--r--platform/osx/export/codesign.cpp1564
-rw-r--r--platform/osx/export/codesign.h369
-rw-r--r--platform/osx/export/export.cpp3
-rw-r--r--platform/osx/export/export_plugin.cpp422
-rw-r--r--platform/osx/export/export_plugin.h6
-rw-r--r--platform/osx/export/lipo.cpp243
-rw-r--r--platform/osx/export/lipo.h (renamed from modules/pvr/register_types.cpp)56
-rw-r--r--platform/osx/export/macho.cpp556
-rw-r--r--platform/osx/export/macho.h217
-rw-r--r--platform/osx/export/plist.cpp570
-rw-r--r--platform/osx/export/plist.h (renamed from modules/pvr/image_compress_pvrtc.cpp)146
-rw-r--r--platform/osx/joypad_osx.cpp17
-rw-r--r--platform/osx/os_osx.h2
-rw-r--r--platform/osx/os_osx.mm10
-rw-r--r--platform/uwp/joypad_uwp.cpp13
-rw-r--r--platform/uwp/joypad_uwp.h2
-rw-r--r--platform/uwp/os_uwp.cpp4
-rw-r--r--platform/uwp/os_uwp.h4
-rw-r--r--platform/windows/detect.py20
-rw-r--r--platform/windows/display_server_windows.cpp86
-rw-r--r--platform/windows/display_server_windows.h8
-rw-r--r--platform/windows/export/export_plugin.cpp58
-rw-r--r--platform/windows/export/export_plugin.h8
-rw-r--r--platform/windows/godot_windows.cpp7
-rw-r--r--platform/windows/joypad_windows.cpp42
-rw-r--r--platform/windows/joypad_windows.h2
-rw-r--r--platform/windows/os_windows.cpp193
-rw-r--r--platform/windows/os_windows.h5
-rw-r--r--scene/2d/animated_sprite_2d.cpp2
-rw-r--r--scene/2d/camera_2d.cpp2
-rw-r--r--scene/2d/collision_object_2d.cpp4
-rw-r--r--scene/2d/collision_object_2d.h2
-rw-r--r--scene/2d/gpu_particles_2d.cpp58
-rw-r--r--scene/2d/gpu_particles_2d.h16
-rw-r--r--scene/2d/line_2d.cpp2
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp7
-rw-r--r--scene/2d/navigation_region_2d.cpp2
-rw-r--r--scene/2d/node_2d.cpp2
-rw-r--r--scene/2d/physics_body_2d.cpp50
-rw-r--r--scene/2d/physics_body_2d.h14
-rw-r--r--scene/2d/ray_cast_2d.cpp20
-rw-r--r--scene/2d/ray_cast_2d.h6
-rw-r--r--scene/2d/shape_cast_2d.cpp20
-rw-r--r--scene/2d/shape_cast_2d.h6
-rw-r--r--scene/2d/tile_map.cpp23
-rw-r--r--scene/3d/collision_object_3d.cpp4
-rw-r--r--scene/3d/collision_object_3d.h2
-rw-r--r--scene/3d/light_3d.cpp4
-rw-r--r--scene/3d/light_3d.h2
-rw-r--r--scene/3d/lightmap_gi.cpp2
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp7
-rw-r--r--scene/3d/node_3d.cpp5
-rw-r--r--scene/3d/physics_body_3d.cpp31
-rw-r--r--scene/3d/physics_body_3d.h8
-rw-r--r--scene/3d/ray_cast_3d.cpp20
-rw-r--r--scene/3d/ray_cast_3d.h6
-rw-r--r--scene/3d/sprite_3d.cpp3
-rw-r--r--scene/3d/visual_instance_3d.cpp8
-rw-r--r--scene/3d/visual_instance_3d.h2
-rw-r--r--scene/3d/voxel_gi.cpp2
-rw-r--r--scene/animation/animation_blend_space_1d.cpp4
-rw-r--r--scene/animation/animation_blend_space_2d.cpp6
-rw-r--r--scene/animation/animation_blend_tree.cpp4
-rw-r--r--scene/animation/animation_tree.cpp32
-rw-r--r--scene/animation/animation_tree.h16
-rw-r--r--scene/animation/tween.cpp13
-rw-r--r--scene/debugger/scene_debugger.h1
-rw-r--r--scene/gui/base_button.cpp15
-rw-r--r--scene/gui/button.cpp7
-rw-r--r--scene/gui/code_edit.cpp73
-rw-r--r--scene/gui/control.cpp24
-rw-r--r--scene/gui/control.h2
-rw-r--r--scene/gui/file_dialog.cpp41
-rw-r--r--scene/gui/file_dialog.h4
-rw-r--r--scene/gui/flow_container.cpp252
-rw-r--r--scene/gui/flow_container.h (renamed from modules/pvr/texture_loader_pvr.h)54
-rw-r--r--scene/gui/gradient_edit.h1
-rw-r--r--scene/gui/graph_edit.cpp26
-rw-r--r--scene/gui/graph_edit.h7
-rw-r--r--scene/gui/graph_node.cpp2
-rw-r--r--scene/gui/label.cpp45
-rw-r--r--scene/gui/label.h1
-rw-r--r--scene/gui/line_edit.cpp19
-rw-r--r--scene/gui/line_edit.h4
-rw-r--r--scene/gui/link_button.cpp2
-rw-r--r--scene/gui/menu_button.cpp8
-rw-r--r--scene/gui/option_button.cpp56
-rw-r--r--scene/gui/popup_menu.cpp63
-rw-r--r--scene/gui/popup_menu.h1
-rw-r--r--scene/gui/rich_text_label.cpp188
-rw-r--r--scene/gui/rich_text_label.h15
-rw-r--r--scene/gui/tab_bar.cpp706
-rw-r--r--scene/gui/tab_bar.h17
-rw-r--r--scene/gui/text_edit.cpp238
-rw-r--r--scene/gui/text_edit.h30
-rw-r--r--scene/gui/texture_button.cpp5
-rw-r--r--scene/gui/tree.cpp82
-rw-r--r--scene/gui/tree.h35
-rw-r--r--scene/gui/view_panner.cpp110
-rw-r--r--scene/gui/view_panner.h38
-rw-r--r--scene/main/canvas_item.cpp4
-rw-r--r--scene/main/http_request.cpp71
-rw-r--r--scene/main/node.cpp19
-rw-r--r--scene/main/scene_tree.cpp4
-rw-r--r--scene/main/viewport.cpp98
-rw-r--r--scene/main/viewport.h8
-rw-r--r--scene/main/window.cpp39
-rw-r--r--scene/register_scene_types.cpp20
-rw-r--r--scene/resources/animation.cpp24
-rw-r--r--scene/resources/bit_map.cpp2
-rw-r--r--scene/resources/box_shape_3d.cpp2
-rw-r--r--scene/resources/capsule_shape_2d.cpp6
-rw-r--r--scene/resources/capsule_shape_3d.h4
-rw-r--r--scene/resources/circle_shape_2d.cpp11
-rw-r--r--scene/resources/curve.cpp14
-rw-r--r--scene/resources/cylinder_shape_3d.h2
-rw-r--r--scene/resources/default_theme/SCsub17
-rw-r--r--scene/resources/default_theme/add.svg1
-rw-r--r--scene/resources/default_theme/arrow_down.pngbin109 -> 0 bytes
-rw-r--r--scene/resources/default_theme/arrow_down.svg1
-rw-r--r--scene/resources/default_theme/arrow_left.pngbin159 -> 0 bytes
-rw-r--r--scene/resources/default_theme/arrow_left.svg1
-rw-r--r--scene/resources/default_theme/arrow_right.pngbin103 -> 0 bytes
-rw-r--r--scene/resources/default_theme/arrow_right.svg1
-rw-r--r--scene/resources/default_theme/bar_arrow.pngbin208 -> 0 bytes
-rw-r--r--scene/resources/default_theme/bookmark.pngbin160 -> 0 bytes
-rw-r--r--scene/resources/default_theme/bookmark.svg1
-rw-r--r--scene/resources/default_theme/breakpoint.svg1
-rw-r--r--scene/resources/default_theme/button_disabled.pngbin256 -> 0 bytes
-rw-r--r--scene/resources/default_theme/button_focus.pngbin203 -> 0 bytes
-rw-r--r--scene/resources/default_theme/button_hover.pngbin342 -> 0 bytes
-rw-r--r--scene/resources/default_theme/button_normal.pngbin336 -> 0 bytes
-rw-r--r--scene/resources/default_theme/button_pressed.pngbin499 -> 0 bytes
-rw-r--r--scene/resources/default_theme/checked.pngbin363 -> 0 bytes
-rw-r--r--scene/resources/default_theme/checked.svg1
-rw-r--r--scene/resources/default_theme/checked_disabled.pngbin421 -> 0 bytes
-rw-r--r--scene/resources/default_theme/checked_disabled.svg1
-rw-r--r--scene/resources/default_theme/checker_bg.pngbin77 -> 0 bytes
-rw-r--r--scene/resources/default_theme/close.pngbin155 -> 0 bytes
-rw-r--r--scene/resources/default_theme/close.svg1
-rw-r--r--scene/resources/default_theme/close_hl.pngbin155 -> 0 bytes
-rw-r--r--scene/resources/default_theme/close_hl.svg1
-rw-r--r--scene/resources/default_theme/color_picker_bar_arrow.svg1
-rw-r--r--scene/resources/default_theme/color_picker_cursor.svg1
-rw-r--r--scene/resources/default_theme/color_picker_hue.pngbin141 -> 0 bytes
-rw-r--r--scene/resources/default_theme/color_picker_hue.svg1
-rw-r--r--scene/resources/default_theme/color_picker_overbright.svg1
-rw-r--r--scene/resources/default_theme/color_picker_pipette.svg1
-rw-r--r--scene/resources/default_theme/color_picker_sample.pngbin117 -> 0 bytes
-rw-r--r--scene/resources/default_theme/color_picker_sample.svg1
-rw-r--r--scene/resources/default_theme/default_theme.cpp774
-rw-r--r--scene/resources/default_theme/default_theme.h4
-rw-r--r--scene/resources/default_theme/default_theme_icons_builders.py78
-rw-r--r--scene/resources/default_theme/dialog_bg.pngbin1314 -> 0 bytes
-rw-r--r--scene/resources/default_theme/dropdown.pngbin133 -> 0 bytes
-rw-r--r--scene/resources/default_theme/ellipsis.pngbin193 -> 0 bytes
-rw-r--r--scene/resources/default_theme/error_icon.pngbin106 -> 0 bytes
-rw-r--r--scene/resources/default_theme/error_icon.svg1
-rw-r--r--scene/resources/default_theme/error_icon.xpm38
-rw-r--r--scene/resources/default_theme/file.svg1
-rw-r--r--scene/resources/default_theme/focus.pngbin200 -> 0 bytes
-rw-r--r--scene/resources/default_theme/folder.svg1
-rw-r--r--scene/resources/default_theme/folder_up.svg1
-rw-r--r--scene/resources/default_theme/graph_node.pngbin762 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_breakpoint.pngbin140 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_close.pngbin152 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_comment.pngbin382 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_comment_focus.pngbin373 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_default.pngbin205 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_default_focus.pngbin195 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_position.pngbin140 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_node_selected.pngbin827 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_port.pngbin167 -> 0 bytes
-rw-r--r--scene/resources/default_theme/graph_port.svg1
-rw-r--r--scene/resources/default_theme/grid_layout.svg1
-rw-r--r--scene/resources/default_theme/grid_minimap.svg1
-rw-r--r--scene/resources/default_theme/grid_snap.svg1
-rw-r--r--scene/resources/default_theme/hseparator.pngbin112 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_bg.pngbin263 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber.pngbin300 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber_disabled.pngbin288 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_grabber_hl.pngbin450 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_tick.pngbin149 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hslider_tick.svg1
-rw-r--r--scene/resources/default_theme/hsplit_bg.pngbin85 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hsplitter.pngbin97 -> 0 bytes
-rw-r--r--scene/resources/default_theme/hsplitter.svg1
-rw-r--r--scene/resources/default_theme/icon_add.pngbin86 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_close.pngbin155 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_color_pick.pngbin227 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_file.pngbin183 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_folder.pngbin103 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_grid_layout.pngbin2170 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_grid_minimap.pngbin640 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_parent_folder.pngbin161 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_reload.pngbin234 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_snap_grid.pngbin226 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_visibility.pngbin448 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_less.pngbin76 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_more.pngbin85 -> 0 bytes
-rw-r--r--scene/resources/default_theme/icon_zoom_reset.pngbin108 -> 0 bytes
-rw-r--r--scene/resources/default_theme/indeterminate.pngbin242 -> 0 bytes
-rw-r--r--scene/resources/default_theme/indeterminate.svg1
-rw-r--r--scene/resources/default_theme/line_edit.pngbin176 -> 0 bytes
-rw-r--r--scene/resources/default_theme/line_edit_clear.pngbin158 -> 0 bytes
-rw-r--r--scene/resources/default_theme/line_edit_clear.svg1
-rw-r--r--scene/resources/default_theme/line_edit_disabled.pngbin135 -> 0 bytes
-rwxr-xr-xscene/resources/default_theme/make_header.py73
-rw-r--r--scene/resources/default_theme/mini_checkerboard.pngbin80 -> 0 bytes
-rw-r--r--scene/resources/default_theme/mini_checkerboard.svg1
-rw-r--r--scene/resources/default_theme/option_arrow.pngbin119 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_arrow.svg1
-rw-r--r--scene/resources/default_theme/option_button_disabled.pngbin656 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_disabled_mirrored.pngbin704 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_hover.pngbin667 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_hover_mirrored.pngbin728 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_normal.pngbin668 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_normal_mirrored.pngbin726 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_pressed.pngbin677 -> 0 bytes
-rw-r--r--scene/resources/default_theme/option_button_pressed_mirrored.pngbin736 -> 0 bytes
-rw-r--r--scene/resources/default_theme/overbright_indicator.pngbin210 -> 0 bytes
-rw-r--r--scene/resources/default_theme/panel_bg.pngbin85 -> 0 bytes
-rw-r--r--scene/resources/default_theme/picker_cursor.pngbin300 -> 0 bytes
-rw-r--r--scene/resources/default_theme/popup_bg.pngbin410 -> 0 bytes
-rw-r--r--scene/resources/default_theme/popup_bg_disabled.pngbin362 -> 0 bytes
-rw-r--r--scene/resources/default_theme/popup_window.pngbin921 -> 0 bytes
-rw-r--r--scene/resources/default_theme/progress_bar.pngbin194 -> 0 bytes
-rw-r--r--scene/resources/default_theme/progress_fill.pngbin112 -> 0 bytes
-rw-r--r--scene/resources/default_theme/radio_checked.pngbin257 -> 0 bytes
-rw-r--r--scene/resources/default_theme/radio_checked.svg1
-rw-r--r--scene/resources/default_theme/radio_checked_disabled.pngbin235 -> 0 bytes
-rw-r--r--scene/resources/default_theme/radio_checked_disabled.svg1
-rw-r--r--scene/resources/default_theme/radio_unchecked.pngbin207 -> 0 bytes
-rw-r--r--scene/resources/default_theme/radio_unchecked.svg1
-rw-r--r--scene/resources/default_theme/radio_unchecked_disabled.pngbin183 -> 0 bytes
-rw-r--r--scene/resources/default_theme/radio_unchecked_disabled.svg1
-rw-r--r--scene/resources/default_theme/reload.svg1
-rw-r--r--scene/resources/default_theme/resizer_nw.svg1
-rw-r--r--scene/resources/default_theme/resizer_se.svg1
-rw-r--r--scene/resources/default_theme/scroll_bg.pngbin252 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_left.pngbin196 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_left.svg1
-rw-r--r--scene/resources/default_theme/scroll_button_left_hl.pngbin200 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_left_hl.svg1
-rw-r--r--scene/resources/default_theme/scroll_button_right.pngbin198 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_right.svg1
-rw-r--r--scene/resources/default_theme/scroll_button_right_hl.pngbin210 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_button_right_hl.svg1
-rw-r--r--scene/resources/default_theme/scroll_grabber.pngbin264 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_grabber_hl.pngbin275 -> 0 bytes
-rw-r--r--scene/resources/default_theme/scroll_grabber_pressed.pngbin104 -> 0 bytes
-rw-r--r--scene/resources/default_theme/selection.pngbin195 -> 0 bytes
-rw-r--r--scene/resources/default_theme/selection_oof.pngbin196 -> 0 bytes
-rw-r--r--scene/resources/default_theme/slider_grabber.svg1
-rw-r--r--scene/resources/default_theme/slider_grabber_disabled.svg1
-rw-r--r--scene/resources/default_theme/slider_grabber_hl.svg1
-rw-r--r--scene/resources/default_theme/space.pngbin99 -> 0 bytes
-rw-r--r--scene/resources/default_theme/spinbox_updown.pngbin146 -> 0 bytes
-rw-r--r--scene/resources/default_theme/submenu.pngbin103 -> 0 bytes
-rw-r--r--scene/resources/default_theme/submenu_mirrored.pngbin157 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab.pngbin82 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_behind.pngbin282 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_close.pngbin158 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_container_bg.pngbin336 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_current.pngbin346 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_disabled.pngbin258 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_menu.pngbin111 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tab_menu_hl.pngbin111 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tabs_menu.svg1
-rw-r--r--scene/resources/default_theme/tabs_menu_hl.svg1
-rw-r--r--scene/resources/default_theme/text_edit_ellipsis.svg1
-rw-r--r--scene/resources/default_theme/text_edit_space.svg1
-rw-r--r--scene/resources/default_theme/text_edit_tab.svg1
-rw-r--r--scene/resources/default_theme/theme_data.h517
-rw-r--r--scene/resources/default_theme/toggle_off.pngbin1077 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_off.svg1
-rw-r--r--scene/resources/default_theme/toggle_off_disabled.pngbin932 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_off_disabled.svg1
-rw-r--r--scene/resources/default_theme/toggle_off_disabled_mirrored.pngbin535 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_off_disabled_mirrored.svg1
-rw-r--r--scene/resources/default_theme/toggle_off_mirrored.pngbin1169 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_off_mirrored.svg1
-rw-r--r--scene/resources/default_theme/toggle_on.pngbin1034 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_on.svg1
-rw-r--r--scene/resources/default_theme/toggle_on_disabled.pngbin1039 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_disabled.svg1
-rw-r--r--scene/resources/default_theme/toggle_on_disabled_mirrored.pngbin496 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_disabled_mirrored.svg1
-rw-r--r--scene/resources/default_theme/toggle_on_mirrored.pngbin1133 -> 0 bytes
-rw-r--r--scene/resources/default_theme/toggle_on_mirrored.svg1
-rw-r--r--scene/resources/default_theme/tooltip_bg.pngbin198 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tree_bg.pngbin176 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tree_bg_disabled.pngbin135 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tree_title.pngbin86 -> 0 bytes
-rw-r--r--scene/resources/default_theme/tree_title_pressed.pngbin86 -> 0 bytes
-rw-r--r--scene/resources/default_theme/unchecked.pngbin222 -> 0 bytes
-rw-r--r--scene/resources/default_theme/unchecked.svg1
-rw-r--r--scene/resources/default_theme/unchecked_disabled.pngbin246 -> 0 bytes
-rw-r--r--scene/resources/default_theme/unchecked_disabled.svg1
-rw-r--r--scene/resources/default_theme/updown.pngbin144 -> 0 bytes
-rw-r--r--scene/resources/default_theme/updown.svg1
-rw-r--r--scene/resources/default_theme/visibility_visible.svg1
-rw-r--r--scene/resources/default_theme/vseparator.pngbin108 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_bg.pngbin276 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber.pngbin245 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber_disabled.pngbin237 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_grabber_hl.pngbin261 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_tick.pngbin145 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vslider_tick.svg1
-rw-r--r--scene/resources/default_theme/vsplit_bg.pngbin85 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vsplitter.pngbin95 -> 0 bytes
-rw-r--r--scene/resources/default_theme/vsplitter.svg1
-rw-r--r--scene/resources/default_theme/window_resizer.pngbin87 -> 0 bytes
-rw-r--r--scene/resources/default_theme/window_resizer_mirrored.pngbin109 -> 0 bytes
-rw-r--r--scene/resources/default_theme/zoom_less.svg1
-rw-r--r--scene/resources/default_theme/zoom_more.svg1
-rw-r--r--scene/resources/default_theme/zoom_reset.svg1
-rw-r--r--scene/resources/environment.cpp59
-rw-r--r--scene/resources/environment.h19
-rw-r--r--scene/resources/font.cpp762
-rw-r--r--scene/resources/font.h9
-rw-r--r--scene/resources/immediate_mesh.cpp2
-rw-r--r--scene/resources/mesh.cpp4
-rw-r--r--scene/resources/mesh_library.cpp33
-rw-r--r--scene/resources/packed_scene.cpp14
-rw-r--r--scene/resources/primitive_meshes.h10
-rw-r--r--scene/resources/skeleton_modification_3d.cpp3
-rw-r--r--scene/resources/skin.cpp1
-rw-r--r--scene/resources/sky_material.cpp51
-rw-r--r--scene/resources/sky_material.h7
-rw-r--r--scene/resources/surface_tool.cpp1
-rw-r--r--scene/resources/syntax_highlighter.cpp48
-rw-r--r--scene/resources/text_line.cpp6
-rw-r--r--scene/resources/text_line.h2
-rw-r--r--scene/resources/text_paragraph.cpp6
-rw-r--r--scene/resources/text_paragraph.h2
-rw-r--r--scene/resources/texture.cpp10
-rw-r--r--scene/resources/texture.h6
-rw-r--r--scene/resources/tile_set.cpp21
-rw-r--r--scene/resources/tile_set.h2
-rw-r--r--scene/resources/visual_shader.cpp570
-rw-r--r--scene/resources/visual_shader.h10
-rw-r--r--scene/resources/visual_shader_nodes.cpp1414
-rw-r--r--scene/resources/visual_shader_nodes.h277
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp16
-rw-r--r--scene/resources/visual_shader_particle_nodes.h2
-rw-r--r--scene/resources/visual_shader_sdf_nodes.cpp6
-rw-r--r--scene/resources/visual_shader_sdf_nodes.h2
-rw-r--r--scene/resources/world_2d.cpp4
-rw-r--r--servers/audio/effects/audio_effect_record.cpp2
-rw-r--r--servers/audio_server.cpp6
-rw-r--r--servers/display_server.cpp33
-rw-r--r--servers/display_server.h15
-rw-r--r--servers/physics_3d/godot_shape_3d.cpp28
-rw-r--r--servers/physics_3d/godot_soft_body_3d.cpp2
-rw-r--r--servers/register_server_types.cpp4
-rw-r--r--servers/rendering/rasterizer_dummy.h4
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp2
-rw-r--r--servers/rendering/renderer_rd/effects_rd.cpp42
-rw-r--r--servers/rendering/renderer_rd/effects_rd.h7
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp1
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h1
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp6
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp1
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp9
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp6
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_environment_rd.h8
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp10
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_gi_rd.h2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp57
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h7
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp97
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.h5
-rw-r--r--servers/rendering/renderer_rd/shaders/light_data_inc.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/tonemap.glsl10
-rw-r--r--servers/rendering/renderer_scene.h4
-rw-r--r--servers/rendering/renderer_scene_cull.h4
-rw-r--r--servers/rendering/renderer_scene_render.h4
-rw-r--r--servers/rendering/renderer_viewport.cpp49
-rw-r--r--servers/rendering/rendering_device.cpp10
-rw-r--r--servers/rendering/rendering_device.h10
-rw-r--r--servers/rendering/rendering_server_default.cpp6
-rw-r--r--servers/rendering/rendering_server_default.h5
-rw-r--r--servers/rendering/shader_compiler.cpp125
-rw-r--r--servers/rendering/shader_language.cpp1352
-rw-r--r--servers/rendering/shader_language.h59
-rw-r--r--servers/rendering/shader_types.cpp1
-rw-r--r--servers/rendering/shader_warnings.cpp16
-rw-r--r--servers/rendering_server.cpp22
-rw-r--r--servers/rendering_server.h13
-rw-r--r--servers/text/text_server_extension.cpp53
-rw-r--r--servers/text/text_server_extension.h16
-rw-r--r--servers/text_server.cpp10
-rw-r--r--servers/text_server.h27
-rw-r--r--servers/xr/xr_interface.cpp3
-rw-r--r--servers/xr/xr_interface.h9
-rw-r--r--servers/xr/xr_interface_extension.cpp36
-rw-r--r--servers/xr/xr_interface_extension.h11
-rw-r--r--servers/xr/xr_pose.cpp16
-rw-r--r--servers/xr/xr_pose.h13
-rw-r--r--servers/xr/xr_positional_tracker.cpp5
-rw-r--r--servers/xr/xr_positional_tracker.h2
-rw-r--r--servers/xr_server.cpp50
-rw-r--r--servers/xr_server.h18
-rw-r--r--tests/core/math/test_math.cpp2
-rw-r--r--tests/core/math/test_rect2.h6
-rw-r--r--tests/core/math/test_vector2.h385
-rw-r--r--tests/core/math/test_vector2i.h144
-rw-r--r--tests/core/math/test_vector3.h414
-rw-r--r--tests/core/math/test_vector3i.h145
-rw-r--r--tests/core/object/test_method_bind.h16
-rw-r--r--tests/core/string/test_string.h13
-rw-r--r--tests/core/templates/test_vector.h25
-rw-r--r--tests/core/variant/test_array.h35
-rw-r--r--tests/core/variant/test_dictionary.h2
-rw-r--r--tests/scene/test_code_edit.h115
-rw-r--r--tests/servers/test_shader_lang.cpp3
-rw-r--r--tests/test_main.cpp6
-rw-r--r--thirdparty/README.md58
-rw-r--r--thirdparty/fonts/Hack_Regular.ttfbin309408 -> 0 bytes
-rw-r--r--thirdparty/fonts/JetBrainsMono_Regular.ttfbin0 -> 203952 bytes
-rw-r--r--thirdparty/fonts/LICENSE.JetBrainsMono.txt93
-rw-r--r--thirdparty/fonts/LICENSE_Hack.md45
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-common.hh2
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh2
-rw-r--r--thirdparty/harfbuzz/src/hb-algs.hh32
-rw-r--r--thirdparty/harfbuzz/src/hb-array.hh2
-rw-r--r--thirdparty/harfbuzz/src/hb-bimap.hh14
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer.cc185
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer.h92
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer.hh165
-rw-r--r--thirdparty/harfbuzz/src/hb-cff-interp-common.hh14
-rw-r--r--thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh9
-rw-r--r--thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh14
-rw-r--r--thirdparty/harfbuzz/src/hb-common.cc78
-rw-r--r--thirdparty/harfbuzz/src/hb-coretext.cc9
-rw-r--r--thirdparty/harfbuzz/src/hb-directwrite.cc3
-rw-r--r--thirdparty/harfbuzz/src/hb-draw.h2
-rw-r--r--thirdparty/harfbuzz/src/hb-face.cc23
-rw-r--r--thirdparty/harfbuzz/src/hb-font.cc114
-rw-r--r--thirdparty/harfbuzz/src/hb-font.h8
-rw-r--r--thirdparty/harfbuzz/src/hb-font.hh3
-rw-r--r--thirdparty/harfbuzz/src/hb-graphite2.cc3
-rw-r--r--thirdparty/harfbuzz/src/hb-iter.hh6
-rw-r--r--thirdparty/harfbuzz/src/hb-kern.hh9
-rw-r--r--thirdparty/harfbuzz/src/hb-machinery.hh6
-rw-r--r--thirdparty/harfbuzz/src/hb-map.hh71
-rw-r--r--thirdparty/harfbuzz/src/hb-meta.hh39
-rw-r--r--thirdparty/harfbuzz/src/hb-ms-feature-ranges.cc177
-rw-r--r--thirdparty/harfbuzz/src/hb-ms-feature-ranges.hh145
-rw-r--r--thirdparty/harfbuzz/src/hb-object.hh6
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff-common.hh2
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff1-table.hh38
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff2-table.hh63
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cmap-table.hh13
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh24
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh14
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color-colrv1-closure.hh2
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh9
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh8
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color.cc12
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-glyf-table.hh17
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh20
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-common.hh21
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh17
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-gpos-table.hh172
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-gsub-table.hh22
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh304
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout.cc8
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout.hh5
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-meta-table.hh8
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-metrics.cc45
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-name-table.hh13
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh7
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-post-table.hh17
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh32
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.cc21
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-hangul.cc4
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-syllabic.cc2
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-thai.cc2
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc2
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-fallback.cc19
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc7
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape.cc29
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-tag-table.hh97
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh2
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh10
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh7
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var.cc3
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var.h2
-rw-r--r--thirdparty/harfbuzz/src/hb-repacker.hh38
-rw-r--r--thirdparty/harfbuzz/src/hb-serialize.hh14
-rw-r--r--thirdparty/harfbuzz/src/hb-style.cc15
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff-common.hh102
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff1.cc42
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff2.cc43
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-plan.cc26
-rw-r--r--thirdparty/harfbuzz/src/hb-uniscribe.cc3
-rw-r--r--thirdparty/harfbuzz/src/hb-vector.hh200
-rw-r--r--thirdparty/harfbuzz/src/hb-version.h6
-rw-r--r--thirdparty/libwebp/AUTHORS1
-rw-r--r--thirdparty/libwebp/src/dec/vp8_dec.c2
-rw-r--r--thirdparty/libwebp/src/dec/vp8i_dec.h2
-rw-r--r--thirdparty/libwebp/src/dec/vp8l_dec.c2
-rw-r--r--thirdparty/libwebp/src/demux/anim_decode.c40
-rw-r--r--thirdparty/libwebp/src/demux/demux.c2
-rw-r--r--thirdparty/libwebp/src/dsp/dsp.h7
-rw-r--r--thirdparty/libwebp/src/dsp/enc_neon.c2
-rw-r--r--thirdparty/libwebp/src/dsp/lossless.c58
-rw-r--r--thirdparty/libwebp/src/dsp/lossless.h45
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_common.h2
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_enc.c2
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_mips_dsp_r2.c37
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_neon.c20
-rw-r--r--thirdparty/libwebp/src/dsp/lossless_sse2.c41
-rw-r--r--thirdparty/libwebp/src/dsp/msa_macro.h5
-rw-r--r--thirdparty/libwebp/src/dsp/neon.h7
-rw-r--r--thirdparty/libwebp/src/dsp/yuv.h2
-rw-r--r--thirdparty/libwebp/src/enc/frame_enc.c9
-rw-r--r--thirdparty/libwebp/src/enc/predictor_enc.c2
-rw-r--r--thirdparty/libwebp/src/enc/quant_enc.c58
-rw-r--r--thirdparty/libwebp/src/enc/vp8i_enc.h2
-rw-r--r--thirdparty/libwebp/src/mux/muxi.h2
-rw-r--r--thirdparty/libwebp/src/utils/huffman_encode_utils.c2
-rw-r--r--thirdparty/libwebp/src/utils/quant_levels_dec_utils.c2
-rw-r--r--thirdparty/libwebp/src/utils/utils.c2
-rw-r--r--thirdparty/libwebp/src/webp/decode.h2
-rw-r--r--thirdparty/nanosvg/LICENSE.txt18
-rw-r--r--thirdparty/nanosvg/nanosvg.cc8
-rw-r--r--thirdparty/nanosvg/nanosvg.h3008
-rw-r--r--thirdparty/nanosvg/nanosvgrast.h1452
-rw-r--r--thirdparty/pvrtccompressor/AlphaBitmap.h18
-rw-r--r--thirdparty/pvrtccompressor/BitScale.cpp184
-rw-r--r--thirdparty/pvrtccompressor/BitScale.h28
-rw-r--r--thirdparty/pvrtccompressor/BitUtility.h19
-rw-r--r--thirdparty/pvrtccompressor/Bitmap.h34
-rw-r--r--thirdparty/pvrtccompressor/ColorRgba.h152
-rw-r--r--thirdparty/pvrtccompressor/Interval.h21
-rw-r--r--thirdparty/pvrtccompressor/LICENSE.TXT25
-rw-r--r--thirdparty/pvrtccompressor/MortonTable.cpp43
-rw-r--r--thirdparty/pvrtccompressor/MortonTable.h18
-rw-r--r--thirdparty/pvrtccompressor/Point2.h17
-rw-r--r--thirdparty/pvrtccompressor/PvrTcDecoder.cpp144
-rw-r--r--thirdparty/pvrtccompressor/PvrTcDecoder.h25
-rw-r--r--thirdparty/pvrtccompressor/PvrTcEncoder.cpp474
-rw-r--r--thirdparty/pvrtccompressor/PvrTcEncoder.h40
-rw-r--r--thirdparty/pvrtccompressor/PvrTcPacket.cpp209
-rw-r--r--thirdparty/pvrtccompressor/PvrTcPacket.h65
-rw-r--r--thirdparty/pvrtccompressor/RgbBitmap.h23
-rw-r--r--thirdparty/pvrtccompressor/RgbaBitmap.h23
-rw-r--r--thirdparty/thorvg/AUTHORS17
-rw-r--r--thirdparty/thorvg/LICENSE7
-rw-r--r--thirdparty/thorvg/inc/config.h17
-rw-r--r--thirdparty/thorvg/inc/thorvg.h1589
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h359
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp333
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp128
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp502
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp156
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp1503
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h179
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h64
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h126
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h602
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h163
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp686
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h81
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp1043
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp664
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp932
-rw-r--r--thirdparty/thorvg/src/lib/tvgAccessor.cpp84
-rw-r--r--thirdparty/thorvg/src/lib/tvgArray.h109
-rw-r--r--thirdparty/thorvg/src/lib/tvgBezier.cpp150
-rw-r--r--thirdparty/thorvg/src/lib/tvgBezier.h48
-rw-r--r--thirdparty/thorvg/src/lib/tvgBinaryDesc.h96
-rw-r--r--thirdparty/thorvg/src/lib/tvgCanvas.cpp73
-rw-r--r--thirdparty/thorvg/src/lib/tvgCanvasImpl.h142
-rw-r--r--thirdparty/thorvg/src/lib/tvgCommon.h76
-rw-r--r--thirdparty/thorvg/src/lib/tvgFill.cpp115
-rw-r--r--thirdparty/thorvg/src/lib/tvgFill.h88
-rw-r--r--thirdparty/thorvg/src/lib/tvgGlCanvas.cpp87
-rw-r--r--thirdparty/thorvg/src/lib/tvgInitializer.cpp152
-rw-r--r--thirdparty/thorvg/src/lib/tvgIteratorAccessor.h42
-rw-r--r--thirdparty/thorvg/src/lib/tvgLinearGradient.cpp99
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoadModule.h58
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoader.cpp213
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoader.h36
-rw-r--r--thirdparty/thorvg/src/lib/tvgLzw.cpp428
-rw-r--r--thirdparty/thorvg/src/lib/tvgLzw.h31
-rw-r--r--thirdparty/thorvg/src/lib/tvgMath.h161
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.cpp407
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.h200
-rw-r--r--thirdparty/thorvg/src/lib/tvgPicture.cpp121
-rw-r--r--thirdparty/thorvg/src/lib/tvgPictureImpl.h251
-rw-r--r--thirdparty/thorvg/src/lib/tvgRadialGradient.cpp97
-rw-r--r--thirdparty/thorvg/src/lib/tvgRender.cpp73
-rw-r--r--thirdparty/thorvg/src/lib/tvgRender.h113
-rw-r--r--thirdparty/thorvg/src/lib/tvgSaveModule.h41
-rw-r--r--thirdparty/thorvg/src/lib/tvgSaver.cpp140
-rw-r--r--thirdparty/thorvg/src/lib/tvgScene.cpp76
-rw-r--r--thirdparty/thorvg/src/lib/tvgSceneImpl.h217
-rw-r--r--thirdparty/thorvg/src/lib/tvgShape.cpp427
-rw-r--r--thirdparty/thorvg/src/lib/tvgShapeImpl.h392
-rw-r--r--thirdparty/thorvg/src/lib/tvgSwCanvas.cpp105
-rw-r--r--thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp192
-rw-r--r--thirdparty/thorvg/src/lib/tvgTaskScheduler.h88
-rw-r--r--thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp164
-rw-r--r--thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h52
-rw-r--r--thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp122
-rw-r--r--thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h46
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp137
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h51
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp3029
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h35
-rw-r--r--thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp2647
-rw-r--r--thirdparty/thorvg/src/loaders/png/tvgLodePng.h174
-rw-r--r--thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp194
-rw-r--r--thirdparty/thorvg/src/loaders/png/tvgPngLoader.h54
-rw-r--r--thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp87
-rw-r--r--thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h42
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp3078
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h59
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h412
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp564
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h30
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp653
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h30
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp273
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h33
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp528
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h57
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp450
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h54
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp232
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h61
-rw-r--r--thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp775
-rw-r--r--thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h77
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh28
-rw-r--r--version.py2
1281 files changed, 57375 insertions, 21210 deletions
diff --git a/.clang-tidy b/.clang-tidy
index 59d0facb96..b623aef133 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,10 +1,9 @@
---
-Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,modernize-redundant-void-arg,modernize-use-bool-literals,modernize-use-default-member-init,modernize-use-nullptr,readability-braces-around-statements'
+Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,modernize-redundant-void-arg,modernize-use-bool-literals,modernize-use-default-member-init,modernize-use-nullptr,readability-braces-around-statements'
WarningsAsErrors: ''
-HeaderFilterRegex: '.*'
+HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
-FormatStyle: none
-CheckOptions:
+FormatStyle: none
CheckOptions:
- key: cert-dcl16-c.NewSuffixes
value: 'L;LL;LU;LLU'
diff --git a/AUTHORS.md b/AUTHORS.md
index 4d6d8919f7..430596e611 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -55,6 +55,7 @@ name is available.
bruvzg
Cameron Reikes (creikey)
Camille Mohr-Daurat (pouleyKetchoupp)
+ Caner Demirer (cdemirer)
Carl Olsson (not-surt)
Carter Anderson (cart)
Chris Bradfield (cbscribe)
@@ -147,6 +148,7 @@ name is available.
Marcus Elg (MCrafterzz)
Mariano Javier Suligoy (MarianoGnu)
Mario Schlack (hurikhan)
+ Marios Staikopoulos (marstaik)
Martin Capitanio (capnm)
Martin Liška (marxin)
Martin Sjursen (binbitten)
@@ -160,6 +162,7 @@ name is available.
Meru Patel (Janglee123)
Michael Alexsander (YeldhamDev)
MichiRecRoom (LikeLakers2)
+ Morris "Tabor" Arroad (mortarroad)
mrezai
muiroc
Nathan Franke (nathanfranke)
@@ -169,6 +172,7 @@ name is available.
Nils André-Chang (NilsIrl)
Noah Beard (TwistedTwigleg)
Nuno Donato (nunodonato)
+ Omar El Sheikh (The-O-King)
Ovnuniarchos
Pascal Richter (ShyRed)
Patrick (firefly2442)
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 8ea5ee24e6..368e321a2d 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -171,17 +171,16 @@ Comment: DroidSans font
Copyright: 2008, The Android Open Source Project
License: Apache-2.0
+Files: ./thirdparty/fonts/JetBrainsMono_Regular.ttf
+Comment: JetBrains Mono font
+Copyright: 2020, JetBrains s.r.o.
+License: OFL-1.1
+
Files: ./thirdparty/fonts/NotoSans*.ttf
Comment: Noto Sans font
Copyright: 2012, Google Inc.
License: OFL-1.1
-Files: ./thirdparty/fonts/Hack_Regular.ttf
-Comment: Hack font
-Copyright: 2018, Source Foundry Authors
- 2003, Bitstream Inc.
-License: Expat and Bitstream Vera Fonts Copyright
-
Files: ./thirdparty/freetype/
Comment: The FreeType Project
Copyright: 1996-2021, David Turner, Robert Wilhelm, and Werner Lemberg.
@@ -362,10 +361,6 @@ Comment: Multi-channel signed distance field generator
Copyright: 2016, Viktor Chlumsky
License: MIT
-Files: ./thirdparty/nanosvg/
-Comment: NanoSVG
-Copyright: 2013-2014, Mikko Mononen
-License: Zlib
Files: ./thirdparty/oidn/
Comment: Intel Open Image Denoise
@@ -378,11 +373,6 @@ Copyright: 1997-2021, University of Cambridge
2009-2021, Zoltan Herczeg
License: BSD-3-clause
-Files: ./thirdparty/pvrtccompressor/
-Comment: PvrTcCompressor
-Copyright: 2014, Jeffrey Lim.
-License: BSD-3-clause
-
Files: ./thirdparty/recastnavigation/
Comment: Recast
Copyright: 2009, Mikko Mononen
@@ -403,6 +393,11 @@ Comment: libSquish
Copyright: 2006, Simon Brown
License: Expat
+Files: ./thirdparty/thorvg/
+Comment: ThorVG
+Copyright: 2020-2021, Samsung Electronics Co., Ltd.
+License: Expat
+
Files: ./thirdparty/tinyexr/
Comment: TinyEXR
Copyright: 2014-2021, Syoyo Fujita
diff --git a/DONORS.md b/DONORS.md
index 64c610c65f..9fdd603e58 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -42,9 +42,9 @@ generous deed immortalized in the next stable release of Godot Engine.
## Mini sponsors
AD Ford
+ Andres Hernandez
Andrew Bowen
Andrew Dunai
- Angry Skull
anti666
blurp
Christian Baune
@@ -86,15 +86,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Steve
Thomas Krampl
Violin Iliev
- Xwdit
- Zetaphor
## Gold donors
Acheron
Adam Brown
albinaask
- Andres Hernandez
Arisaka Mayuki
Asher Glick
Barugon
@@ -107,6 +104,7 @@ generous deed immortalized in the next stable release of Godot Engine.
First Last
Florian Rämisch
Gamejunkey
+ Hunter Jones
Jacobus Dens
Jakub Grzesik
Javier Roman
@@ -118,12 +116,11 @@ generous deed immortalized in the next stable release of Godot Engine.
Maciej Pendolski
Manuele Finocchiaro
Markus Wiesner
+ Mateo Navarrete
Mathieu
Matthew Hillier
- Mick
Officine Pixel S.n.c.
Patrick Brock
- Paul E Hansen
Pedro Silva
Retro Village
Rob Messick
@@ -161,11 +158,9 @@ generous deed immortalized in the next stable release of Godot Engine.
Arch Henderson III
Arthur S. Muszynski
Brandon Hawkinson
- Caleb Sizemore
Cameron Connolly
Charlie Whitfield
Chase Taranto
- Chelsea Hash
Chris Petrich
Chris Serino
Cow
@@ -182,10 +177,10 @@ generous deed immortalized in the next stable release of Godot Engine.
Darrian Little
Dennis Belfrage
Dev To be curious
+ Dima Fedotov
Dimitri Nüscheler
Donn Eddy
Douglas Hammond
- Edgar Sun
Eric Brand
Eugenio Hugo Salgüero Jáñez
EXUREI
@@ -193,6 +188,7 @@ generous deed immortalized in the next stable release of Godot Engine.
flesk
foxydevloper
Fransiska
+ Freeman
Gabrielius Vaiškūnas
Gary Hulst
gavlig
@@ -211,6 +207,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Hunter Barabas
HurrieCrane
Jake Burga
+ Jamal Bencharki
James Couzens
Jan Sælid
Jared
@@ -256,8 +253,8 @@ generous deed immortalized in the next stable release of Godot Engine.
medecau
Michael Dürwald
Michael Policastro
+ Michael Seawell
MikadoSC
- Mike B
Mike Barbee
nate etan
Nick Abousselam
@@ -273,6 +270,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Petr Malac
PhaineOfCatz
Piotr Wyszyński
+ Rafał Michno
Raymond Harris
Reilt
Rene Tailleur
@@ -283,7 +281,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Robert Willes
Rob McInroy
Rocknight Studios
- Rod Zilla
+ RodZilla
Romeo Disca
Ronnie Ashlock
Ronny Mühle
@@ -301,7 +299,6 @@ generous deed immortalized in the next stable release of Godot Engine.
SKison
Song Junwoo
spacechase0
- SpiderGlitch_2002
Stephan Hennion
Stephen Brown
Steven Landow
@@ -332,11 +329,11 @@ generous deed immortalized in the next stable release of Godot Engine.
Yifan Lai
Yuancheng Zhang
Zie Weaver
+ Артём Равбецкий
## Silver donors
1D_Inc
- Aaron Mayfield
Aaron Oldenburg
A. B.
Adam Brunnmeier
@@ -349,13 +346,11 @@ generous deed immortalized in the next stable release of Godot Engine.
Adisibio
Adrien de Pierres
Agustinus Arya
- Ahmet Kalyoncu
Aidan O'Flannagain
Aki Mimoto
Alan Beauchamp
Alberto Salazar Muñoz
Alberto Vilches
- Albin Jonasson Svärdsby
Alder Stefano
Alejandro Saucedo
AleMax
@@ -386,15 +381,15 @@ generous deed immortalized in the next stable release of Godot Engine.
Arturo Rosales
Ashley Claymore
Aubrey Falconer
+ Auré Franky
aurelien condomines
Avner
AzulCrescent
b110110
Balázs Batári
- Balázs Kondákor
Bálint Horváth
- Baptiste Le Bourhis
bcat
+ Beau Seymour
Benedikt
Benoit Jauvin-Girard
Ben Ridley
@@ -405,7 +400,6 @@ generous deed immortalized in the next stable release of Godot Engine.
bitbrain
Bjarne Voigtländer
Black Block
- blackjacksike
Blair Allen
Blunderjack
Bobby CC Wong
@@ -425,7 +419,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Carl van der Geest
Casey
Cassidy James
- Cédric Givord
Chad Steadman
Checkpoint Charlie
Chris Chapin
@@ -438,12 +431,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Christopher Chin
Christoph Woinke
Cody Parker
+ CoffeeFingers
Conall O
Conner Lane
Corchari
Craig Maloney
Craig Post
- C. R. Messen
damucz
Daniel Cheney
Daren Scot Wilson
@@ -456,7 +449,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Devin Carraway
Diego Pereira
Dimitri Roche
- Dmitry Fisher
+ Dmitry Fisher (Raccoon path)
Dmytro Korchynskyi
Dominik Wetzel
Don B
@@ -465,7 +458,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Dr Ewan Murray
Duobix
Duodecimal
- DurrDiss
Eduardo Teixeira
Edward Herbert
Edward Swartz
@@ -480,25 +472,22 @@ generous deed immortalized in the next stable release of Godot Engine.
Erika Sanders
Erkki Seppälä
Faisal Alkubaisi
- Fancy Ants Studios
+ Fault Boy
fby
Fekinox
Felix Adam
Felix Bohmann
Fer DC
Filip Lundby
- Forty Doubleu
Francisco Garcia Florez
- Francois Holland
Frank
freakazoid
FrostMarble
Game Endeavor
+ Garett Bass
Gary Thomas
gebba
George Marques
- Georgi Petkov
- Graham Overby
Green Fox
Greg Lincoln
Greg Olson
@@ -512,12 +501,14 @@ generous deed immortalized in the next stable release of Godot Engine.
Haplo
Hayden Foley
Heribert Hirth
+ Hillel Taub-Tabib
+ Hinken
Houdini Blueprints
- Hunter Jones
Ian ORourke
Ian Williams
IndustrialRobot
iveks
+ izzy neuhaus
Jackson Harmer
Jacob D
Jaguar
@@ -533,6 +524,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Jamie Massey
Janis Skuja
Jan Vetulani
+ Japortie
JARKKO PARVIAINEN
Jason Bolton
Jason Evans
@@ -573,6 +565,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Juan Maggi
Juan Uys
Jueast
+ Julian le Roux
Julian Murgia
June Little
Justin Hamilton
@@ -580,6 +573,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Justin Oaksford
Justin Spedding
KaDokta
+ Katsuomi Kobayashi
Keedong Park
keeganstoybox
Keinan Powers
@@ -591,7 +585,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Ketafuki
Kevin van Rooijen
Kiri Jolly
- Kjetil Haugland
Kodera Software
Kolandrious
Konstantin Goncharov
@@ -607,16 +600,16 @@ generous deed immortalized in the next stable release of Godot Engine.
La diagonale du poulpe
Lasse le Dous
Laurent CHEA
+ Laurent Dethoor
Laxman Pradhan
LEMMiNO
Leonardo Dimano
- Lin Chear
Linus Lind Lundgren
Logan Apple
Ludovic DELVAL
Luigi Renna
Luis Gaemperle
- Luis M
+ Luke Kasz
LunaticInAHat
Major Haul
Malcolm
@@ -638,9 +631,11 @@ generous deed immortalized in the next stable release of Godot Engine.
Maverick
Max Fiedler
Maxime Blade
+ Maxime Santerre
Maxwell
Melissa Mears
Merlyn Morgan-Graham
+ Metal Demon 2000
mewin
mhilbrunner
Michael
@@ -649,8 +644,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Michael Morrison
Michael Toporkov
Michał Skwarek
- Michel Candries
- MidoriBunn 'tis BS
Mikael Nordenberg
Mikayla
Mike
@@ -694,6 +687,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Pascal
Patrick Indermühle
Patrick Nafarrete
+ Paul E Hansen
Paul Gieske
Paweł Kowal
Paweł Łyczkowski
@@ -705,6 +699,7 @@ generous deed immortalized in the next stable release of Godot Engine.
pj
Point08
Preethi Vaidyanathan
+ PsycHead
pwab
RabidTunes
RackBar Dingum
@@ -730,19 +725,18 @@ generous deed immortalized in the next stable release of Godot Engine.
Roger Smith
Roglozor
Roland Rząsa
- Roman Tinkov
Ronald Ho Hip (CrimsonZA)
Ronan
Roy Scayged
Ryan Groom
Sam Caulfield
Sam Edson
- Samuel Egger
+ Sammy Fischer
Sangeeth Pavithran
+ schroedinger's possum
Scott Longley
Sean Wall
Sebastian Michailidis
- Sébastien
SeongWan Kim
Sessamekesh
SeungJong k
@@ -784,11 +778,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Thomas Bechtold
Thomas Detoy
Thomas Pickett
- Tianren Qin
+ thommy
Till1805
Tim Drumheller
Tim Erskine
Tim Gleason
+ Tim Klein
Timothy B. MacDonald
Title Plinsut
TMoney
@@ -798,7 +793,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Tom Webster
Torgeir Lilleskog
Torsten Crass
- toupeira
+ TQQQ
Travis O'Brien
Trent Skinner
tril zerobyte
diff --git a/SConstruct b/SConstruct
index 03b409e09b..b8063589c6 100644
--- a/SConstruct
+++ b/SConstruct
@@ -10,6 +10,7 @@ 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
@@ -24,6 +25,21 @@ def _helper_module(name, path):
module = module_from_spec(spec)
spec.loader.exec_module(module)
sys.modules[name] = module
+ # Ensure the module's parents are in loaded to avoid loading the wrong parent
+ # when doing "import foo.bar" while only "foo.bar" as declared as helper module
+ child_module = module
+ parent_name = name
+ while True:
+ try:
+ parent_name, child_name = parent_name.rsplit(".", 1)
+ except ValueError:
+ break
+ try:
+ parent_module = sys.modules[parent_name]
+ except KeyError:
+ parent_module = ModuleType(parent_name)
+ sys.modules[parent_name] = parent_module
+ setattr(parent_module, child_name, child_module)
_helper_module("gles3_builders", "gles3_builders.py")
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 0a13becf6d..d9abf5e5e9 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -186,6 +186,10 @@ bool Engine::is_abort_on_gpu_errors_enabled() const {
return abort_on_gpu_errors;
}
+int32_t Engine::get_gpu_index() const {
+ return gpu_idx;
+}
+
bool Engine::is_validation_layers_enabled() const {
return use_validation_layers;
}
diff --git a/core/config/engine.h b/core/config/engine.h
index 3ec522eafc..65ca58ba1a 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -63,6 +63,7 @@ private:
double _physics_interpolation_fraction = 0.0f;
bool abort_on_gpu_errors = false;
bool use_validation_layers = false;
+ int32_t gpu_idx = -1;
uint64_t _process_frames = 0;
bool _in_physics = false;
@@ -135,6 +136,7 @@ public:
bool is_abort_on_gpu_errors_enabled() const;
bool is_validation_layers_enabled() const;
+ int32_t get_gpu_index() const;
Engine();
virtual ~Engine() {}
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 45776c03e4..b5f1015ff5 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1235,8 +1235,8 @@ ProjectSettings::ProjectSettings() {
// Keep the enum values in sync with the `DisplayServer::VSyncMode` enum.
custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::INT, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox");
custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded");
- GLOBAL_DEF("physics/2d/run_on_thread", false);
- GLOBAL_DEF("physics/3d/run_on_thread", false);
+ GLOBAL_DEF("physics/2d/run_on_separate_thread", false);
+ GLOBAL_DEF("physics/3d/run_on_separate_thread", false);
GLOBAL_DEF("debug/settings/profiler/max_functions", 16384);
custom_prop_info["debug/settings/profiler/max_functions"] = PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1");
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index ef28f43f05..8d03f35617 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -224,14 +224,14 @@ Error OS::shell_open(String p_uri) {
return ::OS::get_singleton()->shell_open(p_uri);
}
-int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr) {
+int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr, bool p_open_console) {
List<String> args;
for (int i = 0; i < p_arguments.size(); i++) {
args.push_back(p_arguments[i]);
}
String pipe;
int exitcode = 0;
- Error err = ::OS::get_singleton()->execute(p_path, args, &pipe, &exitcode, p_read_stderr);
+ Error err = ::OS::get_singleton()->execute(p_path, args, &pipe, &exitcode, p_read_stderr, nullptr, p_open_console);
r_output.push_back(pipe);
if (err != OK) {
return -1;
@@ -252,13 +252,13 @@ int OS::create_instance(const Vector<String> &p_arguments) {
return pid;
}
-int OS::create_process(const String &p_path, const Vector<String> &p_arguments) {
+int OS::create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console) {
List<String> args;
for (int i = 0; i < p_arguments.size(); i++) {
args.push_back(p_arguments[i]);
}
::OS::ProcessID pid = 0;
- Error err = ::OS::get_singleton()->create_process(p_path, args, &pid);
+ Error err = ::OS::get_singleton()->create_process(p_path, args, &pid, p_open_console);
if (err != OK) {
return -1;
}
@@ -557,8 +557,8 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_processor_count"), &OS::get_processor_count);
ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
- ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr"), &OS::execute, DEFVAL(Array()), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("create_process", "path", "arguments"), &OS::create_process);
+ ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);
ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open);
@@ -623,7 +623,6 @@ void OS::_bind_methods() {
// Those default values need to be specified for the docs generator,
// to avoid using values from the documentation writer's own OS instance.
- ADD_PROPERTY_DEFAULT("exit_code", 0);
ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false);
ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900);
diff --git a/core/core_bind.h b/core/core_bind.h
index c7a406b2f3..ac0e92a87a 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -164,8 +164,8 @@ public:
void crash(const String &p_message);
String get_executable_path() const;
- int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false);
- int create_process(const String &p_path, const Vector<String> &p_arguments);
+ int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
+ int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
int create_instance(const Vector<String> &p_arguments);
Error kill(int p_pid);
Error shell_open(String p_uri);
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 6f26288eb7..2f5fd05e6a 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -584,6 +584,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALE_ID);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NONE);
diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp
index a45430465f..8c6e95e276 100644
--- a/core/debugger/remote_debugger_peer.cpp
+++ b/core/debugger/remote_debugger_peer.cpp
@@ -68,8 +68,8 @@ void RemoteDebuggerPeerTCP::close() {
running = false;
thread.wait_to_finish();
tcp_client->disconnect_from_host();
- out_buf.resize(0);
- in_buf.resize(0);
+ out_buf.clear();
+ in_buf.clear();
}
RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) {
@@ -190,7 +190,8 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po
}
void RemoteDebuggerPeerTCP::_thread_func(void *p_ud) {
- const uint64_t min_tick = 100;
+ // Update in time for 144hz monitors
+ const uint64_t min_tick = 6900;
RemoteDebuggerPeerTCP *peer = (RemoteDebuggerPeerTCP *)p_ud;
while (peer->running && peer->is_peer_connected()) {
uint64_t ticks_usec = OS::get_singleton()->get_ticks_usec();
diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp
index 36723e5568..4dd93249ef 100644
--- a/core/debugger/script_debugger.cpp
+++ b/core/debugger/script_debugger.cpp
@@ -104,7 +104,7 @@ void ScriptDebugger::send_error(const String &p_func, const String &p_file, int
// Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way.
error_stack_info.append_array(p_stack_info);
EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type);
- error_stack_info.resize(0);
+ error_stack_info.clear();
}
Vector<ScriptLanguage::StackInfo> ScriptDebugger::get_error_stack_info() const {
diff --git a/core/input/input.cpp b/core/input/input.cpp
index b543e273fe..d36d0f4da0 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -90,6 +90,7 @@ Input::MouseMode Input::get_mouse_mode() const {
}
void Input::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("is_anything_pressed"), &Input::is_anything_pressed);
ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed);
ClassDB::bind_method(D_METHOD("is_physical_key_pressed", "keycode"), &Input::is_physical_key_pressed);
ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed);
@@ -223,6 +224,19 @@ Input::VelocityTrack::VelocityTrack() {
reset();
}
+bool Input::is_anything_pressed() const {
+ _THREAD_SAFE_METHOD_
+
+ for (Map<StringName, Input::Action>::Element *E = action_state.front(); E; E = E->next()) {
+ if (E->get().pressed) {
+ return true;
+ }
+ }
+ return !keys_pressed.is_empty() ||
+ !joy_buttons_pressed.is_empty() ||
+ mouse_button_mask > MouseButton::NONE;
+}
+
bool Input::is_key_pressed(Key p_keycode) const {
_THREAD_SAFE_METHOD_
return keys_pressed.has(p_keycode);
@@ -509,18 +523,20 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
- Point2 pos = mm->get_global_position();
- if (mouse_pos != pos) {
- set_mouse_position(pos);
+ Point2 position = mm->get_global_position();
+ if (mouse_pos != position) {
+ set_mouse_position(position);
}
+ Vector2 relative = mm->get_relative();
+ mouse_velocity_track.update(relative);
if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && (mm->get_button_mask() & MouseButton::LEFT) != MouseButton::NONE) {
Ref<InputEventScreenDrag> drag_event;
drag_event.instantiate();
- drag_event->set_position(mm->get_position());
- drag_event->set_relative(mm->get_relative());
- drag_event->set_velocity(mm->get_velocity());
+ drag_event->set_position(position);
+ drag_event->set_relative(relative);
+ drag_event->set_velocity(get_last_mouse_velocity());
event_dispatch_function(drag_event);
}
@@ -701,7 +717,6 @@ void Input::set_gyroscope(const Vector3 &p_gyroscope) {
}
void Input::set_mouse_position(const Point2 &p_posf) {
- mouse_velocity_track.update(p_posf - mouse_pos);
mouse_pos = p_posf;
}
@@ -922,40 +937,25 @@ void Input::joy_button(int p_device, JoyButton p_button, bool p_pressed) {
// no event?
}
-void Input::joy_axis(int p_device, JoyAxis p_axis, const JoyAxisValue &p_value) {
+void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
_THREAD_SAFE_METHOD_;
ERR_FAIL_INDEX((int)p_axis, (int)JoyAxis::MAX);
Joypad &joy = joy_names[p_device];
- if (joy.last_axis[(size_t)p_axis] == p_value.value) {
+ if (joy.last_axis[(size_t)p_axis] == p_value) {
return;
}
- //when changing direction quickly, insert fake event to release pending inputmap actions
- float last = joy.last_axis[(size_t)p_axis];
- if (p_value.min == 0 && (last < 0.25 || last > 0.75) && (last - 0.5) * (p_value.value - 0.5) < 0) {
- JoyAxisValue jx;
- jx.min = p_value.min;
- jx.value = p_value.value < 0.5 ? 0.6 : 0.4;
- joy_axis(p_device, p_axis, jx);
- } else if (ABS(last) > 0.5 && last * p_value.value <= 0) {
- JoyAxisValue jx;
- jx.min = p_value.min;
- jx.value = last > 0 ? 0.1 : -0.1;
- joy_axis(p_device, p_axis, jx);
- }
-
- joy.last_axis[(size_t)p_axis] = p_value.value;
- float val = p_value.min == 0 ? -1.0f + 2.0f * p_value.value : p_value.value;
+ joy.last_axis[(size_t)p_axis] = p_value;
if (joy.mapping == -1) {
- _axis_event(p_device, p_axis, val);
+ _axis_event(p_device, p_axis, p_value);
return;
}
- JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, val);
+ JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, p_value);
if (map.type == TYPE_BUTTON) {
bool pressed = map.value > 0.5;
@@ -995,10 +995,15 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, const JoyAxisValue &p_value)
}
if (map.type == TYPE_AXIS) {
- _axis_event(p_device, (JoyAxis)map.index, map.value);
+ JoyAxis axis = JoyAxis(map.index);
+ float value = map.value;
+ if (axis == JoyAxis::TRIGGER_LEFT || axis == JoyAxis::TRIGGER_RIGHT) {
+ // Convert to a value between 0.0f and 1.0f.
+ value = 0.5f + value / 2.0f;
+ }
+ _axis_event(p_device, axis, value);
return;
}
- //printf("invalid mapping\n");
}
void Input::joy_hat(int p_device, HatMask p_val) {
diff --git a/core/input/input.h b/core/input/input.h
index b661560b37..ab2cd377f4 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -77,11 +77,6 @@ public:
JOYPADS_MAX = 16,
};
- struct JoyAxisValue {
- int min;
- float value;
- };
-
typedef void (*EventDispatchFunc)(const Ref<InputEvent> &p_event);
private:
@@ -247,6 +242,7 @@ public:
static Input *get_singleton();
+ bool is_anything_pressed() const;
bool is_key_pressed(Key p_keycode) const;
bool is_physical_key_pressed(Key p_keycode) const;
bool is_mouse_button_pressed(MouseButton p_button) const;
@@ -313,7 +309,7 @@ public:
void parse_mapping(String p_mapping);
void joy_button(int p_device, JoyButton p_button, bool p_pressed);
- void joy_axis(int p_device, JoyAxis p_axis, const JoyAxisValue &p_value);
+ void joy_axis(int p_device, JoyAxis p_axis, float p_value);
void joy_hat(int p_device, HatMask p_val);
void add_joy_mapping(String p_mapping, bool p_update_existing = false);
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index c608076a21..ab0f36132f 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -86,7 +86,7 @@ Ref<InputEvent> InputEvent::xformed_by(const Transform2D &p_xform, const Vector2
return Ref<InputEvent>((InputEvent *)this);
}
-bool InputEvent::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const {
+bool InputEvent::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
return false;
}
@@ -412,35 +412,32 @@ Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode) {
return ie;
}
-bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const {
+bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
Ref<InputEventKey> key = p_event;
if (key.is_null()) {
return false;
}
- bool match = false;
- if (get_keycode() == Key::NONE) {
- Key code = get_physical_keycode_with_modifiers();
- Key event_code = key->get_physical_keycode_with_modifiers();
-
- match = get_physical_keycode() == key->get_physical_keycode() && (!key->is_pressed() || (code & event_code) == code);
+ bool match;
+ if (keycode != Key::NONE) {
+ match = keycode == key->keycode;
} else {
- Key code = get_keycode_with_modifiers();
- Key event_code = key->get_keycode_with_modifiers();
-
- match = get_keycode() == key->get_keycode() && (!key->is_pressed() || (code & event_code) == code);
+ match = get_physical_keycode() == key->get_physical_keycode();
+ }
+ if (p_exact_match) {
+ match &= get_modifiers_mask() == key->get_modifiers_mask();
}
if (match) {
bool pressed = key->is_pressed();
- if (p_pressed != nullptr) {
- *p_pressed = pressed;
+ if (r_pressed != nullptr) {
+ *r_pressed = pressed;
}
float strength = pressed ? 1.0f : 0.0f;
- if (p_strength != nullptr) {
- *p_strength = strength;
+ if (r_strength != nullptr) {
+ *r_strength = strength;
}
- if (p_raw_strength != nullptr) {
- *p_raw_strength = strength;
+ if (r_raw_strength != nullptr) {
+ *r_raw_strength = strength;
}
}
return match;
@@ -585,24 +582,27 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co
return mb;
}
-bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const {
+bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_null()) {
return false;
}
- bool match = mb->button_index == button_index;
+ bool match = button_index == mb->button_index;
+ if (p_exact_match) {
+ match &= get_modifiers_mask() == mb->get_modifiers_mask();
+ }
if (match) {
bool pressed = mb->is_pressed();
- if (p_pressed != nullptr) {
- *p_pressed = pressed;
+ if (r_pressed != nullptr) {
+ *r_pressed = pressed;
}
float strength = pressed ? 1.0f : 0.0f;
- if (p_strength != nullptr) {
- *p_strength = strength;
+ if (r_strength != nullptr) {
+ *r_strength = strength;
}
- if (p_raw_strength != nullptr) {
- *p_raw_strength = strength;
+ if (r_raw_strength != nullptr) {
+ *r_raw_strength = strength;
}
}
@@ -887,36 +887,40 @@ bool InputEventJoypadMotion::is_pressed() const {
return Math::abs(axis_value) >= 0.5f;
}
-bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const {
+bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
Ref<InputEventJoypadMotion> jm = p_event;
if (jm.is_null()) {
return false;
}
- bool match = (axis == jm->axis); // Matches even if not in the same direction, but returns a "not pressed" event.
+ // Matches even if not in the same direction, but returns a "not pressed" event.
+ bool match = axis == jm->axis;
+ if (p_exact_match) {
+ match &= (axis_value < 0) == (jm->axis_value < 0);
+ }
if (match) {
float jm_abs_axis_value = Math::abs(jm->get_axis_value());
bool same_direction = (((axis_value < 0) == (jm->axis_value < 0)) || jm->axis_value == 0);
bool pressed = same_direction && jm_abs_axis_value >= p_deadzone;
- if (p_pressed != nullptr) {
- *p_pressed = pressed;
+ if (r_pressed != nullptr) {
+ *r_pressed = pressed;
}
- if (p_strength != nullptr) {
+ if (r_strength != nullptr) {
if (pressed) {
if (p_deadzone == 1.0f) {
- *p_strength = 1.0f;
+ *r_strength = 1.0f;
} else {
- *p_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, jm_abs_axis_value), 0.0f, 1.0f);
+ *r_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, jm_abs_axis_value), 0.0f, 1.0f);
}
} else {
- *p_strength = 0.0f;
+ *r_strength = 0.0f;
}
}
- if (p_raw_strength != nullptr) {
+ if (r_raw_strength != nullptr) {
if (same_direction) { // NOT pressed, because we want to ignore the deadzone.
- *p_raw_strength = jm_abs_axis_value;
+ *r_raw_strength = jm_abs_axis_value;
} else {
- *p_raw_strength = 0.0f;
+ *r_raw_strength = 0.0f;
}
}
}
@@ -994,7 +998,7 @@ float InputEventJoypadButton::get_pressure() const {
return pressure;
}
-bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const {
+bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
Ref<InputEventJoypadButton> jb = p_event;
if (jb.is_null()) {
return false;
@@ -1003,15 +1007,15 @@ bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *
bool match = button_index == jb->button_index;
if (match) {
bool pressed = jb->is_pressed();
- if (p_pressed != nullptr) {
- *p_pressed = pressed;
+ if (r_pressed != nullptr) {
+ *r_pressed = pressed;
}
float strength = pressed ? 1.0f : 0.0f;
- if (p_strength != nullptr) {
- *p_strength = strength;
+ if (r_strength != nullptr) {
+ *r_strength = strength;
}
- if (p_raw_strength != nullptr) {
- *p_raw_strength = strength;
+ if (r_raw_strength != nullptr) {
+ *r_raw_strength = strength;
}
}
@@ -1217,8 +1221,9 @@ String InputEventScreenDrag::to_string() {
bool InputEventScreenDrag::accumulate(const Ref<InputEvent> &p_event) {
Ref<InputEventScreenDrag> drag = p_event;
- if (drag.is_null())
+ if (drag.is_null()) {
return false;
+ }
if (get_index() != drag->get_index()) {
return false;
@@ -1288,7 +1293,7 @@ bool InputEventAction::is_action(const StringName &p_action) const {
return action == p_action;
}
-bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const {
+bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
Ref<InputEventAction> act = p_event;
if (act.is_null()) {
return false;
@@ -1297,15 +1302,15 @@ bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pres
bool match = action == act->action;
if (match) {
bool pressed = act->pressed;
- if (p_pressed != nullptr) {
- *p_pressed = pressed;
+ if (r_pressed != nullptr) {
+ *r_pressed = pressed;
}
float strength = pressed ? 1.0f : 0.0f;
- if (p_strength != nullptr) {
- *p_strength = strength;
+ if (r_strength != nullptr) {
+ *r_strength = strength;
}
- if (p_raw_strength != nullptr) {
- *p_raw_strength = strength;
+ if (r_raw_strength != nullptr) {
+ *r_raw_strength = strength;
}
}
return match;
diff --git a/core/input/input_event.h b/core/input/input_event.h
index 29450dfc52..114db46623 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -79,7 +79,7 @@ public:
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const;
virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const;
virtual bool is_action_type() const;
@@ -192,7 +192,7 @@ public:
Key get_keycode_with_modifiers() const;
Key get_physical_keycode_with_modifiers() const;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override;
virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
virtual bool is_action_type() const override { return true; }
@@ -255,7 +255,7 @@ public:
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override;
virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
virtual bool is_action_type() const override { return true; }
@@ -315,7 +315,7 @@ public:
virtual bool is_pressed() const override;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override;
virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
virtual bool is_action_type() const override { return true; }
@@ -344,7 +344,7 @@ public:
void set_pressure(float p_pressure);
float get_pressure() const;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override;
virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
virtual bool is_action_type() const override { return true; }
@@ -437,7 +437,7 @@ public:
virtual bool is_action(const StringName &p_action) const;
- virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength, float p_deadzone) const override;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override;
virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override;
virtual bool is_action_type() const override { return true; }
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 753ac72ab6..41083b4c47 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -126,15 +126,13 @@ List<StringName> InputMap::get_actions() const {
return actions;
}
-List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const {
+List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
ERR_FAIL_COND_V(!p_event.is_valid(), nullptr);
for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
int device = E->get()->get_device();
if (device == ALL_DEVICES || device == p_event->get_device()) {
- if (p_exact_match && E->get()->is_match(p_event, true)) {
- return E;
- } else if (!p_exact_match && E->get()->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) {
+ if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) {
return E;
}
}
@@ -217,40 +215,28 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName
return event_get_action_status(p_event, p_action, p_exact_match);
}
-bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const {
+bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
OrderedHashMap<StringName, Action>::Element E = input_map.find(p_action);
ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action));
Ref<InputEventAction> input_event_action = p_event;
if (input_event_action.is_valid()) {
- bool pressed = input_event_action->is_pressed();
- if (p_pressed != nullptr) {
- *p_pressed = pressed;
+ const bool pressed = input_event_action->is_pressed();
+ if (r_pressed != nullptr) {
+ *r_pressed = pressed;
+ }
+ const float strength = pressed ? input_event_action->get_strength() : 0.0f;
+ if (r_strength != nullptr) {
+ *r_strength = strength;
}
- if (p_strength != nullptr) {
- *p_strength = pressed ? input_event_action->get_strength() : 0.0f;
+ if (r_raw_strength != nullptr) {
+ *r_raw_strength = strength;
}
return input_event_action->get_action() == p_action;
}
- bool pressed;
- float strength;
- float raw_strength;
- List<Ref<InputEvent>>::Element *event = _find_event(E.get(), p_event, p_exact_match, &pressed, &strength, &raw_strength);
- if (event != nullptr) {
- if (p_pressed != nullptr) {
- *p_pressed = pressed;
- }
- if (p_strength != nullptr) {
- *p_strength = strength;
- }
- if (p_raw_strength != nullptr) {
- *p_raw_strength = raw_strength;
- }
- return true;
- } else {
- return false;
- }
+ List<Ref<InputEvent>>::Element *event = _find_event(E.get(), p_event, p_exact_match, r_pressed, r_strength, r_raw_strength);
+ return event != nullptr;
}
const OrderedHashMap<StringName, InputMap::Action> &InputMap::get_action_map() const {
diff --git a/core/input/input_map.h b/core/input/input_map.h
index e896d1f679..79b4d1038f 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -58,7 +58,7 @@ private:
OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_cache;
OrderedHashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache;
- List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const;
+ List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const;
Array _action_get_events(const StringName &p_action);
Array _get_actions();
@@ -83,7 +83,7 @@ public:
const List<Ref<InputEvent>> *action_get_events(const StringName &p_action);
bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const;
- bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const;
+ bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const;
const OrderedHashMap<StringName, Action> &get_action_map() const;
void load_from_project_settings();
diff --git a/core/io/compression.cpp b/core/io/compression.cpp
index d1f915f064..ae5ccf8354 100644
--- a/core/io/compression.cpp
+++ b/core/io/compression.cpp
@@ -212,7 +212,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s
strm.avail_in = p_src_size;
// Ensure the destination buffer is empty
- p_dst_vect->resize(0);
+ p_dst_vect->clear();
// decompress until deflate stream ends or end of file
do {
@@ -244,7 +244,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s
WARN_PRINT(strm.msg);
}
(void)inflateEnd(&strm);
- p_dst_vect->resize(0);
+ p_dst_vect->clear();
return ret;
}
} while (strm.avail_out > 0 && strm.avail_in > 0);
@@ -254,7 +254,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s
// Enforce max output size
if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) {
(void)inflateEnd(&strm);
- p_dst_vect->resize(0);
+ p_dst_vect->clear();
return Z_BUF_ERROR;
}
} while (ret != Z_STREAM_END);
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index 3da9288ffd..db0758d7fc 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -145,14 +145,18 @@ Error DirAccess::make_dir_recursive(String p_dir) {
full_dir = full_dir.replace("\\", "/");
- //int slices = full_dir.get_slice_count("/");
-
String base;
if (full_dir.begins_with("res://")) {
base = "res://";
} else if (full_dir.begins_with("user://")) {
base = "user://";
+ } else if (full_dir.is_network_share_path()) {
+ int pos = full_dir.find("/", 2);
+ ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
+ pos = full_dir.find("/", pos + 1);
+ ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
+ base = full_dir.substr(0, pos + 1);
} else if (full_dir.begins_with("/")) {
base = "/";
} else if (full_dir.find(":/") != -1) {
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 4d0747c591..52b1120b2a 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -96,6 +96,17 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
return query.substr(1);
}
+Error HTTPClient::verify_headers(const Vector<String> &p_headers) {
+ for (int i = 0; i < p_headers.size(); i++) {
+ String sanitized = p_headers[i].strip_edges();
+ ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, "Invalid HTTP header at index " + itos(i) + ": empty.");
+ ERR_FAIL_COND_V_MSG(sanitized.find(":") < 1, ERR_INVALID_PARAMETER,
+ "Invalid HTTP header at index " + itos(i) + ": String must contain header-value pair, delimited by ':', but was: " + p_headers[i]);
+ }
+
+ return OK;
+}
+
Dictionary HTTPClient::_get_response_headers_as_dictionary() {
List<String> rh;
get_response_headers(&rh);
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 8be6e6524c..de6045f647 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -165,6 +165,7 @@ public:
static HTTPClient *create();
String query_string_from_dict(const Dictionary &p_dict);
+ Error verify_headers(const Vector<String> &p_headers);
virtual Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) = 0;
virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) = 0;
@@ -180,7 +181,7 @@ public:
virtual bool is_response_chunked() const = 0;
virtual int get_response_code() const = 0;
virtual Error get_response_headers(List<String> *r_response) = 0;
- virtual int get_response_body_length() const = 0;
+ virtual int64_t get_response_body_length() const = 0;
virtual PackedByteArray read_response_body_chunk() = 0; // Can't get body as partial text because of most encodings UTF8, gzip, etc.
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index 4c27cd1b10..e61833ce7c 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -71,7 +71,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
connection = tcp_connection;
if (ssl && https_proxy_port != -1) {
- proxy_client.instantiate(); // Needs proxy negotiation
+ proxy_client.instantiate(); // Needs proxy negotiation.
server_host = https_proxy_host;
server_port = https_proxy_port;
} else if (!ssl && http_proxy_port != -1) {
@@ -83,7 +83,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
}
if (server_host.is_valid_ip_address()) {
- // Host contains valid IP
+ // Host contains valid IP.
Error err = tcp_connection->connect_to_host(IPAddress(server_host), server_port);
if (err) {
status = STATUS_CANT_CONNECT;
@@ -92,7 +92,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
status = STATUS_CONNECTING;
} else {
- // Host contains hostname and needs to be resolved to IP
+ // Host contains hostname and needs to be resolved to IP.
resolving = IP::get_singleton()->resolve_hostname_queue_item(server_host);
status = STATUS_RESOLVING;
}
@@ -124,7 +124,7 @@ Ref<StreamPeer> HTTPClientTCP::get_connection() const {
static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_url) {
switch (p_method) {
case HTTPClientTCP::METHOD_CONNECT: {
- // Authority in host:port format, as in RFC7231
+ // Authority in host:port format, as in RFC7231.
int pos = p_url.find_char(':');
return 0 < pos && pos < p_url.length() - 1;
}
@@ -135,7 +135,7 @@ static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_u
[[fallthrough]];
}
default:
- // Absolute path or absolute URL
+ // Absolute path or absolute URL.
return p_url.begins_with("/") || p_url.begins_with("http://") || p_url.begins_with("https://");
}
}
@@ -146,6 +146,11 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector<
ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA);
+ Error err = verify_headers(p_headers);
+ if (err) {
+ return err;
+ }
+
String uri = p_url;
if (!ssl && http_proxy_port != -1) {
uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url);
@@ -173,7 +178,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector<
}
if (add_host) {
if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) {
- // Don't append the standard ports
+ // Don't append the standard ports.
request += "Host: " + conn_host + "\r\n";
} else {
request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
@@ -192,21 +197,12 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector<
request += "\r\n";
CharString cs = request.utf8();
- Vector<uint8_t> data;
- data.resize(cs.length() + p_body_size);
- memcpy(data.ptrw(), cs.get_data(), cs.length());
+ request_buffer->clear();
+ request_buffer->put_data((const uint8_t *)cs.get_data(), cs.length());
if (p_body_size > 0) {
- memcpy(data.ptrw() + cs.length(), p_body, p_body_size);
- }
-
- // TODO Implement non-blocking requests.
- Error err = connection->put_data(data.ptr(), data.size());
-
- if (err) {
- close();
- status = STATUS_CONNECTION_ERROR;
- return err;
+ request_buffer->put_data(p_body, p_body_size);
}
+ request_buffer->seek(0);
status = STATUS_REQUESTING;
head_request = p_method == METHOD_HEAD;
@@ -257,6 +253,7 @@ void HTTPClientTCP::close() {
ip_candidates.clear();
response_headers.clear();
response_str.clear();
+ request_buffer->clear();
body_size = -1;
body_left = 0;
chunk_left = 0;
@@ -274,7 +271,7 @@ Error HTTPClientTCP::poll() {
IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving);
switch (rstatus) {
case IP::RESOLVER_STATUS_WAITING:
- return OK; // Still resolving
+ return OK; // Still resolving.
case IP::RESOLVER_STATUS_DONE: {
ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolving);
@@ -356,7 +353,7 @@ Error HTTPClientTCP::poll() {
} else if (ssl) {
Ref<StreamPeerSSL> ssl;
if (!handshaking) {
- // Connect the StreamPeerSSL and start handshaking
+ // Connect the StreamPeerSSL and start handshaking.
ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
ssl->set_blocking_handshake_enabled(false);
Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host);
@@ -368,7 +365,7 @@ Error HTTPClientTCP::poll() {
connection = ssl;
handshaking = true;
} else {
- // We are already handshaking, which means we can use your already active SSL connection
+ // We are already handshaking, which means we can use your already active SSL connection.
ssl = static_cast<Ref<StreamPeerSSL>>(connection);
if (ssl.is_null()) {
close();
@@ -376,22 +373,22 @@ Error HTTPClientTCP::poll() {
return ERR_CANT_CONNECT;
}
- ssl->poll(); // Try to finish the handshake
+ ssl->poll(); // Try to finish the handshake.
}
if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
- // Handshake has been successful
+ // Handshake has been successful.
handshaking = false;
ip_candidates.clear();
status = STATUS_CONNECTED;
return OK;
} else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
- // Handshake has failed
+ // Handshake has failed.
close();
status = STATUS_SSL_HANDSHAKE_ERROR;
return ERR_CANT_CONNECT;
}
- // ... we will need to poll more for handshake to finish
+ // ... we will need to poll more for handshake to finish.
} else {
ip_candidates.clear();
status = STATUS_CONNECTED;
@@ -416,7 +413,7 @@ Error HTTPClientTCP::poll() {
} break;
case STATUS_BODY:
case STATUS_CONNECTED: {
- // Check if we are still connected
+ // Check if we are still connected.
if (ssl) {
Ref<StreamPeerSSL> tmp = connection;
tmp->poll();
@@ -428,10 +425,34 @@ Error HTTPClientTCP::poll() {
status = STATUS_CONNECTION_ERROR;
return ERR_CONNECTION_ERROR;
}
- // Connection established, requests can now be made
+ // Connection established, requests can now be made.
return OK;
} break;
case STATUS_REQUESTING: {
+ if (request_buffer->get_available_bytes()) {
+ int avail = request_buffer->get_available_bytes();
+ int pos = request_buffer->get_position();
+ const Vector<uint8_t> data = request_buffer->get_data_array();
+ int wrote = 0;
+ Error err;
+ if (blocking) {
+ err = connection->put_data(data.ptr() + pos, avail);
+ wrote += avail;
+ } else {
+ err = connection->put_partial_data(data.ptr() + pos, avail, wrote);
+ }
+ if (err != OK) {
+ close();
+ status = STATUS_CONNECTION_ERROR;
+ return ERR_CONNECTION_ERROR;
+ }
+ pos += wrote;
+ request_buffer->seek(pos);
+ if (avail - wrote > 0) {
+ return OK;
+ }
+ request_buffer->clear();
+ }
while (true) {
uint8_t byte;
int rec = 0;
@@ -534,7 +555,7 @@ Error HTTPClientTCP::poll() {
return OK;
}
-int HTTPClientTCP::get_response_body_length() const {
+int64_t HTTPClientTCP::get_response_body_length() const {
return body_size;
}
@@ -547,7 +568,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() {
if (chunked) {
while (true) {
if (chunk_trailer_part) {
- // We need to consume the trailer part too or keep-alive will break
+ // We need to consume the trailer part too or keep-alive will break.
uint8_t b;
int rec = 0;
err = _get_http_data(&b, 1, rec);
@@ -560,18 +581,18 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() {
int cs = chunk.size();
if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) {
if (cs == 2) {
- // Finally over
+ // Finally over.
chunk_trailer_part = false;
status = STATUS_CONNECTED;
chunk.clear();
break;
} else {
- // We do not process nor return the trailer data
+ // We do not process nor return the trailer data.
chunk.clear();
}
}
} else if (chunk_left == 0) {
- // Reading length
+ // Reading length.
uint8_t b;
int rec = 0;
err = _get_http_data(&b, 1, rec);
@@ -658,7 +679,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() {
uint8_t *w = ret.ptrw();
err = _get_http_data(w + _offset, to_read, rec);
}
- if (rec <= 0) { // Ended up reading less
+ if (rec <= 0) { // Ended up reading less.
ret.resize(_offset);
break;
} else {
@@ -679,7 +700,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() {
close();
if (err == ERR_FILE_EOF) {
- status = STATUS_DISCONNECTED; // Server disconnected
+ status = STATUS_DISCONNECTED; // Server disconnected.
} else {
status = STATUS_CONNECTION_ERROR;
}
@@ -759,6 +780,7 @@ void HTTPClientTCP::set_https_proxy(const String &p_host, int p_port) {
HTTPClientTCP::HTTPClientTCP() {
tcp_connection.instantiate();
+ request_buffer.instantiate();
}
HTTPClient *(*HTTPClient::_create)() = HTTPClientTCP::_create_func;
diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h
index 886ad0ef48..c10e0b1eca 100644
--- a/core/io/http_client_tcp.h
+++ b/core/io/http_client_tcp.h
@@ -38,13 +38,13 @@ private:
Status status = STATUS_DISCONNECTED;
IP::ResolverID resolving = IP::RESOLVER_INVALID_ID;
Array ip_candidates;
- int conn_port = -1; // Server to make requests to
+ int conn_port = -1; // Server to make requests to.
String conn_host;
- int server_port = -1; // Server to connect to (might be a proxy server)
+ int server_port = -1; // Server to connect to (might be a proxy server).
String server_host;
- int http_proxy_port = -1; // Proxy server for http requests
+ int http_proxy_port = -1; // Proxy server for http requests.
String http_proxy_host;
- int https_proxy_port = -1; // Proxy server for https requests
+ int https_proxy_port = -1; // Proxy server for https requests.
String https_proxy_host;
bool ssl = false;
bool ssl_verify_host = false;
@@ -62,9 +62,10 @@ private:
int64_t body_left = 0;
bool read_until_eof = false;
+ Ref<StreamPeerBuffer> request_buffer;
Ref<StreamPeerTCP> tcp_connection;
Ref<StreamPeer> connection;
- Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server
+ Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server.
int response_num = 0;
Vector<String> response_headers;
@@ -87,7 +88,7 @@ public:
bool is_response_chunked() const override;
int get_response_code() const override;
Error get_response_headers(List<String> *r_response) override;
- int get_response_body_length() const override;
+ int64_t get_response_body_length() const override;
PackedByteArray read_response_body_chunk() override;
void set_blocking_mode(bool p_enable) override;
bool is_blocking_mode_enabled() const override;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 4a1f129245..9df2b6835c 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -65,10 +65,6 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
"BPTC_RGBA",
"BPTC_RGBF",
"BPTC_RGBFU",
- "PVRTC1_2", //pvrtc
- "PVRTC1_2A",
- "PVRTC1_4",
- "PVRTC1_4A",
"ETC", //etc1
"ETC2_R11", //etc2
"ETC2_R11S", //signed", NOT srgb.
@@ -148,14 +144,6 @@ int Image::get_format_pixel_size(Format p_format) {
return 1; //float /
case FORMAT_BPTC_RGBFU:
return 1; //unsigned float
- case FORMAT_PVRTC1_2:
- return 1; //pvrtc
- case FORMAT_PVRTC1_2A:
- return 1;
- case FORMAT_PVRTC1_4:
- return 1;
- case FORMAT_PVRTC1_4A:
- return 1;
case FORMAT_ETC:
return 1; //etc1
case FORMAT_ETC2_R11:
@@ -193,16 +181,6 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) {
r_w = 4;
r_h = 4;
} break;
- case FORMAT_PVRTC1_2:
- case FORMAT_PVRTC1_2A: {
- r_w = 16;
- r_h = 8;
- } break;
- case FORMAT_PVRTC1_4A:
- case FORMAT_PVRTC1_4: {
- r_w = 8;
- r_h = 8;
- } break;
case FORMAT_ETC: {
r_w = 4;
r_h = 4;
@@ -235,10 +213,8 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) {
}
int Image::get_format_pixel_rshift(Format p_format) {
- if (p_format == FORMAT_DXT1 || p_format == FORMAT_RGTC_R || p_format == FORMAT_PVRTC1_4 || p_format == FORMAT_PVRTC1_4A || p_format == FORMAT_ETC || p_format == FORMAT_ETC2_R11 || p_format == FORMAT_ETC2_R11S || p_format == FORMAT_ETC2_RGB8 || p_format == FORMAT_ETC2_RGB8A1) {
+ if (p_format == FORMAT_DXT1 || p_format == FORMAT_RGTC_R || p_format == FORMAT_ETC || p_format == FORMAT_ETC2_R11 || p_format == FORMAT_ETC2_R11S || p_format == FORMAT_ETC2_RGB8 || p_format == FORMAT_ETC2_RGB8A1) {
return 1;
- } else if (p_format == FORMAT_PVRTC1_2 || p_format == FORMAT_PVRTC1_2A) {
- return 2;
} else {
return 0;
}
@@ -254,14 +230,6 @@ int Image::get_format_block_size(Format p_format) {
return 4;
}
- case FORMAT_PVRTC1_2:
- case FORMAT_PVRTC1_2A: {
- return 4;
- }
- case FORMAT_PVRTC1_4A:
- case FORMAT_PVRTC1_4: {
- return 4;
- }
case FORMAT_ETC: {
return 4;
}
@@ -1466,12 +1434,11 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &
}
// Set mipmap size.
- // It might be necessary to put this after the minimum mipmap size check because of the possible occurrence of "1 >> 1".
if (r_mm_width) {
- *r_mm_width = bw >> 1;
+ *r_mm_width = w;
}
if (r_mm_height) {
- *r_mm_height = bh >> 1;
+ *r_mm_height = h;
}
// Reach target mipmap.
@@ -2222,8 +2189,6 @@ bool Image::is_invisible() const {
} break;
- case FORMAT_PVRTC1_2A:
- case FORMAT_PVRTC1_4A:
case FORMAT_DXT3:
case FORMAT_DXT5: {
detected = true;
@@ -2264,8 +2229,6 @@ Image::AlphaMode Image::detect_alpha() const {
}
} break;
- case FORMAT_PVRTC1_2A:
- case FORMAT_PVRTC1_4A:
case FORMAT_DXT3:
case FORMAT_DXT5: {
detected = true;
@@ -2361,8 +2324,6 @@ Error Image::decompress() {
_image_decompress_bc(this);
} else if (format >= FORMAT_BPTC_RGBA && format <= FORMAT_BPTC_RGBFU && _image_decompress_bptc) {
_image_decompress_bptc(this);
- } else if (format >= FORMAT_PVRTC1_2 && format <= FORMAT_PVRTC1_4A && _image_decompress_pvrtc) {
- _image_decompress_pvrtc(this);
} else if (format == FORMAT_ETC && _image_decompress_etc1) {
_image_decompress_etc1(this);
} else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RA_AS_RG && _image_decompress_etc2) {
@@ -2385,10 +2346,6 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels
ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE);
_image_compress_bc_func(this, p_lossy_quality, p_channels);
} break;
- case COMPRESS_PVRTC1_4: {
- ERR_FAIL_COND_V(!_image_compress_pvrtc1_4bpp_func, ERR_UNAVAILABLE);
- _image_compress_pvrtc1_4bpp_func(this);
- } break;
case COMPRESS_ETC: {
ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE);
_image_compress_etc1_func(this, p_lossy_quality);
@@ -2752,10 +2709,8 @@ ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = nullptr;
-void (*Image::_image_compress_pvrtc1_4bpp_func)(Image *) = nullptr;
void (*Image::_image_compress_etc1_func)(Image *, float) = nullptr;
void (*Image::_image_compress_etc2_func)(Image *, float, Image::UsedChannels) = nullptr;
-void (*Image::_image_decompress_pvrtc)(Image *) = nullptr;
void (*Image::_image_decompress_bc)(Image *) = nullptr;
void (*Image::_image_decompress_bptc)(Image *) = nullptr;
void (*Image::_image_decompress_etc1)(Image *) = nullptr;
@@ -3238,10 +3193,6 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBA); //btpc bc6h
BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBF); //float /
BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBFU); //unsigned float
- BIND_ENUM_CONSTANT(FORMAT_PVRTC1_2); //pvrtc
- BIND_ENUM_CONSTANT(FORMAT_PVRTC1_2A);
- BIND_ENUM_CONSTANT(FORMAT_PVRTC1_4);
- BIND_ENUM_CONSTANT(FORMAT_PVRTC1_4A);
BIND_ENUM_CONSTANT(FORMAT_ETC); //etc1
BIND_ENUM_CONSTANT(FORMAT_ETC2_R11); //etc2
BIND_ENUM_CONSTANT(FORMAT_ETC2_R11S); //signed ); NOT srgb.
@@ -3265,7 +3216,6 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(ALPHA_BLEND);
BIND_ENUM_CONSTANT(COMPRESS_S3TC);
- BIND_ENUM_CONSTANT(COMPRESS_PVRTC1_4);
BIND_ENUM_CONSTANT(COMPRESS_ETC);
BIND_ENUM_CONSTANT(COMPRESS_ETC2);
BIND_ENUM_CONSTANT(COMPRESS_BPTC);
diff --git a/core/io/image.h b/core/io/image.h
index 29236a55e5..ddfb2bb01d 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -89,10 +89,6 @@ public:
FORMAT_BPTC_RGBA, //btpc bc7
FORMAT_BPTC_RGBF, //float bc6h
FORMAT_BPTC_RGBFU, //unsigned float bc6hu
- FORMAT_PVRTC1_2, //pvrtc1
- FORMAT_PVRTC1_2A,
- FORMAT_PVRTC1_4,
- FORMAT_PVRTC1_4A,
FORMAT_ETC, //etc1
FORMAT_ETC2_R11, //etc2
FORMAT_ETC2_R11S, //signed, NOT srgb.
@@ -136,11 +132,9 @@ public:
static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels);
static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels);
- static void (*_image_compress_pvrtc1_4bpp_func)(Image *);
static void (*_image_compress_etc1_func)(Image *, float);
static void (*_image_compress_etc2_func)(Image *, float, UsedChannels p_channels);
- static void (*_image_decompress_pvrtc)(Image *);
static void (*_image_decompress_bc)(Image *);
static void (*_image_decompress_bptc)(Image *);
static void (*_image_decompress_etc1)(Image *);
@@ -332,7 +326,6 @@ public:
enum CompressMode {
COMPRESS_S3TC,
- COMPRESS_PVRTC1_4,
COMPRESS_ETC,
COMPRESS_ETC2,
COMPRESS_BPTC,
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index 4970afc1d3..8e0d47e762 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -98,6 +98,11 @@ struct _IP_ResolverPrivate {
if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) {
continue;
}
+ // We might be overriding another result, but we don't care as long as the result is valid.
+ if (response.size()) {
+ String key = get_cache_key(hostname, type);
+ cache[key] = response;
+ }
queue[i].response = response;
queue[i].status.set(response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE);
}
@@ -120,30 +125,8 @@ struct _IP_ResolverPrivate {
};
IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) {
- List<IPAddress> res;
- String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
-
- resolver->mutex.lock();
- if (resolver->cache.has(key)) {
- res = resolver->cache[key];
- } else {
- // This should be run unlocked so the resolver thread can keep
- // resolving other requests.
- resolver->mutex.unlock();
- _resolve_hostname(res, p_hostname, p_type);
- resolver->mutex.lock();
- // We might be overriding another result, but we don't care (they are the
- // same hostname).
- resolver->cache[key] = res;
- }
- resolver->mutex.unlock();
-
- for (int i = 0; i < res.size(); ++i) {
- if (res[i].is_valid()) {
- return res[i];
- }
- }
- return IPAddress();
+ const Array addresses = resolve_hostname_addresses(p_hostname, p_type);
+ return addresses.size() ? addresses[0].operator IPAddress() : IPAddress();
}
Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) {
@@ -159,17 +142,16 @@ Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) {
resolver->mutex.unlock();
_resolve_hostname(res, p_hostname, p_type);
resolver->mutex.lock();
- // We might be overriding another result, but we don't care (they are the
- // same hostname).
- resolver->cache[key] = res;
+ // We might be overriding another result, but we don't care as long as the result is valid.
+ if (res.size()) {
+ resolver->cache[key] = res;
+ }
}
resolver->mutex.unlock();
Array result;
for (int i = 0; i < res.size(); ++i) {
- if (res[i].is_valid()) {
- result.push_back(String(res[i]));
- }
+ result.push_back(String(res[i]));
}
return result;
}
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index 6a0668f027..555d4f6df4 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -52,9 +52,8 @@ ObjectID EncodedObjectAsID::get_object_id() const {
return id;
}
-#define _S(a) ((int32_t)a)
-#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err)
-#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err)
+#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(b)) < 0 || ((int32_t)(a)) < 0 || ((int32_t)(a)) > INT_MAX - ((int32_t)(b)), err)
+#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(a)) < 0 || ((int32_t)(b)) <= 0 || ((int32_t)(a)) > INT_MAX / ((int32_t)(b)), err)
#define ENCODE_MASK 0xFF
#define ENCODE_FLAG_64 1 << 16
diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp
index e90d1695e5..0af236f766 100644
--- a/core/io/packet_peer.cpp
+++ b/core/io/packet_peer.cpp
@@ -39,7 +39,7 @@ void PacketPeer::set_encode_buffer_max_size(int p_max_size) {
ERR_FAIL_COND_MSG(p_max_size < 1024, "Max encode buffer must be at least 1024 bytes");
ERR_FAIL_COND_MSG(p_max_size > 256 * 1024 * 1024, "Max encode buffer cannot exceed 256 MiB");
encode_buffer_max_size = next_power_of_2(p_max_size);
- encode_buffer.resize(0);
+ encode_buffer.clear();
}
int PacketPeer::get_encode_buffer_max_size() const {
diff --git a/core/io/resource.h b/core/io/resource.h
index a0800c57cb..dea2160616 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -37,6 +37,8 @@
#include "core/templates/safe_refcount.h"
#include "core/templates/self_list.h"
+class Node;
+
#define RES_BASE_EXTENSION(m_ext) \
public: \
static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension(m_ext, get_class_static()); } \
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 8fe1ac29b3..21bf566b1b 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -806,38 +806,26 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
// To find the path of the remapped resource, we extract the locale name after
// the last ':' to match the project locale.
- // We also fall back in case of regional locales as done in TranslationServer::translate
- // (e.g. 'ru_RU' -> 'ru' if the former has no specific mapping).
String locale = TranslationServer::get_singleton()->get_locale();
ERR_FAIL_COND_V_MSG(locale.length() < 2, p_path, "Could not remap path '" + p_path + "' for translation as configured locale '" + locale + "' is invalid.");
- String lang = TranslationServer::get_language_code(locale);
Vector<String> &res_remaps = *translation_remaps.getptr(new_path);
- bool near_match = false;
+ int best_score = 0;
for (int i = 0; i < res_remaps.size(); i++) {
int split = res_remaps[i].rfind(":");
if (split == -1) {
continue;
}
-
String l = res_remaps[i].substr(split + 1).strip_edges();
- if (l == locale) { // Exact match.
- new_path = res_remaps[i].left(split);
- break;
- } else if (near_match) {
- continue; // Already found near match, keep going for potential exact match.
- }
-
- // No exact match (e.g. locale 'ru_RU' but remap is 'ru'), let's look further
- // for a near match (same language code, i.e. first 2 or 3 letters before
- // regional code, if included).
- if (TranslationServer::get_language_code(l) == lang) {
- // Language code matches, that's a near match. Keep looking for exact match.
- near_match = true;
+ int score = TranslationServer::get_singleton()->compare_locales(locale, l);
+ if (score > 0 && score >= best_score) {
new_path = res_remaps[i].left(split);
- continue;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
}
}
diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp
index 28ebe811c9..c65968ef03 100644
--- a/core/io/stream_peer.cpp
+++ b/core/io/stream_peer.cpp
@@ -98,7 +98,7 @@ Array StreamPeer::_get_partial_data(int p_bytes) {
Error err = get_partial_data(&w[0], p_bytes, received);
if (err != OK) {
- data.resize(0);
+ data.clear();
} else if (received != data.size()) {
data.resize(received);
}
@@ -563,7 +563,7 @@ Vector<uint8_t> StreamPeerBuffer::get_data_array() const {
}
void StreamPeerBuffer::clear() {
- data.resize(0);
+ data.clear();
pointer = 0;
}
diff --git a/core/math/aabb.h b/core/math/aabb.h
index 2eaaafa27d..3d19410ddf 100644
--- a/core/math/aabb.h
+++ b/core/math/aabb.h
@@ -41,7 +41,7 @@
*/
class Variant;
-class AABB {
+class _NO_DISCARD_ AABB {
public:
Vector3 position;
Vector3 size;
diff --git a/core/math/basis.h b/core/math/basis.h
index 709f2cb3cf..802da82089 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -34,7 +34,7 @@
#include "core/math/quaternion.h"
#include "core/math/vector3.h"
-class Basis {
+class _NO_DISCARD_ Basis {
private:
void _set_diagonal(const Vector3 &p_diag);
diff --git a/core/math/color.h b/core/math/color.h
index 6c09f7941c..72a4a5f8be 100644
--- a/core/math/color.h
+++ b/core/math/color.h
@@ -34,7 +34,7 @@
#include "core/math/math_funcs.h"
#include "core/string/ustring.h"
-struct Color {
+struct _NO_DISCARD_ Color {
union {
struct {
float r;
diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp
index 912ffb8b16..bd292f4c2a 100644
--- a/core/math/convex_hull.cpp
+++ b/core/math/convex_hull.cpp
@@ -2129,7 +2129,7 @@ bool ConvexHullInternal::shift_face(Face *p_face, real_t p_amount, LocalVector<V
printf("Needed %d iterations to remove part\n", n);
#endif
- p_stack.resize(0);
+ p_stack.clear();
p_face->origin = shifted_origin;
return true;
@@ -2167,9 +2167,9 @@ real_t ConvexHullComputer::compute(const Vector3 *p_coords, int32_t p_count, rea
return shift;
}
- vertices.resize(0);
- edges.resize(0);
- faces.resize(0);
+ vertices.clear();
+ edges.clear();
+ faces.clear();
LocalVector<ConvexHullInternal::Vertex *> old_vertices;
get_vertex_copy(hull.vertex_list, old_vertices);
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 4f8e79038f..203566579d 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -197,6 +197,7 @@ Error Expression::_get_token(Token &r_token) {
case '\'':
case '"': {
String str;
+ char32_t prev = 0;
while (true) {
char32_t ch = GET_CHAR();
@@ -234,9 +235,11 @@ Error Expression::_get_token(Token &r_token) {
case 'r':
res = 13;
break;
+ case 'U':
case 'u': {
- // hex number
- for (int j = 0; j < 4; j++) {
+ // Hexadecimal sequence.
+ int hex_len = (next == 'U') ? 6 : 4;
+ for (int j = 0; j < hex_len; j++) {
char32_t c = GET_CHAR();
if (c == 0) {
@@ -273,12 +276,46 @@ Error Expression::_get_token(Token &r_token) {
} break;
}
+ // Parse UTF-16 pair.
+ if ((res & 0xfffffc00) == 0xd800) {
+ if (prev == 0) {
+ prev = res;
+ continue;
+ } else {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ } else if ((res & 0xfffffc00) == 0xdc00) {
+ if (prev == 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired trail surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ } else {
+ res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev = 0;
+ }
+ }
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += res;
-
} else {
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += ch;
}
}
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
r_token.type = TK_CONSTANT;
r_token.value = str;
diff --git a/core/math/face3.cpp b/core/math/face3.cpp
index ba10b50465..d588f34e5d 100644
--- a/core/math/face3.cpp
+++ b/core/math/face3.cpp
@@ -260,8 +260,8 @@ void Face3::project_range(const Vector3 &p_normal, const Transform3D &p_transfor
}
void Face3::get_support(const Vector3 &p_normal, const Transform3D &p_transform, Vector3 *p_vertices, int *p_count, int p_max) const {
-#define _FACE_IS_VALID_SUPPORT_THRESHOLD 0.98
-#define _EDGE_IS_VALID_SUPPORT_THRESHOLD 0.05
+ constexpr double face_support_threshold = 0.98;
+ constexpr double edge_support_threshold = 0.05;
if (p_max <= 0) {
return;
@@ -270,7 +270,7 @@ void Face3::get_support(const Vector3 &p_normal, const Transform3D &p_transform,
Vector3 n = p_transform.basis.xform_inv(p_normal);
/** TEST FACE AS SUPPORT **/
- if (get_plane().normal.dot(n) > _FACE_IS_VALID_SUPPORT_THRESHOLD) {
+ if (get_plane().normal.dot(n) > face_support_threshold) {
*p_count = MIN(3, p_max);
for (int i = 0; i < *p_count; i++) {
@@ -304,7 +304,7 @@ void Face3::get_support(const Vector3 &p_normal, const Transform3D &p_transform,
// check if edge is valid as a support
real_t dot = (vertex[i] - vertex[(i + 1) % 3]).normalized().dot(n);
dot = ABS(dot);
- if (dot < _EDGE_IS_VALID_SUPPORT_THRESHOLD) {
+ if (dot < edge_support_threshold) {
*p_count = MIN(2, p_max);
for (int j = 0; j < *p_count; j++) {
diff --git a/core/math/face3.h b/core/math/face3.h
index 5a34858ccb..3dbbca09e0 100644
--- a/core/math/face3.h
+++ b/core/math/face3.h
@@ -36,7 +36,7 @@
#include "core/math/transform_3d.h"
#include "core/math/vector3.h"
-class Face3 {
+class _NO_DISCARD_ Face3 {
public:
enum Side {
SIDE_OVER,
diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp
index 98a2c27d93..a9ff46410e 100644
--- a/core/math/geometry_3d.cpp
+++ b/core/math/geometry_3d.cpp
@@ -281,16 +281,16 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int
int div_y = len_y > 1 ? 2 : 1;
int div_z = len_z > 1 ? 2 : 1;
-#define _SPLIT(m_i, m_div, m_v, m_len_v, m_new_v, m_new_len_v) \
- if (m_div == 1) { \
- m_new_v = m_v; \
- m_new_len_v = 1; \
- } else if (m_i == 0) { \
- m_new_v = m_v; \
- m_new_len_v = m_len_v / 2; \
- } else { \
- m_new_v = m_v + m_len_v / 2; \
- m_new_len_v = m_len_v - m_len_v / 2; \
+#define SPLIT_DIV(m_i, m_div, m_v, m_len_v, m_new_v, m_new_len_v) \
+ if (m_div == 1) { \
+ m_new_v = m_v; \
+ m_new_len_v = 1; \
+ } else if (m_i == 0) { \
+ m_new_v = m_v; \
+ m_new_len_v = m_len_v / 2; \
+ } else { \
+ m_new_v = m_v + m_len_v / 2; \
+ m_new_len_v = m_len_v - m_len_v / 2; \
}
int new_x;
@@ -301,18 +301,20 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int
int new_len_z;
for (int i = 0; i < div_x; i++) {
- _SPLIT(i, div_x, x, len_x, new_x, new_len_x);
+ SPLIT_DIV(i, div_x, x, len_x, new_x, new_len_x);
for (int j = 0; j < div_y; j++) {
- _SPLIT(j, div_y, y, len_y, new_y, new_len_y);
+ SPLIT_DIV(j, div_y, y, len_y, new_y, new_len_y);
for (int k = 0; k < div_z; k++) {
- _SPLIT(k, div_z, z, len_z, new_z, new_len_z);
+ SPLIT_DIV(k, div_z, z, len_z, new_z, new_len_z);
_plot_face(p_cell_status, new_x, new_y, new_z, new_len_x, new_len_y, new_len_z, voxelsize, p_face);
}
}
}
+
+#undef SPLIT_DIV
}
static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, int len_x, int len_y, int len_z) {
@@ -491,11 +493,10 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i
}
Vector<Face3> Geometry3D::wrap_geometry(Vector<Face3> p_array, real_t *p_error) {
-#define _MIN_SIZE 1.0
-#define _MAX_LENGTH 20
-
int face_count = p_array.size();
const Face3 *faces = p_array.ptr();
+ constexpr double min_size = 1.0;
+ constexpr int max_length = 20;
AABB global_aabb;
@@ -512,22 +513,22 @@ Vector<Face3> Geometry3D::wrap_geometry(Vector<Face3> p_array, real_t *p_error)
// Determine amount of cells in grid axis.
int div_x, div_y, div_z;
- if (global_aabb.size.x / _MIN_SIZE < _MAX_LENGTH) {
- div_x = (int)(global_aabb.size.x / _MIN_SIZE) + 1;
+ if (global_aabb.size.x / min_size < max_length) {
+ div_x = (int)(global_aabb.size.x / min_size) + 1;
} else {
- div_x = _MAX_LENGTH;
+ div_x = max_length;
}
- if (global_aabb.size.y / _MIN_SIZE < _MAX_LENGTH) {
- div_y = (int)(global_aabb.size.y / _MIN_SIZE) + 1;
+ if (global_aabb.size.y / min_size < max_length) {
+ div_y = (int)(global_aabb.size.y / min_size) + 1;
} else {
- div_y = _MAX_LENGTH;
+ div_y = max_length;
}
- if (global_aabb.size.z / _MIN_SIZE < _MAX_LENGTH) {
- div_z = (int)(global_aabb.size.z / _MIN_SIZE) + 1;
+ if (global_aabb.size.z / min_size < max_length) {
+ div_z = (int)(global_aabb.size.z / min_size) + 1;
} else {
- div_z = _MAX_LENGTH;
+ div_z = max_length;
}
Vector3 voxelsize = global_aabb.size;
diff --git a/core/math/plane.h b/core/math/plane.h
index bac946502b..8cb6f62b3b 100644
--- a/core/math/plane.h
+++ b/core/math/plane.h
@@ -35,7 +35,7 @@
class Variant;
-class Plane {
+class _NO_DISCARD_ Plane {
public:
Vector3 normal;
real_t d = 0;
diff --git a/core/math/quaternion.h b/core/math/quaternion.h
index cf3762e090..2575d7d229 100644
--- a/core/math/quaternion.h
+++ b/core/math/quaternion.h
@@ -36,7 +36,7 @@
#include "core/math/vector3.h"
#include "core/string/ustring.h"
-class Quaternion {
+class _NO_DISCARD_ Quaternion {
public:
union {
struct {
diff --git a/core/math/rect2.h b/core/math/rect2.h
index 1f14a01103..b14c69302c 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -35,7 +35,7 @@
struct Transform2D;
-struct Rect2 {
+struct _NO_DISCARD_ Rect2 {
Point2 position;
Size2 size;
@@ -207,11 +207,6 @@ struct Rect2 {
bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; }
inline Rect2 grow(real_t p_amount) const {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0)) {
- ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size.");
- }
-#endif
Rect2 g = *this;
g.grow_by(p_amount);
return g;
@@ -238,11 +233,6 @@ struct Rect2 {
}
inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0)) {
- ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size.");
- }
-#endif
Rect2 g = *this;
g.position.x -= p_left;
g.position.y -= p_top;
@@ -373,7 +363,7 @@ struct Rect2 {
}
};
-struct Rect2i {
+struct _NO_DISCARD_ Rect2i {
Point2i position;
Size2i size;
@@ -392,16 +382,16 @@ struct Rect2i {
ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
}
#endif
- if (position.x > (p_rect.position.x + p_rect.size.width)) {
+ if (position.x >= (p_rect.position.x + p_rect.size.width)) {
return false;
}
- if ((position.x + size.width) < p_rect.position.x) {
+ if ((position.x + size.width) <= p_rect.position.x) {
return false;
}
- if (position.y > (p_rect.position.y + p_rect.size.height)) {
+ if (position.y >= (p_rect.position.y + p_rect.size.height)) {
return false;
}
- if ((position.y + size.height) < p_rect.position.y) {
+ if ((position.y + size.height) <= p_rect.position.y) {
return false;
}
@@ -415,8 +405,8 @@ struct Rect2i {
}
#endif
return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) &&
- ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) &&
- ((p_rect.position.y + p_rect.size.y) < (position.y + size.y));
+ ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) &&
+ ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y));
}
_FORCE_INLINE_ bool has_no_area() const {
@@ -488,11 +478,6 @@ struct Rect2i {
bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; }
Rect2i grow(int p_amount) const {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
Rect2i g = *this;
g.position.x -= p_amount;
g.position.y -= p_amount;
@@ -515,11 +500,6 @@ struct Rect2i {
}
inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
Rect2i g = *this;
g.position.x -= p_left;
g.position.y -= p_top;
diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h
index 752a885eba..6c2d51bd9b 100644
--- a/core/math/transform_2d.h
+++ b/core/math/transform_2d.h
@@ -33,7 +33,7 @@
#include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring
-struct Transform2D {
+struct _NO_DISCARD_ Transform2D {
// Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper":
// M = (elements[0][0] elements[1][0])
// (elements[0][1] elements[1][1])
diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h
index c0ef2ecfc1..c16c278e74 100644
--- a/core/math/transform_3d.h
+++ b/core/math/transform_3d.h
@@ -35,7 +35,7 @@
#include "core/math/basis.h"
#include "core/math/plane.h"
-class Transform3D {
+class _NO_DISCARD_ Transform3D {
public:
Basis basis;
Vector3 origin;
diff --git a/core/math/vector2.h b/core/math/vector2.h
index a340036ac7..af40b9e68d 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -36,7 +36,7 @@
struct Vector2i;
-struct Vector2 {
+struct _NO_DISCARD_ Vector2 {
static const int AXIS_COUNT = 2;
enum Axis {
@@ -284,7 +284,7 @@ typedef Vector2 Point2;
/* INTEGER STUFF */
-struct Vector2i {
+struct _NO_DISCARD_ Vector2i {
enum Axis {
AXIS_X,
AXIS_Y,
diff --git a/core/math/vector3.h b/core/math/vector3.h
index d7a72b05a8..b62edef40f 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -37,7 +37,7 @@
#include "core/string/ustring.h"
class Basis;
-struct Vector3 {
+struct _NO_DISCARD_ Vector3 {
static const int AXIS_COUNT = 3;
enum Axis {
diff --git a/core/math/vector3i.h b/core/math/vector3i.h
index 1416c98057..1564ee9173 100644
--- a/core/math/vector3i.h
+++ b/core/math/vector3i.h
@@ -35,7 +35,7 @@
#include "core/string/ustring.h"
#include "core/typedefs.h"
-struct Vector3i {
+struct _NO_DISCARD_ Vector3i {
enum Axis {
AXIS_X,
AXIS_Y,
diff --git a/core/object/object.h b/core/object/object.h
index 602bd3cda1..1a0a81581d 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -94,6 +94,7 @@ enum PropertyHint {
PROPERTY_HINT_INT_IS_OBJECTID,
PROPERTY_HINT_ARRAY_TYPE,
PROPERTY_HINT_INT_IS_POINTER,
+ PROPERTY_HINT_LOCALE_ID,
PROPERTY_HINT_MAX,
// When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit
};
@@ -699,8 +700,9 @@ public:
static String get_category_static() { return String(); }
virtual String get_class() const {
- if (_extension)
+ if (_extension) {
return _extension->class_name.operator String();
+ }
return "Object";
}
virtual String get_save_class() const { return get_class(); } //class stored when saving
diff --git a/core/os/os.h b/core/os/os.h
index b4ad2c7345..36d85da70b 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -149,8 +149,8 @@ public:
virtual int get_low_processor_usage_mode_sleep_usec() const;
virtual String get_executable_path() const;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) = 0;
- virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) = 0;
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0;
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0;
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) { return create_process(get_executable_path(), p_arguments, r_child_id); };
virtual Error kill(const ProcessID &p_pid) = 0;
virtual int get_process_id() const;
@@ -295,6 +295,9 @@ public:
virtual void debug_break();
virtual int get_exit_code() const;
+ // `set_exit_code` should only be used from `SceneTree` (or from a similar
+ // level, e.g. from the `Main::start` if leaving without creating a `SceneTree`).
+ // For other components, `SceneTree.quit()` should be used instead.
virtual void set_exit_code(int p_code);
virtual int get_processor_count() const;
diff --git a/core/string/locales.h b/core/string/locales.h
new file mode 100644
index 0000000000..32d6608ec2
--- /dev/null
+++ b/core/string/locales.h
@@ -0,0 +1,1197 @@
+/*************************************************************************/
+/* locales.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef LOCALES_H
+#define LOCALES_H
+
+// Windows has some weird locale identifiers which do not honor the ISO 639-1
+// standardized nomenclature. Whenever those don't conflict with existing ISO
+// identifiers, we override them.
+//
+// Reference:
+// - https://msdn.microsoft.com/en-us/library/windows/desktop/ms693062(v=vs.85).aspx
+
+static const char *locale_renames[][2] = {
+ { "in", "id" }, // Indonesian
+ { "iw", "he" }, // Hebrew
+ { "no", "nb" }, // Norwegian Bokmål
+ { "C", "en" }, // Locale is not set, fallback to English.
+ { nullptr, nullptr }
+};
+
+// Additional script information to preferred scripts.
+// Language code, script code, default country, supported countries.
+// Reference:
+// - https://lh.2xlibre.net/locales/
+// - https://www.localeplanet.com/icu/index.html
+// - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f
+
+static const char *locale_scripts[][4] = {
+ { "az", "Latn", "", "AZ" },
+ { "az", "Arab", "", "IR" },
+ { "bs", "Latn", "", "BA" },
+ { "ff", "Latn", "", "BF,CM,GH,GM,GN,GW,LR,MR,NE,NG,SL,SN" },
+ { "pa", "Arab", "PK", "PK" },
+ { "pa", "Guru", "IN", "IN" },
+ { "sd", "Arab", "PK", "PK" },
+ { "sd", "Deva", "IN", "IN" },
+ { "shi", "Tfng", "", "MA" },
+ { "sr", "Cyrl", "", "BA,RS,XK" },
+ { "sr", "Latn", "", "ME" },
+ { "uz", "Latn", "", "UZ" },
+ { "uz", "Arab", "AF", "AF" },
+ { "vai", "Vaii", "", "LR" },
+ { "yue", "Hans", "CN", "CN" },
+ { "yue", "Hant", "HK", "HK" },
+ { "zh", "Hans", "CN", "CN,SG" },
+ { "zh", "Hant", "TW", "HK,MO,TW" },
+ { nullptr, nullptr, nullptr, nullptr }
+};
+
+// Additional mapping for outdated, temporary or exceptionally reserved country codes.
+// Reference:
+// - https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+// - https://www.iso.org/obp/ui/#search/code/
+
+static const char *country_renames[][2] = {
+ { "BU", "MM" }, // Burma, name changed to Myanmar.
+ { "KV", "XK" }, // Kosovo (temporary FIPS code to European Commission code), no official ISO code assigned.
+ { "TP", "TL" }, // East Timor, name changed to Timor-Leste.
+ { "UK", "GB" }, // United Kingdom, exceptionally reserved code.
+ { nullptr, nullptr }
+};
+
+// Country code, country name.
+// Reference:
+// - https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
+// - https://www.iso.org/obp/ui/#search/code/
+
+static const char *country_names[][2] = {
+ { "AC", "Ascension Island" }, // Exceptionally reserved.
+ { "AD", "Andorra" },
+ { "AE", "United Arab Emirates" },
+ { "AF", "Afghanistan" },
+ { "AG", "Antigua and Barbuda" },
+ { "AI", "Anguilla" },
+ { "AL", "Albania" },
+ { "AM", "Armenia" },
+ { "AN", "Netherlands Antilles" }, // Transitionally reserved, divided into BQ, CW and SX.
+ { "AO", "Angola" },
+ { "AQ", "Antarctica" },
+ { "AR", "Argentina" },
+ { "AS", "American Samoa" },
+ { "AT", "Austria" },
+ { "AU", "Australia" },
+ { "AW", "Aruba" },
+ { "AX", "Åland Islands" },
+ { "AZ", "Azerbaijan" },
+ { "BA", "Bosnia and Herzegovina" },
+ { "BB", "Barbados" },
+ { "BD", "Bangladesh" },
+ { "BE", "Belgium" },
+ { "BF", "Burkina Faso" },
+ { "BG", "Bulgaria" },
+ { "BH", "Bahrain" },
+ { "BI", "Burundi" },
+ { "BJ", "Benin" },
+ { "BL", "St. Barthélemy" },
+ { "BM", "Bermuda" },
+ { "BN", "Brunei" },
+ { "BO", "Bolivia" },
+ { "BQ", "Caribbean Netherlands" },
+ { "BR", "Brazil" },
+ { "BS", "Bahamas" },
+ { "BT", "Bhutan" },
+ { "BV", "Bouvet Island" },
+ { "BW", "Botswana" },
+ { "BY", "Belarus" },
+ { "BZ", "Belize" },
+ { "CA", "Canada" },
+ { "CC", "Cocos (Keeling) Islands" },
+ { "CD", "Congo - Kinshasa" },
+ { "CF", "Central African Republic" },
+ { "CG", "Congo - Brazzaville" },
+ { "CH", "Switzerland" },
+ { "CI", "Côte d'Ivoire" },
+ { "CK", "Cook Islands" },
+ { "CL", "Chile" },
+ { "CM", "Cameroon" },
+ { "CN", "China" },
+ { "CO", "Colombia" },
+ { "CP", "Clipperton Island" }, // Exceptionally reserved.
+ { "CR", "Costa Rica" },
+ { "CQ", "Island of Sark" }, // Exceptionally reserved.
+ { "CU", "Cuba" },
+ { "CV", "Cabo Verde" },
+ { "CW", "Curaçao" },
+ { "CX", "Christmas Island" },
+ { "CY", "Cyprus" },
+ { "CZ", "Czechia" },
+ { "DE", "Germany" },
+ { "DG", "Diego Garcia" }, // Exceptionally reserved.
+ { "DJ", "Djibouti" },
+ { "DK", "Denmark" },
+ { "DM", "Dominica" },
+ { "DO", "Dominican Republic" },
+ { "DZ", "Algeria" },
+ { "EA", "Ceuta and Melilla" }, // Exceptionally reserved.
+ { "EC", "Ecuador" },
+ { "EE", "Estonia" },
+ { "EG", "Egypt" },
+ { "EH", "Western Sahara" },
+ { "ER", "Eritrea" },
+ { "ES", "Spain" },
+ { "ET", "Ethiopia" },
+ { "EU", "European Union" }, // Exceptionally reserved.
+ { "EZ", "Eurozone" }, // Exceptionally reserved.
+ { "FI", "Finland" },
+ { "FJ", "Fiji" },
+ { "FK", "Falkland Islands" },
+ { "FM", "Micronesia" },
+ { "FO", "Faroe Islands" },
+ { "FR", "France" },
+ { "FX", "France, Metropolitan" }, // Exceptionally reserved.
+ { "GA", "Gabon" },
+ { "GB", "United Kingdom" },
+ { "GD", "Grenada" },
+ { "GE", "Georgia" },
+ { "GF", "French Guiana" },
+ { "GG", "Guernsey" },
+ { "GH", "Ghana" },
+ { "GI", "Gibraltar" },
+ { "GL", "Greenland" },
+ { "GM", "Gambia" },
+ { "GN", "Guinea" },
+ { "GP", "Guadeloupe" },
+ { "GQ", "Equatorial Guinea" },
+ { "GR", "Greece" },
+ { "GS", "South Georgia and South Sandwich Islands" },
+ { "GT", "Guatemala" },
+ { "GU", "Guam" },
+ { "GW", "Guinea-Bissau" },
+ { "GY", "Guyana" },
+ { "HK", "Hong Kong" },
+ { "HM", "Heard Island and McDonald Islands" },
+ { "HN", "Honduras" },
+ { "HR", "Croatia" },
+ { "HT", "Haiti" },
+ { "HU", "Hungary" },
+ { "IC", "Canary Islands" }, // Exceptionally reserved.
+ { "ID", "Indonesia" },
+ { "IE", "Ireland" },
+ { "IL", "Israel" },
+ { "IM", "Isle of Man" },
+ { "IN", "India" },
+ { "IO", "British Indian Ocean Territory" },
+ { "IQ", "Iraq" },
+ { "IR", "Iran" },
+ { "IS", "Iceland" },
+ { "IT", "Italy" },
+ { "JE", "Jersey" },
+ { "JM", "Jamaica" },
+ { "JO", "Jordan" },
+ { "JP", "Japan" },
+ { "KE", "Kenya" },
+ { "KG", "Kyrgyzstan" },
+ { "KH", "Cambodia" },
+ { "KI", "Kiribati" },
+ { "KM", "Comoros" },
+ { "KN", "St. Kitts and Nevis" },
+ { "KP", "North Korea" },
+ { "KR", "South Korea" },
+ { "KW", "Kuwait" },
+ { "KY", "Cayman Islands" },
+ { "KZ", "Kazakhstan" },
+ { "LA", "Laos" },
+ { "LB", "Lebanon" },
+ { "LC", "St. Lucia" },
+ { "LI", "Liechtenstein" },
+ { "LK", "Sri Lanka" },
+ { "LR", "Liberia" },
+ { "LS", "Lesotho" },
+ { "LT", "Lithuania" },
+ { "LU", "Luxembourg" },
+ { "LV", "Latvia" },
+ { "LY", "Libya" },
+ { "MA", "Morocco" },
+ { "MC", "Monaco" },
+ { "MD", "Moldova" },
+ { "ME", "Montenegro" },
+ { "MF", "St. Martin" },
+ { "MG", "Madagascar" },
+ { "MH", "Marshall Islands" },
+ { "MK", "North Macedonia" },
+ { "ML", "Mali" },
+ { "MM", "Myanmar" },
+ { "MN", "Mongolia" },
+ { "MO", "Macao" },
+ { "MP", "Northern Mariana Islands" },
+ { "MQ", "Martinique" },
+ { "MR", "Mauritania" },
+ { "MS", "Montserrat" },
+ { "MT", "Malta" },
+ { "MU", "Mauritius" },
+ { "MV", "Maldives" },
+ { "MW", "Malawi" },
+ { "MX", "Mexico" },
+ { "MY", "Malaysia" },
+ { "MZ", "Mozambique" },
+ { "NA", "Namibia" },
+ { "NC", "New Caledonia" },
+ { "NE", "Niger" },
+ { "NF", "Norfolk Island" },
+ { "NG", "Nigeria" },
+ { "NI", "Nicaragua" },
+ { "NL", "Netherlands" },
+ { "NO", "Norway" },
+ { "NP", "Nepal" },
+ { "NR", "Nauru" },
+ { "NU", "Niue" },
+ { "NZ", "New Zealand" },
+ { "OM", "Oman" },
+ { "PA", "Panama" },
+ { "PE", "Peru" },
+ { "PF", "French Polynesia" },
+ { "PG", "Papua New Guinea" },
+ { "PH", "Philippines" },
+ { "PK", "Pakistan" },
+ { "PL", "Poland" },
+ { "PM", "St. Pierre and Miquelon" },
+ { "PN", "Pitcairn Islands" },
+ { "PR", "Puerto Rico" },
+ { "PS", "Palestine" },
+ { "PT", "Portugal" },
+ { "PW", "Palau" },
+ { "PY", "Paraguay" },
+ { "QA", "Qatar" },
+ { "RE", "Réunion" },
+ { "RO", "Romania" },
+ { "RS", "Serbia" },
+ { "RU", "Russia" },
+ { "RW", "Rwanda" },
+ { "SA", "Saudi Arabia" },
+ { "SB", "Solomon Islands" },
+ { "SC", "Seychelles" },
+ { "SD", "Sudan" },
+ { "SE", "Sweden" },
+ { "SG", "Singapore" },
+ { "SH", "St. Helena, Ascension and Tristan da Cunha" },
+ { "SI", "Slovenia" },
+ { "SJ", "Svalbard and Jan Mayen" },
+ { "SK", "Slovakia" },
+ { "SL", "Sierra Leone" },
+ { "SM", "San Marino" },
+ { "SN", "Senegal" },
+ { "SO", "Somalia" },
+ { "SR", "Suriname" },
+ { "SS", "South Sudan" },
+ { "ST", "Sao Tome and Principe" },
+ { "SV", "El Salvador" },
+ { "SX", "Sint Maarten" },
+ { "SY", "Syria" },
+ { "SZ", "Eswatini" },
+ { "TA", "Tristan da Cunha" }, // Exceptionally reserved.
+ { "TC", "Turks and Caicos Islands" },
+ { "TD", "Chad" },
+ { "TF", "French Southern Territories" },
+ { "TG", "Togo" },
+ { "TH", "Thailand" },
+ { "TJ", "Tajikistan" },
+ { "TK", "Tokelau" },
+ { "TL", "Timor-Leste" },
+ { "TM", "Turkmenistan" },
+ { "TN", "Tunisia" },
+ { "TO", "Tonga" },
+ { "TR", "Turkey" },
+ { "TT", "Trinidad and Tobago" },
+ { "TV", "Tuvalu" },
+ { "TW", "Taiwan" },
+ { "TZ", "Tanzania" },
+ { "UA", "Ukraine" },
+ { "UG", "Uganda" },
+ { "UM", "U.S. Outlying Islands" },
+ { "US", "United States of America" },
+ { "UY", "Uruguay" },
+ { "UZ", "Uzbekistan" },
+ { "VA", "Holy See" },
+ { "VC", "St. Vincent and the Grenadines" },
+ { "VE", "Venezuela" },
+ { "VG", "British Virgin Islands" },
+ { "VI", "U.S. Virgin Islands" },
+ { "VN", "Viet Nam" },
+ { "VU", "Vanuatu" },
+ { "WF", "Wallis and Futuna" },
+ { "WS", "Samoa" },
+ { "XK", "Kosovo" }, // Temporary code, no official ISO code assigned.
+ { "YE", "Yemen" },
+ { "YT", "Mayotte" },
+ { "ZA", "South Africa" },
+ { "ZM", "Zambia" },
+ { "ZW", "Zimbabwe" },
+ { nullptr, nullptr }
+};
+
+// Languages code, language name.
+// Reference:
+// - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+// - https://www.localeplanet.com/icu/index.html
+// - https://lh.2xlibre.net/locales/
+
+static const char *language_list[][2] = {
+ { "aa", "Afar" },
+ { "ab", "Abkhazian" },
+ { "ace", "Achinese" },
+ { "ach", "Acoli" },
+ { "ada", "Adangme" },
+ { "ady", "Adyghe" },
+ { "ae", "Avestan" },
+ { "aeb", "Tunisian Arabic" },
+ { "af", "Afrikaans" },
+ { "afh", "Afrihili" },
+ { "agq", "Aghem" },
+ { "ain", "Ainu" },
+ { "agr", "Aguaruna" },
+ { "ak", "Akan" },
+ { "akk", "Akkadian" },
+ { "akz", "Alabama" },
+ { "ale", "Aleut" },
+ { "aln", "Gheg Albanian" },
+ { "alt", "Southern Altai" },
+ { "am", "Amharic" },
+ { "an", "Aragonese" },
+ { "ang", "Old English" },
+ { "anp", "Angika" },
+ { "ar", "Arabic" },
+ { "arc", "Aramaic" },
+ { "arn", "Mapudungun" },
+ { "aro", "Araona" },
+ { "arp", "Arapaho" },
+ { "arq", "Algerian Arabic" },
+ { "ars", "Najdi Arabic" },
+ { "arw", "Arawak" },
+ { "ary", "Moroccan Arabic" },
+ { "arz", "Egyptian Arabic" },
+ { "as", "Assamese" },
+ { "asa", "Asu" },
+ { "ase", "American Sign Language" },
+ { "ast", "Asturian" },
+ { "av", "Avaric" },
+ { "avk", "Kotava" },
+ { "awa", "Awadhi" },
+ { "ayc", "Southern Aymara" },
+ { "ay", "Aymara" },
+ { "az", "Azerbaijani" },
+ { "ba", "Bashkir" },
+ { "bal", "Baluchi" },
+ { "ban", "Balinese" },
+ { "bar", "Bavarian" },
+ { "bas", "Bassa" },
+ { "bax", "Bamun" },
+ { "bbc", "Batak Toba" },
+ { "bbj", "Ghomala" },
+ { "be", "Belarusian" },
+ { "bej", "Beja" },
+ { "bem", "Bemba" },
+ { "ber", "Berber" },
+ { "bew", "Betawi" },
+ { "bez", "Bena" },
+ { "bfd", "Bafut" },
+ { "bfq", "Badaga" },
+ { "bg", "Bulgarian" },
+ { "bhb", "Bhili" },
+ { "bgn", "Western Balochi" },
+ { "bho", "Bhojpuri" },
+ { "bi", "Bislama" },
+ { "bik", "Bikol" },
+ { "bin", "Bini" },
+ { "bjn", "Banjar" },
+ { "bkm", "Kom" },
+ { "bla", "Siksika" },
+ { "bm", "Bambara" },
+ { "bn", "Bengali" },
+ { "bo", "Tibetan" },
+ { "bpy", "Bishnupriya" },
+ { "bqi", "Bakhtiari" },
+ { "br", "Breton" },
+ { "brh", "Brahui" },
+ { "brx", "Bodo" },
+ { "bs", "Bosnian" },
+ { "bss", "Akoose" },
+ { "bua", "Buriat" },
+ { "bug", "Buginese" },
+ { "bum", "Bulu" },
+ { "byn", "Bilin" },
+ { "byv", "Medumba" },
+ { "ca", "Catalan" },
+ { "cad", "Caddo" },
+ { "car", "Carib" },
+ { "cay", "Cayuga" },
+ { "cch", "Atsam" },
+ { "ccp", "Chakma" },
+ { "ce", "Chechen" },
+ { "ceb", "Cebuano" },
+ { "cgg", "Chiga" },
+ { "ch", "Chamorro" },
+ { "chb", "Chibcha" },
+ { "chg", "Chagatai" },
+ { "chk", "Chuukese" },
+ { "chm", "Mari" },
+ { "chn", "Chinook Jargon" },
+ { "cho", "Choctaw" },
+ { "chp", "Chipewyan" },
+ { "chr", "Cherokee" },
+ { "chy", "Cheyenne" },
+ { "cic", "Chickasaw" },
+ { "ckb", "Central Kurdish" },
+ { "csb", "Kashubian" },
+ { "cmn", "Mandarin Chinese" },
+ { "co", "Corsican" },
+ { "cop", "Coptic" },
+ { "cps", "Capiznon" },
+ { "cr", "Cree" },
+ { "crh", "Crimean Tatar" },
+ { "crs", "Seselwa Creole French" },
+ { "cs", "Czech" },
+ { "csb", "Kashubian" },
+ { "cu", "Church Slavic" },
+ { "cv", "Chuvash" },
+ { "cy", "Welsh" },
+ { "da", "Danish" },
+ { "dak", "Dakota" },
+ { "dar", "Dargwa" },
+ { "dav", "Taita" },
+ { "de", "German" },
+ { "del", "Delaware" },
+ { "den", "Slave" },
+ { "dgr", "Dogrib" },
+ { "din", "Dinka" },
+ { "dje", "Zarma" },
+ { "doi", "Dogri" },
+ { "dsb", "Lower Sorbian" },
+ { "dtp", "Central Dusun" },
+ { "dua", "Duala" },
+ { "dum", "Middle Dutch" },
+ { "dv", "Dhivehi" },
+ { "dyo", "Jola-Fonyi" },
+ { "dyu", "Dyula" },
+ { "dz", "Dzongkha" },
+ { "dzg", "Dazaga" },
+ { "ebu", "Embu" },
+ { "ee", "Ewe" },
+ { "efi", "Efik" },
+ { "egl", "Emilian" },
+ { "egy", "Ancient Egyptian" },
+ { "eka", "Ekajuk" },
+ { "el", "Greek" },
+ { "elx", "Elamite" },
+ { "en", "English" },
+ { "enm", "Middle English" },
+ { "eo", "Esperanto" },
+ { "es", "Spanish" },
+ { "esu", "Central Yupik" },
+ { "et", "Estonian" },
+ { "eu", "Basque" },
+ { "ewo", "Ewondo" },
+ { "ext", "Extremaduran" },
+ { "fa", "Persian" },
+ { "fan", "Fang" },
+ { "fat", "Fanti" },
+ { "ff", "Fulah" },
+ { "fi", "Finnish" },
+ { "fil", "Filipino" },
+ { "fit", "Tornedalen Finnish" },
+ { "fj", "Fijian" },
+ { "fo", "Faroese" },
+ { "fon", "Fon" },
+ { "fr", "French" },
+ { "frc", "Cajun French" },
+ { "frm", "Middle French" },
+ { "fro", "Old French" },
+ { "frp", "Arpitan" },
+ { "frr", "Northern Frisian" },
+ { "frs", "Eastern Frisian" },
+ { "fur", "Friulian" },
+ { "fy", "Western Frisian" },
+ { "ga", "Irish" },
+ { "gaa", "Ga" },
+ { "gag", "Gagauz" },
+ { "gan", "Gan Chinese" },
+ { "gay", "Gayo" },
+ { "gba", "Gbaya" },
+ { "gbz", "Zoroastrian Dari" },
+ { "gd", "Scottish Gaelic" },
+ { "gez", "Geez" },
+ { "gil", "Gilbertese" },
+ { "gl", "Galician" },
+ { "glk", "Gilaki" },
+ { "gmh", "Middle High German" },
+ { "gn", "Guarani" },
+ { "goh", "Old High German" },
+ { "gom", "Goan Konkani" },
+ { "gon", "Gondi" },
+ { "gor", "Gorontalo" },
+ { "got", "Gothic" },
+ { "grb", "Grebo" },
+ { "grc", "Ancient Greek" },
+ { "gsw", "Swiss German" },
+ { "gu", "Gujarati" },
+ { "guc", "Wayuu" },
+ { "gur", "Frafra" },
+ { "guz", "Gusii" },
+ { "gv", "Manx" },
+ { "gwi", "Gwichʼin" },
+ { "ha", "Hausa" },
+ { "hai", "Haida" },
+ { "hak", "Hakka Chinese" },
+ { "haw", "Hawaiian" },
+ { "he", "Hebrew" },
+ { "hi", "Hindi" },
+ { "hif", "Fiji Hindi" },
+ { "hil", "Hiligaynon" },
+ { "hit", "Hittite" },
+ { "hmn", "Hmong" },
+ { "ho", "Hiri Motu" },
+ { "hne", "Chhattisgarhi" },
+ { "hr", "Croatian" },
+ { "hsb", "Upper Sorbian" },
+ { "hsn", "Xiang Chinese" },
+ { "ht", "Haitian" },
+ { "hu", "Hungarian" },
+ { "hup", "Hupa" },
+ { "hus", "Huastec" },
+ { "hy", "Armenian" },
+ { "hz", "Herero" },
+ { "ia", "Interlingua" },
+ { "iba", "Iban" },
+ { "ibb", "Ibibio" },
+ { "id", "Indonesian" },
+ { "ie", "Interlingue" },
+ { "ig", "Igbo" },
+ { "ii", "Sichuan Yi" },
+ { "ik", "Inupiaq" },
+ { "ilo", "Iloko" },
+ { "inh", "Ingush" },
+ { "io", "Ido" },
+ { "is", "Icelandic" },
+ { "it", "Italian" },
+ { "iu", "Inuktitut" },
+ { "izh", "Ingrian" },
+ { "ja", "Japanese" },
+ { "jam", "Jamaican Creole English" },
+ { "jbo", "Lojban" },
+ { "jgo", "Ngomba" },
+ { "jmc", "Machame" },
+ { "jpr", "Judeo-Persian" },
+ { "jrb", "Judeo-Arabic" },
+ { "jut", "Jutish" },
+ { "jv", "Javanese" },
+ { "ka", "Georgian" },
+ { "kaa", "Kara-Kalpak" },
+ { "kab", "Kabyle" },
+ { "kac", "Kachin" },
+ { "kaj", "Jju" },
+ { "kam", "Kamba" },
+ { "kaw", "Kawi" },
+ { "kbd", "Kabardian" },
+ { "kbl", "Kanembu" },
+ { "kcg", "Tyap" },
+ { "kde", "Makonde" },
+ { "kea", "Kabuverdianu" },
+ { "ken", "Kenyang" },
+ { "kfo", "Koro" },
+ { "kg", "Kongo" },
+ { "kgp", "Kaingang" },
+ { "kha", "Khasi" },
+ { "kho", "Khotanese" },
+ { "khq", "Koyra Chiini" },
+ { "khw", "Khowar" },
+ { "ki", "Kikuyu" },
+ { "kiu", "Kirmanjki" },
+ { "kj", "Kuanyama" },
+ { "kk", "Kazakh" },
+ { "kkj", "Kako" },
+ { "kl", "Kalaallisut" },
+ { "kln", "Kalenjin" },
+ { "km", "Central Khmer" },
+ { "kmb", "Kimbundu" },
+ { "kn", "Kannada" },
+ { "ko", "Korean" },
+ { "koi", "Komi-Permyak" },
+ { "kok", "Konkani" },
+ { "kos", "Kosraean" },
+ { "kpe", "Kpelle" },
+ { "kr", "Kanuri" },
+ { "krc", "Karachay-Balkar" },
+ { "kri", "Krio" },
+ { "krj", "Kinaray-a" },
+ { "krl", "Karelian" },
+ { "kru", "Kurukh" },
+ { "ks", "Kashmiri" },
+ { "ksb", "Shambala" },
+ { "ksf", "Bafia" },
+ { "ksh", "Colognian" },
+ { "ku", "Kurdish" },
+ { "kum", "Kumyk" },
+ { "kut", "Kutenai" },
+ { "kv", "Komi" },
+ { "kw", "Cornish" },
+ { "ky", "Kirghiz" },
+ { "lag", "Langi" },
+ { "la", "Latin" },
+ { "lad", "Ladino" },
+ { "lag", "Langi" },
+ { "lah", "Lahnda" },
+ { "lam", "Lamba" },
+ { "lb", "Luxembourgish" },
+ { "lez", "Lezghian" },
+ { "lfn", "Lingua Franca Nova" },
+ { "lg", "Ganda" },
+ { "li", "Limburgan" },
+ { "lij", "Ligurian" },
+ { "liv", "Livonian" },
+ { "lkt", "Lakota" },
+ { "lmo", "Lombard" },
+ { "ln", "Lingala" },
+ { "lo", "Lao" },
+ { "lol", "Mongo" },
+ { "lou", "Louisiana Creole" },
+ { "loz", "Lozi" },
+ { "lrc", "Northern Luri" },
+ { "lt", "Lithuanian" },
+ { "ltg", "Latgalian" },
+ { "lu", "Luba-Katanga" },
+ { "lua", "Luba-Lulua" },
+ { "lui", "Luiseno" },
+ { "lun", "Lunda" },
+ { "luo", "Luo" },
+ { "lus", "Mizo" },
+ { "luy", "Luyia" },
+ { "lv", "Latvian" },
+ { "lzh", "Literary Chinese" },
+ { "lzz", "Laz" },
+ { "mad", "Madurese" },
+ { "maf", "Mafa" },
+ { "mag", "Magahi" },
+ { "mai", "Maithili" },
+ { "mak", "Makasar" },
+ { "man", "Mandingo" },
+ { "mas", "Masai" },
+ { "mde", "Maba" },
+ { "mdf", "Moksha" },
+ { "mdr", "Mandar" },
+ { "men", "Mende" },
+ { "mer", "Meru" },
+ { "mfe", "Morisyen" },
+ { "mg", "Malagasy" },
+ { "mga", "Middle Irish" },
+ { "mgh", "Makhuwa-Meetto" },
+ { "mgo", "Metaʼ" },
+ { "mh", "Marshallese" },
+ { "mhr", "Eastern Mari" },
+ { "mi", "Māori" },
+ { "mic", "Mi'kmaq" },
+ { "min", "Minangkabau" },
+ { "miq", "Mískito" },
+ { "mjw", "Karbi" },
+ { "mk", "Macedonian" },
+ { "ml", "Malayalam" },
+ { "mn", "Mongolian" },
+ { "mnc", "Manchu" },
+ { "mni", "Manipuri" },
+ { "mnw", "Mon" },
+ { "mos", "Mossi" },
+ { "moh", "Mohawk" },
+ { "mr", "Marathi" },
+ { "mrj", "Western Mari" },
+ { "ms", "Malay" },
+ { "mt", "Maltese" },
+ { "mua", "Mundang" },
+ { "mus", "Muscogee" },
+ { "mwl", "Mirandese" },
+ { "mwr", "Marwari" },
+ { "mwv", "Mentawai" },
+ { "my", "Burmese" },
+ { "mye", "Myene" },
+ { "myv", "Erzya" },
+ { "mzn", "Mazanderani" },
+ { "na", "Nauru" },
+ { "nah", "Nahuatl" },
+ { "nan", "Min Nan Chinese" },
+ { "nap", "Neapolitan" },
+ { "naq", "Nama" },
+ { "nan", "Min Nan Chinese" },
+ { "nb", "Norwegian Bokmål" },
+ { "nd", "North Ndebele" },
+ { "nds", "Low German" },
+ { "ne", "Nepali" },
+ { "new", "Newari" },
+ { "nhn", "Central Nahuatl" },
+ { "ng", "Ndonga" },
+ { "nia", "Nias" },
+ { "niu", "Niuean" },
+ { "njo", "Ao Naga" },
+ { "nl", "Dutch" },
+ { "nmg", "Kwasio" },
+ { "nn", "Norwegian Nynorsk" },
+ { "nnh", "Ngiemboon" },
+ { "nog", "Nogai" },
+ { "non", "Old Norse" },
+ { "nov", "Novial" },
+ { "nqo", "N'ko" },
+ { "nr", "South Ndebele" },
+ { "nso", "Pedi" },
+ { "nus", "Nuer" },
+ { "nv", "Navajo" },
+ { "nwc", "Classical Newari" },
+ { "ny", "Nyanja" },
+ { "nym", "Nyamwezi" },
+ { "nyn", "Nyankole" },
+ { "nyo", "Nyoro" },
+ { "nzi", "Nzima" },
+ { "oc", "Occitan" },
+ { "oj", "Ojibwa" },
+ { "om", "Oromo" },
+ { "or", "Odia" },
+ { "os", "Ossetic" },
+ { "osa", "Osage" },
+ { "ota", "Ottoman Turkish" },
+ { "pa", "Panjabi" },
+ { "pag", "Pangasinan" },
+ { "pal", "Pahlavi" },
+ { "pam", "Pampanga" },
+ { "pap", "Papiamento" },
+ { "pau", "Palauan" },
+ { "pcd", "Picard" },
+ { "pcm", "Nigerian Pidgin" },
+ { "pdc", "Pennsylvania German" },
+ { "pdt", "Plautdietsch" },
+ { "peo", "Old Persian" },
+ { "pfl", "Palatine German" },
+ { "phn", "Phoenician" },
+ { "pi", "Pali" },
+ { "pl", "Polish" },
+ { "pms", "Piedmontese" },
+ { "pnt", "Pontic" },
+ { "pon", "Pohnpeian" },
+ { "pr", "Pirate" },
+ { "prg", "Prussian" },
+ { "pro", "Old Provençal" },
+ { "prs", "Dari" },
+ { "ps", "Pushto" },
+ { "pt", "Portuguese" },
+ { "qu", "Quechua" },
+ { "quc", "K'iche" },
+ { "qug", "Chimborazo Highland Quichua" },
+ { "quy", "Ayacucho Quechua" },
+ { "quz", "Cusco Quechua" },
+ { "raj", "Rajasthani" },
+ { "rap", "Rapanui" },
+ { "rar", "Rarotongan" },
+ { "rgn", "Romagnol" },
+ { "rif", "Riffian" },
+ { "rm", "Romansh" },
+ { "rn", "Rundi" },
+ { "ro", "Romanian" },
+ { "rof", "Rombo" },
+ { "rom", "Romany" },
+ { "rtm", "Rotuman" },
+ { "ru", "Russian" },
+ { "rue", "Rusyn" },
+ { "rug", "Roviana" },
+ { "rup", "Aromanian" },
+ { "rw", "Kinyarwanda" },
+ { "rwk", "Rwa" },
+ { "sa", "Sanskrit" },
+ { "sad", "Sandawe" },
+ { "sah", "Sakha" },
+ { "sam", "Samaritan Aramaic" },
+ { "saq", "Samburu" },
+ { "sas", "Sasak" },
+ { "sat", "Santali" },
+ { "saz", "Saurashtra" },
+ { "sba", "Ngambay" },
+ { "sbp", "Sangu" },
+ { "sc", "Sardinian" },
+ { "scn", "Sicilian" },
+ { "sco", "Scots" },
+ { "sd", "Sindhi" },
+ { "sdc", "Sassarese Sardinian" },
+ { "sdh", "Southern Kurdish" },
+ { "se", "Northern Sami" },
+ { "see", "Seneca" },
+ { "seh", "Sena" },
+ { "sei", "Seri" },
+ { "sel", "Selkup" },
+ { "ses", "Koyraboro Senni" },
+ { "sg", "Sango" },
+ { "sga", "Old Irish" },
+ { "sgs", "Samogitian" },
+ { "sh", "Serbo-Croatian" },
+ { "shi", "Tachelhit" },
+ { "shn", "Shan" },
+ { "shs", "Shuswap" },
+ { "shu", "Chadian Arabic" },
+ { "si", "Sinhala" },
+ { "sid", "Sidamo" },
+ { "sk", "Slovak" },
+ { "sl", "Slovenian" },
+ { "sli", "Lower Silesian" },
+ { "sly", "Selayar" },
+ { "sm", "Samoan" },
+ { "sma", "Southern Sami" },
+ { "smj", "Lule Sami" },
+ { "smn", "Inari Sami" },
+ { "sms", "Skolt Sami" },
+ { "sn", "Shona" },
+ { "snk", "Soninke" },
+ { "so", "Somali" },
+ { "sog", "Sogdien" },
+ { "son", "Songhai" },
+ { "sq", "Albanian" },
+ { "sr", "Serbian" },
+ { "srn", "Sranan Tongo" },
+ { "srr", "Serer" },
+ { "ss", "Swati" },
+ { "ssy", "Saho" },
+ { "st", "Southern Sotho" },
+ { "stq", "Saterland Frisian" },
+ { "su", "Sundanese" },
+ { "suk", "Sukuma" },
+ { "sus", "Susu" },
+ { "sux", "Sumerian" },
+ { "sv", "Swedish" },
+ { "sw", "Swahili" },
+ { "swb", "Comorian" },
+ { "swc", "Congo Swahili" },
+ { "syc", "Classical Syriac" },
+ { "syr", "Syriac" },
+ { "szl", "Silesian" },
+ { "ta", "Tamil" },
+ { "tcy", "Tulu" },
+ { "te", "Telugu" },
+ { "tem", "Timne" },
+ { "teo", "Teso" },
+ { "ter", "Tereno" },
+ { "tet", "Tetum" },
+ { "tg", "Tajik" },
+ { "th", "Thai" },
+ { "the", "Chitwania Tharu" },
+ { "ti", "Tigrinya" },
+ { "tig", "Tigre" },
+ { "tiv", "Tiv" },
+ { "tk", "Turkmen" },
+ { "tkl", "Tokelau" },
+ { "tkr", "Tsakhur" },
+ { "tl", "Tagalog" },
+ { "tlh", "Klingon" },
+ { "tli", "Tlingit" },
+ { "tly", "Talysh" },
+ { "tmh", "Tamashek" },
+ { "tn", "Tswana" },
+ { "to", "Tongan" },
+ { "tog", "Nyasa Tonga" },
+ { "tpi", "Tok Pisin" },
+ { "tr", "Turkish" },
+ { "tru", "Turoyo" },
+ { "trv", "Taroko" },
+ { "ts", "Tsonga" },
+ { "tsd", "Tsakonian" },
+ { "tsi", "Tsimshian" },
+ { "tt", "Tatar" },
+ { "ttt", "Muslim Tat" },
+ { "tum", "Tumbuka" },
+ { "tvl", "Tuvalu" },
+ { "tw", "Twi" },
+ { "twq", "Tasawaq" },
+ { "ty", "Tahitian" },
+ { "tyv", "Tuvinian" },
+ { "tzm", "Central Atlas Tamazight" },
+ { "udm", "Udmurt" },
+ { "ug", "Uyghur" },
+ { "uga", "Ugaritic" },
+ { "uk", "Ukrainian" },
+ { "umb", "Umbundu" },
+ { "unm", "Unami" },
+ { "ur", "Urdu" },
+ { "uz", "Uzbek" },
+ { "vai", "Vai" },
+ { "ve", "Venda" },
+ { "vec", "Venetian" },
+ { "vep", "Veps" },
+ { "vi", "Vietnamese" },
+ { "vls", "West Flemish" },
+ { "vmf", "Main-Franconian" },
+ { "vo", "Volapük" },
+ { "vot", "Votic" },
+ { "vro", "Võro" },
+ { "vun", "Vunjo" },
+ { "wa", "Walloon" },
+ { "wae", "Walser" },
+ { "wal", "Wolaytta" },
+ { "war", "Waray" },
+ { "was", "Washo" },
+ { "wbp", "Warlpiri" },
+ { "wo", "Wolof" },
+ { "wuu", "Wu Chinese" },
+ { "xal", "Kalmyk" },
+ { "xh", "Xhosa" },
+ { "xmf", "Mingrelian" },
+ { "xog", "Soga" },
+ { "yao", "Yao" },
+ { "yap", "Yapese" },
+ { "yav", "Yangben" },
+ { "ybb", "Yemba" },
+ { "yi", "Yiddish" },
+ { "yo", "Yoruba" },
+ { "yrl", "Nheengatu" },
+ { "yue", "Yue Chinese" },
+ { "yuw", "Papua New Guinea" },
+ { "za", "Zhuang" },
+ { "zap", "Zapotec" },
+ { "zbl", "Blissymbols" },
+ { "zea", "Zeelandic" },
+ { "zen", "Zenaga" },
+ { "zgh", "Standard Moroccan Tamazight" },
+ { "zh", "Chinese" },
+ { "zu", "Zulu" },
+ { "zun", "Zuni" },
+ { "zza", "Zaza" },
+ { nullptr, nullptr }
+};
+
+// Additional regional variants.
+// Variant name, supported languages.
+
+static const char *locale_variants[][2] = {
+ { "valencia", "ca" },
+ { "iqtelif", "tt" },
+ { "saaho", "aa" },
+ { "tradnl", "es" },
+ { nullptr, nullptr },
+};
+
+// Script names and codes (excludes typographic variants, special codes, reserved codes and aliases for combined scripts).
+// Reference:
+// - https://en.wikipedia.org/wiki/ISO_15924
+
+static const char *script_list[][2] = {
+ { "Adlam", "Adlm" },
+ { "Afaka", "Afak" },
+ { "Caucasian Albanian", "Aghb" },
+ { "Ahom", "Ahom" },
+ { "Arabic", "Arab" },
+ { "Imperial Aramaic", "Armi" },
+ { "Armenian", "Armn" },
+ { "Avestan", "Avst" },
+ { "Balinese", "Bali" },
+ { "Bamum", "Bamu" },
+ { "Bassa Vah", "Bass" },
+ { "Batak", "Batk" },
+ { "Bengali", "Beng" },
+ { "Bhaiksuki", "Bhks" },
+ { "Blissymbols", "Blis" },
+ { "Bopomofo", "Bopo" },
+ { "Brahmi", "Brah" },
+ { "Braille", "Brai" },
+ { "Buginese", "Bugi" },
+ { "Buhid", "Buhd" },
+ { "Chakma", "Cakm" },
+ { "Unified Canadian Aboriginal", "Cans" },
+ { "Carian", "Cari" },
+ { "Cham", "Cham" },
+ { "Cherokee", "Cher" },
+ { "Chorasmian", "Chrs" },
+ { "Cirth", "Cirt" },
+ { "Coptic", "Copt" },
+ { "Cypro-Minoan", "Cpmn" },
+ { "Cypriot", "Cprt" },
+ { "Cyrillic", "Cyrl" },
+ { "Devanagari", "Deva" },
+ { "Dives Akuru", "Diak" },
+ { "Dogra", "Dogr" },
+ { "Deseret", "Dsrt" },
+ { "Duployan", "Dupl" },
+ { "Egyptian demotic", "Egyd" },
+ { "Egyptian hieratic", "Egyh" },
+ { "Egyptian hieroglyphs", "Egyp" },
+ { "Elbasan", "Elba" },
+ { "Elymaic", "Elym" },
+ { "Ethiopic", "Ethi" },
+ { "Khutsuri", "Geok" },
+ { "Georgian", "Geor" },
+ { "Glagolitic", "Glag" },
+ { "Gunjala Gondi", "Gong" },
+ { "Masaram Gondi", "Gonm" },
+ { "Gothic", "Goth" },
+ { "Grantha", "Gran" },
+ { "Greek", "Grek" },
+ { "Gujarati", "Gujr" },
+ { "Gurmukhi", "Guru" },
+ { "Hangul", "Hang" },
+ { "Han", "Hani" },
+ { "Hanunoo", "Hano" },
+ { "Simplified", "Hans" },
+ { "Traditional", "Hant" },
+ { "Hatran", "Hatr" },
+ { "Hebrew", "Hebr" },
+ { "Hiragana", "Hira" },
+ { "Anatolian Hieroglyphs", "Hluw" },
+ { "Pahawh Hmong", "Hmng" },
+ { "Nyiakeng Puachue Hmong", "Hmnp" },
+ { "Old Hungarian", "Hung" },
+ { "Indus", "Inds" },
+ { "Old Italic", "Ital" },
+ { "Javanese", "Java" },
+ { "Jurchen", "Jurc" },
+ { "Kayah Li", "Kali" },
+ { "Katakana", "Kana" },
+ { "Kharoshthi", "Khar" },
+ { "Khmer", "Khmr" },
+ { "Khojki", "Khoj" },
+ { "Khitan large script", "Kitl" },
+ { "Khitan small script", "Kits" },
+ { "Kannada", "Knda" },
+ { "Kpelle", "Kpel" },
+ { "Kaithi", "Kthi" },
+ { "Tai Tham", "Lana" },
+ { "Lao", "Laoo" },
+ { "Latin", "Latn" },
+ { "Leke", "Leke" },
+ { "Lepcha", "Lepc" },
+ { "Limbu", "Limb" },
+ { "Linear A", "Lina" },
+ { "Linear B", "Linb" },
+ { "Lisu", "Lisu" },
+ { "Loma", "Loma" },
+ { "Lycian", "Lyci" },
+ { "Lydian", "Lydi" },
+ { "Mahajani", "Mahj" },
+ { "Makasar", "Maka" },
+ { "Mandaic", "Mand" },
+ { "Manichaean", "Mani" },
+ { "Marchen", "Marc" },
+ { "Mayan Hieroglyphs", "Maya" },
+ { "Medefaidrin", "Medf" },
+ { "Mende Kikakui", "Mend" },
+ { "Meroitic Cursive", "Merc" },
+ { "Meroitic Hieroglyphs", "Mero" },
+ { "Malayalam", "Mlym" },
+ { "Modi", "Modi" },
+ { "Mongolian", "Mong" },
+ { "Moon", "Moon" },
+ { "Mro", "Mroo" },
+ { "Meitei Mayek", "Mtei" },
+ { "Multani", "Mult" },
+ { "Myanmar (Burmese)", "Mymr" },
+ { "Nandinagari", "Nand" },
+ { "Old North Arabian", "Narb" },
+ { "Nabataean", "Nbat" },
+ { "Newa", "Newa" },
+ { "Naxi Dongba", "Nkdb" },
+ { "Nakhi Geba", "Nkgb" },
+ { "N'ko", "Nkoo" },
+ { "Nüshu", "Nshu" },
+ { "Ogham", "Ogam" },
+ { "Ol Chiki", "Olck" },
+ { "Old Turkic", "Orkh" },
+ { "Oriya", "Orya" },
+ { "Osage", "Osge" },
+ { "Osmanya", "Osma" },
+ { "Old Uyghur", "Ougr" },
+ { "Palmyrene", "Palm" },
+ { "Pau Cin Hau", "Pauc" },
+ { "Proto-Cuneiform", "Pcun" },
+ { "Proto-Elamite", "Pelm" },
+ { "Old Permic", "Perm" },
+ { "Phags-pa", "Phag" },
+ { "Inscriptional Pahlavi", "Phli" },
+ { "Psalter Pahlavi", "Phlp" },
+ { "Book Pahlavi", "Phlv" },
+ { "Phoenician", "Phnx" },
+ { "Klingon", "Piqd" },
+ { "Miao", "Plrd" },
+ { "Inscriptional Parthian", "Prti" },
+ { "Proto-Sinaitic", "Psin" },
+ { "Ranjana", "Ranj" },
+ { "Rejang", "Rjng" },
+ { "Hanifi Rohingya", "Rohg" },
+ { "Rongorongo", "Roro" },
+ { "Runic", "Runr" },
+ { "Samaritan", "Samr" },
+ { "Sarati", "Sara" },
+ { "Old South Arabian", "Sarb" },
+ { "Saurashtra", "Saur" },
+ { "SignWriting", "Sgnw" },
+ { "Shavian", "Shaw" },
+ { "Sharada", "Shrd" },
+ { "Shuishu", "Shui" },
+ { "Siddham", "Sidd" },
+ { "Khudawadi", "Sind" },
+ { "Sinhala", "Sinh" },
+ { "Sogdian", "Sogd" },
+ { "Old Sogdian", "Sogo" },
+ { "Sora Sompeng", "Sora" },
+ { "Soyombo", "Soyo" },
+ { "Sundanese", "Sund" },
+ { "Syloti Nagri", "Sylo" },
+ { "Syriac", "Syrc" },
+ { "Tagbanwa", "Tagb" },
+ { "Takri", "Takr" },
+ { "Tai Le", "Tale" },
+ { "New Tai Lue", "Talu" },
+ { "Tamil", "Taml" },
+ { "Tangut", "Tang" },
+ { "Tai Viet", "Tavt" },
+ { "Telugu", "Telu" },
+ { "Tengwar", "Teng" },
+ { "Tifinagh", "Tfng" },
+ { "Tagalog", "Tglg" },
+ { "Thaana", "Thaa" },
+ { "Thai", "Thai" },
+ { "Tibetan", "Tibt" },
+ { "Tirhuta", "Tirh" },
+ { "Tangsa", "Tnsa" },
+ { "Toto", "Toto" },
+ { "Ugaritic", "Ugar" },
+ { "Vai", "Vaii" },
+ { "Visible Speech", "Visp" },
+ { "Vithkuqi", "Vith" },
+ { "Warang Citi", "Wara" },
+ { "Wancho", "Wcho" },
+ { "Woleai", "Wole" },
+ { "Old Persian", "Xpeo" },
+ { "Cuneiform", "Xsux" },
+ { "Yezidi", "Yezi" },
+ { "Yi", "Yiii" },
+ { "Zanabazar Square", "Zanb" },
+ { nullptr, nullptr }
+};
+
+#endif // LOCALES_H
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 615ecf2432..355ee238e8 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -33,793 +33,12 @@
#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
#include "core/os/os.h"
+#include "core/string/locales.h"
#ifdef TOOLS_ENABLED
#include "main/main.h"
#endif
-// ISO 639-1 language codes (and a couple of three-letter ISO 639-2 codes),
-// with the addition of glibc locales with their regional identifiers.
-// This list must match the language names (in English) of locale_names.
-//
-// References:
-// - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
-// - https://lh.2xlibre.net/locales/
-// - https://iso639-3.sil.org/
-
-static const char *locale_list[] = {
- "aa", // Afar
- "aa_DJ", // Afar (Djibouti)
- "aa_ER", // Afar (Eritrea)
- "aa_ET", // Afar (Ethiopia)
- "af", // Afrikaans
- "af_ZA", // Afrikaans (South Africa)
- "agr_PE", // Aguaruna (Peru)
- "ak_GH", // Akan (Ghana)
- "am_ET", // Amharic (Ethiopia)
- "an_ES", // Aragonese (Spain)
- "anp_IN", // Angika (India)
- "ar", // Arabic
- "ar_AE", // Arabic (United Arab Emirates)
- "ar_BH", // Arabic (Bahrain)
- "ar_DZ", // Arabic (Algeria)
- "ar_EG", // Arabic (Egypt)
- "ar_IN", // Arabic (India)
- "ar_IQ", // Arabic (Iraq)
- "ar_JO", // Arabic (Jordan)
- "ar_KW", // Arabic (Kuwait)
- "ar_LB", // Arabic (Lebanon)
- "ar_LY", // Arabic (Libya)
- "ar_MA", // Arabic (Morocco)
- "ar_OM", // Arabic (Oman)
- "ar_QA", // Arabic (Qatar)
- "ar_SA", // Arabic (Saudi Arabia)
- "ar_SD", // Arabic (Sudan)
- "ar_SS", // Arabic (South Soudan)
- "ar_SY", // Arabic (Syria)
- "ar_TN", // Arabic (Tunisia)
- "ar_YE", // Arabic (Yemen)
- "as_IN", // Assamese (India)
- "ast_ES", // Asturian (Spain)
- "ayc_PE", // Southern Aymara (Peru)
- "ay_PE", // Aymara (Peru)
- "az", // Azerbaijani
- "az_AZ", // Azerbaijani (Azerbaijan)
- "be", // Belarusian
- "be_BY", // Belarusian (Belarus)
- "bem_ZM", // Bemba (Zambia)
- "ber_DZ", // Berber languages (Algeria)
- "ber_MA", // Berber languages (Morocco)
- "bg", // Bulgarian
- "bg_BG", // Bulgarian (Bulgaria)
- "bhb_IN", // Bhili (India)
- "bho_IN", // Bhojpuri (India)
- "bi_TV", // Bislama (Tuvalu)
- "bn", // Bengali
- "bn_BD", // Bengali (Bangladesh)
- "bn_IN", // Bengali (India)
- "bo", // Tibetan
- "bo_CN", // Tibetan (China)
- "bo_IN", // Tibetan (India)
- "br", // Breton
- "br_FR", // Breton (France)
- "brx_IN", // Bodo (India)
- "bs_BA", // Bosnian (Bosnia and Herzegovina)
- "byn_ER", // Bilin (Eritrea)
- "ca", // Catalan
- "ca_AD", // Catalan (Andorra)
- "ca_ES", // Catalan (Spain)
- "ca_FR", // Catalan (France)
- "ca_IT", // Catalan (Italy)
- "ce_RU", // Chechen (Russia)
- "chr_US", // Cherokee (United States)
- "cmn_TW", // Mandarin Chinese (Taiwan)
- "crh_UA", // Crimean Tatar (Ukraine)
- "csb_PL", // Kashubian (Poland)
- "cs", // Czech
- "cs_CZ", // Czech (Czech Republic)
- "cv_RU", // Chuvash (Russia)
- "cy_GB", // Welsh (United Kingdom)
- "da", // Danish
- "da_DK", // Danish (Denmark)
- "de", // German
- "de_AT", // German (Austria)
- "de_BE", // German (Belgium)
- "de_CH", // German (Switzerland)
- "de_DE", // German (Germany)
- "de_IT", // German (Italy)
- "de_LU", // German (Luxembourg)
- "doi_IN", // Dogri (India)
- "dv_MV", // Dhivehi (Maldives)
- "dz_BT", // Dzongkha (Bhutan)
- "el", // Greek
- "el_CY", // Greek (Cyprus)
- "el_GR", // Greek (Greece)
- "en", // English
- "en_AG", // English (Antigua and Barbuda)
- "en_AU", // English (Australia)
- "en_BW", // English (Botswana)
- "en_CA", // English (Canada)
- "en_DK", // English (Denmark)
- "en_GB", // English (United Kingdom)
- "en_HK", // English (Hong Kong)
- "en_IE", // English (Ireland)
- "en_IL", // English (Israel)
- "en_IN", // English (India)
- "en_NG", // English (Nigeria)
- "en_NZ", // English (New Zealand)
- "en_PH", // English (Philippines)
- "en_SG", // English (Singapore)
- "en_US", // English (United States)
- "en_ZA", // English (South Africa)
- "en_ZM", // English (Zambia)
- "en_ZW", // English (Zimbabwe)
- "eo", // Esperanto
- "es", // Spanish
- "es_AR", // Spanish (Argentina)
- "es_BO", // Spanish (Bolivia)
- "es_CL", // Spanish (Chile)
- "es_CO", // Spanish (Colombia)
- "es_CR", // Spanish (Costa Rica)
- "es_CU", // Spanish (Cuba)
- "es_DO", // Spanish (Dominican Republic)
- "es_EC", // Spanish (Ecuador)
- "es_ES", // Spanish (Spain)
- "es_GT", // Spanish (Guatemala)
- "es_HN", // Spanish (Honduras)
- "es_MX", // Spanish (Mexico)
- "es_NI", // Spanish (Nicaragua)
- "es_PA", // Spanish (Panama)
- "es_PE", // Spanish (Peru)
- "es_PR", // Spanish (Puerto Rico)
- "es_PY", // Spanish (Paraguay)
- "es_SV", // Spanish (El Salvador)
- "es_US", // Spanish (United States)
- "es_UY", // Spanish (Uruguay)
- "es_VE", // Spanish (Venezuela)
- "et", // Estonian
- "et_EE", // Estonian (Estonia)
- "eu", // Basque
- "eu_ES", // Basque (Spain)
- "fa", // Persian
- "fa_IR", // Persian (Iran)
- "ff_SN", // Fulah (Senegal)
- "fi", // Finnish
- "fi_FI", // Finnish (Finland)
- "fil", // Filipino
- "fil_PH", // Filipino (Philippines)
- "fo_FO", // Faroese (Faroe Islands)
- "fr", // French
- "fr_BE", // French (Belgium)
- "fr_CA", // French (Canada)
- "fr_CH", // French (Switzerland)
- "fr_FR", // French (France)
- "fr_LU", // French (Luxembourg)
- "fur_IT", // Friulian (Italy)
- "fy_DE", // Western Frisian (Germany)
- "fy_NL", // Western Frisian (Netherlands)
- "ga", // Irish
- "ga_IE", // Irish (Ireland)
- "gd_GB", // Scottish Gaelic (United Kingdom)
- "gez_ER", // Geez (Eritrea)
- "gez_ET", // Geez (Ethiopia)
- "gl", // Galician
- "gl_ES", // Galician (Spain)
- "gu_IN", // Gujarati (India)
- "gv_GB", // Manx (United Kingdom)
- "hak_TW", // Hakka Chinese (Taiwan)
- "ha_NG", // Hausa (Nigeria)
- "he", // Hebrew
- "he_IL", // Hebrew (Israel)
- "hi", // Hindi
- "hi_IN", // Hindi (India)
- "hne_IN", // Chhattisgarhi (India)
- "hr", // Croatian
- "hr_HR", // Croatian (Croatia)
- "hsb_DE", // Upper Sorbian (Germany)
- "ht_HT", // Haitian (Haiti)
- "hu", // Hungarian
- "hu_HU", // Hungarian (Hungary)
- "hus_MX", // Huastec (Mexico)
- "hy_AM", // Armenian (Armenia)
- "ia_FR", // Interlingua (France)
- "id", // Indonesian
- "id_ID", // Indonesian (Indonesia)
- "ig_NG", // Igbo (Nigeria)
- "ik_CA", // Inupiaq (Canada)
- "is", // Icelandic
- "is_IS", // Icelandic (Iceland)
- "it", // Italian
- "it_CH", // Italian (Switzerland)
- "it_IT", // Italian (Italy)
- "iu_CA", // Inuktitut (Canada)
- "ja", // Japanese
- "ja_JP", // Japanese (Japan)
- "kab_DZ", // Kabyle (Algeria)
- "ka", // Georgian
- "ka_GE", // Georgian (Georgia)
- "kk_KZ", // Kazakh (Kazakhstan)
- "kl_GL", // Kalaallisut (Greenland)
- "km", // Central Khmer
- "km_KH", // Central Khmer (Cambodia)
- "kn_IN", // Kannada (India)
- "kok_IN", // Konkani (India)
- "ko", // Korean
- "ko_KR", // Korean (South Korea)
- "ks_IN", // Kashmiri (India)
- "ku", // Kurdish
- "ku_TR", // Kurdish (Turkey)
- "kw_GB", // Cornish (United Kingdom)
- "ky_KG", // Kirghiz (Kyrgyzstan)
- "lb_LU", // Luxembourgish (Luxembourg)
- "lg_UG", // Ganda (Uganda)
- "li_BE", // Limburgan (Belgium)
- "li_NL", // Limburgan (Netherlands)
- "lij_IT", // Ligurian (Italy)
- "ln_CD", // Lingala (Congo)
- "lo_LA", // Lao (Laos)
- "lt", // Lithuanian
- "lt_LT", // Lithuanian (Lithuania)
- "lv", // Latvian
- "lv_LV", // Latvian (Latvia)
- "lzh_TW", // Literary Chinese (Taiwan)
- "mag_IN", // Magahi (India)
- "mai_IN", // Maithili (India)
- "mg_MG", // Malagasy (Madagascar)
- "mh_MH", // Marshallese (Marshall Islands)
- "mhr_RU", // Eastern Mari (Russia)
- "mi", // Māori
- "mi_NZ", // Māori (New Zealand)
- "miq_NI", // Mískito (Nicaragua)
- "mk", // Macedonian
- "mk_MK", // Macedonian (Macedonia)
- "ml", // Malayalam
- "ml_IN", // Malayalam (India)
- "mni_IN", // Manipuri (India)
- "mn_MN", // Mongolian (Mongolia)
- "mr", // Marathi
- "mr_IN", // Marathi (India)
- "ms", // Malay
- "ms_MY", // Malay (Malaysia)
- "mt", // Maltese
- "mt_MT", // Maltese (Malta)
- "my_MM", // Burmese (Myanmar)
- "myv_RU", // Erzya (Russia)
- "nah_MX", // Nahuatl languages (Mexico)
- "nan_TW", // Min Nan Chinese (Taiwan)
- "nb", // Norwegian Bokmål
- "nb_NO", // Norwegian Bokmål (Norway)
- "nds_DE", // Low German (Germany)
- "nds_NL", // Low German (Netherlands)
- "ne_NP", // Nepali (Nepal)
- "nhn_MX", // Central Nahuatl (Mexico)
- "niu_NU", // Niuean (Niue)
- "niu_NZ", // Niuean (New Zealand)
- "nl", // Dutch
- "nl_AW", // Dutch (Aruba)
- "nl_BE", // Dutch (Belgium)
- "nl_NL", // Dutch (Netherlands)
- "nn", // Norwegian Nynorsk
- "nn_NO", // Norwegian Nynorsk (Norway)
- "nr_ZA", // South Ndebele (South Africa)
- "nso_ZA", // Pedi (South Africa)
- "oc_FR", // Occitan (France)
- "om", // Oromo
- "om_ET", // Oromo (Ethiopia)
- "om_KE", // Oromo (Kenya)
- "or", // Oriya
- "or_IN", // Oriya (India)
- "os_RU", // Ossetian (Russia)
- "pa_IN", // Panjabi (India)
- "pap", // Papiamento
- "pap_AN", // Papiamento (Netherlands Antilles)
- "pap_AW", // Papiamento (Aruba)
- "pap_CW", // Papiamento (Curaçao)
- "pa_PK", // Panjabi (Pakistan)
- "pl", // Polish
- "pl_PL", // Polish (Poland)
- "pr", // Pirate
- "ps_AF", // Pushto (Afghanistan)
- "pt", // Portuguese
- "pt_BR", // Portuguese (Brazil)
- "pt_PT", // Portuguese (Portugal)
- "quy_PE", // Ayacucho Quechua (Peru)
- "quz_PE", // Cusco Quechua (Peru)
- "raj_IN", // Rajasthani (India)
- "ro", // Romanian
- "ro_RO", // Romanian (Romania)
- "ru", // Russian
- "ru_RU", // Russian (Russia)
- "ru_UA", // Russian (Ukraine)
- "rw_RW", // Kinyarwanda (Rwanda)
- "sa_IN", // Sanskrit (India)
- "sat_IN", // Santali (India)
- "sc_IT", // Sardinian (Italy)
- "sco", // Scots
- "sd_IN", // Sindhi (India)
- "se_NO", // Northern Sami (Norway)
- "sgs_LT", // Samogitian (Lithuania)
- "shs_CA", // Shuswap (Canada)
- "sid_ET", // Sidamo (Ethiopia)
- "si", // Sinhala
- "si_LK", // Sinhala (Sri Lanka)
- "sk", // Slovak
- "sk_SK", // Slovak (Slovakia)
- "sl", // Slovenian
- "sl_SI", // Slovenian (Slovenia)
- "so", // Somali
- "so_DJ", // Somali (Djibouti)
- "so_ET", // Somali (Ethiopia)
- "so_KE", // Somali (Kenya)
- "so_SO", // Somali (Somalia)
- "son_ML", // Songhai languages (Mali)
- "sq", // Albanian
- "sq_AL", // Albanian (Albania)
- "sq_KV", // Albanian (Kosovo)
- "sq_MK", // Albanian (Macedonia)
- "sr", // Serbian
- "sr_Cyrl", // Serbian (Cyrillic)
- "sr_Latn", // Serbian (Latin)
- "sr_ME", // Serbian (Montenegro)
- "sr_RS", // Serbian (Serbia)
- "ss_ZA", // Swati (South Africa)
- "st_ZA", // Southern Sotho (South Africa)
- "sv", // Swedish
- "sv_FI", // Swedish (Finland)
- "sv_SE", // Swedish (Sweden)
- "sw_KE", // Swahili (Kenya)
- "sw_TZ", // Swahili (Tanzania)
- "szl_PL", // Silesian (Poland)
- "ta", // Tamil
- "ta_IN", // Tamil (India)
- "ta_LK", // Tamil (Sri Lanka)
- "tcy_IN", // Tulu (India)
- "te", // Telugu
- "te_IN", // Telugu (India)
- "tg_TJ", // Tajik (Tajikistan)
- "the_NP", // Chitwania Tharu (Nepal)
- "th", // Thai
- "th_TH", // Thai (Thailand)
- "ti", // Tigrinya
- "ti_ER", // Tigrinya (Eritrea)
- "ti_ET", // Tigrinya (Ethiopia)
- "tig_ER", // Tigre (Eritrea)
- "tk_TM", // Turkmen (Turkmenistan)
- "tl_PH", // Tagalog (Philippines)
- "tn_ZA", // Tswana (South Africa)
- "tr", // Turkish
- "tr_CY", // Turkish (Cyprus)
- "tr_TR", // Turkish (Turkey)
- "ts_ZA", // Tsonga (South Africa)
- "tt", // Tatar
- "tt_RU", // Tatar (Russia)
- "tzm", // Central Atlas Tamazight
- "tzm_MA", // Central Atlas Tamazight (Marrocos)
- "ug_CN", // Uighur (China)
- "uk", // Ukrainian
- "uk_UA", // Ukrainian (Ukraine)
- "unm_US", // Unami (United States)
- "ur", // Urdu
- "ur_IN", // Urdu (India)
- "ur_PK", // Urdu (Pakistan)
- "uz", // Uzbek
- "uz_UZ", // Uzbek (Uzbekistan)
- "ve_ZA", // Venda (South Africa)
- "vi", // Vietnamese
- "vi_VN", // Vietnamese (Vietnam)
- "wa_BE", // Walloon (Belgium)
- "wae_CH", // Walser (Switzerland)
- "wal_ET", // Wolaytta (Ethiopia)
- "wo_SN", // Wolof (Senegal)
- "xh_ZA", // Xhosa (South Africa)
- "yi_US", // Yiddish (United States)
- "yo_NG", // Yoruba (Nigeria)
- "yue_HK", // Yue Chinese (Hong Kong)
- "zh", // Chinese
- "zh_CN", // Chinese (China)
- "zh_HK", // Chinese (Hong Kong)
- "zh_SG", // Chinese (Singapore)
- "zh_TW", // Chinese (Taiwan)
- "zu_ZA", // Zulu (South Africa)
- nullptr
-};
-
-static const char *locale_names[] = {
- "Afar",
- "Afar (Djibouti)",
- "Afar (Eritrea)",
- "Afar (Ethiopia)",
- "Afrikaans",
- "Afrikaans (South Africa)",
- "Aguaruna (Peru)",
- "Akan (Ghana)",
- "Amharic (Ethiopia)",
- "Aragonese (Spain)",
- "Angika (India)",
- "Arabic",
- "Arabic (United Arab Emirates)",
- "Arabic (Bahrain)",
- "Arabic (Algeria)",
- "Arabic (Egypt)",
- "Arabic (India)",
- "Arabic (Iraq)",
- "Arabic (Jordan)",
- "Arabic (Kuwait)",
- "Arabic (Lebanon)",
- "Arabic (Libya)",
- "Arabic (Morocco)",
- "Arabic (Oman)",
- "Arabic (Qatar)",
- "Arabic (Saudi Arabia)",
- "Arabic (Sudan)",
- "Arabic (South Soudan)",
- "Arabic (Syria)",
- "Arabic (Tunisia)",
- "Arabic (Yemen)",
- "Assamese (India)",
- "Asturian (Spain)",
- "Southern Aymara (Peru)",
- "Aymara (Peru)",
- "Azerbaijani",
- "Azerbaijani (Azerbaijan)",
- "Belarusian",
- "Belarusian (Belarus)",
- "Bemba (Zambia)",
- "Berber languages (Algeria)",
- "Berber languages (Morocco)",
- "Bulgarian",
- "Bulgarian (Bulgaria)",
- "Bhili (India)",
- "Bhojpuri (India)",
- "Bislama (Tuvalu)",
- "Bengali",
- "Bengali (Bangladesh)",
- "Bengali (India)",
- "Tibetan",
- "Tibetan (China)",
- "Tibetan (India)",
- "Breton",
- "Breton (France)",
- "Bodo (India)",
- "Bosnian (Bosnia and Herzegovina)",
- "Bilin (Eritrea)",
- "Catalan",
- "Catalan (Andorra)",
- "Catalan (Spain)",
- "Catalan (France)",
- "Catalan (Italy)",
- "Chechen (Russia)",
- "Cherokee (United States)",
- "Mandarin Chinese (Taiwan)",
- "Crimean Tatar (Ukraine)",
- "Kashubian (Poland)",
- "Czech",
- "Czech (Czech Republic)",
- "Chuvash (Russia)",
- "Welsh (United Kingdom)",
- "Danish",
- "Danish (Denmark)",
- "German",
- "German (Austria)",
- "German (Belgium)",
- "German (Switzerland)",
- "German (Germany)",
- "German (Italy)",
- "German (Luxembourg)",
- "Dogri (India)",
- "Dhivehi (Maldives)",
- "Dzongkha (Bhutan)",
- "Greek",
- "Greek (Cyprus)",
- "Greek (Greece)",
- "English",
- "English (Antigua and Barbuda)",
- "English (Australia)",
- "English (Botswana)",
- "English (Canada)",
- "English (Denmark)",
- "English (United Kingdom)",
- "English (Hong Kong)",
- "English (Ireland)",
- "English (Israel)",
- "English (India)",
- "English (Nigeria)",
- "English (New Zealand)",
- "English (Philippines)",
- "English (Singapore)",
- "English (United States)",
- "English (South Africa)",
- "English (Zambia)",
- "English (Zimbabwe)",
- "Esperanto",
- "Spanish",
- "Spanish (Argentina)",
- "Spanish (Bolivia)",
- "Spanish (Chile)",
- "Spanish (Colombia)",
- "Spanish (Costa Rica)",
- "Spanish (Cuba)",
- "Spanish (Dominican Republic)",
- "Spanish (Ecuador)",
- "Spanish (Spain)",
- "Spanish (Guatemala)",
- "Spanish (Honduras)",
- "Spanish (Mexico)",
- "Spanish (Nicaragua)",
- "Spanish (Panama)",
- "Spanish (Peru)",
- "Spanish (Puerto Rico)",
- "Spanish (Paraguay)",
- "Spanish (El Salvador)",
- "Spanish (United States)",
- "Spanish (Uruguay)",
- "Spanish (Venezuela)",
- "Estonian",
- "Estonian (Estonia)",
- "Basque",
- "Basque (Spain)",
- "Persian",
- "Persian (Iran)",
- "Fulah (Senegal)",
- "Finnish",
- "Finnish (Finland)",
- "Filipino",
- "Filipino (Philippines)",
- "Faroese (Faroe Islands)",
- "French",
- "French (Belgium)",
- "French (Canada)",
- "French (Switzerland)",
- "French (France)",
- "French (Luxembourg)",
- "Friulian (Italy)",
- "Western Frisian (Germany)",
- "Western Frisian (Netherlands)",
- "Irish",
- "Irish (Ireland)",
- "Scottish Gaelic (United Kingdom)",
- "Geez (Eritrea)",
- "Geez (Ethiopia)",
- "Galician",
- "Galician (Spain)",
- "Gujarati (India)",
- "Manx (United Kingdom)",
- "Hakka Chinese (Taiwan)",
- "Hausa (Nigeria)",
- "Hebrew",
- "Hebrew (Israel)",
- "Hindi",
- "Hindi (India)",
- "Chhattisgarhi (India)",
- "Croatian",
- "Croatian (Croatia)",
- "Upper Sorbian (Germany)",
- "Haitian (Haiti)",
- "Hungarian",
- "Hungarian (Hungary)",
- "Huastec (Mexico)",
- "Armenian (Armenia)",
- "Interlingua (France)",
- "Indonesian",
- "Indonesian (Indonesia)",
- "Igbo (Nigeria)",
- "Inupiaq (Canada)",
- "Icelandic",
- "Icelandic (Iceland)",
- "Italian",
- "Italian (Switzerland)",
- "Italian (Italy)",
- "Inuktitut (Canada)",
- "Japanese",
- "Japanese (Japan)",
- "Kabyle (Algeria)",
- "Georgian",
- "Georgian (Georgia)",
- "Kazakh (Kazakhstan)",
- "Kalaallisut (Greenland)",
- "Central Khmer",
- "Central Khmer (Cambodia)",
- "Kannada (India)",
- "Konkani (India)",
- "Korean",
- "Korean (South Korea)",
- "Kashmiri (India)",
- "Kurdish",
- "Kurdish (Turkey)",
- "Cornish (United Kingdom)",
- "Kirghiz (Kyrgyzstan)",
- "Luxembourgish (Luxembourg)",
- "Ganda (Uganda)",
- "Limburgan (Belgium)",
- "Limburgan (Netherlands)",
- "Ligurian (Italy)",
- "Lingala (Congo)",
- "Lao (Laos)",
- "Lithuanian",
- "Lithuanian (Lithuania)",
- "Latvian",
- "Latvian (Latvia)",
- "Literary Chinese (Taiwan)",
- "Magahi (India)",
- "Maithili (India)",
- "Malagasy (Madagascar)",
- "Marshallese (Marshall Islands)",
- "Eastern Mari (Russia)",
- "Māori",
- "Māori (New Zealand)",
- "Mískito (Nicaragua)",
- "Macedonian",
- "Macedonian (Macedonia)",
- "Malayalam",
- "Malayalam (India)",
- "Manipuri (India)",
- "Mongolian (Mongolia)",
- "Marathi",
- "Marathi (India)",
- "Malay",
- "Malay (Malaysia)",
- "Maltese",
- "Maltese (Malta)",
- "Burmese (Myanmar)",
- "Erzya (Russia)",
- "Nahuatl languages (Mexico)",
- "Min Nan Chinese (Taiwan)",
- "Norwegian Bokmål",
- "Norwegian Bokmål (Norway)",
- "Low German (Germany)",
- "Low German (Netherlands)",
- "Nepali (Nepal)",
- "Central Nahuatl (Mexico)",
- "Niuean (Niue)",
- "Niuean (New Zealand)",
- "Dutch",
- "Dutch (Aruba)",
- "Dutch (Belgium)",
- "Dutch (Netherlands)",
- "Norwegian Nynorsk",
- "Norwegian Nynorsk (Norway)",
- "South Ndebele (South Africa)",
- "Pedi (South Africa)",
- "Occitan (France)",
- "Oromo",
- "Oromo (Ethiopia)",
- "Oromo (Kenya)",
- "Oriya",
- "Oriya (India)",
- "Ossetian (Russia)",
- "Panjabi (India)",
- "Papiamento",
- "Papiamento (Netherlands Antilles)",
- "Papiamento (Aruba)",
- "Papiamento (Curaçao)",
- "Panjabi (Pakistan)",
- "Polish",
- "Polish (Poland)",
- "Pirate",
- "Pushto (Afghanistan)",
- "Portuguese",
- "Portuguese (Brazil)",
- "Portuguese (Portugal)",
- "Ayacucho Quechua (Peru)",
- "Cusco Quechua (Peru)",
- "Rajasthani (India)",
- "Romanian",
- "Romanian (Romania)",
- "Russian",
- "Russian (Russia)",
- "Russian (Ukraine)",
- "Kinyarwanda (Rwanda)",
- "Sanskrit (India)",
- "Santali (India)",
- "Sardinian (Italy)",
- "Scots (Scotland)",
- "Sindhi (India)",
- "Northern Sami (Norway)",
- "Samogitian (Lithuania)",
- "Shuswap (Canada)",
- "Sidamo (Ethiopia)",
- "Sinhala",
- "Sinhala (Sri Lanka)",
- "Slovak",
- "Slovak (Slovakia)",
- "Slovenian",
- "Slovenian (Slovenia)",
- "Somali",
- "Somali (Djibouti)",
- "Somali (Ethiopia)",
- "Somali (Kenya)",
- "Somali (Somalia)",
- "Songhai languages (Mali)",
- "Albanian",
- "Albanian (Albania)",
- "Albanian (Kosovo)",
- "Albanian (Macedonia)",
- "Serbian",
- "Serbian (Cyrillic)",
- "Serbian (Latin)",
- "Serbian (Montenegro)",
- "Serbian (Serbia)",
- "Swati (South Africa)",
- "Southern Sotho (South Africa)",
- "Swedish",
- "Swedish (Finland)",
- "Swedish (Sweden)",
- "Swahili (Kenya)",
- "Swahili (Tanzania)",
- "Silesian (Poland)",
- "Tamil",
- "Tamil (India)",
- "Tamil (Sri Lanka)",
- "Tulu (India)",
- "Telugu",
- "Telugu (India)",
- "Tajik (Tajikistan)",
- "Chitwania Tharu (Nepal)",
- "Thai",
- "Thai (Thailand)",
- "Tigrinya",
- "Tigrinya (Eritrea)",
- "Tigrinya (Ethiopia)",
- "Tigre (Eritrea)",
- "Turkmen (Turkmenistan)",
- "Tagalog (Philippines)",
- "Tswana (South Africa)",
- "Turkish",
- "Turkish (Cyprus)",
- "Turkish (Turkey)",
- "Tsonga (South Africa)",
- "Tatar",
- "Tatar (Russia)",
- "Central Atlas Tamazight",
- "Central Atlas Tamazight (Marrocos)",
- "Uighur (China)",
- "Ukrainian",
- "Ukrainian (Ukraine)",
- "Unami (United States)",
- "Urdu",
- "Urdu (India)",
- "Urdu (Pakistan)",
- "Uzbek",
- "Uzbek (Uzbekistan)",
- "Venda (South Africa)",
- "Vietnamese",
- "Vietnamese (Vietnam)",
- "Walloon (Belgium)",
- "Walser (Switzerland)",
- "Wolaytta (Ethiopia)",
- "Wolof (Senegal)",
- "Xhosa (South Africa)",
- "Yiddish (United States)",
- "Yoruba (Nigeria)",
- "Yue Chinese (Hong Kong)",
- "Chinese",
- "Chinese (China)",
- "Chinese (Hong Kong)",
- "Chinese (Singapore)",
- "Chinese (Taiwan)",
- "Zulu (South Africa)",
- nullptr
-};
-
-// Windows has some weird locale identifiers which do not honor the ISO 639-1
-// standardized nomenclature. Whenever those don't conflict with existing ISO
-// identifiers, we override them.
-//
-// Reference:
-// - https://msdn.microsoft.com/en-us/library/windows/desktop/ms693062(v=vs.85).aspx
-
-static const char *locale_renames[][2] = {
- { "in", "id" }, // Indonesian
- { "iw", "he" }, // Hebrew
- { "no", "nb" }, // Norwegian Bokmål
- { "C", "en" }, // "C" is the simple/default/untranslated Computer locale.
- // ASCII-only, English, no currency symbols. Godot treats this as "en".
- // See https://unix.stackexchange.com/a/87763/164141 "The C locale is"...
- { nullptr, nullptr }
-};
-
-///////////////////////////////////////////////
-
Dictionary Translation::_get_messages() const {
Dictionary d;
for (const KeyValue<StringName, StringName> &E : translation_map) {
@@ -849,17 +68,7 @@ void Translation::_set_messages(const Dictionary &p_messages) {
}
void Translation::set_locale(const String &p_locale) {
- String univ_locale = TranslationServer::standardize_locale(p_locale);
-
- if (!TranslationServer::is_locale_valid(univ_locale)) {
- String trimmed_locale = TranslationServer::get_language_code(univ_locale);
-
- ERR_FAIL_COND_MSG(!TranslationServer::is_locale_valid(trimmed_locale), "Invalid locale: " + trimmed_locale + ".");
-
- locale = trimmed_locale;
- } else {
- locale = univ_locale;
- }
+ locale = TranslationServer::get_singleton()->standardize_locale(p_locale);
if (OS::get_singleton()->get_main_loop() && TranslationServer::get_singleton()->get_loaded_locales().has(this)) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
@@ -1004,124 +213,314 @@ static _character_accent_pair _character_to_accented[] = {
{ 'z', U"ź" },
};
-bool TranslationServer::is_locale_valid(const String &p_locale) {
- const char **ptr = locale_list;
+static _FORCE_INLINE_ bool is_upper_case(char32_t c) {
+ return (c >= 'A' && c <= 'Z');
+}
+
+static _FORCE_INLINE_ bool is_lower_case(char32_t c) {
+ return (c >= 'a' && c <= 'z');
+}
+
+Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info;
+
+Map<String, String> TranslationServer::language_map;
+Map<String, String> TranslationServer::script_map;
+Map<String, String> TranslationServer::locale_rename_map;
+Map<String, String> TranslationServer::country_name_map;
+Map<String, String> TranslationServer::variant_map;
+Map<String, String> TranslationServer::country_rename_map;
+
+void TranslationServer::init_locale_info() {
+ // Init locale info.
+ language_map.clear();
+ int idx = 0;
+ while (language_list[idx][0] != nullptr) {
+ language_map[language_list[idx][0]] = String::utf8(language_list[idx][1]);
+ idx++;
+ }
- while (*ptr) {
- if (*ptr == p_locale) {
- return true;
+ // Init locale-script map.
+ locale_script_info.clear();
+ idx = 0;
+ while (locale_scripts[idx][0] != nullptr) {
+ LocaleScriptInfo info;
+ info.name = locale_scripts[idx][0];
+ info.script = locale_scripts[idx][1];
+ info.default_country = locale_scripts[idx][2];
+ Vector<String> supported_countries = String(locale_scripts[idx][3]).split(",", false);
+ for (int i = 0; i < supported_countries.size(); i++) {
+ info.supported_countries.insert(supported_countries[i]);
}
- ptr++;
+ locale_script_info.push_back(info);
+ idx++;
}
- return false;
-}
+ // Init supported script list.
+ script_map.clear();
+ idx = 0;
+ while (script_list[idx][0] != nullptr) {
+ script_map[script_list[idx][1]] = String::utf8(script_list[idx][0]);
+ idx++;
+ }
-String TranslationServer::standardize_locale(const String &p_locale) {
- // Replaces '-' with '_' for macOS Sierra-style locales
- String univ_locale = p_locale.replace("-", "_");
+ // Init regional variant map.
+ variant_map.clear();
+ idx = 0;
+ while (locale_variants[idx][0] != nullptr) {
+ variant_map[locale_variants[idx][0]] = locale_variants[idx][1];
+ idx++;
+ }
- // Handles known non-ISO locale names used e.g. on Windows
- int idx = 0;
+ // Init locale renames.
+ locale_rename_map.clear();
+ idx = 0;
while (locale_renames[idx][0] != nullptr) {
- if (locale_renames[idx][0] == univ_locale) {
- univ_locale = locale_renames[idx][1];
- break;
+ if (!String(locale_renames[idx][1]).is_empty()) {
+ locale_rename_map[locale_renames[idx][0]] = locale_renames[idx][1];
}
idx++;
}
- return univ_locale;
+ // Init country names.
+ country_name_map.clear();
+ idx = 0;
+ while (country_names[idx][0] != nullptr) {
+ country_name_map[String(country_names[idx][0])] = String::utf8(country_names[idx][1]);
+ idx++;
+ }
+
+ // Init country renames.
+ country_rename_map.clear();
+ idx = 0;
+ while (country_renames[idx][0] != nullptr) {
+ if (!String(country_renames[idx][1]).is_empty()) {
+ country_rename_map[country_renames[idx][0]] = country_renames[idx][1];
+ }
+ idx++;
+ }
}
-String TranslationServer::get_language_code(const String &p_locale) {
- ERR_FAIL_COND_V_MSG(p_locale.length() < 2, p_locale, "Invalid locale '" + p_locale + "'.");
- // Most language codes are two letters, but some are three,
- // so we have to look for a regional code separator ('_' or '-')
- // to extract the left part.
- // For example we get 'nah_MX' as input and should return 'nah'.
- int split = p_locale.find("_");
- if (split == -1) {
- split = p_locale.find("-");
+String TranslationServer::standardize_locale(const String &p_locale) const {
+ // Replaces '-' with '_' for macOS style locales.
+ String univ_locale = p_locale.replace("-", "_");
+
+ // Extract locale elements.
+ String lang, script, country, variant;
+ Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_");
+ lang = locale_elements[0];
+ if (locale_elements.size() >= 2) {
+ if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) {
+ script = locale_elements[1];
+ }
+ if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) {
+ country = locale_elements[1];
+ }
+ }
+ if (locale_elements.size() >= 3) {
+ if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) {
+ country = locale_elements[2];
+ } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang) {
+ variant = locale_elements[2].to_lower();
+ }
}
- if (split == -1) { // No separator, so the locale is already only a language code.
- return p_locale;
+ if (locale_elements.size() >= 4) {
+ if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang) {
+ variant = locale_elements[3].to_lower();
+ }
}
- return p_locale.left(split);
-}
-void TranslationServer::set_locale(const String &p_locale) {
- String univ_locale = standardize_locale(p_locale);
+ // Try extract script and variant from the extra part.
+ Vector<String> script_extra = univ_locale.get_slice("@", 1).split(";");
+ for (int i = 0; i < script_extra.size(); i++) {
+ if (script_extra[i].to_lower() == "cyrillic") {
+ script = "Cyrl";
+ break;
+ } else if (script_extra[i].to_lower() == "latin") {
+ script = "Latn";
+ break;
+ } else if (script_extra[i].to_lower() == "devanagari") {
+ script = "Deva";
+ break;
+ } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang) {
+ variant = script_extra[i].to_lower();
+ }
+ }
- if (!is_locale_valid(univ_locale)) {
- String trimmed_locale = get_language_code(univ_locale);
- print_verbose(vformat("Unsupported locale '%s', falling back to '%s'.", p_locale, trimmed_locale));
+ // Handles known non-ISO language names used e.g. on Windows.
+ if (locale_rename_map.has(lang)) {
+ lang = locale_rename_map[lang];
+ }
- if (!is_locale_valid(trimmed_locale)) {
- ERR_PRINT(vformat("Unsupported locale '%s', falling back to 'en'.", trimmed_locale));
- locale = "en";
- } else {
- locale = trimmed_locale;
+ // Handle country renames.
+ if (country_rename_map.has(country)) {
+ country = country_rename_map[country];
+ }
+
+ // Remove unsupported script codes.
+ if (!script_map.has(script)) {
+ script = "";
+ }
+
+ // Add script code base on language and country codes for some ambiguous cases.
+ if (script.is_empty()) {
+ for (int i = 0; i < locale_script_info.size(); i++) {
+ const LocaleScriptInfo &info = locale_script_info[i];
+ if (info.name == lang) {
+ if (country.is_empty() || info.supported_countries.has(country)) {
+ script = info.script;
+ break;
+ }
+ }
+ }
+ }
+ if (!script.is_empty() && country.is_empty()) {
+ // Add conntry code based on script for some ambiguous cases.
+ for (int i = 0; i < locale_script_info.size(); i++) {
+ const LocaleScriptInfo &info = locale_script_info[i];
+ if (info.name == lang && info.script == script) {
+ country = info.default_country;
+ break;
+ }
+ }
+ }
+
+ // Combine results.
+ String locale = lang;
+ if (!script.is_empty()) {
+ locale = locale + "_" + script;
+ }
+ if (!country.is_empty()) {
+ locale = locale + "_" + country;
+ }
+ if (!variant.is_empty()) {
+ locale = locale + "_" + variant;
+ }
+ return locale;
+}
+
+int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const {
+ String locale_a = standardize_locale(p_locale_a);
+ String locale_b = standardize_locale(p_locale_b);
+
+ if (locale_a == locale_b) {
+ // Exact match.
+ return 10;
+ }
+
+ Vector<String> locale_a_elements = locale_a.split("_");
+ Vector<String> locale_b_elements = locale_b.split("_");
+ if (locale_a_elements[0] == locale_b_elements[0]) {
+ // Matching language, both locales have extra parts.
+ // Return number of matching elements.
+ int matching_elements = 1;
+ for (int i = 1; i < locale_a_elements.size(); i++) {
+ for (int j = 1; j < locale_b_elements.size(); j++) {
+ if (locale_a_elements[i] == locale_b_elements[j]) {
+ matching_elements++;
+ }
+ }
}
+ return matching_elements;
} else {
- locale = univ_locale;
+ // No match.
+ return 0;
}
+}
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+String TranslationServer::get_locale_name(const String &p_locale) const {
+ String locale = standardize_locale(p_locale);
+
+ String lang, script, country;
+ Vector<String> locale_elements = locale.split("_");
+ lang = locale_elements[0];
+ if (locale_elements.size() >= 2) {
+ if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) {
+ script = locale_elements[1];
+ }
+ if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) {
+ country = locale_elements[1];
+ }
+ }
+ if (locale_elements.size() >= 3) {
+ if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) {
+ country = locale_elements[2];
+ }
}
- ResourceLoader::reload_translation_remaps();
+ String name = language_map[lang];
+ if (!script.is_empty()) {
+ name = name + " (" + script_map[script] + ")";
+ }
+ if (!country.is_empty()) {
+ name = name + ", " + country_name_map[country];
+ }
+ return name;
}
-String TranslationServer::get_locale() const {
- return locale;
+Vector<String> TranslationServer::get_all_languages() const {
+ Vector<String> languages;
+
+ for (const Map<String, String>::Element *E = language_map.front(); E; E = E->next()) {
+ languages.push_back(E->key());
+ }
+
+ return languages;
}
-String TranslationServer::get_locale_name(const String &p_locale) const {
- if (!locale_name_map.has(p_locale)) {
- return String();
+String TranslationServer::get_language_name(const String &p_language) const {
+ return language_map[p_language];
+}
+
+Vector<String> TranslationServer::get_all_scripts() const {
+ Vector<String> scripts;
+
+ for (const Map<String, String>::Element *E = script_map.front(); E; E = E->next()) {
+ scripts.push_back(E->key());
}
- return locale_name_map[p_locale];
+
+ return scripts;
}
-Array TranslationServer::get_loaded_locales() const {
- Array locales;
- for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) {
- const Ref<Translation> &t = E->get();
- ERR_FAIL_COND_V(t.is_null(), Array());
- String l = t->get_locale();
+String TranslationServer::get_script_name(const String &p_script) const {
+ return script_map[p_script];
+}
- if (!locales.has(l)) {
- locales.push_back(l);
- }
+Vector<String> TranslationServer::get_all_countries() const {
+ Vector<String> countries;
+
+ for (const Map<String, String>::Element *E = country_name_map.front(); E; E = E->next()) {
+ countries.push_back(E->key());
}
- locales.sort();
- return locales;
+ return countries;
}
-Vector<String> TranslationServer::get_all_locales() {
- Vector<String> locales;
+String TranslationServer::get_country_name(const String &p_country) const {
+ return country_name_map[p_country];
+}
- const char **ptr = locale_list;
+void TranslationServer::set_locale(const String &p_locale) {
+ locale = standardize_locale(p_locale);
- while (*ptr) {
- locales.push_back(*ptr);
- ptr++;
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
- return locales;
+ ResourceLoader::reload_translation_remaps();
}
-Vector<String> TranslationServer::get_all_locale_names() {
- Vector<String> locales;
+String TranslationServer::get_locale() const {
+ return locale;
+}
- const char **ptr = locale_names;
+Array TranslationServer::get_loaded_locales() const {
+ Array locales;
+ for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) {
+ const Ref<Translation> &t = E->get();
+ ERR_FAIL_COND_V(t.is_null(), Array());
+ String l = t->get_locale();
- while (*ptr) {
- locales.push_back(String::utf8(*ptr));
- ptr++;
+ locales.push_back(l);
}
return locales;
@@ -1137,23 +536,20 @@ void TranslationServer::remove_translation(const Ref<Translation> &p_translation
Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) {
Ref<Translation> res;
- String lang = get_language_code(p_locale);
- bool near_match_found = false;
+ int best_score = 0;
for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) {
const Ref<Translation> &t = E->get();
ERR_FAIL_COND_V(t.is_null(), nullptr);
String l = t->get_locale();
- // Exact match.
- if (l == p_locale) {
- return t;
- }
-
- // If near match found, keep that match, but keep looking to try to look for perfect match.
- if (get_language_code(l) == lang && !near_match_found) {
+ int score = compare_locales(p_locale, l);
+ if (score > 0 && score >= best_score) {
res = t;
- near_match_found = true;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
}
}
return res;
@@ -1170,8 +566,6 @@ StringName TranslationServer::translate(const StringName &p_message, const Strin
return p_message;
}
- ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid.");
-
StringName res = _get_message_from_translations(p_message, p_context, locale, false);
if (!res && fallback.length() >= 2) {
@@ -1193,8 +587,6 @@ StringName TranslationServer::translate_plural(const StringName &p_message, cons
return p_message_plural;
}
- ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid.");
-
StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n);
if (!res && fallback.length() >= 2) {
@@ -1212,51 +604,30 @@ StringName TranslationServer::translate_plural(const StringName &p_message, cons
}
StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const {
- // Locale can be of the form 'll_CC', i.e. language code and regional code,
- // e.g. 'en_US', 'en_GB', etc. It might also be simply 'll', e.g. 'en'.
- // To find the relevant translation, we look for those with locale starting
- // with the language code, and then if any is an exact match for the long
- // form. If not found, we fall back to a near match (another locale with
- // same language code).
-
- // Note: ResourceLoader::_path_remap reproduces this locale near matching
- // logic, so be sure to propagate changes there when changing things here.
-
StringName res;
- String lang = get_language_code(p_locale);
- bool near_match = false;
+ int best_score = 0;
for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) {
const Ref<Translation> &t = E->get();
ERR_FAIL_COND_V(t.is_null(), p_message);
String l = t->get_locale();
- bool exact_match = (l == p_locale);
- if (!exact_match) {
- if (near_match) {
- continue; // Only near-match once, but keep looking for exact matches.
+ int score = compare_locales(p_locale, l);
+ if (score > 0 && score >= best_score) {
+ StringName r;
+ if (!plural) {
+ r = t->get_message(p_message, p_context);
+ } else {
+ r = t->get_plural_message(p_message, p_message_plural, p_n, p_context);
}
- if (get_language_code(l) != lang) {
- continue; // Language code does not match.
+ if (!r) {
+ continue;
+ }
+ res = r;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
}
- }
-
- StringName r;
- if (!plural) {
- r = t->get_message(p_message, p_context);
- } else {
- r = t->get_plural_message(p_message, p_message_plural, p_n, p_context);
- }
-
- if (!r) {
- continue;
- }
- res = r;
-
- if (exact_match) {
- break;
- } else {
- near_match = true;
}
}
@@ -1308,18 +679,7 @@ void TranslationServer::setup() {
pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true);
#ifdef TOOLS_ENABLED
- {
- String options = "";
- int idx = 0;
- while (locale_list[idx]) {
- if (idx > 0) {
- options += ",";
- }
- options += locale_list[idx];
- idx++;
- }
- ProjectSettings::get_singleton()->set_custom_property_info("internationalization/locale/fallback", PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_ENUM, options));
- }
+ ProjectSettings::get_singleton()->set_custom_property_info("internationalization/locale/fallback", PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, ""));
#endif
}
@@ -1574,6 +934,18 @@ void TranslationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale);
ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale);
+ ClassDB::bind_method(D_METHOD("compare_locales", "locale_a", "locale_b"), &TranslationServer::compare_locales);
+ ClassDB::bind_method(D_METHOD("standardize_locale", "locale"), &TranslationServer::standardize_locale);
+
+ ClassDB::bind_method(D_METHOD("get_all_languages"), &TranslationServer::get_all_languages);
+ ClassDB::bind_method(D_METHOD("get_language_name", "language"), &TranslationServer::get_language_name);
+
+ ClassDB::bind_method(D_METHOD("get_all_scripts"), &TranslationServer::get_all_scripts);
+ ClassDB::bind_method(D_METHOD("get_script_name", "script"), &TranslationServer::get_script_name);
+
+ ClassDB::bind_method(D_METHOD("get_all_countries"), &TranslationServer::get_all_countries);
+ ClassDB::bind_method(D_METHOD("get_country_name", "country"), &TranslationServer::get_country_name);
+
ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name);
ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(""));
@@ -1606,8 +978,5 @@ void TranslationServer::load_translations() {
TranslationServer::TranslationServer() {
singleton = this;
-
- for (int i = 0; locale_list[i]; ++i) {
- locale_name_map.insert(locale_list[i], String::utf8(locale_names[i]));
- }
+ init_locale_info();
}
diff --git a/core/string/translation.h b/core/string/translation.h
index e95d15e2ab..947ca4c6d8 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -78,8 +78,6 @@ class TranslationServer : public Object {
Ref<Translation> tool_translation;
Ref<Translation> doc_translation;
- Map<String, String> locale_name_map;
-
bool enabled = true;
bool pseudolocalization_enabled = false;
@@ -109,6 +107,23 @@ class TranslationServer : public Object {
static void _bind_methods();
+ struct LocaleScriptInfo {
+ String name;
+ String script;
+ String default_country;
+ Set<String> supported_countries;
+ };
+ static Vector<LocaleScriptInfo> locale_script_info;
+
+ static Map<String, String> language_map;
+ static Map<String, String> script_map;
+ static Map<String, String> locale_rename_map;
+ static Map<String, String> country_name_map;
+ static Map<String, String> country_rename_map;
+ static Map<String, String> variant_map;
+
+ void init_locale_info();
+
public:
_FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; }
@@ -119,6 +134,15 @@ public:
String get_locale() const;
Ref<Translation> get_translation_object(const String &p_locale);
+ Vector<String> get_all_languages() const;
+ String get_language_name(const String &p_language) const;
+
+ Vector<String> get_all_scripts() const;
+ String get_script_name(const String &p_script) const;
+
+ Vector<String> get_all_countries() const;
+ String get_country_name(const String &p_country) const;
+
String get_locale_name(const String &p_locale) const;
Array get_loaded_locales() const;
@@ -136,11 +160,9 @@ public:
void set_editor_pseudolocalization(bool p_enabled);
void reload_pseudolocalization();
- static Vector<String> get_all_locales();
- static Vector<String> get_all_locale_names();
- static bool is_locale_valid(const String &p_locale);
- static String standardize_locale(const String &p_locale);
- static String get_language_code(const String &p_locale);
+ String standardize_locale(const String &p_locale) const;
+
+ int compare_locales(const String &p_locale_a, const String &p_locale_b) const;
String get_tool_locale();
void set_tool_translation(const Ref<Translation> &p_translation);
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 0949980713..6e0a7c7022 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -1527,115 +1527,24 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) {
}
String String::num_real(double p_num, bool p_trailing) {
- if (Math::is_nan(p_num)) {
- return "nan";
- }
-
- if (Math::is_inf(p_num)) {
- if (signbit(p_num)) {
- return "-inf";
+ if (p_num == (double)(int64_t)p_num) {
+ if (p_trailing) {
+ return num_int64((int64_t)p_num) + ".0";
} else {
- return "inf";
+ return num_int64((int64_t)p_num);
}
}
-
- String s;
- String sd;
-
- // Integer part.
-
- bool neg = p_num < 0;
- p_num = ABS(p_num);
- int64_t intn = (int64_t)p_num;
-
- // Decimal part.
-
- if (intn != p_num) {
- double dec = p_num - (double)intn;
-
- int digit = 0;
-
#ifdef REAL_T_IS_DOUBLE
- int decimals = 14;
- double tolerance = 1e-14;
+ int decimals = 14;
#else
- int decimals = 6;
- double tolerance = 1e-6;
+ int decimals = 6;
#endif
- // We want to align the digits to the above sane default, so we only
- // need to subtract log10 for numbers with a positive power of ten.
- if (p_num > 10) {
- decimals -= (int)floor(log10(p_num));
- }
-
- if (decimals > MAX_DECIMALS) {
- decimals = MAX_DECIMALS;
- }
-
- // In case the value ends up ending in "99999", we want to add a
- // tiny bit to the value we're checking when deciding when to stop,
- // so we multiply by slightly above 1 (1 + 1e-7 or 1e-15).
- double check_multiplier = 1 + tolerance / 10;
-
- int64_t dec_int = 0;
- int64_t dec_max = 0;
-
- while (true) {
- dec *= 10.0;
- dec_int = dec_int * 10 + (int64_t)dec % 10;
- dec_max = dec_max * 10 + 9;
- digit++;
-
- if ((dec - (double)(int64_t)(dec * check_multiplier)) < tolerance) {
- break;
- }
-
- if (digit == decimals) {
- break;
- }
- }
-
- dec *= 10;
- int last = (int64_t)dec % 10;
-
- if (last > 5) {
- if (dec_int == dec_max) {
- dec_int = 0;
- intn++;
- } else {
- dec_int++;
- }
- }
-
- String decimal;
- for (int i = 0; i < digit; i++) {
- char num[2] = { 0, 0 };
- num[0] = '0' + dec_int % 10;
- decimal = num + decimal;
- dec_int /= 10;
- }
- sd = '.' + decimal;
- } else if (p_trailing) {
- sd = ".0";
- } else {
- sd = "";
- }
-
- if (intn == 0) {
- s = "0";
- } else {
- while (intn) {
- char32_t num = '0' + (intn % 10);
- intn /= 10;
- s = num + s;
- }
- }
-
- s = s + sd;
- if (neg) {
- s = "-" + s;
+ // We want to align the digits to the above sane default, so we only
+ // need to subtract log10 for numbers with a positive power of ten.
+ if (p_num > 10) {
+ decimals -= (int)floor(log10(p_num));
}
- return s;
+ return num(p_num, decimals);
}
String String::num_scientific(double p_num) {
@@ -1723,7 +1632,7 @@ String String::utf8(const char *p_utf8, int p_len) {
}
bool String::parse_utf8(const char *p_utf8, int p_len) {
-#define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?");
+#define UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-8?");
if (!p_utf8) {
return true;
@@ -1764,12 +1673,12 @@ bool String::parse_utf8(const char *p_utf8, int p_len) {
} else if ((c & 0xf8) == 0xf0) {
skip = 3;
} else {
- _UNICERROR("invalid skip at " + num_int64(cstr_size));
+ UNICERROR("invalid skip at " + num_int64(cstr_size));
return true; //invalid utf8
}
if (skip == 1 && (c & 0x1e) == 0) {
- _UNICERROR("overlong rejected at " + num_int64(cstr_size));
+ UNICERROR("overlong rejected at " + num_int64(cstr_size));
return true; //reject overlong
}
@@ -1784,7 +1693,7 @@ bool String::parse_utf8(const char *p_utf8, int p_len) {
}
if (skip) {
- _UNICERROR("no space left");
+ UNICERROR("no space left");
return true; //not enough space
}
}
@@ -1811,17 +1720,17 @@ bool String::parse_utf8(const char *p_utf8, int p_len) {
} else if ((*p_utf8 & 0xf8) == 0xf0) {
len = 4;
} else {
- _UNICERROR("invalid len");
+ UNICERROR("invalid len");
return true; //invalid UTF8
}
if (len > cstr_size) {
- _UNICERROR("no space left");
+ UNICERROR("no space left");
return true; //not enough space
}
if (len == 2 && (*p_utf8 & 0x1E) == 0) {
- _UNICERROR("no space left");
+ UNICERROR("no space left");
return true; //reject overlong
}
@@ -1836,18 +1745,18 @@ bool String::parse_utf8(const char *p_utf8, int p_len) {
for (int i = 1; i < len; i++) {
if ((p_utf8[i] & 0xc0) != 0x80) {
- _UNICERROR("invalid utf8");
+ UNICERROR("invalid utf8");
return true; //invalid utf8
}
if (unichar == 0 && i == 2 && ((p_utf8[i] & 0x7f) >> (7 - len)) == 0) {
- _UNICERROR("invalid utf8 overlong");
+ UNICERROR("invalid utf8 overlong");
return true; //no overlong
}
unichar = (unichar << 6) | (p_utf8[i] & 0x3f);
}
}
if (unichar >= 0xd800 && unichar <= 0xdfff) {
- _UNICERROR("invalid code point");
+ UNICERROR("invalid code point");
return CharString();
}
@@ -1857,7 +1766,7 @@ bool String::parse_utf8(const char *p_utf8, int p_len) {
}
return false;
-#undef _UNICERROR
+#undef UNICERROR
}
CharString String::utf8() const {
@@ -1931,7 +1840,7 @@ String String::utf16(const char16_t *p_utf16, int p_len) {
}
bool String::parse_utf16(const char16_t *p_utf16, int p_len) {
-#define _UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-16?");
+#define UNICERROR(m_err) print_error("Unicode parsing error: " + String(m_err) + ". Is the string valid UTF-16?");
if (!p_utf16) {
return true;
@@ -1971,7 +1880,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) {
if ((c & 0xfffffc00) == 0xd800) {
skip = 1; // lead surrogate
} else if ((c & 0xfffffc00) == 0xdc00) {
- _UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size));
+ UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size));
return true; // invalid UTF16
} else {
skip = 0;
@@ -1981,7 +1890,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) {
if ((c & 0xfffffc00) == 0xdc00) { // trail surrogate
--skip;
} else {
- _UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size));
+ UNICERROR("invalid utf16 surrogate at " + num_int64(cstr_size));
return true; // invalid UTF16
}
}
@@ -1991,7 +1900,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) {
}
if (skip) {
- _UNICERROR("no space left");
+ UNICERROR("no space left");
return true; // not enough space
}
}
@@ -2016,7 +1925,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) {
}
if (len > cstr_size) {
- _UNICERROR("no space left");
+ UNICERROR("no space left");
return true; //not enough space
}
@@ -2034,7 +1943,7 @@ bool String::parse_utf16(const char16_t *p_utf16, int p_len) {
}
return false;
-#undef _UNICERROR
+#undef UNICERROR
}
Char16String String::utf16() const {
@@ -3171,7 +3080,7 @@ bool String::is_subsequence_of(const String &p_string) const {
return _base_is_subsequence_of(p_string, false);
}
-bool String::is_subsequence_ofi(const String &p_string) const {
+bool String::is_subsequence_ofn(const String &p_string) const {
return _base_is_subsequence_of(p_string, true);
}
@@ -3649,6 +3558,10 @@ String String::rstrip(const String &p_chars) const {
return substr(0, end + 1);
}
+bool String::is_network_share_path() const {
+ return begins_with("//") || begins_with("\\\\");
+}
+
String String::simplify_path() const {
String s = *this;
String drive;
@@ -3661,6 +3574,9 @@ String String::simplify_path() const {
} else if (s.begins_with("user://")) {
drive = "user://";
s = s.substr(7, s.length());
+ } else if (is_network_share_path()) {
+ drive = s.substr(0, 2);
+ s = s.substr(2, s.length() - 2);
} else if (s.begins_with("/") || s.begins_with("\\")) {
drive = s.substr(0, 1);
s = s.substr(1, s.length() - 1);
@@ -4362,13 +4278,13 @@ bool String::is_relative_path() const {
String String::get_base_dir() const {
int end = 0;
- // url scheme style base
+ // URL scheme style base.
int basepos = find("://");
if (basepos != -1) {
end = basepos + 3;
}
- // windows top level directory base
+ // Windows top level directory base.
if (end == 0) {
basepos = find(":/");
if (basepos == -1) {
@@ -4379,7 +4295,24 @@ String String::get_base_dir() const {
}
}
- // unix root directory base
+ // Windows UNC network share path.
+ if (end == 0) {
+ if (is_network_share_path()) {
+ basepos = find("/", 2);
+ if (basepos == -1) {
+ basepos = find("\\", 2);
+ }
+ int servpos = find("/", basepos + 1);
+ if (servpos == -1) {
+ servpos = find("\\", basepos + 1);
+ }
+ if (servpos != -1) {
+ end = servpos + 1;
+ }
+ }
+ }
+
+ // Unix root directory base.
if (end == 0) {
if (begins_with("/")) {
end = 1;
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 4840c236c0..6df87280a4 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -285,7 +285,7 @@ public:
bool ends_with(const String &p_string) const;
bool is_enclosed_in(const String &p_string) const;
bool is_subsequence_of(const String &p_string) const;
- bool is_subsequence_ofi(const String &p_string) const;
+ bool is_subsequence_ofn(const String &p_string) const;
bool is_quoted() const;
Vector<String> bigrams() const;
float similarity(const String &p_string) const;
@@ -405,6 +405,7 @@ public:
String get_file() const;
static String humanize_size(uint64_t p_size);
String simplify_path() const;
+ bool is_network_share_path() const;
String xml_escape(bool p_escape_quotes = false) const;
String xml_unescape() const;
diff --git a/core/templates/vector.h b/core/templates/vector.h
index e53c502f67..0877e04e01 100644
--- a/core/templates/vector.h
+++ b/core/templates/vector.h
@@ -42,6 +42,7 @@
#include "core/templates/search_array.h"
#include "core/templates/sort_array.h"
+#include <climits>
#include <initializer_list>
template <class T>
@@ -94,9 +95,7 @@ public:
void append_array(Vector<T> p_other);
- bool has(const T &p_val) const {
- return find(p_val, 0) != -1;
- }
+ _FORCE_INLINE_ bool has(const T &p_val) const { return find(p_val) != -1; }
template <class C>
void sort_custom() {
@@ -144,25 +143,29 @@ public:
return ret;
}
- Vector<T> slice(int p_begin, int p_end) const {
+ Vector<T> slice(int p_begin, int p_end = INT_MAX) const {
Vector<T> result;
- if (p_end < 0) {
- p_end += size() + 1;
- }
+ const int s = size();
- ERR_FAIL_INDEX_V(p_begin, size(), result);
- ERR_FAIL_INDEX_V(p_end, size() + 1, result);
+ int begin = CLAMP(p_begin, -s, s);
+ if (begin < 0) {
+ begin += s;
+ }
+ int end = CLAMP(p_end, -s, s);
+ if (end < 0) {
+ end += s;
+ }
- ERR_FAIL_COND_V(p_begin > p_end, result);
+ ERR_FAIL_COND_V(begin > end, result);
- int result_size = p_end - p_begin;
+ int result_size = end - begin;
result.resize(result_size);
const T *const r = ptr();
T *const w = result.ptrw();
for (int i = 0; i < result_size; ++i) {
- w[i] = r[p_begin + i];
+ w[i] = r[begin + i];
}
return result;
diff --git a/core/typedefs.h b/core/typedefs.h
index e6034eb375..5929b5123b 100644
--- a/core/typedefs.h
+++ b/core/typedefs.h
@@ -71,6 +71,17 @@
#endif
#endif
+// No discard allows the compiler to flag warnings if we don't use the return value of functions / classes
+#ifndef _NO_DISCARD_
+#define _NO_DISCARD_ [[nodiscard]]
+#endif
+
+// In some cases _NO_DISCARD_ will get false positives,
+// we can prevent the warning in specific cases by preceding the call with a cast.
+#ifndef _ALLOW_DISCARD_
+#define _ALLOW_DISCARD_ (void)
+#endif
+
// Windows badly defines a lot of stuff we'll never use. Undefine it.
#ifdef _WIN32
#undef min // override standard definition
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 8d20b1bc79..1b39558dff 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -370,20 +370,24 @@ Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const {
ERR_FAIL_COND_V_MSG(p_step == 0, result, "Slice step cannot be zero.");
- if (p_end < 0) {
- p_end += size() + 1;
- }
+ const int s = size();
- ERR_FAIL_INDEX_V(p_begin, size(), result);
- ERR_FAIL_INDEX_V(p_end, size() + 1, result);
+ int begin = CLAMP(p_begin, -s, s);
+ if (begin < 0) {
+ begin += s;
+ }
+ int end = CLAMP(p_end, -s, s);
+ if (end < 0) {
+ end += s;
+ }
- ERR_FAIL_COND_V_MSG(p_step > 0 && p_begin > p_end, result, "Slice is positive, but bounds is decreasing");
- ERR_FAIL_COND_V_MSG(p_step < 0 && p_begin < p_end, result, "Slice is negative, but bounds is increasing");
+ ERR_FAIL_COND_V_MSG(p_step > 0 && begin > end, result, "Slice is positive, but bounds is decreasing.");
+ ERR_FAIL_COND_V_MSG(p_step < 0 && begin < end, result, "Slice is negative, but bounds is increasing.");
- int result_size = (p_end - p_begin) / p_step;
+ int result_size = (end - begin) / p_step;
result.resize(result_size);
- for (int src_idx = p_begin, dest_idx = 0; dest_idx < result_size; ++dest_idx) {
+ for (int src_idx = begin, dest_idx = 0; dest_idx < result_size; ++dest_idx) {
result[dest_idx] = p_deep ? get(src_idx).duplicate(true) : get(src_idx);
src_idx += p_step;
}
@@ -627,7 +631,7 @@ Variant Array::max() const {
}
const void *Array::id() const {
- return _p->array.ptr();
+ return _p;
}
Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_name, const Variant &p_script) {
diff --git a/core/variant/array.h b/core/variant/array.h
index f48444bb39..72bed5932c 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -33,6 +33,8 @@
#include "core/typedefs.h"
+#include <climits>
+
class Variant;
class ArrayPrivate;
class Object;
@@ -102,7 +104,7 @@ public:
Array duplicate(bool p_deep = false) const;
Array recursive_duplicate(bool p_deep, int recursion_count) const;
- Array slice(int p_begin, int p_end, int p_step = 1, bool p_deep = false) const;
+ Array slice(int p_begin, int p_end = INT_MAX, int p_step = 1, bool p_deep = false) const;
Array filter(const Callable &p_callable) const;
Array map(const Callable &p_callable) const;
Variant reduce(const Callable &p_callable, const Variant &p_accum) const;
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index 14f49d530c..b6fdb4d902 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -44,24 +44,42 @@
#include <stdio.h>
+// Variant cannot define an implicit cast operator for every Object subclass, so the
+// casting is done here, to allow binding methods with parameters more specific than Object *
+
template <class T>
struct VariantCaster {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
- return p_variant;
+ using TStripped = std::remove_pointer_t<T>;
+ if constexpr (std::is_base_of<Object, TStripped>::value) {
+ return Object::cast_to<TStripped>(p_variant);
+ } else {
+ return p_variant;
+ }
}
};
template <class T>
struct VariantCaster<T &> {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
- return p_variant;
+ using TStripped = std::remove_pointer_t<T>;
+ if constexpr (std::is_base_of<Object, TStripped>::value) {
+ return Object::cast_to<TStripped>(p_variant);
+ } else {
+ return p_variant;
+ }
}
};
template <class T>
struct VariantCaster<const T &> {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
- return p_variant;
+ using TStripped = std::remove_pointer_t<T>;
+ if constexpr (std::is_base_of<Object, TStripped>::value) {
+ return Object::cast_to<TStripped>(p_variant);
+ } else {
+ return p_variant;
+ }
}
};
@@ -135,7 +153,13 @@ struct PtrToArg<char32_t> {
template <typename T>
struct VariantObjectClassChecker {
static _FORCE_INLINE_ bool check(const Variant &p_variant) {
- return true;
+ using TStripped = std::remove_pointer_t<T>;
+ if constexpr (std::is_base_of<Object, TStripped>::value) {
+ Object *obj = p_variant;
+ return Object::cast_to<TStripped>(p_variant) || !obj;
+ } else {
+ return true;
+ }
}
};
@@ -151,24 +175,6 @@ struct VariantObjectClassChecker<const Ref<T> &> {
}
};
-template <>
-struct VariantObjectClassChecker<Node *> {
- static _FORCE_INLINE_ bool check(const Variant &p_variant) {
- Object *obj = p_variant;
- Node *node = p_variant;
- return node || !obj;
- }
-};
-
-template <>
-struct VariantObjectClassChecker<Control *> {
- static _FORCE_INLINE_ bool check(const Variant &p_variant) {
- Object *obj = p_variant;
- Control *control = p_variant;
- return control || !obj;
- }
-};
-
#ifdef DEBUG_METHODS_ENABLED
template <class T>
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index cc04ae712b..0f2f8fc8ed 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -350,7 +350,7 @@ void Dictionary::operator=(const Dictionary &p_dictionary) {
}
const void *Dictionary::id() const {
- return _p->variant_map.id();
+ return _p;
}
Dictionary::Dictionary(const Dictionary &p_from) {
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index db198da54a..fcfa530388 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -38,8 +38,6 @@
#include "core/math/math_funcs.h"
#include "core/string/print_string.h"
#include "core/variant/variant_parser.h"
-#include "scene/gui/control.h"
-#include "scene/main/node.h"
String Variant::get_type_name(Variant::Type p_type) {
switch (p_type) {
@@ -1692,8 +1690,6 @@ String Variant::stringify(int recursion_count) const {
pairs.push_back(sp);
}
- pairs.sort();
-
for (int i = 0; i < pairs.size(); i++) {
if (i > 0) {
str += ", ";
@@ -2006,22 +2002,6 @@ Object *Variant::get_validated_object() const {
}
}
-Variant::operator Node *() const {
- if (type == OBJECT) {
- return Object::cast_to<Node>(_get_obj().obj);
- } else {
- return nullptr;
- }
-}
-
-Variant::operator Control *() const {
- if (type == OBJECT) {
- return Object::cast_to<Control>(_get_obj().obj);
- } else {
- return nullptr;
- }
-}
-
Variant::operator Dictionary() const {
if (type == DICTIONARY) {
return *reinterpret_cast<const Dictionary *>(_data._mem);
@@ -3256,7 +3236,7 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const
return false;
}
-bool Variant::is_ref() const {
+bool Variant::is_ref_counted() const {
return type == OBJECT && _get_obj().id.is_ref_counted();
}
@@ -3416,7 +3396,7 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method,
}
String class_name = p_base->get_class();
- Ref<Script> script = p_base->get_script();
+ Ref<Resource> script = p_base->get_script();
if (script.is_valid() && script->get_path().is_resource_file()) {
class_name += "(" + script->get_path().get_file() + ")";
}
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 0860e7fdc3..36fa755647 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -53,8 +53,6 @@
#include "core/variant/dictionary.h"
class Object;
-class Node; // helper
-class Control; // helper
struct PropertyInfo;
struct MethodInfo;
@@ -287,7 +285,7 @@ public:
static bool can_convert(Type p_type_from, Type p_type_to);
static bool can_convert_strict(Type p_type_from, Type p_type_to);
- bool is_ref() const;
+ bool is_ref_counted() const;
_FORCE_INLINE_ bool is_num() const {
return type == INT || type == FLOAT;
}
@@ -339,8 +337,6 @@ public:
operator ::RID() const;
operator Object *() const;
- operator Node *() const;
- operator Control *() const;
operator Callable() const;
operator Signal() const;
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index ecf5009fb6..aecc6e9a26 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1382,7 +1382,7 @@ static void _register_variant_builtin_methods() {
bind_methodv(String, begins_with, static_cast<bool (String::*)(const String &) const>(&String::begins_with), sarray("text"), varray());
bind_method(String, ends_with, sarray("text"), varray());
bind_method(String, is_subsequence_of, sarray("text"), varray());
- bind_method(String, is_subsequence_ofi, sarray("text"), varray());
+ bind_method(String, is_subsequence_ofn, sarray("text"), varray());
bind_method(String, bigrams, sarray(), varray());
bind_method(String, similarity, sarray("text"), varray());
@@ -1789,6 +1789,7 @@ static void _register_variant_builtin_methods() {
bind_method(Transform3D, scaled, sarray("scale"), varray());
bind_method(Transform3D, translated, sarray("offset"), varray());
bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
+ bind_method(Transform3D, sphere_interpolate_with, sarray("xform", "weight"), varray());
bind_method(Transform3D, interpolate_with, sarray("xform", "weight"), varray());
bind_method(Transform3D, is_equal_approx, sarray("xform"), varray());
@@ -1838,7 +1839,7 @@ static void _register_variant_builtin_methods() {
bind_method(Array, bsearch_custom, sarray("value", "func", "before"), varray(true));
bind_method(Array, reverse, sarray(), varray());
bind_method(Array, duplicate, sarray("deep"), varray(false));
- bind_method(Array, slice, sarray("begin", "end", "step", "deep"), varray(1, false));
+ bind_method(Array, slice, sarray("begin", "end", "step", "deep"), varray(INT_MAX, 1, false));
bind_method(Array, filter, sarray("method"), varray());
bind_method(Array, map, sarray("method"), varray());
bind_method(Array, reduce, sarray("method", "accum"), varray(Variant()));
@@ -1858,7 +1859,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedByteArray, resize, sarray("new_size"), varray());
bind_method(PackedByteArray, has, sarray("value"), varray());
bind_method(PackedByteArray, reverse, sarray(), varray());
- bind_method(PackedByteArray, slice, sarray("begin", "end"), varray());
+ bind_method(PackedByteArray, slice, sarray("begin", "end"), varray(INT_MAX));
bind_method(PackedByteArray, sort, sarray(), varray());
bind_method(PackedByteArray, bsearch, sarray("value", "before"), varray(true));
bind_method(PackedByteArray, duplicate, sarray(), varray());
@@ -1919,7 +1920,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedInt32Array, resize, sarray("new_size"), varray());
bind_method(PackedInt32Array, has, sarray("value"), varray());
bind_method(PackedInt32Array, reverse, sarray(), varray());
- bind_method(PackedInt32Array, slice, sarray("begin", "end"), varray());
+ bind_method(PackedInt32Array, slice, sarray("begin", "end"), varray(INT_MAX));
bind_method(PackedInt32Array, to_byte_array, sarray(), varray());
bind_method(PackedInt32Array, sort, sarray(), varray());
bind_method(PackedInt32Array, bsearch, sarray("value", "before"), varray(true));
@@ -1939,7 +1940,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedInt64Array, resize, sarray("new_size"), varray());
bind_method(PackedInt64Array, has, sarray("value"), varray());
bind_method(PackedInt64Array, reverse, sarray(), varray());
- bind_method(PackedInt64Array, slice, sarray("begin", "end"), varray());
+ bind_method(PackedInt64Array, slice, sarray("begin", "end"), varray(INT_MAX));
bind_method(PackedInt64Array, to_byte_array, sarray(), varray());
bind_method(PackedInt64Array, sort, sarray(), varray());
bind_method(PackedInt64Array, bsearch, sarray("value", "before"), varray(true));
@@ -1959,7 +1960,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedFloat32Array, resize, sarray("new_size"), varray());
bind_method(PackedFloat32Array, has, sarray("value"), varray());
bind_method(PackedFloat32Array, reverse, sarray(), varray());
- bind_method(PackedFloat32Array, slice, sarray("begin", "end"), varray());
+ bind_method(PackedFloat32Array, slice, sarray("begin", "end"), varray(INT_MAX));
bind_method(PackedFloat32Array, to_byte_array, sarray(), varray());
bind_method(PackedFloat32Array, sort, sarray(), varray());
bind_method(PackedFloat32Array, bsearch, sarray("value", "before"), varray(true));
@@ -1979,7 +1980,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedFloat64Array, resize, sarray("new_size"), varray());
bind_method(PackedFloat64Array, has, sarray("value"), varray());
bind_method(PackedFloat64Array, reverse, sarray(), varray());
- bind_method(PackedFloat64Array, slice, sarray("begin", "end"), varray());
+ bind_method(PackedFloat64Array, slice, sarray("begin", "end"), varray(INT_MAX));
bind_method(PackedFloat64Array, to_byte_array, sarray(), varray());
bind_method(PackedFloat64Array, sort, sarray(), varray());
bind_method(PackedFloat64Array, bsearch, sarray("value", "before"), varray(true));
@@ -1999,7 +2000,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedStringArray, resize, sarray("new_size"), varray());
bind_method(PackedStringArray, has, sarray("value"), varray());
bind_method(PackedStringArray, reverse, sarray(), varray());
- bind_method(PackedStringArray, slice, sarray("begin", "end"), varray());
+ bind_method(PackedStringArray, slice, sarray("begin", "end"), varray(INT_MAX));
bind_method(PackedStringArray, to_byte_array, sarray(), varray());
bind_method(PackedStringArray, sort, sarray(), varray());
bind_method(PackedStringArray, bsearch, sarray("value", "before"), varray(true));
@@ -2019,7 +2020,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector2Array, resize, sarray("new_size"), varray());
bind_method(PackedVector2Array, has, sarray("value"), varray());
bind_method(PackedVector2Array, reverse, sarray(), varray());
- bind_method(PackedVector2Array, slice, sarray("begin", "end"), varray());
+ bind_method(PackedVector2Array, slice, sarray("begin", "end"), varray(INT_MAX));
bind_method(PackedVector2Array, to_byte_array, sarray(), varray());
bind_method(PackedVector2Array, sort, sarray(), varray());
bind_method(PackedVector2Array, bsearch, sarray("value", "before"), varray(true));
@@ -2039,7 +2040,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector3Array, resize, sarray("new_size"), varray());
bind_method(PackedVector3Array, has, sarray("value"), varray());
bind_method(PackedVector3Array, reverse, sarray(), varray());
- bind_method(PackedVector3Array, slice, sarray("begin", "end"), varray());
+ bind_method(PackedVector3Array, slice, sarray("begin", "end"), varray(INT_MAX));
bind_method(PackedVector3Array, to_byte_array, sarray(), varray());
bind_method(PackedVector3Array, sort, sarray(), varray());
bind_method(PackedVector3Array, bsearch, sarray("value", "before"), varray(true));
@@ -2059,7 +2060,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedColorArray, resize, sarray("new_size"), varray());
bind_method(PackedColorArray, has, sarray("value"), varray());
bind_method(PackedColorArray, reverse, sarray(), varray());
- bind_method(PackedColorArray, slice, sarray("begin", "end"), varray());
+ bind_method(PackedColorArray, slice, sarray("begin", "end"), varray(INT_MAX));
bind_method(PackedColorArray, to_byte_array, sarray(), varray());
bind_method(PackedColorArray, sort, sarray(), varray());
bind_method(PackedColorArray, bsearch, sarray("value", "before"), varray(true));
diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h
index 9791cb67b3..f72a92d31a 100644
--- a/core/variant/variant_op.h
+++ b/core/variant/variant_op.h
@@ -775,6 +775,7 @@ public:
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *r_ret = Array();
_add_arrays(*VariantGetInternalPtr<Array>::get_ptr(r_ret), *VariantGetInternalPtr<Array>::get_ptr(left), *VariantGetInternalPtr<Array>::get_ptr(right));
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 571181f665..96cdc0678e 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -217,6 +217,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
}
case '"': {
String str;
+ char32_t prev = 0;
while (true) {
char32_t ch = p_stream->get_char();
@@ -252,10 +253,13 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
case 'r':
res = 13;
break;
+ case 'U':
case 'u': {
- //hex number
- for (int j = 0; j < 4; j++) {
+ // Hexadecimal sequence.
+ int hex_len = (next == 'U') ? 6 : 4;
+ for (int j = 0; j < hex_len; j++) {
char32_t c = p_stream->get_char();
+
if (c == 0) {
r_err_str = "Unterminated String";
r_token.type = TK_ERROR;
@@ -290,15 +294,49 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
} break;
}
+ // Parse UTF-16 pair.
+ if ((res & 0xfffffc00) == 0xd800) {
+ if (prev == 0) {
+ prev = res;
+ continue;
+ } else {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ } else if ((res & 0xfffffc00) == 0xdc00) {
+ if (prev == 0) {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired trail surrogate";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ } else {
+ res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev = 0;
+ }
+ }
+ if (prev != 0) {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += res;
-
} else {
+ if (prev != 0) {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
if (ch == '\n') {
line++;
}
str += ch;
}
}
+ if (prev != 0) {
+ r_err_str = "Invalid UTF-16 sequence in string, unpaired lead surrogate";
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
if (p_stream->is_utf8()) {
str.parse_utf8(str.ascii(true).get_data());
@@ -1649,12 +1687,13 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
dict.get_key_list(&keys);
keys.sort();
+ if (keys.is_empty()) { // Avoid unnecessary line break.
+ p_store_string_func(p_store_string_ud, "{}");
+ break;
+ }
+
p_store_string_func(p_store_string_ud, "{\n");
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- /*
- if (!_check_type(dict[E->get()]))
- continue;
- */
write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count);
p_store_string_func(p_store_string_ud, ": ");
write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count);
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 3fd8eb5474..60950099d2 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -433,7 +433,7 @@ struct VariantUtilityFunctions {
static inline Variant weakref(const Variant &obj, Callable::CallError &r_error) {
if (obj.get_type() == Variant::OBJECT) {
r_error.error = Callable::CallError::CALL_OK;
- if (obj.is_ref()) {
+ if (obj.is_ref_counted()) {
Ref<WeakRef> wref = memnew(WeakRef);
REF r = obj;
if (r.is_valid()) {
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index be7ac1164e..d8c9ca08e8 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -2493,7 +2493,10 @@
</constant>
<constant name="PROPERTY_HINT_ARRAY_TYPE" value="39" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_MAX" value="41" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_LOCALE_ID" value="41" enum="PropertyHint">
+ Hints that a string property is a locale code. Editing it will show a locale dialog for picking language and country.
+ </constant>
+ <constant name="PROPERTY_HINT_MAX" value="42" enum="PropertyHint">
</constant>
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags">
</constant>
diff --git a/doc/classes/AnimationNodeOneShot.xml b/doc/classes/AnimationNodeOneShot.xml
index 116b54e39e..71ed82cf46 100644
--- a/doc/classes/AnimationNodeOneShot.xml
+++ b/doc/classes/AnimationNodeOneShot.xml
@@ -10,19 +10,6 @@
<link title="AnimationTree">$DOCS_URL/tutorials/animation/animation_tree.html</link>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
- <methods>
- <method name="get_mix_mode" qualifiers="const">
- <return type="int" enum="AnimationNodeOneShot.MixMode" />
- <description>
- </description>
- </method>
- <method name="set_mix_mode">
- <return type="void" />
- <argument index="0" name="mode" type="int" enum="AnimationNodeOneShot.MixMode" />
- <description>
- </description>
- </method>
- </methods>
<members>
<member name="autorestart" type="bool" setter="set_autorestart" getter="has_autorestart" default="false">
If [code]true[/code], the sub-animation will restart automatically after finishing.
@@ -37,6 +24,8 @@
</member>
<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.1">
</member>
+ <member name="mix_mode" type="int" setter="set_mix_mode" getter="get_mix_mode" enum="AnimationNodeOneShot.MixMode" default="0">
+ </member>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync" default="false">
</member>
</members>
diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml
index 0f7e6799be..5280b7445d 100644
--- a/doc/classes/Area2D.xml
+++ b/doc/classes/Area2D.xml
@@ -31,7 +31,7 @@
<return type="bool" />
<argument index="0" name="area" type="Node" />
<description>
- If [code]true[/code], the given area overlaps the Area2D.
+ Returns [code]true[/code] if the given [Area2D] intersects or overlaps this [Area2D], [code]false[/code] otherwise.
[b]Note:[/b] The result of this test is not immediate after moving objects. For performance, the list of overlaps is updated once per frame and before the physics step. Consider using signals instead.
</description>
</method>
@@ -39,9 +39,9 @@
<return type="bool" />
<argument index="0" name="body" type="Node" />
<description>
- If [code]true[/code], the given physics body overlaps the Area2D.
+ Returns [code]true[/code] if the given physics body intersects or overlaps this [Area2D], [code]false[/code] otherwise.
[b]Note:[/b] The result of this test is not immediate after moving objects. For performance, list of overlaps is updated once per frame and before the physics step. Consider using signals instead.
- The [code]body[/code] argument can either be a [PhysicsBody2D] or a [TileMap] instance (while TileMaps are not physics bodies themselves, they register their tiles with collision shapes as a virtual physics body).
+ The [code]body[/code] argument can either be a [PhysicsBody2D] or a [TileMap] instance. While TileMaps are not physics bodies themselves, they register their tiles with collision shapes as a virtual physics body.
</description>
</method>
</methods>
diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml
index 450ed44307..4f456cb56d 100644
--- a/doc/classes/Area3D.xml
+++ b/doc/classes/Area3D.xml
@@ -29,7 +29,7 @@
<return type="bool" />
<argument index="0" name="area" type="Node" />
<description>
- If [code]true[/code], the given area overlaps the Area3D.
+ Returns [code]true[/code] if the given [Area3D] intersects or overlaps this [Area3D], [code]false[/code] otherwise.
[b]Note:[/b] The result of this test is not immediate after moving objects. For performance, list of overlaps is updated once per frame and before the physics step. Consider using signals instead.
</description>
</method>
@@ -37,9 +37,9 @@
<return type="bool" />
<argument index="0" name="body" type="Node" />
<description>
- If [code]true[/code], the given physics body overlaps the Area3D.
+ Returns [code]true[/code] if the given physics body intersects or overlaps this [Area3D], [code]false[/code] otherwise.
[b]Note:[/b] The result of this test is not immediate after moving objects. For performance, list of overlaps is updated once per frame and before the physics step. Consider using signals instead.
- The [code]body[/code] argument can either be a [PhysicsBody3D] or a [GridMap] instance (while GridMaps are not physics body themselves, they register their tiles with collision shapes as a virtual physics body).
+ The [code]body[/code] argument can either be a [PhysicsBody3D] or a [GridMap] instance. While GridMaps are not physics body themselves, they register their tiles with collision shapes as a virtual physics body.
</description>
</method>
</methods>
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 5b1861bc9a..57f51c7ccf 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -445,13 +445,14 @@
<method name="slice" qualifiers="const">
<return type="Array" />
<argument index="0" name="begin" type="int" />
- <argument index="1" name="end" type="int" />
+ <argument index="1" name="end" type="int" default="2147483647" />
<argument index="2" name="step" type="int" default="1" />
<argument index="3" name="deep" type="bool" default="false" />
<description>
Returns the slice of the [Array], from [code]begin[/code] (inclusive) to [code]end[/code] (exclusive), as a new [Array].
- If [code]end[/code] is negative, it will be relative to the end of the array.
- If specified, [code]step[/code] is the relative index between source elements.
+ The absolute value of [code]begin[/code] and [code]end[/code] will be clamped to the array size, so the default value for [code]end[/code] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+ If either [code]begin[/code] or [code]end[/code] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
+ If specified, [code]step[/code] is the relative index between source elements. It can be negative, then [code]begin[/code] must be higher than [code]end[/code]. For example, [code][0, 1, 2, 3, 4, 5].slice(5, 1, -2)[/code] returns [code][5, 3][/code]).
If [code]deep[/code] is true, each element will be copied by value rather than by reference.
</description>
</method>
@@ -470,6 +471,12 @@
// There is no sort support for Godot.Collections.Array
[/csharp]
[/codeblocks]
+ To perform natural order sorting, you can use [method sort_custom] with [method String.naturalnocasecmp_to] as follows:
+ [codeblock]
+ var strings = ["string1", "string2", "string10", "string11"]
+ strings.sort_custom(func(a, b): return a.naturalnocasecmp_to(b) &lt; 0)
+ print(strings) # Prints [string1, string2, string10, string11]
+ [/codeblock]
</description>
</method>
<method name="sort_custom">
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index c986947dfb..fa980d9f75 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -62,8 +62,7 @@
<argument index="0" name="primitive" type="int" enum="Mesh.PrimitiveType" />
<argument index="1" name="arrays" type="Array" />
<argument index="2" name="blend_shapes" type="Array" default="[]" />
- <argument index="3" name="lods" type="Dictionary" default="{
-}" />
+ <argument index="3" name="lods" type="Dictionary" default="{}" />
<argument index="4" name="compress_flags" type="int" default="0" />
<description>
Creates a new surface.
diff --git a/doc/classes/AudioEffectSpectrumAnalyzer.xml b/doc/classes/AudioEffectSpectrumAnalyzer.xml
index b2f2c55aa2..50e38d5d1e 100644
--- a/doc/classes/AudioEffectSpectrumAnalyzer.xml
+++ b/doc/classes/AudioEffectSpectrumAnalyzer.xml
@@ -8,8 +8,8 @@
See also [AudioStreamGenerator] for procedurally generating sounds.
</description>
<tutorials>
- <link title="https://godotengine.org/asset-library/asset/528">Audio Spectrum Demo</link>
- <link title="https://godotengine.org/article/godot-32-will-get-new-audio-features">Godot 3.2 will get new audio features</link>
+ <link title="Audio Spectrum Demo">https://godotengine.org/asset-library/asset/528</link>
+ <link title="Godot 3.2 will get new audio features">https://godotengine.org/article/godot-32-will-get-new-audio-features</link>
</tutorials>
<members>
<member name="buffer_length" type="float" setter="set_buffer_length" getter="get_buffer_length" default="2.0">
diff --git a/doc/classes/AudioStreamGenerator.xml b/doc/classes/AudioStreamGenerator.xml
index 05406846ce..e54ce27a83 100644
--- a/doc/classes/AudioStreamGenerator.xml
+++ b/doc/classes/AudioStreamGenerator.xml
@@ -10,7 +10,7 @@
</description>
<tutorials>
<link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/526</link>
- <link title="https://godotengine.org/article/godot-32-will-get-new-audio-features">Godot 3.2 will get new audio features</link>
+ <link title="Godot 3.2 will get new audio features">https://godotengine.org/article/godot-32-will-get-new-audio-features</link>
</tutorials>
<members>
<member name="buffer_length" type="float" setter="set_buffer_length" getter="get_buffer_length" default="0.5">
diff --git a/doc/classes/AudioStreamGeneratorPlayback.xml b/doc/classes/AudioStreamGeneratorPlayback.xml
index 7520d5d97a..42caa23763 100644
--- a/doc/classes/AudioStreamGeneratorPlayback.xml
+++ b/doc/classes/AudioStreamGeneratorPlayback.xml
@@ -8,7 +8,7 @@
</description>
<tutorials>
<link title="Audio Generator Demo">https://godotengine.org/asset-library/asset/526</link>
- <link title="https://godotengine.org/article/godot-32-will-get-new-audio-features">Godot 3.2 will get new audio features</link>
+ <link title="Godot 3.2 will get new audio features">https://godotengine.org/article/godot-32-will-get-new-audio-features</link>
</tutorials>
<methods>
<method name="can_push_buffer" qualifiers="const">
diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml
index 1b77a5b4d8..714af426b3 100644
--- a/doc/classes/BaseButton.xml
+++ b/doc/classes/BaseButton.xml
@@ -38,7 +38,7 @@
<return type="void" />
<argument index="0" name="pressed" type="bool" />
<description>
- Changes the [member pressed] state of the button, without emitting [signal toggled]. Use when you just want to change the state of the button without sending the pressed event (e.g. when initializing scene). Only works if [member toggle_mode] is [code]true[/code].
+ Changes the [member button_pressed] state of the button, without emitting [signal toggled]. Use when you just want to change the state of the button without sending the pressed event (e.g. when initializing scene). Only works if [member toggle_mode] is [code]true[/code].
[b]Note:[/b] This method doesn't unpress other buttons in [member button_group].
</description>
</method>
@@ -54,6 +54,10 @@
Binary mask to choose which mouse buttons this button will respond to.
To allow both left-click and right-click, use [code]MOUSE_BUTTON_MASK_LEFT | MOUSE_BUTTON_MASK_RIGHT[/code].
</member>
+ <member name="button_pressed" type="bool" setter="set_pressed" getter="is_pressed" default="false">
+ If [code]true[/code], the button's state is pressed. Means the button is pressed down or toggled (if [member toggle_mode] is active). Only works if [member toggle_mode] is [code]true[/code].
+ [b]Note:[/b] Setting [member button_pressed] will result in [signal toggled] to be emitted. If you want to change the pressed state without emitting that signal, use [method set_pressed_no_signal].
+ </member>
<member name="disabled" type="bool" setter="set_disabled" getter="is_disabled" default="false">
If [code]true[/code], the button is in disabled state and can't be clicked or toggled.
</member>
@@ -62,10 +66,6 @@
If [code]true[/code], the button stays pressed when moving the cursor outside the button while pressing it.
[b]Note:[/b] This property only affects the button's visual appearance. Signals will be emitted at the same moment regardless of this property's value.
</member>
- <member name="pressed" type="bool" setter="set_pressed" getter="is_pressed" default="false">
- If [code]true[/code], the button's state is pressed. Means the button is pressed down or toggled (if [member toggle_mode] is active). Only works if [member toggle_mode] is [code]true[/code].
- [b]Note:[/b] Setting [member pressed] will result in [signal toggled] to be emitted. If you want to change the pressed state without emitting that signal, use [method set_pressed_no_signal].
- </member>
<member name="shortcut" type="Shortcut" setter="set_shortcut" getter="get_shortcut">
[Shortcut] associated to the button.
</member>
diff --git a/doc/classes/BitMap.xml b/doc/classes/BitMap.xml
index dc655ee3b0..ebcdcab75e 100644
--- a/doc/classes/BitMap.xml
+++ b/doc/classes/BitMap.xml
@@ -9,6 +9,12 @@
<tutorials>
</tutorials>
<methods>
+ <method name="convert_to_image" qualifiers="const">
+ <return type="Image" />
+ <description>
+ Returns an image of the same size as the bitmap and with a [enum Image.Format] of type [code]FORMAT_L8[/code]. [code]true[/code] bits of the bitmap are being converted into white pixels, and [code]false[/code] bits into black.
+ </description>
+ </method>
<method name="create">
<return type="void" />
<argument index="0" name="size" type="Vector2" />
@@ -64,6 +70,13 @@
[code]epsilon[/code] is passed to RDP to control how accurately the polygons cover the bitmap: a lower [code]epsilon[/code] corresponds to more points in the polygons.
</description>
</method>
+ <method name="resize">
+ <return type="void" />
+ <argument index="0" name="new_size" type="Vector2" />
+ <description>
+ Resizes the image to [code]new_size[/code].
+ </description>
+ </method>
<method name="set_bit">
<return type="void" />
<argument index="0" name="position" type="Vector2" />
diff --git a/doc/classes/BoxMesh.xml b/doc/classes/BoxMesh.xml
index af3365b6ea..bf499c8971 100644
--- a/doc/classes/BoxMesh.xml
+++ b/doc/classes/BoxMesh.xml
@@ -11,7 +11,7 @@
<tutorials>
</tutorials>
<members>
- <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(2, 2, 2)">
+ <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(1, 1, 1)">
The box's width, height and depth.
</member>
<member name="subdivide_depth" type="int" setter="set_subdivide_depth" getter="get_subdivide_depth" default="0">
diff --git a/doc/classes/BoxShape3D.xml b/doc/classes/BoxShape3D.xml
index 3bfded6512..cf2bf2338b 100644
--- a/doc/classes/BoxShape3D.xml
+++ b/doc/classes/BoxShape3D.xml
@@ -12,7 +12,7 @@
<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link>
</tutorials>
<members>
- <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(2, 2, 2)">
+ <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(1, 1, 1)">
The box's width, height and depth.
</member>
</members>
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index 65b66b2fd5..ef4eba62b2 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -94,16 +94,16 @@
</member>
</members>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Default text [Color] of the [Button].
</theme_item>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.2)">
+ <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
Text [Color] used when the [Button] is disabled.
</theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] used when the [Button] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
</theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] used when the [Button] is being hovered.
</theme_item>
<theme_item name="font_hover_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
diff --git a/doc/classes/ButtonGroup.xml b/doc/classes/ButtonGroup.xml
index e3e781ac10..24bdc95baf 100644
--- a/doc/classes/ButtonGroup.xml
+++ b/doc/classes/ButtonGroup.xml
@@ -28,7 +28,7 @@
</members>
<signals>
<signal name="pressed">
- <argument index="0" name="button" type="Object" />
+ <argument index="0" name="button" type="BaseButton" />
<description>
Emitted when one of the buttons of the group is pressed.
</description>
diff --git a/doc/classes/CapsuleMesh.xml b/doc/classes/CapsuleMesh.xml
index 3b4e60ce93..45dd64c174 100644
--- a/doc/classes/CapsuleMesh.xml
+++ b/doc/classes/CapsuleMesh.xml
@@ -9,13 +9,13 @@
<tutorials>
</tutorials>
<members>
- <member name="height" type="float" setter="set_height" getter="get_height" default="3.0">
+ <member name="height" type="float" setter="set_height" getter="get_height" default="2.0">
Total height of the capsule mesh (including the hemispherical ends).
</member>
<member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" default="64">
Number of radial segments on the capsule mesh.
</member>
- <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
Radius of the capsule mesh.
</member>
<member name="rings" type="int" setter="set_rings" getter="get_rings" default="8">
diff --git a/doc/classes/CapsuleShape3D.xml b/doc/classes/CapsuleShape3D.xml
index 8856ec3779..ba4ead4930 100644
--- a/doc/classes/CapsuleShape3D.xml
+++ b/doc/classes/CapsuleShape3D.xml
@@ -10,10 +10,10 @@
<link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link>
</tutorials>
<members>
- <member name="height" type="float" setter="set_height" getter="get_height" default="3.0">
+ <member name="height" type="float" setter="set_height" getter="get_height" default="2.0">
The capsule's height.
</member>
- <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
The capsule's radius.
</member>
</members>
diff --git a/doc/classes/CharacterBody2D.xml b/doc/classes/CharacterBody2D.xml
index 28a9107db6..1c4475dd70 100644
--- a/doc/classes/CharacterBody2D.xml
+++ b/doc/classes/CharacterBody2D.xml
@@ -165,9 +165,6 @@
If [code]true[/code], the body will not slide on slopes when calling [method move_and_slide] when the body is standing still.
If [code]false[/code], the body will slide on floor's slopes when [member motion_velocity] applies a downward force.
</member>
- <member name="free_mode_min_slide_angle" type="float" setter="set_free_mode_min_slide_angle" getter="get_free_mode_min_slide_angle" default="0.261799">
- Minimum angle (in radians) where the body is allowed to slide when it encounters a slope. The default value equals 15 degrees.
- </member>
<member name="max_slides" type="int" setter="set_max_slides" getter="get_max_slides" default="4">
Maximum number of times the body can change direction before it stops when calling [method move_and_slide].
</member>
@@ -192,12 +189,15 @@
<member name="up_direction" type="Vector2" setter="set_up_direction" getter="get_up_direction" default="Vector2(0, -1)">
Direction vector used to determine what is a wall and what is a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. Defaults to [code]Vector2.UP[/code]. If set to [code]Vector2(0, 0)[/code], everything is considered a wall. This is useful for topdown games.
</member>
+ <member name="wall_min_slide_angle" type="float" setter="set_wall_min_slide_angle" getter="get_wall_min_slide_angle" default="0.261799">
+ Minimum angle (in radians) where the body is allowed to slide when it encounters a slope. The default value equals 15 degrees. This property only affects movement when [member motion_mode] is [constant MOTION_MODE_FLOATING].
+ </member>
</members>
<constants>
<constant name="MOTION_MODE_GROUNDED" value="0" enum="MotionMode">
Apply when notions of walls, ceiling and floor are relevant. In this mode the body motion will react to slopes (acceleration/slowdown). This mode is suitable for sided games like platformers.
</constant>
- <constant name="MOTION_MODE_FREE" value="1" enum="MotionMode">
+ <constant name="MOTION_MODE_FLOATING" value="1" enum="MotionMode">
Apply when there is no notion of floor or ceiling. All collisions will be reported as [code]on_wall[/code]. In this mode, when you slide, the speed will always be constant. This mode is suitable for top-down games.
</constant>
<constant name="PLATFORM_VEL_ON_LEAVE_ALWAYS" value="0" enum="MovingPlatformApplyVelocityOnLeave">
diff --git a/doc/classes/CharacterBody3D.xml b/doc/classes/CharacterBody3D.xml
index 819190fd69..a5df0fd97e 100644
--- a/doc/classes/CharacterBody3D.xml
+++ b/doc/classes/CharacterBody3D.xml
@@ -175,14 +175,14 @@
Direction vector used to determine what is a wall and what is a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. Defaults to [code]Vector3.UP[/code]. If set to [code]Vector3(0, 0, 0)[/code], everything is considered a wall. This is useful for topdown games.
</member>
<member name="wall_min_slide_angle" type="float" setter="set_wall_min_slide_angle" getter="get_wall_min_slide_angle" default="0.261799">
- Minimum angle (in radians) where the body is allowed to slide when it encounters a slope. The default value equals 15 degrees. In [code]MOTION_MODE_GROUNDED[/code], it works only when [member floor_block_on_wall] is [code]true[/code].
+ Minimum angle (in radians) where the body is allowed to slide when it encounters a slope. The default value equals 15 degrees. When [member motion_mode] is [constant MOTION_MODE_GROUNDED], it only affects movement if [member floor_block_on_wall] is [code]true[/code].
</member>
</members>
<constants>
<constant name="MOTION_MODE_GROUNDED" value="0" enum="MotionMode">
Apply when notions of walls, ceiling and floor are relevant. In this mode the body motion will react to slopes (acceleration/slowdown). This mode is suitable for grounded games like platformers.
</constant>
- <constant name="MOTION_MODE_FREE" value="1" enum="MotionMode">
+ <constant name="MOTION_MODE_FLOATING" value="1" enum="MotionMode">
Apply when there is no notion of floor or ceiling. All collisions will be reported as [code]on_wall[/code]. In this mode, when you slide, the speed will always be constant. This mode is suitable for games without ground like space games.
</constant>
<constant name="PLATFORM_VEL_ON_LEAVE_ALWAYS" value="0" enum="MovingPlatformApplyVelocityOnLeave">
diff --git a/doc/classes/CheckBox.xml b/doc/classes/CheckBox.xml
index 6180f56858..3626420589 100644
--- a/doc/classes/CheckBox.xml
+++ b/doc/classes/CheckBox.xml
@@ -14,16 +14,16 @@
<member name="toggle_mode" type="bool" setter="set_toggle_mode" getter="is_toggle_mode" overrides="BaseButton" default="true" />
</members>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
The [CheckBox] text's font color.
</theme_item>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.2)">
+ <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
The [CheckBox] text's font color when it's disabled.
</theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
The [CheckBox] text's font color when it's focused. Only replaces the normal text color of the checkbox. Disabled, hovered, and pressed states take precedence over this color.
</theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
The [CheckBox] text's font color when it's hovered.
</theme_item>
<theme_item name="font_hover_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
diff --git a/doc/classes/CheckButton.xml b/doc/classes/CheckButton.xml
index 659fb5126f..74d438f24e 100644
--- a/doc/classes/CheckButton.xml
+++ b/doc/classes/CheckButton.xml
@@ -14,16 +14,16 @@
<member name="toggle_mode" type="bool" setter="set_toggle_mode" getter="is_toggle_mode" overrides="BaseButton" default="true" />
</members>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
The [CheckButton] text's font color.
</theme_item>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.2)">
+ <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
The [CheckButton] text's font color when it's disabled.
</theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
The [CheckButton] text's font color when it's focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
</theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
The [CheckButton] text's font color when it's hovered.
</theme_item>
<theme_item name="font_hover_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml
index 697b0443f1..09696d4d2a 100644
--- a/doc/classes/CodeEdit.xml
+++ b/doc/classes/CodeEdit.xml
@@ -345,7 +345,7 @@
<return type="void" />
<argument index="0" name="force" type="bool" default="false" />
<description>
- Emits [signal request_code_completion], if [code]force[/code] is true will bypass all checks. Otherwise will check that the caret is in a word or in front of a prefix. Will ignore the request if all current options are of type file path, node path or signal.
+ Emits [signal code_completion_requested], if [code]force[/code] is true will bypass all checks. Otherwise will check that the caret is in a word or in front of a prefix. Will ignore the request if all current options are of type file path, node path or signal.
</description>
</method>
<method name="set_code_completion_selected_index">
@@ -442,7 +442,7 @@
<member name="auto_brace_completion_highlight_matching" type="bool" setter="set_highlight_matching_braces_enabled" getter="is_highlight_matching_braces_enabled" default="false">
Highlight mismatching brace pairs.
</member>
- <member name="auto_brace_completion_pairs" type="Dictionary" setter="set_auto_brace_completion_pairs" getter="get_auto_brace_completion_pairs" default="{&quot;\&quot;&quot;: &quot;\&quot;&quot;,&quot;&apos;&quot;: &quot;&apos;&quot;,&quot;(&quot;: &quot;)&quot;,&quot;[&quot;: &quot;]&quot;,&quot;{&quot;: &quot;}&quot;}">
+ <member name="auto_brace_completion_pairs" type="Dictionary" setter="set_auto_brace_completion_pairs" getter="get_auto_brace_completion_pairs" default="{ &quot;\&quot;&quot;: &quot;\&quot;&quot;, &quot;&apos;&quot;: &quot;&apos;&quot;, &quot;(&quot;: &quot;)&quot;, &quot;[&quot;: &quot;]&quot;, &quot;{&quot;: &quot;}&quot; }">
Sets the brace pairs to be autocompleted.
</member>
<member name="code_completion_enabled" type="bool" setter="set_code_completion_enabled" getter="is_code_completion_enabled" default="false">
@@ -506,7 +506,7 @@
Emitted when a breakpoint is added or removed from a line. If the line is moved via backspace a removed is emitted at the old line.
</description>
</signal>
- <signal name="request_code_completion">
+ <signal name="code_completion_requested">
<description>
Emitted when the user requests code completion.
</description>
@@ -574,7 +574,7 @@
<theme_item name="caret_background_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
[Color] of the text behind the caret when block caret is enabled.
</theme_item>
- <theme_item name="caret_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="caret_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
[Color] of the caret.
</theme_item>
<theme_item name="code_folding_color" data_type="color" type="Color" default="Color(0.8, 0.8, 0.8, 0.8)">
@@ -601,13 +601,16 @@
<theme_item name="executing_line_color" data_type="color" type="Color" default="Color(0.98, 0.89, 0.27, 1)">
[Color] of the executing icon for executing lines.
</theme_item>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Sets the font [Color].
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the [CodeEdit].
</theme_item>
- <theme_item name="font_readonly_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 0.5)">
+ <theme_item name="font_placeholder_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.6)">
+ Font color for [member TextEdit.placeholder_text].
+ </theme_item>
+ <theme_item name="font_readonly_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
Sets the font [Color] when [member TextEdit.editable] is disabled.
</theme_item>
<theme_item name="font_selected_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
@@ -625,7 +628,7 @@
<theme_item name="search_result_color" data_type="color" type="Color" default="Color(0.3, 0.3, 0.3, 1)">
[Color] behind the text that matches the search query.
</theme_item>
- <theme_item name="selection_color" data_type="color" type="Color" default="Color(0.49, 0.49, 0.49, 1)">
+ <theme_item name="selection_color" data_type="color" type="Color" default="Color(0.5, 0.5, 0.5, 1)">
Sets the highlight [Color] of text selections.
</theme_item>
<theme_item name="word_highlighted_color" data_type="color" type="Color" default="Color(0.8, 0.9, 0.9, 0.15)">
diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml
index 63492bf9a0..828b24be83 100644
--- a/doc/classes/CollisionObject2D.xml
+++ b/doc/classes/CollisionObject2D.xml
@@ -208,7 +208,7 @@
Defines the behavior in physics when [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED]. See [enum DisableMode] for more details about the different modes.
</member>
<member name="input_pickable" type="bool" setter="set_pickable" getter="is_pickable" default="true">
- If [code]true[/code], this object is pickable. A pickable object can detect the mouse pointer entering/leaving, and if the mouse is inside it, report input events. Requires at least one [code]collision_layer[/code] bit to be set.
+ If [code]true[/code], this object is pickable. A pickable object can detect the mouse pointer entering/leaving, and if the mouse is inside it, report input events. Requires at least one [member collision_layer] bit to be set.
</member>
</members>
<signals>
diff --git a/doc/classes/CollisionObject3D.xml b/doc/classes/CollisionObject3D.xml
index 33305471b8..96912cf469 100644
--- a/doc/classes/CollisionObject3D.xml
+++ b/doc/classes/CollisionObject3D.xml
@@ -183,7 +183,7 @@
If [code]true[/code], the [CollisionObject3D] will continue to receive input events as the mouse is dragged across its shapes.
</member>
<member name="input_ray_pickable" type="bool" setter="set_ray_pickable" getter="is_ray_pickable" default="true">
- If [code]true[/code], the [CollisionObject3D]'s shapes will respond to [RayCast3D]s.
+ If [code]true[/code], this object is pickable. A pickable object can detect the mouse pointer entering/leaving, and if the mouse is inside it, report input events. Requires at least one [member collision_layer] bit to be set.
</member>
</members>
<signals>
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index d5551e1e04..b6c2dac33c 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -380,12 +380,6 @@
Returns the focus neighbor for the specified [enum Side]. A getter method for [member focus_neighbor_bottom], [member focus_neighbor_left], [member focus_neighbor_right] and [member focus_neighbor_top].
</description>
</method>
- <method name="get_focus_owner" qualifiers="const">
- <return type="Control" />
- <description>
- Returns the control that has the keyboard focus or [code]null[/code] if none.
- </description>
- </method>
<method name="get_global_rect" qualifiers="const">
<return type="Rect2" />
<description>
diff --git a/doc/classes/CurveTexture.xml b/doc/classes/CurveTexture.xml
index fe75f029f0..99b5ef2f4f 100644
--- a/doc/classes/CurveTexture.xml
+++ b/doc/classes/CurveTexture.xml
@@ -14,7 +14,7 @@
</member>
<member name="texture_mode" type="int" setter="set_texture_mode" getter="get_texture_mode" enum="CurveTexture.TextureMode" default="0">
</member>
- <member name="width" type="int" setter="set_width" getter="get_width" default="2048">
+ <member name="width" type="int" setter="set_width" getter="get_width" default="256">
The width of the texture.
</member>
</members>
diff --git a/doc/classes/CurveXYZTexture.xml b/doc/classes/CurveXYZTexture.xml
index 815653e987..b6dd90c7d3 100644
--- a/doc/classes/CurveXYZTexture.xml
+++ b/doc/classes/CurveXYZTexture.xml
@@ -13,7 +13,7 @@
</member>
<member name="curve_z" type="Curve" setter="set_curve_z" getter="get_curve_z">
</member>
- <member name="width" type="int" setter="set_width" getter="get_width" default="2048">
+ <member name="width" type="int" setter="set_width" getter="get_width" default="256">
</member>
</members>
</class>
diff --git a/doc/classes/CylinderMesh.xml b/doc/classes/CylinderMesh.xml
index 077435990b..ce34cc91ad 100644
--- a/doc/classes/CylinderMesh.xml
+++ b/doc/classes/CylinderMesh.xml
@@ -9,7 +9,7 @@
<tutorials>
</tutorials>
<members>
- <member name="bottom_radius" type="float" setter="set_bottom_radius" getter="get_bottom_radius" default="1.0">
+ <member name="bottom_radius" type="float" setter="set_bottom_radius" getter="get_bottom_radius" default="0.5">
Bottom radius of the cylinder. If set to [code]0.0[/code], the bottom faces will not be generated, resulting in a conic shape.
</member>
<member name="height" type="float" setter="set_height" getter="get_height" default="2.0">
@@ -21,7 +21,7 @@
<member name="rings" type="int" setter="set_rings" getter="get_rings" default="4">
Number of edge rings along the height of the cylinder. Changing [member rings] does not have any visual impact unless a shader or procedural mesh tool is used to alter the vertex data. Higher values result in more subdivisions, which can be used to create smoother-looking effects with shaders or procedural mesh tools (at the cost of performance). When not altering the vertex data using a shader or procedural mesh tool, [member rings] should be kept to its default value.
</member>
- <member name="top_radius" type="float" setter="set_top_radius" getter="get_top_radius" default="1.0">
+ <member name="top_radius" type="float" setter="set_top_radius" getter="get_top_radius" default="0.5">
Top radius of the cylinder. If set to [code]0.0[/code], the top faces will not be generated, resulting in a conic shape.
</member>
</members>
diff --git a/doc/classes/CylinderShape3D.xml b/doc/classes/CylinderShape3D.xml
index d40b96710b..6fa6c4d75e 100644
--- a/doc/classes/CylinderShape3D.xml
+++ b/doc/classes/CylinderShape3D.xml
@@ -15,7 +15,7 @@
<member name="height" type="float" setter="set_height" getter="get_height" default="2.0">
The cylinder's height.
</member>
- <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
The cylinder's radius.
</member>
</members>
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index a41875385c..8be944b105 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -20,6 +20,12 @@
[b]Note:[/b] This method is only implemented on Linux.
</description>
</method>
+ <method name="clipboard_has" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if there is content on the user's clipboard.
+ </description>
+ </method>
<method name="clipboard_set">
<return type="void" />
<argument index="0" name="clipboard" type="String" />
@@ -35,12 +41,6 @@
[b]Note:[/b] This method is only implemented on Linux.
</description>
</method>
- <method name="console_set_visible">
- <return type="void" />
- <argument index="0" name="console_visible" type="bool" />
- <description>
- </description>
- </method>
<method name="create_sub_window">
<return type="int" />
<argument index="0" name="mode" type="int" enum="DisplayServer.WindowMode" />
@@ -281,11 +281,6 @@
<description>
</description>
</method>
- <method name="is_console_visible" qualifiers="const">
- <return type="bool" />
- <description>
- </description>
- </method>
<method name="keyboard_get_current_layout" qualifiers="const">
<return type="int" />
<description>
@@ -332,11 +327,6 @@
[b]Note:[/b] This method is implemented on Linux, macOS and Windows.
</description>
</method>
- <method name="mouse_get_absolute_position" qualifiers="const">
- <return type="Vector2i" />
- <description>
- </description>
- </method>
<method name="mouse_get_button_state" qualifiers="const">
<return type="int" enum="MouseButton" />
<description>
@@ -579,6 +569,15 @@
Returns the mode of the given window.
</description>
</method>
+ <method name="window_get_native_handle" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="handle_type" type="int" enum="DisplayServer.HandleType" />
+ <argument index="1" name="window_id" type="int" default="0" />
+ <description>
+ Returns internal structure pointers for use in plugins.
+ [b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows.
+ </description>
+ </method>
<method name="window_get_position" qualifiers="const">
<return type="Vector2i" />
<argument index="0" name="window_id" type="int" default="0" />
@@ -803,23 +802,21 @@
</constant>
<constant name="FEATURE_NATIVE_DIALOG" value="9" enum="Feature">
</constant>
- <constant name="FEATURE_CONSOLE_WINDOW" value="10" enum="Feature">
+ <constant name="FEATURE_IME" value="10" enum="Feature">
</constant>
- <constant name="FEATURE_IME" value="11" enum="Feature">
+ <constant name="FEATURE_WINDOW_TRANSPARENCY" value="11" enum="Feature">
</constant>
- <constant name="FEATURE_WINDOW_TRANSPARENCY" value="12" enum="Feature">
+ <constant name="FEATURE_HIDPI" value="12" enum="Feature">
</constant>
- <constant name="FEATURE_HIDPI" value="13" enum="Feature">
+ <constant name="FEATURE_ICON" value="13" enum="Feature">
</constant>
- <constant name="FEATURE_ICON" value="14" enum="Feature">
+ <constant name="FEATURE_NATIVE_ICON" value="14" enum="Feature">
</constant>
- <constant name="FEATURE_NATIVE_ICON" value="15" enum="Feature">
+ <constant name="FEATURE_ORIENTATION" value="15" enum="Feature">
</constant>
- <constant name="FEATURE_ORIENTATION" value="16" enum="Feature">
+ <constant name="FEATURE_SWAP_BUFFERS" value="16" enum="Feature">
</constant>
- <constant name="FEATURE_SWAP_BUFFERS" value="17" enum="Feature">
- </constant>
- <constant name="FEATURE_CLIPBOARD_PRIMARY" value="19" enum="Feature">
+ <constant name="FEATURE_CLIPBOARD_PRIMARY" value="18" enum="Feature">
</constant>
<constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode">
Makes the mouse cursor visible if it is hidden.
@@ -942,5 +939,22 @@
Displays the most recent image in the queue on vertical blanking intervals, while rendering to the other images (no tearing is visible).
Although not guaranteed, the images can be rendered as fast as possible, which may reduce input lag.
</constant>
+ <constant name="DISPLAY_HANDLE" value="0" enum="HandleType">
+ Display handle:
+ - Linux: [code]X11::Display*[/code] for the display.
+ </constant>
+ <constant name="WINDOW_HANDLE" value="1" enum="HandleType">
+ Window handle:
+ - Windows: [code]HWND[/code] for the window.
+ - Linux: [code]X11::Window*[/code] for the window.
+ - MacOS: [code]NSWindow*[/code] for the window.
+ - iOS: [code]UIViewController*[/code] for the view controller.
+ - Android: [code]jObject[/code] for the activity.
+ </constant>
+ <constant name="WINDOW_VIEW" value="2" enum="HandleType">
+ Window view:
+ - MacOS: [code]NSView*[/code] for the window main view.
+ - iOS: [code]UIView*[/code] for the window main view.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml
index 39589138fa..de122cb8bb 100644
--- a/doc/classes/EditorInspector.xml
+++ b/doc/classes/EditorInspector.xml
@@ -13,6 +13,11 @@
<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>
+ <signal name="edited_object_changed">
+ <description>
+ Emitted when the object being edited by the inspector has changed.
+ </description>
+ </signal>
<signal name="object_id_selected">
<argument index="0" name="id" type="int" />
<description>
@@ -54,8 +59,8 @@
</description>
</signal>
<signal name="resource_selected">
- <argument index="0" name="res" type="Object" />
- <argument index="1" name="prop" type="String" />
+ <argument index="0" name="resource" type="Resource" />
+ <argument index="1" name="path" type="String" />
<description>
Emitted when a resource is selected in the inspector.
</description>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 7b0a300782..cc1243898f 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -438,9 +438,9 @@
<method name="add_tool_submenu_item">
<return type="void" />
<argument index="0" name="name" type="String" />
- <argument index="1" name="submenu" type="Object" />
+ <argument index="1" name="submenu" type="PopupMenu" />
<description>
- Adds a custom submenu under [b]Project &gt; Tools &gt;[/b] [code]name[/code]. [code]submenu[/code] should be an object of class [PopupMenu]. Use [code]remove_tool_menu_item(name)[/code] on plugin clean up to remove the menu.
+ Adds a custom [PopupMenu] submenu under [b]Project &gt; Tools &gt;[/b] [code]name[/code]. Use [code]remove_tool_menu_item(name)[/code] on plugin clean up to remove the menu.
</description>
</method>
<method name="add_translation_parser_plugin">
diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index 2090672acc..c8c0494378 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -154,6 +154,13 @@
<member name="glow_levels/7" type="float" setter="set_glow_level" getter="get_glow_level" default="0.0">
The intensity of the 7th level of glow. This is the most "global" level (blurriest).
</member>
+ <member name="glow_map" type="Texture" setter="set_glow_map" getter="get_glow_map">
+ The texture that should be used as a glow map to [i]multiply[/i] the resulting glow color according to [member glow_map_strength]. This can be used to create a "lens dirt" effect. The texture's RGB color channels are used for modulation, but the alpha channel is ignored.
+ [b]Note:[/b] The texture will be stretched to fit the screen. Therefore, it's recommended to use a texture with an aspect ratio that matches your project's base aspect ratio (typically 16:9).
+ </member>
+ <member name="glow_map_strength" type="float" setter="set_glow_map_strength" getter="get_glow_map_strength" default="0.8">
+ How strong of an impact the [member glow_map] should have on the overall glow effect. A strength of [code]0.0[/code] means the glow map has no effect on the overall glow effect. A strength of [code]1.0[/code] means the glow has a full effect on the overall glow effect (and can turn off glow entirely in specific areas of the screen if the glow map has black areas).
+ </member>
<member name="glow_mix" type="float" setter="set_glow_mix" getter="get_glow_mix" default="0.05">
</member>
<member name="glow_normalized" type="bool" setter="set_glow_normalized" getter="is_glow_normalized" default="false">
@@ -168,10 +175,11 @@
</member>
<member name="sdfgi_cascade0_distance" type="float" setter="set_sdfgi_cascade0_distance" getter="get_sdfgi_cascade0_distance" default="12.8">
</member>
- <member name="sdfgi_cascades" type="int" setter="set_sdfgi_cascades" getter="get_sdfgi_cascades" enum="Environment.SDFGICascades" default="1">
+ <member name="sdfgi_cascades" type="int" setter="set_sdfgi_cascades" getter="get_sdfgi_cascades" default="6">
+ The number of cascades to use for SDFGI (between 1 and 8). A higher number of cascades allows displaying SDFGI further away while preserving detail up close, at the cost of performance. When using SDFGI on small-scale levels, [member sdfgi_cascades] can often be decreased between [code]1[/code] and [code]4[/code] to improve performance.
</member>
<member name="sdfgi_enabled" type="bool" setter="set_sdfgi_enabled" getter="is_sdfgi_enabled" default="false">
- If [code]true[/code], enables signed distance field global illumination for meshes that have their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_BAKED]. SDFGI is a real-time global illumination technique that works well with procedurally generated and user-built levels, including in situations where geometry is created during gameplay. The signed distance field is automatically generated around the camera as it moves. Dynamic lights are supported, but dynamic occluders and emissive surfaces are not.
+ If [code]true[/code], enables signed distance field global illumination for meshes that have their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_STATIC]. SDFGI is a real-time global illumination technique that works well with procedurally generated and user-built levels, including in situations where geometry is created during gameplay. The signed distance field is automatically generated around the camera as it moves. Dynamic lights are supported, but dynamic occluders and emissive surfaces are not.
[b]Performance:[/b] SDFGI is relatively demanding on the GPU and is not suited to low-end hardware such as integrated graphics (consider [LightmapGI] instead). To improve SDFGI performance, enable [member ProjectSettings.rendering/global_illumination/gi/use_half_resolution] in the Project Settings.
[b]Note:[/b] Meshes should have sufficiently thick walls to avoid light leaks (avoid one-sided walls). For interior levels, enclose your level geometry in a sufficiently large box and bridge the loops to close the mesh.
</member>
@@ -371,12 +379,6 @@
<constant name="GLOW_BLEND_MODE_MIX" value="4" enum="GlowBlendMode">
Mixes the glow with the underlying color to avoid increasing brightness as much while still maintaining a glow effect.
</constant>
- <constant name="SDFGI_CASCADES_4" value="0" enum="SDFGICascades">
- </constant>
- <constant name="SDFGI_CASCADES_6" value="1" enum="SDFGICascades">
- </constant>
- <constant name="SDFGI_CASCADES_8" value="2" enum="SDFGICascades">
- </constant>
<constant name="SDFGI_Y_SCALE_DISABLED" value="0" enum="SDFGIYScale">
</constant>
<constant name="SDFGI_Y_SCALE_75_PERCENT" value="1" enum="SDFGIYScale">
diff --git a/doc/classes/FlowContainer.xml b/doc/classes/FlowContainer.xml
new file mode 100644
index 0000000000..482d879b09
--- /dev/null
+++ b/doc/classes/FlowContainer.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="FlowContainer" inherits="Container" version="4.0">
+ <brief_description>
+ Base class for flow containers.
+ </brief_description>
+ <description>
+ Arranges child [Control] nodes vertically or horizontally in a left-to-right or top-to-bottom flow.
+ A line is filled with [Control] nodes until no more fit on the same line, similar to text in an autowrapped label.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_line_count" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the current line count.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/FontData.xml b/doc/classes/FontData.xml
index 36976f7083..55b715c3fc 100644
--- a/doc/classes/FontData.xml
+++ b/doc/classes/FontData.xml
@@ -306,6 +306,22 @@
Returns [code]true[/code], if font supports given script ([url=https://en.wikipedia.org/wiki/ISO_15924]ISO 15924[/url] code).
</description>
</method>
+ <method name="load_bitmap_font">
+ <return type="int" enum="Error" />
+ <argument index="0" name="path" type="String" />
+ <description>
+ Loads an AngelCode BMFont (.fnt, .font) bitmap font from file [code]path[/code].
+ [b]Warning:[/b] This method should only be used in the editor or in cases when you need to load external fonts at run-time, such as fonts located at the [code]user://[/code] directory.
+ </description>
+ </method>
+ <method name="load_dynamic_font">
+ <return type="int" enum="Error" />
+ <argument index="0" name="path" type="String" />
+ <description>
+ Loads a TrueType (.ttf), OpenType (.otf), WOFF (.woff) or Type 1 (.pfb, .pfm) dynamic font from file [code]path[/code].
+ [b]Warning:[/b] This method should only be used in the editor or in cases when you need to load external fonts at run-time, such as fonts located at the [code]user://[/code] directory.
+ </description>
+ </method>
<method name="remove_cache">
<return type="void" />
<argument index="0" name="cache_index" type="int" />
diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml
index 72adc49742..f97198658e 100644
--- a/doc/classes/GPUParticles2D.xml
+++ b/doc/classes/GPUParticles2D.xml
@@ -18,6 +18,17 @@
Returns a rectangle containing the positions of all existing particles.
</description>
</method>
+ <method name="emit_particle">
+ <return type="void" />
+ <argument index="0" name="xform" type="Transform2D" />
+ <argument index="1" name="velocity" type="Vector2" />
+ <argument index="2" name="color" type="Color" />
+ <argument index="3" name="custom" type="Color" />
+ <argument index="4" name="flags" type="int" />
+ <description>
+ Emits a single particle. Whether [code]xform[/code], [code]velocity[/code], [code]color[/code] and [code]custom[/code] are applied depends on the value of [code]flags[/code]. See [enum EmitFlags].
+ </description>
+ </method>
<method name="restart">
<return type="void" />
<description>
@@ -67,6 +78,9 @@
<member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0">
Particle system's running speed scaling ratio. A value of [code]0[/code] can be used to pause the particles.
</member>
+ <member name="sub_emitter" type="NodePath" setter="set_sub_emitter" getter="get_sub_emitter" default="NodePath(&quot;&quot;)">
+ The [NodePath] to the [GPUParticles2D] used for sub-emissions.
+ </member>
<member name="texture" type="Texture2D" setter="set_texture" getter="get_texture">
Particle texture. If [code]null[/code], particles will be squares.
</member>
@@ -92,5 +106,20 @@
</constant>
<constant name="DRAW_ORDER_REVERSE_LIFETIME" value="2" enum="DrawOrder">
</constant>
+ <constant name="EMIT_FLAG_POSITION" value="1" enum="EmitFlags">
+ Particle starts at the specified position.
+ </constant>
+ <constant name="EMIT_FLAG_ROTATION_SCALE" value="2" enum="EmitFlags">
+ Particle starts with specified rotation and scale.
+ </constant>
+ <constant name="EMIT_FLAG_VELOCITY" value="4" enum="EmitFlags">
+ Particle starts with the specified velocity vector, which defines the emission direction and speed.
+ </constant>
+ <constant name="EMIT_FLAG_COLOR" value="8" enum="EmitFlags">
+ Particle starts with specified color.
+ </constant>
+ <constant name="EMIT_FLAG_CUSTOM" value="16" enum="EmitFlags">
+ Particle starts with specificed [code]CUSTOM[/code] data.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml
index 771056cb93..62ac846077 100644
--- a/doc/classes/GPUParticles3D.xml
+++ b/doc/classes/GPUParticles3D.xml
@@ -26,6 +26,7 @@
<argument index="3" name="custom" type="Color" />
<argument index="4" name="flags" type="int" />
<description>
+ Emits a single particle. Whether [code]xform[/code], [code]velocity[/code], [code]color[/code] and [code]custom[/code] are applied depends on the value of [code]flags[/code]. See [enum EmitFlags].
</description>
</method>
<method name="get_draw_pass_mesh" qualifiers="const">
@@ -137,14 +138,19 @@
Particles are drawn in order of depth.
</constant>
<constant name="EMIT_FLAG_POSITION" value="1" enum="EmitFlags">
+ Particle starts at the specified position.
</constant>
<constant name="EMIT_FLAG_ROTATION_SCALE" value="2" enum="EmitFlags">
+ Particle starts with specified rotation and scale.
</constant>
<constant name="EMIT_FLAG_VELOCITY" value="4" enum="EmitFlags">
+ Particle starts with the specified velocity vector, which defines the emission direction and speed.
</constant>
<constant name="EMIT_FLAG_COLOR" value="8" enum="EmitFlags">
+ Particle starts with specified color.
</constant>
<constant name="EMIT_FLAG_CUSTOM" value="16" enum="EmitFlags">
+ Particle starts with specificed [code]CUSTOM[/code] data.
</constant>
<constant name="MAX_DRAW_PASSES" value="4">
Maximum number of draw passes supported.
diff --git a/doc/classes/GeometryInstance3D.xml b/doc/classes/GeometryInstance3D.xml
index cecd1e518f..c03a2b8b7c 100644
--- a/doc/classes/GeometryInstance3D.xml
+++ b/doc/classes/GeometryInstance3D.xml
@@ -41,7 +41,8 @@
The texel density to use for lightmapping in [LightmapGI]. Greater scale values provide higher resolution in the lightmap, which can result in sharper shadows for lights that have both direct and indirect light baked. However, greater scale values will also increase the space taken by the mesh in the lightmap texture, which increases the memory, storage, and bake time requirements. When using a single mesh at different scales, consider adjusting this value to keep the lightmap texel density consistent across meshes.
</member>
<member name="gi_mode" type="int" setter="set_gi_mode" getter="get_gi_mode" enum="GeometryInstance3D.GIMode" default="0">
- The global illumination mode to use for the whole geometry. Use a mode that matches the purpose
+ The global illumination mode to use for the whole geometry. To avoid inconsistent results, use a mode that matches the purpose of the mesh during gameplay (static/dynamic).
+ [b]Note:[/b] Lights' bake mode will also affect the global illumination rendering. See [member Light3D.light_bake_mode].
</member>
<member name="ignore_occlusion_culling" type="bool" setter="set_ignore_occlusion_culling" getter="is_ignoring_occlusion_culling" default="false">
</member>
@@ -93,13 +94,13 @@
In other words, the actual mesh will not be visible, only the shadows casted from the mesh will be.
</constant>
<constant name="GI_MODE_DISABLED" value="0" enum="GIMode">
- Disabled global illumination mode. Use for dynamic objects that do not contribute to global illumination (such as characters). When using [VoxelGI] and SDFGI, the geometry will [i]receive[/i] indirect lighting and reflections but will not be considered in GI baking. When using [LightmapGI], the object will receive indirect lighting using lightmap probes instead of using the lightmap texture.
+ Disabled global illumination mode. Use for dynamic objects that do not contribute to global illumination (such as characters). When using [VoxelGI] and SDFGI, the geometry will [i]receive[/i] indirect lighting and reflections but the geometry will not be considered in GI baking. When using [LightmapGI], the object will receive indirect lighting using lightmap probes instead of using the baked lightmap texture.
</constant>
- <constant name="GI_MODE_BAKED" value="1" enum="GIMode">
+ <constant name="GI_MODE_STATIC" value="1" enum="GIMode">
Baked global illumination mode. Use for static objects that contribute to global illumination (such as level geometry). This GI mode is effective when using [VoxelGI], SDFGI and [LightmapGI].
</constant>
<constant name="GI_MODE_DYNAMIC" value="2" enum="GIMode">
- Dynamic global illumination mode. Use for dynamic objects that contribute to global illumination. This GI mode is only effective when using [VoxelGI], but it has a higher performance impact than [constant GI_MODE_BAKED].
+ Dynamic global illumination mode. Use for dynamic objects that contribute to global illumination. This GI mode is only effective when using [VoxelGI], but it has a higher performance impact than [constant GI_MODE_STATIC]. When using other GI methods, this will act the same as [constant GI_MODE_DISABLED].
</constant>
<constant name="LIGHTMAP_SCALE_1X" value="0" enum="LightmapScale">
The standard texel density for lightmapping with [LightmapGI].
diff --git a/doc/classes/GradientTexture1D.xml b/doc/classes/GradientTexture1D.xml
index 223439956c..4730410ea9 100644
--- a/doc/classes/GradientTexture1D.xml
+++ b/doc/classes/GradientTexture1D.xml
@@ -15,7 +15,7 @@
<member name="use_hdr" type="bool" setter="set_use_hdr" getter="is_using_hdr" default="false">
If [code]true[/code], the generated texture will support high dynamic range ([constant Image.FORMAT_RGBAF] format). This allows for glow effects to work if [member Environment.glow_enabled] is [code]true[/code]. If [code]false[/code], the generated texture will use low dynamic range; overbright colors will be clamped ([constant Image.FORMAT_RGBA8] format).
</member>
- <member name="width" type="int" setter="set_width" getter="get_width" default="2048">
+ <member name="width" type="int" setter="set_width" getter="get_width" default="256">
The number of color samples that will be obtained from the [Gradient].
</member>
</members>
diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml
index d5780dce2d..c8606cf67f 100644
--- a/doc/classes/GraphNode.xml
+++ b/doc/classes/GraphNode.xml
@@ -292,25 +292,25 @@
</constant>
</constants>
<theme_items>
- <theme_item name="close_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
+ <theme_item name="close_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
The color modulation applied to the close button icon.
</theme_item>
- <theme_item name="resizer_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
+ <theme_item name="resizer_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
The color modulation applied to the resizer icon.
</theme_item>
- <theme_item name="title_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
+ <theme_item name="title_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Color of the title text.
</theme_item>
- <theme_item name="close_offset" data_type="constant" type="int" default="18">
+ <theme_item name="close_offset" data_type="constant" type="int" default="22">
The vertical offset of the close button.
</theme_item>
- <theme_item name="port_offset" data_type="constant" type="int" default="3">
+ <theme_item name="port_offset" data_type="constant" type="int" default="0">
Horizontal offset for the ports.
</theme_item>
- <theme_item name="separation" data_type="constant" type="int" default="1">
+ <theme_item name="separation" data_type="constant" type="int" default="2">
The vertical distance between ports.
</theme_item>
- <theme_item name="title_offset" data_type="constant" type="int" default="20">
+ <theme_item name="title_offset" data_type="constant" type="int" default="26">
Vertical offset of the title text.
</theme_item>
<theme_item name="title_font" data_type="font" type="Font">
@@ -334,10 +334,6 @@
<theme_item name="commentfocus" data_type="style" type="StyleBox">
The [StyleBox] used when [member comment] is enabled and the [GraphNode] is focused.
</theme_item>
- <theme_item name="defaultfocus" data_type="style" type="StyleBox">
- </theme_item>
- <theme_item name="defaultframe" data_type="style" type="StyleBox">
- </theme_item>
<theme_item name="frame" data_type="style" type="StyleBox">
The default background for [GraphNode].
</theme_item>
diff --git a/doc/classes/HFlowContainer.xml b/doc/classes/HFlowContainer.xml
new file mode 100644
index 0000000000..8ee2da69b7
--- /dev/null
+++ b/doc/classes/HFlowContainer.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="HFlowContainer" inherits="FlowContainer" version="4.0">
+ <brief_description>
+ Horizontal flow container.
+ </brief_description>
+ <description>
+ Horizontal version of [FlowContainer].
+ </description>
+ <tutorials>
+ </tutorials>
+ <theme_items>
+ <theme_item name="hseparation" data_type="constant" type="int" default="4">
+ The horizontal separation of children nodes.
+ </theme_item>
+ <theme_item name="vseparation" data_type="constant" type="int" default="4">
+ The vertical separation of children nodes.
+ </theme_item>
+ </theme_items>
+</class>
diff --git a/doc/classes/HSplitContainer.xml b/doc/classes/HSplitContainer.xml
index f2c505b1bc..6ba079bc66 100644
--- a/doc/classes/HSplitContainer.xml
+++ b/doc/classes/HSplitContainer.xml
@@ -18,7 +18,5 @@
<theme_item name="grabber" data_type="icon" type="Texture2D">
The icon used for the grabber drawn in the middle area.
</theme_item>
- <theme_item name="bg" data_type="style" type="StyleBox">
- </theme_item>
</theme_items>
</class>
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index 29feb107d4..60d4b664d2 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -135,7 +135,7 @@
<return type="int" enum="Error" />
<description>
Decompresses the image if it is VRAM compressed in a supported format. Returns [constant OK] if the format is supported, otherwise [constant ERR_UNAVAILABLE].
- [b]Note:[/b] The following formats can be decompressed: DXT, RGTC, BPTC, PVRTC1. The formats ETC1 and ETC2 are not supported.
+ [b]Note:[/b] The following formats can be decompressed: DXT, RGTC, BPTC. The formats ETC1 and ETC2 are not supported.
</description>
</method>
<method name="detect_alpha" qualifiers="const">
@@ -452,7 +452,7 @@
</method>
</methods>
<members>
- <member name="data" type="Dictionary" setter="_set_data" getter="_get_data" default="{&quot;data&quot;: PackedByteArray(),&quot;format&quot;: &quot;Lum8&quot;,&quot;height&quot;: 0,&quot;mipmaps&quot;: false,&quot;width&quot;: 0}">
+ <member name="data" type="Dictionary" setter="_set_data" getter="_get_data" default="{ &quot;data&quot;: PackedByteArray(), &quot;format&quot;: &quot;Lum8&quot;, &quot;height&quot;: 0, &quot;mipmaps&quot;: false, &quot;width&quot;: 0 }">
Holds all the image's color data in a given format. See [enum Format] constants.
</member>
</members>
@@ -543,52 +543,38 @@
<constant name="FORMAT_BPTC_RGBFU" value="24" enum="Format">
Texture format that uses [url=https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression]BPTC[/url] compression with unsigned floating-point RGB components.
</constant>
- <constant name="FORMAT_PVRTC1_2" value="25" enum="Format">
- Texture format used on PowerVR-supported mobile platforms, uses 2-bit color depth with no alpha. More information can be found [url=https://en.wikipedia.org/wiki/PVRTC]here[/url].
- [b]Note:[/b] When creating an [ImageTexture], an sRGB to linear color space conversion is performed.
- </constant>
- <constant name="FORMAT_PVRTC1_2A" value="26" enum="Format">
- Same as [constant FORMAT_PVRTC1_2], but with an alpha component.
- </constant>
- <constant name="FORMAT_PVRTC1_4" value="27" enum="Format">
- Texture format used on PowerVR-supported mobile platforms, uses 4-bit color depth with no alpha. More information can be found [url=https://en.wikipedia.org/wiki/PVRTC]here[/url].
- [b]Note:[/b] When creating an [ImageTexture], an sRGB to linear color space conversion is performed.
- </constant>
- <constant name="FORMAT_PVRTC1_4A" value="28" enum="Format">
- Same as [constant FORMAT_PVRTC1_4], but with an alpha component.
- </constant>
- <constant name="FORMAT_ETC" value="29" enum="Format">
+ <constant name="FORMAT_ETC" value="25" enum="Format">
[url=https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC1]Ericsson Texture Compression format 1[/url], also referred to as "ETC1", and is part of the OpenGL ES graphics standard. This format cannot store an alpha channel.
</constant>
- <constant name="FORMAT_ETC2_R11" value="30" enum="Format">
+ <constant name="FORMAT_ETC2_R11" value="26" enum="Format">
[url=https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC2_and_EAC]Ericsson Texture Compression format 2[/url] ([code]R11_EAC[/code] variant), which provides one channel of unsigned data.
</constant>
- <constant name="FORMAT_ETC2_R11S" value="31" enum="Format">
+ <constant name="FORMAT_ETC2_R11S" value="27" enum="Format">
[url=https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC2_and_EAC]Ericsson Texture Compression format 2[/url] ([code]SIGNED_R11_EAC[/code] variant), which provides one channel of signed data.
</constant>
- <constant name="FORMAT_ETC2_RG11" value="32" enum="Format">
+ <constant name="FORMAT_ETC2_RG11" value="28" enum="Format">
[url=https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC2_and_EAC]Ericsson Texture Compression format 2[/url] ([code]RG11_EAC[/code] variant), which provides two channels of unsigned data.
</constant>
- <constant name="FORMAT_ETC2_RG11S" value="33" enum="Format">
+ <constant name="FORMAT_ETC2_RG11S" value="29" enum="Format">
[url=https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC2_and_EAC]Ericsson Texture Compression format 2[/url] ([code]SIGNED_RG11_EAC[/code] variant), which provides two channels of signed data.
</constant>
- <constant name="FORMAT_ETC2_RGB8" value="34" enum="Format">
+ <constant name="FORMAT_ETC2_RGB8" value="30" enum="Format">
[url=https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC2_and_EAC]Ericsson Texture Compression format 2[/url] ([code]RGB8[/code] variant), which is a follow-up of ETC1 and compresses RGB888 data.
[b]Note:[/b] When creating an [ImageTexture], an sRGB to linear color space conversion is performed.
</constant>
- <constant name="FORMAT_ETC2_RGBA8" value="35" enum="Format">
+ <constant name="FORMAT_ETC2_RGBA8" value="31" enum="Format">
[url=https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC2_and_EAC]Ericsson Texture Compression format 2[/url] ([code]RGBA8[/code]variant), which compresses RGBA8888 data with full alpha support.
[b]Note:[/b] When creating an [ImageTexture], an sRGB to linear color space conversion is performed.
</constant>
- <constant name="FORMAT_ETC2_RGB8A1" value="36" enum="Format">
+ <constant name="FORMAT_ETC2_RGB8A1" value="32" enum="Format">
[url=https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC2_and_EAC]Ericsson Texture Compression format 2[/url] ([code]RGB8_PUNCHTHROUGH_ALPHA1[/code] variant), which compresses RGBA data to make alpha either fully transparent or fully opaque.
[b]Note:[/b] When creating an [ImageTexture], an sRGB to linear color space conversion is performed.
</constant>
- <constant name="FORMAT_ETC2_RA_AS_RG" value="37" enum="Format">
+ <constant name="FORMAT_ETC2_RA_AS_RG" value="33" enum="Format">
</constant>
- <constant name="FORMAT_DXT5_RA_AS_RG" value="38" enum="Format">
+ <constant name="FORMAT_DXT5_RA_AS_RG" value="34" enum="Format">
</constant>
- <constant name="FORMAT_MAX" value="39" enum="Format">
+ <constant name="FORMAT_MAX" value="35" enum="Format">
Represents the size of the [enum Format] enum.
</constant>
<constant name="INTERPOLATE_NEAREST" value="0" enum="Interpolation">
@@ -622,16 +608,13 @@
<constant name="COMPRESS_S3TC" value="0" enum="CompressMode">
Use S3TC compression.
</constant>
- <constant name="COMPRESS_PVRTC1_4" value="1" enum="CompressMode">
- Use PVRTC1 4-bpp compression.
- </constant>
- <constant name="COMPRESS_ETC" value="2" enum="CompressMode">
+ <constant name="COMPRESS_ETC" value="1" enum="CompressMode">
Use ETC compression.
</constant>
- <constant name="COMPRESS_ETC2" value="3" enum="CompressMode">
+ <constant name="COMPRESS_ETC2" value="2" enum="CompressMode">
Use ETC2 compression.
</constant>
- <constant name="COMPRESS_BPTC" value="4" enum="CompressMode">
+ <constant name="COMPRESS_BPTC" value="3" enum="CompressMode">
Use BPTC compression.
</constant>
<constant name="USED_CHANNELS_L" value="0" enum="UsedChannels">
diff --git a/doc/classes/ImporterMesh.xml b/doc/classes/ImporterMesh.xml
index ab344f908c..4c323156c9 100644
--- a/doc/classes/ImporterMesh.xml
+++ b/doc/classes/ImporterMesh.xml
@@ -23,8 +23,7 @@
<argument index="0" name="primitive" type="int" enum="Mesh.PrimitiveType" />
<argument index="1" name="arrays" type="Array" />
<argument index="2" name="blend_shapes" type="Array" default="[]" />
- <argument index="3" name="lods" type="Dictionary" default="{
-}" />
+ <argument index="3" name="lods" type="Dictionary" default="{}" />
<argument index="4" name="material" type="Material" default="null" />
<argument index="5" name="name" type="String" default="&quot;&quot;" />
<argument index="6" name="flags" type="int" default="0" />
@@ -178,7 +177,7 @@
</method>
</methods>
<members>
- <member name="_data" type="Dictionary" setter="_set_data" getter="_get_data" default="{&quot;surfaces&quot;: []}">
+ <member name="_data" type="Dictionary" setter="_set_data" getter="_get_data" default="{ &quot;surfaces&quot;: [] }">
</member>
</members>
</class>
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index 7129f2b67d..b5468755c1 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -209,6 +209,12 @@
[b]Note:[/b] Due to keyboard ghosting, [method is_action_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information.
</description>
</method>
+ <method name="is_anything_pressed" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if any action, key, joypad button, or mouse button is being pressed. This will also return [code]true[/code] if any action is simulated via code by calling [method action_press].
+ </description>
+ </method>
<method name="is_joy_button_pressed" qualifiers="const">
<return type="bool" />
<argument index="0" name="device" type="int" />
@@ -377,7 +383,8 @@
<return type="void" />
<argument index="0" name="to" type="Vector2" />
<description>
- Sets the mouse position to the specified vector.
+ Sets the mouse position to the specified vector, provided in pixels and relative to an origin at the upper left corner of the game window.
+ Mouse position is clipped to the limits of the screen resolution, or to the limits of the game window if [enum MouseMode] is set to [code]MOUSE_MODE_CONFINED[/code] or [code]MOUSE_MODE_CONFINED_HIDDEN[/code].
</description>
</method>
</methods>
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index 593d0c9523..c87c5d2868 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -463,7 +463,7 @@
</constant>
</constants>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.63, 0.63, 0.63, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.65, 0.65, 0.65, 1)">
Default text [Color] of the item.
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
diff --git a/doc/classes/JSON.xml b/doc/classes/JSON.xml
index 63e6307b39..fed9df8bd8 100644
--- a/doc/classes/JSON.xml
+++ b/doc/classes/JSON.xml
@@ -66,7 +66,7 @@
Converts a [Variant] var to JSON text and returns the result. Useful for serializing data to store or send over the network.
[b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, converting a Variant to JSON text will convert all numerical values to [float] types.
[b]Note:[/b] If [code]full_precision[/code] is true, when stringifying floats, the unreliable digits are stringified in addition to the reliable digits to guarantee exact decoding.
- Use [code]indent[/code] parameter to pretty stringify the output.
+ The [code]indent[/code] parameter controls if and how something is indented, the string used for this parameter will be used where there should be an indent in the output, even spaces like [code]" "[/code] will work. [code]\t[/code] and [code]\n[/code] can also be used for a tab indent, or to make a newline for each indent respectively.
[b]Example output:[/b]
[codeblock]
## JSON.stringify(my_dictionary)
@@ -74,18 +74,34 @@
## JSON.stringify(my_dictionary, "\t")
{
- "name": "my_dictionary",
- "version": "1.0.0",
- "entities": [
- {
- "name": "entity_0",
- "value": "value_0"
- },
- {
- "name": "entity_1",
- "value": "value_1"
- }
- ]
+ "name": "my_dictionary",
+ "version": "1.0.0",
+ "entities": [
+ {
+ "name": "entity_0",
+ "value": "value_0"
+ },
+ {
+ "name": "entity_1",
+ "value": "value_1"
+ }
+ ]
+ }
+
+ ## JSON.stringify(my_dictionary, "...")
+ {
+ ..."name": "my_dictionary",
+ ..."version": "1.0.0",
+ ..."entities": [
+ ......{
+ ........."name": "entity_0",
+ ........."value": "value_0"
+ ......},
+ ......{
+ ........."name": "entity_1",
+ ........."value": "value_1"
+ ......}
+ ...]
}
[/codeblock]
</description>
diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml
index 009ad1f609..b8d46755c7 100644
--- a/doc/classes/Light3D.xml
+++ b/doc/classes/Light3D.xml
@@ -34,8 +34,9 @@
<member name="light_angular_distance" type="float" setter="set_param" getter="get_param" default="0.0">
The light's angular size in degrees. Increasing this will make shadows softer at greater distances. Only available for [DirectionalLight3D]s. For reference, the Sun from the Earth is approximately [code]0.5[/code].
</member>
- <member name="light_bake_mode" type="int" setter="set_bake_mode" getter="get_bake_mode" enum="Light3D.BakeMode" default="1">
- The light's bake mode. See [enum BakeMode].
+ <member name="light_bake_mode" type="int" setter="set_bake_mode" getter="get_bake_mode" enum="Light3D.BakeMode" default="2">
+ The light's bake mode. This will affect the global illumination techniques that have an effect on the light's rendering. See [enum BakeMode].
+ [b]Note:[/b] Meshes' global illumination mode will also affect the global illumination rendering. See [member GeometryInstance3D.gi_mode].
</member>
<member name="light_color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)">
The light's color. An [i]overbright[/i] color can be used to achieve a result equivalent to increasing the light's [member light_energy].
@@ -146,12 +147,14 @@
Represents the size of the [enum Param] enum.
</constant>
<constant name="BAKE_DISABLED" value="0" enum="BakeMode">
- Light is ignored when baking.
- [b]Note:[/b] Hiding a light does [i]not[/i] affect baking.
+ Light is ignored when baking. This is the fastest mode, but the light will be taken into account when baking global illumination. This mode should generally be used for dynamic lights that change quickly, as the effect of global illumination is less noticeable on those lights.
+ [b]Note:[/b] Hiding a light does [i]not[/i] affect baking [LightmapGI]. Hiding a light will still affect baking [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled).
</constant>
- <constant name="BAKE_DYNAMIC" value="1" enum="BakeMode">
+ <constant name="BAKE_STATIC" value="1" enum="BakeMode">
+ Light is taken into account in static baking ([VoxelGI], [LightmapGI], SDFGI ([member Environment.sdfgi_enabled])). The light can be moved around or modified, but its global illumination will not update in real-time. This is suitable for subtle changes (such as flickering torches), but generally not large changes such as toggling a light on and off.
</constant>
- <constant name="BAKE_STATIC" value="2" enum="BakeMode">
+ <constant name="BAKE_DYNAMIC" value="2" enum="BakeMode">
+ Light is taken into account in dynamic baking ([VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) only). The light can be moved around or modified with global illumination updating in real-time. The light's global illumination appearance will be slightly different compared to [constant BAKE_STATIC]. This has a greater performance cost compared to [constant BAKE_STATIC].
</constant>
</constants>
</class>
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index ff01ad72fd..0b97865770 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -233,9 +233,6 @@
[b]Note:[/b] This method is only implemented on Linux.
</member>
<member name="mouse_default_cursor_shape" type="int" setter="set_default_cursor_shape" getter="get_default_cursor_shape" overrides="Control" enum="Control.CursorShape" default="1" />
- <member name="placeholder_alpha" type="float" setter="set_placeholder_alpha" getter="get_placeholder_alpha" default="0.6">
- Opacity of the [member placeholder_text]. From [code]0[/code] to [code]1[/code].
- </member>
<member name="placeholder_text" type="String" setter="set_placeholder" getter="get_placeholder" default="&quot;&quot;">
Text shown when the [LineEdit] is empty. It is [b]not[/b] the [LineEdit]'s default value (see [member text]).
</member>
@@ -382,28 +379,31 @@
</constant>
</constants>
<theme_items>
- <theme_item name="caret_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="caret_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Color of the [LineEdit]'s caret (text cursor). This can be set to a fully transparent color to hide the caret entirely.
</theme_item>
- <theme_item name="clear_button_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="clear_button_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Color used as default tint for the clear button.
</theme_item>
<theme_item name="clear_button_color_pressed" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Color used for the clear button when it's pressed.
</theme_item>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Default font color.
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the [LineEdit].
</theme_item>
- <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
+ <theme_item name="font_placeholder_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.6)">
+ Font color for [member placeholder_text].
+ </theme_item>
+ <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Font color for selected text (inside the selection rectangle).
</theme_item>
- <theme_item name="font_uneditable_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 0.5)">
+ <theme_item name="font_uneditable_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
Font color when editing is disabled.
</theme_item>
- <theme_item name="selection_color" data_type="color" type="Color" default="Color(0.49, 0.49, 0.49, 1)">
+ <theme_item name="selection_color" data_type="color" type="Color" default="Color(0.5, 0.5, 0.5, 1)">
Color of the selection rectangle.
</theme_item>
<theme_item name="caret_width" data_type="constant" type="int" default="1">
diff --git a/doc/classes/LinkButton.xml b/doc/classes/LinkButton.xml
index a20db4d7e9..4377fc1036 100644
--- a/doc/classes/LinkButton.xml
+++ b/doc/classes/LinkButton.xml
@@ -66,13 +66,13 @@
</constant>
</constants>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Default text [Color] of the [LinkButton].
</theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] used when the [LinkButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
</theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] used when the [LinkButton] is being hovered.
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
diff --git a/doc/classes/MenuButton.xml b/doc/classes/MenuButton.xml
index 632b22c1de..79d1c05be4 100644
--- a/doc/classes/MenuButton.xml
+++ b/doc/classes/MenuButton.xml
@@ -46,16 +46,16 @@
</signal>
</signals>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Default text [Color] of the [MenuButton].
</theme_item>
<theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(1, 1, 1, 0.3)">
Text [Color] used when the [MenuButton] is disabled.
</theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] used when the [MenuButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
</theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] used when the [MenuButton] is being hovered.
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 1ed4b335ad..c752291588 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -51,8 +51,10 @@
<return type="int" />
<argument index="0" name="path" type="String" />
<argument index="1" name="arguments" type="PackedStringArray" />
+ <argument index="2" name="open_console" type="bool" default="false" />
<description>
Creates a new process that runs independently of Godot. It will not terminate if Godot terminates. The path specified in [code]path[/code] must exist and be executable file or macOS .app bundle. Platform path resolution will be used. The [code]arguments[/code] are used in the given order and separated by a space.
+ On Windows, if [code]open_console[/code] is [code]true[/code] and the process is a console app, a new terminal window will be opened. This is ignored on other platforms.
If the process creation succeeds, the method will return the new process ID, which you can use to monitor the process (and potentially terminate it with [method kill]). If the process creation fails, the method will return [code]-1[/code].
For example, running another instance of the project:
[codeblocks]
@@ -109,8 +111,10 @@
<argument index="1" name="arguments" type="PackedStringArray" />
<argument index="2" name="output" type="Array" default="[]" />
<argument index="3" name="read_stderr" type="bool" default="false" />
+ <argument index="4" name="open_console" type="bool" default="false" />
<description>
Executes a command. The file specified in [code]path[/code] must exist and be executable. Platform path resolution will be used. The [code]arguments[/code] are used in the given order and separated by a space. If an [code]output[/code] [Array] is provided, the complete shell output of the process will be appended as a single [String] element in [code]output[/code]. If [code]read_stderr[/code] is [code]true[/code], the output to the standard error stream will be included too.
+ On Windows, if [code]open_console[/code] is [code]true[/code] and the process is a console app, a new terminal window will be opened. This is ignored on other platforms.
If the command is successfully executed, the method will return the exit code of the command, or [code]-1[/code] if it fails.
[b]Note:[/b] The Godot thread will pause its execution until the executed command terminates. Use [Thread] to create a separate thread that will not pause the Godot thread, or use [method create_process] to create a completely independent process.
For example, to retrieve a list of the working directory's contents:
@@ -124,7 +128,7 @@
int exitCode = OS.Execute("ls", new string[] {"-l", "/tmp"}, output);
[/csharp]
[/codeblocks]
- To execute a composite command, a platform-specific shell can be invoked. For example:
+ If you wish to access a shell built-in or execute a composite command, a platform-specific shell can be invoked. For example:
[codeblocks]
[gdscript]
var output = []
@@ -136,6 +140,9 @@
[/csharp]
[/codeblocks]
[b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
+ [b]Note:[/b] To execute a Windows command interpreter built-in command, specify [code]cmd.exe[/code] in [code]path[/code], [code]/c[/code] as the first argument, and the desired command as the second argument.
+ [b]Note:[/b] To execute a PowerShell built-in command, specify [code]powershell.exe[/code] in [code]path[/code], [code]-Command[/code] as the first argument, and the desired command as the second argument.
+ [b]Note:[/b] To execute a Unix shell built-in command, specify shell executable name in [code]path[/code], [code]-c[/code] as the first argument, and the desired command as the second argument.
[b]Note:[/b] On macOS, sandboxed applications are limited to run only embedded helper executables, specified during export.
</description>
</method>
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index f5f6ba8b6d..bdc3a09322 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -490,7 +490,7 @@
<argument index="0" name="property" type="String" />
<argument index="1" name="value" type="Variant" />
<description>
- Assigns a new value to the given property. If the [code]property[/code] does not exist, nothing will happen.
+ Assigns a new value to the given property. If the [code]property[/code] does not exist or the given value's type doesn't match, nothing will happen.
[b]Note:[/b] In C#, the property name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined properties where you should use the same convention as in the C# source (typically PascalCase).
</description>
</method>
diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml
index 87561917f3..02226df46b 100644
--- a/doc/classes/OptionButton.xml
+++ b/doc/classes/OptionButton.xml
@@ -84,7 +84,7 @@
<method name="get_selected_id" qualifiers="const">
<return type="int" />
<description>
- Returns the ID of the selected item, or [code]0[/code] if no item is selected.
+ Returns the ID of the selected item, or [code]-1[/code] if no item is selected.
</description>
</method>
<method name="get_selected_metadata" qualifiers="const">
@@ -112,6 +112,7 @@
<argument index="0" name="idx" type="int" />
<description>
Selects an item by index and makes it the current item. This will work even if the item is disabled.
+ Passing [code]-1[/code] as the index deselects any currently selected item.
</description>
</method>
<method name="set_item_disabled">
@@ -182,16 +183,16 @@
</signal>
</signals>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Default text [Color] of the [OptionButton].
</theme_item>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.2)">
+ <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
Text [Color] used when the [OptionButton] is disabled.
</theme_item>
- <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] used when the [OptionButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
</theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] used when the [OptionButton] is being hovered.
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
@@ -200,7 +201,7 @@
<theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Text [Color] used when the [OptionButton] is being pressed.
</theme_item>
- <theme_item name="arrow_margin" data_type="constant" type="int" default="2">
+ <theme_item name="arrow_margin" data_type="constant" type="int" default="4">
The horizontal space between the arrow icon and the right edge of the button.
</theme_item>
<theme_item name="hseparation" data_type="constant" type="int" default="2">
diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml
index 686854ffe8..3dc8307d44 100644
--- a/doc/classes/PackedByteArray.xml
+++ b/doc/classes/PackedByteArray.xml
@@ -369,10 +369,11 @@
<method name="slice" qualifiers="const">
<return type="PackedByteArray" />
<argument index="0" name="begin" type="int" />
- <argument index="1" name="end" type="int" />
+ <argument index="1" name="end" type="int" default="2147483647" />
<description>
Returns the slice of the [PackedByteArray], from [code]begin[/code] (inclusive) to [code]end[/code] (exclusive), as a new [PackedByteArray].
- If [code]end[/code]is negative, it will be relative to the end of the array.
+ The absolute value of [code]begin[/code] and [code]end[/code] will be clamped to the array size, so the default value for [code]end[/code] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+ If either [code]begin[/code] or [code]end[/code] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
</description>
</method>
<method name="sort">
diff --git a/doc/classes/PackedColorArray.xml b/doc/classes/PackedColorArray.xml
index d875549319..8aac7d1bf4 100644
--- a/doc/classes/PackedColorArray.xml
+++ b/doc/classes/PackedColorArray.xml
@@ -132,8 +132,11 @@
<method name="slice" qualifiers="const">
<return type="PackedColorArray" />
<argument index="0" name="begin" type="int" />
- <argument index="1" name="end" type="int" />
+ <argument index="1" name="end" type="int" default="2147483647" />
<description>
+ Returns the slice of the [PackedColorArray], from [code]begin[/code] (inclusive) to [code]end[/code] (exclusive), as a new [PackedColorArray].
+ The absolute value of [code]begin[/code] and [code]end[/code] will be clamped to the array size, so the default value for [code]end[/code] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+ If either [code]begin[/code] or [code]end[/code] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
</description>
</method>
<method name="sort">
diff --git a/doc/classes/PackedFloat32Array.xml b/doc/classes/PackedFloat32Array.xml
index 6c77c4bee2..0e66dd7967 100644
--- a/doc/classes/PackedFloat32Array.xml
+++ b/doc/classes/PackedFloat32Array.xml
@@ -133,8 +133,11 @@
<method name="slice" qualifiers="const">
<return type="PackedFloat32Array" />
<argument index="0" name="begin" type="int" />
- <argument index="1" name="end" type="int" />
+ <argument index="1" name="end" type="int" default="2147483647" />
<description>
+ Returns the slice of the [PackedFloat32Array], from [code]begin[/code] (inclusive) to [code]end[/code] (exclusive), as a new [PackedFloat32Array].
+ The absolute value of [code]begin[/code] and [code]end[/code] will be clamped to the array size, so the default value for [code]end[/code] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+ If either [code]begin[/code] or [code]end[/code] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
</description>
</method>
<method name="sort">
diff --git a/doc/classes/PackedFloat64Array.xml b/doc/classes/PackedFloat64Array.xml
index a8282c099a..eaad4fec54 100644
--- a/doc/classes/PackedFloat64Array.xml
+++ b/doc/classes/PackedFloat64Array.xml
@@ -133,8 +133,11 @@
<method name="slice" qualifiers="const">
<return type="PackedFloat64Array" />
<argument index="0" name="begin" type="int" />
- <argument index="1" name="end" type="int" />
+ <argument index="1" name="end" type="int" default="2147483647" />
<description>
+ Returns the slice of the [PackedFloat64Array], from [code]begin[/code] (inclusive) to [code]end[/code] (exclusive), as a new [PackedFloat64Array].
+ The absolute value of [code]begin[/code] and [code]end[/code] will be clamped to the array size, so the default value for [code]end[/code] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+ If either [code]begin[/code] or [code]end[/code] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
</description>
</method>
<method name="sort">
diff --git a/doc/classes/PackedInt32Array.xml b/doc/classes/PackedInt32Array.xml
index da26e93b96..ec698ed8e5 100644
--- a/doc/classes/PackedInt32Array.xml
+++ b/doc/classes/PackedInt32Array.xml
@@ -133,8 +133,11 @@
<method name="slice" qualifiers="const">
<return type="PackedInt32Array" />
<argument index="0" name="begin" type="int" />
- <argument index="1" name="end" type="int" />
+ <argument index="1" name="end" type="int" default="2147483647" />
<description>
+ Returns the slice of the [PackedInt32Array], from [code]begin[/code] (inclusive) to [code]end[/code] (exclusive), as a new [PackedInt32Array].
+ The absolute value of [code]begin[/code] and [code]end[/code] will be clamped to the array size, so the default value for [code]end[/code] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+ If either [code]begin[/code] or [code]end[/code] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
</description>
</method>
<method name="sort">
diff --git a/doc/classes/PackedInt64Array.xml b/doc/classes/PackedInt64Array.xml
index 9ddf43a837..ec4b3c1209 100644
--- a/doc/classes/PackedInt64Array.xml
+++ b/doc/classes/PackedInt64Array.xml
@@ -133,8 +133,11 @@
<method name="slice" qualifiers="const">
<return type="PackedInt64Array" />
<argument index="0" name="begin" type="int" />
- <argument index="1" name="end" type="int" />
+ <argument index="1" name="end" type="int" default="2147483647" />
<description>
+ Returns the slice of the [PackedInt64Array], from [code]begin[/code] (inclusive) to [code]end[/code] (exclusive), as a new [PackedInt64Array].
+ The absolute value of [code]begin[/code] and [code]end[/code] will be clamped to the array size, so the default value for [code]end[/code] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+ If either [code]begin[/code] or [code]end[/code] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
</description>
</method>
<method name="sort">
diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml
index d3a770b35b..4bba6563bb 100644
--- a/doc/classes/PackedScene.xml
+++ b/doc/classes/PackedScene.xml
@@ -104,7 +104,7 @@
</method>
</methods>
<members>
- <member name="_bundled" type="Dictionary" setter="_set_bundled_scene" getter="_get_bundled_scene" default="{&quot;conn_count&quot;: 0,&quot;conns&quot;: PackedInt32Array(),&quot;editable_instances&quot;: [],&quot;names&quot;: PackedStringArray(),&quot;node_count&quot;: 0,&quot;node_paths&quot;: [],&quot;nodes&quot;: PackedInt32Array(),&quot;variants&quot;: [],&quot;version&quot;: 2}">
+ <member name="_bundled" type="Dictionary" setter="_set_bundled_scene" getter="_get_bundled_scene" default="{ &quot;conn_count&quot;: 0, &quot;conns&quot;: PackedInt32Array(), &quot;editable_instances&quot;: [], &quot;names&quot;: PackedStringArray(), &quot;node_count&quot;: 0, &quot;node_paths&quot;: [], &quot;nodes&quot;: PackedInt32Array(), &quot;variants&quot;: [], &quot;version&quot;: 2 }">
A dictionary representation of the scene contents.
Available keys include "rnames" and "variants" for resources, "node_count", "nodes", "node_paths" for nodes, "editable_instances" for base scene children overrides, "conn_count" and "conns" for signal connections, and "version" for the format style of the PackedScene.
</member>
diff --git a/doc/classes/PackedStringArray.xml b/doc/classes/PackedStringArray.xml
index 439d59dde7..ebe9c591b8 100644
--- a/doc/classes/PackedStringArray.xml
+++ b/doc/classes/PackedStringArray.xml
@@ -133,8 +133,11 @@
<method name="slice" qualifiers="const">
<return type="PackedStringArray" />
<argument index="0" name="begin" type="int" />
- <argument index="1" name="end" type="int" />
+ <argument index="1" name="end" type="int" default="2147483647" />
<description>
+ Returns the slice of the [PackedStringArray], from [code]begin[/code] (inclusive) to [code]end[/code] (exclusive), as a new [PackedStringArray].
+ The absolute value of [code]begin[/code] and [code]end[/code] will be clamped to the array size, so the default value for [code]end[/code] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+ If either [code]begin[/code] or [code]end[/code] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
</description>
</method>
<method name="sort">
diff --git a/doc/classes/PackedVector2Array.xml b/doc/classes/PackedVector2Array.xml
index 8c4f052016..d72ca4b4bb 100644
--- a/doc/classes/PackedVector2Array.xml
+++ b/doc/classes/PackedVector2Array.xml
@@ -133,8 +133,11 @@
<method name="slice" qualifiers="const">
<return type="PackedVector2Array" />
<argument index="0" name="begin" type="int" />
- <argument index="1" name="end" type="int" />
+ <argument index="1" name="end" type="int" default="2147483647" />
<description>
+ Returns the slice of the [PackedVector2Array], from [code]begin[/code] (inclusive) to [code]end[/code] (exclusive), as a new [PackedVector2Array].
+ The absolute value of [code]begin[/code] and [code]end[/code] will be clamped to the array size, so the default value for [code]end[/code] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+ If either [code]begin[/code] or [code]end[/code] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
</description>
</method>
<method name="sort">
diff --git a/doc/classes/PackedVector3Array.xml b/doc/classes/PackedVector3Array.xml
index 8229af984d..cbae0c40e7 100644
--- a/doc/classes/PackedVector3Array.xml
+++ b/doc/classes/PackedVector3Array.xml
@@ -132,8 +132,11 @@
<method name="slice" qualifiers="const">
<return type="PackedVector3Array" />
<argument index="0" name="begin" type="int" />
- <argument index="1" name="end" type="int" />
+ <argument index="1" name="end" type="int" default="2147483647" />
<description>
+ Returns the slice of the [PackedVector3Array], from [code]begin[/code] (inclusive) to [code]end[/code] (exclusive), as a new [PackedVector3Array].
+ The absolute value of [code]begin[/code] and [code]end[/code] will be clamped to the array size, so the default value for [code]end[/code] makes it slice to the size of the array by default (i.e. [code]arr.slice(1)[/code] is a shorthand for [code]arr.slice(1, arr.size())[/code]).
+ If either [code]begin[/code] or [code]end[/code] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
</description>
</method>
<method name="sort">
diff --git a/doc/classes/PanoramaSkyMaterial.xml b/doc/classes/PanoramaSkyMaterial.xml
index 6707c03fac..a04626e9b0 100644
--- a/doc/classes/PanoramaSkyMaterial.xml
+++ b/doc/classes/PanoramaSkyMaterial.xml
@@ -11,6 +11,9 @@
<tutorials>
</tutorials>
<members>
+ <member name="filter" type="bool" setter="set_filtering_enabled" getter="is_filtering_enabled" default="true">
+ A boolean value to determine if the background texture should be filtered or not.
+ </member>
<member name="panorama" type="Texture2D" setter="set_panorama" getter="get_panorama">
[Texture2D] to be applied to the [PanoramaSkyMaterial].
</member>
diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml
index 43e27ea437..68f7b94551 100644
--- a/doc/classes/PhysicsBody2D.xml
+++ b/doc/classes/PhysicsBody2D.xml
@@ -25,12 +25,12 @@
</method>
<method name="move_and_collide">
<return type="KinematicCollision2D" />
- <argument index="0" name="linear_velocity" type="Vector2" />
+ <argument index="0" name="distance" type="Vector2" />
<argument index="1" name="test_only" type="bool" default="false" />
<argument index="2" name="safe_margin" type="float" default="0.08" />
<description>
- Moves the body along the vector [code]linear_velocity[/code]. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed.
- The body will stop if it collides. Returns a [KinematicCollision2D], which contains information about the collision when stopped, or when touching another body along the motion.
+ Moves the body along the vector [code]distance[/code]. In order to be frame rate independent in [method Node._physics_process] or [method Node._process], [code]distance[/code] should be computed using [code]delta[/code].
+ Returns a [KinematicCollision2D], which contains information about the collision when stopped, or when touching another body along the motion.
If [code]test_only[/code] is [code]true[/code], the body does not move but the would-be collision information is given.
[code]safe_margin[/code] is the extra margin used for collision recovery (see [member CharacterBody2D.collision/safe_margin] for more details).
</description>
@@ -45,12 +45,12 @@
<method name="test_move">
<return type="bool" />
<argument index="0" name="from" type="Transform2D" />
- <argument index="1" name="linear_velocity" type="Vector2" />
+ <argument index="1" name="distance" type="Vector2" />
<argument index="2" name="collision" type="KinematicCollision2D" default="null" />
<argument index="3" name="safe_margin" type="float" default="0.08" />
<description>
- Checks for collisions without moving the body. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed.
- Virtually sets the node's position, scale and rotation to that of the given [Transform2D], then tries to move the body along the vector [code]linear_velocity[/code]. Returns [code]true[/code] if a collision would stop the body from moving along the whole path.
+ Checks for collisions without moving the body. In order to be frame rate independent in [method Node._physics_process] or [method Node._process], [code]distance[/code] should be computed using [code]delta[/code].
+ Virtually sets the node's position, scale and rotation to that of the given [Transform2D], then tries to move the body along the vector [code]distance[/code]. Returns [code]true[/code] if a collision would stop the body from moving along the whole path.
[code]collision[/code] is an optional object of type [KinematicCollision2D], which contains additional information about the collision when stopped, or when touching another body along the motion.
[code]safe_margin[/code] is the extra margin used for collision recovery (see [member CharacterBody2D.collision/safe_margin] for more details).
</description>
diff --git a/doc/classes/PhysicsBody3D.xml b/doc/classes/PhysicsBody3D.xml
index 3c52850eec..4ea93d9f54 100644
--- a/doc/classes/PhysicsBody3D.xml
+++ b/doc/classes/PhysicsBody3D.xml
@@ -32,12 +32,12 @@
</method>
<method name="move_and_collide">
<return type="KinematicCollision3D" />
- <argument index="0" name="linear_velocity" type="Vector3" />
+ <argument index="0" name="distance" type="Vector3" />
<argument index="1" name="test_only" type="bool" default="false" />
<argument index="2" name="safe_margin" type="float" default="0.001" />
<argument index="3" name="max_collisions" type="int" default="1" />
<description>
- Moves the body along the vector [code]linear_velocity[/code]. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed.
+ Moves the body along the vector [code]distance[/code]. In order to be frame rate independent in [method Node._physics_process] or [method Node._process], [code]distance[/code] should be computed using [code]delta[/code].
The body will stop if it collides. Returns a [KinematicCollision3D], which contains information about the collision when stopped, or when touching another body along the motion.
If [code]test_only[/code] is [code]true[/code], the body does not move but the would-be collision information is given.
[code]safe_margin[/code] is the extra margin used for collision recovery (see [member CharacterBody3D.collision/safe_margin] for more details).
@@ -62,13 +62,13 @@
<method name="test_move">
<return type="bool" />
<argument index="0" name="from" type="Transform3D" />
- <argument index="1" name="linear_velocity" type="Vector3" />
+ <argument index="1" name="distance" type="Vector3" />
<argument index="2" name="collision" type="KinematicCollision3D" default="null" />
<argument index="3" name="safe_margin" type="float" default="0.001" />
<argument index="4" name="max_collisions" type="int" default="1" />
<description>
- Checks for collisions without moving the body. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed.
- Virtually sets the node's position, scale and rotation to that of the given [Transform3D], then tries to move the body along the vector [code]linear_velocity[/code]. Returns [code]true[/code] if a collision would stop the body from moving along the whole path.
+ Checks for collisions without moving the body. In order to be frame rate independent in [method Node._physics_process] or [method Node._process], [code]distance[/code] should be computed using [code]delta[/code].
+ Virtually sets the node's position, scale and rotation to that of the given [Transform3D], then tries to move the body along the vector [code]distance[/code]. Returns [code]true[/code] if a collision would stop the body from moving along the whole path.
[code]collision[/code] is an optional object of type [KinematicCollision3D], which contains additional information about the collision when stopped, or when touching another body along the motion.
[code]safe_margin[/code] is the extra margin used for collision recovery (see [member CharacterBody3D.collision/safe_margin] for more details).
[code]max_collisions[/code] allows to retrieve more than one collision result.
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index b45f62394d..eb1b0aada7 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -331,6 +331,13 @@
[b]Note:[/b] The indices of items after the removed item will be shifted by one.
</description>
</method>
+ <method name="set_current_index">
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <description>
+ Sets the currently focused item as the given [code]index[/code].
+ </description>
+ </method>
<method name="set_item_accelerator">
<return type="void" />
<argument index="0" name="index" type="int" />
@@ -538,19 +545,19 @@
<theme_item name="font_accelerator_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 0.8)">
The text [Color] used for shortcuts and accelerators that show next to the menu item name when defined. See [method get_item_accelerator] for more info on accelerators.
</theme_item>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
The default text [Color] for menu items' names.
</theme_item>
<theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.4, 0.4, 0.4, 0.8)">
[Color] used for disabled menu items' text.
</theme_item>
- <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
[Color] used for the hovered text.
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the menu item.
</theme_item>
- <theme_item name="font_separator_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_separator_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
[Color] used for labeled separators' text. See [method add_separator].
</theme_item>
<theme_item name="hseparation" data_type="constant" type="int" default="4">
diff --git a/doc/classes/ProgressBar.xml b/doc/classes/ProgressBar.xml
index 24cc490ce7..923ce0c53e 100644
--- a/doc/classes/ProgressBar.xml
+++ b/doc/classes/ProgressBar.xml
@@ -16,7 +16,7 @@
<member name="step" type="float" setter="set_step" getter="get_step" overrides="Range" default="0.01" />
</members>
<theme_items>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
The color of the text.
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 181bcccf4b..dc40d2fd1b 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -487,7 +487,7 @@
</member>
<member name="display/window/handheld/orientation" type="int" setter="" getter="" default="0">
The default screen orientation to use on mobile devices. See [enum DisplayServer.ScreenOrientation] for possible values.
- [b]Note:[/b] When set to a portrait orientation, this project setting does not flip the project resolution's width and height automatically. Instead, you have to set [member display/window/size/width] and [member display/window/size/height] accordingly.
+ [b]Note:[/b] When set to a portrait orientation, this project setting does not flip the project resolution's width and height automatically. Instead, you have to set [member display/window/size/viewport_width] and [member display/window/size/viewport_height] accordingly.
</member>
<member name="display/window/ios/hide_home_indicator" type="bool" setter="" getter="" default="true">
If [code]true[/code], the home indicator is hidden automatically. This only affects iOS devices without a physical home button.
@@ -505,21 +505,23 @@
Regardless of the platform, enabling fullscreen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling fullscreen mode.
[b]Note:[/b] This setting is ignored on iOS, Android, and HTML5.
</member>
- <member name="display/window/size/height" type="int" setter="" getter="" default="600">
- Sets the game's main viewport height. On desktop platforms, this is the default window size. Stretch mode settings also use this as a reference when enabled.
- </member>
<member name="display/window/size/resizable" type="bool" setter="" getter="" default="true">
Allows the window to be resizable by default.
[b]Note:[/b] This setting is ignored on iOS and Android.
</member>
- <member name="display/window/size/test_height" type="int" setter="" getter="" default="0">
- If greater than zero, overrides the window height when running the game. Useful for testing stretch modes.
+ <member name="display/window/size/viewport_height" type="int" setter="" getter="" default="600">
+ Sets the game's main viewport height. On desktop platforms, this is also the initial window height.
+ </member>
+ <member name="display/window/size/viewport_width" type="int" setter="" getter="" default="1024">
+ Sets the game's main viewport width. On desktop platforms, this is also the initial window width.
</member>
- <member name="display/window/size/test_width" type="int" setter="" getter="" default="0">
- If greater than zero, overrides the window width when running the game. Useful for testing stretch modes.
+ <member name="display/window/size/window_height_override" type="int" setter="" getter="" default="0">
+ On desktop platforms, sets the game's initial window height.
+ [b]Note:[/b] By default, or when set to 0, the initial window height is the [member display/window/size/viewport_height]. This setting is ignored on iOS, Android, and HTML5.
</member>
- <member name="display/window/size/width" type="int" setter="" getter="" default="1024">
- Sets the game's main viewport width. On desktop platforms, this is the default window size. Stretch mode settings also use this as a reference when enabled.
+ <member name="display/window/size/window_width_override" type="int" setter="" getter="" default="0">
+ On desktop platforms, sets the game's initial window width.
+ [b]Note:[/b] By default, or when set to 0, the initial window width is the viewport [member display/window/size/viewport_width]. This setting is ignored on iOS, Android, and HTML5.
</member>
<member name="display/window/vsync/vsync_mode" type="int" setter="" getter="" default="1">
Sets the VSync mode for the main game window.
@@ -560,8 +562,7 @@
<member name="gui/theme/custom_font" type="String" setter="" getter="" default="&quot;&quot;">
Path to a custom [Font] resource to use as default for all GUI elements of the project.
</member>
- <member name="gui/theme/use_hidpi" type="bool" setter="" getter="" default="false">
- If [code]true[/code], makes sure the theme used works with HiDPI.
+ <member name="gui/theme/default_theme_scale" type="float" setter="" getter="" default="1.0">
</member>
<member name="gui/timers/incremental_search_max_interval_msec" type="int" setter="" getter="" default="2000">
Timer setting for incremental search in [Tree], [ItemList], etc. controls (in milliseconds).
@@ -1328,10 +1329,10 @@
The policy to use for unhandled Mono (C#) exceptions. The default "Terminate Application" exits the project as soon as an unhandled exception is thrown. "Log Error" logs an error message to the console instead, and will not interrupt the project execution when an unhandled exception is thrown.
[b]Note:[/b] The unhandled exception policy is always set to "Log Error" in the editor, which also includes C# [code]tool[/code] scripts running within the editor as well as editor plugin code.
</member>
- <member name="navigation/2d/default_cell_size" type="int" setter="" getter="" default="10">
+ <member name="navigation/2d/default_cell_size" type="int" setter="" getter="" default="1">
Default cell size for 2D navigation maps. See [method NavigationServer2D.map_set_cell_size].
</member>
- <member name="navigation/2d/default_edge_connection_margin" type="int" setter="" getter="" default="5">
+ <member name="navigation/2d/default_edge_connection_margin" type="int" setter="" getter="" default="1">
Default edge connection margin for 2D navigation maps. See [method NavigationServer2D.map_set_edge_connection_margin].
</member>
<member name="navigation/3d/default_cell_size" type="float" setter="" getter="" default="0.3">
@@ -1411,8 +1412,8 @@
Sets which physics engine to use for 2D physics.
"DEFAULT" and "GodotPhysics2D" are the same, as there is currently no alternative 2D physics server implemented.
</member>
- <member name="physics/2d/run_on_thread" type="bool" setter="" getter="" default="false">
- Sets whether 2D physics is run on the main thread or a separate one. Running the server on a thread increases performance, but restricts API access to only physics process.
+ <member name="physics/2d/run_on_separate_thread" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], the 2D physics server runs on a separate thread, making better use of multi-core CPUs. If [code]false[/code], the 2D physics server runs on the main thread. Running the physics server on a separate thread can increase performance, but restricts API access to only physics process.
</member>
<member name="physics/2d/sleep_threshold_angular" type="float" setter="" getter="" default="0.139626">
Threshold angular velocity under which a 2D physics body will be considered inactive. See [constant PhysicsServer2D.SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD].
@@ -1483,8 +1484,8 @@
Sets which physics engine to use for 3D physics.
"DEFAULT" is currently the [url=https://bulletphysics.org]Bullet[/url] physics engine. The "GodotPhysics3D" engine is still supported as an alternative.
</member>
- <member name="physics/3d/run_on_thread" type="bool" setter="" getter="" default="false">
- Sets whether 3D physics is run on the main thread or a separate one. Running the server on a thread increases performance, but restricts API access to only physics process.
+ <member name="physics/3d/run_on_separate_thread" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], the 3D physics server runs on a separate thread, making better use of multi-core CPUs. If [code]false[/code], the 3D physics server runs on the main thread. Running the physics server on a separate thread can increase performance, but restricts API access to only physics process.
</member>
<member name="physics/3d/sleep_threshold_angular" type="float" setter="" getter="" default="0.139626">
Threshold angular velocity under which a 3D physics body will be considered inactive. See [constant PhysicsServer3D.SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD].
@@ -1889,10 +1890,6 @@
If [code]true[/code], the texture importer will import VRAM-compressed textures using the Ericsson Texture Compression 2 algorithm. This texture compression algorithm is only supported when using the Vulkan renderer.
[b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
</member>
- <member name="rendering/textures/vram_compression/import_pvrtc" type="bool" setter="" getter="" default="false">
- If [code]true[/code], the texture importer will import VRAM-compressed textures using the PowerVR Texture Compression algorithm. This texture compression algorithm is only supported on iOS.
- [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
- </member>
<member name="rendering/textures/vram_compression/import_s3tc" type="bool" setter="" getter="" default="true">
If [code]true[/code], the texture importer will import VRAM-compressed textures using the S3 Texture Compression algorithm. This algorithm is only supported on desktop platforms and consoles.
[b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
diff --git a/doc/classes/RDTextureView.xml b/doc/classes/RDTextureView.xml
index 441d1f4079..fff794b01a 100644
--- a/doc/classes/RDTextureView.xml
+++ b/doc/classes/RDTextureView.xml
@@ -7,7 +7,7 @@
<tutorials>
</tutorials>
<members>
- <member name="format_override" type="int" setter="set_format_override" getter="get_format_override" enum="RenderingDevice.DataFormat" default="226">
+ <member name="format_override" type="int" setter="set_format_override" getter="get_format_override" enum="RenderingDevice.DataFormat" default="218">
</member>
<member name="swizzle_a" type="int" setter="set_swizzle_a" getter="get_swizzle_a" enum="RenderingDevice.TextureSwizzle" default="6">
</member>
diff --git a/doc/classes/RDVertexAttribute.xml b/doc/classes/RDVertexAttribute.xml
index 17a55260c7..53af568934 100644
--- a/doc/classes/RDVertexAttribute.xml
+++ b/doc/classes/RDVertexAttribute.xml
@@ -7,7 +7,7 @@
<tutorials>
</tutorials>
<members>
- <member name="format" type="int" setter="set_format" getter="get_format" enum="RenderingDevice.DataFormat" default="226">
+ <member name="format" type="int" setter="set_format" getter="get_format" enum="RenderingDevice.DataFormat" default="218">
</member>
<member name="frequency" type="int" setter="set_frequency" getter="get_frequency" enum="RenderingDevice.VertexFrequency" default="0">
</member>
diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml
index fe2885378a..27e02d0ee5 100644
--- a/doc/classes/RayCast2D.xml
+++ b/doc/classes/RayCast2D.xml
@@ -16,9 +16,9 @@
<methods>
<method name="add_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject2D" />
<description>
- Adds a collision exception so the ray does not report collisions with the specified node.
+ Adds a collision exception so the ray does not report collisions with the specified [CollisionObject2D] node.
</description>
</method>
<method name="add_exception_rid">
@@ -81,9 +81,9 @@
</method>
<method name="remove_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject2D" />
<description>
- Removes a collision exception so the ray does report collisions with the specified node.
+ Removes a collision exception so the ray does report collisions with the specified [CollisionObject2D] node.
</description>
</method>
<method name="remove_exception_rid">
diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml
index 8973857ace..6be5861b84 100644
--- a/doc/classes/RayCast3D.xml
+++ b/doc/classes/RayCast3D.xml
@@ -17,9 +17,9 @@
<methods>
<method name="add_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject3D" />
<description>
- Adds a collision exception so the ray does not report collisions with the specified node.
+ Adds a collision exception so the ray does not report collisions with the specified [CollisionObject3D] node.
</description>
</method>
<method name="add_exception_rid">
@@ -82,9 +82,9 @@
</method>
<method name="remove_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject3D" />
<description>
- Removes a collision exception so the ray does report collisions with the specified node.
+ Removes a collision exception so the ray does report collisions with the specified [CollisionObject3D] node.
</description>
</method>
<method name="remove_exception_rid">
diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml
index 65d1654c21..6285b1d155 100644
--- a/doc/classes/Rect2.xml
+++ b/doc/classes/Rect2.xml
@@ -93,7 +93,7 @@
<method name="get_area" qualifiers="const">
<return type="float" />
<description>
- Returns the area of the [Rect2].
+ Returns the area of the [Rect2]. See also [method has_no_area].
</description>
</method>
<method name="get_center" qualifiers="const">
@@ -130,7 +130,8 @@
<method name="has_no_area" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if the [Rect2] is flat or empty.
+ Returns [code]true[/code] if the [Rect2] is flat or empty, [code]false[/code] otherwise. See also [method get_area].
+ [b]Note:[/b] If the [Rect2] has a negative size and is not flat or empty, [method has_no_area] will return [code]true[/code].
</description>
</method>
<method name="has_point" qualifiers="const">
diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml
index 5909784135..a542bad82b 100644
--- a/doc/classes/Rect2i.xml
+++ b/doc/classes/Rect2i.xml
@@ -70,7 +70,7 @@
<return type="Rect2i" />
<argument index="0" name="to" type="Vector2i" />
<description>
- Returns a copy of this [Rect2i] expanded to include a given point.
+ Returns a copy of this [Rect2i] expanded so that the borders align with the given point.
[codeblocks]
[gdscript]
# position (-3, 2), size (1, 1)
@@ -90,7 +90,7 @@
<method name="get_area" qualifiers="const">
<return type="int" />
<description>
- Returns the area of the [Rect2i].
+ Returns the area of the [Rect2i]. See also [method has_no_area].
</description>
</method>
<method name="get_center" qualifiers="const">
@@ -128,7 +128,8 @@
<method name="has_no_area" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if the [Rect2i] is flat or empty.
+ Returns [code]true[/code] if the [Rect2i] is flat or empty, [code]false[/code] otherwise. See also [method get_area].
+ [b]Note:[/b] If the [Rect2i] has a negative size and is not flat or empty, [method has_no_area] will return [code]true[/code].
</description>
</method>
<method name="has_point" qualifiers="const">
diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml
index 5d207c0db7..63f436fa03 100644
--- a/doc/classes/ReflectionProbe.xml
+++ b/doc/classes/ReflectionProbe.xml
@@ -43,6 +43,7 @@
</member>
<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="0.0">
The maximum distance away from the [ReflectionProbe] an object can be before it is culled. Decrease this to improve performance, especially when using the [constant UPDATE_ALWAYS] [member update_mode].
+ [b]Note:[/b] The maximum reflection distance is always at least equal to the [member extents]. This means that decreasing [member max_distance] will not always cull objects from reflections, especially if the reflection probe's [member extents] are already large.
</member>
<member name="mesh_lod_threshold" type="float" setter="set_mesh_lod_threshold" getter="get_mesh_lod_threshold" default="1.0">
The automatic LOD bias to use for meshes rendered within the [ReflectionProbe] (this is analog to [member Viewport.mesh_lod_threshold]). Higher values will use less detailed versions of meshes that have LOD variations generated. If set to [code]0.0[/code], automatic LOD is disabled. Increase [member mesh_lod_threshold] to improve performance at the cost of geometry detail, especially when using the [constant UPDATE_ALWAYS] [member update_mode].
diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index 31c372ec80..d6510993d8 100644
--- a/doc/classes/RenderingDevice.xml
+++ b/doc/classes/RenderingDevice.xml
@@ -305,7 +305,7 @@
<description>
</description>
</method>
- <method name="free">
+ <method name="free_rid">
<return type="void" />
<argument index="0" name="rid" type="RID" />
<description>
@@ -1135,23 +1135,7 @@
</constant>
<constant name="DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM" value="217" enum="DataFormat">
</constant>
- <constant name="DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG" value="218" enum="DataFormat">
- </constant>
- <constant name="DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG" value="219" enum="DataFormat">
- </constant>
- <constant name="DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG" value="220" enum="DataFormat">
- </constant>
- <constant name="DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG" value="221" enum="DataFormat">
- </constant>
- <constant name="DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG" value="222" enum="DataFormat">
- </constant>
- <constant name="DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG" value="223" enum="DataFormat">
- </constant>
- <constant name="DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG" value="224" enum="DataFormat">
- </constant>
- <constant name="DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG" value="225" enum="DataFormat">
- </constant>
- <constant name="DATA_FORMAT_MAX" value="226" enum="DataFormat">
+ <constant name="DATA_FORMAT_MAX" value="218" enum="DataFormat">
</constant>
<constant name="TEXTURE_TYPE_1D" value="0" enum="TextureType">
</constant>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 009e9d3df4..446db40dd8 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -994,6 +994,8 @@
<argument index="8" name="hdr_bleed_threshold" type="float" />
<argument index="9" name="hdr_bleed_scale" type="float" />
<argument index="10" name="hdr_luminance_cap" type="float" />
+ <argument index="11" name="glow_map_strength" type="float" />
+ <argument index="12" name="glow_map" type="RID" />
<description>
</description>
</method>
@@ -1001,7 +1003,7 @@
<return type="void" />
<argument index="0" name="env" type="RID" />
<argument index="1" name="enable" type="bool" />
- <argument index="2" name="cascades" type="int" enum="RenderingServer.EnvironmentSDFGICascades" />
+ <argument index="2" name="cascades" type="int" />
<argument index="3" name="min_cell_size" type="float" />
<argument index="4" name="y_scale" type="int" enum="RenderingServer.EnvironmentSDFGIYScale" />
<argument index="5" name="use_occlusion" type="bool" />
@@ -1334,7 +1336,7 @@
<return type="bool" />
<argument index="0" name="feature" type="String" />
<description>
- Returns [code]true[/code] if the OS supports a certain feature. Features might be [code]s3tc[/code], [code]etc[/code], [code]etc2[/code] and [code]pvrtc[/code].
+ Returns [code]true[/code] if the OS supports a certain feature. Features might be [code]s3tc[/code], [code]etc[/code], and [code]etc2[/code].
</description>
</method>
<method name="instance_attach_object_instance_id">
@@ -1838,8 +1840,7 @@
<argument index="1" name="primitive" type="int" enum="RenderingServer.PrimitiveType" />
<argument index="2" name="arrays" type="Array" />
<argument index="3" name="blend_shapes" type="Array" default="[]" />
- <argument index="4" name="lods" type="Dictionary" default="{
-}" />
+ <argument index="4" name="lods" type="Dictionary" default="{}" />
<argument index="5" name="compress_format" type="int" default="0" />
<description>
</description>
@@ -3758,9 +3759,9 @@
</constant>
<constant name="LIGHT_BAKE_DISABLED" value="0" enum="LightBakeMode">
</constant>
- <constant name="LIGHT_BAKE_DYNAMIC" value="1" enum="LightBakeMode">
+ <constant name="LIGHT_BAKE_STATIC" value="1" enum="LightBakeMode">
</constant>
- <constant name="LIGHT_BAKE_STATIC" value="2" enum="LightBakeMode">
+ <constant name="LIGHT_BAKE_DYNAMIC" value="2" enum="LightBakeMode">
</constant>
<constant name="LIGHT_OMNI_SHADOW_DUAL_PARABOLOID" value="0" enum="LightOmniShadowMode">
Use a dual paraboloid shadow map for omni lights.
@@ -4180,12 +4181,6 @@
<constant name="ENV_SSIL_QUALITY_ULTRA" value="4" enum="EnvironmentSSILQuality">
Highest quality screen-space indirect lighting. Uses the adaptive target setting which can be dynamically adjusted to smoothly balance performance and visual quality.
</constant>
- <constant name="ENV_SDFGI_CASCADES_4" value="0" enum="EnvironmentSDFGICascades">
- </constant>
- <constant name="ENV_SDFGI_CASCADES_6" value="1" enum="EnvironmentSDFGICascades">
- </constant>
- <constant name="ENV_SDFGI_CASCADES_8" value="2" enum="EnvironmentSDFGICascades">
- </constant>
<constant name="ENV_SDFGI_Y_SCALE_DISABLED" value="0" enum="EnvironmentSDFGIYScale">
</constant>
<constant name="ENV_SDFGI_Y_SCALE_75_PERCENT" value="1" enum="EnvironmentSDFGIYScale">
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index c8ed373271..95dffd3e28 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -372,6 +372,9 @@
</method>
</methods>
<members>
+ <member name="autowrap_mode" type="int" setter="set_autowrap_mode" getter="get_autowrap_mode" enum="RichTextLabel.AutowrapMode" default="3">
+ If set to something other than [constant AUTOWRAP_OFF], the text gets wrapped inside the node's bounding rectangle. To see how each mode behaves, see [enum AutowrapMode].
+ </member>
<member name="bbcode_enabled" type="bool" setter="set_use_bbcode" getter="is_using_bbcode" default="false">
If [code]true[/code], the label uses BBCode formatting.
</member>
@@ -420,7 +423,7 @@
</member>
<member name="text" type="String" setter="set_text" getter="get_text" default="&quot;&quot;">
The label's text in BBCode format. Is not representative of manual modifications to the internal tag stack. Erases changes made by other methods when edited.
- [b]Note:[/b] If [member bbcode_enabled] is [code]true[/code], it is unadvised to use the [code]+=[/code] operator with [code]text[/code] (e.g. [code]text += "some string"[/code]) as it replaces the whole text and can cause slowdowns. Use [method append_text] for adding text instead, unless you absolutely need to close a tag that was opened in an earlier method call.
+ [b]Note:[/b] If [member bbcode_enabled] is [code]true[/code], it is unadvised to use the [code]+=[/code] operator with [code]text[/code] (e.g. [code]text += "some string"[/code]) as it replaces the whole text and can cause slowdowns. It will also erase all BBCode that was added to stack using [code]push_*[/code] methods. Use [method append_text] for adding text instead, unless you absolutely need to close a tag that was opened in an earlier method call.
</member>
<member name="text_direction" type="int" setter="set_text_direction" getter="get_text_direction" enum="Control.TextDirection" default="0">
Base text writing direction.
@@ -454,6 +457,18 @@
</signal>
</signals>
<constants>
+ <constant name="AUTOWRAP_OFF" value="0" enum="AutowrapMode">
+ Autowrap is disabled.
+ </constant>
+ <constant name="AUTOWRAP_ARBITRARY" value="1" enum="AutowrapMode">
+ Wraps the text inside the node's bounding rectangle by allowing to break lines at arbitrary positions, which is useful when very limited space is available.
+ </constant>
+ <constant name="AUTOWRAP_WORD" value="2" enum="AutowrapMode">
+ Wraps the text inside the node's bounding rectangle by soft-breaking between words.
+ </constant>
+ <constant name="AUTOWRAP_WORD_SMART" value="3" enum="AutowrapMode">
+ Behaves similarly to [constant AUTOWRAP_WORD], but force-breaks a word if that single word does not fit in one line.
+ </constant>
<constant name="LIST_NUMBERS" value="0" enum="ListType">
Each list item has a number marker.
</constant>
diff --git a/doc/classes/ShapeCast2D.xml b/doc/classes/ShapeCast2D.xml
index 74ebafe069..7229d6f72a 100644
--- a/doc/classes/ShapeCast2D.xml
+++ b/doc/classes/ShapeCast2D.xml
@@ -14,9 +14,9 @@
<methods>
<method name="add_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject2D" />
<description>
- Adds a collision exception so the shape does not report collisions with the specified node.
+ Adds a collision exception so the shape does not report collisions with the specified [CollisionObject2D] node.
</description>
</method>
<method name="add_exception_rid">
@@ -101,9 +101,9 @@
</method>
<method name="remove_exception">
<return type="void" />
- <argument index="0" name="node" type="Object" />
+ <argument index="0" name="node" type="CollisionObject2D" />
<description>
- Removes a collision exception so the shape does report collisions with the specified node.
+ Removes a collision exception so the shape does report collisions with the specified [CollisionObject2D] node.
</description>
</method>
<method name="remove_exception_rid">
diff --git a/doc/classes/Skin.xml b/doc/classes/Skin.xml
index d24963a887..572558c3f5 100644
--- a/doc/classes/Skin.xml
+++ b/doc/classes/Skin.xml
@@ -14,6 +14,13 @@
<description>
</description>
</method>
+ <method name="add_named_bind">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <argument index="1" name="pose" type="Transform3D" />
+ <description>
+ </description>
+ </method>
<method name="clear_binds">
<return type="void" />
<description>
diff --git a/doc/classes/SoftDynamicBody3D.xml b/doc/classes/SoftDynamicBody3D.xml
index fceebddf35..801b25f1b0 100644
--- a/doc/classes/SoftDynamicBody3D.xml
+++ b/doc/classes/SoftDynamicBody3D.xml
@@ -5,6 +5,7 @@
</brief_description>
<description>
A deformable physics body. Used to create elastic or deformable objects such as cloth, rubber, or other flexible materials.
+ [b]Note:[/b] There are many known bugs in [SoftDynamicBody3D]. Therefore, it's not recommended to use them for things that can affect gameplay (such as a player character made entirely out of soft bodies).
</description>
<tutorials>
<link title="SoftBody">$DOCS_URL/tutorials/physics/soft_body.html</link>
diff --git a/doc/classes/StreamPeerBuffer.xml b/doc/classes/StreamPeerBuffer.xml
index 989864760f..e335987ff5 100644
--- a/doc/classes/StreamPeerBuffer.xml
+++ b/doc/classes/StreamPeerBuffer.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="StreamPeerBuffer" inherits="StreamPeer" version="4.0">
<brief_description>
+ Data buffer stream peer.
</brief_description>
<description>
+ Data buffer stream peer that uses a byte array as the stream. This object can be used to handle binary data from network sessions. To handle binary data stored in files, [File] can be used directly.
+ A [StreamPeerBuffer] object keeps an internal cursor which is the offset in bytes to the start of the buffer. Get and put operations are performed at the cursor position and will move the cursor accordingly.
</description>
<tutorials>
</tutorials>
@@ -10,38 +13,45 @@
<method name="clear">
<return type="void" />
<description>
+ Clears the [member data_array] and resets the cursor.
</description>
</method>
<method name="duplicate" qualifiers="const">
<return type="StreamPeerBuffer" />
<description>
+ Returns a new [StreamPeerBuffer] with the same [member data_array] content.
</description>
</method>
<method name="get_position" qualifiers="const">
<return type="int" />
<description>
+ Returns the current cursor position.
</description>
</method>
<method name="get_size" qualifiers="const">
<return type="int" />
<description>
+ Returns the size of [member data_array].
</description>
</method>
<method name="resize">
<return type="void" />
<argument index="0" name="size" type="int" />
<description>
+ Resizes the [member data_array]. This [i]doesn't[/i] update the cursor.
</description>
</method>
<method name="seek">
<return type="void" />
<argument index="0" name="position" type="int" />
<description>
+ Moves the cursor to the specified position. [code]position[/code] must be a valid index of [member data_array].
</description>
</method>
</methods>
<members>
<member name="data_array" type="PackedByteArray" setter="set_data_array" getter="get_data_array" default="PackedByteArray()">
+ The underlying data buffer. Setting this value resets the cursor.
</member>
</members>
</class>
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index d86c577e5e..1a9b9ccdcc 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -285,7 +285,7 @@
Returns [code]true[/code] if this string is a subsequence of the given string.
</description>
</method>
- <method name="is_subsequence_ofi" qualifiers="const">
+ <method name="is_subsequence_ofn" qualifiers="const">
<return type="bool" />
<argument index="0" name="text" type="String" />
<description>
@@ -684,13 +684,24 @@
<method name="to_float" qualifiers="const">
<return type="float" />
<description>
- Converts a string containing a decimal number into a [code]float[/code].
+ Converts a string containing a decimal number into a [code]float[/code]. The method will stop on the first non-number character except the first [code].[/code] (decimal point), and [code]e[/code] which is used for exponential.
+ [codeblock]
+ print("12.3".to_float()) # 12.3
+ print("1.2.3".to_float()) # 1.2
+ print("12ab3".to_float()) # 12
+ print("1e3".to_float()) # 1000
+ [/codeblock]
</description>
</method>
<method name="to_int" qualifiers="const">
<return type="int" />
<description>
- Converts a string containing an integer number into an [code]int[/code].
+ Converts a string containing an integer number into an [code]int[/code]. The method will remove any non-number character and stop if it encounters a [code].[/code].
+ [codeblock]
+ print("123".to_int()) # 123
+ print("a1b2c3".to_int()) # 123
+ print("1.2.3".to_int()) # 1
+ [/codeblock]
</description>
</method>
<method name="to_lower" qualifiers="const">
diff --git a/doc/classes/TabBar.xml b/doc/classes/TabBar.xml
index 71df40a693..c286629395 100644
--- a/doc/classes/TabBar.xml
+++ b/doc/classes/TabBar.xml
@@ -43,10 +43,11 @@
Returns the previously active tab index.
</description>
</method>
- <method name="get_select_with_rmb" qualifiers="const">
- <return type="bool" />
+ <method name="get_tab_button_icon" qualifiers="const">
+ <return type="Texture2D" />
+ <argument index="0" name="tab_idx" type="int" />
<description>
- Returns [code]true[/code] if select with right mouse button is enabled.
+ Returns the [Texture2D] for the right button of the tab at index [code]tab_idx[/code] or [code]null[/code] if the button has no [Texture2D].
</description>
</method>
<method name="get_tab_icon" qualifiers="const">
@@ -111,6 +112,13 @@
Returns [code]true[/code] if the tab at index [code]tab_idx[/code] is disabled.
</description>
</method>
+ <method name="is_tab_hidden" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="tab_idx" type="int" />
+ <description>
+ Returns [code]true[/code] if the tab at index [code]tab_idx[/code] is hidden.
+ </description>
+ </method>
<method name="move_tab">
<return type="void" />
<argument index="0" name="from" type="int" />
@@ -126,11 +134,12 @@
Removes the tab at index [code]tab_idx[/code].
</description>
</method>
- <method name="set_select_with_rmb">
+ <method name="set_tab_button_icon">
<return type="void" />
- <argument index="0" name="enabled" type="bool" />
+ <argument index="0" name="tab_idx" type="int" />
+ <argument index="1" name="icon" type="Texture2D" />
<description>
- If [code]true[/code], enables selecting a tab with the right mouse button.
+ Sets an [code]icon[/code] for the button of the tab at index [code]tab_idx[/code] (located to the right, before the close button), making it visible and clickable (See [signal tab_button_pressed]). Giving it a [code]null[/code] value will hide the button.
</description>
</method>
<method name="set_tab_disabled">
@@ -141,6 +150,14 @@
If [code]disabled[/code] is [code]true[/code], disables the tab at index [code]tab_idx[/code], making it non-interactable.
</description>
</method>
+ <method name="set_tab_hidden">
+ <return type="void" />
+ <argument index="0" name="tab_idx" type="int" />
+ <argument index="1" name="hidden" type="bool" />
+ <description>
+ If [code]hidden[/code] is [code]true[/code], hides the tab at index [code]tab_idx[/code], making it disappear from the tab area.
+ </description>
+ </method>
<method name="set_tab_icon">
<return type="void" />
<argument index="0" name="tab_idx" type="int" />
@@ -200,10 +217,17 @@
<member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled" default="false">
If [code]true[/code], tabs can be rearranged with mouse drag.
</member>
+ <member name="scroll_to_selected" type="bool" setter="set_scroll_to_selected" getter="get_scroll_to_selected" default="true">
+ If [code]true[/code], the tab offset will be changed to keep the the currently selected tab visible.
+ </member>
<member name="scrolling_enabled" type="bool" setter="set_scrolling_enabled" getter="get_scrolling_enabled" default="true">
if [code]true[/code], the mouse's scroll wheel can be used to navigate the scroll view.
</member>
+ <member name="select_with_rmb" type="bool" setter="set_select_with_rmb" getter="get_select_with_rmb" default="false">
+ If [code]true[/code], enables selecting a tab with the right mouse button.
+ </member>
<member name="tab_alignment" type="int" setter="set_tab_alignment" getter="get_tab_alignment" enum="TabBar.AlignmentMode" default="1">
+ Sets the position at which tabs will be placed. See [enum AlignmentMode] for details.
</member>
<member name="tab_close_display_policy" type="int" setter="set_tab_close_display_policy" getter="get_tab_close_display_policy" enum="TabBar.CloseButtonDisplayPolicy" default="0">
Sets when the close button will appear on the tabs. See [enum CloseButtonDisplayPolicy] for details.
@@ -219,6 +243,12 @@
Emitted when the active tab is rearranged via mouse drag. See [member drag_to_rearrange_enabled].
</description>
</signal>
+ <signal name="tab_button_pressed">
+ <argument index="0" name="tab" type="int" />
+ <description>
+ Emitted when a tab's right button is pressed. See [method set_tab_button_icon].
+ </description>
+ </signal>
<signal name="tab_changed">
<argument index="0" name="tab" type="int" />
<description>
@@ -255,18 +285,28 @@
<signal name="tab_rmb_clicked">
<argument index="0" name="tab" type="int" />
<description>
- Emitted when a tab is right-clicked.
+ Emitted when a tab is right-clicked. [member select_with_rmb] must be enabled.
+ </description>
+ </signal>
+ <signal name="tab_selected">
+ <argument index="0" name="tab" type="int" />
+ <description>
+ Emitted when a tab is selected via click or script, even if it is the current tab.
</description>
</signal>
</signals>
<constants>
<constant name="ALIGNMENT_LEFT" value="0" enum="AlignmentMode">
+ Places tabs to the left.
</constant>
<constant name="ALIGNMENT_CENTER" value="1" enum="AlignmentMode">
+ Places tabs in the middle.
</constant>
<constant name="ALIGNMENT_RIGHT" value="2" enum="AlignmentMode">
+ Places tabs to the right.
</constant>
<constant name="ALIGNMENT_MAX" value="3" enum="AlignmentMode">
+ Represents the size of the [enum AlignmentMode] enum.
</constant>
<constant name="CLOSE_BUTTON_SHOW_NEVER" value="0" enum="CloseButtonDisplayPolicy">
Never show the close buttons.
@@ -282,16 +322,16 @@
</constant>
</constants>
<theme_items>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.2)">
+ <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
Font color of disabled tabs.
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the tab name.
</theme_item>
- <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Font color of the currently selected tab.
</theme_item>
- <theme_item name="font_unselected_color" data_type="color" type="Color" default="Color(0.69, 0.69, 0.69, 1)">
+ <theme_item name="font_unselected_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 1)">
Font color of the other, unselected tabs.
</theme_item>
<theme_item name="hseparation" data_type="constant" type="int" default="4">
@@ -321,11 +361,11 @@
<theme_item name="increment_highlight" data_type="icon" type="Texture2D">
Icon for the right arrow button that appears when there are too many tabs to fit in the container width. Used when the button is being hovered with the cursor.
</theme_item>
- <theme_item name="close_bg_highlight" data_type="style" type="StyleBox">
- Background of the close button when it's being hovered with the cursor.
+ <theme_item name="button_highlight" data_type="style" type="StyleBox">
+ Background of the tab and close buttons when they're being hovered with the cursor.
</theme_item>
- <theme_item name="close_bg_pressed" data_type="style" type="StyleBox">
- Background of the close button when it's being pressed.
+ <theme_item name="button_pressed" data_type="style" type="StyleBox">
+ Background of the tab and close buttons when it's being pressed.
</theme_item>
<theme_item name="tab_disabled" data_type="style" type="StyleBox">
The style of disabled tabs.
diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml
index d6e1e13505..3b814b6177 100644
--- a/doc/classes/TabContainer.xml
+++ b/doc/classes/TabContainer.xml
@@ -178,16 +178,16 @@
</constant>
</constants>
<theme_items>
- <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.2)">
+ <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
Font color of disabled tabs.
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the tab name.
</theme_item>
- <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Font color of the currently selected tab.
</theme_item>
- <theme_item name="font_unselected_color" data_type="color" type="Color" default="Color(0.69, 0.69, 0.69, 1)">
+ <theme_item name="font_unselected_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 1)">
Font color of the other, unselected tabs.
</theme_item>
<theme_item name="icon_separation" data_type="constant" type="int" default="4">
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 3197e59248..6b9eb6efc3 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -729,8 +729,7 @@
<method name="set_gutter_custom_draw">
<return type="void" />
<argument index="0" name="column" type="int" />
- <argument index="1" name="object" type="Object" />
- <argument index="2" name="callback" type="StringName" />
+ <argument index="1" name="draw_callback" type="Callable" />
<description>
Set a custom draw method for the gutter. The callback method must take the following args: [code]line: int, gutter: int, Area: Rect2[/code].
</description>
@@ -907,11 +906,9 @@
</method>
<method name="set_tooltip_request_func">
<return type="void" />
- <argument index="0" name="object" type="Object" />
- <argument index="1" name="callback" type="StringName" />
- <argument index="2" name="data" type="Variant" />
+ <argument index="0" name="callback" type="Callable" />
<description>
- Provide custom tooltip text. The callback method must take the following args: [code]hovered_word: String, data: Variant[/code]
+ Provide custom tooltip text. The callback method must take the following args: [code]hovered_word: String[/code]
</description>
</method>
<method name="swap_lines">
@@ -995,6 +992,9 @@
<member name="override_selected_font_color" type="bool" setter="set_override_selected_font_color" getter="is_overriding_selected_font_color" default="false">
If [code]true[/code], custom [code]font_selected_color[/code] will be used for selected text.
</member>
+ <member name="placeholder_text" type="String" setter="set_placeholder" getter="get_placeholder" default="&quot;&quot;">
+ Text shown when the [TextEdit] is empty. It is [b]not[/b] the [TextEdit]'s default value (see [member text]).
+ </member>
<member name="scroll_horizontal" type="int" setter="set_h_scroll" getter="get_h_scroll" default="0">
If there is a horizontal scrollbar, this determines the current horizontal scroll value in pixels.
</member>
@@ -1222,22 +1222,25 @@
<theme_item name="caret_background_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
[Color] of the text behind the caret when using a block caret.
</theme_item>
- <theme_item name="caret_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="caret_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
[Color] of the caret. This can be set to a fully transparent color to hide the caret entirely.
</theme_item>
<theme_item name="current_line_color" data_type="color" type="Color" default="Color(0.25, 0.25, 0.26, 0.8)">
Background [Color] of the line containing the caret.
</theme_item>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Sets the font [Color].
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The tint of text outline of the [TextEdit].
</theme_item>
- <theme_item name="font_readonly_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 0.5)">
+ <theme_item name="font_placeholder_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.6)">
+ Font color for [member placeholder_text].
+ </theme_item>
+ <theme_item name="font_readonly_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
Sets the font [Color] when [member editable] is disabled.
</theme_item>
- <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
+ <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Sets the [Color] of the selected text. [member override_selected_font_color] has to be enabled.
</theme_item>
<theme_item name="search_result_border_color" data_type="color" type="Color" default="Color(0.3, 0.3, 0.3, 0.4)">
@@ -1246,10 +1249,10 @@
<theme_item name="search_result_color" data_type="color" type="Color" default="Color(0.3, 0.3, 0.3, 1)">
[Color] behind the text that matches the search query.
</theme_item>
- <theme_item name="selection_color" data_type="color" type="Color" default="Color(0.49, 0.49, 0.49, 1)">
+ <theme_item name="selection_color" data_type="color" type="Color" default="Color(0.5, 0.5, 0.5, 1)">
Sets the highlight [Color] of text selections.
</theme_item>
- <theme_item name="word_highlighted_color" data_type="color" type="Color" default="Color(0.8, 0.9, 0.9, 0.15)">
+ <theme_item name="word_highlighted_color" data_type="color" type="Color" default="Color(0.5, 0.5, 0.5, 0.25)">
Sets the highlight [Color] of multiple occurrences. [member highlight_all_occurrences] has to be enabled.
</theme_item>
<theme_item name="caret_width" data_type="constant" type="int" default="1">
diff --git a/doc/classes/TextLine.xml b/doc/classes/TextLine.xml
index 1eaccf4604..7bfc7eed7e 100644
--- a/doc/classes/TextLine.xml
+++ b/doc/classes/TextLine.xml
@@ -24,9 +24,9 @@
<argument index="0" name="text" type="String" />
<argument index="1" name="fonts" type="Font" />
<argument index="2" name="size" type="int" />
- <argument index="3" name="opentype_features" type="Dictionary" default="{
-}" />
+ <argument index="3" name="opentype_features" type="Dictionary" default="{}" />
<argument index="4" name="language" type="String" default="&quot;&quot;" />
+ <argument index="5" name="meta" type="Variant" default="null" />
<description>
Adds text span and font to draw it.
</description>
diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml
index ff193a0e4b..acf3dcd43b 100644
--- a/doc/classes/TextParagraph.xml
+++ b/doc/classes/TextParagraph.xml
@@ -24,9 +24,9 @@
<argument index="0" name="text" type="String" />
<argument index="1" name="fonts" type="Font" />
<argument index="2" name="size" type="int" />
- <argument index="3" name="opentype_features" type="Dictionary" default="{
-}" />
+ <argument index="3" name="opentype_features" type="Dictionary" default="{}" />
<argument index="4" name="language" type="String" default="&quot;&quot;" />
+ <argument index="5" name="meta" type="Variant" default="null" />
<description>
Adds text span and font to draw it.
</description>
@@ -259,8 +259,7 @@
<argument index="1" name="fonts" type="Font" />
<argument index="2" name="size" type="int" />
<argument index="3" name="dropcap_margins" type="Rect2" default="Rect2(0, 0, 0, 0)" />
- <argument index="4" name="opentype_features" type="Dictionary" default="{
-}" />
+ <argument index="4" name="opentype_features" type="Dictionary" default="{}" />
<argument index="5" name="language" type="String" default="&quot;&quot;" />
<description>
Sets drop cap, overrides previously set drop cap. Drop cap (dropped capital) is a decorative element at the beginning of a paragraph that is larger than the rest of the text.
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 2117451281..9025e4ff2a 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -21,8 +21,8 @@
<argument index="1" name="orientation" type="int" enum="TextServer.Orientation" default="0" />
<description>
Creates new buffer for complex text layout, with the given [code]direction[/code] and [code]orientation[/code]. To free the resulting buffer, use [method free_rid] method.
- [b]Note:[/b] Direction is ignored if server does not support [code]FEATURE_BIDI_LAYOUT[/code] feature.
- [b]Note:[/b] Orientation is ignored if server does not support [code]FEATURE_VERTICAL_LAYOUT[/code] feature.
+ [b]Note:[/b] Direction is ignored if server does not support [constant FEATURE_BIDI_LAYOUT] feature (supported by [TextServerAdvanced]).
+ [b]Note:[/b] Orientation is ignored if server does not support [constant FEATURE_VERTICAL_LAYOUT] feature (supported by [TextServerAdvanced]).
</description>
</method>
<method name="draw_hex_code_box" qualifiers="const">
@@ -896,6 +896,32 @@
[b]Note:[/b] This function is used by during project export, to include TextServer database.
</description>
</method>
+ <method name="shaped_get_span_count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="shaped" type="RID" />
+ <description>
+ Returns number of text spans added using [method shaped_text_add_string] or [method shaped_text_add_object].
+ </description>
+ </method>
+ <method name="shaped_get_span_meta" qualifiers="const">
+ <return type="Variant" />
+ <argument index="0" name="shaped" type="RID" />
+ <argument index="1" name="index" type="int" />
+ <description>
+ Returns text span metadata.
+ </description>
+ </method>
+ <method name="shaped_set_span_update_font">
+ <return type="void" />
+ <argument index="0" name="shaped" type="RID" />
+ <argument index="1" name="index" type="int" />
+ <argument index="2" name="fonts" type="Array" />
+ <argument index="3" name="size" type="int" />
+ <argument index="4" name="opentype_features" type="Dictionary" default="{}" />
+ <description>
+ Changes text span font, font size and OpenType features, without changing the text.
+ </description>
+ </method>
<method name="shaped_text_add_object">
<return type="bool" />
<argument index="0" name="shaped" type="RID" />
@@ -913,9 +939,9 @@
<argument index="1" name="text" type="String" />
<argument index="2" name="fonts" type="Array" />
<argument index="3" name="size" type="int" />
- <argument index="4" name="opentype_features" type="Dictionary" default="{
-}" />
+ <argument index="4" name="opentype_features" type="Dictionary" default="{}" />
<argument index="5" name="language" type="String" default="&quot;&quot;" />
+ <argument index="6" name="meta" type="Variant" default="null" />
<description>
Adds text span and font to draw it to the text buffer.
</description>
@@ -1263,7 +1289,7 @@
<argument index="1" name="direction" type="int" enum="TextServer.Direction" default="0" />
<description>
Sets desired text direction. If set to [code]TEXT_DIRECTION_AUTO[/code], direction will be detected based on the buffer contents and current locale.
- [b]Note:[/b] Direction is ignored if server does not support [code]FEATURE_BIDI_LAYOUT[/code] feature.
+ [b]Note:[/b] Direction is ignored if server does not support [constant FEATURE_BIDI_LAYOUT] feature (supported by [TextServerAdvanced]).
</description>
</method>
<method name="shaped_text_set_orientation">
@@ -1272,7 +1298,7 @@
<argument index="1" name="orientation" type="int" enum="TextServer.Orientation" default="0" />
<description>
Sets desired text orientation.
- [b]Note:[/b] Orientation is ignored if server does not support [code]FEATURE_VERTICAL_LAYOUT[/code] feature.
+ [b]Note:[/b] Orientation is ignored if server does not support [constant FEATURE_VERTICAL_LAYOUT] feature (supported by [TextServerAdvanced]).
</description>
</method>
<method name="shaped_text_set_preserve_control">
@@ -1323,6 +1349,26 @@
Aligns shaped text to the given tab-stops.
</description>
</method>
+ <method name="string_to_lower" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="string" type="String" />
+ <argument index="1" name="language" type="String" default="&quot;&quot;" />
+ <description>
+ Returns the string converted to lowercase.
+ [b]Note:[/b] Casing is locale dependent and context sensitive if server support [constant FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION] feature (supported by [TextServerAdvanced]).
+ [b]Note:[/b] The result may be longer or shorter than the original.
+ </description>
+ </method>
+ <method name="string_to_upper" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="string" type="String" />
+ <argument index="1" name="language" type="String" default="&quot;&quot;" />
+ <description>
+ Returns the string converted to uppercase.
+ [b]Note:[/b] Casing is locale dependent and context sensitive if server support [constant FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION] feature (supported by [TextServerAdvanced]).
+ [b]Note:[/b] The result may be longer or shorter than the original.
+ </description>
+ </method>
<method name="strip_diacritics" qualifiers="const">
<return type="String" />
<argument index="0" name="string" type="String" />
@@ -1468,7 +1514,10 @@
<constant name="FEATURE_FONT_VARIABLE" value="64" enum="Feature">
TextServer supports variable fonts.
</constant>
- <constant name="FEATURE_USE_SUPPORT_DATA" value="128" enum="Feature">
+ <constant name="FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION" value="128" enum="Feature">
+ TextServer supports locale dependent and context sensitive case conversion.
+ </constant>
+ <constant name="FEATURE_USE_SUPPORT_DATA" value="256" enum="Feature">
TextServer require external data file for some features.
</constant>
<constant name="CONTOUR_CURVE_TAG_ON" value="1" enum="ContourPointTag">
diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml
index 40a1f89395..5967340f8c 100644
--- a/doc/classes/TextServerExtension.xml
+++ b/doc/classes/TextServerExtension.xml
@@ -904,6 +904,32 @@
[b]Note:[/b] This function is used by during project export, to include TextServer database.
</description>
</method>
+ <method name="_shaped_get_span_count" qualifiers="virtual const">
+ <return type="int" />
+ <argument index="0" name="shaped" type="RID" />
+ <description>
+ Returns number of text spans added using [method _shaped_text_add_string] or [method _shaped_text_add_object].
+ </description>
+ </method>
+ <method name="_shaped_get_span_meta" qualifiers="virtual const">
+ <return type="Variant" />
+ <argument index="0" name="shaped" type="RID" />
+ <argument index="1" name="index" type="int" />
+ <description>
+ Returns text span metadata.
+ </description>
+ </method>
+ <method name="_shaped_set_span_update_font" qualifiers="virtual">
+ <return type="void" />
+ <argument index="0" name="shaped" type="RID" />
+ <argument index="1" name="index" type="int" />
+ <argument index="2" name="fonts" type="Array" />
+ <argument index="3" name="size" type="int" />
+ <argument index="4" name="opentype_features" type="Dictionary" />
+ <description>
+ Changes text span font, font size and OpenType features, without changing the text.
+ </description>
+ </method>
<method name="_shaped_text_add_object" qualifiers="virtual">
<return type="bool" />
<argument index="0" name="shaped" type="RID" />
@@ -923,6 +949,7 @@
<argument index="3" name="size" type="int" />
<argument index="4" name="opentype_features" type="Dictionary" />
<argument index="5" name="language" type="String" />
+ <argument index="6" name="meta" type="Variant" />
<description>
Adds text span and font to draw it to the text buffer.
</description>
@@ -1348,6 +1375,22 @@
Updates justification opportunities (spaces, kashidas, etc.).
</description>
</method>
+ <method name="_string_to_lower" qualifiers="virtual const">
+ <return type="String" />
+ <argument index="0" name="string" type="String" />
+ <argument index="1" name="language" type="String" />
+ <description>
+ Returns the string converted to lowercase. Casing is locale dependent and context sensitive. The result may be longer or shorter than the original.
+ </description>
+ </method>
+ <method name="_string_to_upper" qualifiers="virtual const">
+ <return type="String" />
+ <argument index="0" name="string" type="String" />
+ <argument index="1" name="language" type="String" />
+ <description>
+ Returns the string converted to uppercase. Casing is locale dependent and context sensitive. The result may be longer or shorter than the original.
+ </description>
+ </method>
<method name="_tag_to_name" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="tag" type="int" />
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index e37031f3fd..80a6458cec 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -202,7 +202,8 @@
<return type="Vector2" />
<argument index="0" name="map_position" type="Vector2i" />
<description>
- Returns the local position corresponding to the given tilemap (grid-based) coordinates.
+ Returns a local position of the center of the cell at the given tilemap (grid-based) coordinates.
+ [b]Note:[/b] This doesn't correspond to the visual position of the tile, i.e. it ignores the [member TileData.texture_offset] property of individual tiles.
</description>
</method>
<method name="move_layer">
@@ -217,7 +218,7 @@
<return type="void" />
<argument index="0" name="layer" type="int" />
<description>
- Moves the layer at index [code]layer_index[/code] to the given position [code]to_position[/code] in the array.
+ Removes the layer at index [code]layer[/code].
</description>
</method>
<method name="set_cell">
diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml
index 6580c6bd4c..f984d33098 100644
--- a/doc/classes/TileSetAtlasSource.xml
+++ b/doc/classes/TileSetAtlasSource.xml
@@ -111,7 +111,7 @@
</description>
</method>
<method name="get_tile_data" qualifiers="const">
- <return type="Object" />
+ <return type="TileData" />
<argument index="0" name="atlas_coords" type="Vector2i" />
<argument index="1" name="alternative_tile" type="int" />
<description>
diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml
index e679a8cfeb..ccecaaa6ac 100644
--- a/doc/classes/Transform3D.xml
+++ b/doc/classes/Transform3D.xml
@@ -106,6 +106,14 @@
Scales basis and origin of the transform by the given scale factor, using matrix multiplication.
</description>
</method>
+ <method name="sphere_interpolate_with" qualifiers="const">
+ <return type="Transform3D" />
+ <argument index="0" name="xform" type="Transform3D" />
+ <argument index="1" name="weight" type="float" />
+ <description>
+ Returns a transform spherically interpolated between this transform and another by a given [code]weight[/code] (on the range of 0.0 to 1.0).
+ </description>
+ </method>
<method name="translated" qualifiers="const">
<return type="Transform3D" />
<argument index="0" name="offset" type="Vector3" />
diff --git a/doc/classes/TranslationServer.xml b/doc/classes/TranslationServer.xml
index a1b4404079..c90cb2987c 100644
--- a/doc/classes/TranslationServer.xml
+++ b/doc/classes/TranslationServer.xml
@@ -24,6 +24,46 @@
Clears the server from all translations.
</description>
</method>
+ <method name="compare_locales" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="locale_a" type="String" />
+ <argument index="1" name="locale_b" type="String" />
+ <description>
+ Compares two locales and return similarity score between [code]0[/code](no match) and [code]10[/code](full match).
+ </description>
+ </method>
+ <method name="get_all_countries" qualifiers="const">
+ <return type="PackedStringArray" />
+ <description>
+ Returns array of known country codes.
+ </description>
+ </method>
+ <method name="get_all_languages" qualifiers="const">
+ <return type="PackedStringArray" />
+ <description>
+ Returns array of known language codes.
+ </description>
+ </method>
+ <method name="get_all_scripts" qualifiers="const">
+ <return type="PackedStringArray" />
+ <description>
+ Returns array of known script codes.
+ </description>
+ </method>
+ <method name="get_country_name" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="country" type="String" />
+ <description>
+ Returns readable country name for the [code]country[/code] code.
+ </description>
+ </method>
+ <method name="get_language_name" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="language" type="String" />
+ <description>
+ Returns readable language name for the [code]language[/code] code.
+ </description>
+ </method>
<method name="get_loaded_locales" qualifiers="const">
<return type="Array" />
<description>
@@ -44,6 +84,13 @@
Returns a locale's language and its variant (e.g. [code]"en_US"[/code] would return [code]"English (United States)"[/code]).
</description>
</method>
+ <method name="get_script_name" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="script" type="String" />
+ <description>
+ Returns readable script name for the [code]script[/code] code.
+ </description>
+ </method>
<method name="get_translation_object">
<return type="Translation" />
<argument index="0" name="locale" type="String" />
@@ -80,6 +127,13 @@
If translations have been loaded beforehand for the new locale, they will be applied.
</description>
</method>
+ <method name="standardize_locale" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="locale" type="String" />
+ <description>
+ Retunrs [code]locale[/code] string standardized to match known locales (e.g. [code]en-US[/code] would be matched to [code]en_US[/code]).
+ </description>
+ </method>
<method name="translate" qualifiers="const">
<return type="StringName" />
<argument index="0" name="message" type="StringName" />
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 10bbdc0301..766c740a2c 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -50,10 +50,10 @@
</method>
<method name="create_item">
<return type="TreeItem" />
- <argument index="0" name="parent" type="Object" default="null" />
+ <argument index="0" name="parent" type="TreeItem" default="null" />
<argument index="1" name="idx" type="int" default="-1" />
<description>
- Creates an item in the tree and adds it as a child of [code]parent[/code].
+ Creates an item in the tree and adds it as a child of [code]parent[/code], which can be either a valid [TreeItem] or [code]null[/code].
If [code]parent[/code] is [code]null[/code], the root item will be the parent, or the new item will be the root itself if the tree is empty.
The new item will be the [code]idx[/code]th child of parent, or it will be the last child if there are not enough siblings.
</description>
@@ -170,10 +170,10 @@
</method>
<method name="get_item_area_rect" qualifiers="const">
<return type="Rect2" />
- <argument index="0" name="item" type="Object" />
+ <argument index="0" name="item" type="TreeItem" />
<argument index="1" name="column" type="int" default="-1" />
<description>
- Returns the rectangle area for the specified item. If [code]column[/code] is specified, only get the position and size of that column, otherwise get the rectangle containing all columns.
+ Returns the rectangle area for the specified [TreeItem]. If [code]column[/code] is specified, only get the position and size of that column, otherwise get the rectangle containing all columns.
</description>
</method>
<method name="get_item_at_position" qualifiers="const">
@@ -185,9 +185,9 @@
</method>
<method name="get_next_selected">
<return type="TreeItem" />
- <argument index="0" name="from" type="Object" />
+ <argument index="0" name="from" type="TreeItem" />
<description>
- Returns the next selected item after the given one, or [code]null[/code] if the end is reached.
+ Returns the next selected [TreeItem] after the given one, or [code]null[/code] if the end is reached.
If [code]from[/code] is [code]null[/code], this returns the first selected item.
</description>
</method>
@@ -239,9 +239,9 @@
</method>
<method name="scroll_to_item">
<return type="void" />
- <argument index="0" name="item" type="Object" />
+ <argument index="0" name="item" type="TreeItem" />
<description>
- Causes the [Tree] to jump to the specified item.
+ Causes the [Tree] to jump to the specified [TreeItem].
</description>
</method>
<method name="set_column_clip_content">
@@ -357,6 +357,13 @@
Emitted when a cell is selected.
</description>
</signal>
+ <signal name="check_propagated_to_item">
+ <argument index="0" name="item" type="TreeItem" />
+ <argument index="1" name="column" type="int" />
+ <description>
+ Emitted when [method TreeItem.propagate_check] is called. Connect to this signal to process the items that are affected when [method TreeItem.propagate_check] is invoked. The order that the items affected will be processed is as follows: the item that invoked the method, children of that item, and finally parents of that item.
+ </description>
+ </signal>
<signal name="column_title_pressed">
<argument index="0" name="column" type="int" />
<description>
@@ -467,13 +474,13 @@
<theme_item name="children_hl_line_color" data_type="color" type="Color" default="Color(0.27, 0.27, 0.27, 1)">
The [Color] of the relationship lines between the selected [TreeItem] and its children.
</theme_item>
- <theme_item name="custom_button_font_highlight" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ <theme_item name="custom_button_font_highlight" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
Text [Color] for a [constant TreeItem.CELL_MODE_CUSTOM] mode cell when it's hovered.
</theme_item>
<theme_item name="drop_position_color" data_type="color" type="Color" default="Color(1, 0.3, 0.2, 1)">
[Color] used to draw possible drop locations. See [enum DropModeFlags] constants for further description of drop locations.
</theme_item>
- <theme_item name="font_color" data_type="color" type="Color" default="Color(0.69, 0.69, 0.69, 1)">
+ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 1)">
Default text [Color] of the item.
</theme_item>
<theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
@@ -482,7 +489,7 @@
<theme_item name="font_selected_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Text [Color] used when the item is selected.
</theme_item>
- <theme_item name="guide_color" data_type="color" type="Color" default="Color(0, 0, 0, 0.1)">
+ <theme_item name="guide_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 0.25)">
[Color] of the guideline.
</theme_item>
<theme_item name="parent_hl_line_color" data_type="color" type="Color" default="Color(0.27, 0.27, 0.27, 1)">
@@ -491,7 +498,7 @@
<theme_item name="relationship_line_color" data_type="color" type="Color" default="Color(0.27, 0.27, 0.27, 1)">
The default [Color] of the relationship lines.
</theme_item>
- <theme_item name="title_button_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
+ <theme_item name="title_button_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
Default text [Color] of the title button.
</theme_item>
<theme_item name="button_margin" data_type="constant" type="int" default="4">
@@ -509,7 +516,7 @@
<theme_item name="hseparation" data_type="constant" type="int" default="4">
The horizontal space between item cells. This is also used as the margin at the start of an item when folding is disabled.
</theme_item>
- <theme_item name="item_margin" data_type="constant" type="int" default="12">
+ <theme_item name="item_margin" data_type="constant" type="int" default="16">
The horizontal margin at the start of an item. This is used when folding is enabled for the item.
</theme_item>
<theme_item name="outline_size" data_type="constant" type="int" default="0">
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index 7da98788bb..12c91cdd10 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -371,7 +371,7 @@
</method>
<method name="move_after">
<return type="void" />
- <argument index="0" name="item" type="Object" />
+ <argument index="0" name="item" type="TreeItem" />
<description>
Moves this TreeItem right after the given [code]item[/code].
[b]Note:[/b] You can't move to the root or move the root.
@@ -379,15 +379,23 @@
</method>
<method name="move_before">
<return type="void" />
- <argument index="0" name="item" type="Object" />
+ <argument index="0" name="item" type="TreeItem" />
<description>
Moves this TreeItem right before the given [code]item[/code].
[b]Note:[/b] You can't move to the root or move the root.
</description>
</method>
+ <method name="propagate_check">
+ <return type="void" />
+ <argument index="0" name="column" type="int" />
+ <argument index="1" name="emit_signal" type="bool" default="true" />
+ <description>
+ Propagates this item's checked status to its children and parents for the given [code]column[/code]. It is possible to process the items affected by this method call by connecting to [signal Tree.check_propagated_to_item]. The order that the items affected will be processed is as follows: the item invoking this method, children of that item, and finally parents of that item. If [code]emit_signal[/code] is [code]false[/code], then [signal Tree.check_propagated_to_item] will not be emitted.
+ </description>
+ </method>
<method name="remove_child">
<return type="void" />
- <argument index="0" name="child" type="Object" />
+ <argument index="0" name="child" type="TreeItem" />
<description>
Removes the given child [TreeItem] and all its children from the [Tree]. Note that it doesn't free the item from memory, so it can be reused later. To completely remove a [TreeItem] use [method Object.free].
</description>
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index 1cba995366..a1b53346d8 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -146,7 +146,7 @@
<description>
Sets the number of times the tweening sequence will be repeated, i.e. [code]set_loops(2)[/code] will run the animation twice.
Calling this method without arguments will make the [Tween] run infinitely, until it is either killed by [method kill] or by freeing bound node, or all the animated objects have been freed (which makes further animation impossible).
- [b]Warning:[/b] Make sure to always add some duration/delay when using infinite loops. 0-duration looped animations (e.g. single [CallbackTweener] with no delay) are equivalent to infinite [code]while[/code] loops and will freeze your game.
+ [b]Warning:[/b] Make sure to always add some duration/delay when using infinite loops. 0-duration looped animations (e.g. single [CallbackTweener] with no delay or [PropertyTweener] with invalid node) are equivalent to infinite [code]while[/code] loops and will freeze your game. If a [Tween]'s lifetime depends on some node, always use [method bind_node].
</description>
</method>
<method name="set_parallel">
diff --git a/doc/classes/VFlowContainer.xml b/doc/classes/VFlowContainer.xml
new file mode 100644
index 0000000000..f58075a140
--- /dev/null
+++ b/doc/classes/VFlowContainer.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VFlowContainer" inherits="FlowContainer" version="4.0">
+ <brief_description>
+ Vertical flow container.
+ </brief_description>
+ <description>
+ Vertical version of [FlowContainer].
+ </description>
+ <tutorials>
+ </tutorials>
+ <theme_items>
+ <theme_item name="hseparation" data_type="constant" type="int" default="4">
+ The horizontal separation of children nodes.
+ </theme_item>
+ <theme_item name="vseparation" data_type="constant" type="int" default="4">
+ The vertical separation of children nodes.
+ </theme_item>
+ </theme_items>
+</class>
diff --git a/doc/classes/VSplitContainer.xml b/doc/classes/VSplitContainer.xml
index 6316068e9d..33470ac7be 100644
--- a/doc/classes/VSplitContainer.xml
+++ b/doc/classes/VSplitContainer.xml
@@ -18,7 +18,5 @@
<theme_item name="grabber" data_type="icon" type="Texture2D">
The icon used for the grabber drawn in the middle area.
</theme_item>
- <theme_item name="bg" data_type="style" type="StyleBox">
- </theme_item>
</theme_items>
</class>
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 4a11fbb489..1b37cab68e 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -107,6 +107,12 @@
Returns the drag data from the GUI, that was previously returned by [method Control._get_drag_data].
</description>
</method>
+ <method name="gui_get_focus_owner">
+ <return type="Control" />
+ <description>
+ Returns the [Control] having the focus within this viewport. If no [Control] has the focus, returns null.
+ </description>
+ </method>
<method name="gui_is_drag_successful" qualifiers="const">
<return type="bool" />
<description>
@@ -119,6 +125,12 @@
Returns [code]true[/code] if the viewport is currently performing a drag operation.
</description>
</method>
+ <method name="gui_release_focus">
+ <return type="void" />
+ <description>
+ Removes the focus from the currently focussed [Control] within this viewport. If no [Control] has the focus, does nothing.
+ </description>
+ </method>
<method name="is_embedding_subwindows" qualifiers="const">
<return type="bool" />
<description>
diff --git a/doc/classes/VisualShaderNode.xml b/doc/classes/VisualShaderNode.xml
index cebe7f215f..0067240820 100644
--- a/doc/classes/VisualShaderNode.xml
+++ b/doc/classes/VisualShaderNode.xml
@@ -47,6 +47,7 @@
<return type="void" />
<argument index="0" name="port" type="int" />
<argument index="1" name="value" type="Variant" />
+ <argument index="2" name="prev_value" type="Variant" default="null" />
<description>
Sets the default value for the selected input [code]port[/code].
</description>
@@ -71,19 +72,22 @@
<constant name="PORT_TYPE_SCALAR_INT" value="1" enum="PortType">
Integer scalar. Translated to [code]int[/code] type in shader code.
</constant>
- <constant name="PORT_TYPE_VECTOR" value="2" enum="PortType">
+ <constant name="PORT_TYPE_VECTOR_2D" value="2" enum="PortType">
+ 2D vector of floating-point values. Translated to [code]vec2[/code] type in shader code.
+ </constant>
+ <constant name="PORT_TYPE_VECTOR" value="3" enum="PortType">
3D vector of floating-point values. Translated to [code]vec3[/code] type in shader code.
</constant>
- <constant name="PORT_TYPE_BOOLEAN" value="3" enum="PortType">
+ <constant name="PORT_TYPE_BOOLEAN" value="4" enum="PortType">
Boolean type. Translated to [code]bool[/code] type in shader code.
</constant>
- <constant name="PORT_TYPE_TRANSFORM" value="4" enum="PortType">
+ <constant name="PORT_TYPE_TRANSFORM" value="5" enum="PortType">
Transform type. Translated to [code]mat4[/code] type in shader code.
</constant>
- <constant name="PORT_TYPE_SAMPLER" value="5" enum="PortType">
+ <constant name="PORT_TYPE_SAMPLER" value="6" enum="PortType">
Sampler type. Translated to reference of sampler uniform in shader code. Can only be used for input ports in non-uniform nodes.
</constant>
- <constant name="PORT_TYPE_MAX" value="6" enum="PortType">
+ <constant name="PORT_TYPE_MAX" value="7" enum="PortType">
Represents the size of the [enum PortType] enum.
</constant>
</constants>
diff --git a/doc/classes/VisualShaderNodeClamp.xml b/doc/classes/VisualShaderNodeClamp.xml
index a68cbbec49..7219ba14b7 100644
--- a/doc/classes/VisualShaderNodeClamp.xml
+++ b/doc/classes/VisualShaderNodeClamp.xml
@@ -20,10 +20,13 @@
<constant name="OP_TYPE_INT" value="1" enum="OpType">
An integer scalar.
</constant>
- <constant name="OP_TYPE_VECTOR" value="2" enum="OpType">
- A vector type.
+ <constant name="OP_TYPE_VECTOR_2D" value="2" enum="OpType">
+ A 2D vector type.
</constant>
- <constant name="OP_TYPE_MAX" value="3" enum="OpType">
+ <constant name="OP_TYPE_VECTOR_3D" value="3" enum="OpType">
+ A 3D vector type.
+ </constant>
+ <constant name="OP_TYPE_MAX" value="4" enum="OpType">
Represents the size of the [enum OpType] enum.
</constant>
</constants>
diff --git a/doc/classes/VisualShaderNodeCompare.xml b/doc/classes/VisualShaderNodeCompare.xml
index 49bf952b31..ddd7f87cb8 100644
--- a/doc/classes/VisualShaderNodeCompare.xml
+++ b/doc/classes/VisualShaderNodeCompare.xml
@@ -10,7 +10,7 @@
</tutorials>
<members>
<member name="condition" type="int" setter="set_condition" getter="get_condition" enum="VisualShaderNodeCompare.Condition" default="0">
- Extra condition which is applied if [member type] is set to [constant CTYPE_VECTOR].
+ Extra condition which is applied if [member type] is set to [constant CTYPE_VECTOR_3D].
</member>
<member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeCompare.Function" default="0">
A comparison function. See [enum Function] for options.
@@ -26,16 +26,19 @@
<constant name="CTYPE_SCALAR_INT" value="1" enum="ComparisonType">
An integer scalar.
</constant>
- <constant name="CTYPE_VECTOR" value="2" enum="ComparisonType">
+ <constant name="CTYPE_VECTOR_2D" value="2" enum="ComparisonType">
+ A 2D vector type.
+ </constant>
+ <constant name="CTYPE_VECTOR_3D" value="3" enum="ComparisonType">
A 3D vector type.
</constant>
- <constant name="CTYPE_BOOLEAN" value="3" enum="ComparisonType">
+ <constant name="CTYPE_BOOLEAN" value="4" enum="ComparisonType">
A boolean type.
</constant>
- <constant name="CTYPE_TRANSFORM" value="4" enum="ComparisonType">
+ <constant name="CTYPE_TRANSFORM" value="5" enum="ComparisonType">
A transform ([code]mat4[/code]) type.
</constant>
- <constant name="CTYPE_MAX" value="5" enum="ComparisonType">
+ <constant name="CTYPE_MAX" value="6" enum="ComparisonType">
Represents the size of the [enum ComparisonType] enum.
</constant>
<constant name="FUNC_EQUAL" value="0" enum="Function">
diff --git a/doc/classes/VisualShaderNodeDerivativeFunc.xml b/doc/classes/VisualShaderNodeDerivativeFunc.xml
new file mode 100644
index 0000000000..704af44d04
--- /dev/null
+++ b/doc/classes/VisualShaderNodeDerivativeFunc.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeDerivativeFunc" inherits="VisualShaderNode" version="4.0">
+ <brief_description>
+ Calculates a derivative within the visual shader graph.
+ </brief_description>
+ <description>
+ This node is only available in [code]Fragment[/code] and [code]Light[/code] visual shaders.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeDerivativeFunc.Function" default="0">
+ A derivative function type. See [enum Function] for options.
+ </member>
+ <member name="op_type" type="int" setter="set_op_type" getter="get_op_type" enum="VisualShaderNodeDerivativeFunc.OpType" default="0">
+ A type of operands and returned value. See [enum OpType] for options.
+ </member>
+ </members>
+ <constants>
+ <constant name="OP_TYPE_SCALAR" value="0" enum="OpType">
+ A floating-point scalar.
+ </constant>
+ <constant name="OP_TYPE_VECTOR_2D" value="1" enum="OpType">
+ A 2D vector type.
+ </constant>
+ <constant name="OP_TYPE_VECTOR_3D" value="2" enum="OpType">
+ A 3D vector type.
+ </constant>
+ <constant name="OP_TYPE_MAX" value="3" enum="OpType">
+ Represents the size of the [enum OpType] enum.
+ </constant>
+ <constant name="FUNC_SUM" value="0" enum="Function">
+ Sum of absolute derivative in [code]x[/code] and [code]y[/code].
+ </constant>
+ <constant name="FUNC_X" value="1" enum="Function">
+ Derivative in [code]x[/code] using local differencing.
+ </constant>
+ <constant name="FUNC_Y" value="2" enum="Function">
+ Derivative in [code]y[/code] using local differencing.
+ </constant>
+ <constant name="FUNC_MAX" value="3" enum="Function">
+ Represents the size of the [enum Function] enum.
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeFaceForward.xml b/doc/classes/VisualShaderNodeFaceForward.xml
index 80cb8aea4e..cd2b3972b7 100644
--- a/doc/classes/VisualShaderNodeFaceForward.xml
+++ b/doc/classes/VisualShaderNodeFaceForward.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeFaceForward" inherits="VisualShaderNode" version="4.0">
+<class name="VisualShaderNodeFaceForward" inherits="VisualShaderNodeVectorBase" version="4.0">
<brief_description>
Returns the vector that points in the same direction as a reference vector within the visual shader graph.
</brief_description>
diff --git a/doc/classes/VisualShaderNodeMix.xml b/doc/classes/VisualShaderNodeMix.xml
index 1ef580a983..8a2f289a11 100644
--- a/doc/classes/VisualShaderNodeMix.xml
+++ b/doc/classes/VisualShaderNodeMix.xml
@@ -15,15 +15,21 @@
</members>
<constants>
<constant name="OP_TYPE_SCALAR" value="0" enum="OpType">
- A scalar type.
+ A floating-point scalar.
</constant>
- <constant name="OP_TYPE_VECTOR" value="1" enum="OpType">
- A vector type.
+ <constant name="OP_TYPE_VECTOR_2D" value="1" enum="OpType">
+ A 2D vector type.
</constant>
- <constant name="OP_TYPE_VECTOR_SCALAR" value="2" enum="OpType">
- A vector type. [code]weight[/code] port is using a scalar type.
+ <constant name="OP_TYPE_VECTOR_2D_SCALAR" value="2" enum="OpType">
+ The [code]a[/code] and [code]b[/code] ports use a 2D vector type. The [code]weight[/code] port uses a scalar type.
</constant>
- <constant name="OP_TYPE_MAX" value="3" enum="OpType">
+ <constant name="OP_TYPE_VECTOR_3D" value="3" enum="OpType">
+ A 3D vector type.
+ </constant>
+ <constant name="OP_TYPE_VECTOR_3D_SCALAR" value="4" enum="OpType">
+ The [code]a[/code] and [code]b[/code] ports use a 3D vector type. The [code]weight[/code] port uses a scalar type.
+ </constant>
+ <constant name="OP_TYPE_MAX" value="5" enum="OpType">
Represents the size of the [enum OpType] enum.
</constant>
</constants>
diff --git a/doc/classes/VisualShaderNodeMultiplyAdd.xml b/doc/classes/VisualShaderNodeMultiplyAdd.xml
index a0e9aef703..a6168e4df1 100644
--- a/doc/classes/VisualShaderNodeMultiplyAdd.xml
+++ b/doc/classes/VisualShaderNodeMultiplyAdd.xml
@@ -15,12 +15,15 @@
</members>
<constants>
<constant name="OP_TYPE_SCALAR" value="0" enum="OpType">
- A scalar type.
+ A floating-point scalar type.
</constant>
- <constant name="OP_TYPE_VECTOR" value="1" enum="OpType">
- A vector type.
+ <constant name="OP_TYPE_VECTOR_2D" value="1" enum="OpType">
+ A 2D vector type.
</constant>
- <constant name="OP_TYPE_MAX" value="2" enum="OpType">
+ <constant name="OP_TYPE_VECTOR_3D" value="2" enum="OpType">
+ A 3D vector type.
+ </constant>
+ <constant name="OP_TYPE_MAX" value="3" enum="OpType">
Represents the size of the [enum OpType] enum.
</constant>
</constants>
diff --git a/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml b/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml
deleted file mode 100644
index 8d108a5d28..0000000000
--- a/doc/classes/VisualShaderNodeScalarDerivativeFunc.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeScalarDerivativeFunc" inherits="VisualShaderNode" version="4.0">
- <brief_description>
- Calculates a scalar derivative within the visual shader graph.
- </brief_description>
- <description>
- This node is only available in [code]Fragment[/code] and [code]Light[/code] visual shaders.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeScalarDerivativeFunc.Function" default="0">
- The derivative type. See [enum Function] for options.
- </member>
- </members>
- <constants>
- <constant name="FUNC_SUM" value="0" enum="Function">
- Sum of absolute derivative in [code]x[/code] and [code]y[/code].
- </constant>
- <constant name="FUNC_X" value="1" enum="Function">
- Derivative in [code]x[/code] using local differencing.
- </constant>
- <constant name="FUNC_Y" value="2" enum="Function">
- Derivative in [code]y[/code] using local differencing.
- </constant>
- <constant name="FUNC_MAX" value="3" enum="Function">
- Represents the size of the [enum Function] enum.
- </constant>
- </constants>
-</class>
diff --git a/doc/classes/VisualShaderNodeSmoothStep.xml b/doc/classes/VisualShaderNodeSmoothStep.xml
index 2f8c7e0f33..714b9d8267 100644
--- a/doc/classes/VisualShaderNodeSmoothStep.xml
+++ b/doc/classes/VisualShaderNodeSmoothStep.xml
@@ -16,15 +16,21 @@
</members>
<constants>
<constant name="OP_TYPE_SCALAR" value="0" enum="OpType">
- A scalar type.
+ A floating-point scalar type.
</constant>
- <constant name="OP_TYPE_VECTOR" value="1" enum="OpType">
- A vector type.
+ <constant name="OP_TYPE_VECTOR_2D" value="1" enum="OpType">
+ A 2D vector type.
</constant>
- <constant name="OP_TYPE_VECTOR_SCALAR" value="2" enum="OpType">
- A vector type. [code]edge0[/code] and [code]edge1[/code] are using a scalar type.
+ <constant name="OP_TYPE_VECTOR_2D_SCALAR" value="2" enum="OpType">
+ The [code]x[/code] port uses a 2D vector type. The first two ports use a floating-point scalar type.
</constant>
- <constant name="OP_TYPE_MAX" value="3" enum="OpType">
+ <constant name="OP_TYPE_VECTOR_3D" value="3" enum="OpType">
+ A 3D vector type.
+ </constant>
+ <constant name="OP_TYPE_VECTOR_3D_SCALAR" value="4" enum="OpType">
+ The [code]x[/code] port uses a 3D vector type. The first two ports use a floating-point scalar type.
+ </constant>
+ <constant name="OP_TYPE_MAX" value="5" enum="OpType">
Represents the size of the [enum OpType] enum.
</constant>
</constants>
diff --git a/doc/classes/VisualShaderNodeStep.xml b/doc/classes/VisualShaderNodeStep.xml
index 5d8b464814..f65c95ea9e 100644
--- a/doc/classes/VisualShaderNodeStep.xml
+++ b/doc/classes/VisualShaderNodeStep.xml
@@ -16,15 +16,21 @@
</members>
<constants>
<constant name="OP_TYPE_SCALAR" value="0" enum="OpType">
- A scalar type.
+ A floating-point scalar type.
</constant>
- <constant name="OP_TYPE_VECTOR" value="1" enum="OpType">
- A vector type.
+ <constant name="OP_TYPE_VECTOR_2D" value="1" enum="OpType">
+ A 2D vector type.
</constant>
- <constant name="OP_TYPE_VECTOR_SCALAR" value="2" enum="OpType">
- A vector type. [code]edge[/code] port is using a scalar type.
+ <constant name="OP_TYPE_VECTOR_2D_SCALAR" value="2" enum="OpType">
+ The [code]x[/code] port uses a 2D vector type, while the [code]edge[/code] port uses a floating-point scalar type.
</constant>
- <constant name="OP_TYPE_MAX" value="3" enum="OpType">
+ <constant name="OP_TYPE_VECTOR_3D" value="3" enum="OpType">
+ A 3D vector type.
+ </constant>
+ <constant name="OP_TYPE_VECTOR_3D_SCALAR" value="4" enum="OpType">
+ The [code]x[/code] port uses a 3D vector type, while the [code]edge[/code] port uses a floating-point scalar type.
+ </constant>
+ <constant name="OP_TYPE_MAX" value="5" enum="OpType">
Represents the size of the [enum OpType] enum.
</constant>
</constants>
diff --git a/doc/classes/VisualShaderNodeSwitch.xml b/doc/classes/VisualShaderNodeSwitch.xml
index 921092cd07..71888ec2c3 100644
--- a/doc/classes/VisualShaderNodeSwitch.xml
+++ b/doc/classes/VisualShaderNodeSwitch.xml
@@ -20,16 +20,19 @@
<constant name="OP_TYPE_INT" value="1" enum="OpType">
An integer scalar.
</constant>
- <constant name="OP_TYPE_VECTOR" value="2" enum="OpType">
- A vector type.
+ <constant name="OP_TYPE_VECTOR_2D" value="2" enum="OpType">
+ A 2D vector type.
</constant>
- <constant name="OP_TYPE_BOOLEAN" value="3" enum="OpType">
+ <constant name="OP_TYPE_VECTOR_3D" value="3" enum="OpType">
+ A 3D vector type.
+ </constant>
+ <constant name="OP_TYPE_BOOLEAN" value="4" enum="OpType">
A boolean type.
</constant>
- <constant name="OP_TYPE_TRANSFORM" value="4" enum="OpType">
+ <constant name="OP_TYPE_TRANSFORM" value="5" enum="OpType">
A transform type.
</constant>
- <constant name="OP_TYPE_MAX" value="5" enum="OpType">
+ <constant name="OP_TYPE_MAX" value="6" enum="OpType">
Represents the size of the [enum OpType] enum.
</constant>
</constants>
diff --git a/doc/classes/VisualShaderNodeVec2Constant.xml b/doc/classes/VisualShaderNodeVec2Constant.xml
new file mode 100644
index 0000000000..f897ab4091
--- /dev/null
+++ b/doc/classes/VisualShaderNodeVec2Constant.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeVec2Constant" inherits="VisualShaderNodeConstant" version="4.0">
+ <brief_description>
+ A [Vector2] constant to be used within the visual shader graph.
+ </brief_description>
+ <description>
+ A constant [Vector2], which can be used as an input node.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="constant" type="Vector2" setter="set_constant" getter="get_constant" default="Vector2(0, 0)">
+ A [Vector2] constant which represents the state of this node.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/VisualShaderNodeVec2Uniform.xml b/doc/classes/VisualShaderNodeVec2Uniform.xml
new file mode 100644
index 0000000000..d5cf7e57c8
--- /dev/null
+++ b/doc/classes/VisualShaderNodeVec2Uniform.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeVec2Uniform" inherits="VisualShaderNodeUniform" version="4.0">
+ <brief_description>
+ A [Vector2] uniform to be used within the visual shader graph.
+ </brief_description>
+ <description>
+ Translated to [code]uniform vec2[/code] in the shader language.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="default_value" type="Vector2" setter="set_default_value" getter="get_default_value" default="Vector2(0, 0)">
+ A default value to be assigned within the shader.
+ </member>
+ <member name="default_value_enabled" type="bool" setter="set_default_value_enabled" getter="is_default_value_enabled" default="false">
+ Enables usage of the [member default_value].
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/VisualShaderNodeVectorBase.xml b/doc/classes/VisualShaderNodeVectorBase.xml
new file mode 100644
index 0000000000..8a560c1951
--- /dev/null
+++ b/doc/classes/VisualShaderNodeVectorBase.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeVectorBase" inherits="VisualShaderNode" version="4.0">
+ <brief_description>
+ A base type for the nodes using different vector types within the visual shader graph.
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="op_type" type="int" setter="set_op_type" getter="get_op_type" enum="VisualShaderNodeVectorBase.OpType" default="1">
+ A base type.
+ </member>
+ </members>
+ <constants>
+ <constant name="OP_TYPE_VECTOR_2D" value="0" enum="OpType">
+ A 2D vector type.
+ </constant>
+ <constant name="OP_TYPE_VECTOR_3D" value="1" enum="OpType">
+ A 3D vector type.
+ </constant>
+ <constant name="OP_TYPE_MAX" value="2" enum="OpType">
+ Represents the size of the [enum OpType] enum.
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/VisualShaderNodeVectorCompose.xml b/doc/classes/VisualShaderNodeVectorCompose.xml
index ebc30d03f4..b8caf154f9 100644
--- a/doc/classes/VisualShaderNodeVectorCompose.xml
+++ b/doc/classes/VisualShaderNodeVectorCompose.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeVectorCompose" inherits="VisualShaderNode" version="4.0">
+<class name="VisualShaderNodeVectorCompose" inherits="VisualShaderNodeVectorBase" version="4.0">
<brief_description>
Composes a [Vector3] from three scalars within the visual shader graph.
</brief_description>
diff --git a/doc/classes/VisualShaderNodeVectorDecompose.xml b/doc/classes/VisualShaderNodeVectorDecompose.xml
index 09986bf969..9ef9db0cfb 100644
--- a/doc/classes/VisualShaderNodeVectorDecompose.xml
+++ b/doc/classes/VisualShaderNodeVectorDecompose.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeVectorDecompose" inherits="VisualShaderNode" version="4.0">
+<class name="VisualShaderNodeVectorDecompose" inherits="VisualShaderNodeVectorBase" version="4.0">
<brief_description>
Decomposes a [Vector3] into three scalars within the visual shader graph.
</brief_description>
diff --git a/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml b/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml
deleted file mode 100644
index e0c7c8618c..0000000000
--- a/doc/classes/VisualShaderNodeVectorDerivativeFunc.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeVectorDerivativeFunc" inherits="VisualShaderNode" version="4.0">
- <brief_description>
- Calculates a vector derivative within the visual shader graph.
- </brief_description>
- <description>
- This node is only available in [code]Fragment[/code] and [code]Light[/code] visual shaders.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeVectorDerivativeFunc.Function" default="0">
- A derivative type. See [enum Function] for options.
- </member>
- </members>
- <constants>
- <constant name="FUNC_SUM" value="0" enum="Function">
- Sum of absolute derivative in [code]x[/code] and [code]y[/code].
- </constant>
- <constant name="FUNC_X" value="1" enum="Function">
- Derivative in [code]x[/code] using local differencing.
- </constant>
- <constant name="FUNC_Y" value="2" enum="Function">
- Derivative in [code]y[/code] using local differencing.
- </constant>
- <constant name="FUNC_MAX" value="3" enum="Function">
- Represents the size of the [enum Function] enum.
- </constant>
- </constants>
-</class>
diff --git a/doc/classes/VisualShaderNodeVectorDistance.xml b/doc/classes/VisualShaderNodeVectorDistance.xml
index 098787e583..c0d8ffe943 100644
--- a/doc/classes/VisualShaderNodeVectorDistance.xml
+++ b/doc/classes/VisualShaderNodeVectorDistance.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeVectorDistance" inherits="VisualShaderNode" version="4.0">
+<class name="VisualShaderNodeVectorDistance" inherits="VisualShaderNodeVectorBase" version="4.0">
<brief_description>
Returns the distance between two points. To be used within the visual shader graph.
</brief_description>
diff --git a/doc/classes/VisualShaderNodeVectorFunc.xml b/doc/classes/VisualShaderNodeVectorFunc.xml
index 27ae82e11b..17a6418b94 100644
--- a/doc/classes/VisualShaderNodeVectorFunc.xml
+++ b/doc/classes/VisualShaderNodeVectorFunc.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeVectorFunc" inherits="VisualShaderNode" version="4.0">
+<class name="VisualShaderNodeVectorFunc" inherits="VisualShaderNodeVectorBase" version="4.0">
<brief_description>
A vector function to be used within the visual shader graph.
</brief_description>
diff --git a/doc/classes/VisualShaderNodeVectorLen.xml b/doc/classes/VisualShaderNodeVectorLen.xml
index 165455e622..f1b6919bdc 100644
--- a/doc/classes/VisualShaderNodeVectorLen.xml
+++ b/doc/classes/VisualShaderNodeVectorLen.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeVectorLen" inherits="VisualShaderNode" version="4.0">
+<class name="VisualShaderNodeVectorLen" inherits="VisualShaderNodeVectorBase" version="4.0">
<brief_description>
Returns the length of a [Vector3] within the visual shader graph.
</brief_description>
diff --git a/doc/classes/VisualShaderNodeVectorOp.xml b/doc/classes/VisualShaderNodeVectorOp.xml
index 5e8f0abda3..6a579abb9a 100644
--- a/doc/classes/VisualShaderNodeVectorOp.xml
+++ b/doc/classes/VisualShaderNodeVectorOp.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualShaderNodeVectorOp" inherits="VisualShaderNode" version="4.0">
+<class name="VisualShaderNodeVectorOp" inherits="VisualShaderNodeVectorBase" version="4.0">
<brief_description>
A vector operator to be used within the visual shader graph.
</brief_description>
diff --git a/doc/classes/VoxelGI.xml b/doc/classes/VoxelGI.xml
index 2c3605aa1c..ac6f026a37 100644
--- a/doc/classes/VoxelGI.xml
+++ b/doc/classes/VoxelGI.xml
@@ -7,7 +7,7 @@
[VoxelGI]s are used to provide high-quality real-time indirect light and reflections to scenes. They precompute the effect of objects that emit light and the effect of static geometry to simulate the behavior of complex light in real-time. [VoxelGI]s need to be baked before having a visible effect. However, once baked, dynamic objects will receive light from them. Furthermore, lights can be fully dynamic or baked.
[b]Procedural generation:[/b] [VoxelGI] can be baked in an exported project, which makes it suitable for procedurally generated or user-built levels as long as all the geometry is generated in advance. For games where geometry is generated at any time during gameplay, SDFGI is more suitable (see [member Environment.sdfgi_enabled]).
[b]Performance:[/b] [VoxelGI] is relatively demanding on the GPU and is not suited to low-end hardware such as integrated graphics (consider [LightmapGI] instead). To improve performance, adjust [member ProjectSettings.rendering/global_illumination/voxel_gi/quality] and enable [member ProjectSettings.rendering/global_illumination/gi/use_half_resolution] in the Project Settings. To provide a fallback for low-end hardware, consider adding an option to disable [VoxelGI] in your project's options menus. A [VoxelGI] node can be disabled by hiding it.
- [b]Note:[/b] Meshes should have sufficiently thick walls to avoid light leaks (avoid one-sided walls). For interior levels, enclose your level geometry in a sufficiently large box and bridge the loops to close the mesh. To further prevent light leaks, you can also strategically place temporary [MeshInstance3D] nodes with their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_BAKED]. These temporary nodes can then be hidden after baking the [VoxelGI] node.
+ [b]Note:[/b] Meshes should have sufficiently thick walls to avoid light leaks (avoid one-sided walls). For interior levels, enclose your level geometry in a sufficiently large box and bridge the loops to close the mesh. To further prevent light leaks, you can also strategically place temporary [MeshInstance3D] nodes with their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_STATIC]. These temporary nodes can then be hidden after baking the [VoxelGI] node.
</description>
<tutorials>
<link title="GI probes">$DOCS_URL/tutorials/3d/gi_probes.html</link>
@@ -19,7 +19,7 @@
<argument index="0" name="from_node" type="Node" default="null" />
<argument index="1" name="create_visual_debug" type="bool" default="false" />
<description>
- Bakes the effect from all [GeometryInstance3D]s marked with [constant GeometryInstance3D.GI_MODE_BAKED] and [Light3D]s marked with either [constant Light3D.BAKE_DYNAMIC] or [constant Light3D.BAKE_STATIC]. If [code]create_visual_debug[/code] is [code]true[/code], after baking the light, this will generate a [MultiMesh] that has a cube representing each solid cell with each cube colored to the cell's albedo color. This can be used to visualize the [VoxelGI]'s data and debug any issues that may be occurring.
+ Bakes the effect from all [GeometryInstance3D]s marked with [constant GeometryInstance3D.GI_MODE_STATIC] and [Light3D]s marked with either [constant Light3D.BAKE_STATIC] or [constant Light3D.BAKE_DYNAMIC]. If [code]create_visual_debug[/code] is [code]true[/code], after baking the light, this will generate a [MultiMesh] that has a cube representing each solid cell with each cube colored to the cell's albedo color. This can be used to visualize the [VoxelGI]'s data and debug any issues that may be occurring.
[b]Note:[/b] [method bake] works from the editor and in exported projects. This makes it suitable for procedurally generated or user-built levels. Baking a [VoxelGI] node generally takes from 5 to 20 seconds in most scenes. Reducing [member subdiv] can speed up baking.
</description>
</method>
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 75161d3c5b..3bb6603646 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -368,6 +368,10 @@
<description>
</description>
</signal>
+ <signal name="theme_changed">
+ <description>
+ </description>
+ </signal>
<signal name="visibility_changed">
<description>
</description>
@@ -439,20 +443,20 @@
</constant>
</constants>
<theme_items>
- <theme_item name="title_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)">
+ <theme_item name="title_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
</theme_item>
<theme_item name="title_outline_modulate" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
The color of the title outline.
</theme_item>
<theme_item name="close_h_ofs" data_type="constant" type="int" default="18">
</theme_item>
- <theme_item name="close_v_ofs" data_type="constant" type="int" default="18">
+ <theme_item name="close_v_ofs" data_type="constant" type="int" default="24">
</theme_item>
<theme_item name="resize_margin" data_type="constant" type="int" default="4">
</theme_item>
<theme_item name="scaleborder_size" data_type="constant" type="int" default="4">
</theme_item>
- <theme_item name="title_height" data_type="constant" type="int" default="20">
+ <theme_item name="title_height" data_type="constant" type="int" default="36">
</theme_item>
<theme_item name="title_outline_size" data_type="constant" type="int" default="0">
The size of the title outline.
diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml
index d2bb6aa59a..cd09f83335 100644
--- a/doc/classes/XRInterfaceExtension.xml
+++ b/doc/classes/XRInterfaceExtension.xml
@@ -9,46 +9,52 @@
<tutorials>
</tutorials>
<methods>
- <method name="_commit_views" qualifiers="virtual">
+ <method name="_end_frame" qualifiers="virtual">
<return type="void" />
- <argument index="0" name="render_target" type="RID" />
- <argument index="1" name="screen_rect" type="Rect2" />
<description>
+ Called if interface is active and queues have been submitted.
</description>
</method>
<method name="_get_anchor_detection_is_enabled" qualifiers="virtual const">
<return type="bool" />
<description>
+ Return [code]true[/code] if anchor detection is enabled for this interface.
</description>
</method>
<method name="_get_camera_feed_id" qualifiers="virtual const">
<return type="int" />
<description>
+ Returns the camera feed id for the [CameraFeed] registered with the [CameraServer] that should be presented as the background on an AR capable device (if applicable).
</description>
</method>
<method name="_get_camera_transform" qualifiers="virtual">
<return type="Transform3D" />
<description>
+ Returns the [Transform3D] that positions the [XRCamera3D] in the world.
</description>
</method>
<method name="_get_capabilities" qualifiers="virtual const">
<return type="int" />
<description>
+ Returns the capabilities of this interface.
</description>
</method>
<method name="_get_name" qualifiers="virtual const">
<return type="StringName" />
<description>
+ Returns the name of this interface.
</description>
</method>
<method name="_get_play_area" qualifiers="virtual const">
<return type="PackedVector3Array" />
<description>
+ Returns an [PackedVector3Array] that denotes the play areas boundaries (if applicable).
</description>
</method>
<method name="_get_play_area_mode" qualifiers="virtual const">
<return type="int" />
<description>
+ Returns the [enum XRInterface.PlayAreaMode] that sets up our play area.
</description>
</method>
<method name="_get_projection_for_view" qualifiers="virtual">
@@ -58,27 +64,32 @@
<argument index="2" name="z_near" type="float" />
<argument index="3" name="z_far" type="float" />
<description>
+ Returns the projection matrix for the given view as a [PackedFloat64Array].
</description>
</method>
<method name="_get_render_target_size" qualifiers="virtual">
<return type="Vector2" />
<description>
+ Returns the size of our render target for this interface, this overrides the size of the [Viewport] marked as the xr viewport.
</description>
</method>
<method name="_get_suggested_pose_names" qualifiers="virtual const">
<return type="PackedStringArray" />
<argument index="0" name="tracker_name" type="StringName" />
<description>
+ Returns a [PackedStringArray] with pose names configured by this interface. Note that user configuration can override this list.
</description>
</method>
<method name="_get_suggested_tracker_names" qualifiers="virtual const">
<return type="PackedStringArray" />
<description>
+ Returns a [PackedStringArray] with tracker names configured by this interface. Note that user configuration can override this list.
</description>
</method>
<method name="_get_tracking_status" qualifiers="virtual const">
<return type="int" />
<description>
+ Returns a [enum XRInterface.TrackingStatus] specifying the current status of our tracking.
</description>
</method>
<method name="_get_transform_for_view" qualifiers="virtual">
@@ -86,50 +97,80 @@
<argument index="0" name="view" type="int" />
<argument index="1" name="cam_transform" type="Transform3D" />
<description>
+ Returns a [Transform3D] for a given view.
</description>
</method>
<method name="_get_view_count" qualifiers="virtual">
<return type="int" />
<description>
+ Returns the number of views this interface requires, 1 for mono, 2 for stereoscopic.
</description>
</method>
<method name="_initialize" qualifiers="virtual">
<return type="bool" />
<description>
+ Initializes the interface, returns [code]true[/code] on success.
</description>
</method>
<method name="_is_initialized" qualifiers="virtual const">
<return type="bool" />
<description>
+ Returns [code]true[/code] if this interface has been initialised.
</description>
</method>
<method name="_notification" qualifiers="virtual">
<return type="void" />
<argument index="0" name="what" type="int" />
<description>
+ Informs the interface of an applicable system notification.
+ </description>
+ </method>
+ <method name="_post_draw_viewport" qualifiers="virtual">
+ <return type="void" />
+ <argument index="0" name="render_target" type="RID" />
+ <argument index="1" name="screen_rect" type="Rect2" />
+ <description>
+ Called after the XR [Viewport] draw logic has completed.
+ </description>
+ </method>
+ <method name="_pre_draw_viewport" qualifiers="virtual">
+ <return type="bool" />
+ <argument index="0" name="render_target" type="RID" />
+ <description>
+ Called if this is our primary [XRInterfaceExtension] before we start processing a [Viewport] for every active XR [Viewport], returns [code]true[/code] if that viewport should be rendered. An XR interface may return [code]false[/code] if the user has taken off their headset and we can pause rendering.
+ </description>
+ </method>
+ <method name="_pre_render" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called if this [XRInterfaceExtension] is active before rendering starts, most XR interfaces will sync tracking at this point in time.
</description>
</method>
<method name="_process" qualifiers="virtual">
<return type="void" />
<description>
+ Called if this [XRInterfaceExtension] is active before our physics and game process is called. most XR interfaces will update its [XRPositionalTracker]s at this point in time.
</description>
</method>
<method name="_set_anchor_detection_is_enabled" qualifiers="virtual">
<return type="void" />
<argument index="0" name="enabled" type="bool" />
<description>
+ Enables anchor detection on this interface if supported.
</description>
</method>
<method name="_set_play_area_mode" qualifiers="virtual const">
<return type="bool" />
<argument index="0" name="mode" type="int" />
<description>
+ Set the play area mode for this interface.
</description>
</method>
<method name="_supports_play_area_mode" qualifiers="virtual const">
<return type="bool" />
<argument index="0" name="mode" type="int" enum="XRInterface.PlayAreaMode" />
<description>
+ Returns [code]true[/code] if this interface supports this play area mode.
</description>
</method>
<method name="_trigger_haptic_pulse" qualifiers="virtual">
@@ -141,11 +182,13 @@
<argument index="4" name="duration_sec" type="float" />
<argument index="5" name="delay_sec" type="float" />
<description>
+ Triggers a haptic pulse to be emitted on the specified tracker.
</description>
</method>
<method name="_uninitialize" qualifiers="virtual">
<return type="void" />
<description>
+ Uninitialize the interface.
</description>
</method>
<method name="add_blit">
@@ -169,6 +212,7 @@
<return type="RID" />
<argument index="0" name="render_target" type="RID" />
<description>
+ Returns a valid [RID] for a texture to which we should render the current frame if supported by the interface.
</description>
</method>
</methods>
diff --git a/doc/classes/XRPose.xml b/doc/classes/XRPose.xml
index 0de2bc9e48..4a09122b40 100644
--- a/doc/classes/XRPose.xml
+++ b/doc/classes/XRPose.xml
@@ -34,8 +34,22 @@
- [code]grip[/code] defines the location where the user grips the controller
- [code]skeleton[/code] defines the root location a hand mesh should be placed when using hand tracking and the animated skeleton supplied by the XR runtime.
</member>
+ <member name="tracking_confidence" type="int" setter="set_tracking_confidence" getter="get_tracking_confidence" enum="XRPose.TrackingConfidence" default="0">
+ The tracking confidence for this pose, provides insight on how accurate the spatial positioning of this record is.
+ </member>
<member name="transform" type="Transform3D" setter="set_transform" getter="get_transform" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">
The transform containing the original and transform as reported by the XR runtime.
</member>
</members>
+ <constants>
+ <constant name="XR_TRACKING_CONFIDENCE_NONE" value="0" enum="TrackingConfidence">
+ No tracking information is available for this pose.
+ </constant>
+ <constant name="XR_TRACKING_CONFIDENCE_LOW" value="1" enum="TrackingConfidence">
+ Tracking information may be inaccurate or estimated. For instance with inside out tracking this would indicate a controller may be (partially) obscured.
+ </constant>
+ <constant name="XR_TRACKING_CONFIDENCE_HIGH" value="2" enum="TrackingConfidence">
+ Tracking information is deemed accurate and up to date.
+ </constant>
+ </constants>
</class>
diff --git a/doc/classes/XRPositionalTracker.xml b/doc/classes/XRPositionalTracker.xml
index 439bcfc382..c39a4aa7a7 100644
--- a/doc/classes/XRPositionalTracker.xml
+++ b/doc/classes/XRPositionalTracker.xml
@@ -54,8 +54,9 @@
<argument index="1" name="transform" type="Transform3D" />
<argument index="2" name="linear_velocity" type="Vector3" />
<argument index="3" name="angular_velocity" type="Vector3" />
+ <argument index="4" name="tracking_confidence" type="int" enum="XRPose.TrackingConfidence" />
<description>
- Sets the transform, linear velocity and angular velocity for the given pose. This method is called by a [XRInterface] implementation and should not be used directly.
+ Sets the transform, linear velocity, angular velocity and tracking confidence for the given pose. This method is called by a [XRInterface] implementation and should not be used directly.
</description>
</method>
</methods>
diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml
index 3a7fdea8d0..335ca4c35f 100644
--- a/doc/classes/XRServer.xml
+++ b/doc/classes/XRServer.xml
@@ -69,24 +69,6 @@
Returns a list of available interfaces the ID and name of each interface.
</description>
</method>
- <method name="get_last_commit_usec">
- <return type="int" />
- <description>
- Returns the absolute timestamp (in μs) of the last [XRServer] commit of the AR/VR eyes to [RenderingServer]. The value comes from an internal call to [method Time.get_ticks_usec].
- </description>
- </method>
- <method name="get_last_frame_usec">
- <return type="int" />
- <description>
- Returns the duration (in μs) of the last frame. This is computed as the difference between [method get_last_commit_usec] and [method get_last_process_usec] when committing.
- </description>
- </method>
- <method name="get_last_process_usec">
- <return type="int" />
- <description>
- Returns the absolute timestamp (in μs) of the last [XRServer] process callback. The value comes from an internal call to [method Time.get_ticks_usec].
- </description>
- </method>
<method name="get_reference_frame" qualifiers="const">
<return type="Transform3D" />
<description>
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index 9e18866ca3..68f3b66f43 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -438,7 +438,7 @@ def main(): # type: () -> None
for path in args.path:
# Cut off trailing slashes so os.path.basename doesn't choke.
- if path.endswith(os.sep):
+ if path.endswith("/") or path.endswith("\\"):
path = path[:-1]
if os.path.basename(path) == "modules":
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 270725ee63..451960d772 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -184,7 +184,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
}
state.current_tex = RID();
- state.current_tex_ptr = NULL;
+ state.current_tex_ptr = nullptr;
state.current_normal = RID();
state.current_specular = RID();
state.canvas_texscreen_used = false;
@@ -233,13 +233,13 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
if (material != prev_material) {
- RasterizerStorageGLES3::Shader *shader_ptr = NULL;
+ RasterizerStorageGLES3::Shader *shader_ptr = nullptr;
if (material_ptr) {
shader_ptr = material_ptr->shader;
if (shader_ptr && shader_ptr->mode != RS::SHADER_CANVAS_ITEM) {
- shader_ptr = NULL; // not a canvas item shader, don't use.
+ shader_ptr = nullptr; // not a canvas item shader, don't use.
}
}
@@ -295,8 +295,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
t->detect_normal(t->detect_normal_ud);
}
#endif
- if (t->render_target)
+ if (t->render_target) {
t->render_target->used_in_frame = true;
+ }
glBindTexture(t->target, t->tex_id);
}
@@ -582,7 +583,7 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
if (pb->index_buffer != 0) {
- glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, 0);
+ glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr);
} else {
glDrawArrays(prim[polygon->primitive], 0, pb->count);
}
@@ -946,7 +947,7 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe
if (!texture) {
state.current_tex = RID();
- state.current_tex_ptr = NULL;
+ state.current_tex_ptr = nullptr;
ct->size_cache = Size2i(1, 1);
glActiveTexture(GL_TEXTURE0);
@@ -1096,7 +1097,7 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec
{
// Always uses vertex positions
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
- glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), NULL);
+ glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), nullptr);
const Vector2 *points_ptr = p_points.ptr();
for (uint32_t i = 0; i < vertex_count; i++) {
@@ -1313,7 +1314,7 @@ void RasterizerCanvasGLES3::initialize() {
glGenBuffers(1, &data.ninepatch_vertices);
glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices);
- glBufferData(GL_ARRAY_BUFFER, sizeof(float) * (16 + 16) * 2, NULL, GL_DYNAMIC_DRAW);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * (16 + 16) * 2, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -1443,7 +1444,7 @@ void fragment() {
default_canvas_texture = storage->canvas_texture_allocate();
storage->canvas_texture_initialize(default_canvas_texture);
- state.using_light = NULL;
+ state.using_light = nullptr;
state.using_transparent_rt = false;
state.using_skeleton = false;
state.current_shader_version = state.canvas_shader_default_version;
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 32ead8aa7e..087bf36473 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -130,46 +130,51 @@ void RasterizerGLES3::end_frame(bool p_swap_buffers) {
#ifdef CAN_DEBUG
static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) {
- if (type == _EXT_DEBUG_TYPE_OTHER_ARB)
+ if (type == _EXT_DEBUG_TYPE_OTHER_ARB) {
return;
+ }
- if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB)
+ if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB) {
return; //these are ultimately annoying, so removing for now
+ }
char debSource[256], debType[256], debSev[256];
- if (source == _EXT_DEBUG_SOURCE_API_ARB)
+ if (source == _EXT_DEBUG_SOURCE_API_ARB) {
strcpy(debSource, "OpenGL");
- else if (source == _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB)
+ } else if (source == _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB) {
strcpy(debSource, "Windows");
- else if (source == _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB)
+ } else if (source == _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB) {
strcpy(debSource, "Shader Compiler");
- else if (source == _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB)
+ } else if (source == _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB) {
strcpy(debSource, "Third Party");
- else if (source == _EXT_DEBUG_SOURCE_APPLICATION_ARB)
+ } else if (source == _EXT_DEBUG_SOURCE_APPLICATION_ARB) {
strcpy(debSource, "Application");
- else if (source == _EXT_DEBUG_SOURCE_OTHER_ARB)
+ } else if (source == _EXT_DEBUG_SOURCE_OTHER_ARB) {
strcpy(debSource, "Other");
+ }
- if (type == _EXT_DEBUG_TYPE_ERROR_ARB)
+ if (type == _EXT_DEBUG_TYPE_ERROR_ARB) {
strcpy(debType, "Error");
- else if (type == _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB)
+ } else if (type == _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB) {
strcpy(debType, "Deprecated behavior");
- else if (type == _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB)
+ } else if (type == _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB) {
strcpy(debType, "Undefined behavior");
- else if (type == _EXT_DEBUG_TYPE_PORTABILITY_ARB)
+ } else if (type == _EXT_DEBUG_TYPE_PORTABILITY_ARB) {
strcpy(debType, "Portability");
- else if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB)
+ } else if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB) {
strcpy(debType, "Performance");
- else if (type == _EXT_DEBUG_TYPE_OTHER_ARB)
+ } else if (type == _EXT_DEBUG_TYPE_OTHER_ARB) {
strcpy(debType, "Other");
+ }
- if (severity == _EXT_DEBUG_SEVERITY_HIGH_ARB)
+ if (severity == _EXT_DEBUG_SEVERITY_HIGH_ARB) {
strcpy(debSev, "High");
- else if (severity == _EXT_DEBUG_SEVERITY_MEDIUM_ARB)
+ } else if (severity == _EXT_DEBUG_SEVERITY_MEDIUM_ARB) {
strcpy(debSev, "Medium");
- else if (severity == _EXT_DEBUG_SEVERITY_LOW_ARB)
+ } else if (severity == _EXT_DEBUG_SEVERITY_LOW_ARB) {
strcpy(debSev, "Low");
+ }
String output = String() + "GL ERROR: Source: " + debSource + "\tType: " + debType + "\tID: " + itos(id) + "\tSeverity: " + debSev + "\tMessage: " + message;
@@ -203,7 +208,7 @@ void RasterizerGLES3::initialize() {
if (OS::get_singleton()->is_stdout_verbose()) {
if (GLAD_GL_ARB_debug_output) {
glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
- glDebugMessageCallbackARB(_gl_debug_print, NULL);
+ glDebugMessageCallbackARB(_gl_debug_print, nullptr);
glEnable(_EXT_DEBUG_OUTPUT);
} else {
print_line("OpenGL debugging not supported!");
@@ -215,12 +220,12 @@ void RasterizerGLES3::initialize() {
#ifdef CAN_DEBUG
#ifdef GLES_OVER_GL
if (OS::get_singleton()->is_stdout_verbose() && GLAD_GL_ARB_debug_output) {
- glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_ERROR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE);
- glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE);
- glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE);
- glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PORTABILITY_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE);
- glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PERFORMANCE_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE);
- glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_OTHER_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, NULL, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_ERROR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PORTABILITY_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PERFORMANCE_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_OTHER_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
// glDebugMessageInsertARB(
// GL_DEBUG_SOURCE_API_ARB,
// GL_DEBUG_TYPE_OTHER_ARB, 1,
@@ -299,8 +304,9 @@ void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_sc
}
void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) {
- if (p_image.is_null() || p_image->is_empty())
+ if (p_image.is_null() || p_image->is_empty()) {
return;
+ }
Size2i win_size = DisplayServer::get_singleton()->screen_get_size();
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 9e7d4f5435..121dc86fb2 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -208,7 +208,7 @@ void RasterizerSceneGLES3::environment_set_canvas_max_layer(RID p_env, int p_max
void RasterizerSceneGLES3::environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, RS::EnvironmentReflectionSource p_reflection_source) {
}
-void RasterizerSceneGLES3::environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) {
+void RasterizerSceneGLES3::environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) {
}
void RasterizerSceneGLES3::environment_glow_set_use_bicubic_upscale(bool p_enable) {
@@ -234,7 +234,7 @@ void RasterizerSceneGLES3::environment_set_ssil(RID p_env, bool p_enable, float
void RasterizerSceneGLES3::environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) {
}
-void RasterizerSceneGLES3::environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) {
+void RasterizerSceneGLES3::environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) {
}
void RasterizerSceneGLES3::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) {
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index b73c053bc7..246b908c14 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -115,7 +115,7 @@ public:
void environment_set_canvas_max_layer(RID p_env, int p_max_layer) override;
void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG) override;
- void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) override;
+ void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) override;
void environment_glow_set_use_bicubic_upscale(bool p_enable) override;
void environment_glow_set_use_high_quality(bool p_enable) override;
@@ -126,7 +126,7 @@ public:
void environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) override;
void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override;
- void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override;
+ void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override;
void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override;
void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override;
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index db449b7a08..fda208b812 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -55,16 +55,6 @@ GLuint RasterizerStorageGLES3::system_fbo = 0;
#define _EXT_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
#define _EXT_ETC1_RGB8_OES 0x8D64
-#define _EXT_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
-#define _EXT_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
-#define _EXT_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
-#define _EXT_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
-
-#define _EXT_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT 0x8A54
-#define _EXT_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT 0x8A55
-#define _EXT_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT 0x8A56
-#define _EXT_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT 0x8A57
-
#define _EXT_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C
#define _EXT_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D
#define _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E
@@ -312,57 +302,6 @@ Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_
need_decompress = true;
}
} break;
- case Image::FORMAT_PVRTC1_2: {
- if (config.pvrtc_supported) {
- r_gl_internal_format = _EXT_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
- r_gl_format = GL_RGBA;
- r_gl_type = GL_UNSIGNED_BYTE;
- r_compressed = true;
- //r_srgb = true;
-
- } else {
- need_decompress = true;
- }
- } break;
- case Image::FORMAT_PVRTC1_2A: {
- if (config.pvrtc_supported) {
- r_gl_internal_format = _EXT_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
- r_gl_format = GL_RGBA;
- r_gl_type = GL_UNSIGNED_BYTE;
- r_compressed = true;
- //r_srgb = true;
-
- } else {
- need_decompress = true;
- }
-
- } break;
- case Image::FORMAT_PVRTC1_4: {
- if (config.pvrtc_supported) {
- r_gl_internal_format = _EXT_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
- r_gl_format = GL_RGBA;
- r_gl_type = GL_UNSIGNED_BYTE;
- r_compressed = true;
- //r_srgb = true;
-
- } else {
- need_decompress = true;
- }
-
- } break;
- case Image::FORMAT_PVRTC1_4A: {
- if (config.pvrtc_supported) {
- r_gl_internal_format = _EXT_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
- r_gl_format = GL_RGBA;
- r_gl_type = GL_UNSIGNED_BYTE;
- r_compressed = true;
- //r_srgb = true;
-
- } else {
- need_decompress = true;
- }
-
- } break;
case Image::FORMAT_ETC: {
if (config.etc_supported) {
r_gl_internal_format = _EXT_ETC1_RGB8_OES;
@@ -1037,7 +976,7 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer)
*/
void RasterizerStorageGLES3::_texture_set_state_from_flags(Texture *p_tex) {
- if ((p_tex->flags & TEXTURE_FLAG_MIPMAPS) && !p_tex->ignore_mipmaps)
+ if ((p_tex->flags & TEXTURE_FLAG_MIPMAPS) && !p_tex->ignore_mipmaps) {
if (p_tex->flags & TEXTURE_FLAG_FILTER) {
// these do not exactly correspond ...
p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS);
@@ -1046,7 +985,7 @@ void RasterizerStorageGLES3::_texture_set_state_from_flags(Texture *p_tex) {
p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
//texture->glTexParam_MinFilter(texture->target, config.use_fast_texture_filter ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR);
}
- else {
+ } else {
if (p_tex->flags & TEXTURE_FLAG_FILTER) {
p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
//texture->glTexParam_MinFilter(texture->target, GL_LINEAR);
@@ -1186,8 +1125,9 @@ void RasterizerStorageGLES3::texture_debug_usage(List<RS::TextureInfo> *r_info)
for (List<RID>::Element *E = textures.front(); E; E = E->next()) {
Texture *t = texture_owner.get_or_null(E->get());
- if (!t)
+ if (!t) {
continue;
+ }
RS::TextureInfo tinfo;
tinfo.path = t->path;
tinfo.format = t->format;
@@ -1234,7 +1174,7 @@ void RasterizerStorageGLES3::texture_set_proxy(RID p_texture, RID p_proxy) {
if (texture->proxy) {
texture->proxy->proxy_owners.erase(texture);
- texture->proxy = NULL;
+ texture->proxy = nullptr;
}
if (p_proxy.is_valid()) {
@@ -1359,8 +1299,9 @@ void RasterizerStorageGLES3::shader_initialize(RID p_rid) {
//}
void RasterizerStorageGLES3::_shader_make_dirty(Shader *p_shader) {
- if (p_shader->dirty_list.in_list())
+ if (p_shader->dirty_list.in_list()) {
return;
+ }
_shader_dirty_list.add(&p_shader->dirty_list);
}
@@ -1432,7 +1373,7 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
}
ShaderCompiler::GeneratedCode gen_code;
- ShaderCompiler::IdentifierActions *actions = NULL;
+ ShaderCompiler::IdentifierActions *actions = nullptr;
switch (p_shader->mode) {
case RS::SHADER_CANVAS_ITEM: {
@@ -1758,8 +1699,9 @@ RID RasterizerStorageGLES3::shader_get_default_texture_param(RID p_shader, const
/* COMMON MATERIAL API */
void RasterizerStorageGLES3::_material_make_dirty(Material *p_material) const {
- if (p_material->dirty_list.in_list())
+ if (p_material->dirty_list.in_list()) {
return;
+ }
_material_dirty_list.add(&p_material->dirty_list);
}
@@ -2015,8 +1957,9 @@ void RasterizerStorageGLES3::_update_material(Material *p_material) {
p_material->textures.resize(p_material->shader->texture_uniforms.size());
for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = p_material->shader->uniforms.front(); E; E = E->next()) {
- if (E->get().texture_order < 0)
+ if (E->get().texture_order < 0) {
continue; // not a texture, does not go here
+ }
RID texture;
@@ -2970,7 +2913,7 @@ void RasterizerStorageGLES3::_set_current_render_target(RID p_render_target) {
_dims.win_height = rt->height;
} else {
- frame.current_rt = NULL;
+ frame.current_rt = nullptr;
frame.clear_request = false;
bind_framebuffer_system();
}
@@ -2978,8 +2921,9 @@ void RasterizerStorageGLES3::_set_current_render_target(RID p_render_target) {
void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) {
// do not allocate a render target with no size
- if (rt->width <= 0 || rt->height <= 0)
+ if (rt->width <= 0 || rt->height <= 0) {
return;
+ }
// do not allocate a render target that is attached to the screen
if (rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) {
@@ -3027,7 +2971,7 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) {
glGenTextures(1, &rt->color);
glBindTexture(GL_TEXTURE_2D, rt->color);
- glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, rt->width, rt->height, 0, color_format, color_type, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, rt->width, rt->height, 0, color_format, color_type, nullptr);
if (texture->flags & TEXTURE_FLAG_FILTER) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -3047,7 +2991,7 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) {
if (config.support_depth_texture) {
glGenTextures(1, &rt->depth);
glBindTexture(GL_TEXTURE_2D, rt->depth);
- glTexImage2D(GL_TEXTURE_2D, 0, config.depth_internalformat, rt->width, rt->height, 0, GL_DEPTH_COMPONENT, config.depth_type, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, config.depth_internalformat, rt->width, rt->height, 0, GL_DEPTH_COMPONENT, config.depth_type, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -3170,9 +3114,9 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) {
glBindTexture(GL_TEXTURE_2D, rt->copy_screen_effect.color);
if (rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rt->width, rt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rt->width, rt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
} else {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, rt->width, rt->height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, rt->width, rt->height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -3220,8 +3164,9 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) {
w >>= 1;
h >>= 1;
- if (w < 2 || h < 2)
+ if (w < 2 || h < 2) {
break;
+ }
level++;
}
@@ -3234,7 +3179,7 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) {
glBindTexture(GL_TEXTURE_2D, rt->mip_maps[i].color);
for (int l = 0; l < level + 1; l++) {
- glTexImage2D(GL_TEXTURE_2D, l, color_internal_format, width, height, 0, color_format, color_type, NULL);
+ glTexImage2D(GL_TEXTURE_2D, l, color_internal_format, width, height, 0, color_format, color_type, nullptr);
width = MAX(1, (width / 2));
height = MAX(1, (height / 2));
}
@@ -3247,7 +3192,7 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) {
for (int l = 0; l < level + 1; l++) {
glGenTextures(1, &rt->mip_maps[i].sizes.write[l].color);
glBindTexture(GL_TEXTURE_2D, rt->mip_maps[i].sizes[l].color);
- glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, width, height, 0, color_format, color_type, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, width, height, 0, color_format, color_type, nullptr);
width = MAX(1, (width / 2));
height = MAX(1, (height / 2));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -3316,8 +3261,9 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) {
void RasterizerStorageGLES3::_render_target_clear(RenderTarget *rt) {
// there is nothing to clear when DIRECT_TO_SCREEN is used
- if (rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN])
+ if (rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) {
return;
+ }
if (rt->fbo) {
glDeleteFramebuffers(1, &rt->fbo);
@@ -3434,8 +3380,9 @@ void RasterizerStorageGLES3::render_target_set_size(RID p_render_target, int p_w
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND(!rt);
- if (p_width == rt->width && p_height == rt->height)
+ if (p_width == rt->width && p_height == rt->height) {
return;
+ }
_render_target_clear(rt);
@@ -3622,8 +3569,9 @@ void RasterizerStorageGLES3::render_target_set_msaa(RID p_render_target, RS::Vie
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND(!rt);
- if (rt->msaa == p_msaa)
+ if (rt->msaa == p_msaa) {
return;
+ }
_render_target_clear(rt);
rt->msaa = p_msaa;
@@ -3699,8 +3647,9 @@ void RasterizerStorageGLES3::render_target_mark_sdf_enabled(RID p_render_target,
RID RasterizerStorageGLES3::canvas_light_shadow_buffer_create(int p_width) {
CanvasLightShadow *cls = memnew(CanvasLightShadow);
- if (p_width > config.max_texture_size)
+ if (p_width > config.max_texture_size) {
p_width = config.max_texture_size;
+ }
cls->size = p_width;
cls->height = 16;
@@ -3718,10 +3667,10 @@ RID RasterizerStorageGLES3::canvas_light_shadow_buffer_create(int p_width) {
glGenTextures(1, &cls->distance);
glBindTexture(GL_TEXTURE_2D, cls->distance);
if (config.use_rgba_2d_shadows) {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cls->size, cls->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cls->size, cls->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
} else {
#ifdef GLES_OVER_GL
- glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cls->size, cls->height, 0, _RED_OES, GL_FLOAT, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cls->size, cls->height, 0, _RED_OES, GL_FLOAT, nullptr);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_FLOAT, cls->size, cls->height, 0, _RED_OES, GL_FLOAT, NULL);
#endif
@@ -3917,7 +3866,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
while (shader->materials.first()) {
Material *m = shader->materials.first()->self();
- m->shader = NULL;
+ m->shader = nullptr;
_material_make_dirty(m);
shader->materials.remove(shader->materials.first());
@@ -4085,17 +4034,17 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
}
bool RasterizerStorageGLES3::has_os_feature(const String &p_feature) const {
- if (p_feature == "pvrtc")
- return config.pvrtc_supported;
-
- if (p_feature == "s3tc")
+ if (p_feature == "s3tc") {
return config.s3tc_supported;
+ }
- if (p_feature == "etc")
+ if (p_feature == "etc") {
return config.etc_supported;
+ }
- if (p_feature == "skinning_fallback")
+ if (p_feature == "skinning_fallback") {
return config.use_skeleton_software;
+ }
return false;
}
@@ -4220,7 +4169,6 @@ void RasterizerStorageGLES3::initialize() {
#ifdef GLES_OVER_GL
config.float_texture_supported = true;
config.s3tc_supported = true;
- config.pvrtc_supported = false;
config.etc_supported = false;
config.support_npot_repeat_mipmap = true;
config.depth_buffer_internalformat = GL_DEPTH_COMPONENT24;
@@ -4228,7 +4176,6 @@ void RasterizerStorageGLES3::initialize() {
config.float_texture_supported = config.extensions.has("GL_ARB_texture_float") || config.extensions.has("GL_OES_texture_float");
config.s3tc_supported = config.extensions.has("GL_EXT_texture_compression_s3tc") || config.extensions.has("WEBGL_compressed_texture_s3tc");
config.etc_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture") || config.extensions.has("WEBGL_compressed_texture_etc1");
- config.pvrtc_supported = config.extensions.has("GL_IMG_texture_compression_pvrtc") || config.extensions.has("WEBGL_compressed_texture_pvrtc");
config.support_npot_repeat_mipmap = config.extensions.has("GL_OES_texture_npot");
#ifdef JAVASCRIPT_ENABLED
@@ -4297,7 +4244,6 @@ void RasterizerStorageGLES3::initialize() {
config.etc_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture");
config.latc_supported = config.extensions.has("GL_EXT_texture_compression_latc");
config.bptc_supported = config.extensions.has("GL_ARB_texture_compression_bptc");
- config.pvrtc_supported = config.extensions.has("GL_IMG_texture_compression_pvrtc");
config.rgtc_supported = config.extensions.has("GL_EXT_texture_compression_rgtc") || config.extensions.has("GL_ARB_texture_compression_rgtc") || config.extensions.has("EXT_texture_compression_rgtc");
config.bptc_supported = config.extensions.has("GL_ARB_texture_compression_bptc") || config.extensions.has("EXT_texture_compression_bptc");
config.srgb_decode_supported = config.extensions.has("GL_EXT_texture_sRGB_decode");
@@ -4312,7 +4258,7 @@ void RasterizerStorageGLES3::initialize() {
GLuint depth;
glGenTextures(1, &depth);
glBindTexture(GL_TEXTURE_2D, depth);
- glTexImage2D(GL_TEXTURE_2D, 0, config.depth_internalformat, 32, 32, 0, GL_DEPTH_COMPONENT, config.depth_type, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, config.depth_internalformat, 32, 32, 0, GL_DEPTH_COMPONENT, config.depth_type, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -4344,7 +4290,7 @@ void RasterizerStorageGLES3::initialize() {
glGenTextures(1, &depth);
glBindTexture(GL_TEXTURE_2D, depth);
- glTexImage2D(GL_TEXTURE_2D, 0, config.depth_internalformat, 32, 32, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, config.depth_internalformat, 32, 32, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -4372,7 +4318,7 @@ void RasterizerStorageGLES3::initialize() {
frame.count = 0;
frame.delta = 0;
- frame.current_rt = NULL;
+ frame.current_rt = nullptr;
frame.clear_request = false;
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &config.max_vertex_texture_image_units);
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index c080d28f94..0dfc909777 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -79,7 +79,6 @@ public:
bool bptc_supported;
bool etc_supported;
bool etc2_supported;
- bool pvrtc_supported;
bool srgb_decode_supported;
bool keep_original_textures;
@@ -143,8 +142,8 @@ public:
} shaders;
struct Info {
- uint64_t texture_mem;
- uint64_t vertex_mem;
+ uint64_t texture_mem = 0;
+ uint64_t vertex_mem = 0;
struct Render {
uint32_t object_count;
@@ -168,9 +167,7 @@ public:
}
} render, render_final, snap;
- Info() :
- texture_mem(0),
- vertex_mem(0) {
+ Info() {
render.reset();
render_final.reset();
}
@@ -276,8 +273,9 @@ public:
// texture coords start from bottom left, means we need to draw render target textures upside down
// to be compatible with vulkan etc.
bool is_upside_down() const {
- if (proxy)
+ if (proxy) {
return proxy->is_upside_down();
+ }
return render_target != nullptr;
}
@@ -365,7 +363,7 @@ public:
images.clear();
for (Set<Texture *>::Element *E = proxy_owners.front(); E; E = E->next()) {
- E->get()->proxy = NULL;
+ E->get()->proxy = nullptr;
}
if (proxy) {
@@ -375,8 +373,9 @@ public:
// texture state
void GLSetFilter(GLenum p_target, RS::CanvasItemTextureFilter p_filter) {
- if (p_filter == state_filter)
+ if (p_filter == state_filter) {
return;
+ }
state_filter = p_filter;
GLint pmin = GL_LINEAR; // param min
GLint pmag = GL_LINEAR; // param mag
@@ -400,8 +399,9 @@ public:
glTexParameteri(p_target, GL_TEXTURE_MAG_FILTER, pmag);
}
void GLSetRepeat(GLenum p_target, RS::CanvasItemTextureRepeat p_repeat) {
- if (p_repeat == state_repeat)
+ if (p_repeat == state_repeat) {
return;
+ }
state_repeat = p_repeat;
GLint prep = GL_CLAMP_TO_EDGE; // parameter repeat
switch (state_repeat) {
@@ -638,7 +638,7 @@ public:
Shader() :
dirty_list(this) {
- shader = NULL;
+ shader = nullptr;
valid = false;
version = RID();
last_pass = 0;
@@ -698,7 +698,7 @@ public:
dirty_list(this) {
can_cast_shadow_cache = false;
is_animated_cache = false;
- shader = NULL;
+ shader = nullptr;
line_width = 1.0;
last_pass = 0;
render_priority = 0;
@@ -1152,27 +1152,23 @@ public:
struct RenderTarget {
RID self;
- GLuint fbo;
- GLuint color;
- GLuint depth;
+ GLuint fbo = 0;
+ GLuint color = 0;
+ GLuint depth = 0;
- GLuint multisample_fbo;
- GLuint multisample_color;
- GLuint multisample_depth;
- bool multisample_active;
+ GLuint multisample_fbo = 0;
+ GLuint multisample_color = 0;
+ GLuint multisample_depth = 0;
+ bool multisample_active = false;
struct Effect {
- GLuint fbo;
- int width;
- int height;
+ GLuint fbo = 0;
+ int width = 0;
+ int height = 0;
- GLuint color;
+ GLuint color = 0;
- Effect() :
- fbo(0),
- width(0),
- height(0),
- color(0) {
+ Effect() {
}
};
@@ -1187,71 +1183,47 @@ public:
};
Vector<Size> sizes;
- GLuint color;
- int levels;
+ GLuint color = 0;
+ int levels = 0;
- MipMaps() :
- color(0),
- levels(0) {
+ MipMaps() {
}
};
MipMaps mip_maps[2];
struct External {
- GLuint fbo;
- GLuint color;
- GLuint depth;
+ GLuint fbo = 0;
+ GLuint color = 0;
+ GLuint depth = 0;
RID texture;
- External() :
- fbo(0),
- color(0),
- depth(0) {
+ External() {
}
} external;
- int x, y, width, height;
+ int x = 0, y = 0, width = 0, height = 0;
bool flags[RENDER_TARGET_FLAG_MAX];
// instead of allocating sized render targets immediately,
// defer this for faster startup
bool allocate_is_dirty = false;
- bool used_in_frame;
- RS::ViewportMSAA msaa;
+ bool used_in_frame = false;
+ RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
- bool use_fxaa;
- bool use_debanding;
+ bool use_fxaa = false;
+ bool use_debanding = false;
RID texture;
- bool used_dof_blur_near;
- bool mip_maps_allocated;
-
- Color clear_color;
- bool clear_requested;
-
- RenderTarget() :
- fbo(0),
- color(0),
- depth(0),
- multisample_fbo(0),
- multisample_color(0),
- multisample_depth(0),
- multisample_active(false),
- x(0),
- y(0),
- width(0),
- height(0),
- used_in_frame(false),
- msaa(RS::VIEWPORT_MSAA_DISABLED),
- use_fxaa(false),
- use_debanding(false),
- used_dof_blur_near(false),
- mip_maps_allocated(false),
- clear_color(Color(1, 1, 1, 1)),
- clear_requested(false) {
+ bool used_dof_blur_near = false;
+ bool mip_maps_allocated = false;
+
+ Color clear_color = Color(1, 1, 1, 1);
+ bool clear_requested = false;
+
+ RenderTarget() {
for (int i = 0; i < RENDER_TARGET_FLAG_MAX; ++i) {
flags[i] = false;
}
@@ -1428,8 +1400,9 @@ inline bool RasterizerStorageGLES3::safe_buffer_sub_data(unsigned int p_total_bu
r_offset_after = p_offset + p_data_size;
#ifdef DEBUG_ENABLED
// we are trying to write across the edge of the buffer
- if (r_offset_after > p_total_buffer_size)
+ if (r_offset_after > p_total_buffer_size) {
return false;
+ }
#endif
glBufferSubData(p_target, p_offset, p_data_size, p_data);
return true;
@@ -1441,7 +1414,7 @@ inline void RasterizerStorageGLES3::buffer_orphan_and_upload(unsigned int p_buff
// Orphan the buffer to avoid CPU/GPU sync points caused by glBufferSubData
// Was previously #ifndef GLES_OVER_GL however this causes stalls on desktop mac also (and possibly other)
if (!p_optional_orphan || (config.should_orphan)) {
- glBufferData(p_target, p_buffer_size, NULL, p_usage);
+ glBufferData(p_target, p_buffer_size, nullptr, p_usage);
#ifdef RASTERIZER_EXTRA_CHECKS
// fill with garbage off the end of the array
if (p_buffer_size) {
diff --git a/drivers/gles3/texture_loader_gles3.cpp b/drivers/gles3/texture_loader_gles3.cpp
index ca52eaeacb..1cbda02121 100644
--- a/drivers/gles3/texture_loader_gles3.cpp
+++ b/drivers/gles3/texture_loader_gles3.cpp
@@ -61,8 +61,9 @@ RES ResourceFormatGLES2Texture::load(const String &p_path, const String &p_origi
Ref<ImageTexture> texture = memnew(ImageTexture);
texture->create_from_image(img);
- if (r_error)
+ if (r_error) {
*r_error = OK;
+ }
return texture;
}
@@ -78,7 +79,6 @@ void ResourceFormatGLES2Texture::get_recognized_extensions(List<String> *p_exten
p_extensions->push_back("png");
p_extensions->push_back("pvr");
p_extensions->push_back("svg");
- p_extensions->push_back("svgz");
p_extensions->push_back("tga");
p_extensions->push_back("webp");
}
@@ -100,7 +100,6 @@ String ResourceFormatGLES2Texture::get_resource_type(const String &p_path) const
extension == "png" ||
extension == "pvr" ||
extension == "svg" ||
- extension == "svgz" ||
extension == "tga" ||
extension == "webp") {
return "ImageTexture";
diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp
index 400dc25f11..d82dcb8a8d 100644
--- a/drivers/unix/ip_unix.cpp
+++ b/drivers/unix/ip_unix.cpp
@@ -115,7 +115,7 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
continue;
}
IPAddress ip = _sockaddr2ip(next->ai_addr);
- if (!r_addresses.find(ip)) {
+ if (ip.is_valid() && !r_addresses.find(ip)) {
r_addresses.push_back(ip);
}
next = next->ai_next;
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index a004bdbe3d..00f84eba8c 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -249,7 +249,7 @@ uint64_t OS_Unix::get_ticks_usec() const {
return longtime;
}
-Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
#ifdef __EMSCRIPTEN__
// Don't compile this code at all to avoid undefined references.
// Actual virtual call goes to OS_JavaScript.
@@ -318,7 +318,7 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, St
#endif
}
-Error OS_Unix::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
+Error OS_Unix::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
#ifdef __EMSCRIPTEN__
// Don't compile this code at all to avoid undefined references.
// Actual virtual call goes to OS_JavaScript.
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index 5e56091d54..3f0e8a171c 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -81,8 +81,8 @@ public:
virtual void delay_usec(uint32_t p_usec) const override;
virtual uint64_t get_ticks_usec() const override;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) override;
- virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 708ea4b265..ba623eb298 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -387,14 +387,6 @@ const VkFormat RenderingDeviceVulkan::vulkan_formats[RenderingDevice::DATA_FORMA
VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM,
VK_FORMAT_G16_B16R16_2PLANE_422_UNORM,
VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM,
- VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG,
- VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG,
- VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG,
- VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG,
- VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG,
- VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG,
- VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG,
- VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG,
};
const char *RenderingDeviceVulkan::named_formats[RenderingDevice::DATA_FORMAT_MAX] = {
@@ -616,14 +608,6 @@ const char *RenderingDeviceVulkan::named_formats[RenderingDevice::DATA_FORMAT_MA
"G16_B16_R16_3Plane_422_Unorm",
"G16_B16R16_2Plane_422_Unorm",
"G16_B16_R16_3Plane_444_Unorm",
- "Pvrtc1_2Bpp_Unorm_Block_Img",
- "Pvrtc1_4Bpp_Unorm_Block_Img",
- "Pvrtc2_2Bpp_Unorm_Block_Img",
- "Pvrtc2_4Bpp_Unorm_Block_Img",
- "Pvrtc1_2Bpp_Srgb_Block_Img",
- "Pvrtc1_4Bpp_Srgb_Block_Img",
- "Pvrtc2_2Bpp_Srgb_Block_Img",
- "Pvrtc2_4Bpp_Srgb_Block_Img"
};
int RenderingDeviceVulkan::get_format_vertex_size(DataFormat p_format) {
@@ -970,15 +954,6 @@ uint32_t RenderingDeviceVulkan::get_image_format_pixel_size(DataFormat p_format)
case DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM:
case DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
return 8;
- case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
- return 1;
default: {
ERR_PRINT("Format not handled, bug");
}
@@ -1048,20 +1023,6 @@ void RenderingDeviceVulkan::get_compressed_image_format_block_dimensions(DataFor
r_w = 4;
r_h = 4;
return;
- case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
- r_w = 4;
- r_h = 4;
- return;
- case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
- r_w = 8;
- r_h = 4;
- return;
default: {
r_w = 1;
r_h = 1;
@@ -1138,15 +1099,6 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(Data
case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK:
case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK:
return 8; //wrong
- case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
- return 8; //what varies is resolution
default: {
}
}
@@ -1167,16 +1119,7 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_pixel_rshift(DataFor
case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
case DATA_FORMAT_EAC_R11_UNORM_BLOCK:
case DATA_FORMAT_EAC_R11_SNORM_BLOCK:
- case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
return 1;
- case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: //these formats are quarter byte size, so rshift is 1
- case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
- return 2;
default: {
}
}
@@ -7575,6 +7518,9 @@ Error RenderingDeviceVulkan::draw_list_switch_to_next_pass_split(uint32_t p_spli
}
Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) {
+ // Lock while draw_list is active
+ _THREAD_SAFE_LOCK_
+
if (p_splits == 0) {
draw_list = memnew(DrawList);
draw_list->command_buffer = frames[frame].draw_command_buffer;
@@ -7685,6 +7631,9 @@ void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) {
memdelete(draw_list);
draw_list = nullptr;
}
+
+ // draw_list is no longer active
+ _THREAD_SAFE_UNLOCK_
}
void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) {
@@ -7798,6 +7747,9 @@ RenderingDevice::ComputeListID RenderingDeviceVulkan::compute_list_begin(bool p_
ERR_FAIL_COND_V_MSG(!p_allow_draw_overlap && draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time.");
ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time.");
+ // Lock while compute_list is active
+ _THREAD_SAFE_LOCK_
+
compute_list = memnew(ComputeList);
compute_list->command_buffer = frames[frame].draw_command_buffer;
compute_list->state.allow_draw_overlap = p_allow_draw_overlap;
@@ -8271,6 +8223,9 @@ void RenderingDeviceVulkan::compute_list_end(uint32_t p_post_barrier) {
memdelete(compute_list);
compute_list = nullptr;
+
+ // compute_list is no longer active
+ _THREAD_SAFE_UNLOCK_
}
void RenderingDeviceVulkan::barrier(uint32_t p_from, uint32_t p_to) {
@@ -8429,11 +8384,11 @@ void RenderingDeviceVulkan::_free_internal(RID p_id) {
} else if (uniform_set_owner.owns(p_id)) {
UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id);
frames[frame].uniform_sets_to_dispose_of.push_back(*uniform_set);
+ uniform_set_owner.free(p_id);
+
if (uniform_set->invalidated_callback != nullptr) {
- uniform_set->invalidated_callback(p_id, uniform_set->invalidated_callback_userdata);
+ uniform_set->invalidated_callback(uniform_set->invalidated_callback_userdata);
}
-
- uniform_set_owner.free(p_id);
} else if (render_pipeline_owner.owns(p_id)) {
RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id);
frames[frame].render_pipelines_to_dispose_of.push_back(*pipeline);
@@ -8986,7 +8941,7 @@ void RenderingDeviceVulkan::_free_rids(T &p_owner, const char *p_type) {
}
void RenderingDeviceVulkan::capture_timestamp(const String &p_name) {
- ERR_FAIL_COND_MSG(draw_list != nullptr, "Capturing timestamps during draw list creation is not allowed. Offending timestap was: " + p_name);
+ ERR_FAIL_COND_MSG(draw_list != nullptr, "Capturing timestamps during draw list creation is not allowed. Offending timestamp was: " + p_name);
ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements);
//this should be optional for profiling, else it will slow things down
diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h
index 408fddf4bf..f42929ffa4 100644
--- a/drivers/vulkan/rendering_device_vulkan.h
+++ b/drivers/vulkan/rendering_device_vulkan.h
@@ -175,7 +175,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
// These are temporary buffers on CPU memory that hold
// the information until the CPU fetches it and places it
// either on GPU buffers, or images (textures). It ensures
- // updates are properly synchronized with whathever the
+ // updates are properly synchronized with whatever the
// GPU is doing.
//
// The logic here is as follows, only 3 of these
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index 75a3ab26ea..44455b927b 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -538,7 +538,7 @@ Error VulkanContext::_check_capabilities() {
VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features;
shader_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
- shader_features.pNext = NULL;
+ shader_features.pNext = nullptr;
device_features.pNext = &shader_features;
@@ -547,7 +547,7 @@ Error VulkanContext::_check_capabilities() {
VkPhysicalDevice16BitStorageFeaturesKHR storage_feature;
storage_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR;
- storage_feature.pNext = NULL;
+ storage_feature.pNext = nullptr;
device_features.pNext = &storage_feature;
@@ -615,7 +615,7 @@ Error VulkanContext::_check_capabilities() {
return OK;
}
-Error VulkanContext::_create_physical_device() {
+Error VulkanContext::_create_instance() {
/* obtain version */
_obtain_vulkan_version();
@@ -678,8 +678,6 @@ Error VulkanContext::_create_physical_device() {
inst_info.pNext = &dbg_report_callback_create_info;
}
- uint32_t gpu_count;
-
VkResult err = vkCreateInstance(&inst_info, nullptr, &inst);
ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE,
"Cannot find a compatible Vulkan installable client driver (ICD).\n\n"
@@ -700,10 +698,85 @@ Error VulkanContext::_create_physical_device() {
volkLoadInstance(inst);
#endif
+ if (enabled_debug_utils) {
+ // Setup VK_EXT_debug_utils function pointers always (we use them for
+ // debug labels and names).
+ CreateDebugUtilsMessengerEXT =
+ (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT");
+ DestroyDebugUtilsMessengerEXT =
+ (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugUtilsMessengerEXT");
+ SubmitDebugUtilsMessageEXT =
+ (PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(inst, "vkSubmitDebugUtilsMessageEXT");
+ CmdBeginDebugUtilsLabelEXT =
+ (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdBeginDebugUtilsLabelEXT");
+ CmdEndDebugUtilsLabelEXT =
+ (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdEndDebugUtilsLabelEXT");
+ CmdInsertDebugUtilsLabelEXT =
+ (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdInsertDebugUtilsLabelEXT");
+ SetDebugUtilsObjectNameEXT =
+ (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(inst, "vkSetDebugUtilsObjectNameEXT");
+ if (nullptr == CreateDebugUtilsMessengerEXT || nullptr == DestroyDebugUtilsMessengerEXT ||
+ nullptr == SubmitDebugUtilsMessageEXT || nullptr == CmdBeginDebugUtilsLabelEXT ||
+ nullptr == CmdEndDebugUtilsLabelEXT || nullptr == CmdInsertDebugUtilsLabelEXT ||
+ nullptr == SetDebugUtilsObjectNameEXT) {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "GetProcAddr: Failed to init VK_EXT_debug_utils\n"
+ "GetProcAddr: Failure");
+ }
+
+ err = CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_create_info, nullptr, &dbg_messenger);
+ switch (err) {
+ case VK_SUCCESS:
+ break;
+ case VK_ERROR_OUT_OF_HOST_MEMORY:
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "CreateDebugUtilsMessengerEXT: out of host memory\n"
+ "CreateDebugUtilsMessengerEXT Failure");
+ break;
+ default:
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "CreateDebugUtilsMessengerEXT: unknown failure\n"
+ "CreateDebugUtilsMessengerEXT Failure");
+ ERR_FAIL_V(ERR_CANT_CREATE);
+ break;
+ }
+ } else if (enabled_debug_report) {
+ CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT");
+ DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT");
+ DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT");
+
+ if (nullptr == CreateDebugReportCallbackEXT || nullptr == DebugReportMessageEXT || nullptr == DestroyDebugReportCallbackEXT) {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "GetProcAddr: Failed to init VK_EXT_debug_report\n"
+ "GetProcAddr: Failure");
+ }
+
+ err = CreateDebugReportCallbackEXT(inst, &dbg_report_callback_create_info, nullptr, &dbg_debug_report);
+ switch (err) {
+ case VK_SUCCESS:
+ break;
+ case VK_ERROR_OUT_OF_HOST_MEMORY:
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "CreateDebugReportCallbackEXT: out of host memory\n"
+ "CreateDebugReportCallbackEXT Failure");
+ break;
+ default:
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "CreateDebugReportCallbackEXT: unknown failure\n"
+ "CreateDebugReportCallbackEXT Failure");
+ ERR_FAIL_V(ERR_CANT_CREATE);
+ break;
+ }
+ }
+
+ return OK;
+}
+
+Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) {
/* Make initial call to query gpu_count, then second call for gpu info*/
- err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr);
+ uint32_t gpu_count = 0;
+ VkResult err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
-
ERR_FAIL_COND_V_MSG(gpu_count == 0, ERR_CANT_CREATE,
"vkEnumeratePhysicalDevices reported zero accessible devices.\n\n"
"Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
@@ -716,24 +789,120 @@ Error VulkanContext::_create_physical_device() {
ERR_FAIL_V(ERR_CANT_CREATE);
}
+ static const struct {
+ uint32_t id;
+ const char *name;
+ } vendor_names[] = {
+ { 0x1002, "AMD" },
+ { 0x1010, "ImgTec" },
+ { 0x106B, "Apple" },
+ { 0x10DE, "NVIDIA" },
+ { 0x13B5, "ARM" },
+ { 0x5143, "Qualcomm" },
+ { 0x8086, "Intel" },
+ { 0, nullptr },
+ };
+
// TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances.
// The device should really be a preference, but for now choosing a discrete GPU over the
// integrated one is better than the default.
- // Default to first device
- uint32_t device_index = 0;
-
+ int32_t device_index = -1;
+ int type_selected = -1;
+ print_verbose("Vulkan devices:");
for (uint32_t i = 0; i < gpu_count; ++i) {
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(physical_devices[i], &props);
- if (props.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
- // Prefer discrete GPU.
- device_index = i;
- break;
+ bool present_supported = false;
+
+ uint32_t device_queue_family_count = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, nullptr);
+ VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties));
+ vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props);
+ for (uint32_t j = 0; j < device_queue_family_count; j++) {
+ VkBool32 supports;
+ vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, p_surface, &supports);
+ if (supports && ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) {
+ present_supported = true;
+ } else {
+ continue;
+ }
+ }
+ String name = props.deviceName;
+ String vendor = "Unknown";
+ String dev_type;
+ switch (props.deviceType) {
+ case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: {
+ dev_type = "Discrete";
+ } break;
+ case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: {
+ dev_type = "Integrated";
+ } break;
+ case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: {
+ dev_type = "Virtual";
+ } break;
+ case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: {
+ dev_type = "CPU";
+ } break;
+ default: {
+ dev_type = "Other";
+ } break;
+ }
+ uint32_t vendor_idx = 0;
+ while (vendor_names[vendor_idx].name != nullptr) {
+ if (props.vendorID == vendor_names[vendor_idx].id) {
+ vendor = vendor_names[vendor_idx].name;
+ break;
+ }
+ vendor_idx++;
+ }
+ free(device_queue_props);
+ print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + dev_type);
+
+ if (present_supported) { // Select first supported device of preffered type: Discrete > Integrated > Virtual > CPU > Other.
+ switch (props.deviceType) {
+ case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: {
+ if (type_selected < 4) {
+ type_selected = 4;
+ device_index = i;
+ }
+ } break;
+ case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: {
+ if (type_selected < 3) {
+ type_selected = 3;
+ device_index = i;
+ }
+ } break;
+ case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: {
+ if (type_selected < 2) {
+ type_selected = 2;
+ device_index = i;
+ }
+ } break;
+ case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: {
+ if (type_selected < 1) {
+ type_selected = 1;
+ device_index = i;
+ }
+ } break;
+ default: {
+ if (type_selected < 0) {
+ type_selected = 0;
+ device_index = i;
+ }
+ } break;
+ }
}
}
+ int32_t user_device_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU.
+ if (user_device_index >= 0 && user_device_index < (int32_t)gpu_count) {
+ device_index = user_device_index;
+ }
+
+ ERR_FAIL_COND_V_MSG(device_index == -1, ERR_CANT_CREATE, "None of Vulkan devices supports both graphics and present queues.");
+
gpu = physical_devices[device_index];
free(physical_devices);
@@ -746,19 +915,6 @@ Error VulkanContext::_create_physical_device() {
/* Get identifier properties */
vkGetPhysicalDeviceProperties(gpu, &gpu_props);
- static const struct {
- uint32_t id;
- const char *name;
- } vendor_names[] = {
- { 0x1002, "AMD" },
- { 0x1010, "ImgTec" },
- { 0x106B, "Apple" },
- { 0x10DE, "NVIDIA" },
- { 0x13B5, "ARM" },
- { 0x5143, "Qualcomm" },
- { 0x8086, "Intel" },
- { 0, nullptr },
- };
device_name = gpu_props.deviceName;
device_type = gpu_props.deviceType;
pipeline_cache_id = String::hex_encode_buffer(gpu_props.pipelineCacheUUID, VK_UUID_SIZE);
@@ -851,77 +1007,6 @@ Error VulkanContext::_create_physical_device() {
" extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\n"
"vkCreateInstance Failure");
- if (enabled_debug_utils) {
- // Setup VK_EXT_debug_utils function pointers always (we use them for
- // debug labels and names).
- CreateDebugUtilsMessengerEXT =
- (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT");
- DestroyDebugUtilsMessengerEXT =
- (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugUtilsMessengerEXT");
- SubmitDebugUtilsMessageEXT =
- (PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(inst, "vkSubmitDebugUtilsMessageEXT");
- CmdBeginDebugUtilsLabelEXT =
- (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdBeginDebugUtilsLabelEXT");
- CmdEndDebugUtilsLabelEXT =
- (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdEndDebugUtilsLabelEXT");
- CmdInsertDebugUtilsLabelEXT =
- (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdInsertDebugUtilsLabelEXT");
- SetDebugUtilsObjectNameEXT =
- (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(inst, "vkSetDebugUtilsObjectNameEXT");
- if (nullptr == CreateDebugUtilsMessengerEXT || nullptr == DestroyDebugUtilsMessengerEXT ||
- nullptr == SubmitDebugUtilsMessageEXT || nullptr == CmdBeginDebugUtilsLabelEXT ||
- nullptr == CmdEndDebugUtilsLabelEXT || nullptr == CmdInsertDebugUtilsLabelEXT ||
- nullptr == SetDebugUtilsObjectNameEXT) {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE,
- "GetProcAddr: Failed to init VK_EXT_debug_utils\n"
- "GetProcAddr: Failure");
- }
-
- err = CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_create_info, nullptr, &dbg_messenger);
- switch (err) {
- case VK_SUCCESS:
- break;
- case VK_ERROR_OUT_OF_HOST_MEMORY:
- ERR_FAIL_V_MSG(ERR_CANT_CREATE,
- "CreateDebugUtilsMessengerEXT: out of host memory\n"
- "CreateDebugUtilsMessengerEXT Failure");
- break;
- default:
- ERR_FAIL_V_MSG(ERR_CANT_CREATE,
- "CreateDebugUtilsMessengerEXT: unknown failure\n"
- "CreateDebugUtilsMessengerEXT Failure");
- ERR_FAIL_V(ERR_CANT_CREATE);
- break;
- }
- } else if (enabled_debug_report) {
- CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT");
- DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT");
- DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT");
-
- if (nullptr == CreateDebugReportCallbackEXT || nullptr == DebugReportMessageEXT || nullptr == DestroyDebugReportCallbackEXT) {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE,
- "GetProcAddr: Failed to init VK_EXT_debug_report\n"
- "GetProcAddr: Failure");
- }
-
- err = CreateDebugReportCallbackEXT(inst, &dbg_report_callback_create_info, nullptr, &dbg_debug_report);
- switch (err) {
- case VK_SUCCESS:
- break;
- case VK_ERROR_OUT_OF_HOST_MEMORY:
- ERR_FAIL_V_MSG(ERR_CANT_CREATE,
- "CreateDebugReportCallbackEXT: out of host memory\n"
- "CreateDebugReportCallbackEXT Failure");
- break;
- default:
- ERR_FAIL_V_MSG(ERR_CANT_CREATE,
- "CreateDebugReportCallbackEXT: unknown failure\n"
- "CreateDebugReportCallbackEXT Failure");
- ERR_FAIL_V(ERR_CANT_CREATE);
- break;
- }
- }
-
/* Call with nullptr data to get count */
vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, nullptr);
ERR_FAIL_COND_V(queue_family_count == 0, ERR_CANT_CREATE);
@@ -957,6 +1042,7 @@ Error VulkanContext::_create_physical_device() {
}
}
+ device_initialized = true;
return OK;
}
@@ -1209,25 +1295,17 @@ bool VulkanContext::_use_validation_layers() {
Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height) {
ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER);
+ if (!device_initialized) {
+ Error err = _create_physical_device(p_surface);
+ ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
+ }
+
if (!queues_initialized) {
// We use a single GPU, but we need a surface to initialize the
// queues, so this process must be deferred until a surface
// is created.
Error err = _initialize_queues(p_surface);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
- } else {
- // make sure any of the surfaces supports present (validation layer complains if this is not done).
- bool any_supports_present = false;
- for (uint32_t i = 0; i < queue_family_count; i++) {
- VkBool32 supports;
- fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supports);
- if (supports) {
- any_supports_present = true;
- break;
- }
- }
-
- ERR_FAIL_COND_V_MSG(!any_supports_present, ERR_CANT_CREATE, "Surface passed for sub-window creation does not support presenting");
}
Window window;
@@ -1694,12 +1772,12 @@ Error VulkanContext::initialize() {
return FAILED;
}
#endif
- Error err = _create_physical_device();
- if (err) {
+
+ Error err = _create_instance();
+ if (err != OK) {
return err;
}
- device_initialized = true;
return OK;
}
diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h
index 5cac7e7771..67a675f6c6 100644
--- a/drivers/vulkan/vulkan_context.h
+++ b/drivers/vulkan/vulkan_context.h
@@ -224,7 +224,9 @@ private:
const char *pMessage,
void *pUserData);
- Error _create_physical_device();
+ Error _create_instance();
+
+ Error _create_physical_device(VkSurfaceKHR p_surface);
Error _initialize_queues(VkSurfaceKHR p_surface);
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index 6cd21b5615..6f3bad12c1 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -165,8 +165,11 @@ Error DirAccessWindows::make_dir(String p_dir) {
bool success;
int err;
- p_dir = "\\\\?\\" + p_dir; //done according to
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
+ if (!p_dir.is_network_share_path()) {
+ p_dir = "\\\\?\\" + p_dir;
+ // Add "\\?\" to the path to extend max. path length past 248, if it's not a network share UNC path.
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
+ }
success = CreateDirectoryW((LPCWSTR)(p_dir.utf16().get_data()), nullptr);
err = GetLastError();
@@ -349,6 +352,10 @@ String DirAccessWindows::get_filesystem_type() const {
ERR_FAIL_COND_V(unit_end == -1, String());
String unit = path.substr(0, unit_end + 1) + "\\";
+ if (path.is_network_share_path()) {
+ return "Network Share";
+ }
+
WCHAR szVolumeName[100];
WCHAR szFileSystemName[10];
DWORD dwSerialNumber = 0;
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index 1085d34c4e..da376c588e 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -32,6 +32,7 @@
#include "editor/editor_node.h"
#include "editor_scale.h"
+#include "scene/gui/view_panner.h"
#include "scene/resources/text_line.h"
float AnimationBezierTrackEdit::_bezier_h_to_pixel(float p_h) {
@@ -216,6 +217,9 @@ void AnimationBezierTrackEdit::_draw_line_clipped(const Vector2 &p_from, const V
}
void AnimationBezierTrackEdit::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+ panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
+ }
if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
close_button->set_icon(get_theme_icon(SNAME("Close"), SNAME("EditorIcons")));
@@ -610,6 +614,11 @@ void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int
void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
+ if (panner->gui_input(p_event)) {
+ accept_event();
+ return;
+ }
+
if (p_event->is_pressed()) {
if (ED_GET_SHORTCUT("animation_editor/duplicate_selection")->matches_event(p_event)) {
duplicate_selection();
@@ -623,43 +632,6 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) {
- const float v_zoom_orig = v_zoom;
- if (mb->is_command_pressed()) {
- timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05);
- } else {
- if (v_zoom < 100000) {
- v_zoom *= 1.2;
- }
- }
- v_scroll = v_scroll + (mb->get_position().y - get_size().y / 2) * (v_zoom - v_zoom_orig);
- update();
- }
-
- if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
- const float v_zoom_orig = v_zoom;
- if (mb->is_command_pressed()) {
- timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05);
- } else {
- if (v_zoom > 0.000001) {
- v_zoom /= 1.2;
- }
- }
- v_scroll = v_scroll + (mb->get_position().y - get_size().y / 2) * (v_zoom - v_zoom_orig);
- update();
- }
-
- if (mb.is_valid() && mb->get_button_index() == MouseButton::MIDDLE) {
- if (mb->is_pressed()) {
- int x = mb->get_position().x - timeline->get_name_limit();
- panning_timeline_from = x / timeline->get_zoom_scale();
- panning_timeline = true;
- panning_timeline_at = timeline->get_value();
- } else {
- panning_timeline = false;
- }
- }
-
if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
menu_insert_key = mb->get_position();
if (menu_insert_key.x >= timeline->get_name_limit() && menu_insert_key.x <= get_size().width - timeline->get_buttons_width()) {
@@ -934,22 +906,6 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE) {
- v_scroll += mm->get_relative().y * v_zoom;
- if (v_scroll > 100000) {
- v_scroll = 100000;
- }
- if (v_scroll < -100000) {
- v_scroll = -100000;
- }
-
- int x = mm->get_position().x - timeline->get_name_limit();
- float ofs = x / timeline->get_zoom_scale();
- float diff = ofs - panning_timeline_from;
- timeline->set_value(panning_timeline_at - diff);
-
- update();
- }
if (moving_selection_attempt && mm.is_valid()) {
if (!moving_selection) {
moving_selection = true;
@@ -1038,6 +994,37 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
+void AnimationBezierTrackEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) {
+ _pan_callback(-p_scroll_vec * 32);
+}
+
+void AnimationBezierTrackEdit::_pan_callback(Vector2 p_scroll_vec) {
+ v_scroll += p_scroll_vec.y * v_zoom;
+ v_scroll = CLAMP(v_scroll, -100000, 100000);
+ timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale());
+ update();
+}
+
+void AnimationBezierTrackEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) {
+ const float v_zoom_orig = v_zoom;
+ if (p_alt) {
+ // Alternate zoom (doesn't affect timeline).
+ if (p_scroll_vec.y > 0) {
+ v_zoom = MIN(v_zoom * 1.2, 100000);
+ } else {
+ v_zoom = MAX(v_zoom / 1.2, 0.000001);
+ }
+ } else {
+ if (p_scroll_vec.y > 0) {
+ timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05);
+ } else {
+ timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05);
+ }
+ }
+ v_scroll = v_scroll + (p_origin.y - get_size().y / 2) * (v_zoom - v_zoom_orig);
+ update();
+}
+
void AnimationBezierTrackEdit::_menu_selected(int p_index) {
switch (p_index) {
case MENU_KEY_INSERT: {
@@ -1171,6 +1158,9 @@ void AnimationBezierTrackEdit::_bind_methods() {
}
AnimationBezierTrackEdit::AnimationBezierTrackEdit() {
+ panner.instantiate();
+ panner->set_callbacks(callable_mp(this, &AnimationBezierTrackEdit::_scroll_callback), callable_mp(this, &AnimationBezierTrackEdit::_pan_callback), callable_mp(this, &AnimationBezierTrackEdit::_zoom_callback));
+
play_position = memnew(Control);
play_position->set_mouse_filter(MOUSE_FILTER_PASS);
add_child(play_position);
diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h
index d9bc85a258..cf719a0355 100644
--- a/editor/animation_bezier_editor.h
+++ b/editor/animation_bezier_editor.h
@@ -33,6 +33,8 @@
#include "animation_track_editor.h"
+class ViewPanner;
+
class AnimationBezierTrackEdit : public Control {
GDCLASS(AnimationBezierTrackEdit, Control);
@@ -123,9 +125,10 @@ class AnimationBezierTrackEdit : public Control {
Set<int> selection;
- bool panning_timeline = false;
- float panning_timeline_from;
- float panning_timeline_at;
+ Ref<ViewPanner> panner;
+ void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
+ void _pan_callback(Vector2 p_scroll_vec);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt);
void _draw_line_clipped(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, int p_clip_left, int p_clip_right);
void _draw_track(int p_track, const Color &p_color);
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index f4e719f552..e36d0b846b 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -32,12 +32,12 @@
#include "animation_track_editor_plugins.h"
#include "core/input/input.h"
-#include "core/os/keyboard.h"
#include "editor/animation_bezier_editor.h"
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor_node.h"
#include "editor_scale.h"
#include "scene/animation/animation_player.h"
+#include "scene/gui/view_panner.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
@@ -1458,6 +1458,10 @@ int AnimationTimelineEdit::get_name_limit() const {
}
void AnimationTimelineEdit::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+ panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
+ }
+
if (p_what == NOTIFICATION_ENTER_TREE) {
add_track->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons")));
@@ -1760,17 +1764,12 @@ void AnimationTimelineEdit::_play_position_draw() {
void AnimationTimelineEdit::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- const Ref<InputEventMouseButton> mb = p_event;
-
- if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
- get_zoom()->set_value(get_zoom()->get_value() * 1.05);
+ if (panner->gui_input(p_event)) {
accept_event();
+ return;
}
- if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) {
- get_zoom()->set_value(get_zoom()->get_value() / 1.05);
- accept_event();
- }
+ const Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->is_pressed() && mb->is_alt_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
if (track_edit) {
@@ -1796,29 +1795,19 @@ void AnimationTimelineEdit::gui_input(const Ref<InputEvent> &p_event) {
dragging_hsize = false;
}
if (mb.is_valid() && mb->get_position().x > get_name_limit() && mb->get_position().x < (get_size().width - get_buttons_width())) {
- if (!panning_timeline && mb->get_button_index() == MouseButton::LEFT) {
+ if (!panner->is_panning() && mb->get_button_index() == MouseButton::LEFT) {
int x = mb->get_position().x - get_name_limit();
float ofs = x / get_zoom_scale() + get_value();
- emit_signal(SNAME("timeline_changed"), ofs, false, Input::get_singleton()->is_key_pressed(Key::ALT));
+ emit_signal(SNAME("timeline_changed"), ofs, false, mb->is_alt_pressed());
dragging_timeline = true;
}
- if (!dragging_timeline && mb->get_button_index() == MouseButton::MIDDLE) {
- int x = mb->get_position().x - get_name_limit();
- panning_timeline_from = x / get_zoom_scale();
- panning_timeline = true;
- panning_timeline_at = get_value();
- }
}
if (dragging_timeline && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
dragging_timeline = false;
}
- if (panning_timeline && mb.is_valid() && mb->get_button_index() == MouseButton::MIDDLE && !mb->is_pressed()) {
- panning_timeline = false;
- }
-
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
@@ -1839,17 +1828,29 @@ void AnimationTimelineEdit::gui_input(const Ref<InputEvent> &p_event) {
if (dragging_timeline) {
int x = mm->get_position().x - get_name_limit();
float ofs = x / get_zoom_scale() + get_value();
- emit_signal(SNAME("timeline_changed"), ofs, false, Input::get_singleton()->is_key_pressed(Key::ALT));
- }
- if (panning_timeline) {
- int x = mm->get_position().x - get_name_limit();
- float ofs = x / get_zoom_scale();
- float diff = ofs - panning_timeline_from;
- set_value(panning_timeline_at - diff);
+ emit_signal(SNAME("timeline_changed"), ofs, false, mm->is_alt_pressed());
}
}
}
+void AnimationTimelineEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) {
+ // Timeline has no vertical scroll, so we change it to horizontal.
+ p_scroll_vec.x += p_scroll_vec.y;
+ _pan_callback(-p_scroll_vec * 32);
+}
+
+void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec) {
+ set_value(get_value() - p_scroll_vec.x / get_zoom_scale());
+}
+
+void AnimationTimelineEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) {
+ if (p_scroll_vec.y < 0) {
+ get_zoom()->set_value(get_zoom()->get_value() * 1.05);
+ } else {
+ get_zoom()->set_value(get_zoom()->get_value() / 1.05);
+ }
+}
+
void AnimationTimelineEdit::set_use_fps(bool p_use_fps) {
use_fps = p_use_fps;
update_values();
@@ -1871,7 +1872,7 @@ void AnimationTimelineEdit::_track_added(int p_track) {
void AnimationTimelineEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("zoom_changed"));
ADD_SIGNAL(MethodInfo("name_limit_changed"));
- ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag")));
+ ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag"), PropertyInfo(Variant::BOOL, "timeline_only")));
ADD_SIGNAL(MethodInfo("track_added", PropertyInfo(Variant::INT, "track")));
ADD_SIGNAL(MethodInfo("length_changed", PropertyInfo(Variant::FLOAT, "size")));
}
@@ -1926,10 +1927,12 @@ AnimationTimelineEdit::AnimationTimelineEdit() {
add_track->get_popup()->connect("index_pressed", callable_mp(this, &AnimationTimelineEdit::_track_added));
len_hb->hide();
- panning_timeline = false;
dragging_timeline = false;
dragging_hsize = false;
+ panner.instantiate();
+ panner->set_callbacks(callable_mp(this, &AnimationTimelineEdit::_scroll_callback), callable_mp(this, &AnimationTimelineEdit::_pan_callback), callable_mp(this, &AnimationTimelineEdit::_zoom_callback));
+
set_layout_direction(Control::LAYOUT_DIRECTION_LTR);
}
@@ -3118,7 +3121,7 @@ void AnimationTrackEdit::append_to_selection(const Rect2 &p_box, bool p_deselect
}
void AnimationTrackEdit::_bind_methods() {
- ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag")));
+ ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag"), PropertyInfo(Variant::BOOL, "timeline_only")));
ADD_SIGNAL(MethodInfo("remove_request", PropertyInfo(Variant::INT, "track")));
ADD_SIGNAL(MethodInfo("dropped", PropertyInfo(Variant::INT, "from_track"), PropertyInfo(Variant::INT, "to_track")));
ADD_SIGNAL(MethodInfo("insert_key", PropertyInfo(Variant::FLOAT, "ofs")));
@@ -3286,7 +3289,7 @@ AnimationTrackEditGroup::AnimationTrackEditGroup() {
//////////////////////////////////////
void AnimationTrackEditor::add_track_edit_plugin(const Ref<AnimationTrackEditPlugin> &p_plugin) {
- if (track_edit_plugins.find(p_plugin) != -1) {
+ if (track_edit_plugins.has(p_plugin)) {
return;
}
track_edit_plugins.push_back(p_plugin);
@@ -3370,7 +3373,13 @@ Node *AnimationTrackEditor::get_root() const {
}
void AnimationTrackEditor::update_keying() {
- bool keying_enabled = is_visible_in_tree() && animation.is_valid();
+ bool keying_enabled = false;
+
+ EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
+ if (is_visible_in_tree() && animation.is_valid() && editor_history->get_path_size() > 0) {
+ Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0));
+ keying_enabled = Object::cast_to<Node>(obj) != nullptr;
+ }
if (keying_enabled == keying) {
return;
@@ -3472,7 +3481,7 @@ void AnimationTrackEditor::_track_remove_request(int p_track) {
void AnimationTrackEditor::_track_grab_focus(int p_track) {
// Don't steal focus if not working with the track editor.
- if (Object::cast_to<AnimationTrackEdit>(get_focus_owner())) {
+ if (Object::cast_to<AnimationTrackEdit>(get_viewport()->gui_get_focus_owner())) {
track_edits[p_track]->grab_focus();
}
}
@@ -3639,7 +3648,7 @@ void AnimationTrackEditor::_insert_track(bool p_create_reset, bool p_create_bezi
pos = animation->get_length();
}
set_anim_pos(pos);
- emit_signal(SNAME("timeline_changed"), pos, true);
+ emit_signal(SNAME("timeline_changed"), pos, true, false);
}
}
@@ -4500,6 +4509,10 @@ MenuButton *AnimationTrackEditor::get_edit_menu() {
}
void AnimationTrackEditor::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+ panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
+ }
+
if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
zoom_icon->set_texture(get_theme_icon(SNAME("Zoom"), SNAME("EditorIcons")));
snap->set_icon(get_theme_icon(SNAME("Snap"), SNAME("EditorIcons")));
@@ -4516,8 +4529,6 @@ void AnimationTrackEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
update_keying();
- EditorNode::get_singleton()->update_keying();
- emit_signal(SNAME("keying_changed"));
}
}
@@ -4999,7 +5010,7 @@ struct _AnimMoveRestore {
void AnimationTrackEditor::_clear_key_edit() {
if (key_edit) {
// If key edit is the object being inspected, remove it first.
- if (EditorNode::get_singleton()->get_inspector()->get_edited_object() == key_edit) {
+ if (InspectorDock::get_inspector_singleton()->get_edited_object() == key_edit) {
EditorNode::get_singleton()->push_item(nullptr);
}
@@ -5009,7 +5020,7 @@ void AnimationTrackEditor::_clear_key_edit() {
}
if (multi_key_edit) {
- if (EditorNode::get_singleton()->get_inspector()->get_edited_object() == multi_key_edit) {
+ if (InspectorDock::get_inspector_singleton()->get_edited_object() == multi_key_edit) {
EditorNode::get_singleton()->push_item(nullptr);
}
@@ -5212,27 +5223,12 @@ void AnimationTrackEditor::_box_selection_draw() {
}
void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) {
- Ref<InputEventMouseButton> mb = p_event;
-
- if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
- timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05);
- scroll->accept_event();
- }
-
- if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) {
- timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05);
- scroll->accept_event();
- }
-
- if (mb.is_valid() && mb->is_pressed() && mb->is_alt_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
- goto_prev_step(true);
+ if (panner->gui_input(p_event)) {
scroll->accept_event();
+ return;
}
- if (mb.is_valid() && mb->is_pressed() && mb->is_alt_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) {
- goto_next_step(true);
- scroll->accept_event();
- }
+ Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
@@ -5262,10 +5258,6 @@ void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE) {
- timeline->set_value(timeline->get_value() - mm->get_relative().x / timeline->get_zoom_scale());
- }
-
if (mm.is_valid() && box_selecting) {
if ((mm->get_button_mask() & MouseButton::MASK_LEFT) == MouseButton::NONE) {
// No longer.
@@ -5302,6 +5294,31 @@ void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) {
}
}
+void AnimationTrackEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) {
+ if (p_alt) {
+ if (p_scroll_vec.x < 0 || p_scroll_vec.y < 0) {
+ goto_prev_step(true);
+ } else {
+ goto_next_step(true);
+ }
+ } else {
+ _pan_callback(-p_scroll_vec * 32);
+ }
+}
+
+void AnimationTrackEditor::_pan_callback(Vector2 p_scroll_vec) {
+ timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale());
+ scroll->set_v_scroll(scroll->get_v_scroll() - p_scroll_vec.y);
+}
+
+void AnimationTrackEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) {
+ if (p_scroll_vec.y < 0) {
+ timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05);
+ } else {
+ timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05);
+ }
+}
+
void AnimationTrackEditor::_cancel_bezier_edit() {
bezier_edit->hide();
scroll->show();
@@ -5428,7 +5445,7 @@ void AnimationTrackEditor::goto_prev_step(bool p_from_mouse_event) {
pos = 0;
}
set_anim_pos(pos);
- emit_signal(SNAME("timeline_changed"), pos, true);
+ emit_signal(SNAME("timeline_changed"), pos, true, false);
}
void AnimationTrackEditor::goto_next_step(bool p_from_mouse_event) {
@@ -5455,7 +5472,7 @@ void AnimationTrackEditor::goto_next_step(bool p_from_mouse_event) {
}
set_anim_pos(pos);
- emit_signal(SNAME("timeline_changed"), pos, true);
+ emit_signal(SNAME("timeline_changed"), pos, true, false);
}
void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
@@ -5679,16 +5696,16 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
to_restore.push_back(amr);
}
-#define _NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * ABS(s) + from_t
+#define NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * ABS(s) + from_t
// 3 - Move the keys (re insert them).
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
- float newpos = _NEW_POS(E->get().pos);
+ float newpos = NEW_POS(E->get().pos);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
}
// 4 - (Undo) Remove inserted keys.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
- float newpos = _NEW_POS(E->get().pos);
+ float newpos = NEW_POS(E->get().pos);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newpos);
}
@@ -5708,13 +5725,13 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
// 7-reselect.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float oldpos = E->get().pos;
- float newpos = _NEW_POS(oldpos);
+ float newpos = NEW_POS(oldpos);
if (newpos >= 0) {
undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos);
}
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos);
}
-#undef _NEW_POS
+#undef NEW_POS
undo_redo->commit_action();
} break;
case EDIT_DUPLICATE_SELECTION: {
@@ -5979,7 +5996,7 @@ void AnimationTrackEditor::_bind_methods() {
ClassDB::bind_method("_key_deselected", &AnimationTrackEditor::_key_deselected); // Still used by some connect_compat.
ClassDB::bind_method("_clear_selection", &AnimationTrackEditor::_clear_selection); // Still used by some connect_compat.
- ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag")));
+ ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag"), PropertyInfo(Variant::BOOL, "timeline_only")));
ADD_SIGNAL(MethodInfo("keying_changed"));
ADD_SIGNAL(MethodInfo("animation_len_changed", PropertyInfo(Variant::FLOAT, "len")));
ADD_SIGNAL(MethodInfo("animation_step_changed", PropertyInfo(Variant::FLOAT, "step")));
@@ -6088,13 +6105,18 @@ AnimationTrackEditor::AnimationTrackEditor() {
timeline->connect("value_changed", callable_mp(this, &AnimationTrackEditor::_timeline_value_changed));
timeline->connect("length_changed", callable_mp(this, &AnimationTrackEditor::_update_length));
+ panner.instantiate();
+ panner->set_callbacks(callable_mp(this, &AnimationTrackEditor::_scroll_callback), callable_mp(this, &AnimationTrackEditor::_pan_callback), callable_mp(this, &AnimationTrackEditor::_zoom_callback));
+
scroll = memnew(ScrollContainer);
timeline_vbox->add_child(scroll);
scroll->set_v_size_flags(SIZE_EXPAND_FILL);
VScrollBar *sb = scroll->get_v_scroll_bar();
scroll->remove_child(sb);
timeline_scroll->add_child(sb); // Move here so timeline and tracks are always aligned.
+ scroll->set_focus_mode(FOCUS_CLICK);
scroll->connect("gui_input", callable_mp(this, &AnimationTrackEditor::_scroll_input));
+ scroll->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
bezier_edit = memnew(AnimationBezierTrackEdit);
timeline_vbox->add_child(bezier_edit);
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index b5d44bc0d3..50c5c692c0 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -48,8 +48,8 @@
#include "scene_tree_editor.h"
class AnimationPlayer;
-
class AnimationTrackEdit;
+class ViewPanner;
class AnimationTimelineEdit : public Range {
GDCLASS(AnimationTimelineEdit, Range);
@@ -81,9 +81,11 @@ class AnimationTimelineEdit : public Range {
bool editing;
bool use_fps;
- bool panning_timeline;
- float panning_timeline_from;
- float panning_timeline_at;
+ Ref<ViewPanner> panner;
+ void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
+ void _pan_callback(Vector2 p_scroll_vec);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt);
+
bool dragging_timeline;
bool dragging_hsize;
float dragging_hsize_from;
@@ -314,7 +316,7 @@ class AnimationTrackEditor : public VBoxContainer {
void _update_tracks();
void _name_limit_changed();
- void _timeline_changed(float p_new_pos, bool p_drag, bool p_timeline_only = false);
+ void _timeline_changed(float p_new_pos, bool p_drag, bool p_timeline_only);
void _track_remove_request(int p_track);
void _track_grab_focus(int p_track);
@@ -374,6 +376,11 @@ class AnimationTrackEditor : public VBoxContainer {
PropertyInfo _find_hint_for_track(int p_idx, NodePath &r_base_path, Variant *r_current_val = nullptr);
+ Ref<ViewPanner> panner;
+ void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
+ void _pan_callback(Vector2 p_scroll_vec);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt);
+
void _timeline_value_changed(double);
float insert_key_from_track_call_ofs;
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 4669e56e20..967f7e0d1f 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -122,7 +122,7 @@ void FindReplaceBar::unhandled_input(const Ref<InputEvent> &p_event) {
return;
}
- Control *focus_owner = get_focus_owner();
+ Control *focus_owner = get_viewport()->gui_get_focus_owner();
if (text_editor->has_focus() || (focus_owner && vbc_lineedit->is_ancestor_of(focus_owner))) {
bool accepted = true;
@@ -724,7 +724,7 @@ void CodeTextEditor::input(const Ref<InputEvent> &event) {
}
if (!text_editor->has_focus()) {
- if ((find_replace_bar != nullptr && find_replace_bar->is_visible()) && (find_replace_bar->has_focus() || find_replace_bar->is_ancestor_of(get_focus_owner()))) {
+ if ((find_replace_bar != nullptr && find_replace_bar->is_visible()) && (find_replace_bar->has_focus() || find_replace_bar->is_ancestor_of(get_viewport()->gui_get_focus_owner()))) {
if (ED_IS_SHORTCUT("script_text_editor/find_next", key_event)) {
find_replace_bar->search_next();
accept_event();
@@ -1663,17 +1663,29 @@ void CodeTextEditor::_error_pressed(const Ref<InputEvent> &p_event) {
void CodeTextEditor::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
+ error_button->set_icon(get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")));
+ error_button->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ error_button->add_theme_font_override("font", get_theme_font(SNAME("status_source"), SNAME("EditorFonts")));
+ error_button->add_theme_font_size_override("font_size", get_theme_font_size(SNAME("status_source_size"), SNAME("EditorFonts")));
+
+ warning_button->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons")));
+ warning_button->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
+ warning_button->add_theme_font_override("font", get_theme_font(SNAME("status_source"), SNAME("EditorFonts")));
+ warning_button->add_theme_font_size_override("font_size", get_theme_font_size(SNAME("status_source_size"), SNAME("EditorFonts")));
+
+ line_and_col_txt->add_theme_font_override("font", get_theme_font(SNAME("status_source"), SNAME("EditorFonts")));
+ line_and_col_txt->add_theme_font_size_override("font_size", get_theme_font_size(SNAME("status_source_size"), SNAME("EditorFonts")));
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ break;
+ }
if (toggle_scripts_button->is_visible()) {
update_toggle_scripts_button();
}
_update_text_editor_theme();
} break;
- case NOTIFICATION_ENTER_TREE: {
- error_button->set_icon(get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")));
- warning_button->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons")));
- add_theme_constant_override("separation", 4 * EDSCALE);
- } break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (toggle_scripts_button->is_visible()) {
update_toggle_scripts_button();
@@ -1874,10 +1886,6 @@ CodeTextEditor::CodeTextEditor() {
error_button->connect("pressed", callable_mp(this, &CodeTextEditor::_error_button_pressed));
error_button->set_tooltip(TTR("Errors"));
- error_button->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor")));
- error_button->add_theme_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("status_source"), SNAME("EditorFonts")));
- error_button->add_theme_font_size_override("font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("status_source_size"), SNAME("EditorFonts")));
-
is_errors_panel_opened = false;
set_error_count(0);
@@ -1890,10 +1898,6 @@ CodeTextEditor::CodeTextEditor() {
warning_button->connect("pressed", callable_mp(this, &CodeTextEditor::_warning_button_pressed));
warning_button->set_tooltip(TTR("Warnings"));
- warning_button->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("warning_color"), SNAME("Editor")));
- warning_button->add_theme_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("status_source"), SNAME("EditorFonts")));
- warning_button->add_theme_font_size_override("font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("status_source_size"), SNAME("EditorFonts")));
-
is_warnings_panel_opened = false;
set_warning_count(0);
@@ -1901,15 +1905,13 @@ CodeTextEditor::CodeTextEditor() {
line_and_col_txt = memnew(Label);
status_bar->add_child(line_and_col_txt);
line_and_col_txt->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER);
- line_and_col_txt->add_theme_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("status_source"), SNAME("EditorFonts")));
- line_and_col_txt->add_theme_font_size_override("font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("status_source_size"), SNAME("EditorFonts")));
line_and_col_txt->set_tooltip(TTR("Line and column numbers."));
line_and_col_txt->set_mouse_filter(MOUSE_FILTER_STOP);
text_editor->connect("gui_input", callable_mp(this, &CodeTextEditor::_text_editor_gui_input));
text_editor->connect("caret_changed", callable_mp(this, &CodeTextEditor::_line_col_changed));
text_editor->connect("text_changed", callable_mp(this, &CodeTextEditor::_text_changed));
- text_editor->connect("request_code_completion", callable_mp(this, &CodeTextEditor::_complete_request));
+ text_editor->connect("code_completion_requested", callable_mp(this, &CodeTextEditor::_complete_request));
TypedArray<String> cs;
cs.push_back(".");
cs.push_back(",");
@@ -1933,4 +1935,5 @@ CodeTextEditor::CodeTextEditor() {
font_resize_timer->connect("timeout", callable_mp(this, &CodeTextEditor::_font_resize_timeout));
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CodeTextEditor::_on_settings_change));
+ add_theme_constant_override("separation", 4 * EDSCALE);
}
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 0edbb182e1..1d5fa9cbbb 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -540,13 +540,27 @@ ConnectDialog::~ConnectDialog() {
// Originally copied and adapted from EditorProperty, try to keep style in sync.
Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const {
EditorHelpBit *help_bit = memnew(EditorHelpBit);
- help_bit->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel")));
help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE);
- String text = TTR("Signal:") + " [u][b]" + p_text.get_slice("::", 0) + "[/b][/u]";
- text += p_text.get_slice("::", 1).strip_edges() + "\n";
- text += p_text.get_slice("::", 2).strip_edges();
- help_bit->call_deferred(SNAME("set_text"), text); // Hack so it uses proper theme once inside scene.
+ // p_text is expected to be something like this:
+ // "gui_input::(event: InputEvent)::<Signal description>"
+ // with the latter being possibly empty.
+ PackedStringArray slices = p_text.split("::", false);
+ if (slices.size() < 2) {
+ // Shouldn't happen here, but just in case pass the text along.
+ help_bit->set_text(p_text);
+ return help_bit;
+ }
+
+ String text = TTR("Signal:") + " [u][b]" + slices[0] + "[/b][/u]";
+ text += slices[1].strip_edges() + "\n";
+ if (slices.size() > 2) {
+ text += slices[2].strip_edges();
+ } else {
+ text += "[i]" + TTR("No description.") + "[/i]";
+ }
+ help_bit->set_text(text);
+
return help_bit;
}
@@ -649,8 +663,8 @@ void ConnectionsDock::_connect(ConnectDialog::ConnectionData p_cd) {
undo_redo->add_undo_method(source, "disconnect", p_cd.signal, callable);
undo_redo->add_do_method(this, "update_tree");
undo_redo->add_undo_method(this, "update_tree");
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); // To force redraw of scene tree.
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); // To force redraw of scene tree.
+ undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
}
@@ -671,8 +685,8 @@ void ConnectionsDock::_disconnect(TreeItem &p_item) {
undo_redo->add_undo_method(selected_node, "connect", cd.signal, callable, cd.binds, cd.flags);
undo_redo->add_do_method(this, "update_tree");
undo_redo->add_undo_method(this, "update_tree");
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); // To force redraw of scene tree.
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); // To force redraw of scene tree.
+ undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
}
@@ -702,8 +716,8 @@ void ConnectionsDock::_disconnect_all() {
undo_redo->add_do_method(this, "update_tree");
undo_redo->add_undo_method(this, "update_tree");
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
}
@@ -922,8 +936,14 @@ void ConnectionsDock::_connect_pressed() {
}
void ConnectionsDock::_notification(int p_what) {
- if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
- update_tree();
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ } break;
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ update_tree();
+ } break;
}
}
@@ -1003,7 +1023,7 @@ void ConnectionsDock::update_tree() {
PackedStringArray argnames;
String filter_text = search_box->get_text();
- if (!filter_text.is_subsequence_ofi(signal_name)) {
+ if (!filter_text.is_subsequence_ofn(signal_name)) {
continue;
}
@@ -1135,7 +1155,6 @@ ConnectionsDock::ConnectionsDock(EditorNode *p_editor) {
search_box = memnew(LineEdit);
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search_box->set_placeholder(TTR("Filter signals"));
- search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
search_box->set_clear_button_enabled(true);
search_box->connect("text_changed", callable_mp(this, &ConnectionsDock::_filter_changed));
vbc->add_child(search_box);
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 608eab9a9f..e8fd97fa1a 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -170,7 +170,7 @@ void CreateDialog::_update_search() {
root->set_text(0, base_type);
root->set_icon(0, search_options->get_theme_icon(icon_fallback, SNAME("EditorIcons")));
search_options_types[base_type] = root;
- _configure_search_option_item(root, base_type, ClassDB::class_exists(base_type));
+ _configure_search_option_item(root, base_type, ClassDB::class_exists(base_type) ? TypeCategory::CPP_TYPE : TypeCategory::OTHER_TYPE);
const String search_text = search_box->get_text();
bool empty_search = search_text.is_empty();
@@ -178,14 +178,14 @@ void CreateDialog::_update_search() {
// Filter all candidate results.
Vector<String> candidates;
for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) {
- if (empty_search || search_text.is_subsequence_ofi(I->get())) {
+ if (empty_search || search_text.is_subsequence_ofn(I->get())) {
candidates.push_back(I->get());
}
}
// Build the type tree.
for (int i = 0; i < candidates.size(); i++) {
- _add_type(candidates[i], ClassDB::class_exists(candidates[i]));
+ _add_type(candidates[i], ClassDB::class_exists(candidates[i]) ? TypeCategory::CPP_TYPE : TypeCategory::OTHER_TYPE);
}
// Select the best result.
@@ -202,31 +202,80 @@ void CreateDialog::_update_search() {
}
}
-void CreateDialog::_add_type(const String &p_type, bool p_cpp_type) {
+void CreateDialog::_add_type(const String &p_type, const TypeCategory p_type_category) {
if (search_options_types.has(p_type)) {
return;
}
String inherits;
- if (p_cpp_type) {
+
+ TypeCategory inherited_type = TypeCategory::OTHER_TYPE;
+
+ if (p_type_category == TypeCategory::CPP_TYPE) {
inherits = ClassDB::get_parent_class(p_type);
- } else if (ScriptServer::is_global_class(p_type)) {
- inherits = EditorNode::get_editor_data().script_class_get_base(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> script = ResourceLoader::load(p_type, "Script");
+ ERR_FAIL_COND(script.is_null());
+
+ Ref<Script> base = script->get_base_script();
+ if (base.is_null()) {
+ String extends;
+ script->get_language()->get_global_class_name(script->get_path(), &extends);
+
+ inherits = extends;
+ inherited_type = TypeCategory::CPP_TYPE;
+ } else {
+ inherits = script->get_language()->get_global_class_name(base->get_path());
+ if (inherits.is_empty()) {
+ inherits = base->get_path();
+ inherited_type = TypeCategory::PATH_TYPE;
+ }
+ }
} else {
- inherits = custom_type_parents[p_type];
+ if (ScriptServer::is_global_class(p_type)) {
+ inherits = EditorNode::get_editor_data().script_class_get_base(p_type);
+ if (inherits.is_empty()) {
+ Ref<Script> script = EditorNode::get_editor_data().script_class_load_script(p_type);
+ ERR_FAIL_COND(script.is_null());
+
+ Ref<Script> base = script->get_base_script();
+ if (base.is_null()) {
+ String extends;
+ script->get_language()->get_global_class_name(script->get_path(), &extends);
+
+ inherits = extends;
+ inherited_type = TypeCategory::CPP_TYPE;
+ } else {
+ inherits = base->get_path();
+ inherited_type = TypeCategory::PATH_TYPE;
+ }
+ }
+ } else {
+ inherits = custom_type_parents[p_type];
+ if (ClassDB::class_exists(inherits)) {
+ inherited_type = TypeCategory::CPP_TYPE;
+ }
+ }
}
- _add_type(inherits, p_cpp_type || ClassDB::class_exists(inherits));
+ // Should never happen, but just in case...
+ ERR_FAIL_COND(inherits.is_empty());
+
+ _add_type(inherits, inherited_type);
TreeItem *item = search_options->create_item(search_options_types[inherits]);
search_options_types[p_type] = item;
- _configure_search_option_item(item, p_type, p_cpp_type);
+ _configure_search_option_item(item, p_type, p_type_category);
}
-void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String &p_type, const bool p_cpp_type) {
+void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String &p_type, const TypeCategory p_type_category) {
bool script_type = ScriptServer::is_global_class(p_type);
- if (p_cpp_type) {
+ if (p_type_category == TypeCategory::CPP_TYPE) {
r_item->set_text(0, p_type);
+ } else if (p_type_category == TypeCategory::PATH_TYPE) {
+ r_item->set_text(0, "\"" + p_type + "\"");
} else if (script_type) {
r_item->set_metadata(0, p_type);
r_item->set_text(0, p_type + " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")");
@@ -235,7 +284,9 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String
r_item->set_text(0, p_type);
}
- bool can_instantiate = (p_cpp_type && ClassDB::can_instantiate(p_type)) || !p_cpp_type;
+ bool can_instantiate = (p_type_category == TypeCategory::CPP_TYPE && ClassDB::can_instantiate(p_type)) ||
+ p_type_category == TypeCategory::OTHER_TYPE;
+
if (!can_instantiate) {
r_item->set_custom_color(0, search_options->get_theme_color(SNAME("disabled_font_color"), SNAME("Editor")));
r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, "NodeDisabled"));
@@ -259,7 +310,7 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String
const String &description = DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description);
r_item->set_tooltip(0, description);
- if (!p_cpp_type && !script_type) {
+ if (p_type_category == TypeCategory::OTHER_TYPE && !script_type) {
Ref<Texture2D> icon = EditorNode::get_editor_data().get_custom_types()[custom_type_parents[p_type]][custom_type_indices[p_type]].icon;
if (icon.is_valid()) {
r_item->set_icon(0, icon);
@@ -367,13 +418,16 @@ void CreateDialog::_sbox_input(const Ref<InputEvent> &p_ie) {
}
}
+void CreateDialog::_update_theme() {
+ search_box->set_right_icon(search_options->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ favorite->set_icon(search_options->get_theme_icon(SNAME("Favorites"), SNAME("EditorIcons")));
+}
+
void CreateDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
connect("confirmed", callable_mp(this, &CreateDialog::_confirmed));
- search_box->set_right_icon(search_options->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
- search_box->set_clear_button_enabled(true);
- favorite->set_icon(search_options->get_theme_icon(SNAME("Favorites"), SNAME("EditorIcons")));
+ _update_theme();
} break;
case NOTIFICATION_EXIT_TREE: {
disconnect("confirmed", callable_mp(this, &CreateDialog::_confirmed));
@@ -386,6 +440,9 @@ void CreateDialog::_notification(int p_what) {
EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "create_new_node", Rect2(get_position(), get_size()));
}
} break;
+ case NOTIFICATION_THEME_CHANGED: {
+ _update_theme();
+ } break;
}
}
@@ -410,7 +467,7 @@ void CreateDialog::select_type(const String &p_type) {
}
favorite->set_disabled(false);
- favorite->set_pressed(favorite_list.find(p_type) != -1);
+ favorite->set_pressed(favorite_list.has(p_type));
get_ok_button()->set_disabled(false);
}
@@ -482,12 +539,12 @@ void CreateDialog::_favorite_toggled() {
String name = item->get_text(0);
- if (favorite_list.find(name) == -1) {
- favorite_list.push_back(name);
- favorite->set_pressed(true);
- } else {
+ if (favorite_list.has(name)) {
favorite_list.erase(name);
favorite->set_pressed(false);
+ } else {
+ favorite_list.push_back(name);
+ favorite->set_pressed(true);
}
_save_and_update_favorite_list();
@@ -711,6 +768,7 @@ CreateDialog::CreateDialog() {
hsc->add_child(vbc);
search_box = memnew(LineEdit);
+ search_box->set_clear_button_enabled(true);
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search_box->connect("text_changed", callable_mp(this, &CreateDialog::_text_changed));
search_box->connect("gui_input", callable_mp(this, &CreateDialog::_sbox_input));
diff --git a/editor/create_dialog.h b/editor/create_dialog.h
index c36730c4f0..f905160df3 100644
--- a/editor/create_dialog.h
+++ b/editor/create_dialog.h
@@ -41,6 +41,12 @@
class CreateDialog : public ConfirmationDialog {
GDCLASS(CreateDialog, ConfirmationDialog);
+ enum TypeCategory {
+ CPP_TYPE,
+ PATH_TYPE,
+ OTHER_TYPE
+ };
+
LineEdit *search_box;
Tree *search_options;
@@ -62,8 +68,8 @@ class CreateDialog : public ConfirmationDialog {
void _update_search();
bool _should_hide_type(const String &p_type) const;
- void _add_type(const String &p_current, bool p_cpp_type);
- void _configure_search_option_item(TreeItem *r_item, const String &p_type, const bool p_cpp_type);
+ 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;
float _score_type(const String &p_type, const String &p_search) const;
bool _is_type_preferred(const String &p_type) const;
@@ -95,6 +101,8 @@ class CreateDialog : public ConfirmationDialog {
bool _is_class_disabled_by_feature_profile(const StringName &p_class) const;
void _load_favorites_and_history();
+ void _update_theme();
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp
index ddcd32c16f..38bdbe2870 100644
--- a/editor/debugger/editor_debugger_inspector.cpp
+++ b/editor/debugger/editor_debugger_inspector.cpp
@@ -262,11 +262,18 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
variables->prop_values[type + n] = v;
variables->update();
edit(variables);
+
+ // To prevent constantly resizing when using filtering.
+ int size_x = get_size().x;
+ if (size_x > get_custom_minimum_size().x) {
+ set_custom_minimum_size(Size2(size_x, 0));
+ }
}
void EditorDebuggerInspector::clear_stack_variables() {
variables->clear();
variables->update();
+ set_custom_minimum_size(Size2(0, 0));
}
String EditorDebuggerInspector::get_stack_variable(const String &p_var) {
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 5bc2be60a7..79853b6809 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -75,8 +75,8 @@ EditorDebuggerNode::EditorDebuggerNode() {
remote_scene_tree = memnew(EditorDebuggerTree);
remote_scene_tree->connect("object_selected", callable_mp(this, &EditorDebuggerNode::_remote_object_requested));
remote_scene_tree->connect("save_node", callable_mp(this, &EditorDebuggerNode::_save_node_requested));
- EditorNode::get_singleton()->get_scene_tree_dock()->add_remote_tree_editor(remote_scene_tree);
- EditorNode::get_singleton()->get_scene_tree_dock()->connect("remote_tree_selected", callable_mp(this, &EditorDebuggerNode::request_remote_tree));
+ SceneTreeDock::get_singleton()->add_remote_tree_editor(remote_scene_tree);
+ SceneTreeDock::get_singleton()->connect("remote_tree_selected", callable_mp(this, &EditorDebuggerNode::request_remote_tree));
remote_scene_tree_timeout = EDITOR_DEF("debugger/remote_scene_tree_refresh_interval", 1.0);
inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2);
@@ -95,6 +95,7 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
node->connect("stopped", callable_mp(this, &EditorDebuggerNode::_debugger_stopped), varray(id));
node->connect("stack_frame_selected", callable_mp(this, &EditorDebuggerNode::_stack_frame_selected), varray(id));
node->connect("error_selected", callable_mp(this, &EditorDebuggerNode::_error_selected), varray(id));
+ node->connect("breakpoint_selected", callable_mp(this, &EditorDebuggerNode::_error_selected), varray(id));
node->connect("clear_execution", callable_mp(this, &EditorDebuggerNode::_clear_execution));
node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked), varray(id));
node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated), varray(id));
@@ -331,10 +332,10 @@ void EditorDebuggerNode::_notification(int p_what) {
// Switch to remote tree view if so desired.
auto_switch_remote_scene_tree = (bool)EditorSettings::get_singleton()->get("debugger/auto_switch_to_remote_scene_tree");
if (auto_switch_remote_scene_tree) {
- EditorNode::get_singleton()->get_scene_tree_dock()->show_remote_tree();
+ SceneTreeDock::get_singleton()->show_remote_tree();
}
// Good to go.
- EditorNode::get_singleton()->get_scene_tree_dock()->show_tab_buttons();
+ SceneTreeDock::get_singleton()->show_tab_buttons();
debugger->set_editor_remote_tree(remote_scene_tree);
debugger->start(server->take_connection());
// Send breakpoints.
@@ -360,8 +361,8 @@ void EditorDebuggerNode::_debugger_stopped(int p_id) {
if (!found) {
EditorNode::get_singleton()->get_pause_button()->set_pressed(false);
EditorNode::get_singleton()->get_pause_button()->set_disabled(true);
- EditorNode::get_singleton()->get_scene_tree_dock()->hide_remote_tree();
- EditorNode::get_singleton()->get_scene_tree_dock()->hide_tab_buttons();
+ SceneTreeDock::get_singleton()->hide_remote_tree();
+ SceneTreeDock::get_singleton()->hide_tab_buttons();
EditorNode::get_singleton()->notify_all_debug_sessions_exited();
}
}
@@ -575,7 +576,7 @@ void EditorDebuggerNode::_remote_object_property_updated(ObjectID p_id, const St
if (obj->remote_object_id != p_id) {
return;
}
- EditorNode::get_singleton()->get_inspector()->update_property(p_property);
+ InspectorDock::get_inspector_singleton()->update_property(p_property);
}
}
diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp
index 70d64615ae..41f4db541d 100644
--- a/editor/debugger/editor_debugger_tree.cpp
+++ b/editor/debugger/editor_debugger_tree.cpp
@@ -128,7 +128,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position) {
void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger) {
updating_scene_tree = true;
const String last_path = get_selected_path();
- const String filter = EditorNode::get_singleton()->get_scene_tree_dock()->get_filter();
+ const String filter = SceneTreeDock::get_singleton()->get_filter();
bool filter_changed = filter != last_filter;
TreeItem *scroll_item = nullptr;
@@ -186,7 +186,7 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
// Apply filters.
while (parent) {
const bool had_siblings = item->get_prev() || item->get_next();
- if (filter.is_subsequence_ofi(item->get_text(0))) {
+ if (filter.is_subsequence_ofn(item->get_text(0))) {
break; // Filter matches, must survive.
}
parent->remove_child(item);
diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp
index d5e825a26c..da1d6a54f2 100644
--- a/editor/debugger/editor_profiler.cpp
+++ b/editor/debugger/editor_profiler.cpp
@@ -456,8 +456,9 @@ void EditorProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) {
if (mb.is_valid() || (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
updating_frame = true;
- if (x < total_metrics)
+ if (x < total_metrics) {
cursor_metric_edit->set_value(_get_frame_metric(x).frame_number);
+ }
updating_frame = false;
if (activate->is_pressed()) {
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index b72a20ee2f..ee844fff64 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -788,8 +788,10 @@ void ScriptEditorDebugger::_notification(int p_what) {
le_clear->connect("pressed", callable_mp(this, &ScriptEditorDebugger::_live_edit_clear));
error_tree->connect("item_selected", callable_mp(this, &ScriptEditorDebugger::_error_selected));
error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated));
+ breakpoints_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_breakpoint_tree_clicked));
vmem_refresh->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
vmem_export->set_icon(get_theme_icon(SNAME("Save"), SNAME("EditorIcons")));
+ search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
reason->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
@@ -864,6 +866,7 @@ void ScriptEditorDebugger::_notification(int p_what) {
docontinue->set_icon(get_theme_icon(SNAME("DebugContinue"), SNAME("EditorIcons")));
vmem_refresh->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
vmem_export->set_icon(get_theme_icon(SNAME("Save"), SNAME("EditorIcons")));
+ search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
} break;
}
}
@@ -893,6 +896,13 @@ void ScriptEditorDebugger::_clear_breakpoints() {
emit_signal("clear_breakpoints");
}
+void ScriptEditorDebugger::_breakpoint_tree_clicked() {
+ TreeItem *selected = breakpoints_tree->get_selected();
+ if (selected->has_meta("line")) {
+ emit_signal(SNAME("breakpoint_selected"), selected->get_parent()->get_text(0), int(selected->get_meta("line")));
+ }
+}
+
void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {
_clear_errors_list();
stop();
@@ -1111,7 +1121,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
NodePath path = editor->get_edited_scene()->get_path_to(node);
int pathid = _get_node_path_cache(path);
- if (p_value.is_ref()) {
+ if (p_value.is_ref_counted()) {
Ref<Resource> res = p_value;
if (res.is_valid() && !res->get_path().is_empty()) {
Array msg;
@@ -1137,7 +1147,7 @@ void ScriptEditorDebugger::_property_changed(Object *p_base, const StringName &p
String respath = res->get_path();
int pathid = _get_res_path_cache(respath);
- if (p_value.is_ref()) {
+ if (p_value.is_ref_counted()) {
Ref<Resource> res2 = p_value;
if (res2.is_valid() && !res2->get_path().is_empty()) {
Array msg;
@@ -1351,6 +1361,45 @@ void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool
msg.push_back(p_line);
msg.push_back(p_enabled);
_put_msg("breakpoint", msg);
+
+ TreeItem *path_item = breakpoints_tree->search_item_text(p_path);
+ if (path_item == nullptr) {
+ if (!p_enabled) {
+ return;
+ }
+ path_item = breakpoints_tree->create_item();
+ path_item->set_text(0, p_path);
+ }
+
+ int idx = 0;
+ TreeItem *breakpoint_item;
+ for (breakpoint_item = path_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) {
+ if ((int)breakpoint_item->get_meta("line") < p_line) {
+ idx++;
+ continue;
+ }
+
+ if ((int)breakpoint_item->get_meta("line") == p_line) {
+ break;
+ }
+ }
+
+ if (breakpoint_item == nullptr) {
+ if (!p_enabled) {
+ return;
+ }
+ breakpoint_item = breakpoints_tree->create_item(path_item, idx);
+ breakpoint_item->set_meta("line", p_line);
+ breakpoint_item->set_text(0, vformat(TTR("Line %d"), p_line));
+ return;
+ }
+
+ if (!p_enabled) {
+ path_item->remove_child(breakpoint_item);
+ if (path_item->get_first_child() == nullptr) {
+ breakpoints_tree->get_root()->remove_child(path_item);
+ }
+ }
}
void ScriptEditorDebugger::reload_scripts() {
@@ -1417,6 +1466,23 @@ void ScriptEditorDebugger::_clear_errors_list() {
clear_button->set_disabled(true);
}
+void ScriptEditorDebugger::_breakpoints_item_rmb_selected(const Vector2 &p_pos) {
+ breakpoints_menu->clear();
+ breakpoints_menu->set_size(Size2(1, 1));
+
+ const TreeItem *selected = breakpoints_tree->get_selected();
+ String file = selected->get_text(0);
+ if (selected->has_meta("line")) {
+ breakpoints_menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete Breakpoint"), ACTION_DELETE_BREAKPOINT);
+ file = selected->get_parent()->get_text(0);
+ }
+ breakpoints_menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete All Breakpoints in: ") + file, ACTION_DELETE_BREAKPOINTS_IN_FILE);
+ breakpoints_menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete All Breakpoints"), ACTION_DELETE_ALL_BREAKPOINTS);
+
+ breakpoints_menu->set_position(breakpoints_tree->get_global_position() + p_pos);
+ breakpoints_menu->popup();
+}
+
// Right click on specific file(s) or folder(s).
void ScriptEditorDebugger::_error_tree_item_rmb_selected(const Vector2 &p_pos) {
item_menu->clear();
@@ -1491,6 +1557,29 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) {
line_number));
}
} break;
+ case ACTION_DELETE_BREAKPOINT: {
+ const TreeItem *selected = breakpoints_tree->get_selected();
+ _set_breakpoint(selected->get_parent()->get_text(0), selected->get_meta("line"), false);
+ } break;
+ case ACTION_DELETE_BREAKPOINTS_IN_FILE: {
+ TreeItem *file_item = breakpoints_tree->get_selected();
+ if (file_item->has_meta("line")) {
+ file_item = file_item->get_parent();
+ }
+
+ // Store first else we will be removing as we loop.
+ List<int> lines;
+ for (TreeItem *breakpoint_item = file_item->get_first_child(); breakpoint_item; breakpoint_item = breakpoint_item->get_next()) {
+ lines.push_back(breakpoint_item->get_meta("line"));
+ }
+
+ for (const int &line : lines) {
+ _set_breakpoint(file_item->get_text(0), line, false);
+ }
+ } break;
+ case ACTION_DELETE_ALL_BREAKPOINTS: {
+ _clear_breakpoints();
+ } break;
}
}
@@ -1517,6 +1606,7 @@ void ScriptEditorDebugger::_bind_methods() {
ADD_SIGNAL(MethodInfo("stop_requested"));
ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame")));
ADD_SIGNAL(MethodInfo("error_selected", PropertyInfo(Variant::INT, "error")));
+ ADD_SIGNAL(MethodInfo("breakpoint_selected", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));
ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line")));
ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script")));
ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug"), PropertyInfo(Variant::STRING, "reason"), PropertyInfo(Variant::BOOL, "has_stackdump")));
@@ -1644,9 +1734,15 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue"));
docontinue->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_continue));
+ HSplitContainer *parent_sc = memnew(HSplitContainer);
+ vbc->add_child(parent_sc);
+ parent_sc->set_v_size_flags(SIZE_EXPAND_FILL);
+ parent_sc->set_split_offset(500 * EDSCALE);
+
HSplitContainer *sc = memnew(HSplitContainer);
- vbc->add_child(sc);
sc->set_v_size_flags(SIZE_EXPAND_FILL);
+ sc->set_h_size_flags(SIZE_EXPAND_FILL);
+ parent_sc->add_child(sc);
stack_dump = memnew(Tree);
stack_dump->set_allow_reselect(true);
@@ -1658,15 +1754,47 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
stack_dump->connect("cell_selected", callable_mp(this, &ScriptEditorDebugger::_stack_dump_frame_selected));
sc->add_child(stack_dump);
+ VBoxContainer *inspector_vbox = memnew(VBoxContainer);
+ inspector_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ sc->add_child(inspector_vbox);
+
+ HBoxContainer *tools_hb = memnew(HBoxContainer);
+ inspector_vbox->add_child(tools_hb);
+
+ search = memnew(LineEdit);
+ search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ search->set_placeholder(TTR("Filter stack variables"));
+ search->set_clear_button_enabled(true);
+ tools_hb->add_child(search);
+
inspector = memnew(EditorDebuggerInspector);
inspector->set_h_size_flags(SIZE_EXPAND_FILL);
+ inspector->set_v_size_flags(SIZE_EXPAND_FILL);
inspector->set_enable_capitalize_paths(false);
inspector->set_read_only(true);
inspector->connect("object_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected));
inspector->connect("object_edited", callable_mp(this, &ScriptEditorDebugger::_remote_object_edited));
inspector->connect("object_property_updated", callable_mp(this, &ScriptEditorDebugger::_remote_object_property_updated));
- sc->add_child(inspector);
+ inspector->register_text_enter(search);
+ inspector->set_use_filter(true);
+ inspector_vbox->add_child(inspector);
+
+ breakpoints_tree = memnew(Tree);
+ breakpoints_tree->set_h_size_flags(SIZE_EXPAND_FILL);
+ breakpoints_tree->set_column_titles_visible(true);
+ breakpoints_tree->set_column_title(0, TTR("Breakpoints"));
+ breakpoints_tree->set_allow_reselect(true);
+ breakpoints_tree->set_allow_rmb_select(true);
+ breakpoints_tree->set_hide_root(true);
+ breakpoints_tree->connect("item_rmb_selected", callable_mp(this, &ScriptEditorDebugger::_breakpoints_item_rmb_selected));
+ breakpoints_tree->create_item();
+
+ parent_sc->add_child(breakpoints_tree);
tabs->add_child(dbg);
+
+ breakpoints_menu = memnew(PopupMenu);
+ breakpoints_menu->connect("id_pressed", callable_mp(this, &ScriptEditorDebugger::_item_menu_id_pressed));
+ breakpoints_tree->add_child(breakpoints_menu);
}
{ //errors
diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h
index ff1a852f26..c061e7c61e 100644
--- a/editor/debugger/script_editor_debugger.h
+++ b/editor/debugger/script_editor_debugger.h
@@ -81,6 +81,9 @@ private:
enum Actions {
ACTION_COPY_ERROR,
ACTION_OPEN_SOURCE,
+ ACTION_DELETE_BREAKPOINT,
+ ACTION_DELETE_BREAKPOINTS_IN_FILE,
+ ACTION_DELETE_ALL_BREAKPOINTS,
};
AcceptDialog *msgdialog;
@@ -99,6 +102,9 @@ private:
Button *clear_button;
PopupMenu *item_menu;
+ Tree *breakpoints_tree;
+ PopupMenu *breakpoints_menu;
+
EditorFileDialog *file_dialog;
enum FileDialogPurpose {
SAVE_MONITORS_CSV,
@@ -134,6 +140,7 @@ private:
LineEdit *vmem_total;
Tree *stack_dump;
+ LineEdit *search = nullptr;
EditorDebuggerInspector *inspector;
SceneDebuggerTree *scene_tree;
@@ -197,6 +204,7 @@ private:
void _clear_errors_list();
+ void _breakpoints_item_rmb_selected(const Vector2 &p_pos);
void _error_tree_item_rmb_selected(const Vector2 &p_pos);
void _item_menu_id_pressed(int p_option);
void _tab_changed(int p_tab);
@@ -210,6 +218,8 @@ private:
void _set_breakpoint(const String &p_path, const int &p_line, const bool &p_enabled);
void _clear_breakpoints();
+ void _breakpoint_tree_clicked();
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index a71e16b66c..749ab7e2f6 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -350,7 +350,7 @@ void DocTools::generate(bool p_basic_types) {
List<PropertyInfo> properties;
List<PropertyInfo> own_properties;
if (name == "ProjectSettings") {
- //special case for project settings, so settings can be documented
+ // Special case for project settings, so settings can be documented.
ProjectSettings::get_singleton()->get_property_list(&properties);
own_properties = properties;
} else {
@@ -358,9 +358,12 @@ void DocTools::generate(bool p_basic_types) {
ClassDB::get_property_list(name, &own_properties, true);
}
+ properties.sort();
+ own_properties.sort();
+
List<PropertyInfo>::Element *EO = own_properties.front();
for (const PropertyInfo &E : properties) {
- bool inherited = EO == nullptr;
+ bool inherited = true;
if (EO && EO->get() == E) {
inherited = false;
EO = EO->next();
@@ -410,7 +413,7 @@ void DocTools::generate(bool p_basic_types) {
//used to track uninitialized values using valgrind
//print_line("getting default value for " + String(name) + "." + String(E.name));
if (default_value_valid && default_value.get_type() != Variant::OBJECT) {
- prop.default_value = default_value.get_construct_string().replace("\n", "");
+ prop.default_value = default_value.get_construct_string().replace("\n", " ");
}
StringName setter = ClassDB::get_property_setter(name, E.name);
@@ -522,7 +525,7 @@ void DocTools::generate(bool p_basic_types) {
int darg_idx = i - (E.arguments.size() - E.default_arguments.size());
if (darg_idx >= 0) {
Variant default_arg = E.default_arguments[darg_idx];
- argument.default_value = default_arg.get_construct_string();
+ argument.default_value = default_arg.get_construct_string().replace("\n", " ");
}
method.arguments.push_back(argument);
@@ -531,11 +534,11 @@ void DocTools::generate(bool p_basic_types) {
Vector<Error> errs = ClassDB::get_method_error_return_values(name, E.name);
if (errs.size()) {
- if (errs.find(OK) == -1) {
+ if (!errs.has(OK)) {
errs.insert(0, OK);
}
for (int i = 0; i < errs.size(); i++) {
- if (method.errors_returned.find(errs[i]) == -1) {
+ if (!method.errors_returned.has(errs[i])) {
method.errors_returned.push_back(errs[i]);
}
}
@@ -585,7 +588,7 @@ void DocTools::generate(bool p_basic_types) {
tid.name = E;
tid.type = "Color";
tid.data_type = "color";
- tid.default_value = Variant(Theme::get_default()->get_color(E, cname)).get_construct_string();
+ tid.default_value = Variant(Theme::get_default()->get_color(E, cname)).get_construct_string().replace("\n", " ");
c.theme_properties.push_back(tid);
}
@@ -757,7 +760,7 @@ void DocTools::generate(bool p_basic_types) {
int darg_idx = mi.default_arguments.size() - mi.arguments.size() + j;
if (darg_idx >= 0) {
Variant default_arg = mi.default_arguments[darg_idx];
- ad.default_value = default_arg.get_construct_string();
+ ad.default_value = default_arg.get_construct_string().replace("\n", " ");
}
method.arguments.push_back(ad);
@@ -801,7 +804,7 @@ void DocTools::generate(bool p_basic_types) {
DocData::PropertyDoc property;
property.name = pi.name;
property.type = Variant::get_type_name(pi.type);
- property.default_value = v.get(pi.name).get_construct_string();
+ property.default_value = v.get(pi.name).get_construct_string().replace("\n", " ");
c.properties.push_back(property);
}
@@ -813,7 +816,7 @@ void DocTools::generate(bool p_basic_types) {
DocData::ConstantDoc constant;
constant.name = E;
Variant value = Variant::get_constant_value(Variant::Type(i), E);
- constant.value = value.get_type() == Variant::INT ? itos(value) : value.get_construct_string();
+ constant.value = value.get_type() == Variant::INT ? itos(value) : value.get_construct_string().replace("\n", " ");
constant.is_value_valid = true;
c.constants.push_back(constant);
}
@@ -930,7 +933,7 @@ void DocTools::generate(bool p_basic_types) {
int darg_idx = j - (mi.arguments.size() - mi.default_arguments.size());
if (darg_idx >= 0) {
Variant default_arg = mi.default_arguments[darg_idx];
- ad.default_value = default_arg.get_construct_string();
+ ad.default_value = default_arg.get_construct_string().replace("\n", " ");
}
md.arguments.push_back(ad);
diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp
index 1de67149c6..76c0811166 100644
--- a/editor/editor_asset_installer.cpp
+++ b/editor/editor_asset_installer.cpp
@@ -36,45 +36,6 @@
#include "editor_node.h"
#include "progress_dialog.h"
-void EditorAssetInstaller::_update_subitems(TreeItem *p_item, bool p_check, bool p_first) {
- if (p_check) {
- if (p_item->get_custom_color(0) == Color()) {
- p_item->set_checked(0, true);
- }
- } else {
- p_item->set_checked(0, false);
- }
-
- if (p_item->get_first_child()) {
- _update_subitems(p_item->get_first_child(), p_check);
- }
-
- if (!p_first && p_item->get_next()) {
- _update_subitems(p_item->get_next(), p_check);
- }
-}
-
-void EditorAssetInstaller::_uncheck_parent(TreeItem *p_item) {
- if (!p_item) {
- return;
- }
-
- bool any_checked = false;
- TreeItem *item = p_item->get_first_child();
- while (item) {
- if (item->is_checked(0)) {
- any_checked = true;
- break;
- }
- item = item->get_next();
- }
-
- if (!any_checked) {
- p_item->set_checked(0, false);
- _uncheck_parent(p_item->get_parent());
- }
-}
-
void EditorAssetInstaller::_item_edited() {
if (updating) {
return;
@@ -85,22 +46,17 @@ void EditorAssetInstaller::_item_edited() {
return;
}
- String path = item->get_metadata(0);
-
updating = true;
- if (path.is_empty() || item == tree->get_root()) { //a dir or root
- _update_subitems(item, item->is_checked(0), true);
- }
+ item->propagate_check(0);
+ updating = false;
+}
- if (item->is_checked(0)) {
- while (item) {
- item->set_checked(0, true);
- item = item->get_parent();
- }
- } else {
- _uncheck_parent(item->get_parent());
+void EditorAssetInstaller::_check_propagated_to_item(Object *p_obj, int column) {
+ TreeItem *affected_item = Object::cast_to<TreeItem>(p_obj);
+ if (affected_item && affected_item->get_custom_color(0) != Color()) {
+ affected_item->set_checked(0, false);
+ affected_item->propagate_check(0, false);
}
- updating = false;
}
void EditorAssetInstaller::open(const String &p_path, int p_depth) {
@@ -140,7 +96,6 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) {
extension_guess["jpeg"] = tree->get_theme_icon(SNAME("ImageTexture"), SNAME("EditorIcons"));
extension_guess["png"] = tree->get_theme_icon(SNAME("ImageTexture"), SNAME("EditorIcons"));
extension_guess["svg"] = tree->get_theme_icon(SNAME("ImageTexture"), SNAME("EditorIcons"));
- extension_guess["svgz"] = tree->get_theme_icon(SNAME("ImageTexture"), SNAME("EditorIcons"));
extension_guess["tga"] = tree->get_theme_icon(SNAME("ImageTexture"), SNAME("EditorIcons"));
extension_guess["webp"] = tree->get_theme_icon(SNAME("ImageTexture"), SNAME("EditorIcons"));
@@ -260,6 +215,7 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) {
ti->set_custom_color(0, tree->get_theme_color(SNAME("error_color"), SNAME("Editor")));
ti->set_tooltip(0, vformat(TTR("%s (already exists)"), res_path));
ti->set_checked(0, false);
+ ti->propagate_check(0);
} else {
ti->set_tooltip(0, res_path);
}
@@ -276,7 +232,7 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) {
asset_contents->set_text(vformat(TTR("Contents of asset \"%s\" - No files conflict with your project:"), asset_name));
}
- popup_centered_ratio();
+ popup_centered_ratio(0.5);
updating = false;
}
@@ -305,7 +261,7 @@ void EditorAssetInstaller::ok_pressed() {
String name = String::utf8(fname);
- if (status_map.has(name) && status_map[name]->is_checked(0)) {
+ if (status_map.has(name) && (status_map[name]->is_checked(0) || status_map[name]->is_indeterminate(0))) {
String path = status_map[name]->get_metadata(0);
if (path.is_empty()) { // a dir
@@ -393,6 +349,7 @@ EditorAssetInstaller::EditorAssetInstaller() {
tree = memnew(Tree);
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tree->connect("item_edited", callable_mp(this, &EditorAssetInstaller::_item_edited));
+ tree->connect("check_propagated_to_item", callable_mp(this, &EditorAssetInstaller::_check_propagated_to_item));
vb->add_child(tree);
error = memnew(AcceptDialog);
diff --git a/editor/editor_asset_installer.h b/editor/editor_asset_installer.h
index 2f59250933..f5993f73e7 100644
--- a/editor/editor_asset_installer.h
+++ b/editor/editor_asset_installer.h
@@ -43,9 +43,8 @@ class EditorAssetInstaller : public ConfirmationDialog {
AcceptDialog *error;
Map<String, TreeItem *> status_map;
bool updating;
- void _update_subitems(TreeItem *p_item, bool p_check, bool p_first = false);
- void _uncheck_parent(TreeItem *p_item);
void _item_edited();
+ void _check_propagated_to_item(Object *p_obj, int column);
virtual void ok_pressed() override;
protected:
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index 0829b9d24f..5e4e375db4 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -324,7 +324,7 @@ float EditorAudioBus::_normalized_volume_to_scaled_db(float normalized) {
/* There are three different formulas for the conversion from normalized
* values to relative decibal values.
* One formula is an exponential graph which intends to counteract
- * the logorithmic nature of human hearing. This is an approximation
+ * the logarithmic nature of human hearing. This is an approximation
* of the behaviour of a 'logarithmic potentiometer' found on most
* musical instruments and also emulated in popular software.
* The other two equations are hand-tuned linear tapers that intend to
@@ -1163,7 +1163,7 @@ void EditorAudioBuses::_server_save() {
}
void EditorAudioBuses::_select_layout() {
- EditorNode::get_singleton()->get_filesystem_dock()->select_file(edited_path);
+ FileSystemDock::get_singleton()->select_file(edited_path);
}
void EditorAudioBuses::_save_as_layout() {
diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp
index 1724e87489..d13d1a6c68 100644
--- a/editor/editor_command_palette.cpp
+++ b/editor/editor_command_palette.cpp
@@ -72,7 +72,7 @@ void EditorCommandPalette::_update_command_search(const String &search_text) {
r.shortcut_text = commands[r.key_name].shortcut;
r.last_used = commands[r.key_name].last_used;
- if (search_text.is_subsequence_ofi(r.display_name)) {
+ if (search_text.is_subsequence_ofn(r.display_name)) {
if (!search_text.is_empty()) {
r.score = _score_path(search_text, r.display_name.to_lower());
}
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 014e27ae15..792897e451 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -1501,36 +1501,12 @@ void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, in
}
String EditorExportPlatform::test_etc2() const {
- // String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
- // bool etc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc");
- // bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
- String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
- bool etc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc");
- bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
-
- if (driver == "opengl3" && !etc_supported) {
- return TTR("Target platform requires 'ETC' texture compression for OpenGL. Enable 'Import Etc' in Project Settings.");
- } else if (driver == "vulkan" && !etc2_supported) {
- // FIXME: Review if this is true for Vulkan.
- return TTR("Target platform requires 'ETC2' texture compression for Vulkan. Enable 'Import Etc 2' in Project Settings.");
- }
- return String();
-}
+ const bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
-String EditorExportPlatform::test_etc2_or_pvrtc() const {
- String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
- bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
- bool pvrtc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc");
- // String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
- // bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2");
- // bool pvrtc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc");
-
- if (driver == "opengl3" && !pvrtc_supported) {
- return TTR("Target platform requires 'PVRTC' texture compression for OpenGL. Enable 'Import Pvrtc' in Project Settings.");
- } else if (driver == "vulkan" && !etc2_supported && !pvrtc_supported) {
- // FIXME: Review if this is true for Vulkan.
- return TTR("Target platform requires 'ETC2' or 'PVRTC' texture compression for Vulkan. Enable 'Import Etc 2' or 'Import Pvrtc' in Project Settings.");
+ if (!etc2_supported) {
+ return TTR("Target platform requires 'ETC2' texture compression. Enable 'Import Etc 2' in Project Settings.");
}
+
return String();
}
@@ -1549,7 +1525,7 @@ void EditorExport::remove_export_preset(int p_idx) {
}
void EditorExport::add_export_plugin(const Ref<EditorExportPlugin> &p_plugin) {
- if (export_plugins.find(p_plugin) == -1) {
+ if (!export_plugins.has(p_plugin)) {
export_plugins.push_back(p_plugin);
}
}
diff --git a/editor/editor_export.h b/editor/editor_export.h
index 3d46ae1996..796fb12793 100644
--- a/editor/editor_export.h
+++ b/editor/editor_export.h
@@ -269,8 +269,7 @@ public:
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; }
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }
- String test_etc2() const; //generic test for etc2 since most platforms use it
- String test_etc2_or_pvrtc() const; // test for etc2 or pvrtc support for iOS
+ String test_etc2() const;
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const = 0;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const = 0;
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index dee00b6678..b6d8ea5bd6 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -52,6 +52,15 @@ EditorFileDialog::RegisterFunc EditorFileDialog::unregister_func = nullptr;
void EditorFileDialog::popup_file_dialog() {
popup_centered_clamped(Size2(1050, 700) * EDSCALE, 0.8);
+ _focus_file_text();
+}
+
+void EditorFileDialog::_focus_file_text() {
+ int lp = file->get_text().rfind(".");
+ if (lp != -1) {
+ file->select(0, lp);
+ file->grab_focus();
+ }
}
VBoxContainer *EditorFileDialog::get_vbox() {
@@ -121,6 +130,18 @@ void EditorFileDialog::_notification(int p_what) {
if (!is_visible()) {
set_process_unhandled_input(false);
}
+ } else if (p_what == NOTIFICATION_WM_WINDOW_FOCUS_IN) {
+ // Check if the current directory was removed externally (much less likely to happen while editor window is focused).
+ String previous_dir = get_current_dir();
+ while (!dir_access->dir_exists(get_current_dir())) {
+ _go_up();
+
+ // In case we can't go further up, use some fallback and break.
+ if (get_current_dir() == previous_dir) {
+ _dir_submitted(OS::get_singleton()->get_user_data_dir());
+ break;
+ }
+ }
}
}
@@ -209,7 +230,14 @@ Vector<String> EditorFileDialog::get_selected_files() const {
void EditorFileDialog::update_dir() {
if (drives->is_visible()) {
- drives->select(dir_access->get_current_drive());
+ if (dir_access->get_current_dir().is_network_share_path()) {
+ _update_drives(false);
+ drives->add_item(RTR("Network"));
+ drives->set_item_disabled(drives->get_item_count() - 1, true);
+ drives->select(drives->get_item_count() - 1);
+ } else {
+ drives->select(dir_access->get_current_drive());
+ }
}
dir->set_text(dir_access->get_current_dir(false));
@@ -974,11 +1002,7 @@ void EditorFileDialog::set_current_file(const String &p_file) {
file->set_text(p_file);
update_dir();
invalidate();
- int lp = p_file.rfind(".");
- if (lp != -1) {
- file->select(0, lp);
- file->grab_focus();
- }
+ _focus_file_text();
if (is_visible()) {
_request_single_thumbnail(get_current_dir().plus_file(get_current_file()));
@@ -1135,7 +1159,7 @@ void EditorFileDialog::_select_drive(int p_idx) {
_push_history();
}
-void EditorFileDialog::_update_drives() {
+void EditorFileDialog::_update_drives(bool p_select) {
int dc = dir_access->get_drive_count();
if (dc == 0 || access != ACCESS_FILESYSTEM) {
drives->hide();
@@ -1153,8 +1177,9 @@ void EditorFileDialog::_update_drives() {
String d = dir_access->get_drive(i);
drives->add_item(dir_access->get_drive(i));
}
-
- drives->select(dir_access->get_current_drive());
+ if (p_select) {
+ drives->select(dir_access->get_current_drive());
+ }
}
}
@@ -1301,7 +1326,7 @@ void EditorFileDialog::_recent_selected(int p_idx) {
}
void EditorFileDialog::_go_up() {
- dir_access->change_dir("..");
+ dir_access->change_dir(get_current_dir().get_base_dir());
update_file_list();
update_dir();
_push_history();
diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h
index b7abfe0836..6cfdf53780 100644
--- a/editor/editor_file_dialog.h
+++ b/editor/editor_file_dialog.h
@@ -149,6 +149,8 @@ private:
void update_file_list();
void update_filters();
+ void _focus_file_text();
+
void _update_favorites();
void _favorite_pressed();
void _favorite_selected(int p_idx);
@@ -178,7 +180,7 @@ private:
void _delete_items();
- void _update_drives();
+ void _update_drives(bool p_select = true);
void _go_up();
void _go_back();
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index a0673c8fb7..5beed352a6 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1604,6 +1604,10 @@ void EditorFileSystem::update_file(const String &p_file) {
_queue_update_script_classes();
}
+Set<String> EditorFileSystem::get_valid_extensions() const {
+ return valid_extensions;
+}
+
Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector<String> &p_files) {
String importer_name;
@@ -1644,7 +1648,7 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
config->get_section_keys("params", &sk);
for (const String &param : sk) {
Variant value = config->get_value("params", param);
- //override with whathever is in file
+ //override with whatever is in file
source_file_options[p_files[i]][param] = value;
}
}
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index ecc71e7d42..0c1bfbca47 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -272,6 +272,7 @@ public:
void scan();
void scan_changes();
void update_file(const String &p_file);
+ Set<String> get_valid_extensions() const;
EditorFileSystemDirectory *get_filesystem_path(const String &p_path);
String get_file_type(const String &p_file) const;
diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp
index d556255a8f..0c9a7b2972 100644
--- a/editor/editor_fonts.cpp
+++ b/editor/editor_fonts.cpp
@@ -269,7 +269,10 @@ void editor_register_fonts(Ref<Theme> p_theme) {
/* Hack */
- Ref<FontData> dfmono = load_cached_internal_font(_font_Hack_Regular, _font_Hack_Regular_size, font_hinting, font_antialiased, true);
+ Ref<FontData> dfmono = load_cached_internal_font(_font_JetBrainsMono_Regular, _font_JetBrainsMono_Regular_size, font_hinting, font_antialiased, true);
+ Dictionary opentype_features;
+ opentype_features["calt"] = 0;
+ dfmono->set_opentype_feature_overrides(opentype_features); // Disable contextual alternates (coding ligatures).
// Default font
MAKE_DEFAULT_FONT(df, String());
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 06e3a63f4a..bb76af8f9b 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -44,18 +44,25 @@
DocTools *EditorHelp::doc = nullptr;
-void EditorHelp::_init_colors() {
- title_color = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
- text_color = get_theme_color(SNAME("default_color"), SNAME("RichTextLabel"));
- headline_color = get_theme_color(SNAME("headline_color"), SNAME("EditorHelp"));
- base_type_color = title_color.lerp(text_color, 0.5);
- comment_color = text_color * Color(1, 1, 1, 0.6);
- symbol_color = comment_color;
- value_color = text_color * Color(1, 1, 1, 0.6);
- qualifier_color = text_color * Color(1, 1, 1, 0.8);
- type_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")).lerp(text_color, 0.5);
- class_desc->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4));
- class_desc->add_theme_constant_override("line_separation", Math::round(5 * EDSCALE));
+void EditorHelp::_update_theme() {
+ text_color = get_theme_color("text_color", "EditorHelp");
+ title_color = get_theme_color("title_color", "EditorHelp");
+ headline_color = get_theme_color("headline_color", "EditorHelp");
+ comment_color = get_theme_color("comment_color", "EditorHelp");
+ symbol_color = get_theme_color("symbol_color", "EditorHelp");
+ value_color = get_theme_color("value_color", "EditorHelp");
+ qualifier_color = get_theme_color("qualifier_color", "EditorHelp");
+ type_color = get_theme_color("type_color", "EditorHelp");
+
+ class_desc->add_theme_color_override("selection_color", get_theme_color("selection_color", "EditorHelp"));
+ class_desc->add_theme_constant_override("line_separation", get_theme_constant("line_separation", "EditorHelp"));
+ class_desc->add_theme_constant_override("table_hseparation", get_theme_constant("table_hseparation", "EditorHelp"));
+ class_desc->add_theme_constant_override("table_vseparation", get_theme_constant("table_vseparation", "EditorHelp"));
+
+ doc_font = get_theme_font(SNAME("doc"), SNAME("EditorFonts"));
+ doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts"));
+ doc_title_font = get_theme_font(SNAME("doc_title"), SNAME("EditorFonts"));
+ doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));
}
void EditorHelp::_search(bool p_search_previous) {
@@ -156,18 +163,21 @@ void EditorHelp::_class_desc_select(const String &p_select) {
void EditorHelp::_class_desc_input(const Ref<InputEvent> &p_input) {
}
-void EditorHelp::_class_desc_resized() {
+void EditorHelp::_class_desc_resized(bool p_force_update_theme) {
// Add extra horizontal margins for better readability.
// The margins increase as the width of the editor help container increases.
Ref<Font> doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));
int font_size = get_theme_font_size(SNAME("doc_source_size"), SNAME("EditorFonts"));
real_t char_width = doc_code_font->get_char_size('x', 0, font_size).width;
- const int display_margin = MAX(30 * EDSCALE, get_parent_anchorable_rect().size.width - char_width * 120 * EDSCALE) * 0.5;
-
- Ref<StyleBox> class_desc_stylebox = EditorNode::get_singleton()->get_theme_base()->get_theme_stylebox(SNAME("normal"), SNAME("RichTextLabel"))->duplicate();
- class_desc_stylebox->set_default_margin(SIDE_LEFT, display_margin);
- class_desc_stylebox->set_default_margin(SIDE_RIGHT, display_margin);
- class_desc->add_theme_style_override("normal", class_desc_stylebox);
+ const int new_display_margin = MAX(30 * EDSCALE, get_parent_anchorable_rect().size.width - char_width * 120 * EDSCALE) * 0.5;
+ if (display_margin != new_display_margin || p_force_update_theme) {
+ display_margin = new_display_margin;
+
+ Ref<StyleBox> class_desc_stylebox = EditorNode::get_singleton()->get_theme_base()->get_theme_stylebox(SNAME("normal"), SNAME("RichTextLabel"))->duplicate();
+ class_desc_stylebox->set_default_margin(SIDE_LEFT, display_margin);
+ class_desc_stylebox->set_default_margin(SIDE_RIGHT, display_margin);
+ class_desc->add_theme_style_override("normal", class_desc_stylebox);
+ }
}
void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
@@ -184,8 +194,7 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
t = p_enum.get_slice(".", 0);
}
}
- const Color text_color = get_theme_color(SNAME("default_color"), SNAME("RichTextLabel"));
- const Color type_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")).lerp(text_color, 0.5);
+
class_desc->push_color(type_color);
bool add_array = false;
if (can_ref) {
@@ -496,16 +505,11 @@ void EditorHelp::_update_doc() {
method_line.clear();
section_line.clear();
- _init_colors();
-
- DocData::ClassDoc cd = doc->class_list[edited_class]; //make a copy, so we can sort without worrying
-
- Ref<Font> doc_font = get_theme_font(SNAME("doc"), SNAME("EditorFonts"));
- Ref<Font> doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts"));
- Ref<Font> doc_title_font = get_theme_font(SNAME("doc_title"), SNAME("EditorFonts"));
- Ref<Font> doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts"));
+ _update_theme();
String link_color_text = title_color.to_html(false);
+ DocData::ClassDoc cd = doc->class_list[edited_class]; // Make a copy, so we can sort without worrying.
+
// Class name
section_line.push_back(Pair<String, int>(TTR("Top"), 0));
class_desc->push_font(doc_title_font);
@@ -1476,11 +1480,9 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
Ref<Font> doc_kbd_font = p_rt->get_theme_font(SNAME("doc_keyboard"), SNAME("EditorFonts"));
Color headline_color = p_rt->get_theme_color(SNAME("headline_color"), SNAME("EditorHelp"));
- Color accent_color = p_rt->get_theme_color(SNAME("accent_color"), SNAME("Editor"));
- Color property_color = p_rt->get_theme_color(SNAME("property_color"), SNAME("Editor"));
- Color link_color = accent_color.lerp(headline_color, 0.8);
- Color code_color = accent_color.lerp(headline_color, 0.6);
- Color kbd_color = accent_color.lerp(property_color, 0.6);
+ Color link_color = p_rt->get_theme_color(SNAME("link_color"), SNAME("EditorHelp"));
+ Color code_color = p_rt->get_theme_color(SNAME("code_color"), SNAME("EditorHelp"));
+ Color kbd_color = p_rt->get_theme_color(SNAME("kbd_color"), SNAME("EditorHelp"));
String bbcode = p_bbcode.dedent().replace("\t", "").replace("\r", "").strip_edges();
@@ -1600,24 +1602,28 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
pos = brk_pos + 1;
} else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ")) {
- int tag_end = tag.find(" ");
-
- String link_tag = tag.substr(0, tag_end);
- String link_target = tag.substr(tag_end + 1, tag.length()).lstrip(" ");
+ const int tag_end = tag.find(" ");
+ const String link_tag = tag.substr(0, tag_end);
+ const String link_target = tag.substr(tag_end + 1, tag.length()).lstrip(" ");
+ p_rt->push_font(doc_code_font);
p_rt->push_color(link_color);
p_rt->push_meta("@" + link_tag + " " + link_target);
p_rt->add_text(link_target + (tag.begins_with("method ") ? "()" : ""));
p_rt->pop();
p_rt->pop();
+ p_rt->pop();
pos = brk_end + 1;
} else if (doc->class_list.has(tag)) {
+ // Class reference tag such as [Node2D] or [SceneTree].
+ p_rt->push_font(doc_code_font);
p_rt->push_color(link_color);
p_rt->push_meta("#" + tag);
p_rt->add_text(tag);
p_rt->pop();
p_rt->pop();
+ p_rt->pop();
pos = brk_end + 1;
} else if (tag == "b") {
@@ -1766,7 +1772,7 @@ void EditorHelp::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
if (is_inside_tree()) {
- _class_desc_resized();
+ _class_desc_resized(true);
}
update_toggle_scripts_button();
} break;
@@ -1870,8 +1876,8 @@ EditorHelp::EditorHelp() {
class_desc->connect("meta_clicked", callable_mp(this, &EditorHelp::_class_desc_select));
class_desc->connect("gui_input", callable_mp(this, &EditorHelp::_class_desc_input));
- class_desc->connect("resized", callable_mp(this, &EditorHelp::_class_desc_resized));
- _class_desc_resized();
+ class_desc->connect("resized", callable_mp(this, &EditorHelp::_class_desc_resized), varray(false));
+ _class_desc_resized(false);
// Added second so it opens at the bottom so it won't offset the entire widget.
find_bar = memnew(FindBar);
@@ -1904,6 +1910,8 @@ DocTools *EditorHelp::get_doc_data() {
return doc;
}
+//// EditorHelpBit ///
+
void EditorHelpBit::_go_to_help(String p_what) {
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
ScriptEditor::get_singleton()->goto_help(p_what);
@@ -1941,16 +1949,13 @@ void EditorHelpBit::_bind_methods() {
void EditorHelpBit::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ rich_text->add_theme_color_override("selection_color", get_theme_color(SNAME("selection_color"), SNAME("EditorHelp")));
rich_text->clear();
_add_text_to_rt(text, rich_text);
-
- } break;
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- rich_text->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4));
+ rich_text->reset_size(); // Force recalculating size after parsing bbcode.
} break;
- default:
- break;
}
}
@@ -1964,11 +1969,13 @@ EditorHelpBit::EditorHelpBit() {
rich_text = memnew(RichTextLabel);
add_child(rich_text);
rich_text->connect("meta_clicked", callable_mp(this, &EditorHelpBit::_meta_clicked));
- rich_text->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4));
rich_text->set_override_selected_font_color(false);
- set_custom_minimum_size(Size2(0, 70 * EDSCALE));
+ rich_text->set_fit_content_height(true);
+ set_custom_minimum_size(Size2(0, 50 * EDSCALE));
}
+//// FindBar ///
+
FindBar::FindBar() {
search_text = memnew(LineEdit);
add_child(search_text);
@@ -2121,7 +2128,7 @@ void FindBar::unhandled_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event;
if (k.is_valid()) {
- if (k->is_pressed() && (rich_text_label->has_focus() || is_ancestor_of(get_focus_owner()))) {
+ if (k->is_pressed() && (rich_text_label->has_focus() || is_ancestor_of(get_viewport()->gui_get_focus_owner()))) {
bool accepted = true;
switch (k->get_keycode()) {
diff --git a/editor/editor_help.h b/editor/editor_help.h
index eb879c6d39..10281a764c 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -126,17 +126,21 @@ class EditorHelp : public VBoxContainer {
String base_path;
- Color title_color;
Color text_color;
+ Color title_color;
Color headline_color;
- Color base_type_color;
- Color type_color;
Color comment_color;
Color symbol_color;
Color value_color;
Color qualifier_color;
+ Color type_color;
+
+ Ref<Font> doc_font;
+ Ref<Font> doc_bold_font;
+ Ref<Font> doc_title_font;
+ Ref<Font> doc_code_font;
- void _init_colors();
+ void _update_theme();
void _help_callback(const String &p_topic);
void _add_text(const String &p_bbcode);
@@ -151,7 +155,8 @@ class EditorHelp : public VBoxContainer {
void _class_list_select(const String &p_select);
void _class_desc_select(const String &p_select);
void _class_desc_input(const Ref<InputEvent> &p_input);
- void _class_desc_resized();
+ void _class_desc_resized(bool p_force_update_theme);
+ int display_margin = 0;
Error _goto_desc(const String &p_class, int p_vscr = -1);
//void _update_history_buttons();
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 75e518e050..6f7508b10b 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -623,14 +623,17 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) {
if (property == "frame_coords" && (object->is_class("Sprite2D") || object->is_class("Sprite3D"))) {
Vector2i new_coords = object->get(property);
new_coords.x++;
- if (new_coords.x >= object->get("hframes").operator int64_t()) {
+ if (new_coords.x >= int64_t(object->get("hframes"))) {
new_coords.x = 0;
new_coords.y++;
}
-
- call_deferred(SNAME("emit_changed"), property, new_coords, "", false);
+ if (new_coords.x < int64_t(object->get("hframes")) && new_coords.y < int64_t(object->get("vframes"))) {
+ call_deferred(SNAME("emit_changed"), property, new_coords, "", false);
+ }
} else {
- call_deferred(SNAME("emit_changed"), property, object->get(property).operator int64_t() + 1, "", false);
+ if (int64_t(object->get(property)) + 1 < (int64_t(object->get("hframes")) * int64_t(object->get("vframes")))) {
+ call_deferred(SNAME("emit_changed"), property, object->get(property).operator int64_t() + 1, "", false);
+ }
}
call_deferred(SNAME("update_property"));
@@ -830,30 +833,42 @@ void EditorProperty::_update_pin_flags() {
}
}
-Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
- tooltip_text = p_text;
+static Control *make_help_bit(const String &p_text, bool p_property) {
EditorHelpBit *help_bit = memnew(EditorHelpBit);
- //help_bit->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel")));
help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE);
- String text;
PackedStringArray slices = p_text.split("::", false);
- if (!slices.is_empty()) {
- String property_name = slices[0].strip_edges();
- text = TTR("Property:") + " [u][b]" + property_name + "[/b][/u]";
+ if (slices.is_empty()) {
+ // Shouldn't happen here, but just in case pass the text along.
+ help_bit->set_text(p_text);
+ return help_bit;
+ }
- if (slices.size() > 1) {
- String property_doc = slices[1].strip_edges();
- if (property_name != property_doc) {
- text += "\n" + property_doc;
- }
+ String property_name = slices[0].strip_edges();
+ String text;
+ if (p_property) {
+ text = TTR("Property:") + " ";
+ }
+ text += "[u][b]" + property_name + "[/b][/u]";
+
+ if (slices.size() > 1) {
+ String property_doc = slices[1].strip_edges();
+ if (property_name != property_doc) {
+ text += "\n" + property_doc;
}
- help_bit->call_deferred(SNAME("set_text"), text); //hack so it uses proper theme once inside scene
+ } else {
+ text += "\n[i]" + TTR("No description.") + "[/i]";
}
+ help_bit->set_text(text);
return help_bit;
}
+Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
+ tooltip_text = p_text;
+ return make_help_bit(p_text, true);
+}
+
String EditorProperty::get_tooltip_text() const {
return tooltip_text;
}
@@ -861,10 +876,10 @@ String EditorProperty::get_tooltip_text() const {
void EditorProperty::menu_option(int p_option) {
switch (p_option) {
case MENU_COPY_PROPERTY: {
- EditorNode::get_singleton()->get_inspector()->set_property_clipboard(object->get(property));
+ InspectorDock::get_inspector_singleton()->set_property_clipboard(object->get(property));
} break;
case MENU_PASTE_PROPERTY: {
- emit_changed(property, EditorNode::get_singleton()->get_inspector()->get_property_clipboard());
+ emit_changed(property, InspectorDock::get_inspector_singleton()->get_property_clipboard());
} break;
case MENU_COPY_PROPERTY_PATH: {
DisplayServer::get_singleton()->clipboard_set(property);
@@ -916,6 +931,7 @@ void EditorProperty::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_warning"), "set_draw_warning", "is_draw_warning");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deletable"), "set_deletable", "is_deletable");
+
ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::PACKED_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value")));
ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING_NAME, "property")));
@@ -1090,25 +1106,7 @@ void EditorInspectorCategory::_notification(int p_what) {
Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) const {
tooltip_text = p_text;
- EditorHelpBit *help_bit = memnew(EditorHelpBit);
- help_bit->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel")));
- help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE);
-
- PackedStringArray slices = p_text.split("::", false);
- if (!slices.is_empty()) {
- String property_name = slices[0].strip_edges();
- String text = "[u][b]" + property_name + "[/b][/u]";
-
- if (slices.size() > 1) {
- String property_doc = slices[1].strip_edges();
- if (property_name != property_doc) {
- text += "\n" + property_doc;
- }
- }
- help_bit->call_deferred(SNAME("set_text"), text); //hack so it uses proper theme once inside scene
- }
-
- return help_bit;
+ return make_help_bit(p_text, false);
}
Size2 EditorInspectorCategory::get_minimum_size() const {
@@ -2301,7 +2299,7 @@ void EditorInspector::update_tree() {
if (property_focusable != -1) {
//check focusable is really focusable
bool restore_focus = false;
- Control *focused = get_focus_owner();
+ Control *focused = get_viewport() ? get_viewport()->gui_get_focus_owner() : nullptr;
if (focused) {
Node *parent = focused->get_parent();
while (parent) {
@@ -2592,7 +2590,7 @@ void EditorInspector::update_tree() {
// Ignore properties that do not fit the filter.
if (use_filter && !filter.is_empty()) {
- if (!filter.is_subsequence_ofi(path) && !filter.is_subsequence_ofi(property_label_string) && property_prefix.to_lower().find(filter.to_lower()) == -1) {
+ if (!filter.is_subsequence_ofn(path) && !filter.is_subsequence_ofn(property_label_string) && property_prefix.to_lower().find(filter.to_lower()) == -1) {
continue;
}
}
@@ -2905,6 +2903,7 @@ void EditorInspector::edit(Object *p_object) {
object->connect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback));
update_tree();
}
+ emit_signal("edited_object_changed");
}
void EditorInspector::set_keying(bool p_active) {
@@ -3539,10 +3538,11 @@ void EditorInspector::_bind_methods() {
ADD_SIGNAL(MethodInfo("property_selected", PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "advance")));
ADD_SIGNAL(MethodInfo("property_deleted", PropertyInfo(Variant::STRING, "property")));
- ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop")));
+ ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::STRING, "path")));
ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property")));
ADD_SIGNAL(MethodInfo("property_toggled", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::BOOL, "checked")));
+ ADD_SIGNAL(MethodInfo("edited_object_changed"));
ADD_SIGNAL(MethodInfo("restart_requested"));
}
diff --git a/editor/editor_locale_dialog.cpp b/editor/editor_locale_dialog.cpp
new file mode 100644
index 0000000000..5c4ece7065
--- /dev/null
+++ b/editor/editor_locale_dialog.cpp
@@ -0,0 +1,563 @@
+/*************************************************************************/
+/* editor_locale_dialog.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "editor_locale_dialog.h"
+
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "scene/gui/check_button.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/tree.h"
+
+static _FORCE_INLINE_ bool is_upper_case(char32_t c) {
+ return (c >= 'A' && c <= 'Z');
+}
+
+static _FORCE_INLINE_ bool is_lower_case(char32_t c) {
+ return (c >= 'a' && c <= 'z');
+}
+
+void EditorLocaleDialog::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("locale_selected", PropertyInfo(Variant::STRING, "locale")));
+}
+
+void EditorLocaleDialog::ok_pressed() {
+ if (edit_filters->is_pressed()) {
+ return; // Do not update, if in filter edit mode.
+ }
+
+ String locale;
+ if (lang_code->get_text().is_empty()) {
+ return; // Language code is required.
+ }
+ locale = lang_code->get_text();
+
+ if (!script_code->get_text().is_empty()) {
+ locale += "_" + script_code->get_text();
+ }
+ if (!country_code->get_text().is_empty()) {
+ locale += "_" + country_code->get_text();
+ }
+ if (!variant_code->get_text().is_empty()) {
+ locale += "_" + variant_code->get_text();
+ }
+
+ emit_signal(SNAME("locale_selected"), TranslationServer::get_singleton()->standardize_locale(locale));
+ hide();
+}
+
+void EditorLocaleDialog::_item_selected() {
+ if (updating_lists) {
+ return;
+ }
+
+ if (edit_filters->is_pressed()) {
+ return; // Do not update, if in filter edit mode.
+ }
+
+ TreeItem *l = lang_list->get_selected();
+ if (l) {
+ lang_code->set_text(l->get_metadata(0).operator String());
+ }
+
+ TreeItem *s = script_list->get_selected();
+ if (s) {
+ script_code->set_text(s->get_metadata(0).operator String());
+ }
+
+ TreeItem *c = cnt_list->get_selected();
+ if (c) {
+ country_code->set_text(c->get_metadata(0).operator String());
+ }
+}
+
+void EditorLocaleDialog::_toggle_advanced(bool p_checked) {
+ if (!p_checked) {
+ script_code->set_text("");
+ variant_code->set_text("");
+ }
+ _update_tree();
+}
+
+void EditorLocaleDialog::_post_popup() {
+ ConfirmationDialog::_post_popup();
+
+ if (!locale_set) {
+ lang_code->set_text("");
+ script_code->set_text("");
+ country_code->set_text("");
+ variant_code->set_text("");
+ }
+ edit_filters->set_pressed(false);
+ _update_tree();
+}
+
+void EditorLocaleDialog::_filter_lang_option_changed() {
+ TreeItem *t = lang_list->get_edited();
+ String lang = t->get_metadata(0);
+ bool checked = t->is_checked(0);
+
+ Variant prev;
+ Array f_lang_all;
+
+ if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/language_filter")) {
+ f_lang_all = ProjectSettings::get_singleton()->get("internationalization/locale/language_filter");
+ prev = f_lang_all;
+ }
+
+ int l_idx = f_lang_all.find(lang);
+
+ if (checked) {
+ if (l_idx == -1) {
+ f_lang_all.append(lang);
+ }
+ } else {
+ if (l_idx != -1) {
+ f_lang_all.remove_at(l_idx);
+ }
+ }
+
+ f_lang_all.sort();
+
+ undo_redo->create_action(TTR("Changed Locale Language Filter"));
+ undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/language_filter", f_lang_all);
+ undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/language_filter", prev);
+ undo_redo->commit_action();
+}
+
+void EditorLocaleDialog::_filter_script_option_changed() {
+ TreeItem *t = script_list->get_edited();
+ String script = t->get_metadata(0);
+ bool checked = t->is_checked(0);
+
+ Variant prev;
+ Array f_script_all;
+
+ if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/script_filter")) {
+ f_script_all = ProjectSettings::get_singleton()->get("internationalization/locale/script_filter");
+ prev = f_script_all;
+ }
+
+ int l_idx = f_script_all.find(script);
+
+ if (checked) {
+ if (l_idx == -1) {
+ f_script_all.append(script);
+ }
+ } else {
+ if (l_idx != -1) {
+ f_script_all.remove_at(l_idx);
+ }
+ }
+
+ f_script_all.sort();
+
+ undo_redo->create_action(TTR("Changed Locale Script Filter"));
+ undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/script_filter", f_script_all);
+ undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/script_filter", prev);
+ undo_redo->commit_action();
+}
+
+void EditorLocaleDialog::_filter_cnt_option_changed() {
+ TreeItem *t = cnt_list->get_edited();
+ String cnt = t->get_metadata(0);
+ bool checked = t->is_checked(0);
+
+ Variant prev;
+ Array f_cnt_all;
+
+ if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/country_filter")) {
+ f_cnt_all = ProjectSettings::get_singleton()->get("internationalization/locale/country_filter");
+ prev = f_cnt_all;
+ }
+
+ int l_idx = f_cnt_all.find(cnt);
+
+ if (checked) {
+ if (l_idx == -1) {
+ f_cnt_all.append(cnt);
+ }
+ } else {
+ if (l_idx != -1) {
+ f_cnt_all.remove_at(l_idx);
+ }
+ }
+
+ f_cnt_all.sort();
+
+ undo_redo->create_action(TTR("Changed Locale Country Filter"));
+ undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/country_filter", f_cnt_all);
+ undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/country_filter", prev);
+ undo_redo->commit_action();
+}
+
+void EditorLocaleDialog::_filter_mode_changed(int p_mode) {
+ int f_mode = filter_mode->get_selected_id();
+ Variant prev;
+
+ if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter_mode")) {
+ prev = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter_mode");
+ }
+
+ undo_redo->create_action(TTR("Changed Locale Filter Mode"));
+ undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter_mode", f_mode);
+ undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter_mode", prev);
+ undo_redo->commit_action();
+
+ _update_tree();
+}
+
+void EditorLocaleDialog::_edit_filters(bool p_checked) {
+ _update_tree();
+}
+
+void EditorLocaleDialog::_update_tree() {
+ updating_lists = true;
+
+ int filter = SHOW_ALL_LOCALES;
+ if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter_mode")) {
+ filter = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter_mode");
+ }
+ Array f_lang_all;
+ if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/language_filter")) {
+ f_lang_all = ProjectSettings::get_singleton()->get("internationalization/locale/language_filter");
+ }
+ Array f_cnt_all;
+ if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/country_filter")) {
+ f_cnt_all = ProjectSettings::get_singleton()->get("internationalization/locale/country_filter");
+ }
+ Array f_script_all;
+ if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/script_filter")) {
+ f_script_all = ProjectSettings::get_singleton()->get("internationalization/locale/script_filter");
+ }
+ bool is_edit_mode = edit_filters->is_pressed();
+
+ filter_mode->select(filter);
+
+ // Hide text advanced edit and disable OK button if in filter edit mode.
+ advanced->set_visible(!is_edit_mode);
+ hb_locale->set_visible(!is_edit_mode && advanced->is_pressed());
+ vb_script_list->set_visible(advanced->is_pressed());
+ get_ok_button()->set_disabled(is_edit_mode);
+
+ // Update language list.
+ lang_list->clear();
+ TreeItem *l_root = lang_list->create_item(nullptr);
+ lang_list->set_hide_root(true);
+
+ Vector<String> languages = TranslationServer::get_singleton()->get_all_languages();
+ for (const String &E : languages) {
+ if (is_edit_mode || (filter == SHOW_ALL_LOCALES) || f_lang_all.has(E) || f_lang_all.is_empty()) {
+ const String &lang = TranslationServer::get_singleton()->get_language_name(E);
+ TreeItem *t = lang_list->create_item(l_root);
+ if (is_edit_mode) {
+ t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ t->set_editable(0, true);
+ t->set_checked(0, f_lang_all.has(E));
+ } else if (lang_code->get_text() == E) {
+ t->select(0);
+ }
+ t->set_text(0, vformat("%s [%s]", lang, E));
+ t->set_metadata(0, E);
+ }
+ }
+
+ // Update script list.
+ script_list->clear();
+ TreeItem *s_root = script_list->create_item(nullptr);
+ script_list->set_hide_root(true);
+
+ if (!is_edit_mode) {
+ TreeItem *t = script_list->create_item(s_root);
+ t->set_text(0, "[Default]");
+ t->set_metadata(0, "");
+ }
+
+ Vector<String> scripts = TranslationServer::get_singleton()->get_all_scripts();
+ for (const String &E : scripts) {
+ if (is_edit_mode || (filter == SHOW_ALL_LOCALES) || f_script_all.has(E) || f_script_all.is_empty()) {
+ const String &script = TranslationServer::get_singleton()->get_script_name(E);
+ TreeItem *t = script_list->create_item(s_root);
+ if (is_edit_mode) {
+ t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ t->set_editable(0, true);
+ t->set_checked(0, f_script_all.has(E));
+ } else if (script_code->get_text() == E) {
+ t->select(0);
+ }
+ t->set_text(0, vformat("%s [%s]", script, E));
+ t->set_metadata(0, E);
+ }
+ }
+
+ // Update country list.
+ cnt_list->clear();
+ TreeItem *c_root = cnt_list->create_item(nullptr);
+ cnt_list->set_hide_root(true);
+
+ if (!is_edit_mode) {
+ TreeItem *t = cnt_list->create_item(c_root);
+ t->set_text(0, "[Default]");
+ t->set_metadata(0, "");
+ }
+
+ Vector<String> countries = TranslationServer::get_singleton()->get_all_countries();
+ for (const String &E : countries) {
+ if (is_edit_mode || (filter == SHOW_ALL_LOCALES) || f_cnt_all.has(E) || f_cnt_all.is_empty()) {
+ const String &cnt = TranslationServer::get_singleton()->get_country_name(E);
+ TreeItem *t = cnt_list->create_item(c_root);
+ if (is_edit_mode) {
+ t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ t->set_editable(0, true);
+ t->set_checked(0, f_cnt_all.has(E));
+ } else if (country_code->get_text() == E) {
+ t->select(0);
+ }
+ t->set_text(0, vformat("%s [%s]", cnt, E));
+ t->set_metadata(0, E);
+ }
+ }
+ updating_lists = false;
+}
+
+void EditorLocaleDialog::set_locale(const String &p_locale) {
+ const String &locale = TranslationServer::get_singleton()->standardize_locale(p_locale);
+ if (locale.is_empty()) {
+ locale_set = false;
+
+ lang_code->set_text("");
+ script_code->set_text("");
+ country_code->set_text("");
+ variant_code->set_text("");
+ } else {
+ locale_set = true;
+
+ Vector<String> locale_elements = p_locale.split("_");
+ lang_code->set_text(locale_elements[0]);
+ if (locale_elements.size() >= 2) {
+ if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) {
+ script_code->set_text(locale_elements[1]);
+ advanced->set_pressed(true);
+ }
+ if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) {
+ country_code->set_text(locale_elements[1]);
+ }
+ }
+ if (locale_elements.size() >= 3) {
+ if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) {
+ country_code->set_text(locale_elements[2]);
+ } else {
+ variant_code->set_text(locale_elements[2].to_lower());
+ advanced->set_pressed(true);
+ }
+ }
+ if (locale_elements.size() >= 4) {
+ variant_code->set_text(locale_elements[3].to_lower());
+ advanced->set_pressed(true);
+ }
+ }
+}
+
+void EditorLocaleDialog::popup_locale_dialog() {
+ popup_centered_clamped(Size2(1050, 700) * EDSCALE, 0.8);
+}
+
+EditorLocaleDialog::EditorLocaleDialog() {
+ undo_redo = EditorNode::get_undo_redo();
+
+ set_title(TTR("Select a Locale"));
+
+ VBoxContainer *vb = memnew(VBoxContainer);
+ {
+ HBoxContainer *hb_filter = memnew(HBoxContainer);
+ {
+ filter_mode = memnew(OptionButton);
+ filter_mode->add_item(TTR("Show All Locales"), SHOW_ALL_LOCALES);
+ filter_mode->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ filter_mode->add_item(TTR("Show Selected Locales Only"), SHOW_ONLY_SELECTED_LOCALES);
+ filter_mode->select(0);
+ filter_mode->connect("item_selected", callable_mp(this, &EditorLocaleDialog::_filter_mode_changed));
+ hb_filter->add_child(filter_mode);
+ }
+ {
+ edit_filters = memnew(CheckButton);
+ edit_filters->set_text("Edit Filters");
+ edit_filters->set_toggle_mode(true);
+ edit_filters->set_pressed(false);
+ edit_filters->connect("toggled", callable_mp(this, &EditorLocaleDialog::_edit_filters));
+ hb_filter->add_child(edit_filters);
+ }
+ {
+ advanced = memnew(CheckButton);
+ advanced->set_text("Advanced");
+ advanced->set_toggle_mode(true);
+ advanced->set_pressed(false);
+ advanced->connect("toggled", callable_mp(this, &EditorLocaleDialog::_toggle_advanced));
+ hb_filter->add_child(advanced);
+ }
+ vb->add_child(hb_filter);
+ }
+ {
+ HBoxContainer *hb_lists = memnew(HBoxContainer);
+ hb_lists->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ {
+ VBoxContainer *vb_lang_list = memnew(VBoxContainer);
+ vb_lang_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ {
+ Label *lang_lbl = memnew(Label);
+ lang_lbl->set_text(TTR("Language:"));
+ vb_lang_list->add_child(lang_lbl);
+ }
+ {
+ lang_list = memnew(Tree);
+ lang_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ lang_list->connect("cell_selected", callable_mp(this, &EditorLocaleDialog::_item_selected));
+ lang_list->set_columns(1);
+ lang_list->connect("item_edited", callable_mp(this, &EditorLocaleDialog::_filter_lang_option_changed));
+ vb_lang_list->add_child(lang_list);
+ }
+ hb_lists->add_child(vb_lang_list);
+ }
+ {
+ vb_script_list = memnew(VBoxContainer);
+ vb_script_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ {
+ Label *script_lbl = memnew(Label);
+ script_lbl->set_text(TTR("Script:"));
+ vb_script_list->add_child(script_lbl);
+ }
+ {
+ script_list = memnew(Tree);
+ script_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ script_list->connect("cell_selected", callable_mp(this, &EditorLocaleDialog::_item_selected));
+ script_list->set_columns(1);
+ script_list->connect("item_edited", callable_mp(this, &EditorLocaleDialog::_filter_script_option_changed));
+ vb_script_list->add_child(script_list);
+ }
+ hb_lists->add_child(vb_script_list);
+ }
+ {
+ VBoxContainer *vb_cnt_list = memnew(VBoxContainer);
+ vb_cnt_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ {
+ Label *cnt_lbl = memnew(Label);
+ cnt_lbl->set_text(TTR("Country:"));
+ vb_cnt_list->add_child(cnt_lbl);
+ }
+ {
+ cnt_list = memnew(Tree);
+ cnt_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ cnt_list->connect("cell_selected", callable_mp(this, &EditorLocaleDialog::_item_selected));
+ cnt_list->set_columns(1);
+ cnt_list->connect("item_edited", callable_mp(this, &EditorLocaleDialog::_filter_cnt_option_changed));
+ vb_cnt_list->add_child(cnt_list);
+ }
+ hb_lists->add_child(vb_cnt_list);
+ }
+ vb->add_child(hb_lists);
+ }
+ {
+ hb_locale = memnew(HBoxContainer);
+ hb_locale->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ {
+ {
+ VBoxContainer *vb_language = memnew(VBoxContainer);
+ vb_language->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ {
+ Label *language_lbl = memnew(Label);
+ language_lbl->set_text(TTR("Language"));
+ vb_language->add_child(language_lbl);
+ }
+ {
+ lang_code = memnew(LineEdit);
+ lang_code->set_max_length(3);
+ lang_code->set_tooltip("Language");
+ vb_language->add_child(lang_code);
+ }
+ hb_locale->add_child(vb_language);
+ }
+ {
+ VBoxContainer *vb_script = memnew(VBoxContainer);
+ vb_script->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ {
+ Label *script_lbl = memnew(Label);
+ script_lbl->set_text(TTR("Script"));
+ vb_script->add_child(script_lbl);
+ }
+ {
+ script_code = memnew(LineEdit);
+ script_code->set_max_length(4);
+ script_code->set_tooltip("Script");
+ vb_script->add_child(script_code);
+ }
+ hb_locale->add_child(vb_script);
+ }
+ {
+ VBoxContainer *vb_country = memnew(VBoxContainer);
+ vb_country->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ {
+ Label *country_lbl = memnew(Label);
+ country_lbl->set_text(TTR("Country"));
+ vb_country->add_child(country_lbl);
+ }
+ {
+ country_code = memnew(LineEdit);
+ country_code->set_max_length(2);
+ country_code->set_tooltip("Country");
+ vb_country->add_child(country_code);
+ }
+ hb_locale->add_child(vb_country);
+ }
+ {
+ VBoxContainer *vb_variant = memnew(VBoxContainer);
+ vb_variant->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ {
+ Label *variant_lbl = memnew(Label);
+ variant_lbl->set_text(TTR("Variant"));
+ vb_variant->add_child(variant_lbl);
+ }
+ {
+ variant_code = memnew(LineEdit);
+ variant_code->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ variant_code->set_placeholder("Variant");
+ variant_code->set_tooltip("Variant");
+ vb_variant->add_child(variant_code);
+ }
+ hb_locale->add_child(vb_variant);
+ }
+ }
+ vb->add_child(hb_locale);
+ }
+ add_child(vb);
+ _update_tree();
+
+ get_ok_button()->set_text(TTR("Select"));
+}
diff --git a/modules/pvr/register_types.h b/editor/editor_locale_dialog.h
index faefb98678..7a4828e83a 100644
--- a/modules/pvr/register_types.h
+++ b/editor/editor_locale_dialog.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* register_types.h */
+/* editor_locale_dialog.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,10 +28,66 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PVR_REGISTER_TYPES_H
-#define PVR_REGISTER_TYPES_H
+#ifndef EDITOR_LOCALE_DIALOG_H
+#define EDITOR_LOCALE_DIALOG_H
-void register_pvr_types();
-void unregister_pvr_types();
+#include "core/string/translation.h"
+#include "scene/gui/dialogs.h"
-#endif // PVR_REGISTER_TYPES_H
+class Button;
+class HBoxContainer;
+class VBoxContainer;
+class LineEdit;
+class Tree;
+class OptionButton;
+class UndoRedo;
+
+class EditorLocaleDialog : public ConfirmationDialog {
+ GDCLASS(EditorLocaleDialog, ConfirmationDialog);
+
+ enum LocaleFilter {
+ SHOW_ALL_LOCALES,
+ SHOW_ONLY_SELECTED_LOCALES,
+ };
+
+ HBoxContainer *hb_locale = nullptr;
+ VBoxContainer *vb_script_list = nullptr;
+ OptionButton *filter_mode = nullptr;
+ Button *edit_filters = nullptr;
+ Button *advanced = nullptr;
+ LineEdit *lang_code = nullptr;
+ LineEdit *script_code = nullptr;
+ LineEdit *country_code = nullptr;
+ LineEdit *variant_code = nullptr;
+ Tree *lang_list = nullptr;
+ Tree *script_list = nullptr;
+ Tree *cnt_list = nullptr;
+
+ UndoRedo *undo_redo = nullptr;
+
+ bool locale_set = false;
+ bool updating_lists = false;
+
+protected:
+ static void _bind_methods();
+ virtual void _post_popup() override;
+ virtual void ok_pressed() override;
+
+ void _item_selected();
+ void _filter_lang_option_changed();
+ void _filter_script_option_changed();
+ void _filter_cnt_option_changed();
+ void _filter_mode_changed(int p_mode);
+ void _edit_filters(bool p_checked);
+ void _toggle_advanced(bool p_checked);
+
+ void _update_tree();
+
+public:
+ EditorLocaleDialog();
+
+ void set_locale(const String &p_locale);
+ void popup_locale_dialog();
+};
+
+#endif // EDITOR_LOCALE_DIALOG_H
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 04bfcfac95..db4de3bed0 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -84,6 +84,7 @@ void EditorLog::_update_theme() {
copy_button->set_icon(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")));
collapse_button->set_icon(get_theme_icon(SNAME("CombineLines"), SNAME("EditorIcons")));
show_search_button->set_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
}
void EditorLog::_notification(int p_what) {
@@ -348,7 +349,6 @@ EditorLog::EditorLog() {
search_box = memnew(LineEdit);
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search_box->set_placeholder(TTR("Filter messages"));
- search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
search_box->set_clear_button_enabled(true);
search_box->set_visible(true);
search_box->connect("text_changed", callable_mp(this, &EditorLog::_search_changed));
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index a8cb2e791c..076a774efa 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -93,6 +93,7 @@
#include "editor/editor_run_script.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/editor_settings_dialog.h"
#include "editor/editor_spin_slider.h"
#include "editor/editor_themes.h"
#include "editor/editor_toaster.h"
@@ -190,7 +191,6 @@
#include "editor/project_settings_editor.h"
#include "editor/quick_open.h"
#include "editor/register_exporters.h"
-#include "editor/settings_config_dialog.h"
#include <stdio.h>
#include <stdlib.h>
@@ -340,7 +340,7 @@ void EditorNode::_update_scene_tabs() {
}
if (show_rb && editor_data.get_scene_root_script(i).is_valid()) {
- scene_tabs->set_tab_right_button(i, script_icon);
+ scene_tabs->set_tab_button_icon(i, script_icon);
}
}
@@ -429,7 +429,7 @@ void EditorNode::unhandled_input(const Ref<InputEvent> &p_event) {
_scene_tab_changed(next_tab);
}
if (ED_IS_SHORTCUT("editor/filter_files", p_event)) {
- filesystem_dock->focus_on_filter();
+ FileSystemDock::get_singleton()->focus_on_filter();
}
if (ED_IS_SHORTCUT("editor/editor_2d", p_event)) {
@@ -822,8 +822,8 @@ void EditorNode::_on_plugin_ready(Object *p_script, const String &p_activate_nam
if (p_activate_name.length()) {
set_addon_plugin_enabled(p_activate_name, true);
}
- project_settings->update_plugins();
- project_settings->hide();
+ project_settings_editor->update_plugins();
+ project_settings_editor->hide();
push_item(script.operator->());
}
@@ -957,9 +957,10 @@ void EditorNode::_fs_changed() {
if (!export_error.is_empty()) {
ERR_PRINT(export_error);
- OS::get_singleton()->set_exit_code(EXIT_FAILURE);
+ _exit_editor(EXIT_FAILURE);
+ } else {
+ _exit_editor(EXIT_SUCCESS);
}
- _exit_editor();
}
}
@@ -1099,8 +1100,8 @@ void EditorNode::_version_button_pressed() {
}
void EditorNode::_node_renamed() {
- if (get_inspector()) {
- get_inspector()->update_tree();
+ if (InspectorDock::get_inspector_singleton()) {
+ InspectorDock::get_inspector_singleton()->update_tree();
}
}
@@ -1161,7 +1162,7 @@ Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_d
return ERR_FILE_MISSING_DEPENDENCIES;
}
- inspector_dock->edit_resource(res);
+ InspectorDock::get_singleton()->edit_resource(res);
return OK;
}
@@ -1775,7 +1776,7 @@ void EditorNode::restart_editor() {
to_reopen = get_tree()->get_edited_scene_root()->get_scene_file_path();
}
- _exit_editor();
+ _exit_editor(EXIT_SUCCESS);
List<String> args;
args.push_back("--path");
@@ -2069,10 +2070,10 @@ void EditorNode::edit_item(Object *p_object) {
void EditorNode::push_item(Object *p_object, const String &p_property, bool p_inspector_only) {
if (!p_object) {
- get_inspector()->edit(nullptr);
- node_dock->set_node(nullptr);
- scene_tree_dock->set_selected(nullptr);
- inspector_dock->update(nullptr);
+ InspectorDock::get_inspector_singleton()->edit(nullptr);
+ NodeDock::get_singleton()->set_node(nullptr);
+ SceneTreeDock::get_singleton()->set_selected(nullptr);
+ InspectorDock::get_singleton()->update(nullptr);
_display_top_editors(false);
return;
}
@@ -2145,17 +2146,17 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
this->current = current_obj;
if (!current_obj) {
- scene_tree_dock->set_selected(nullptr);
- get_inspector()->edit(nullptr);
- node_dock->set_node(nullptr);
- inspector_dock->update(nullptr);
+ SceneTreeDock::get_singleton()->set_selected(nullptr);
+ InspectorDock::get_inspector_singleton()->edit(nullptr);
+ NodeDock::get_singleton()->set_node(nullptr);
+ InspectorDock::get_singleton()->update(nullptr);
_display_top_editors(false);
return;
}
- Object *prev_inspected_object = get_inspector()->get_edited_object();
+ Object *prev_inspected_object = InspectorDock::get_inspector_singleton()->get_edited_object();
bool disable_folding = bool(EDITOR_GET("interface/inspector/disable_folding"));
bool is_resource = current_obj->is_class("Resource");
@@ -2166,11 +2167,11 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
if (is_resource) {
Resource *current_res = Object::cast_to<Resource>(current_obj);
ERR_FAIL_COND(!current_res);
- get_inspector()->edit(current_res);
- scene_tree_dock->set_selected(nullptr);
- node_dock->set_node(nullptr);
- inspector_dock->update(nullptr);
- EditorNode::get_singleton()->get_import_dock()->set_edit_path(current_res->get_path());
+ InspectorDock::get_inspector_singleton()->edit(current_res);
+ SceneTreeDock::get_singleton()->set_selected(nullptr);
+ NodeDock::get_singleton()->set_node(nullptr);
+ InspectorDock::get_singleton()->update(nullptr);
+ ImportDock::get_singleton()->set_edit_path(current_res->get_path());
int subr_idx = current_res->get_path().find("::");
if (subr_idx != -1) {
@@ -2191,15 +2192,15 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
Node *current_node = Object::cast_to<Node>(current_obj);
ERR_FAIL_COND(!current_node);
- get_inspector()->edit(current_node);
+ InspectorDock::get_inspector_singleton()->edit(current_node);
if (current_node->is_inside_tree()) {
- node_dock->set_node(current_node);
- scene_tree_dock->set_selected(current_node);
- inspector_dock->update(current_node);
+ NodeDock::get_singleton()->set_node(current_node);
+ SceneTreeDock::get_singleton()->set_selected(current_node);
+ InspectorDock::get_singleton()->update(current_node);
} else {
- node_dock->set_node(nullptr);
- scene_tree_dock->set_selected(nullptr);
- inspector_dock->update(nullptr);
+ NodeDock::get_singleton()->set_node(nullptr);
+ SceneTreeDock::get_singleton()->set_selected(nullptr);
+ InspectorDock::get_singleton()->update(nullptr);
}
if (get_edited_scene() && !get_edited_scene()->get_scene_file_path().is_empty()) {
@@ -2237,21 +2238,21 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
}
}
- get_inspector()->edit(current_obj);
- node_dock->set_node(nullptr);
- scene_tree_dock->set_selected(selected_node);
- inspector_dock->update(nullptr);
+ InspectorDock::get_inspector_singleton()->edit(current_obj);
+ NodeDock::get_singleton()->set_node(nullptr);
+ SceneTreeDock::get_singleton()->set_selected(selected_node);
+ InspectorDock::get_singleton()->update(nullptr);
}
if (current_obj == prev_inspected_object) {
// Make sure inspected properties are restored.
- get_inspector()->update_tree();
+ InspectorDock::get_inspector_singleton()->update_tree();
}
- inspector_dock->set_warning(editable_warning);
+ InspectorDock::get_singleton()->set_warning(editable_warning);
- if (get_inspector()->is_using_folding() == disable_folding) {
- get_inspector()->set_use_folding(!disable_folding);
+ if (InspectorDock::get_inspector_singleton()->is_using_folding() == disable_folding) {
+ InspectorDock::get_inspector_singleton()->set_use_folding(!disable_folding);
}
/* Take care of PLUGIN EDITOR */
@@ -2305,8 +2306,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
}
}
- inspector_dock->update(current_obj);
- inspector_dock->update_keying();
+ InspectorDock::get_singleton()->update(current_obj);
}
void EditorNode::_run(bool p_current, const String &p_custom) {
@@ -2593,13 +2593,21 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
file->set_current_path(path.replacen("." + ext, "." + extensions.front()->get()));
}
}
- } else {
- String existing;
- if (extensions.size()) {
- String root_name(scene->get_name());
- existing = root_name + "." + extensions.front()->get().to_lower();
+ } else if (extensions.size()) {
+ String root_name = scene->get_name();
+ // Very similar to node naming logic.
+ switch (ProjectSettings::get_singleton()->get("editor/scene/scene_naming").operator int()) {
+ case SCENE_NAME_CASING_AUTO:
+ // Use casing of the root node.
+ break;
+ case SCENE_NAME_CASING_PASCAL_CASE: {
+ root_name = root_name.capitalize().replace(" ", "");
+ } break;
+ case SCENE_NAME_CASING_SNAKE_CASE:
+ root_name = root_name.capitalize().replace(" ", "").replace("-", "_").camelcase_to_underscore();
+ break;
}
- file->set_current_path(existing);
+ file->set_current_path(root_name + "." + extensions.front()->get().to_lower());
}
file->popup_file_dialog();
file->set_title(TTR("Save Scene As..."));
@@ -2756,7 +2764,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case FILE_SHOW_IN_FILESYSTEM: {
String path = editor_data.get_scene_path(editor_data.get_edited_scene());
if (!path.is_empty()) {
- filesystem_dock->navigate_to_path(path);
+ FileSystemDock::get_singleton()->navigate_to_path(path);
}
} break;
@@ -2765,7 +2773,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
} break;
case RUN_SETTINGS: {
- project_settings->popup_project_settings();
+ project_settings_editor->popup_project_settings();
} break;
case FILE_INSTALL_ANDROID_SOURCE: {
if (p_confirmed) {
@@ -2838,7 +2846,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
_update_update_spinner();
} break;
case SETTINGS_PREFERENCES: {
- settings_config_dialog->popup_edit_settings();
+ editor_settings_dialog->popup_edit_settings();
} break;
case SETTINGS_EDITOR_DATA_FOLDER: {
OS::get_singleton()->shell_open(String("file://") + EditorPaths::get_singleton()->get_data_dir());
@@ -2861,11 +2869,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
DisplayServer::get_singleton()->window_set_mode(DisplayServer::get_singleton()->window_get_mode() == DisplayServer::WINDOW_MODE_FULLSCREEN ? DisplayServer::WINDOW_MODE_WINDOWED : DisplayServer::WINDOW_MODE_FULLSCREEN);
} break;
- case SETTINGS_TOGGLE_CONSOLE: {
- bool was_visible = DisplayServer::get_singleton()->is_console_visible();
- DisplayServer::get_singleton()->console_set_visible(!was_visible);
- EditorSettings::get_singleton()->set_setting("interface/editor/hide_console_window", was_visible);
- } break;
case EDITOR_SCREENSHOT: {
screenshot_timer->start();
} break;
@@ -2997,7 +3000,7 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) {
return -1;
}
-void EditorNode::_exit_editor() {
+void EditorNode::_exit_editor(int p_exit_code) {
exiting = true;
resource_preview->stop(); // stop early to avoid crashes
_save_docks();
@@ -3005,7 +3008,7 @@ void EditorNode::_exit_editor() {
// Dim the editor window while it's quitting to make it clearer that it's busy
dim_editor(true);
- get_tree()->quit();
+ get_tree()->quit(p_exit_code);
}
void EditorNode::_discard_changes(const String &p_str) {
@@ -3055,12 +3058,12 @@ void EditorNode::_discard_changes(const String &p_str) {
} break;
case FILE_QUIT: {
_menu_option_confirm(RUN_STOP, true);
- _exit_editor();
+ _exit_editor(EXIT_SUCCESS);
} break;
case RUN_PROJECT_MANAGER: {
_menu_option_confirm(RUN_STOP, true);
- _exit_editor();
+ _exit_editor(EXIT_SUCCESS);
String exec = OS::get_singleton()->get_executable_path();
List<String> args;
@@ -3237,7 +3240,7 @@ void EditorNode::_update_addon_config() {
ProjectSettings::get_singleton()->set("editor_plugins/enabled", enabled_addons);
}
- project_settings->queue_save();
+ project_settings_editor->queue_save();
}
void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, bool p_config_changed) {
@@ -3358,7 +3361,7 @@ void EditorNode::set_edited_scene(Node *p_scene) {
if (Object::cast_to<Popup>(p_scene)) {
Object::cast_to<Popup>(p_scene)->show(); // show popups
}
- scene_tree_dock->set_edited_scene(p_scene);
+ SceneTreeDock::get_singleton()->set_edited_scene(p_scene);
if (get_tree()) {
get_tree()->set_edited_scene_root(p_scene);
}
@@ -3383,10 +3386,10 @@ int EditorNode::_get_current_main_editor() {
Dictionary EditorNode::_get_main_scene_state() {
Dictionary state;
state["main_tab"] = _get_current_main_editor();
- state["scene_tree_offset"] = scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value();
- state["property_edit_offset"] = get_inspector()->get_scroll_offset();
+ state["scene_tree_offset"] = SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value();
+ state["property_edit_offset"] = InspectorDock::get_inspector_singleton()->get_scroll_offset();
state["saved_version"] = saved_version;
- state["node_filter"] = scene_tree_dock->get_filter();
+ state["node_filter"] = SceneTreeDock::get_singleton()->get_filter();
return state;
}
@@ -3428,14 +3431,14 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
}
if (p_state.has("scene_tree_offset")) {
- scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->set_value(p_state["scene_tree_offset"]);
+ SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->set_value(p_state["scene_tree_offset"]);
}
if (p_state.has("property_edit_offset")) {
- get_inspector()->set_scroll_offset(p_state["property_edit_offset"]);
+ InspectorDock::get_inspector_singleton()->set_scroll_offset(p_state["property_edit_offset"]);
}
if (p_state.has("node_filter")) {
- scene_tree_dock->set_filter(p_state["node_filter"]);
+ SceneTreeDock::get_singleton()->set_filter(p_state["node_filter"]);
}
// this should only happen at the very end
@@ -3490,7 +3493,7 @@ void EditorNode::set_current_scene(int p_idx) {
Object::cast_to<Popup>(new_scene)->show(); // show popups
}
- scene_tree_dock->set_edited_scene(new_scene);
+ SceneTreeDock::get_singleton()->set_edited_scene(new_scene);
if (get_tree()) {
get_tree()->set_edited_scene_root(new_scene);
}
@@ -3670,7 +3673,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
prev_scene->set_disabled(previous_scenes.size() == 0);
opening_prev = false;
- scene_tree_dock->set_selected(new_scene);
+ SceneTreeDock::get_singleton()->set_selected(new_scene);
EditorDebuggerNode::get_singleton()->update_live_edit_root();
@@ -3695,27 +3698,11 @@ void EditorNode::open_request(const String &p_path) {
}
void EditorNode::request_instance_scene(const String &p_path) {
- scene_tree_dock->instantiate(p_path);
+ SceneTreeDock::get_singleton()->instantiate(p_path);
}
void EditorNode::request_instantiate_scenes(const Vector<String> &p_files) {
- scene_tree_dock->instantiate_scenes(p_files);
-}
-
-ImportDock *EditorNode::get_import_dock() {
- return import_dock;
-}
-
-FileSystemDock *EditorNode::get_filesystem_dock() {
- return filesystem_dock;
-}
-
-SceneTreeDock *EditorNode::get_scene_tree_dock() {
- return scene_tree_dock;
-}
-
-InspectorDock *EditorNode::get_inspector_dock() {
- return inspector_dock;
+ SceneTreeDock::get_singleton()->instantiate_scenes(p_files);
}
void EditorNode::_inherit_request(String p_file) {
@@ -3738,7 +3725,7 @@ void EditorNode::_show_messages() {
void EditorNode::_add_to_recent_scenes(const String &p_scene) {
Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scenes", Array());
- if (rc.find(p_scene) != -1) {
+ if (rc.has(p_scene)) {
rc.erase(p_scene);
}
rc.push_front(p_scene);
@@ -4235,6 +4222,8 @@ void EditorNode::_dock_floating_close_request(Control *p_control) {
_update_dock_containers();
floating_docks.erase(p_control);
+
+ _edit_current();
}
void EditorNode::_dock_make_float() {
@@ -4277,6 +4266,8 @@ void EditorNode::_dock_make_float() {
_update_dock_containers();
floating_docks.push_back(dock);
+
+ _edit_current();
}
void EditorNode::_update_dock_containers() {
@@ -4511,10 +4502,10 @@ void EditorNode::_save_docks_to_config(Ref<ConfigFile> p_layout, const String &p
}
}
- p_layout->set_value(p_section, "dock_filesystem_split", filesystem_dock->get_split_offset());
- p_layout->set_value(p_section, "dock_filesystem_display_mode", filesystem_dock->get_display_mode());
- p_layout->set_value(p_section, "dock_filesystem_file_sort", filesystem_dock->get_file_sort());
- p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", filesystem_dock->get_file_list_display_mode());
+ p_layout->set_value(p_section, "dock_filesystem_split", FileSystemDock::get_singleton()->get_split_offset());
+ p_layout->set_value(p_section, "dock_filesystem_display_mode", FileSystemDock::get_singleton()->get_display_mode());
+ p_layout->set_value(p_section, "dock_filesystem_file_sort", FileSystemDock::get_singleton()->get_file_sort());
+ p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", FileSystemDock::get_singleton()->get_file_list_display_mode());
for (int i = 0; i < vsplits.size(); i++) {
if (vsplits[i]->is_visible_in_tree()) {
@@ -4700,22 +4691,22 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String
if (p_layout->has_section_key(p_section, "dock_filesystem_split")) {
int fs_split_ofs = p_layout->get_value(p_section, "dock_filesystem_split");
- filesystem_dock->set_split_offset(fs_split_ofs);
+ FileSystemDock::get_singleton()->set_split_offset(fs_split_ofs);
}
if (p_layout->has_section_key(p_section, "dock_filesystem_display_mode")) {
FileSystemDock::DisplayMode dock_filesystem_display_mode = FileSystemDock::DisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_display_mode")));
- filesystem_dock->set_display_mode(dock_filesystem_display_mode);
+ FileSystemDock::get_singleton()->set_display_mode(dock_filesystem_display_mode);
}
if (p_layout->has_section_key(p_section, "dock_filesystem_file_sort")) {
FileSystemDock::FileSortOption dock_filesystem_file_sort = FileSystemDock::FileSortOption(int(p_layout->get_value(p_section, "dock_filesystem_file_sort")));
- filesystem_dock->set_file_sort(dock_filesystem_file_sort);
+ FileSystemDock::get_singleton()->set_file_sort(dock_filesystem_file_sort);
}
if (p_layout->has_section_key(p_section, "dock_filesystem_file_list_display_mode")) {
FileSystemDock::FileListDisplayMode dock_filesystem_file_list_display_mode = FileSystemDock::FileListDisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_file_list_display_mode")));
- filesystem_dock->set_file_list_display_mode(dock_filesystem_file_list_display_mode);
+ FileSystemDock::get_singleton()->set_file_list_display_mode(dock_filesystem_file_list_display_mode);
}
for (int i = 0; i < vsplits.size(); i++) {
@@ -4969,7 +4960,7 @@ void EditorNode::_layout_menu_option(int p_id) {
void EditorNode::_scene_tab_script_edited(int p_tab) {
Ref<Script> script = editor_data.get_scene_root_script(p_tab);
if (script.is_valid()) {
- inspector_dock->edit_resource(script);
+ InspectorDock::get_singleton()->edit_resource(script);
}
}
@@ -5434,7 +5425,7 @@ void EditorNode::_global_menu_new_window(const Variant &p_tag) {
}
void EditorNode::_dropped_files(const Vector<String> &p_files, int p_screen) {
- String to_path = ProjectSettings::get_singleton()->globalize_path(get_filesystem_dock()->get_selected_path());
+ String to_path = ProjectSettings::get_singleton()->globalize_path(FileSystemDock::get_singleton()->get_selected_path());
_add_dropped_files_recursive(p_files, to_path);
@@ -5640,15 +5631,15 @@ void EditorNode::_resource_loaded(RES p_resource, const String &p_path) {
void EditorNode::_feature_profile_changed() {
Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile();
- TabContainer *import_tabs = cast_to<TabContainer>(import_dock->get_parent());
- TabContainer *node_tabs = cast_to<TabContainer>(node_dock->get_parent());
- TabContainer *fs_tabs = cast_to<TabContainer>(filesystem_dock->get_parent());
+ TabContainer *import_tabs = cast_to<TabContainer>(ImportDock::get_singleton()->get_parent());
+ TabContainer *node_tabs = cast_to<TabContainer>(NodeDock::get_singleton()->get_parent());
+ TabContainer *fs_tabs = cast_to<TabContainer>(FileSystemDock::get_singleton()->get_parent());
if (profile.is_valid()) {
- node_tabs->set_tab_hidden(node_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_NODE_DOCK));
+ node_tabs->set_tab_hidden(NodeDock::get_singleton()->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_NODE_DOCK));
// The Import dock is useless without the FileSystem dock. Ensure the configuration is valid.
bool fs_dock_disabled = profile->is_feature_disabled(EditorFeatureProfile::FEATURE_FILESYSTEM_DOCK);
- fs_tabs->set_tab_hidden(filesystem_dock->get_index(), fs_dock_disabled);
- import_tabs->set_tab_hidden(import_dock->get_index(), fs_dock_disabled || profile->is_feature_disabled(EditorFeatureProfile::FEATURE_IMPORT_DOCK));
+ fs_tabs->set_tab_hidden(FileSystemDock::get_singleton()->get_index(), fs_dock_disabled);
+ import_tabs->set_tab_hidden(ImportDock::get_singleton()->get_index(), fs_dock_disabled || profile->is_feature_disabled(EditorFeatureProfile::FEATURE_IMPORT_DOCK));
main_editor_buttons[EDITOR_3D]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D));
main_editor_buttons[EDITOR_SCRIPT]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT));
@@ -5661,12 +5652,12 @@ void EditorNode::_feature_profile_changed() {
_editor_select(EDITOR_2D);
}
} else {
- import_tabs->set_tab_hidden(import_dock->get_index(), false);
- node_tabs->set_tab_hidden(node_dock->get_index(), false);
- fs_tabs->set_tab_hidden(filesystem_dock->get_index(), false);
- import_dock->set_visible(true);
- node_dock->set_visible(true);
- filesystem_dock->set_visible(true);
+ import_tabs->set_tab_hidden(ImportDock::get_singleton()->get_index(), false);
+ node_tabs->set_tab_hidden(NodeDock::get_singleton()->get_index(), false);
+ fs_tabs->set_tab_hidden(FileSystemDock::get_singleton()->get_index(), false);
+ ImportDock::get_singleton()->set_visible(true);
+ NodeDock::get_singleton()->set_visible(true);
+ FileSystemDock::get_singleton()->set_visible(true);
main_editor_buttons[EDITOR_3D]->set_visible(true);
main_editor_buttons[EDITOR_SCRIPT]->set_visible(true);
if (StreamPeerSSL::is_available()) {
@@ -5678,6 +5669,8 @@ void EditorNode::_feature_profile_changed() {
}
void EditorNode::_bind_methods() {
+ GLOBAL_DEF("editor/scene/scene_naming", SCENE_NAME_CASING_SNAKE_CASE);
+ ProjectSettings::get_singleton()->set_custom_property_info("editor/scene/scene_naming", PropertyInfo(Variant::INT, "editor/scene/scene_naming", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"));
ClassDB::bind_method("_editor_select", &EditorNode::_editor_select);
ClassDB::bind_method("_node_renamed", &EditorNode::_node_renamed);
ClassDB::bind_method("edit_node", &EditorNode::edit_node);
@@ -5692,8 +5685,6 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process);
- ClassDB::bind_method("get_script_create_dialog", &EditorNode::get_script_create_dialog);
-
ClassDB::bind_method("set_current_scene", &EditorNode::set_current_scene);
ClassDB::bind_method("set_current_version", &EditorNode::set_current_version);
ClassDB::bind_method("_thumbnail_done", &EditorNode::_thumbnail_done);
@@ -6038,9 +6029,8 @@ EditorNode::EditorNode() {
EDITOR_DEF("interface/inspector/default_color_picker_shape", (int32_t)ColorPicker::SHAPE_VHS_CIRCLE);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle", PROPERTY_USAGE_DEFAULT));
EDITOR_DEF("run/auto_save/save_before_running", true);
- EDITOR_DEF("interface/editors/sub_editor_panning_scheme", 0);
- // Should be in sync with ControlScheme in ViewPanner.
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/editors/sub_editor_panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans", PROPERTY_USAGE_DEFAULT));
+
+ ED_SHORTCUT("canvas_item_editor/pan_view", TTR("Pan View"), Key::SPACE);
const Vector<String> textfile_ext = ((String)(EditorSettings::get_singleton()->get("docks/filesystem/textfile_extensions"))).split(",", false);
for (const String &E : textfile_ext) {
@@ -6247,7 +6237,7 @@ EditorNode::EditorNode() {
scene_tabs->set_min_width(int(EDITOR_DEF("interface/scene_tabs/minimum_width", 50)) * EDSCALE);
scene_tabs->set_drag_to_rearrange_enabled(true);
scene_tabs->connect("tab_changed", callable_mp(this, &EditorNode::_scene_tab_changed));
- scene_tabs->connect("tab_rmb_clicked", callable_mp(this, &EditorNode::_scene_tab_script_edited));
+ scene_tabs->connect("tab_button_pressed", callable_mp(this, &EditorNode::_scene_tab_script_edited));
scene_tabs->connect("tab_close_pressed", callable_mp(this, &EditorNode::_scene_tab_closed), varray(SCENE_TAB_CLOSE));
scene_tabs->connect("tab_hovered", callable_mp(this, &EditorNode::_scene_tab_hovered));
scene_tabs->connect("mouse_exited", callable_mp(this, &EditorNode::_scene_tab_exit));
@@ -6339,11 +6329,11 @@ EditorNode::EditorNode() {
dependency_fixer = memnew(DependencyEditor);
gui_base->add_child(dependency_fixer);
- settings_config_dialog = memnew(EditorSettingsDialog);
- gui_base->add_child(settings_config_dialog);
+ editor_settings_dialog = memnew(EditorSettingsDialog);
+ gui_base->add_child(editor_settings_dialog);
- project_settings = memnew(ProjectSettingsEditor(&editor_data));
- gui_base->add_child(project_settings);
+ project_settings_editor = memnew(ProjectSettingsEditor(&editor_data));
+ gui_base->add_child(project_settings_editor);
scene_import_settings = memnew(SceneImportSettings);
gui_base->add_child(scene_import_settings);
@@ -6512,11 +6502,6 @@ EditorNode::EditorNode() {
ED_SHORTCUT_OVERRIDE("editor/fullscreen_mode", "macos", KeyModifierMask::CMD | KeyModifierMask::CTRL | Key::F);
p->add_shortcut(ED_GET_SHORTCUT("editor/fullscreen_mode"), SETTINGS_TOGGLE_FULLSCREEN);
-#if defined(WINDOWS_ENABLED) && defined(WINDOWS_SUBSYSTEM_CONSOLE)
- // The console can only be toggled if the application was built for the console subsystem,
- // not the GUI subsystem.
- p->add_item(TTR("Toggle System Console"), SETTINGS_TOGGLE_CONSOLE);
-#endif
p->add_separator();
if (OS::get_singleton()->get_data_path() == OS::get_singleton()->get_config_path()) {
@@ -6699,35 +6684,35 @@ EditorNode::EditorNode() {
// Instantiate and place editor docks
- scene_tree_dock = memnew(SceneTreeDock(this, scene_root, editor_selection, editor_data));
- inspector_dock = memnew(InspectorDock(this, editor_data));
- import_dock = memnew(ImportDock);
- node_dock = memnew(NodeDock);
+ memnew(SceneTreeDock(this, scene_root, editor_selection, editor_data));
+ memnew(InspectorDock(this, editor_data));
+ memnew(ImportDock);
+ memnew(NodeDock);
- filesystem_dock = memnew(FileSystemDock(this));
+ FileSystemDock *filesystem_dock = memnew(FileSystemDock(this));
filesystem_dock->connect("inherit", callable_mp(this, &EditorNode::_inherit_request));
filesystem_dock->connect("instance", callable_mp(this, &EditorNode::_instantiate_request));
filesystem_dock->connect("display_mode_changed", callable_mp(this, &EditorNode::_save_docks));
// Scene: Top left
- dock_slot[DOCK_SLOT_LEFT_UR]->add_child(scene_tree_dock);
- dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(scene_tree_dock->get_index(), TTR("Scene"));
+ dock_slot[DOCK_SLOT_LEFT_UR]->add_child(SceneTreeDock::get_singleton());
+ dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(SceneTreeDock::get_singleton()->get_index(), TTR("Scene"));
// Import: Top left, behind Scene
- dock_slot[DOCK_SLOT_LEFT_UR]->add_child(import_dock);
- dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(import_dock->get_index(), TTR("Import"));
+ dock_slot[DOCK_SLOT_LEFT_UR]->add_child(ImportDock::get_singleton());
+ dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(ImportDock::get_singleton()->get_index(), TTR("Import"));
// FileSystem: Bottom left
- dock_slot[DOCK_SLOT_LEFT_BR]->add_child(filesystem_dock);
- dock_slot[DOCK_SLOT_LEFT_BR]->set_tab_title(filesystem_dock->get_index(), TTR("FileSystem"));
+ dock_slot[DOCK_SLOT_LEFT_BR]->add_child(FileSystemDock::get_singleton());
+ dock_slot[DOCK_SLOT_LEFT_BR]->set_tab_title(FileSystemDock::get_singleton()->get_index(), TTR("FileSystem"));
// Inspector: Full height right
- dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(inspector_dock);
- dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(inspector_dock->get_index(), TTR("Inspector"));
+ dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(InspectorDock::get_singleton());
+ dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(InspectorDock::get_singleton()->get_index(), TTR("Inspector"));
// Node: Full height right, behind Inspector
- dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(node_dock);
- dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(node_dock->get_index(), TTR("Node"));
+ dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(NodeDock::get_singleton());
+ dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(NodeDock::get_singleton()->get_index(), TTR("Node"));
// Hide unused dock slots and vsplits
dock_slot[DOCK_SLOT_LEFT_UL]->hide();
@@ -7171,7 +7156,7 @@ EditorNode::EditorNode() {
editor_data.set_edited_scene(0);
_update_scene_tabs();
- import_dock->initialize_import_options();
+ ImportDock::get_singleton()->initialize_import_options();
FileAccess::set_file_close_fail_notify_callback(_file_access_close_error_notify);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index af7223ffb4..5c013a18d9 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -188,7 +188,6 @@ private:
SETTINGS_MANAGE_FEATURE_PROFILES,
SETTINGS_INSTALL_ANDROID_BUILD_TEMPLATE,
SETTINGS_PICK_MAIN_SCENE,
- SETTINGS_TOGGLE_CONSOLE,
SETTINGS_TOGGLE_FULLSCREEN,
SETTINGS_HELP,
SCENE_TAB_CLOSE,
@@ -217,6 +216,12 @@ private:
TOOL_MENU_BASE = 1000
};
+ enum ScriptNameCasing {
+ SCENE_NAME_CASING_AUTO,
+ SCENE_NAME_CASING_PASCAL_CASE,
+ SCENE_NAME_CASING_SNAKE_CASE
+ };
+
SubViewport *scene_root; // root of the scene being edited
PanelContainer *scene_root_parent;
@@ -297,11 +302,6 @@ private:
Ref<Theme> theme;
PopupMenu *recent_scenes;
- SceneTreeDock *scene_tree_dock;
- InspectorDock *inspector_dock;
- NodeDock *node_dock;
- ImportDock *import_dock;
- FileSystemDock *filesystem_dock;
EditorRunNative *run_native;
ConfirmationDialog *confirmation;
@@ -323,8 +323,8 @@ private:
ConfirmationDialog *install_android_build_template;
ConfirmationDialog *remove_android_build_template;
- EditorSettingsDialog *settings_config_dialog;
- ProjectSettingsEditor *project_settings;
+ EditorSettingsDialog *editor_settings_dialog;
+ ProjectSettingsEditor *project_settings_editor;
bool settings_changed = true; // make it update settings on first frame
void _update_from_settings();
@@ -530,7 +530,7 @@ private:
void _add_dropped_files_recursive(const Vector<String> &p_files, String to_path);
String _recent_scene;
- void _exit_editor();
+ void _exit_editor(int p_exit_code);
bool convert_old;
@@ -712,11 +712,8 @@ public:
EditorPluginList *get_editor_plugins_over() { return editor_plugins_over; }
EditorPluginList *get_editor_plugins_force_over() { return editor_plugins_force_over; }
EditorPluginList *get_editor_plugins_force_input_forwarding() { return editor_plugins_force_input_forwarding; }
- EditorInspector *get_inspector() { return inspector_dock->get_inspector(); }
- Container *get_inspector_dock_addon_area() { return inspector_dock->get_addon_area(); }
- ScriptCreateDialog *get_script_create_dialog() { return scene_tree_dock->get_script_create_dialog(); }
- ProjectSettingsEditor *get_project_settings() { return project_settings; }
+ ProjectSettingsEditor *get_project_settings() { return project_settings_editor; }
static void add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed = false);
static void remove_editor_plugin(EditorPlugin *p_editor, bool p_config_changed = false);
@@ -738,8 +735,7 @@ public:
bool is_addon_plugin_enabled(const String &p_addon) const;
void edit_node(Node *p_node);
- void edit_resource(const Ref<Resource> &p_resource) { inspector_dock->edit_resource(p_resource); };
- void open_resource(const String &p_type) { inspector_dock->open_resource(p_type); };
+ void edit_resource(const Ref<Resource> &p_resource) { InspectorDock::get_singleton()->edit_resource(p_resource); };
void save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path);
void save_resource(const Ref<Resource> &p_resource);
@@ -790,10 +786,6 @@ public:
void request_instance_scene(const String &p_path);
void request_instantiate_scenes(const Vector<String> &p_files);
- FileSystemDock *get_filesystem_dock();
- ImportDock *get_import_dock();
- SceneTreeDock *get_scene_tree_dock();
- InspectorDock *get_inspector_dock();
static UndoRedo *get_undo_redo() { return &singleton->editor_data.get_undo_redo(); }
EditorSelection *get_editor_selection() { return editor_selection; }
@@ -879,7 +871,6 @@ public:
void edit_current() { _edit_current(); };
- void update_keying() const { inspector_dock->update_keying(); };
bool has_scenes_in_session();
int execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok = true, bool p_close_on_errors = false);
diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp
index a4481cd1eb..d4e40db406 100644
--- a/editor/editor_paths.cpp
+++ b/editor/editor_paths.cpp
@@ -91,6 +91,11 @@ EditorPaths::EditorPaths() {
// Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir.
String exe_path = OS::get_singleton()->get_executable_path().get_base_dir();
+
+ // On macOS, look outside .app bundle, since .app bundle is read-only.
+ if (OS::get_singleton()->has_feature("macos") && exe_path.ends_with("MacOS") && exe_path.plus_file("..").simplify_path().ends_with("Contents")) {
+ exe_path = exe_path.plus_file("../../..").simplify_path();
+ }
{
DirAccessRef d = DirAccess::create_for_path(exe_path);
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index aeca340cb1..a1c031aa1b 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -233,15 +233,15 @@ ScriptEditor *EditorInterface::get_script_editor() {
}
void EditorInterface::select_file(const String &p_file) {
- EditorNode::get_singleton()->get_filesystem_dock()->select_file(p_file);
+ FileSystemDock::get_singleton()->select_file(p_file);
}
String EditorInterface::get_selected_path() const {
- return EditorNode::get_singleton()->get_filesystem_dock()->get_selected_path();
+ return FileSystemDock::get_singleton()->get_selected_path();
}
String EditorInterface::get_current_path() const {
- return EditorNode::get_singleton()->get_filesystem_dock()->get_current_path();
+ return FileSystemDock::get_singleton()->get_current_path();
}
void EditorInterface::inspect_object(Object *p_obj, const String &p_for_property, bool p_inspector_only) {
@@ -253,7 +253,7 @@ EditorFileSystem *EditorInterface::get_resource_file_system() {
}
FileSystemDock *EditorInterface::get_file_system_dock() {
- return EditorNode::get_singleton()->get_filesystem_dock();
+ return FileSystemDock::get_singleton();
}
EditorSelection *EditorInterface::get_selection() {
@@ -288,7 +288,7 @@ bool EditorInterface::is_plugin_enabled(const String &p_plugin) const {
}
EditorInspector *EditorInterface::get_inspector() const {
- return EditorNode::get_singleton()->get_inspector();
+ return InspectorDock::get_inspector_singleton();
}
Error EditorInterface::save_scene() {
@@ -421,14 +421,10 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C
} break;
case CONTAINER_SPATIAL_EDITOR_SIDE_LEFT: {
- Node3DEditor::get_singleton()->get_palette_split()->add_child(p_control);
- Node3DEditor::get_singleton()->get_palette_split()->move_child(p_control, 0);
-
+ Node3DEditor::get_singleton()->add_control_to_left_panel(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT: {
- Node3DEditor::get_singleton()->get_palette_split()->add_child(p_control);
- Node3DEditor::get_singleton()->get_palette_split()->move_child(p_control, 1);
-
+ Node3DEditor::get_singleton()->add_control_to_right_panel(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_BOTTOM: {
Node3DEditor::get_singleton()->get_shader_split()->add_child(p_control);
@@ -439,21 +435,17 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C
} break;
case CONTAINER_CANVAS_EDITOR_SIDE_LEFT: {
- CanvasItemEditor::get_singleton()->get_palette_split()->add_child(p_control);
- CanvasItemEditor::get_singleton()->get_palette_split()->move_child(p_control, 0);
-
+ CanvasItemEditor::get_singleton()->add_control_to_left_panel(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_SIDE_RIGHT: {
- CanvasItemEditor::get_singleton()->get_palette_split()->add_child(p_control);
- CanvasItemEditor::get_singleton()->get_palette_split()->move_child(p_control, 1);
-
+ CanvasItemEditor::get_singleton()->add_control_to_right_panel(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_BOTTOM: {
CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(p_control);
} break;
case CONTAINER_PROPERTY_EDITOR_BOTTOM: {
- EditorNode::get_singleton()->get_inspector_dock_addon_area()->add_child(p_control);
+ InspectorDock::get_singleton()->get_addon_area()->add_child(p_control);
} break;
case CONTAINER_PROJECT_SETTING_TAB_LEFT: {
@@ -481,10 +473,11 @@ void EditorPlugin::remove_control_from_container(CustomControlContainer p_locati
Node3DEditor::get_singleton()->remove_control_from_menu_panel(p_control);
} break;
- case CONTAINER_SPATIAL_EDITOR_SIDE_LEFT:
+ case CONTAINER_SPATIAL_EDITOR_SIDE_LEFT: {
+ Node3DEditor::get_singleton()->remove_control_from_left_panel(p_control);
+ } break;
case CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT: {
- Node3DEditor::get_singleton()->get_palette_split()->remove_child(p_control);
-
+ Node3DEditor::get_singleton()->remove_control_from_right_panel(p_control);
} break;
case CONTAINER_SPATIAL_EDITOR_BOTTOM: {
Node3DEditor::get_singleton()->get_shader_split()->remove_child(p_control);
@@ -494,17 +487,18 @@ void EditorPlugin::remove_control_from_container(CustomControlContainer p_locati
CanvasItemEditor::get_singleton()->remove_control_from_menu_panel(p_control);
} break;
- case CONTAINER_CANVAS_EDITOR_SIDE_LEFT:
+ case CONTAINER_CANVAS_EDITOR_SIDE_LEFT: {
+ CanvasItemEditor::get_singleton()->remove_control_from_left_panel(p_control);
+ } break;
case CONTAINER_CANVAS_EDITOR_SIDE_RIGHT: {
- CanvasItemEditor::get_singleton()->get_palette_split()->remove_child(p_control);
-
+ CanvasItemEditor::get_singleton()->remove_control_from_right_panel(p_control);
} break;
case CONTAINER_CANVAS_EDITOR_BOTTOM: {
CanvasItemEditor::get_singleton()->get_bottom_split()->remove_child(p_control);
} break;
case CONTAINER_PROPERTY_EDITOR_BOTTOM: {
- EditorNode::get_singleton()->get_inspector_dock_addon_area()->remove_child(p_control);
+ InspectorDock::get_singleton()->get_addon_area()->remove_child(p_control);
} break;
case CONTAINER_PROJECT_SETTING_TAB_LEFT:
@@ -519,11 +513,9 @@ void EditorPlugin::add_tool_menu_item(const String &p_name, const Callable &p_ca
EditorNode::get_singleton()->add_tool_menu_item(p_name, p_callable);
}
-void EditorPlugin::add_tool_submenu_item(const String &p_name, Object *p_submenu) {
+void EditorPlugin::add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu) {
ERR_FAIL_NULL(p_submenu);
- PopupMenu *submenu = Object::cast_to<PopupMenu>(p_submenu);
- ERR_FAIL_NULL(submenu);
- EditorNode::get_singleton()->add_tool_submenu_item(p_name, submenu);
+ EditorNode::get_singleton()->add_tool_submenu_item(p_name, p_submenu);
}
void EditorPlugin::remove_tool_menu_item(const String &p_name) {
@@ -839,7 +831,7 @@ EditorInterface *EditorPlugin::get_editor_interface() {
}
ScriptCreateDialog *EditorPlugin::get_script_create_dialog() {
- return EditorNode::get_singleton()->get_script_create_dialog();
+ return SceneTreeDock::get_singleton()->get_script_create_dialog();
}
void EditorPlugin::add_debugger_plugin(const Ref<Script> &p_script) {
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index faa8ae1ce6..06517190f6 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -42,6 +42,7 @@
#include "scene/3d/camera_3d.h"
#include "scene/main/node.h"
#include "scene/resources/texture.h"
+
class EditorNode;
class Node3D;
class Camera3D;
@@ -217,7 +218,7 @@ public:
void remove_control_from_bottom_panel(Control *p_control);
void add_tool_menu_item(const String &p_name, const Callable &p_callable);
- void add_tool_submenu_item(const String &p_name, Object *p_submenu);
+ void add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu);
void remove_tool_menu_item(const String &p_name);
void set_input_event_forwarding_always_enabled();
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 97a38b9200..5ff83649c0 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -343,6 +343,64 @@ EditorPropertyTextEnum::EditorPropertyTextEnum() {
add_focusable(cancel_button);
}
+//////////////////// LOCALE ////////////////////////
+
+void EditorPropertyLocale::_locale_selected(const String &p_locale) {
+ emit_changed(get_edited_property(), p_locale);
+ update_property();
+}
+
+void EditorPropertyLocale::_locale_pressed() {
+ if (!dialog) {
+ dialog = memnew(EditorLocaleDialog);
+ dialog->connect("locale_selected", callable_mp(this, &EditorPropertyLocale::_locale_selected));
+ add_child(dialog);
+ }
+
+ String locale_code = get_edited_object()->get(get_edited_property());
+ dialog->set_locale(locale_code);
+ dialog->popup_locale_dialog();
+}
+
+void EditorPropertyLocale::update_property() {
+ String locale_code = get_edited_object()->get(get_edited_property());
+ locale->set_text(locale_code);
+ locale->set_tooltip(locale_code);
+}
+
+void EditorPropertyLocale::setup(const String &p_hint_text) {
+}
+
+void EditorPropertyLocale::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ locale_edit->set_icon(get_theme_icon(SNAME("Translation"), SNAME("EditorIcons")));
+ }
+}
+
+void EditorPropertyLocale::_locale_focus_exited() {
+ _locale_selected(locale->get_text());
+}
+
+void EditorPropertyLocale::_bind_methods() {
+}
+
+EditorPropertyLocale::EditorPropertyLocale() {
+ HBoxContainer *locale_hb = memnew(HBoxContainer);
+ add_child(locale_hb);
+ locale = memnew(LineEdit);
+ locale_hb->add_child(locale);
+ locale->connect("text_submitted", callable_mp(this, &EditorPropertyLocale::_locale_selected));
+ locale->connect("focus_exited", callable_mp(this, &EditorPropertyLocale::_locale_focus_exited));
+ locale->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ locale_edit = memnew(Button);
+ locale_edit->set_clip_text(true);
+ locale_hb->add_child(locale_edit);
+ add_focusable(locale);
+ dialog = nullptr;
+ locale_edit->connect("pressed", callable_mp(this, &EditorPropertyLocale::_locale_pressed));
+}
+
///////////////////// PATH /////////////////////////
void EditorPropertyPath::_set_read_only(bool p_read_only) {
@@ -733,261 +791,293 @@ EditorPropertyFlags::EditorPropertyFlags() {
///////////////////// LAYERS /////////////////////////
-class EditorPropertyLayersGrid : public Control {
- GDCLASS(EditorPropertyLayersGrid, Control);
-
-private:
- Vector<Rect2> flag_rects;
- Rect2 expand_rect;
- bool expand_hovered = false;
- bool expanded = false;
- int expansion_rows = 0;
- int hovered_index = -1;
- bool read_only = false;
+void EditorPropertyLayersGrid::_rename_pressed(int p_menu) {
+ // Show rename popup for active layer.
+ if (renamed_layer_index == -1) {
+ return;
+ }
+ String name = names[renamed_layer_index];
+ rename_dialog->set_title(vformat(TTR("Renaming layer %d:"), renamed_layer_index + 1));
+ rename_dialog_text->set_text(name);
+ rename_dialog_text->select(0, name.length());
+ rename_dialog->popup_centered(Size2(300, 80) * EDSCALE);
+ rename_dialog_text->grab_focus();
+}
- Size2 get_grid_size() const {
- Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
- int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
- return Vector2(0, font->get_height(font_size) * 3);
+void EditorPropertyLayersGrid::_rename_operation_confirm() {
+ String new_name = rename_dialog_text->get_text().strip_edges();
+ if (new_name.length() == 0) {
+ EditorNode::get_singleton()->show_warning(TTR("No name provided."));
+ return;
+ } else if (new_name.find("/") != -1 || new_name.find("\\") != -1 || new_name.find(":") != -1) {
+ EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
+ return;
}
+ names.set(renamed_layer_index, new_name);
+ tooltips.set(renamed_layer_index, new_name + "\n" + vformat(TTR("Bit %d, value %d"), renamed_layer_index, 1 << renamed_layer_index));
+ emit_signal(SNAME("rename_confirmed"), renamed_layer_index, new_name);
+}
+
+EditorPropertyLayersGrid::EditorPropertyLayersGrid() {
+ rename_dialog = memnew(ConfirmationDialog);
+ VBoxContainer *rename_dialog_vb = memnew(VBoxContainer);
+ rename_dialog->add_child(rename_dialog_vb);
+ rename_dialog_text = memnew(LineEdit);
+ rename_dialog_vb->add_margin_child(TTR("Name:"), rename_dialog_text);
+ rename_dialog->get_ok_button()->set_text(TTR("Rename"));
+ add_child(rename_dialog);
+ rename_dialog->register_text_enter(rename_dialog_text);
+ rename_dialog->connect("confirmed", callable_mp(this, &EditorPropertyLayersGrid::_rename_operation_confirm));
+ layer_rename = memnew(PopupMenu);
+ layer_rename->add_item(TTR("Rename layer"), 0);
+ add_child(layer_rename);
+ layer_rename->connect("id_pressed", callable_mp(this, &EditorPropertyLayersGrid::_rename_pressed));
+}
+
+Size2 EditorPropertyLayersGrid::get_grid_size() const {
+ Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
+ int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
+ return Vector2(0, font->get_height(font_size) * 3);
+}
-public:
- uint32_t value = 0;
- int layer_group_size = 0;
- int layer_count = 0;
- Vector<String> names;
- Vector<String> tooltips;
+void EditorPropertyLayersGrid::set_read_only(bool p_read_only) {
+ read_only = p_read_only;
+}
+
+Size2 EditorPropertyLayersGrid::get_minimum_size() const {
+ Size2 min_size = get_grid_size();
- void set_read_only(bool p_read_only) {
- read_only = p_read_only;
+ // Add extra rows when expanded.
+ if (expanded) {
+ const int bsize = (min_size.height * 80 / 100) / 2;
+ for (int i = 0; i < expansion_rows; ++i) {
+ min_size.y += 2 * (bsize + 1) + 3;
+ }
}
- virtual Size2 get_minimum_size() const override {
- Size2 min_size = get_grid_size();
+ return min_size;
+}
- // Add extra rows when expanded.
- if (expanded) {
- const int bsize = (min_size.height * 80 / 100) / 2;
- for (int i = 0; i < expansion_rows; ++i) {
- min_size.y += 2 * (bsize + 1) + 3;
- }
+String EditorPropertyLayersGrid::get_tooltip(const Point2 &p_pos) const {
+ for (int i = 0; i < flag_rects.size(); i++) {
+ if (i < tooltips.size() && flag_rects[i].has_point(p_pos)) {
+ return tooltips[i];
}
-
- return min_size;
}
+ return String();
+}
- virtual String get_tooltip(const Point2 &p_pos) const override {
- for (int i = 0; i < flag_rects.size(); i++) {
- if (i < tooltips.size() && flag_rects[i].has_point(p_pos)) {
- return tooltips[i];
+void EditorPropertyLayersGrid::gui_input(const Ref<InputEvent> &p_ev) {
+ if (read_only) {
+ return;
+ }
+ const Ref<InputEventMouseMotion> mm = p_ev;
+ if (mm.is_valid()) {
+ bool expand_was_hovered = expand_hovered;
+ expand_hovered = expand_rect.has_point(mm->get_position());
+ if (expand_hovered != expand_was_hovered) {
+ update();
+ }
+
+ if (!expand_hovered) {
+ for (int i = 0; i < flag_rects.size(); i++) {
+ if (flag_rects[i].has_point(mm->get_position())) {
+ // Used to highlight the hovered flag in the layers grid.
+ hovered_index = i;
+ update();
+ return;
+ }
}
}
- return String();
- }
- void gui_input(const Ref<InputEvent> &p_ev) override {
- if (read_only) {
- return;
+ // Remove highlight when no square is hovered.
+ if (hovered_index != -1) {
+ hovered_index = -1;
+ update();
}
- const Ref<InputEventMouseMotion> mm = p_ev;
- if (mm.is_valid()) {
- bool expand_was_hovered = expand_hovered;
- expand_hovered = expand_rect.has_point(mm->get_position());
- if (expand_hovered != expand_was_hovered) {
- update();
- }
- if (!expand_hovered) {
- for (int i = 0; i < flag_rects.size(); i++) {
- if (flag_rects[i].has_point(mm->get_position())) {
- // Used to highlight the hovered flag in the layers grid.
- hovered_index = i;
- update();
- return;
- }
- }
- }
+ return;
+ }
- // Remove highlight when no square is hovered.
- if (hovered_index != -1) {
- hovered_index = -1;
- update();
+ const Ref<InputEventMouseButton> mb = p_ev;
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
+ if (hovered_index >= 0) {
+ // Toggle the flag.
+ // We base our choice on the hovered flag, so that it always matches the hovered flag.
+ if (value & (1 << hovered_index)) {
+ value &= ~(1 << hovered_index);
+ } else {
+ value |= (1 << hovered_index);
}
- return;
+ emit_signal(SNAME("flag_changed"), value);
+ update();
+ } else if (expand_hovered) {
+ expanded = !expanded;
+ update_minimum_size();
+ update();
}
-
- const Ref<InputEventMouseButton> mb = p_ev;
- if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
- if (hovered_index >= 0) {
- // Toggle the flag.
- // We base our choice on the hovered flag, so that it always matches the hovered flag.
- if (value & (1 << hovered_index)) {
- value &= ~(1 << hovered_index);
- } else {
- value |= (1 << hovered_index);
- }
-
- emit_signal(SNAME("flag_changed"), value);
- update();
- } else if (expand_hovered) {
- expanded = !expanded;
- update_minimum_size();
- update();
- }
+ }
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
+ if (hovered_index >= 0) {
+ renamed_layer_index = hovered_index;
+ layer_rename->set_position(get_screen_position() + mb->get_position());
+ layer_rename->reset_size();
+ layer_rename->popup();
}
}
+}
- void _notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_DRAW: {
- Size2 grid_size = get_grid_size();
- grid_size.x = get_size().x;
-
- flag_rects.clear();
+void EditorPropertyLayersGrid::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ Size2 grid_size = get_grid_size();
+ grid_size.x = get_size().x;
- int prev_expansion_rows = expansion_rows;
- expansion_rows = 0;
+ flag_rects.clear();
- const int bsize = (grid_size.height * 80 / 100) / 2;
- const int h = bsize * 2 + 1;
+ int prev_expansion_rows = expansion_rows;
+ expansion_rows = 0;
- Color color = get_theme_color(read_only ? SNAME("disabled_highlight_color") : SNAME("highlight_color"), SNAME("Editor"));
+ const int bsize = (grid_size.height * 80 / 100) / 2;
+ const int h = bsize * 2 + 1;
- Color text_color = get_theme_color(read_only ? SNAME("disabled_font_color") : SNAME("font_color"), SNAME("Editor"));
- text_color.a *= 0.5;
+ Color color = get_theme_color(read_only ? SNAME("disabled_highlight_color") : SNAME("highlight_color"), SNAME("Editor"));
- Color text_color_on = get_theme_color(read_only ? SNAME("disabled_font_color") : SNAME("font_hover_color"), SNAME("Editor"));
- text_color_on.a *= 0.7;
+ Color text_color = get_theme_color(read_only ? SNAME("disabled_font_color") : SNAME("font_color"), SNAME("Editor"));
+ text_color.a *= 0.5;
- const int vofs = (grid_size.height - h) / 2;
+ Color text_color_on = get_theme_color(read_only ? SNAME("disabled_font_color") : SNAME("font_hover_color"), SNAME("Editor"));
+ text_color_on.a *= 0.7;
- int layer_index = 0;
- int block_index = 0;
+ const int vofs = (grid_size.height - h) / 2;
- Point2 arrow_pos;
+ int layer_index = 0;
+ int block_index = 0;
- Point2 block_ofs(4, vofs);
+ Point2 arrow_pos;
- while (true) {
- Point2 ofs = block_ofs;
+ Point2 block_ofs(4, vofs);
- for (int i = 0; i < 2; i++) {
- for (int j = 0; j < layer_group_size; j++) {
- const bool on = value & (1 << layer_index);
- Rect2 rect2 = Rect2(ofs, Size2(bsize, bsize));
+ while (true) {
+ Point2 ofs = block_ofs;
- color.a = on ? 0.6 : 0.2;
- if (layer_index == hovered_index) {
- // Add visual feedback when hovering a flag.
- color.a += 0.15;
- }
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < layer_group_size; j++) {
+ const bool on = value & (1 << layer_index);
+ Rect2 rect2 = Rect2(ofs, Size2(bsize, bsize));
- draw_rect(rect2, color);
- flag_rects.push_back(rect2);
+ color.a = on ? 0.6 : 0.2;
+ if (layer_index == hovered_index) {
+ // Add visual feedback when hovering a flag.
+ color.a += 0.15;
+ }
- Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
- int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
- Vector2 offset;
- offset.y = rect2.size.y * 0.75;
+ draw_rect(rect2, color);
+ flag_rects.push_back(rect2);
- draw_string(font, rect2.position + offset, itos(layer_index + 1), HORIZONTAL_ALIGNMENT_CENTER, rect2.size.x, font_size, on ? text_color_on : text_color);
+ Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
+ int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
+ Vector2 offset;
+ offset.y = rect2.size.y * 0.75;
- ofs.x += bsize + 1;
+ draw_string(font, rect2.position + offset, itos(layer_index + 1), HORIZONTAL_ALIGNMENT_CENTER, rect2.size.x, font_size, on ? text_color_on : text_color);
- ++layer_index;
- }
+ ofs.x += bsize + 1;
- ofs.x = block_ofs.x;
- ofs.y += bsize + 1;
+ ++layer_index;
}
- if (layer_index >= layer_count) {
- if (!flag_rects.is_empty() && (expansion_rows == 0)) {
- const Rect2 &last_rect = flag_rects[flag_rects.size() - 1];
- arrow_pos = last_rect.get_end();
- }
- break;
+ ofs.x = block_ofs.x;
+ ofs.y += bsize + 1;
+ }
+
+ if (layer_index >= layer_count) {
+ if (!flag_rects.is_empty() && (expansion_rows == 0)) {
+ const Rect2 &last_rect = flag_rects[flag_rects.size() - 1];
+ arrow_pos = last_rect.get_end();
}
+ break;
+ }
- int block_size_x = layer_group_size * (bsize + 1);
- block_ofs.x += block_size_x + 3;
+ int block_size_x = layer_group_size * (bsize + 1);
+ block_ofs.x += block_size_x + 3;
- if (block_ofs.x + block_size_x + 12 > grid_size.width) {
- // Keep last valid cell position for the expansion icon.
- if (!flag_rects.is_empty() && (expansion_rows == 0)) {
- const Rect2 &last_rect = flag_rects[flag_rects.size() - 1];
- arrow_pos = last_rect.get_end();
- }
- ++expansion_rows;
-
- if (expanded) {
- // Expand grid to next line.
- block_ofs.x = 4;
- block_ofs.y += 2 * (bsize + 1) + 3;
- } else {
- // Skip remaining blocks.
- break;
- }
+ if (block_ofs.x + block_size_x + 12 > grid_size.width) {
+ // Keep last valid cell position for the expansion icon.
+ if (!flag_rects.is_empty() && (expansion_rows == 0)) {
+ const Rect2 &last_rect = flag_rects[flag_rects.size() - 1];
+ arrow_pos = last_rect.get_end();
+ }
+ ++expansion_rows;
+
+ if (expanded) {
+ // Expand grid to next line.
+ block_ofs.x = 4;
+ block_ofs.y += 2 * (bsize + 1) + 3;
+ } else {
+ // Skip remaining blocks.
+ break;
}
-
- ++block_index;
}
- if ((expansion_rows != prev_expansion_rows) && expanded) {
- update_minimum_size();
- }
+ ++block_index;
+ }
- if ((expansion_rows == 0) && (layer_index == layer_count)) {
- // Whole grid was drawn, no need for expansion icon.
- break;
- }
+ if ((expansion_rows != prev_expansion_rows) && expanded) {
+ update_minimum_size();
+ }
- Ref<Texture2D> arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree"));
- ERR_FAIL_COND(arrow.is_null());
+ if ((expansion_rows == 0) && (layer_index == layer_count)) {
+ // Whole grid was drawn, no need for expansion icon.
+ break;
+ }
- Color arrow_color = get_theme_color(SNAME("highlight_color"), SNAME("Editor"));
- arrow_color.a = expand_hovered ? 1.0 : 0.6;
+ Ref<Texture2D> arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree"));
+ ERR_FAIL_COND(arrow.is_null());
- arrow_pos.x += 2.0;
- arrow_pos.y -= arrow->get_height();
+ Color arrow_color = get_theme_color(SNAME("highlight_color"), SNAME("Editor"));
+ arrow_color.a = expand_hovered ? 1.0 : 0.6;
- Rect2 arrow_draw_rect(arrow_pos, arrow->get_size());
- expand_rect = arrow_draw_rect;
- if (expanded) {
- arrow_draw_rect.size.y *= -1.0; // Flip arrow vertically when expanded.
- }
+ arrow_pos.x += 2.0;
+ arrow_pos.y -= arrow->get_height();
- RID ci = get_canvas_item();
- arrow->draw_rect(ci, arrow_draw_rect, false, arrow_color);
+ Rect2 arrow_draw_rect(arrow_pos, arrow->get_size());
+ expand_rect = arrow_draw_rect;
+ if (expanded) {
+ arrow_draw_rect.size.y *= -1.0; // Flip arrow vertically when expanded.
+ }
- } break;
+ RID ci = get_canvas_item();
+ arrow->draw_rect(ci, arrow_draw_rect, false, arrow_color);
- case NOTIFICATION_MOUSE_EXIT: {
- if (expand_hovered) {
- expand_hovered = false;
- update();
- }
- if (hovered_index != -1) {
- hovered_index = -1;
- update();
- }
- } break;
+ } break;
- default:
- break;
- }
- }
+ case NOTIFICATION_MOUSE_EXIT: {
+ if (expand_hovered) {
+ expand_hovered = false;
+ update();
+ }
+ if (hovered_index != -1) {
+ hovered_index = -1;
+ update();
+ }
+ } break;
- void set_flag(uint32_t p_flag) {
- value = p_flag;
- update();
+ default:
+ break;
}
+}
- static void _bind_methods() {
- ADD_SIGNAL(MethodInfo("flag_changed", PropertyInfo(Variant::INT, "flag")));
- }
-};
+void EditorPropertyLayersGrid::set_flag(uint32_t p_flag) {
+ value = p_flag;
+ update();
+}
+
+void EditorPropertyLayersGrid::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("flag_changed", PropertyInfo(Variant::INT, "flag")));
+ ADD_SIGNAL(MethodInfo("rename_confirmed", PropertyInfo(Variant::INT, "layer_id"), PropertyInfo(Variant::STRING, "new_name")));
+}
void EditorPropertyLayers::_set_read_only(bool p_read_only) {
button->set_disabled(p_read_only);
@@ -1005,7 +1095,7 @@ void EditorPropertyLayers::update_property() {
}
void EditorPropertyLayers::setup(LayerType p_layer_type) {
- String basename;
+ layer_type = p_layer_type;
int layer_group_size = 0;
int layer_count = 0;
switch (p_layer_type) {
@@ -1069,6 +1159,13 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) {
grid->layer_count = layer_count;
}
+void EditorPropertyLayers::set_layer_name(int p_index, const String &p_name) {
+ if (ProjectSettings::get_singleton()->has_setting(basename + vformat("/layer_%d", p_index + 1))) {
+ ProjectSettings::get_singleton()->set(basename + vformat("/layer_%d", p_index + 1), p_name);
+ ProjectSettings::get_singleton()->save();
+ }
+}
+
void EditorPropertyLayers::_button_pressed() {
int layer_count = grid->layer_count;
int layer_group_size = grid->layer_group_size;
@@ -1101,6 +1198,10 @@ void EditorPropertyLayers::_menu_pressed(int p_menu) {
_grid_changed(grid->value);
}
+void EditorPropertyLayers::_refresh_names() {
+ setup(layer_type);
+}
+
void EditorPropertyLayers::_bind_methods() {
}
@@ -1110,6 +1211,7 @@ EditorPropertyLayers::EditorPropertyLayers() {
add_child(hb);
grid = memnew(EditorPropertyLayersGrid);
grid->connect("flag_changed", callable_mp(this, &EditorPropertyLayers::_grid_changed));
+ grid->connect("rename_confirmed", callable_mp(this, &EditorPropertyLayers::set_layer_name));
grid->set_h_size_flags(SIZE_EXPAND_FILL);
hb->add_child(grid);
@@ -1126,6 +1228,7 @@ EditorPropertyLayers::EditorPropertyLayers() {
layers->set_hide_on_checkable_item_selection(false);
layers->connect("id_pressed", callable_mp(this, &EditorPropertyLayers::_menu_pressed));
layers->connect("popup_hide", callable_mp((BaseButton *)button, &BaseButton::set_pressed), varray(false));
+ EditorNode::get_singleton()->connect("project_settings_changed", callable_mp(this, &EditorPropertyLayers::_refresh_names));
}
///////////////////// INT /////////////////////////
@@ -2695,7 +2798,7 @@ void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
}
if (!base_node && get_edited_object()->has_method("get_root_path")) {
- base_node = get_edited_object()->call("get_root_path");
+ base_node = Object::cast_to<Node>(get_edited_object()->call("get_root_path"));
}
if (!base_node && Object::cast_to<RefCounted>(get_edited_object())) {
@@ -3215,9 +3318,9 @@ struct EditorPropertyRangeHint {
bool angle_in_degrees = false;
bool greater = true;
bool lesser = true;
- double min = -99999;
- double max = 99999;
- double step = 0;
+ double min = -99999.0;
+ double max = 99999.0;
+ double step = 1.0;
String suffix;
bool exp_range = false;
bool hide_slider = true;
@@ -3228,18 +3331,25 @@ static EditorPropertyRangeHint _parse_range_hint(PropertyHint p_hint, const Stri
EditorPropertyRangeHint hint;
hint.step = p_default_step;
bool degrees = false;
- if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
- hint.greater = false; //if using ranged, assume false by default
+
+ if (p_hint == PROPERTY_HINT_RANGE) {
+ Vector<String> slices = p_hint_text.split(",");
+ ERR_FAIL_COND_V_MSG(slices.size() < 2, hint,
+ vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": Missing required min and/or max values.", p_hint_text));
+
+ hint.greater = false; // If using ranged, assume false by default.
hint.lesser = false;
- hint.min = p_hint_text.get_slice(",", 0).to_float();
- hint.max = p_hint_text.get_slice(",", 1).to_float();
- if (p_hint_text.get_slice_count(",") >= 3) {
- hint.step = p_hint_text.get_slice(",", 2).to_float();
+ hint.min = slices[0].to_float();
+ hint.max = slices[1].to_float();
+
+ if (slices.size() >= 3 && slices[2].is_valid_float()) {
+ // Step is optional, could be something else if not a number.
+ hint.step = slices[2].to_float();
}
hint.hide_slider = false;
- for (int i = 2; i < p_hint_text.get_slice_count(","); i++) {
- String slice = p_hint_text.get_slice(",", i).strip_edges();
+ for (int i = 2; i < slices.size(); i++) {
+ String slice = slices[i].strip_edges();
if (slice == "radians") {
hint.radians = true;
} else if (slice == "degrees") {
@@ -3262,6 +3372,9 @@ static EditorPropertyRangeHint _parse_range_hint(PropertyHint p_hint, const Stri
hint.suffix = U"\u00B0";
}
+ ERR_FAIL_COND_V_MSG(hint.step == 0, hint,
+ vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": Step cannot be 0.", p_hint_text));
+
return hint;
}
@@ -3332,7 +3445,6 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
-
editor->setup(hint.min, hint.max, hint.step, hint.greater, hint.lesser);
return editor;
@@ -3379,6 +3491,10 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
EditorPropertyClassName *editor = memnew(EditorPropertyClassName);
editor->setup("Object", p_hint_text);
return editor;
+ } else if (p_hint == PROPERTY_HINT_LOCALE_ID) {
+ EditorPropertyLocale *editor = memnew(EditorPropertyLocale);
+ editor->setup(p_hint_text);
+ return editor;
} else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) {
Vector<String> extensions = p_hint_text.split(",");
bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE;
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index e62f6823a3..9a16a78ff8 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -33,6 +33,7 @@
#include "editor/create_dialog.h"
#include "editor/editor_inspector.h"
+#include "editor/editor_locale_dialog.h"
#include "editor/editor_resource_picker.h"
#include "editor/editor_spin_slider.h"
#include "editor/property_selector.h"
@@ -153,6 +154,26 @@ public:
EditorPropertyPath();
};
+class EditorPropertyLocale : public EditorProperty {
+ GDCLASS(EditorPropertyLocale, EditorProperty);
+ EditorLocaleDialog *dialog;
+ LineEdit *locale;
+ Button *locale_edit;
+
+ void _locale_selected(const String &p_locale);
+ void _locale_pressed();
+ void _locale_focus_exited();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ void setup(const String &p_hit_string);
+ virtual void update_property() override;
+ EditorPropertyLocale();
+};
+
class EditorPropertyClassName : public EditorProperty {
GDCLASS(EditorPropertyClassName, EditorProperty);
@@ -258,7 +279,46 @@ public:
EditorPropertyFlags();
};
-class EditorPropertyLayersGrid;
+///////////////////// LAYERS /////////////////////////
+
+class EditorPropertyLayersGrid : public Control {
+ GDCLASS(EditorPropertyLayersGrid, Control);
+
+private:
+ Vector<Rect2> flag_rects;
+ Rect2 expand_rect;
+ bool expand_hovered = false;
+ bool expanded = false;
+ int expansion_rows = 0;
+ int hovered_index = -1;
+ bool read_only = false;
+ int renamed_layer_index = -1;
+ PopupMenu *layer_rename;
+ ConfirmationDialog *rename_dialog;
+ LineEdit *rename_dialog_text;
+
+ void _rename_pressed(int p_menu);
+ void _rename_operation_confirm();
+ Size2 get_grid_size() const;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ uint32_t value = 0;
+ int layer_group_size = 0;
+ int layer_count = 0;
+ Vector<String> names;
+ Vector<String> tooltips;
+
+ void set_read_only(bool p_read_only);
+ virtual Size2 get_minimum_size() const override;
+ virtual String get_tooltip(const Point2 &p_pos) const override;
+ void gui_input(const Ref<InputEvent> &p_ev) override;
+ void set_flag(uint32_t p_flag);
+ EditorPropertyLayersGrid();
+};
class EditorPropertyLayers : public EditorProperty {
GDCLASS(EditorPropertyLayers, EditorProperty);
@@ -276,12 +336,14 @@ public:
private:
EditorPropertyLayersGrid *grid;
void _grid_changed(uint32_t p_grid);
+ String basename;
LayerType layer_type;
PopupMenu *layers;
Button *button;
void _button_pressed();
void _menu_pressed(int p_menu);
+ void _refresh_names();
protected:
virtual void _set_read_only(bool p_read_only) override;
@@ -289,6 +351,7 @@ protected:
public:
void setup(LayerType p_layer_type);
+ void set_layer_name(int p_index, const String &p_name);
virtual void update_property() override;
EditorPropertyLayers();
};
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 36203bca36..71a855b22c 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -627,7 +627,7 @@ void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_eve
}
vbox->move_child(reorder_selected_element_hbox, reorder_to_index % page_length + 2);
// Ensure the moving element is visible.
- EditorNode::get_singleton()->get_inspector()->ensure_control_visible(reorder_selected_element_hbox);
+ InspectorDock::get_inspector_singleton()->ensure_control_visible(reorder_selected_element_hbox);
}
}
}
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 6002bcfadc..716643f812 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -328,7 +328,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
} break;
case OBJ_MENU_SHOW_IN_FILE_SYSTEM: {
- FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
+ FileSystemDock *file_system_dock = FileSystemDock::get_singleton();
file_system_dock->navigate_to_path(edited_resource->get_path());
// Ensure that the FileSystem dock is visible.
@@ -862,6 +862,7 @@ void EditorResourcePicker::_ensure_resource_menu() {
edit_menu->connect("id_pressed", callable_mp(this, &EditorResourcePicker::_edit_menu_cbk));
edit_menu->connect("popup_hide", callable_mp((BaseButton *)edit_button, &BaseButton::set_pressed), varray(false));
}
+
EditorResourcePicker::EditorResourcePicker() {
assign_button = memnew(Button);
assign_button->set_flat(true);
@@ -906,14 +907,14 @@ bool EditorScriptPicker::handle_menu_selected(int p_which) {
switch (p_which) {
case OBJ_MENU_NEW_SCRIPT: {
if (script_owner) {
- EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(script_owner, false);
+ SceneTreeDock::get_singleton()->open_script_dialog(script_owner, false);
}
return true;
}
case OBJ_MENU_EXTEND_SCRIPT: {
if (script_owner) {
- EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(script_owner, true);
+ SceneTreeDock::get_singleton()->open_script_dialog(script_owner, true);
}
return true;
}
@@ -958,7 +959,7 @@ bool EditorShaderPicker::handle_menu_selected(int p_which) {
switch (p_which) {
case OBJ_MENU_NEW_SHADER: {
if (material.is_valid()) {
- EditorNode::get_singleton()->get_scene_tree_dock()->open_shader_dialog(material, preferred_mode);
+ SceneTreeDock::get_singleton()->open_shader_dialog(material, preferred_mode);
return true;
}
} break;
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index 3c1799d80c..574abf85ea 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -98,15 +98,15 @@ Error EditorRun::run(const String &p_scene) {
screen_rect.position = DisplayServer::get_singleton()->screen_get_position(screen);
screen_rect.size = DisplayServer::get_singleton()->screen_get_size(screen);
+ Size2 window_size;
+ window_size.x = ProjectSettings::get_singleton()->get("display/window/size/viewport_width");
+ window_size.y = ProjectSettings::get_singleton()->get("display/window/size/viewport_height");
+
Size2 desired_size;
- desired_size.x = ProjectSettings::get_singleton()->get("display/window/size/width");
- desired_size.y = ProjectSettings::get_singleton()->get("display/window/size/height");
-
- Size2 test_size;
- test_size.x = ProjectSettings::get_singleton()->get("display/window/size/test_width");
- test_size.y = ProjectSettings::get_singleton()->get("display/window/size/test_height");
- if (test_size.x > 0 && test_size.y > 0) {
- desired_size = test_size;
+ desired_size.x = ProjectSettings::get_singleton()->get("display/window/size/window_width_override");
+ desired_size.y = ProjectSettings::get_singleton()->get("display/window/size/window_height_override");
+ if (desired_size.x > 0 && desired_size.y > 0) {
+ window_size = desired_size;
}
int window_placement = EditorSettings::get_singleton()->get("run/window_placement/rect");
@@ -136,7 +136,7 @@ Error EditorRun::run(const String &p_scene) {
args.push_back(itos(screen_rect.position.x) + "," + itos(screen_rect.position.y));
} break;
case 1: { // centered
- Vector2 pos = (screen_rect.position) + ((screen_rect.size - desired_size) / 2).floor();
+ Vector2 pos = (screen_rect.position) + ((screen_rect.size - window_size) / 2).floor();
args.push_back("--position");
args.push_back(itos(pos.x) + "," + itos(pos.y));
} break;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index bb0a2ed7c1..f230a9b435 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -339,7 +339,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
{
String lang_hint = "en";
String host_lang = OS::get_singleton()->get_locale();
- host_lang = TranslationServer::standardize_locale(host_lang);
// Skip locales if Text server lack required features.
Vector<String> locales_to_skip;
@@ -365,27 +364,28 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
}
String best;
+ int best_score = 0;
for (const String &locale : get_editor_locales()) {
// Skip locales which we can't render properly (see above comment).
// Test against language code without regional variants (e.g. ur_PK).
String lang_code = locale.get_slice("_", 0);
- if (locales_to_skip.find(lang_code) != -1) {
+ if (locales_to_skip.has(lang_code)) {
continue;
}
lang_hint += ",";
lang_hint += locale;
- if (host_lang == locale) {
- best = locale;
- }
-
- if (best.is_empty() && host_lang.begins_with(locale)) {
+ int score = TranslationServer::get_singleton()->compare_locales(host_lang, locale);
+ if (score > 0 && score >= best_score) {
best = locale;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
}
}
-
- if (best.is_empty()) {
+ if (best_score == 0) {
best = "en";
}
@@ -428,7 +428,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("interface/editor/separate_distraction_mode", false);
_initial_set("interface/editor/automatically_open_screenshots", true);
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/single_window_mode", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
- _initial_set("interface/editor/hide_console_window", false);
_initial_set("interface/editor/mouse_extra_buttons_navigate_history", true);
_initial_set("interface/editor/save_each_scene_on_quit", true); // Regression
#ifdef DEV_ENABLED
@@ -644,10 +643,15 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/2d/bone_outline_size", 2);
_initial_set("editors/2d/viewport_border_color", Color(0.4, 0.4, 1.0, 0.4));
_initial_set("editors/2d/constrain_editor_view", true);
- _initial_set("editors/2d/warped_mouse_panning", true);
- _initial_set("editors/2d/simple_panning", false);
- _initial_set("editors/2d/scroll_to_pan", false);
- _initial_set("editors/2d/pan_speed", 20);
+
+ // Panning
+ // Enum should be in sync with ControlScheme in ViewPanner.
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/panning/2d_editor_panning_scheme", 0, "Scroll Zooms,Scroll Pans");
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/panning/sub_editors_panning_scheme", 0, "Scroll Zooms,Scroll Pans");
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "editors/panning/animation_editors_panning_scheme", 1, "Scroll Zooms,Scroll Pans");
+ _initial_set("editors/panning/simple_panning", false);
+ _initial_set("editors/panning/warped_mouse_panning", true);
+ _initial_set("editors/panning/2d_editor_pan_speed", 20);
// Tiles editor
_initial_set("editors/tiles_editor/display_grid", true);
diff --git a/editor/settings_config_dialog.cpp b/editor/editor_settings_dialog.cpp
index 71edeefd10..2520d662c5 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* settings_config_dialog.cpp */
+/* editor_settings_dialog.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "settings_config_dialog.h"
+#include "editor_settings_dialog.h"
#include "core/config/project_settings.h"
#include "core/input/input_map.h"
@@ -379,7 +379,7 @@ void EditorSettingsDialog::_update_shortcuts() {
// Join the text of the events with a delimiter so they can all be displayed in one cell.
String events_display_string = event_strings.is_empty() ? "None" : String("; ").join(event_strings);
- if (!shortcut_filter.is_subsequence_ofi(action_name) && (events_display_string == "None" || !shortcut_filter.is_subsequence_ofi(events_display_string))) {
+ if (!shortcut_filter.is_subsequence_ofn(action_name) && (events_display_string == "None" || !shortcut_filter.is_subsequence_ofn(events_display_string))) {
continue;
}
@@ -428,7 +428,7 @@ void EditorSettingsDialog::_update_shortcuts() {
// Shortcut Item
- if (!shortcut_filter.is_subsequence_ofi(sc->get_name())) {
+ if (!shortcut_filter.is_subsequence_ofn(sc->get_name())) {
continue;
}
diff --git a/editor/settings_config_dialog.h b/editor/editor_settings_dialog.h
index c3dfd736d5..f1c4ea7770 100644
--- a/editor/settings_config_dialog.h
+++ b/editor/editor_settings_dialog.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* settings_config_dialog.h */
+/* editor_settings_dialog.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SETTINGS_CONFIG_DIALOG_H
-#define SETTINGS_CONFIG_DIALOG_H
+#ifndef EDITOR_SETTINGS_DIALOG_H
+#define EDITOR_SETTINGS_DIALOG_H
#include "editor/action_map_editor.h"
#include "editor/editor_sectioned_inspector.h"
@@ -128,4 +128,4 @@ public:
~EditorSettingsDialog();
};
-#endif // SETTINGS_CONFIG_DIALOG_H
+#endif // EDITOR_SETTINGS_DIALOG_H
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index a8a1dc37ab..308a268e42 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -30,7 +30,9 @@
#include "editor_themes.h"
+#include "core/error/error_macros.h"
#include "core/io/resource_loader.h"
+#include "core/variant/dictionary.h"
#include "editor_fonts.h"
#include "editor_icons.gen.h"
#include "editor_scale.h"
@@ -95,6 +97,7 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false,
Ref<ImageTexture> texture(memnew(ImageTexture));
Ref<Image> img = p_texture->get_image();
+ ERR_FAIL_NULL_V(img, Ref<Texture2D>());
img = img->duplicate();
if (p_flip_y) {
@@ -109,7 +112,8 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false,
}
#ifdef MODULE_SVG_ENABLED
-static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, float p_saturation = 1.0) {
+// See also `generate_icon()` in `scene/resources/default_theme.cpp`.
+static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, float p_saturation = 1.0, Dictionary p_convert_colors = Dictionary()) {
Ref<ImageTexture> icon = memnew(ImageTexture);
Ref<Image> img = memnew(Image);
@@ -117,8 +121,9 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color,
// Generating upsampled icons is slower, and the benefit is hardly visible
// with integer editor scales.
const bool upsample = !Math::is_equal_approx(Math::round(p_scale), p_scale);
- ImageLoaderSVG::create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_color);
-
+ ImageLoaderSVG img_loader;
+ img_loader.set_replace_colors(p_convert_colors);
+ img_loader.create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_color);
if (p_saturation != 1.0) {
img->adjust_bcs(1.0, 1.0, p_saturation);
}
@@ -135,19 +140,36 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color,
void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = true, int p_thumb_size = 32, bool p_only_thumbs = false, float p_icon_saturation = 1.0) {
#ifdef MODULE_SVG_ENABLED
// The default icon theme is designed to be used for a dark theme.
- // This dictionary stores color codes to convert to other colors
+ // This dictionary stores Color values to convert to other colors
// for better readability on a light theme.
+ // Godot Color values are used to avoid the ambiguity of strings
+ // (where "#ffffff", "fff", and "white" are all equivalent).
Dictionary dark_icon_color_dictionary;
// The names of the icons to never convert, even if one of their colors
// are contained in the dictionary above.
Set<StringName> exceptions;
+ // Some of the colors below are listed for completeness sake.
+ // This can be a basis for proper palette validation later.
if (!p_dark_theme) {
// Convert color: FROM TO
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#478cbf", "#478cbf"); // Godot Blue
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#414042", "#414042"); // Godot Gray
+
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffffff", "#414141"); // Pure white
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#000000", "#bfbfbf"); // Pure black
+ // Keep pure RGB colors as is, but list them for explicity.
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff0000", "#ff0000"); // Pure red
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#00ff00", "#00ff00"); // Pure green
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#0000ff", "#0000ff"); // Pure blue
+
+ // GUI Colors
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#e0e0e0", "#5a5a5a"); // Common icon color
- ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffffff", "#414141"); // White
- ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#b4b4b4", "#363636"); // Script darker color
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#fefefe", "#fefefe"); // Forced light color
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#808080", "#808080"); // GUI disabled color
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#b3b3b3", "#363636"); // GUI disabled light color
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#699ce8", "#699ce8"); // GUI highlight color
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f9f9f9", "#606060"); // Scrollbar grabber highlight color
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#c38ef1", "#a85de9"); // Animation
@@ -155,7 +177,10 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#8da5f3", "#3d64dd"); // 2D
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#4b70ea", "#1a3eac"); // 2D Dark
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#8eef97", "#2fa139"); // Control
- ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffdd65", "#ca8a04"); // Node warning
+
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5fb2ff", "#0079f0"); // Selection (blue)
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#003e7a", "#2b74bb"); // Selection (darker blue)
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f7f5cf", "#615f3a"); // Gizmo (yellow)
// Rainbow
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff4545", "#ff2929"); // Red
@@ -166,31 +191,45 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#8045ff", "#702aff"); // Purple
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff4596", "#ff2781"); // Pink
- // Audio gradient
- // Red is defined further below.
+ // Audio gradients
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#e1da5b", "#d6cf4b"); // Yellow
- ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5fff97", "#00f010"); // Green
- ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffca5f", "#fea900"); // Mesh resource (orange)
- ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#0787ff", "#68b6ff"); // Shape resource (blue)
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#62aeff", "#1678e0"); // Frozen gradient top
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#75d1e6", "#41acc5"); // Frozen gradient middle
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#84ffee", "#49ccba"); // Frozen gradient bottom
- ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff5f5f", "#ff3333"); // Red audio gradient + remove (red)
- ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5fff97", "#00db50"); // Add (green)
- ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5fb2ff", "#5caeff"); // Selection (blue)
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f70000", "#c91616"); // Color track red
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eec315", "#d58c0b"); // Color track orange
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#dbee15", "#b7d10a"); // Color track yellow
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#288027", "#218309"); // Color track green
+
+ // Resource groups
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffca5f", "#fea900"); // Mesh resource (orange)
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#2998ff", "#68b6ff"); // Shape resource (blue)
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#a2d2ff", "#4998e3"); // Shape resource (light blue)
// Animation editor tracks
// The property track icon color is set by the common icon color.
- ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ea7940", "#bd5e2c"); // 3D Transform track
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ea7940", "#bd5e2c"); // 3D Position track
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff2b88", "#bd165f"); // 3D Rotation track
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eac840", "#bd9d1f"); // 3D Scale track
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#3cf34e", "#16a827"); // Call Method track
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#2877f6", "#236be6"); // Bezier Curve track
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eae440", "#9f9722"); // Audio Playback track
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#a448f0", "#9853ce"); // Animation Playback track
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5ad5c4", "#0a9c88"); // Blend Shape track
+
+ // Control layouts
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#d6d6d6", "#474747"); // Highlighted part
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#474747", "#d6d6d6"); // Background part
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#919191", "#6e6e6e"); // Border part
// TileSet editor icons
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#fce00e", "#aa8d24"); // New Single Tile
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#0e71fc", "#0350bd"); // New Autotile
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#c6ced4", "#828f9b"); // New Atlas
+ // Visual script
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#41ecad", "#25e3a0"); // VisualScript variant
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#6f91f0", "#6d8eeb"); // VisualScript bool
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5abbef", "#4fb2e9"); // VisualScript int
@@ -209,6 +248,11 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#41ec80", "#2ce573"); // VisualScript RID
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#55f3e3", "#12d5c3"); // VisualScript Object
ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#54ed9e", "#57e99f"); // VisualScript Dictionary
+ // Visual shaders
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#77ce57", "#67c046"); // Vector funcs
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ea686c", "#d95256"); // Vector transforms
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eac968", "#d9b64f"); // Textures and cubemaps
+ ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#cf68ea", "#c050dd"); // Functions and expressions
exceptions.insert("EditorPivot");
exceptions.insert("EditorHandle");
@@ -239,11 +283,9 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
const Color error_color = p_theme->get_color("error_color", "Editor");
const Color success_color = p_theme->get_color("success_color", "Editor");
const Color warning_color = p_theme->get_color("warning_color", "Editor");
- dark_icon_color_dictionary[Color::html("#ff0000")] = error_color;
- dark_icon_color_dictionary[Color::html("#45ff8b")] = success_color;
- dark_icon_color_dictionary[Color::html("#dbab09")] = warning_color;
-
- ImageLoaderSVG::set_convert_colors(&dark_icon_color_dictionary);
+ dark_icon_color_dictionary[Color::html("#ff5f5f")] = error_color;
+ dark_icon_color_dictionary[Color::html("#5fff97")] = success_color;
+ dark_icon_color_dictionary[Color::html("#ffdd65")] = warning_color;
// Generate icons.
if (!p_only_thumbs) {
@@ -255,7 +297,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
}
const int is_exception = exceptions.has(editor_icons_names[i]);
- const Ref<ImageTexture> icon = editor_generate_icon(i, !is_exception, EDSCALE, saturation);
+ const Ref<ImageTexture> icon = editor_generate_icon(i, !is_exception, EDSCALE, saturation, dark_icon_color_dictionary);
p_theme->set_icon(editor_icons_names[i], "EditorIcons", icon);
}
@@ -269,7 +311,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
for (int i = 0; i < editor_bg_thumbs_count; i++) {
const int index = editor_bg_thumbs_indices[i];
const int is_exception = exceptions.has(editor_icons_names[index]);
- const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter);
+ const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter, dark_icon_color_dictionary);
p_theme->set_icon(editor_icons_names[index], "EditorIcons", icon);
}
@@ -278,13 +320,11 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
for (int i = 0; i < editor_md_thumbs_count; i++) {
const int index = editor_md_thumbs_indices[i];
const bool is_exception = exceptions.has(editor_icons_names[index]);
- const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter);
+ const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter, dark_icon_color_dictionary);
p_theme->set_icon(editor_icons_names[index], "EditorIcons", icon);
}
}
-
- ImageLoaderSVG::set_convert_colors(nullptr);
#else
WARN_PRINT("SVG support disabled, editor icons won't be rendered.");
#endif
@@ -387,6 +427,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color font_focus_color = mono_color.lerp(base_color, 0.125);
const Color font_disabled_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.3);
const Color font_readonly_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.65);
+ const Color font_placeholder_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.6);
const Color selection_color = accent_color * Color(1, 1, 1, 0.4);
const Color disabled_color = mono_color.inverted().lerp(base_color, 0.7);
const Color disabled_bg_color = mono_color.inverted().lerp(base_color, 0.9);
@@ -976,8 +1017,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("SceneTabFG", "EditorStyles", style_tab_selected);
theme->set_stylebox("SceneTabBG", "EditorStyles", style_tab_unselected);
theme->set_icon("close", "TabBar", theme->get_icon("GuiClose", "EditorIcons"));
- theme->set_stylebox("close_bg_pressed", "TabBar", style_menu);
- theme->set_stylebox("close_bg_highlight", "TabBar", style_menu);
+ theme->set_stylebox("button_pressed", "TabBar", style_menu);
+ theme->set_stylebox("button_highlight", "TabBar", style_menu);
theme->set_icon("increment", "TabContainer", theme->get_icon("GuiScrollArrowRight", "EditorIcons"));
theme->set_icon("decrement", "TabContainer", theme->get_icon("GuiScrollArrowLeft", "EditorIcons"));
theme->set_icon("increment", "TabBar", theme->get_icon("GuiScrollArrowRight", "EditorIcons"));
@@ -1069,6 +1110,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color", "LineEdit", font_color);
theme->set_color("font_selected_color", "LineEdit", mono_color);
theme->set_color("font_uneditable_color", "LineEdit", font_readonly_color);
+ theme->set_color("font_placeholder_color", "LineEdit", font_placeholder_color);
theme->set_color("caret_color", "LineEdit", font_color);
theme->set_color("selection_color", "LineEdit", selection_color);
theme->set_color("clear_button_color", "LineEdit", font_color);
@@ -1083,6 +1125,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("space", "TextEdit", theme->get_icon("GuiSpace", "EditorIcons"));
theme->set_color("font_color", "TextEdit", font_color);
theme->set_color("font_readonly_color", "TextEdit", font_readonly_color);
+ theme->set_color("font_placeholder_color", "TextEdit", font_placeholder_color);
theme->set_color("caret_color", "TextEdit", font_color);
theme->set_color("selection_color", "TextEdit", selection_color);
theme->set_constant("line_spacing", "TextEdit", 4 * EDSCALE);
@@ -1121,6 +1164,12 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("margin_bottom", "MarginContainer", 0);
theme->set_constant("hseparation", "GridContainer", default_margin_size * EDSCALE);
theme->set_constant("vseparation", "GridContainer", default_margin_size * EDSCALE);
+ theme->set_constant("hseparation", "FlowContainer", default_margin_size * EDSCALE);
+ theme->set_constant("vseparation", "FlowContainer", default_margin_size * EDSCALE);
+ theme->set_constant("hseparation", "HFlowContainer", default_margin_size * EDSCALE);
+ theme->set_constant("vseparation", "HFlowContainer", default_margin_size * EDSCALE);
+ theme->set_constant("hseparation", "VFlowContainer", default_margin_size * EDSCALE);
+ theme->set_constant("vseparation", "VFlowContainer", default_margin_size * EDSCALE);
// Window
@@ -1211,7 +1260,22 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("focus", "RichTextLabel", make_empty_stylebox());
theme->set_stylebox("normal", "RichTextLabel", style_tree_bg);
+ // Editor help.
+ theme->set_color("title_color", "EditorHelp", accent_color);
theme->set_color("headline_color", "EditorHelp", mono_color);
+ theme->set_color("text_color", "EditorHelp", font_color);
+ theme->set_color("comment_color", "EditorHelp", font_color * Color(1, 1, 1, 0.6));
+ theme->set_color("symbol_color", "EditorHelp", font_color * Color(1, 1, 1, 0.6));
+ theme->set_color("value_color", "EditorHelp", font_color * Color(1, 1, 1, 0.6));
+ theme->set_color("qualifier_color", "EditorHelp", font_color * Color(1, 1, 1, 0.8));
+ theme->set_color("type_color", "EditorHelp", accent_color.lerp(font_color, 0.5));
+ theme->set_color("selection_color", "EditorHelp", accent_color * Color(1, 1, 1, 0.4));
+ theme->set_color("link_color", "EditorHelp", accent_color.lerp(mono_color, 0.8));
+ theme->set_color("code_color", "EditorHelp", accent_color.lerp(mono_color, 0.6));
+ theme->set_color("kbd_color", "EditorHelp", accent_color.lerp(property_color, 0.6));
+ theme->set_constant("line_separation", "EditorHelp", Math::round(6 * EDSCALE));
+ theme->set_constant("table_hseparation", "EditorHelp", 16 * EDSCALE);
+ theme->set_constant("table_vseparation", "EditorHelp", 6 * EDSCALE);
// Panel
theme->set_stylebox("panel", "Panel", make_flat_stylebox(dark_color_1, 6, 4, 6, 4, corner_width));
@@ -1419,6 +1483,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_info_3d_viewport->set_border_width_all(0);
theme->set_stylebox("Information3dViewport", "EditorStyles", style_info_3d_viewport);
+ // Asset Library.
+ theme->set_stylebox("panel", "AssetLib", style_content_panel);
+ theme->set_color("status_color", "AssetLib", Color(0.5, 0.5, 0.5));
+ theme->set_icon("dismiss", "AssetLib", theme->get_icon("Close", "EditorIcons"));
+
// Theme editor.
theme->set_color("preview_picker_overlay_color", "ThemeEditor", Color(0.1, 0.1, 0.1, 0.25));
Color theme_preview_picker_bg_color = accent_color;
diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp
index df0588c641..6c9e4ab0fc 100644
--- a/editor/editor_toaster.cpp
+++ b/editor/editor_toaster.cpp
@@ -362,6 +362,7 @@ Control *EditorToaster::popup(Control *p_control, Severity p_severity, double p_
close_button->set_flat(true);
close_button->set_icon(get_theme_icon("Close", "EditorIcons"));
close_button->connect("pressed", callable_bind(callable_mp(this, &EditorToaster::close), panel));
+ close_button->connect("theme_changed", callable_bind(callable_mp(this, &EditorToaster::_close_button_theme_changed), close_button));
hbox_container->add_child(close_button);
}
@@ -438,6 +439,13 @@ void EditorToaster::close(Control *p_control) {
toasts[p_control].popped = false;
}
+void EditorToaster::_close_button_theme_changed(Control *p_close_button) {
+ Button *close_button = Object::cast_to<Button>(p_close_button);
+ if (close_button) {
+ close_button->set_icon(get_theme_icon("Close", "EditorIcons"));
+ }
+}
+
EditorToaster *EditorToaster::get_singleton() {
return singleton;
}
diff --git a/editor/editor_toaster.h b/editor/editor_toaster.h
index b626a47d0c..5f220e98c4 100644
--- a/editor/editor_toaster.h
+++ b/editor/editor_toaster.h
@@ -95,6 +95,7 @@ private:
void _set_notifications_enabled(bool p_enabled);
void _repop_old();
void _popup_str(String p_message, Severity p_severity, String p_tooltip);
+ void _close_button_theme_changed(Control *p_close_button);
protected:
static EditorToaster *singleton;
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 0253307d5a..bc71072d29 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -48,6 +48,8 @@
#include "servers/display_server.h"
#include "shader_create_dialog.h"
+FileSystemDock *FileSystemDock::singleton = nullptr;
+
Ref<Texture2D> FileSystemDock::_get_tree_item_icon(bool p_is_valid, String p_file_type) {
Ref<Texture2D> file_icon;
if (!p_is_valid) {
@@ -1472,12 +1474,18 @@ void FileSystemDock::_folder_removed(String p_folder) {
void FileSystemDock::_rename_operation_confirm() {
String new_name = rename_dialog_text->get_text().strip_edges();
+ String old_name = tree->get_selected()->get_text(0);
if (new_name.length() == 0) {
EditorNode::get_singleton()->show_warning(TTR("No name provided."));
return;
} else if (new_name.find("/") != -1 || new_name.find("\\") != -1 || new_name.find(":") != -1) {
EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
return;
+ } else if (to_rename.is_file && old_name.get_extension() != new_name.get_extension()) {
+ if (!EditorFileSystem::get_singleton()->get_valid_extensions().find(new_name.get_extension())) {
+ EditorNode::get_singleton()->show_warning(TTR("This file extension is not recognized by the editor.\nIf you want to rename it anyway, use your operating system's file manager.\nAfter renaming to an unknown extension, the file won't be shown in the editor anymore."));
+ return;
+ }
}
String old_path = to_rename.path.ends_with("/") ? to_rename.path.substr(0, to_rename.path.length() - 1) : to_rename.path;
@@ -1793,7 +1801,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
// Add the files from favorites.
Vector<String> favorites = EditorSettings::get_singleton()->get_favorites();
for (int i = 0; i < p_selected.size(); i++) {
- if (favorites.find(p_selected[i]) == -1) {
+ if (!favorites.has(p_selected[i])) {
favorites.push_back(p_selected[i]);
}
}
@@ -2316,7 +2324,7 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data,
Vector<String> fnames = drag_data["files"];
Vector<String> favorites = EditorSettings::get_singleton()->get_favorites();
for (int i = 0; i < fnames.size(); i++) {
- if (favorites.find(fnames[i]) == -1) {
+ if (!favorites.has(fnames[i])) {
favorites.push_back(fnames[i]);
}
}
@@ -2329,7 +2337,7 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data,
String to_dir;
bool favorite;
_get_drag_target_folder(to_dir, favorite, p_point, p_from);
- EditorNode::get_singleton()->get_scene_tree_dock()->save_branch_to_file(to_dir);
+ SceneTreeDock::get_singleton()->save_branch_to_file(to_dir);
}
}
@@ -2500,6 +2508,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
String fpath = p_paths[0];
String item_text = fpath.ends_with("/") ? TTR("Open in File Manager") : TTR("Show in File Manager");
p_popup->add_icon_item(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), item_text, FILE_SHOW_IN_EXPLORER);
+ path = fpath;
}
}
@@ -2536,6 +2545,9 @@ void FileSystemDock::_tree_rmb_empty(const Vector2 &p_pos) {
tree_popup->add_icon_item(get_theme_icon(SNAME("Script"), SNAME("EditorIcons")), TTR("New Script..."), FILE_NEW_SCRIPT);
tree_popup->add_icon_item(get_theme_icon(SNAME("Object"), SNAME("EditorIcons")), TTR("New Resource..."), FILE_NEW_RESOURCE);
tree_popup->add_icon_item(get_theme_icon(SNAME("TextFile"), SNAME("EditorIcons")), TTR("New TextFile..."), FILE_NEW_TEXTFILE);
+ tree_popup->add_separator();
+ tree_popup->add_icon_item(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), TTR("Open in File Manager"), FILE_SHOW_IN_EXPLORER);
+
tree_popup->set_position(tree->get_screen_position() + p_pos);
tree_popup->reset_size();
tree_popup->popup();
@@ -2575,6 +2587,8 @@ void FileSystemDock::_file_list_rmb_pressed(const Vector2 &p_pos) {
return;
}
+ path = current_path->get_text();
+
file_list_popup->clear();
file_list_popup->reset_size();
@@ -2731,11 +2745,11 @@ void FileSystemDock::_update_import_dock() {
}
if (imports.size() == 0) {
- EditorNode::get_singleton()->get_import_dock()->clear();
+ ImportDock::get_singleton()->clear();
} else if (imports.size() == 1) {
- EditorNode::get_singleton()->get_import_dock()->set_edit_path(imports[0]);
+ ImportDock::get_singleton()->set_edit_path(imports[0]);
} else {
- EditorNode::get_singleton()->get_import_dock()->set_edit_multiple_paths(imports);
+ ImportDock::get_singleton()->set_edit_multiple_paths(imports);
}
import_dock_needs_update = false;
@@ -2804,6 +2818,7 @@ void FileSystemDock::_bind_methods() {
}
FileSystemDock::FileSystemDock(EditorNode *p_editor) {
+ singleton = this;
set_name("FileSystem");
editor = p_editor;
path = "res://";
@@ -3039,4 +3054,5 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
}
FileSystemDock::~FileSystemDock() {
+ singleton = nullptr;
}
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 8d50f05da9..1dc986dcb2 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -304,6 +304,12 @@ private:
void _feature_profile_changed();
Vector<String> _remove_self_included_paths(Vector<String> selected_strings);
+private:
+ static FileSystemDock *singleton;
+
+public:
+ static FileSystemDock *get_singleton() { return singleton; }
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp
index c65d4e9e3b..1644bb9dbe 100644
--- a/editor/groups_editor.cpp
+++ b/editor/groups_editor.cpp
@@ -71,13 +71,13 @@ void GroupDialog::_load_nodes(Node *p_current) {
TreeItem *node = nullptr;
NodePath path = scene_tree->get_edited_scene_root()->get_path_to(p_current);
if (keep && p_current->is_in_group(selected_group)) {
- if (remove_filter->get_text().is_subsequence_ofi(String(p_current->get_name()))) {
+ if (remove_filter->get_text().is_subsequence_ofn(String(p_current->get_name()))) {
node = nodes_to_remove->create_item(remove_node_root);
keep = true;
} else {
keep = false;
}
- } else if (keep && add_filter->get_text().is_subsequence_ofi(String(p_current->get_name()))) {
+ } else if (keep && add_filter->get_text().is_subsequence_ofn(String(p_current->get_name()))) {
node = nodes_to_add->create_item(add_node_root);
keep = true;
} else {
@@ -144,8 +144,8 @@ void GroupDialog::_add_pressed() {
undo_redo->add_undo_method(this, "emit_signal", "group_edited");
// To force redraw of scene tree.
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
}
@@ -173,8 +173,8 @@ void GroupDialog::_removed_pressed() {
undo_redo->add_undo_method(this, "emit_signal", "group_edited");
// To force redraw of scene tree.
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
}
@@ -211,6 +211,10 @@ void GroupDialog::_add_group(String p_name) {
groups->ensure_cursor_is_visible();
}
+void GroupDialog::_add_group_text_changed(const String &p_new_text) {
+ add_group_button->set_disabled(p_new_text.strip_edges().is_empty());
+}
+
void GroupDialog::_group_renamed() {
TreeItem *renamed_group = groups->get_edited();
if (!renamed_group) {
@@ -333,8 +337,8 @@ void GroupDialog::_modify_group_pressed(Object *p_item, int p_column, int p_id)
undo_redo->add_undo_method(this, "emit_signal", "group_edited");
// To force redraw of scene tree.
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
} break;
@@ -370,7 +374,8 @@ void GroupDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_TRANSLATION_CHANGED:
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
- case NOTIFICATION_ENTER_TREE: {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
if (is_layout_rtl()) {
add_button->set_icon(groups->get_theme_icon(SNAME("Back"), SNAME("EditorIcons")));
remove_button->set_icon(groups->get_theme_icon(SNAME("Forward"), SNAME("EditorIcons")));
@@ -456,8 +461,9 @@ GroupDialog::GroupDialog() {
chbc->add_child(add_group_text);
add_group_text->set_h_size_flags(Control::SIZE_EXPAND_FILL);
add_group_text->connect("text_submitted", callable_mp(this, &GroupDialog::_add_group_pressed));
+ add_group_text->connect("text_changed", callable_mp(this, &GroupDialog::_add_group_text_changed));
- Button *add_group_button = memnew(Button);
+ add_group_button = memnew(Button);
add_group_button->set_text(TTR("Add"));
chbc->add_child(add_group_button);
add_group_button->connect("pressed", callable_mp(this, &GroupDialog::_add_group_pressed), varray(String()));
@@ -556,6 +562,8 @@ GroupDialog::GroupDialog() {
error = memnew(ConfirmationDialog);
add_child(error);
error->get_ok_button()->set_text(TTR("Close"));
+
+ _add_group_text_changed("");
}
////////////////////////////////////////////////////////////////////////////////
@@ -570,6 +578,7 @@ void GroupsEditor::_add_group(const String &p_group) {
return;
}
+ group_name->clear();
if (node->is_in_group(name)) {
return;
}
@@ -582,12 +591,10 @@ void GroupsEditor::_add_group(const String &p_group) {
undo_redo->add_undo_method(this, "update_tree");
// To force redraw of scene tree.
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
-
- group_name->clear();
}
void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id) {
@@ -610,8 +617,8 @@ void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id) {
undo_redo->add_undo_method(this, "update_tree");
// To force redraw of scene tree.
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
} break;
@@ -621,6 +628,10 @@ void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id) {
}
}
+void GroupsEditor::_group_name_changed(const String &p_new_text) {
+ add->set_disabled(p_new_text.strip_edges().is_empty());
+}
+
struct _GroupInfoComparator {
bool operator()(const Node::GroupInfo &p_a, const Node::GroupInfo &p_b) const {
return p_a.name.operator String() < p_b.name.operator String();
@@ -710,6 +721,7 @@ GroupsEditor::GroupsEditor() {
group_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hbc->add_child(group_name);
group_name->connect("text_submitted", callable_mp(this, &GroupsEditor::_add_group));
+ group_name->connect("text_changed", callable_mp(this, &GroupsEditor::_group_name_changed));
add = memnew(Button);
add->set_text(TTR("Add"));
@@ -723,6 +735,8 @@ GroupsEditor::GroupsEditor() {
tree->connect("button_pressed", callable_mp(this, &GroupsEditor::_modify_group));
tree->add_theme_constant_override("draw_guides", 1);
add_theme_constant_override("separation", 3 * EDSCALE);
+
+ _group_name_changed("");
}
GroupsEditor::~GroupsEditor() {
diff --git a/editor/groups_editor.h b/editor/groups_editor.h
index 677ef14a1f..aa70ac5bc4 100644
--- a/editor/groups_editor.h
+++ b/editor/groups_editor.h
@@ -49,6 +49,7 @@ class GroupDialog : public AcceptDialog {
TreeItem *groups_root;
LineEdit *add_group_text;
+ Button *add_group_button;
Tree *groups;
@@ -77,6 +78,7 @@ class GroupDialog : public AcceptDialog {
void _add_pressed();
void _removed_pressed();
void _add_group_pressed(const String &p_name);
+ void _add_group_text_changed(const String &p_new_text);
void _group_renamed();
void _rename_group_item(const String &p_old_name, const String &p_new_name);
@@ -122,6 +124,7 @@ class GroupsEditor : public VBoxContainer {
void update_tree();
void _add_group(const String &p_group = "");
void _modify_group(Object *p_item, int p_column, int p_id);
+ void _group_name_changed(const String &p_new_text);
void _show_group_dialog();
diff --git a/editor/icons/AudioBusLayout.svg b/editor/icons/AudioBusLayout.svg
index 9928f369e7..c3bbc882e4 100644
--- a/editor/icons/AudioBusLayout.svg
+++ b/editor/icons/AudioBusLayout.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff7a7a"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#66ff9e"/></linearGradient><path d="m3 1c-1.108 0-2 .89199-2 2v10c0 1.108.89199 2 2 2h2c1.108 0 2-.89199 2-2v-10c0-1.108-.89199-2-2-2zm8 0c-1.108 0-2 .89199-2 2v10c0 1.108.89199 2 2 2h2c1.108 0 2-.89199 2-2v-10c0-1.108-.89199-2-2-2zm-8 1h2c.55401 0 1 .44599 1 1v10c0 .55401-.44599 1-1 1h-2c-.55401 0-1-.44599-1-1v-10c0-.55401.44599-1 1-1z" fill="url(#a)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" 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="m3 1c-1.108 0-2 .89199-2 2v10c0 1.108.89199 2 2 2h2c1.108 0 2-.89199 2-2v-10c0-1.108-.89199-2-2-2zm8 0c-1.108 0-2 .89199-2 2v10c0 1.108.89199 2 2 2h2c1.108 0 2-.89199 2-2v-10c0-1.108-.89199-2-2-2zm-8 1h2c.55401 0 1 .44599 1 1v10c0 .55401-.44599 1-1 1h-2c-.55401 0-1-.44599-1-1v-10c0-.55401.44599-1 1-1z" fill="url(#a)"/></svg>
diff --git a/editor/icons/AudioListener2D.svg b/editor/icons/AudioListener2D.svg
index db84dcfed7..9167460290 100644
--- a/editor/icons/AudioListener2D.svg
+++ b/editor/icons/AudioListener2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1a5 5 0 0 0 -5 5h2a3 3 0 0 1 3-3 3 3 0 0 1 3 3c0 1.75-.54175 2.3583-1.1406 2.8574-.29944.2495-.62954.44071-.97656.69141-.17351.1253-.35729.26529-.53711.49219-.17982.227-.3457.58398-.3457.95898 0 1.2778-.31632 1.5742-.63867 1.7676-.32236.1934-.86133.23242-1.3613.23242h-1v2h1c.5 0 1.461.038922 2.3887-.51758.87316-.5239 1.4826-1.6633 1.5566-3.2266.011365-.0098.027247-.024684.10938-.083984.21547-.1556.63537-.40194 1.0859-.77734.90112-.751 1.8594-2.1445 1.8594-4.3945a5 5 0 0 0 -5-5zm7.9277 1-1.7383 1.0039a6 6 0 0 1 .81055 2.9961 6 6 0 0 1 -.80859 2.998l1.7363 1.002a8 8 0 0 0 0-8z" fill="#a5b7f3"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1a5 5 0 0 0 -5 5h2a3 3 0 0 1 3-3 3 3 0 0 1 3 3c0 1.75-.54175 2.3583-1.1406 2.8574-.29944.2495-.62954.44071-.97656.69141-.17351.1253-.35729.26529-.53711.49219-.17982.227-.3457.58398-.3457.95898 0 1.2778-.31632 1.5742-.63867 1.7676-.32236.1934-.86133.23242-1.3613.23242h-1v2h1c.5 0 1.461.038922 2.3887-.51758.87316-.5239 1.4826-1.6633 1.5566-3.2266.011365-.0098.027247-.024684.10938-.083984.21547-.1556.63537-.40194 1.0859-.77734.90112-.751 1.8594-2.1445 1.8594-4.3945a5 5 0 0 0 -5-5zm7.9277 1-1.7383 1.0039a6 6 0 0 1 .81055 2.9961 6 6 0 0 1 -.80859 2.998l1.7363 1.002a8 8 0 0 0 0-8z" fill="#8da5f3"/></svg>
diff --git a/editor/icons/AudioStreamMP3.svg b/editor/icons/AudioStreamMP3.svg
index dc034c90de..2e54de9faa 100644
--- a/editor/icons/AudioStreamMP3.svg
+++ b/editor/icons/AudioStreamMP3.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff7a7a"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#66ff9e"/></linearGradient><path d="m11.971 1.002c-.08326.00207-.16593.014541-.24609.037109l-7 2c-.42881.12287-.7244.51487-.72461.96094v5.5508c-.16454-.033679-.33205-.050692-.5-.050781-1.3807 0-2.5 1.1193-2.5 2.5-.00000475 1.3807 1.1193 2.5 2.5 2.5 1.3456-.0013 2.4488-1.0674 2.4961-2.4121.0025906-.029226.003894-.058551.0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5c-.000916-.56314-.4664-1.0145-1.0293-.99805zm-1.4707 6.998c-.277 0-.5.223-.5.5v5c0 .277.223.5.5.5s.5-.223.5-.5v-5c0-.277-.223-.5-.5-.5zm2 1c-.277 0-.5.223-.5.5v3c0 .277.223.5.5.5s.5-.223.5-.5v-3c0-.277-.223-.5-.5-.5zm-4 1c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5zm6 0c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5z" fill="url(#a)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" 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="m11.971 1.002c-.08326.00207-.16593.014541-.24609.037109l-7 2c-.42881.12287-.7244.51487-.72461.96094v5.5508c-.16454-.033679-.33205-.050692-.5-.050781-1.3807 0-2.5 1.1193-2.5 2.5-.00000475 1.3807 1.1193 2.5 2.5 2.5 1.3456-.0013 2.4488-1.0674 2.4961-2.4121.0025906-.029226.003894-.058551.0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5c-.000916-.56314-.4664-1.0145-1.0293-.99805zm-1.4707 6.998c-.277 0-.5.223-.5.5v5c0 .277.223.5.5.5s.5-.223.5-.5v-5c0-.277-.223-.5-.5-.5zm2 1c-.277 0-.5.223-.5.5v3c0 .277.223.5.5.5s.5-.223.5-.5v-3c0-.277-.223-.5-.5-.5zm-4 1c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5zm6 0c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5z" fill="url(#a)"/></svg>
diff --git a/editor/icons/AudioStreamOGGVorbis.svg b/editor/icons/AudioStreamOGGVorbis.svg
index dc034c90de..2e54de9faa 100644
--- a/editor/icons/AudioStreamOGGVorbis.svg
+++ b/editor/icons/AudioStreamOGGVorbis.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff7a7a"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#66ff9e"/></linearGradient><path d="m11.971 1.002c-.08326.00207-.16593.014541-.24609.037109l-7 2c-.42881.12287-.7244.51487-.72461.96094v5.5508c-.16454-.033679-.33205-.050692-.5-.050781-1.3807 0-2.5 1.1193-2.5 2.5-.00000475 1.3807 1.1193 2.5 2.5 2.5 1.3456-.0013 2.4488-1.0674 2.4961-2.4121.0025906-.029226.003894-.058551.0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5c-.000916-.56314-.4664-1.0145-1.0293-.99805zm-1.4707 6.998c-.277 0-.5.223-.5.5v5c0 .277.223.5.5.5s.5-.223.5-.5v-5c0-.277-.223-.5-.5-.5zm2 1c-.277 0-.5.223-.5.5v3c0 .277.223.5.5.5s.5-.223.5-.5v-3c0-.277-.223-.5-.5-.5zm-4 1c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5zm6 0c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5z" fill="url(#a)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" 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="m11.971 1.002c-.08326.00207-.16593.014541-.24609.037109l-7 2c-.42881.12287-.7244.51487-.72461.96094v5.5508c-.16454-.033679-.33205-.050692-.5-.050781-1.3807 0-2.5 1.1193-2.5 2.5-.00000475 1.3807 1.1193 2.5 2.5 2.5 1.3456-.0013 2.4488-1.0674 2.4961-2.4121.0025906-.029226.003894-.058551.0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5c-.000916-.56314-.4664-1.0145-1.0293-.99805zm-1.4707 6.998c-.277 0-.5.223-.5.5v5c0 .277.223.5.5.5s.5-.223.5-.5v-5c0-.277-.223-.5-.5-.5zm2 1c-.277 0-.5.223-.5.5v3c0 .277.223.5.5.5s.5-.223.5-.5v-3c0-.277-.223-.5-.5-.5zm-4 1c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5zm6 0c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5z" fill="url(#a)"/></svg>
diff --git a/editor/icons/AudioStreamPlayer.svg b/editor/icons/AudioStreamPlayer.svg
index 6d074c0744..31bd847e37 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" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff7a7a"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#66ff9e"/></linearGradient><g transform="translate(0 -1036.4)"><path d="m10.023 1044.4c-.56139-.013-1.0235.4264-1.0234.9724v5.0542c.0006911.7482.83361 1.2154 1.5.8414l4-2.5262c.66694-.3743.66694-1.3104 0-1.6847l-4-2.5261c-.14505-.082-.30893-.1269-.47656-.131z" fill="#e0e0e0"/><path d="m11.971 1.002a1.0001 1.0001 0 0 0 -.24609.037109l-7 2a1.0001 1.0001 0 0 0 -.72461.96094v5.5508a2.5 2.5 0 0 0 -.5-.050781 2.5 2.5 0 0 0 -2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.4961-2.4121 1.0001 1.0001 0 0 0 .0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5a1.0001 1.0001 0 0 0 -1.0293-.99805z" fill="url(#a)" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><g transform="translate(0 -1036.4)"><path d="m10.023 1044.4c-.56139-.013-1.0235.4264-1.0234.9724v5.0542c.0006911.7482.83361 1.2154 1.5.8414l4-2.5262c.66694-.3743.66694-1.3104 0-1.6847l-4-2.5261c-.14505-.082-.30893-.1269-.47656-.131z" fill="#e0e0e0"/><path d="m11.971 1.002a1.0001 1.0001 0 0 0 -.24609.037109l-7 2a1.0001 1.0001 0 0 0 -.72461.96094v5.5508a2.5 2.5 0 0 0 -.5-.050781 2.5 2.5 0 0 0 -2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.4961-2.4121 1.0001 1.0001 0 0 0 .0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5a1.0001 1.0001 0 0 0 -1.0293-.99805z" fill="url(#a)" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/AudioStreamPlayer2D.svg b/editor/icons/AudioStreamPlayer2D.svg
index 0bf091de29..716680f215 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" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff7a7a"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#66ff9e"/></linearGradient><g transform="translate(0 -1036.4)"><path d="m10.023 1044.4c-.56139-.013-1.0235.4264-1.0234.9724v5.0542c.0006911.7482.83361 1.2154 1.5.8414l4-2.5262c.66694-.3743.66694-1.3104 0-1.6847l-4-2.5261c-.14505-.082-.30893-.1269-.47656-.131z" fill="#8da5f3"/><path d="m11.971 1.002a1.0001 1.0001 0 0 0 -.24609.037109l-7 2a1.0001 1.0001 0 0 0 -.72461.96094v5.5508a2.5 2.5 0 0 0 -.5-.050781 2.5 2.5 0 0 0 -2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.4961-2.4121 1.0001 1.0001 0 0 0 .0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5a1.0001 1.0001 0 0 0 -1.0293-.99805z" fill="url(#a)" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><g transform="translate(0 -1036.4)"><path d="m10.023 1044.4c-.56139-.013-1.0235.4264-1.0234.9724v5.0542c.0006911.7482.83361 1.2154 1.5.8414l4-2.5262c.66694-.3743.66694-1.3104 0-1.6847l-4-2.5261c-.14505-.082-.30893-.1269-.47656-.131z" fill="#8da5f3"/><path d="m11.971 1.002a1.0001 1.0001 0 0 0 -.24609.037109l-7 2a1.0001 1.0001 0 0 0 -.72461.96094v5.5508a2.5 2.5 0 0 0 -.5-.050781 2.5 2.5 0 0 0 -2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.4961-2.4121 1.0001 1.0001 0 0 0 .0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5a1.0001 1.0001 0 0 0 -1.0293-.99805z" fill="url(#a)" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/AudioStreamPlayer3D.svg b/editor/icons/AudioStreamPlayer3D.svg
index ebe3128a21..d89dacd588 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" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff7a7a"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#66ff9e"/></linearGradient><g transform="translate(0 -1036.4)"><path d="m10.023 1044.4c-.56139-.013-1.0235.4264-1.0234.9724v5.0542c.0006911.7482.83361 1.2154 1.5.8414l4-2.5262c.66694-.3743.66694-1.3104 0-1.6847l-4-2.5261c-.14505-.082-.30893-.1269-.47656-.131z" fill="#fc7f7f"/><path d="m11.971 1.002a1.0001 1.0001 0 0 0 -.24609.037109l-7 2a1.0001 1.0001 0 0 0 -.72461.96094v5.5508a2.5 2.5 0 0 0 -.5-.050781 2.5 2.5 0 0 0 -2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.4961-2.4121 1.0001 1.0001 0 0 0 .0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5a1.0001 1.0001 0 0 0 -1.0293-.99805z" fill="url(#a)" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><g transform="translate(0 -1036.4)"><path d="m10.023 1044.4c-.56139-.013-1.0235.4264-1.0234.9724v5.0542c.0006911.7482.83361 1.2154 1.5.8414l4-2.5262c.66694-.3743.66694-1.3104 0-1.6847l-4-2.5261c-.14505-.082-.30893-.1269-.47656-.131z" fill="#fc7f7f"/><path d="m11.971 1.002a1.0001 1.0001 0 0 0 -.24609.037109l-7 2a1.0001 1.0001 0 0 0 -.72461.96094v5.5508a2.5 2.5 0 0 0 -.5-.050781 2.5 2.5 0 0 0 -2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.4961-2.4121 1.0001 1.0001 0 0 0 .0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5a1.0001 1.0001 0 0 0 -1.0293-.99805z" fill="url(#a)" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/AudioStreamSample.svg b/editor/icons/AudioStreamSample.svg
index dc034c90de..2e54de9faa 100644
--- a/editor/icons/AudioStreamSample.svg
+++ b/editor/icons/AudioStreamSample.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="1" y2="15"><stop offset="0" stop-color="#ff7a7a"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#66ff9e"/></linearGradient><path d="m11.971 1.002c-.08326.00207-.16593.014541-.24609.037109l-7 2c-.42881.12287-.7244.51487-.72461.96094v5.5508c-.16454-.033679-.33205-.050692-.5-.050781-1.3807 0-2.5 1.1193-2.5 2.5-.00000475 1.3807 1.1193 2.5 2.5 2.5 1.3456-.0013 2.4488-1.0674 2.4961-2.4121.0025906-.029226.003894-.058551.0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5c-.000916-.56314-.4664-1.0145-1.0293-.99805zm-1.4707 6.998c-.277 0-.5.223-.5.5v5c0 .277.223.5.5.5s.5-.223.5-.5v-5c0-.277-.223-.5-.5-.5zm2 1c-.277 0-.5.223-.5.5v3c0 .277.223.5.5.5s.5-.223.5-.5v-3c0-.277-.223-.5-.5-.5zm-4 1c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5zm6 0c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5z" fill="url(#a)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" 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="m11.971 1.002c-.08326.00207-.16593.014541-.24609.037109l-7 2c-.42881.12287-.7244.51487-.72461.96094v5.5508c-.16454-.033679-.33205-.050692-.5-.050781-1.3807 0-2.5 1.1193-2.5 2.5-.00000475 1.3807 1.1193 2.5 2.5 2.5 1.3456-.0013 2.4488-1.0674 2.4961-2.4121.0025906-.029226.003894-.058551.0039062-.087891v-7.2441l5-1.4277v3.1719l2-1v-3.5c-.000916-.56314-.4664-1.0145-1.0293-.99805zm-1.4707 6.998c-.277 0-.5.223-.5.5v5c0 .277.223.5.5.5s.5-.223.5-.5v-5c0-.277-.223-.5-.5-.5zm2 1c-.277 0-.5.223-.5.5v3c0 .277.223.5.5.5s.5-.223.5-.5v-3c0-.277-.223-.5-.5-.5zm-4 1c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5zm6 0c-.277 0-.5.223-.5.5v1c0 .277.223.5.5.5s.5-.223.5-.5v-1c0-.277-.223-.5-.5-.5z" fill="url(#a)"/></svg>
diff --git a/editor/icons/BoxMesh.svg b/editor/icons/BoxMesh.svg
index 6809b989cc..ef2f77a255 100644
--- a/editor/icons/BoxMesh.svg
+++ b/editor/icons/BoxMesh.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 14.999999 14.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 .88867-7 3.5v7.2227l7 3.5 7-3.5v-7.2227zm0 2.1152 3.9395 1.9707-3.9395 1.9688-3.9395-1.9688zm-5 3.5527 4 2v3.9414l-4-2.002zm10 0v3.9395l-4 2.002v-3.9414z" fill="#ffca5f" stroke-width="1.0667" transform="scale(.9375)"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 .889-7 3.5v7.222l7 3.5 7-3.5v-7.222zm0 2.115 3.94 1.971-3.94 1.968-3.939-1.968zm-5 3.553 4 2v3.941l-4-2.002zm10 0v3.939l-4 2.002v-3.941z" fill="#ffca5f" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/BoxShape3D.svg b/editor/icons/BoxShape3D.svg
index f9012d1fe4..a16f0bb1e1 100644
--- a/editor/icons/BoxShape3D.svg
+++ b/editor/icons/BoxShape3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><path d="m8 1-7 3v8l7 3 7-3v-8z" fill="#2998ff" transform="translate(0 1036.4)"/><path d="m8 1051.4-7-3v-8l7 3z" fill="#68b6ff"/><path d="m1 1040.4 7 3 7-3-7-3z" fill="#a2d2ff"/></g></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1-7 3v8l7 3 7-3v-8z" fill="#2998ff"/><path d="m8 15-7-3v-8l7 3z" fill="#5fb2ff"/><path d="m1 4 7 3 7-3-7-3z" fill="#a2d2ff"/></svg>
diff --git a/editor/icons/Breakpoint.svg b/editor/icons/Breakpoint.svg
index b95c2b511e..60d59b66c8 100644
--- a/editor/icons/Breakpoint.svg
+++ b/editor/icons/Breakpoint.svg
@@ -1 +1 @@
-<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8c0 3.866-3.134 7-7 7s-7-3.134-7-7 3.134-7 7-7 7 3.134 7 7" fill="#e1e1e1"/><pathd="m12 8c0 2.209-1.791 4-4 4s-4-1.791-4-4 1.791-4 4-4 4 1.791 4 4" fill="#f6f6f6"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m13.308 8c0 2.931-2.377 5.308-5.308 5.308s-5.308-2.377-5.308-5.308 2.377-5.308 5.308-5.308 5.308 2.377 5.308 5.308" fill="#e0e0e0"/><path d="m11.033 8c0 1.675-1.358 3.033-3.033 3.033s-3.033-1.358-3.033-3.033 1.358-3.033 3.033-3.033 3.033 1.358 3.033 3.033" fill="#fefefe"/></g></svg>
diff --git a/editor/icons/BusVuEmpty.svg b/editor/icons/BusVuEmpty.svg
index cc72e7cd36..88a14bc3ee 100644
--- a/editor/icons/BusVuEmpty.svg
+++ b/editor/icons/BusVuEmpty.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 16 128" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="926.36" y2="1050.36"><stop offset="0" stop-color="#ff7a7a"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#66ff9e"/></linearGradient><g transform="translate(0 -924.36)"><path d="m3 926.36c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 5c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1z" fill="url(#a)"/><path d="m3 2c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 5c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1z" fill-opacity=".19608" transform="translate(0 924.36)"/></g></svg>
+<svg height="128" viewBox="0 0 16 128" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="926.36" y2="1050.36"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><g transform="translate(0 -924.36)"><path d="m3 926.36c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 5c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1z" fill="url(#a)"/><path d="m3 2c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 5c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1z" fill-opacity=".19608" transform="translate(0 924.36)"/></g></svg>
diff --git a/editor/icons/BusVuFull.svg b/editor/icons/BusVuFull.svg
index 34396c3c18..0bc00971c0 100644
--- a/editor/icons/BusVuFull.svg
+++ b/editor/icons/BusVuFull.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 16 128" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="2" y2="126"><stop offset="0" stop-color="#ff7a7a"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#66ff9e"/></linearGradient><path d="m3 2c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 5c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1z" fill="url(#a)"/></svg>
+<svg height="128" viewBox="0 0 16 128" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="2" y2="126"><stop offset="0" stop-color="#ff5f5f"/><stop offset=".5" stop-color="#e1da5b"/><stop offset="1" stop-color="#5fff97"/></linearGradient><path d="m3 2c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 5c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1zm0 3c-.554 0-1 .446-1 1s.446 1 1 1h10c.554 0 1-.446 1-1s-.446-1-1-1z" fill="url(#a)"/></svg>
diff --git a/editor/icons/CPUParticles2D.svg b/editor/icons/CPUParticles2D.svg
index 29770bc240..2a2c616954 100644
--- a/editor/icons/CPUParticles2D.svg
+++ b/editor/icons/CPUParticles2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m4.5587261.60940813c-.4226244 0-.7617187.3410473-.7617187.76367177v.5078126c0 .1028478.020058.199689.056641.2890624h-1.1933597c-.4226245 0-.7617188.3390944-.7617188.7617188v.921875c-.040428-.00657-.0767989-.0234375-.1191406-.0234375h-.5078125c-.42262448 0-.76367188.3410475-.76367188.7636719v.3730468c0 .4226245.3410474.7617188.76367188.7617188h.5078125c.042396 0 .078663-.016851.1191406-.023437v4.4531248c-.040428-.0066-.076799-.02344-.1191406-.02344h-.5078125c-.42262448 0-.76367188.341047-.76367188.763672v.373047c0 .422625.3410474.761718.76367188.761718h.5078125c.042396 0 .078663-.01685.1191406-.02344v1.125c0 .422624.3390944.763672.7617188.763672h1.1367187v.457031c0 .422624.3390943.763672.7617187.763672h.3730469c.4226244 0 .7636719-.341048.7636719-.763672v-.457031h4.4062501v.457031c0 .422624.339094.763672.761719.763672h.373047c.422624 0 .763671-.341048.763671-.763672v-.457031h1.269532c.422625 0 .763672-.341048.763672-.763672v-1.111328c.01774.0012.03272.0098.05078.0098h.507812c.422624 0 .763672-.339093.763672-.761718v-.373047c0-.422624-.341048-.763672-.763672-.763672h-.507812c-.01803 0-.03307.0085-.05078.0098v-4.4258454c.01774.00122.03272.00977.05078.00977h.507812c.422624 0 .763672-.3390943.763672-.7617188v-.3730512c0-.4226244-.341048-.7636719-.763672-.7636719h-.507812c-.01803 0-.03307.00855-.05078.00977v-.9082075c0-.4226244-.341047-.7617187-.763672-.7617188h-1.328125c.03658-.089375.05859-.1862118.05859-.2890624v-.5078126c0-.42262437-.341047-.76367177-.763671-.76367177h-.373047c-.422625 0-.761719.3410474-.761719.76367177v.5078126c0 .1028478.02006.1996891.05664.2890624h-4.5214809c.036585-.0893749.0585938-.1862118.0585938-.2890624v-.5078126c0-.42262437-.3410475-.76367177-.7636719-.76367177zm3.2382813 2.35742177a3.279661 3.6440678 0 0 1 3.2128906 2.9394532 2.1864407 2.1864407 0 0 1 1.888672 2.1621094 2.1864407 2.1864407 0 0 1 -2.1875 2.1855475h-5.8300782a2.1864407 2.1864407 0 0 1 -2.1855469-2.1855475 2.1864407 2.1864407 0 0 1 1.8847656-2.1640626 3.279661 3.6440678 0 0 1 3.2167969-2.9375zm-2.9160156 8.0156251a.72881355.72881355 0 0 1 .7285156.728516.72881355.72881355 0 0 1 -.7285156.730469.72881355.72881355 0 0 1 -.7285157-.730469.72881355.72881355 0 0 1 .7285157-.728516zm5.8300782 0a.72881355.72881355 0 0 1 .730469.728516.72881355.72881355 0 0 1 -.730469.730469.72881355.72881355 0 0 1 -.7285157-.730469.72881355.72881355 0 0 1 .7285157-.728516zm-2.9140626.728516a.72881355.72881355 0 0 1 .7285156.730469.72881355.72881355 0 0 1 -.7285156.728515.72881355.72881355 0 0 1 -.7285156-.728515.72881355.72881355 0 0 1 .7285156-.730469z" fill="#a3b6f3" fill-opacity=".992157"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m4.5587261.60940813c-.4226244 0-.7617187.3410473-.7617187.76367177v.5078126c0 .1028478.020058.199689.056641.2890624h-1.1933597c-.4226245 0-.7617188.3390944-.7617188.7617188v.921875c-.040428-.00657-.0767989-.0234375-.1191406-.0234375h-.5078125c-.42262448 0-.76367188.3410475-.76367188.7636719v.3730468c0 .4226245.3410474.7617188.76367188.7617188h.5078125c.042396 0 .078663-.016851.1191406-.023437v4.4531248c-.040428-.0066-.076799-.02344-.1191406-.02344h-.5078125c-.42262448 0-.76367188.341047-.76367188.763672v.373047c0 .422625.3410474.761718.76367188.761718h.5078125c.042396 0 .078663-.01685.1191406-.02344v1.125c0 .422624.3390944.763672.7617188.763672h1.1367187v.457031c0 .422624.3390943.763672.7617187.763672h.3730469c.4226244 0 .7636719-.341048.7636719-.763672v-.457031h4.4062501v.457031c0 .422624.339094.763672.761719.763672h.373047c.422624 0 .763671-.341048.763671-.763672v-.457031h1.269532c.422625 0 .763672-.341048.763672-.763672v-1.111328c.01774.0012.03272.0098.05078.0098h.507812c.422624 0 .763672-.339093.763672-.761718v-.373047c0-.422624-.341048-.763672-.763672-.763672h-.507812c-.01803 0-.03307.0085-.05078.0098v-4.4258454c.01774.00122.03272.00977.05078.00977h.507812c.422624 0 .763672-.3390943.763672-.7617188v-.3730512c0-.4226244-.341048-.7636719-.763672-.7636719h-.507812c-.01803 0-.03307.00855-.05078.00977v-.9082075c0-.4226244-.341047-.7617187-.763672-.7617188h-1.328125c.03658-.089375.05859-.1862118.05859-.2890624v-.5078126c0-.42262437-.341047-.76367177-.763671-.76367177h-.373047c-.422625 0-.761719.3410474-.761719.76367177v.5078126c0 .1028478.02006.1996891.05664.2890624h-4.5214809c.036585-.0893749.0585938-.1862118.0585938-.2890624v-.5078126c0-.42262437-.3410475-.76367177-.7636719-.76367177zm3.2382813 2.35742177a3.279661 3.6440678 0 0 1 3.2128906 2.9394532 2.1864407 2.1864407 0 0 1 1.888672 2.1621094 2.1864407 2.1864407 0 0 1 -2.1875 2.1855475h-5.8300782a2.1864407 2.1864407 0 0 1 -2.1855469-2.1855475 2.1864407 2.1864407 0 0 1 1.8847656-2.1640626 3.279661 3.6440678 0 0 1 3.2167969-2.9375zm-2.9160156 8.0156251a.72881355.72881355 0 0 1 .7285156.728516.72881355.72881355 0 0 1 -.7285156.730469.72881355.72881355 0 0 1 -.7285157-.730469.72881355.72881355 0 0 1 .7285157-.728516zm5.8300782 0a.72881355.72881355 0 0 1 .730469.728516.72881355.72881355 0 0 1 -.730469.730469.72881355.72881355 0 0 1 -.7285157-.730469.72881355.72881355 0 0 1 .7285157-.728516zm-2.9140626.728516a.72881355.72881355 0 0 1 .7285156.730469.72881355.72881355 0 0 1 -.7285156.728515.72881355.72881355 0 0 1 -.7285156-.728515.72881355.72881355 0 0 1 .7285156-.730469z" fill="#8da5f3" fill-opacity=".992157"/></svg>
diff --git a/editor/icons/Callable.svg b/editor/icons/Callable.svg
index d689f1a4c4..3f0d33a06c 100644
--- a/editor/icons/Callable.svg
+++ b/editor/icons/Callable.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 4.2333 4.2333" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12 1c-2 2-4 4-7 4h-4v5h4c3 .000038 5 2 7 4zm1 4v5c2.5896-.015798 2.5896-4.9849 0-5zm-11 6v4h2l1-4z" fill="#e0e0e0" transform="scale(.26458)"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m12 1c-2 2-4 4-7 4h-4v5h4c3 0 5 2 7 4zm1 4v5c2.59-.016 2.59-4.985 0-5zm-11 6v4h2l1-4z" fill="#e0e0e0" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/CanvasGroup.svg b/editor/icons/CanvasGroup.svg
index 232ae53231..110fcbd205 100644
--- a/editor/icons/CanvasGroup.svg
+++ b/editor/icons/CanvasGroup.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v8h8v-6h6v-8zm2 2h4v4h-4z" fill="#a5b8f3" fill-opacity=".588235"/><path d="m1 1v2c0 .0000234.446 0 1 0s1 .0000234 1 0v-2c0-.00002341-.446 0-1 0s-1-.00002341-1 0zm12 0v2c0 .0000234.446 0 1 0s1 .0000234 1 0v-2c0-.00002341-.446 0-1 0s-1-.00002341-1 0zm-12 12v2c0 .000023.446 0 1 0s1 .000023 1 0v-2c0-.000023-.446 0-1 0s-1-.000023-1 0zm12 0v2c0 .000023.446 0 1 0s1 .000023 1 0v-2c0-.000023-.446 0-1 0s-1-.000023-1 0z" fill="#a5b7f4"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v8h8v-6h6v-8zm2 2h4v4h-4z" fill="#8da5f3" fill-opacity=".588235"/><path d="m1 1v2c0 .0000234.446 0 1 0s1 .0000234 1 0v-2c0-.00002341-.446 0-1 0s-1-.00002341-1 0zm12 0v2c0 .0000234.446 0 1 0s1 .0000234 1 0v-2c0-.00002341-.446 0-1 0s-1-.00002341-1 0zm-12 12v2c0 .000023.446 0 1 0s1 .000023 1 0v-2c0-.000023-.446 0-1 0s-1-.000023-1 0zm12 0v2c0 .000023.446 0 1 0s1 .000023 1 0v-2c0-.000023-.446 0-1 0s-1-.000023-1 0z" fill="#8da5f3"/></svg>
diff --git a/editor/icons/CanvasModulate.svg b/editor/icons/CanvasModulate.svg
index a96fb75643..86cb7ef64c 100644
--- a/editor/icons/CanvasModulate.svg
+++ b/editor/icons/CanvasModulate.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m1 1037.4v14h14v-14zm2 2h10v10h-10z" fill="#8da5f3"/><g fill-rule="evenodd"><path d="m12 1048.4h-5l5-5z" fill="#70bfff"/><path d="m4 1040.4h5l-5 5z" fill="#ff4545"/><path d="m4 1048.4v-3l5-5h3v3l-5 5z" fill="#7aff70"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m1 1037.4v14h14v-14zm2 2h10v10h-10z" fill="#8da5f3"/><g fill-rule="evenodd"><path d="m12 1048.4h-5l5-5z" fill="#45d7ff"/><path d="m4 1040.4h5l-5 5z" fill="#ff4545"/><path d="m4 1048.4v-3l5-5h3v3l-5 5z" fill="#80ff45"/></g></g></svg>
diff --git a/editor/icons/CapsuleShape2D.svg b/editor/icons/CapsuleShape2D.svg
index 99a67d4641..5b3c411f9b 100644
--- a/editor/icons/CapsuleShape2D.svg
+++ b/editor/icons/CapsuleShape2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-2.77 0-5 2.23-5 5v4c0 2.77 2.23 5 5 5s5-2.23 5-5v-4c0-2.77-2.23-5-5-5zm0 2c1.662 0 3 1.338 3 3v4c0 1.662-1.338 3-3 3s-3-1.338-3-3v-4c0-1.662 1.338-3 3-3z" fill="#68b6ff"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-2.77 0-5 2.23-5 5v4c0 2.77 2.23 5 5 5s5-2.23 5-5v-4c0-2.77-2.23-5-5-5zm0 2c1.662 0 3 1.338 3 3v4c0 1.662-1.338 3-3 3s-3-1.338-3-3v-4c0-1.662 1.338-3 3-3z" fill="#5fb2ff"/></svg>
diff --git a/editor/icons/CapsuleShape3D.svg b/editor/icons/CapsuleShape3D.svg
index 4d5bc522b1..c566d68f19 100644
--- a/editor/icons/CapsuleShape3D.svg
+++ b/editor/icons/CapsuleShape3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m8 1037.4c-2.7527 0-5 2.2419-5 4.9903v4.0175c0 2.7484 2.2473 4.9922 5 4.9922s5-2.2438 5-4.9922v-4.0175c0-2.7484-2.2473-4.9903-5-4.9903z" fill="#68b6ff"/><circle cx="6.5" cy="1040.9" fill="#a2d2ff" r="1.5"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m8 1037.4c-2.7527 0-5 2.2419-5 4.9903v4.0175c0 2.7484 2.2473 4.9922 5 4.9922s5-2.2438 5-4.9922v-4.0175c0-2.7484-2.2473-4.9903-5-4.9903z" fill="#5fb2ff"/><circle cx="6.5" cy="1040.9" fill="#a2d2ff" r="1.5"/></g></svg>
diff --git a/editor/icons/CircleShape2D.svg b/editor/icons/CircleShape2D.svg
index d23ca6d8a3..37a0903a0c 100644
--- a/editor/icons/CircleShape2D.svg
+++ b/editor/icons/CircleShape2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1038.4a6 6 0 0 0 -6 6 6 6 0 0 0 6 6 6 6 0 0 0 6-6 6 6 0 0 0 -6-6z" fill="none" stroke="#68b6ff" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1036.4)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1038.4a6 6 0 0 0 -6 6 6 6 0 0 0 6 6 6 6 0 0 0 6-6 6 6 0 0 0 -6-6z" fill="none" stroke="#5fb2ff" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1036.4)"/></svg>
diff --git a/editor/icons/CodeEdit.svg b/editor/icons/CodeEdit.svg
index 7f08340ffb..8461c739b4 100644
--- a/editor/icons/CodeEdit.svg
+++ b/editor/icons/CodeEdit.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m29 1042.4h1v1h-1z" fill="#fefeff"/><path d="m3 1c-1.1046 0-2 .8954-2 2v10c0 1.1046.89543 2 2 2h10c1.1046 0 2-.8954 2-2v-10c0-1.1046-.89543-2-2-2zm0 2h10v10h-10zm2 1-1 1 1 1-1 1 1 1 2-2zm2 3v1h2v-1z" fill="#8eef97" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m3 1c-1.1046 0-2 .8954-2 2v10c0 1.1046.89543 2 2 2h10c1.1046 0 2-.8954 2-2v-10c0-1.1046-.89543-2-2-2zm0 2h10v10h-10zm2 1-1 1 1 1-1 1 1 1 2-2zm2 3v1h2v-1z" fill="#8eef97" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/ColorRect.svg b/editor/icons/ColorRect.svg
index f08b17ed1f..e69591104f 100644
--- a/editor/icons/ColorRect.svg
+++ b/editor/icons/ColorRect.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m1 1v14h14v-14zm2 2h10v10h-10z" fill="#8eef97" transform="translate(0 1036.4)"/><g fill-rule="evenodd"><path d="m12 1048.4h-4.8l4.8-4.8z" fill="#70bfff"/><path d="m4 1040.4h4.8l-4.8 4.8z" fill="#ff4545"/><path d="m4 1048.4v-3.2l4.8-4.8h3.2v3.2l-4.8 4.8z" fill="#7aff70"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m1 1v14h14v-14zm2 2h10v10h-10z" fill="#8eef97" transform="translate(0 1036.4)"/><g fill-rule="evenodd"><path d="m12 1048.4h-4.8l4.8-4.8z" fill="#45d7ff"/><path d="m4 1040.4h4.8l-4.8 4.8z" fill="#ff4545"/><path d="m4 1048.4v-3.2l4.8-4.8h3.2v3.2l-4.8 4.8z" fill="#80ff45"/></g></g></svg>
diff --git a/editor/icons/ConcavePolygonShape2D.svg b/editor/icons/ConcavePolygonShape2D.svg
index 463fece525..26eda843a8 100644
--- a/editor/icons/ConcavePolygonShape2D.svg
+++ b/editor/icons/ConcavePolygonShape2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14 1050.4h-12v-12l6 6 6-6z" fill="none" stroke="#68b6ff" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1036.4)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14 1050.4h-12v-12l6 6 6-6z" fill="none" stroke="#5fb2ff" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1036.4)"/></svg>
diff --git a/editor/icons/ConcavePolygonShape3D.svg b/editor/icons/ConcavePolygonShape3D.svg
index 60d1a6234f..67631ec0e7 100644
--- a/editor/icons/ConcavePolygonShape3D.svg
+++ b/editor/icons/ConcavePolygonShape3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><g fill="#2998ff"><path d="m8 1-7 3v8l7 3 7-3v-8z" transform="translate(0 1036.4)"/><path d="m8 1037.4-7 3v8l7 3 7-3v-8z"/><path d="m3 1041.4v6l5 2 5-2v-6l-5-2z"/></g><path d="m8 1049.4 5-2-5-2-5 2z" fill="#a2d2ff"/><path d="m8 1045.4 5 2v-6l-5-2z" fill="#68b6ff"/><g transform="translate(0 1036.4)"><path d="m8 1-7 3 2 1 5-2 5 2 2-1z" fill="#a2d2ff"/><path d="m1 4v8l7 3v-2l-5-2v-6z" fill="#68b6ff"/><path d="m15 4-2 1v6l-5 2v2l7-3z" fill="#2998ff"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><g fill="#2998ff"><path d="m8 1-7 3v8l7 3 7-3v-8z" transform="translate(0 1036.4)"/><path d="m8 1037.4-7 3v8l7 3 7-3v-8z"/><path d="m3 1041.4v6l5 2 5-2v-6l-5-2z"/></g><path d="m8 1049.4 5-2-5-2-5 2z" fill="#a2d2ff"/><path d="m8 1045.4 5 2v-6l-5-2z" fill="#5fb2ff"/><g transform="translate(0 1036.4)"><path d="m8 1-7 3 2 1 5-2 5 2 2-1z" fill="#a2d2ff"/><path d="m1 4v8l7 3v-2l-5-2v-6z" fill="#5fb2ff"/><path d="m15 4-2 1v6l-5 2v2l7-3z" fill="#2998ff"/></g></g></svg>
diff --git a/editor/icons/ConvexPolygonShape2D.svg b/editor/icons/ConvexPolygonShape2D.svg
index dc2b0faf81..fa5369aace 100644
--- a/editor/icons/ConvexPolygonShape2D.svg
+++ b/editor/icons/ConvexPolygonShape2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14 1050.4h-12v-6l6-6 6 6z" fill="none" stroke="#68b6ff" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1036.4)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14 1050.4h-12v-6l6-6 6 6z" fill="none" stroke="#5fb2ff" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1036.4)"/></svg>
diff --git a/editor/icons/ConvexPolygonShape3D.svg b/editor/icons/ConvexPolygonShape3D.svg
index 3478289ab1..f0c9101c72 100644
--- a/editor/icons/ConvexPolygonShape3D.svg
+++ b/editor/icons/ConvexPolygonShape3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><path d="m8 1-7 3v8l7 3 7-3z" fill="#2998ff" transform="translate(0 1036.4)"/><path d="m8 1051.4-7-3v-8l7 3z" fill="#68b6ff"/><path d="m8 1-7 3 7 11 7-3z" fill="#2998ff" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><path d="m8 1-7 3v8l7 3 7-3z" fill="#2998ff" transform="translate(0 1036.4)"/><path d="m8 1051.4-7-3v-8l7 3z" fill="#5fb2ff"/><path d="m8 1-7 3 7 11 7-3z" fill="#2998ff" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/CurveClose.svg b/editor/icons/CurveClose.svg
index 5cb8ab890e..032f1c6c55 100644
--- a/editor/icons/CurveClose.svg
+++ b/editor/icons/CurveClose.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 1049.4c-2-9-1-10 8-8" fill="none" stroke="#f5f5f5" stroke-opacity=".39216" stroke-width="2"/><g transform="translate(0 1036.4)"><path d="m5 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm8 0a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-8 8a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#f5f5f5"/><path d="m10 6v2h2v-2zm0 2h-2v2h2zm-2 2h-2v2h2z" fill="#5fb2ff"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 1049.4c-2-9-1-10 8-8" fill="none" stroke="#e0e0e0" stroke-opacity=".39216" stroke-width="2"/><g transform="translate(0 1036.4)"><path d="m5 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm8 0a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-8 8a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#ffffff"/><path d="m10 6v2h2v-2zm0 2h-2v2h2zm-2 2h-2v2h2z" fill="#5fb2ff"/></g></g></svg>
diff --git a/editor/icons/CurveCreate.svg b/editor/icons/CurveCreate.svg
index 1e80817a34..4e406b35f6 100644
--- a/editor/icons/CurveCreate.svg
+++ b/editor/icons/CurveCreate.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 1049.4c-2-9-1-10 8-8" fill="none" stroke="#f5f5f5" stroke-opacity=".39216" stroke-width="2"/><g transform="translate(0 1036.4)"><path d="m5 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm6 5v3h-3v2h3v3h2v-3h3v-2h-3v-3z" fill="#5fff97"/><path d="m13 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-8 8a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#f5f5f5"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 1049.4c-2-9-1-10 8-8" fill="none" stroke="#e0e0e0" stroke-opacity=".39216" stroke-width="2"/><g transform="translate(0 1036.4)"><path d="m5 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm6 5v3h-3v2h3v3h2v-3h3v-2h-3v-3z" fill="#5fff97"/><path d="m13 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-8 8a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#ffffff"/></g></g></svg>
diff --git a/editor/icons/CurveCurve.svg b/editor/icons/CurveCurve.svg
index e43e7ccd8a..b5312aea24 100644
--- a/editor/icons/CurveCurve.svg
+++ b/editor/icons/CurveCurve.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 1049.4c-2-9-1-10 8-8" fill="none" stroke="#f5f5f5" stroke-opacity=".39216" stroke-width="2"/><g transform="translate(0 1036.4)"><path d="m8.4688.4707-2.6875 2.6875h-.0019531a2 2 0 0 0 -.7793-.1582 2 2 0 0 0 -2 2 2 2 0 0 0 .16016.7793l-2.6914 2.6914 1.0625 1.0605 2.6895-2.6895a2 2 0 0 0 .7793.1582 2 2 0 0 0 2-2 2 2 0 0 0 -.16016-.77734l2.6914-2.6914-1.0625-1.0605z" fill="#5fb2ff"/><path d="m13 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-8 8a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#f5f5f5"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 1049.4c-2-9-1-10 8-8" fill="none" stroke="#e0e0e0" stroke-opacity=".39216" stroke-width="2"/><g transform="translate(0 1036.4)"><path d="m8.4688.4707-2.6875 2.6875h-.0019531a2 2 0 0 0 -.7793-.1582 2 2 0 0 0 -2 2 2 2 0 0 0 .16016.7793l-2.6914 2.6914 1.0625 1.0605 2.6895-2.6895a2 2 0 0 0 .7793.1582 2 2 0 0 0 2-2 2 2 0 0 0 -.16016-.77734l2.6914-2.6914-1.0625-1.0605z" fill="#5fb2ff"/><path d="m13 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-8 8a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#ffffff"/></g></g></svg>
diff --git a/editor/icons/CurveDelete.svg b/editor/icons/CurveDelete.svg
index cf15d75bc1..1c8f5e2aae 100644
--- a/editor/icons/CurveDelete.svg
+++ b/editor/icons/CurveDelete.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 1049.4c-2-9-1-10 8-8" fill="none" stroke="#f5f5f5" stroke-opacity=".39216" stroke-width="2"/><path d="m5 1039.4a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm4.8789 5.4648-1.4141 1.4141 2.1211 2.1211-2.1211 2.1211 1.4141 1.4141 2.1211-2.1211 2.1211 2.1211 1.4141-1.4141-2.1211-2.1211 2.1211-2.1211-1.4141-1.4141-2.1211 2.1211z" fill="#ff5f5f"/><path d="m13 1039.4a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-8 8a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#f5f5f5"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 1049.4c-2-9-1-10 8-8" fill="none" stroke="#e0e0e0" stroke-opacity=".39216" stroke-width="2"/><path d="m5 1039.4a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm4.8789 5.4648-1.4141 1.4141 2.1211 2.1211-2.1211 2.1211 1.4141 1.4141 2.1211-2.1211 2.1211 2.1211 1.4141-1.4141-2.1211-2.1211 2.1211-2.1211-1.4141-1.4141-2.1211 2.1211z" fill="#ff5f5f"/><path d="m13 1039.4a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-8 8a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#ffffff"/></g></svg>
diff --git a/editor/icons/CurveEdit.svg b/editor/icons/CurveEdit.svg
index 57e365f3cd..d8318a6bc3 100644
--- a/editor/icons/CurveEdit.svg
+++ b/editor/icons/CurveEdit.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 1049.4c-2-9-1-10 8-8" fill="none" stroke="#f5f5f5" stroke-opacity=".39216" stroke-width="2"/><g transform="translate(0 1036.4)"><path d="m5 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm3 5 3.291 8 .94726-2.8203 1.8828 1.8828.94336-.94141-1.8848-1.8828 2.8203-.94726-8-3.291z" fill="#5fb2ff"/><path d="m13 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-8 8a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#f5f5f5"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 1049.4c-2-9-1-10 8-8" fill="none" stroke="#e0e0e0" stroke-opacity=".39216" stroke-width="2"/><g transform="translate(0 1036.4)"><path d="m5 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm3 5 3.291 8 .94726-2.8203 1.8828 1.8828.94336-.94141-1.8848-1.8828 2.8203-.94726-8-3.291z" fill="#5fb2ff"/><path d="m13 3a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-8 8a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#ffffff"/></g></g></svg>
diff --git a/editor/icons/CylinderShape3D.svg b/editor/icons/CylinderShape3D.svg
index cbff4c8897..1283097848 100644
--- a/editor/icons/CylinderShape3D.svg
+++ b/editor/icons/CylinderShape3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 14.999999 14.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m.890374 3.687944h13.171325v7.699331h-13.171325z" fill="#68b6ff"/><ellipse cx="7.477298" cy="3.722912" fill="#a2d2ff" rx="6.586479" ry="2.820821"/><ellipse cx="7.474688" cy="11.34481" fill="#68b6ff" rx="6.586479" ry="2.820821"/></svg>
+<svg height="16" viewBox="0 0 14.999999 14.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m.890374 3.687944h13.171325v7.699331h-13.171325z" fill="#5fb2ff"/><ellipse cx="7.477298" cy="3.722912" fill="#a2d2ff" rx="6.586479" ry="2.820821"/><ellipse cx="7.474688" cy="11.34481" fill="#5fb2ff" rx="6.586479" ry="2.820821"/></svg>
diff --git a/editor/icons/DebugSkipBreakpointsOff.svg b/editor/icons/DebugSkipBreakpointsOff.svg
index e7228c599f..aef0d45a05 100644
--- a/editor/icons/DebugSkipBreakpointsOff.svg
+++ b/editor/icons/DebugSkipBreakpointsOff.svg
@@ -1 +1 @@
-<svg height="17" viewBox="0 0 17 17" width="17" xmlns="http://www.w3.org/2000/svg"><path d="m4.8983252 3.006855a1.6192284 1.3289529 0 0 1 -.0000431.0097" stroke-width=".626319"/><path d="m8.796752 5.0553513a2.563139 3.6270869 0 0 1 -.0000683.02648" stroke-width=".626319"/><path d="m13.121337 4.512148a6.1594577 6.0545759 0 0 1 -.248787 8.20109 6.1594577 6.0545759 0 0 1 -8.3355404.427215 6.1594577 6.0545759 0 0 1 -1.1151058-8.1311866 6.1594577 6.0545759 0 0 1 8.1530832-1.7576713" fill="#ff8585" fill-opacity=".996078" stroke-width="1.019123"/></svg>
+<svg height="17" viewBox="0 0 17 17" width="17" xmlns="http://www.w3.org/2000/svg"><path d="m4.8983252 3.006855a1.6192284 1.3289529 0 0 1 -.0000431.0097" stroke-width=".626319"/><path d="m8.796752 5.0553513a2.563139 3.6270869 0 0 1 -.0000683.02648" stroke-width=".626319"/><path d="m13.121337 4.512148a6.1594577 6.0545759 0 0 1 -.248787 8.20109 6.1594577 6.0545759 0 0 1 -8.3355404.427215 6.1594577 6.0545759 0 0 1 -1.1151058-8.1311866 6.1594577 6.0545759 0 0 1 8.1530832-1.7576713" fill="#fc7f7f" fill-opacity=".996078" stroke-width="1.019123"/></svg>
diff --git a/editor/icons/DebugSkipBreakpointsOn.svg b/editor/icons/DebugSkipBreakpointsOn.svg
index 0836954bbb..d8fbc6e43a 100644
--- a/editor/icons/DebugSkipBreakpointsOn.svg
+++ b/editor/icons/DebugSkipBreakpointsOn.svg
@@ -1 +1 @@
-<svg height="17" viewBox="0 0 17 17" width="17" xmlns="http://www.w3.org/2000/svg"><path d="m4.8983252 3.006855a1.6192284 1.3289529 0 0 1 -.0000431.0097" stroke-width=".626319"/><path d="m8.796752 5.0553513a2.563139 3.6270869 0 0 1 -.0000683.02648" stroke-width=".626319"/><path d="m13.121337 4.512148a6.1594577 6.0545759 0 0 1 -.248787 8.20109 6.1594577 6.0545759 0 0 1 -8.3355404.427215 6.1594577 6.0545759 0 0 1 -1.1151058-8.1311866 6.1594577 6.0545759 0 0 1 8.1530832-1.7576713" fill="#ff8585" fill-opacity=".996078" stroke-width="1.019123"/><path d="m-9.290675 10.816157h18.575495v2.518711h-18.575495z" fill="#e0e0e0" stroke-width="1.187332" transform="matrix(.70605846 -.70815355 .70605846 .70815355 0 0)"/></svg>
+<svg height="17" viewBox="0 0 17 17" width="17" xmlns="http://www.w3.org/2000/svg"><path d="m4.8983252 3.006855a1.6192284 1.3289529 0 0 1 -.0000431.0097" stroke-width=".626319"/><path d="m8.796752 5.0553513a2.563139 3.6270869 0 0 1 -.0000683.02648" stroke-width=".626319"/><path d="m13.121337 4.512148a6.1594577 6.0545759 0 0 1 -.248787 8.20109 6.1594577 6.0545759 0 0 1 -8.3355404.427215 6.1594577 6.0545759 0 0 1 -1.1151058-8.1311866 6.1594577 6.0545759 0 0 1 8.1530832-1.7576713" fill="#fc7f7f" fill-opacity=".996078" stroke-width="1.019123"/><path d="m-9.290675 10.816157h18.575495v2.518711h-18.575495z" fill="#e0e0e0" stroke-width="1.187332" transform="matrix(.70605846 -.70815355 .70605846 .70815355 0 0)"/></svg>
diff --git a/editor/icons/Decal.svg b/editor/icons/Decal.svg
index 8c33f44360..d1f362bb26 100644
--- a/editor/icons/Decal.svg
+++ b/editor/icons/Decal.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2c-3.3137085 0-6 2.6862915-6 6 0 2.220299 1.2092804 4.153789 3.0019531 5.191406l8.9082029-6.1894529c-.476307-2.8374399-2.937354-5.0019531-5.910156-5.0019531z" fill="#fc7f7f"/><path d="m5.001954 13.191406 8.908202-6.1894529c-.882819-.510985-1.904638-.808594-2.998046-.808594-3.3137079 0-6 2.686292-6 5.9999999 0 .340906.03522.672663.08984.998047z" fill="#ff5d5d"/><path d="m13.910156 7.0019531-8.908202 6.1894529c.882819.510985 1.904638.808594 2.998046.808594 3.313708 0 6-2.686292 6-5.9999999 0-.340906-.03522-.672663-.08984-.998047z" fill="#fc7f7f" fill-opacity=".392157"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2c-3.3137085 0-6 2.6862915-6 6 0 2.220299 1.2092804 4.153789 3.0019531 5.191406l8.9082029-6.1894529c-.476307-2.8374399-2.937354-5.0019531-5.910156-5.0019531z" fill="#fc7f7f"/><path d="m5.001954 13.191406 8.908202-6.1894529c-.882819-.510985-1.904638-.808594-2.998046-.808594-3.3137079 0-6 2.686292-6 5.9999999 0 .340906.03522.672663.08984.998047z" fill="#ff5f5f"/><path d="m13.910156 7.0019531-8.908202 6.1894529c.882819.510985 1.904638.808594 2.998046.808594 3.313708 0 6-2.686292 6-5.9999999 0-.340906-.03522-.672663-.08984-.998047z" fill="#fc7f7f" fill-opacity=".392157"/></svg>
diff --git a/editor/icons/DirectionalLight2D.svg b/editor/icons/DirectionalLight2D.svg
index f30702b502..bc611a71bd 100644
--- a/editor/icons/DirectionalLight2D.svg
+++ b/editor/icons/DirectionalLight2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v3h2v-3zm-2.5352 2.0508-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm7.0703 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-3.5352 1.9492c-1.6569 0-3 1.3432-3 3s1.3431 3 3 3 3-1.3432 3-3-1.3431-3-3-3zm-7 2v2h3v-2zm11 0v2h3v-2zm-7.5352 3.1211-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm7.0703 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-4.5352 1.8789v3h2v-3z" fill="#a5b7f4"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v3h2v-3zm-2.5352 2.0508-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm7.0703 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-3.5352 1.9492c-1.6569 0-3 1.3432-3 3s1.3431 3 3 3 3-1.3432 3-3-1.3431-3-3-3zm-7 2v2h3v-2zm11 0v2h3v-2zm-7.5352 3.1211-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm7.0703 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-4.5352 1.8789v3h2v-3z" fill="#8da5f3"/></svg>
diff --git a/editor/icons/EditorBoneHandle.svg b/editor/icons/EditorBoneHandle.svg
index a6d7c3f878..378c2ea8c1 100644
--- a/editor/icons/EditorBoneHandle.svg
+++ b/editor/icons/EditorBoneHandle.svg
@@ -1 +1 @@
-<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><circle cx="4" cy="4" fill="#fff" r="4"/><circle cx="4" cy="4" fill="#000" r="2.5"/></svg>
+<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><circle cx="4" cy="4" fill="#fff" r="4"/><circle cx="4" cy="4" fill="#000000" r="2.5"/></svg>
diff --git a/editor/icons/EditorControlAnchor.svg b/editor/icons/EditorControlAnchor.svg
index 3383ea121f..a638ee8d00 100644
--- a/editor/icons/EditorControlAnchor.svg
+++ b/editor/icons/EditorControlAnchor.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 0a5 5 0 0 0 -5 5 5 5 0 0 0 5 5 5 5 0 0 0 1.0566-.11914l9.9434 6.1191-6.1172-9.9395a5 5 0 0 0 .11719-1.0605 5 5 0 0 0 -5-5z" fill-opacity=".39216" transform="translate(0 1036.4)"/><path d="m5 1a4 4 0 0 0 -4 4 4 4 0 0 0 4 4 4 4 0 0 0 1.1406-.16992l9.8594 7.1699-7.168-9.8555a4 4 0 0 0 .16797-1.1445 4 4 0 0 0 -4-4z" fill="#8eef97" fill-rule="evenodd" transform="translate(0 1036.4)"/><ellipse cx="3" cy="1039.4" fill="#6e6e6e"/><circle cx="5" cy="1041.4" fill="#8eef97" r="0"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m5 0a5 5 0 0 0 -5 5 5 5 0 0 0 5 5 5 5 0 0 0 1.0566-.11914l9.9434 6.1191-6.1172-9.9395a5 5 0 0 0 .11719-1.0605 5 5 0 0 0 -5-5z" fill-opacity=".39216" transform="translate(0 1036.4)"/><path d="m5 1a4 4 0 0 0 -4 4 4 4 0 0 0 4 4 4 4 0 0 0 1.1406-.16992l9.8594 7.1699-7.168-9.8555a4 4 0 0 0 .16797-1.1445 4 4 0 0 0 -4-4z" fill="#8eef97" fill-rule="evenodd" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/EditorCurveHandle.svg b/editor/icons/EditorCurveHandle.svg
index e0f3256807..e57d6b5dbb 100644
--- a/editor/icons/EditorCurveHandle.svg
+++ b/editor/icons/EditorCurveHandle.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#fefefe" r="4.4" stroke="#000" stroke-linecap="square" stroke-width="1.6"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#ffffff" r="4.4" stroke="#000000" stroke-linecap="square" stroke-width="1.6"/></svg>
diff --git a/editor/icons/EditorPathSharpHandle.svg b/editor/icons/EditorPathSharpHandle.svg
index 5166930cca..1bdf32df57 100644
--- a/editor/icons/EditorPathSharpHandle.svg
+++ b/editor/icons/EditorPathSharpHandle.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14.868629 8.0000002-6.8686288 6.8686288-6.8686293-6.8686288 6.8686293-6.8686293z" fill="#fefefe" stroke="#000" stroke-linecap="square" stroke-width="1.6"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14.868629 8.0000002-6.8686288 6.8686288-6.8686293-6.8686288 6.8686293-6.8686293z" fill="#ffffff" stroke="#000000" stroke-linecap="square" stroke-width="1.6"/></svg>
diff --git a/editor/icons/EditorPathSmoothHandle.svg b/editor/icons/EditorPathSmoothHandle.svg
index 2ab4f3a96a..d4bd434020 100644
--- a/editor/icons/EditorPathSmoothHandle.svg
+++ b/editor/icons/EditorPathSmoothHandle.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m13.6 2.4v11.2h-11.2v-11.2z" fill="#fefefe" stroke="#000" stroke-linecap="square" stroke-width="1.6"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m13.6 2.4v11.2h-11.2v-11.2z" fill="#ffffff" stroke="#000000" stroke-linecap="square" stroke-width="1.6"/></svg>
diff --git a/editor/icons/EditorPositionPrevious.svg b/editor/icons/EditorPositionPrevious.svg
index ba69650d81..159a4c0167 100644
--- a/editor/icons/EditorPositionPrevious.svg
+++ b/editor/icons/EditorPositionPrevious.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1-.11914 4.2662 4.0576 0 0 1 1 .11914v-3.0605zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199-2.9199 2.9201 2.9201 0 0 0 -2.9199-2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -.13477-1 4.2662 4.0576 0 0 1 .13672-1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 .13477 1 4.2662 4.0576 0 0 1 -.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 .11914 4.2662 4.0576 0 0 1 -1-.11914z" fill="#69f" fill-opacity=".69804"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1-.11914 4.2662 4.0576 0 0 1 1 .11914v-3.0605zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199-2.9199 2.9201 2.9201 0 0 0 -2.9199-2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -.13477-1 4.2662 4.0576 0 0 1 .13672-1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 .13477 1 4.2662 4.0576 0 0 1 -.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 .11914 4.2662 4.0576 0 0 1 -1-.11914z" fill="#5fb2ff" fill-opacity=".69804"/></svg>
diff --git a/editor/icons/EditorPositionUnselected.svg b/editor/icons/EditorPositionUnselected.svg
index 881fcab079..30aaa77659 100644
--- a/editor/icons/EditorPositionUnselected.svg
+++ b/editor/icons/EditorPositionUnselected.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 0v4.4199a4.2662 4.0576 0 0 0 -1.709 1.5801h-4.291v4h4.2949a4.2662 4.0576 0 0 0 1.7051 1.582v4.418h4v-4.4199a4.2662 4.0576 0 0 0 1.709-1.5801h4.291v-4h-4.2949a4.2662 4.0576 0 0 0 -1.7051-1.582v-4.418z" fill-opacity=".41077"/><path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1-.11914 4.2662 4.0576 0 0 1 1 .11914v-3.0605zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199-2.9199 2.9201 2.9201 0 0 0 -2.9199-2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -.13477-1 4.2662 4.0576 0 0 1 .13672-1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 .13477 1 4.2662 4.0576 0 0 1 -.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 .11914 4.2662 4.0576 0 0 1 -1-.11914z" fill="#d9d9d9"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 0v4.4199a4.2662 4.0576 0 0 0 -1.709 1.5801h-4.291v4h4.2949a4.2662 4.0576 0 0 0 1.7051 1.582v4.418h4v-4.4199a4.2662 4.0576 0 0 0 1.709-1.5801h4.291v-4h-4.2949a4.2662 4.0576 0 0 0 -1.7051-1.582v-4.418z" fill-opacity=".41077"/><path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1-.11914 4.2662 4.0576 0 0 1 1 .11914v-3.0605zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199-2.9199 2.9201 2.9201 0 0 0 -2.9199-2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -.13477-1 4.2662 4.0576 0 0 1 .13672-1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 .13477 1 4.2662 4.0576 0 0 1 -.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 .11914 4.2662 4.0576 0 0 1 -1-.11914z" fill="#d6d6d6"/></svg>
diff --git a/editor/icons/Error.svg b/editor/icons/Error.svg
index 4b306ae1ca..8478decb41 100644
--- a/editor/icons/Error.svg
+++ b/editor/icons/Error.svg
@@ -1 +1 @@
-<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><rect fill="#ff5d5d" height="8" ry="4" width="8"/></svg>
+<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><rect fill="#ff5f5f" height="8" ry="4" width="8"/></svg>
diff --git a/editor/icons/ErrorWarning.svg b/editor/icons/ErrorWarning.svg
index 53b7be2763..e034bf194f 100644
--- a/editor/icons/ErrorWarning.svg
+++ b/editor/icons/ErrorWarning.svg
@@ -1 +1 @@
-<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m4 0c-2.216 0-4 1.784-4 4s1.784 4 4 4z" fill="#ff5d5d"/><path d="m4 .00000003c2.216 0 4 1.78399997 4 3.99999997s-1.784 4-4 4z" fill="#ffdd65"/></svg>
+<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m4 0c-2.216 0-4 1.784-4 4s1.784 4 4 4z" fill="#ff5f5f"/><path d="m4 .00000003c2.216 0 4 1.78399997 4 3.99999997s-1.784 4-4 4z" fill="#ffdd65"/></svg>
diff --git a/editor/icons/FileBroken.svg b/editor/icons/FileBroken.svg
index 2f5099aa29..d68c89e240 100644
--- a/editor/icons/FileBroken.svg
+++ b/editor/icons/FileBroken.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1v8.5859l1.293-1.293a1.0001 1.0001 0 0 1 .69141-.29102 1.0001 1.0001 0 0 1 .72266.29102l2.293 2.293 2.293-2.293a1.0001 1.0001 0 0 1 1.4141 0l2.293 2.293 1-1v-3.5859h-5v-5h-7zm8 0v4h4zm-6 9.4141-2 2v2.5859h12v-2.5859l-.29297.29297a1.0001 1.0001 0 0 1 -1.4141 0l-2.293-2.293-2.293 2.293a1.0001 1.0001 0 0 1 -1.4141 0l-2.293-2.293z" fill="#ff5d5d" transform="translate(0 -.000017)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1v8.5859l1.293-1.293a1.0001 1.0001 0 0 1 .69141-.29102 1.0001 1.0001 0 0 1 .72266.29102l2.293 2.293 2.293-2.293a1.0001 1.0001 0 0 1 1.4141 0l2.293 2.293 1-1v-3.5859h-5v-5h-7zm8 0v4h4zm-6 9.4141-2 2v2.5859h12v-2.5859l-.29297.29297a1.0001 1.0001 0 0 1 -1.4141 0l-2.293-2.293-2.293 2.293a1.0001 1.0001 0 0 1 -1.4141 0l-2.293-2.293z" fill="#ff5f5f" transform="translate(0 -.000017)"/></svg>
diff --git a/editor/icons/FileBrokenBigThumb.svg b/editor/icons/FileBrokenBigThumb.svg
index effaa0afe9..7dc5a14452 100644
--- a/editor/icons/FileBrokenBigThumb.svg
+++ b/editor/icons/FileBrokenBigThumb.svg
@@ -1 +1 @@
-<svg height="64" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><path d="m14 5c-2.1987 0-4 1.8013-4 4v26.172a1.0001 1.0001 0 0 0 1.707.70703l3.293-3.293 9.293 9.293a1.0001 1.0001 0 0 0 1.4141 0l9.293-9.293 9.293 9.293a1.0001 1.0001 0 0 0 1.4141 0l8-8a1.0001 1.0001 0 0 0 .29297-.70703v-11.172a1.0001 1.0001 0 0 0 -.29297-.70703l-16-16a1.0001 1.0001 0 0 0 -.70703-.29297h-23zm0 2h22v12c0 2.1987 1.8013 4 4 4h12v9.7578l-7 7-9.293-9.293a1.0001 1.0001 0 0 0 -1.4141 0l-9.293 9.293-9.293-9.293a1.0001 1.0001 0 0 0 -1.4141 0l-2.293 2.293v-23.758c0-1.1253.87473-2 2-2zm.98438 28.83a1.0001 1.0001 0 0 0 -.69141.29297l-4 4a1.0001 1.0001 0 0 0 -.29297.70703v14.17c0 2.1987 1.8013 4 4 4h36c2.1987 0 4-1.8013 4-4v-16.17a1.0001 1.0001 0 0 0 -1.707-.70703l-7.293 7.293-9.293-9.293a1.0001 1.0001 0 0 0 -1.4141 0l-9.293 9.293-9.293-9.293a1.0001 1.0001 0 0 0 -.72266-.29297zm.015625 2.4141 9.293 9.293a1.0001 1.0001 0 0 0 1.4141 0l9.293-9.293 9.293 9.293a1.0001 1.0001 0 0 0 1.4141 0l6.293-6.293v13.756c0 1.1253-.87473 2-2 2h-36c-1.1253 0-2-.87473-2-2v-13.756l3-3z" fill="#ff5d5d" transform="translate(0 -.000017)"/></svg>
+<svg height="64" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><path d="m14 5c-2.1987 0-4 1.8013-4 4v26.172a1.0001 1.0001 0 0 0 1.707.70703l3.293-3.293 9.293 9.293a1.0001 1.0001 0 0 0 1.4141 0l9.293-9.293 9.293 9.293a1.0001 1.0001 0 0 0 1.4141 0l8-8a1.0001 1.0001 0 0 0 .29297-.70703v-11.172a1.0001 1.0001 0 0 0 -.29297-.70703l-16-16a1.0001 1.0001 0 0 0 -.70703-.29297h-23zm0 2h22v12c0 2.1987 1.8013 4 4 4h12v9.7578l-7 7-9.293-9.293a1.0001 1.0001 0 0 0 -1.4141 0l-9.293 9.293-9.293-9.293a1.0001 1.0001 0 0 0 -1.4141 0l-2.293 2.293v-23.758c0-1.1253.87473-2 2-2zm.98438 28.83a1.0001 1.0001 0 0 0 -.69141.29297l-4 4a1.0001 1.0001 0 0 0 -.29297.70703v14.17c0 2.1987 1.8013 4 4 4h36c2.1987 0 4-1.8013 4-4v-16.17a1.0001 1.0001 0 0 0 -1.707-.70703l-7.293 7.293-9.293-9.293a1.0001 1.0001 0 0 0 -1.4141 0l-9.293 9.293-9.293-9.293a1.0001 1.0001 0 0 0 -.72266-.29297zm.015625 2.4141 9.293 9.293a1.0001 1.0001 0 0 0 1.4141 0l9.293-9.293 9.293 9.293a1.0001 1.0001 0 0 0 1.4141 0l6.293-6.293v13.756c0 1.1253-.87473 2-2 2h-36c-1.1253 0-2-.87473-2-2v-13.756l3-3z" fill="#ff5f5f" transform="translate(0 -.000017)"/></svg>
diff --git a/editor/icons/FileDead.svg b/editor/icons/FileDead.svg
index f8df831a22..b5c18f3780 100644
--- a/editor/icons/FileDead.svg
+++ b/editor/icons/FileDead.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1v14h12v-9h-5v-5zm8 0v4h4zm-6.0078 6c.1353-.0020779.26567.050774.36133.14648l.64648.64648.64648-.64648c.09183-.091882.21582-.14442.3457-.14648.1353-.00208.26567.050774.36133.14648.19521.19525.19521.51178 0 .70703l-.64648.64648.64648.64648c.19521.19525.19521.51178 0 .70703-.19525.19521-.51178.19521-.70703 0l-.64648-.64648-.64648.64648c-.19525.19521-.51178.19521-.70703 0-.19521-.19525-.19521-.51178 0-.70703l.64648-.64648-.64648-.64648c-.19521-.19525-.19521-.51178 0-.70703.09183-.091882.21582-.14442.3457-.14648zm6 0c.1353-.00208.26567.050774.36133.14648l.64648.64648.64648-.64648c.09183-.091883.21582-.14442.3457-.14648.1353-.00208.26567.050774.36133.14648.19521.19525.19521.51178 0 .70703l-.64648.64648.64648.64648c.19521.19525.19521.51178 0 .70703-.19525.19521-.51178.19521-.70703 0l-.64648-.64648-.64648.64648c-.19525.19521-.51178.19521-.70703 0-.19521-.19525-.19521-.51178 0-.70703l.64648-.64648-.64648-.64648c-.19521-.19525-.19521-.51178 0-.70703.09183-.091882.21582-.14442.3457-.14648zm-6.4922 4h9c.277 0 .5.223.5.5s-.223.5-.5.5h-4.5c0 1.1046-.89543 2-2 2s-2-.8954-2-2h-.5c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zm1.5 1c-.000019.5523.44771 1 1 1s1-.4477 1-1z" fill="#ff5d5d" transform="translate(0 -.000017)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1v14h12v-9h-5v-5zm8 0v4h4zm-6.0078 6c.1353-.0020779.26567.050774.36133.14648l.64648.64648.64648-.64648c.09183-.091882.21582-.14442.3457-.14648.1353-.00208.26567.050774.36133.14648.19521.19525.19521.51178 0 .70703l-.64648.64648.64648.64648c.19521.19525.19521.51178 0 .70703-.19525.19521-.51178.19521-.70703 0l-.64648-.64648-.64648.64648c-.19525.19521-.51178.19521-.70703 0-.19521-.19525-.19521-.51178 0-.70703l.64648-.64648-.64648-.64648c-.19521-.19525-.19521-.51178 0-.70703.09183-.091882.21582-.14442.3457-.14648zm6 0c.1353-.00208.26567.050774.36133.14648l.64648.64648.64648-.64648c.09183-.091883.21582-.14442.3457-.14648.1353-.00208.26567.050774.36133.14648.19521.19525.19521.51178 0 .70703l-.64648.64648.64648.64648c.19521.19525.19521.51178 0 .70703-.19525.19521-.51178.19521-.70703 0l-.64648-.64648-.64648.64648c-.19525.19521-.51178.19521-.70703 0-.19521-.19525-.19521-.51178 0-.70703l.64648-.64648-.64648-.64648c-.19521-.19525-.19521-.51178 0-.70703.09183-.091882.21582-.14442.3457-.14648zm-6.4922 4h9c.277 0 .5.223.5.5s-.223.5-.5.5h-4.5c0 1.1046-.89543 2-2 2s-2-.8954-2-2h-.5c-.277 0-.5-.223-.5-.5s.223-.5.5-.5zm1.5 1c-.000019.5523.44771 1 1 1s1-.4477 1-1z" fill="#ff5f5f" transform="translate(0 -.000017)"/></svg>
diff --git a/editor/icons/FileDeadBigThumb.svg b/editor/icons/FileDeadBigThumb.svg
index ca4578e7b7..0c1e3e03c6 100644
--- a/editor/icons/FileDeadBigThumb.svg
+++ b/editor/icons/FileDeadBigThumb.svg
@@ -1 +1 @@
-<svg height="64" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><path d="m14 993.36c-2.1987 0-4 1.8013-4 4v46c0 2.1987 1.8013 4 4 4h36c2.1987 0 4-1.8013 4-4v-33h-.0078c.002-.2483-.0793-.501-.28516-.707l-16-16c-.18788-.18693-.44247-.28939-.70704-.28907v-.004zm0 2h22v12c0 2.1987 1.8013 4 4 4h12v32c0 1.1253-.87472 2-2 2h-36c-1.1253 0-2-.8747-2-2v-46c0-1.1253.87472-2 2-2zm2.9512 22.002a1 1 0 0 0 -.60938.2461 1 1 0 0 0 -.09375 1.4121l2.9238 3.3398-2.9238 3.3418a1 1 0 0 0 .09375 1.4121 1 1 0 0 0 1.4102-.094l2.748-3.1407 2.748 3.1407a1 1 0 0 0 1.4102.094 1 1 0 0 0 .09375-1.4121l-2.9238-3.3418 2.9238-3.3398a1 1 0 0 0 -.09375-1.4121 1 1 0 0 0 -.63867-.2461 1 1 0 0 0 -.77148.3398l-2.748 3.1406-2.748-3.1406a1 1 0 0 0 -.80078-.3398zm23 0a1 1 0 0 0 -.60938.2461 1 1 0 0 0 -.09375 1.4121l2.9238 3.3398-2.9238 3.3418a1 1 0 0 0 .09375 1.4121 1 1 0 0 0 1.4102-.094l2.748-3.1407 2.748 3.1407a1 1 0 0 0 1.4102.094 1 1 0 0 0 .09375-1.4121l-2.9238-3.3418 2.9238-3.3398a1 1 0 0 0 -.09375-1.4121 1 1 0 0 0 -.63867-.2461 1 1 0 0 0 -.77148.3398l-2.748 3.1406-2.748-3.1406a1 1 0 0 0 -.80078-.3398zm-18.951 13.998a1 1 0 0 0 -1 1 1 1 0 0 0 1 1h3v3c0 2.7527 2.2473 5 5 5s5-2.2473 5-5v-3h9a1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm5 2h6v3c0 1.6793-1.3207 3-3 3s-3-1.3207-3-3z" fill="#ff5d5d" transform="translate(0 -988.360017)"/></svg>
+<svg height="64" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><path d="m14 993.36c-2.1987 0-4 1.8013-4 4v46c0 2.1987 1.8013 4 4 4h36c2.1987 0 4-1.8013 4-4v-33h-.0078c.002-.2483-.0793-.501-.28516-.707l-16-16c-.18788-.18693-.44247-.28939-.70704-.28907v-.004zm0 2h22v12c0 2.1987 1.8013 4 4 4h12v32c0 1.1253-.87472 2-2 2h-36c-1.1253 0-2-.8747-2-2v-46c0-1.1253.87472-2 2-2zm2.9512 22.002a1 1 0 0 0 -.60938.2461 1 1 0 0 0 -.09375 1.4121l2.9238 3.3398-2.9238 3.3418a1 1 0 0 0 .09375 1.4121 1 1 0 0 0 1.4102-.094l2.748-3.1407 2.748 3.1407a1 1 0 0 0 1.4102.094 1 1 0 0 0 .09375-1.4121l-2.9238-3.3418 2.9238-3.3398a1 1 0 0 0 -.09375-1.4121 1 1 0 0 0 -.63867-.2461 1 1 0 0 0 -.77148.3398l-2.748 3.1406-2.748-3.1406a1 1 0 0 0 -.80078-.3398zm23 0a1 1 0 0 0 -.60938.2461 1 1 0 0 0 -.09375 1.4121l2.9238 3.3398-2.9238 3.3418a1 1 0 0 0 .09375 1.4121 1 1 0 0 0 1.4102-.094l2.748-3.1407 2.748 3.1407a1 1 0 0 0 1.4102.094 1 1 0 0 0 .09375-1.4121l-2.9238-3.3418 2.9238-3.3398a1 1 0 0 0 -.09375-1.4121 1 1 0 0 0 -.63867-.2461 1 1 0 0 0 -.77148.3398l-2.748 3.1406-2.748-3.1406a1 1 0 0 0 -.80078-.3398zm-18.951 13.998a1 1 0 0 0 -1 1 1 1 0 0 0 1 1h3v3c0 2.7527 2.2473 5 5 5s5-2.2473 5-5v-3h9a1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm5 2h6v3c0 1.6793-1.3207 3-3 3s-3-1.3207-3-3z" fill="#ff5f5f" transform="translate(0 -988.360017)"/></svg>
diff --git a/editor/icons/FileDeadMediumThumb.svg b/editor/icons/FileDeadMediumThumb.svg
index 2d1808b90a..6ca43c8903 100644
--- a/editor/icons/FileDeadMediumThumb.svg
+++ b/editor/icons/FileDeadMediumThumb.svg
@@ -1 +1 @@
-<svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="m5 1c-1.6447 0-3 1.3553-3 3v24c0 1.6447 1.3553 3 3 3h22c1.6447 0 3-1.3553 3-3v-16.809c-.000051-.2652-.10543-.51952-.29297-.70703l-9.1816-9.1895c-.18719-.18825-.44155-.29435-.70703-.29492h-14.818zm0 2h14v6c0 1.6447 1.3553 3 3 3h6v16c0 .5713-.42868 1-1 1h-22c-.57133 0-1-.4287-1-1v-24c0-.5713.42867-1 1-1zm1.9863 11.002a1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l1.293 1.293-1.293 1.293a1 1 0 0 0 0 1.4141 1 1 0 0 0 1.4141 0l1.293-1.293 1.293 1.293a1 1 0 0 0 1.4141 0 1 1 0 0 0 0-1.4141l-1.293-1.293 1.293-1.293a1 1 0 0 0 0-1.4141 1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102l-1.293 1.293-1.293-1.293a1 1 0 0 0 -.7207-.29102zm14 0a1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l1.293 1.293-1.293 1.293a1 1 0 0 0 0 1.4141 1 1 0 0 0 1.4141 0l1.293-1.293 1.293 1.293a1 1 0 0 0 1.4141 0 1 1 0 0 0 0-1.4141l-1.293-1.293 1.293-1.293a1 1 0 0 0 0-1.4141 1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102l-1.293 1.293-1.293-1.293a1 1 0 0 0 -.7207-.29102zm-13.986 7.998a1 1 0 0 0 -1 1 1 1 0 0 0 1 1h1a4 4 0 0 0 2 3.4648 4 4 0 0 0 4 0 4 4 0 0 0 2-3.4648h9a1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm3 2h4a2 2 0 0 1 -2 2 2 2 0 0 1 -2-2z" fill="#ff5d5d" transform="translate(0 -.000017)"/></svg>
+<svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="m5 1c-1.6447 0-3 1.3553-3 3v24c0 1.6447 1.3553 3 3 3h22c1.6447 0 3-1.3553 3-3v-16.809c-.000051-.2652-.10543-.51952-.29297-.70703l-9.1816-9.1895c-.18719-.18825-.44155-.29435-.70703-.29492h-14.818zm0 2h14v6c0 1.6447 1.3553 3 3 3h6v16c0 .5713-.42868 1-1 1h-22c-.57133 0-1-.4287-1-1v-24c0-.5713.42867-1 1-1zm1.9863 11.002a1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l1.293 1.293-1.293 1.293a1 1 0 0 0 0 1.4141 1 1 0 0 0 1.4141 0l1.293-1.293 1.293 1.293a1 1 0 0 0 1.4141 0 1 1 0 0 0 0-1.4141l-1.293-1.293 1.293-1.293a1 1 0 0 0 0-1.4141 1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102l-1.293 1.293-1.293-1.293a1 1 0 0 0 -.7207-.29102zm14 0a1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l1.293 1.293-1.293 1.293a1 1 0 0 0 0 1.4141 1 1 0 0 0 1.4141 0l1.293-1.293 1.293 1.293a1 1 0 0 0 1.4141 0 1 1 0 0 0 0-1.4141l-1.293-1.293 1.293-1.293a1 1 0 0 0 0-1.4141 1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102l-1.293 1.293-1.293-1.293a1 1 0 0 0 -.7207-.29102zm-13.986 7.998a1 1 0 0 0 -1 1 1 1 0 0 0 1 1h1a4 4 0 0 0 2 3.4648 4 4 0 0 0 4 0 4 4 0 0 0 2-3.4648h9a1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm3 2h4a2 2 0 0 1 -2 2 2 2 0 0 1 -2-2z" fill="#ff5f5f" transform="translate(0 -.000017)"/></svg>
diff --git a/editor/icons/GizmoCPUParticles3D.svg b/editor/icons/GizmoCPUParticles3D.svg
index 785cd81625..e62dd530c4 100644
--- a/editor/icons/GizmoCPUParticles3D.svg
+++ b/editor/icons/GizmoCPUParticles3D.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m35.503779 1.2819066c-3.570424 0-6.435164 2.9483368-6.435164 6.6019028v4.3900146c0 .889114.169457 1.726301.478513 2.49893h-10.081759c-3.570424 0-6.435167 2.931453-6.435167 6.585021v7.969562c-.341543-.0568-.648813-.202614-1.006525-.202614h-4.2901096c-3.5704232 0-6.451665 2.948338-6.451665 6.601904v3.224972c0 3.653568 2.8812418 6.585016 6.451665 6.585016h4.2901096c.358169 0 .664563-.14568 1.006525-.202618v38.497043c-.341543-.05706-.648814-.202616-1.006525-.202616h-4.2901096c-3.5704232 0-6.451665 2.948332-6.451665 6.601908v3.224971c0 3.653575 2.8812418 6.585017 6.451665 6.585017h4.2901096c.358169 0 .664563-.145692 1.006525-.202612v9.725542c0 3.6536 2.864743 6.60193 6.435167 6.60193h9.603246v3.951c0 3.65357 2.86474 6.60192 6.435164 6.60192h3.15158c3.57042 0 6.451663-2.94836 6.451663-6.60192v-3.95104h37.224955v3.951c0 3.65358 2.86474 6.60193 6.435166 6.60193h3.151583c3.570418 0 6.451653-2.94836 6.451653-6.60193v-3.951h10.725281c3.57043 0 6.45166-2.94833 6.45166-6.60191v-9.607372c.14985.0105.27643.0846.42899.0846h4.29014c3.5704 0 6.45165-2.931432 6.45165-6.585011v-3.224992c0-3.653565-2.88125-6.601906-6.45165-6.601906h-4.29014c-.15231 0-.27938.07348-.42899.08472v-38.261071c.14985.01042.27643.08445.42899.08445h4.29014c3.5704 0 6.45165-2.931451 6.45165-6.585023v-3.224986c0-3.653566-2.88125-6.601906-6.45165-6.601906h-4.29014c-.15231 0-.27938.07392-.42899.08446v-7.851429c0-3.653567-2.88123-6.585019-6.45166-6.585021h-11.220281c.309043-.772641.494982-1.609791.494982-2.498929v-4.3900086c0-3.6535651-2.881246-6.601903-6.451662-6.601903h-3.15158c-3.570428 0-6.435167 2.9483379-6.435167 6.601903v4.3900146c0 .889115.16948 1.726301.478507 2.49893h-38.198448c.309083-.772642.495011-1.609792.495011-2.49893v-4.3900146c0-3.6535651-2.881243-6.601903-6.451663-6.601903z" fill="#f7f5cf" stroke="#b4b4b4" stroke-width="2.563805"/><g fill="#b4b4b4" stroke-width="8.546018"><path d="m62.861474 21.661698a27.707285 31.502779 0 0 1 27.143197 25.411422 18.471523 18.901669 0 0 1 15.955909 18.691329 18.471523 18.901669 0 0 1 -18.480472 18.893947h-49.25376a18.471523 18.901669 0 0 1 -18.463973-18.893947 18.471523 18.901669 0 0 1 15.922908-18.708215 27.707285 31.502779 0 0 1 27.176191-25.394536z"/><path d="m38.226348 90.956369a6.1571744 6.3005562 0 0 1 6.154657 6.297979 6.1571744 6.3005562 0 0 1 -6.154657 6.314882 6.1571744 6.3005562 0 0 1 -6.154657-6.314882 6.1571744 6.3005562 0 0 1 6.154657-6.297979z"/><path d="m87.480108 90.956369a6.1571744 6.3005562 0 0 1 6.171159 6.297979 6.1571744 6.3005562 0 0 1 -6.171159 6.314882 6.1571744 6.3005562 0 0 1 -6.154656-6.314882 6.1571744 6.3005562 0 0 1 6.154656-6.297979z"/><path d="m62.861474 97.254348a6.1571744 6.3005562 0 0 1 6.154662 6.314882 6.1571744 6.3005562 0 0 1 -6.154662 6.29797 6.1571744 6.3005562 0 0 1 -6.154651-6.29797 6.1571744 6.3005562 0 0 1 6.154651-6.314882z"/></g></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m35.503779 1.2819066c-3.570424 0-6.435164 2.9483368-6.435164 6.6019028v4.3900146c0 .889114.169457 1.726301.478513 2.49893h-10.081759c-3.570424 0-6.435167 2.931453-6.435167 6.585021v7.969562c-.341543-.0568-.648813-.202614-1.006525-.202614h-4.2901096c-3.5704232 0-6.451665 2.948338-6.451665 6.601904v3.224972c0 3.653568 2.8812418 6.585016 6.451665 6.585016h4.2901096c.358169 0 .664563-.14568 1.006525-.202618v38.497043c-.341543-.05706-.648814-.202616-1.006525-.202616h-4.2901096c-3.5704232 0-6.451665 2.948332-6.451665 6.601908v3.224971c0 3.653575 2.8812418 6.585017 6.451665 6.585017h4.2901096c.358169 0 .664563-.145692 1.006525-.202612v9.725542c0 3.6536 2.864743 6.60193 6.435167 6.60193h9.603246v3.951c0 3.65357 2.86474 6.60192 6.435164 6.60192h3.15158c3.57042 0 6.451663-2.94836 6.451663-6.60192v-3.95104h37.224955v3.951c0 3.65358 2.86474 6.60193 6.435166 6.60193h3.151583c3.570418 0 6.451653-2.94836 6.451653-6.60193v-3.951h10.725281c3.57043 0 6.45166-2.94833 6.45166-6.60191v-9.607372c.14985.0105.27643.0846.42899.0846h4.29014c3.5704 0 6.45165-2.931432 6.45165-6.585011v-3.224992c0-3.653565-2.88125-6.601906-6.45165-6.601906h-4.29014c-.15231 0-.27938.07348-.42899.08472v-38.261071c.14985.01042.27643.08445.42899.08445h4.29014c3.5704 0 6.45165-2.931451 6.45165-6.585023v-3.224986c0-3.653566-2.88125-6.601906-6.45165-6.601906h-4.29014c-.15231 0-.27938.07392-.42899.08446v-7.851429c0-3.653567-2.88123-6.585019-6.45166-6.585021h-11.220281c.309043-.772641.494982-1.609791.494982-2.498929v-4.3900086c0-3.6535651-2.881246-6.601903-6.451662-6.601903h-3.15158c-3.570428 0-6.435167 2.9483379-6.435167 6.601903v4.3900146c0 .889115.16948 1.726301.478507 2.49893h-38.198448c.309083-.772642.495011-1.609792.495011-2.49893v-4.3900146c0-3.6535651-2.881243-6.601903-6.451663-6.601903z" fill="#f7f5cf" stroke="#b3b3b3" stroke-width="2.563805"/><g fill="#b3b3b3" stroke-width="8.546018"><path d="m62.861474 21.661698a27.707285 31.502779 0 0 1 27.143197 25.411422 18.471523 18.901669 0 0 1 15.955909 18.691329 18.471523 18.901669 0 0 1 -18.480472 18.893947h-49.25376a18.471523 18.901669 0 0 1 -18.463973-18.893947 18.471523 18.901669 0 0 1 15.922908-18.708215 27.707285 31.502779 0 0 1 27.176191-25.394536z"/><path d="m38.226348 90.956369a6.1571744 6.3005562 0 0 1 6.154657 6.297979 6.1571744 6.3005562 0 0 1 -6.154657 6.314882 6.1571744 6.3005562 0 0 1 -6.154657-6.314882 6.1571744 6.3005562 0 0 1 6.154657-6.297979z"/><path d="m87.480108 90.956369a6.1571744 6.3005562 0 0 1 6.171159 6.297979 6.1571744 6.3005562 0 0 1 -6.171159 6.314882 6.1571744 6.3005562 0 0 1 -6.154656-6.314882 6.1571744 6.3005562 0 0 1 6.154656-6.297979z"/><path d="m62.861474 97.254348a6.1571744 6.3005562 0 0 1 6.154662 6.314882 6.1571744 6.3005562 0 0 1 -6.154662 6.29797 6.1571744 6.3005562 0 0 1 -6.154651-6.29797 6.1571744 6.3005562 0 0 1 6.154651-6.314882z"/></g></svg>
diff --git a/editor/icons/GizmoDirectionalLight.svg b/editor/icons/GizmoDirectionalLight.svg
index 041a694773..d943be79f0 100644
--- a/editor/icons/GizmoDirectionalLight.svg
+++ b/editor/icons/GizmoDirectionalLight.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m64 4c-4.432 0-8 3.568-8 8v16c0 4.432 3.568 8 8 8s8-3.568 8-8v-16c0-4.432-3.568-8-8-8zm-36.77 15.223c-2.045 0-4.0893.78461-5.6562 2.3516-3.1339 3.1339-3.1339 8.1786 0 11.312l11.312 11.314c3.1339 3.1339 8.1806 3.1339 11.314 0s3.1339-8.1806 0-11.314l-11.314-11.312c-1.5669-1.5669-3.6113-2.3516-5.6562-2.3516zm73.539 0c-2.045 0-4.0893.78461-5.6562 2.3516l-11.314 11.312c-3.1339 3.1339-3.1339 8.1806 0 11.314s8.1806 3.1339 11.314 0l11.312-11.314c3.1339-3.1339 3.1339-8.1786 0-11.312-1.567-1.5669-3.6113-2.3516-5.6562-2.3516zm-36.77 20.777a24 24 0 0 0 -24 24 24 24 0 0 0 24 24 24 24 0 0 0 24-24 24 24 0 0 0 -24-24zm-52 16c-4.432 0-8 3.568-8 8s3.568 8 8 8h16c4.432 0 8-3.568 8-8s-3.568-8-8-8zm88 0c-4.432 0-8 3.568-8 8s3.568 8 8 8h16c4.432 0 8-3.568 8-8s-3.568-8-8-8zm-61.455 25.449c-2.045 0-4.0913.78266-5.6582 2.3496l-11.312 11.314c-3.1339 3.1339-3.1339 8.1786 0 11.312 3.1339 3.1339 8.1786 3.1339 11.312 0l11.314-11.312c3.1339-3.1339 3.1339-8.1806 0-11.314-1.5669-1.5669-3.6113-2.3496-5.6562-2.3496zm50.91 0c-2.045 0-4.0893.78266-5.6562 2.3496-3.1339 3.1339-3.1339 8.1806 0 11.314l11.314 11.312c3.1339 3.1339 8.1786 3.1339 11.312 0s3.1339-8.1786 0-11.312l-11.312-11.314c-1.5669-1.5669-3.6132-2.3496-5.6582-2.3496zm-25.455 10.551c-4.432 0-8 3.568-8 8v16c0 4.432 3.568 8 8 8s8-3.568 8-8v-16c0-4.432-3.568-8-8-8z" fill-opacity=".29412" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".98824" stroke-width="2"/><path d="m64 8c-2.216 0-4 1.784-4 4v16c0 2.216 1.784 4 4 4s4-1.784 4-4v-16c0-2.216-1.784-4-4-4zm-36.77 15.227c-1.0225 0-2.0447.39231-2.8281 1.1758-1.5669 1.5669-1.5669 4.0893 0 5.6562l11.312 11.314c1.5669 1.5669 4.0913 1.5669 5.6582 0s1.5669-4.0913 0-5.6582l-11.314-11.312c-.78348-.78348-1.8056-1.1758-2.8281-1.1758zm73.539 0c-1.0225 0-2.0446.39231-2.8281 1.1758l-11.314 11.312c-1.5669 1.5669-1.5669 4.0913 0 5.6582s4.0913 1.5669 5.6582 0l11.313-11.314c1.5669-1.5669 1.5669-4.0893 0-5.6562-.78348-.78348-1.8056-1.1758-2.8281-1.1758zm-36.77 20.773c-11.046.00001-20 8.9543-20 20 .000007 11.046 8.9543 20 20 20s20-8.9543 20-20c-.000008-11.046-8.9543-20-20-20zm-52 16c-2.216 0-4 1.784-4 4s1.784 4 4 4h16c2.216 0 4-1.784 4-4s-1.784-4-4-4zm88 0c-2.216 0-4 1.784-4 4s1.784 4 4 4h16c2.216 0 4-1.784 4-4s-1.784-4-4-4zm-61.455 25.453c-1.0225 0-2.0466.39035-2.8301 1.1738l-11.312 11.314c-1.5669 1.5669-1.5669 4.0893 0 5.6563 1.5669 1.5669 4.0893 1.5669 5.6562 0l11.314-11.313c1.5669-1.5669 1.5669-4.0913 0-5.6582-.78347-.78347-1.8056-1.1738-2.8281-1.1738zm50.91 0c-1.0225 0-2.0447.39035-2.8281 1.1738-1.5669 1.5669-1.5669 4.0913 0 5.6582l11.314 11.313c1.5669 1.5669 4.0893 1.5669 5.6563 0 1.5669-1.567 1.5669-4.0893 0-5.6563l-11.313-11.314c-.78347-.78347-1.8076-1.1738-2.8301-1.1738zm-25.455 10.547c-2.216 0-4 1.784-4 4v16c0 2.216 1.784 4 4 4s4-1.784 4-4v-16c0-2.216-1.784-4-4-4z" fill="#fefefe"/></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m64 4c-4.432 0-8 3.568-8 8v16c0 4.432 3.568 8 8 8s8-3.568 8-8v-16c0-4.432-3.568-8-8-8zm-36.77 15.223c-2.045 0-4.0893.78461-5.6562 2.3516-3.1339 3.1339-3.1339 8.1786 0 11.312l11.312 11.314c3.1339 3.1339 8.1806 3.1339 11.314 0s3.1339-8.1806 0-11.314l-11.314-11.312c-1.5669-1.5669-3.6113-2.3516-5.6562-2.3516zm73.539 0c-2.045 0-4.0893.78461-5.6562 2.3516l-11.314 11.312c-3.1339 3.1339-3.1339 8.1806 0 11.314s8.1806 3.1339 11.314 0l11.312-11.314c3.1339-3.1339 3.1339-8.1786 0-11.312-1.567-1.5669-3.6113-2.3516-5.6562-2.3516zm-36.77 20.777a24 24 0 0 0 -24 24 24 24 0 0 0 24 24 24 24 0 0 0 24-24 24 24 0 0 0 -24-24zm-52 16c-4.432 0-8 3.568-8 8s3.568 8 8 8h16c4.432 0 8-3.568 8-8s-3.568-8-8-8zm88 0c-4.432 0-8 3.568-8 8s3.568 8 8 8h16c4.432 0 8-3.568 8-8s-3.568-8-8-8zm-61.455 25.449c-2.045 0-4.0913.78266-5.6582 2.3496l-11.312 11.314c-3.1339 3.1339-3.1339 8.1786 0 11.312 3.1339 3.1339 8.1786 3.1339 11.312 0l11.314-11.312c3.1339-3.1339 3.1339-8.1806 0-11.314-1.5669-1.5669-3.6113-2.3496-5.6562-2.3496zm50.91 0c-2.045 0-4.0893.78266-5.6562 2.3496-3.1339 3.1339-3.1339 8.1806 0 11.314l11.314 11.312c3.1339 3.1339 8.1786 3.1339 11.312 0s3.1339-8.1786 0-11.312l-11.312-11.314c-1.5669-1.5669-3.6132-2.3496-5.6582-2.3496zm-25.455 10.551c-4.432 0-8 3.568-8 8v16c0 4.432 3.568 8 8 8s8-3.568 8-8v-16c0-4.432-3.568-8-8-8z" fill-opacity=".29412" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".98824" stroke-width="2"/><path d="m64 8c-2.216 0-4 1.784-4 4v16c0 2.216 1.784 4 4 4s4-1.784 4-4v-16c0-2.216-1.784-4-4-4zm-36.77 15.227c-1.0225 0-2.0447.39231-2.8281 1.1758-1.5669 1.5669-1.5669 4.0893 0 5.6562l11.312 11.314c1.5669 1.5669 4.0913 1.5669 5.6582 0s1.5669-4.0913 0-5.6582l-11.314-11.312c-.78348-.78348-1.8056-1.1758-2.8281-1.1758zm73.539 0c-1.0225 0-2.0446.39231-2.8281 1.1758l-11.314 11.312c-1.5669 1.5669-1.5669 4.0913 0 5.6582s4.0913 1.5669 5.6582 0l11.313-11.314c1.5669-1.5669 1.5669-4.0893 0-5.6562-.78348-.78348-1.8056-1.1758-2.8281-1.1758zm-36.77 20.773c-11.046.00001-20 8.9543-20 20 .000007 11.046 8.9543 20 20 20s20-8.9543 20-20c-.000008-11.046-8.9543-20-20-20zm-52 16c-2.216 0-4 1.784-4 4s1.784 4 4 4h16c2.216 0 4-1.784 4-4s-1.784-4-4-4zm88 0c-2.216 0-4 1.784-4 4s1.784 4 4 4h16c2.216 0 4-1.784 4-4s-1.784-4-4-4zm-61.455 25.453c-1.0225 0-2.0466.39035-2.8301 1.1738l-11.312 11.314c-1.5669 1.5669-1.5669 4.0893 0 5.6563 1.5669 1.5669 4.0893 1.5669 5.6562 0l11.314-11.313c1.5669-1.5669 1.5669-4.0913 0-5.6582-.78347-.78347-1.8056-1.1738-2.8281-1.1738zm50.91 0c-1.0225 0-2.0447.39035-2.8281 1.1738-1.5669 1.5669-1.5669 4.0913 0 5.6582l11.314 11.313c1.5669 1.5669 4.0893 1.5669 5.6563 0 1.5669-1.567 1.5669-4.0893 0-5.6563l-11.313-11.314c-.78347-.78347-1.8076-1.1738-2.8301-1.1738zm-25.455 10.547c-2.216 0-4 1.784-4 4v16c0 2.216 1.784 4 4 4s4-1.784 4-4v-16c0-2.216-1.784-4-4-4z" fill="#ffffff"/></svg>
diff --git a/editor/icons/GizmoLight.svg b/editor/icons/GizmoLight.svg
index ab828c800e..0f74ebbd3d 100644
--- a/editor/icons/GizmoLight.svg
+++ b/editor/icons/GizmoLight.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m64 2a44 44 0 0 0 -44 44 44 44 0 0 0 24 39.189v5.8105 5 3c0 5.0515 3.3756 9.2769 8 10.578v16.422h24v-16.422c4.6244-1.3012 8-5.5266 8-10.578v-3-5-5.8574a44 44 0 0 0 24-39.143 44 44 0 0 0 -44-44zm0 20a24 24 0 0 1 24 24 24 24 0 0 1 -24 24 24 24 0 0 1 -24-24 24 24 0 0 1 24-24z" fill-opacity=".29412" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".98824" stroke-width="2.2"/><path d="m64 6a40 40 0 0 0 -40 40 40 40 0 0 0 24 36.607v15.393a8 8 0 0 0 8 8h16a8 8 0 0 0 8-8v-15.363a40 40 0 0 0 24-36.637 40 40 0 0 0 -40-40zm0 12a28 28 0 0 1 28 28 28 28 0 0 1 -28 28 28 28 0 0 1 -28-28 28 28 0 0 1 28-28zm-8 96v8h16v-8z" fill="#fefefe"/></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m64 2a44 44 0 0 0 -44 44 44 44 0 0 0 24 39.189v5.8105 5 3c0 5.0515 3.3756 9.2769 8 10.578v16.422h24v-16.422c4.6244-1.3012 8-5.5266 8-10.578v-3-5-5.8574a44 44 0 0 0 24-39.143 44 44 0 0 0 -44-44zm0 20a24 24 0 0 1 24 24 24 24 0 0 1 -24 24 24 24 0 0 1 -24-24 24 24 0 0 1 24-24z" fill-opacity=".29412" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".98824" stroke-width="2.2"/><path d="m64 6a40 40 0 0 0 -40 40 40 40 0 0 0 24 36.607v15.393a8 8 0 0 0 8 8h16a8 8 0 0 0 8-8v-15.363a40 40 0 0 0 24-36.637 40 40 0 0 0 -40-40zm0 12a28 28 0 0 1 28 28 28 28 0 0 1 -28 28 28 28 0 0 1 -28-28 28 28 0 0 1 28-28zm-8 96v8h16v-8z" fill="#ffffff"/></svg>
diff --git a/editor/icons/GizmoSpotLight.svg b/editor/icons/GizmoSpotLight.svg
index 18696c2cdc..d1a576ce22 100644
--- a/editor/icons/GizmoSpotLight.svg
+++ b/editor/icons/GizmoSpotLight.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m52 4c-6.5788 0-12 5.4212-12 12v26.625c-12.263 7.2822-19.978 19.75-20 33.369l-.005859 4.0059h28.578c1.7994 6.8632 8.0265 12 15.428 12s13.628-5.1368 15.428-12h28.576l-.00391-4.0039c-.01526-13.625-7.7323-26.099-20-33.385v-26.611c0-6.5788-5.4212-12-12-12zm-11.689 78.016c-1.536-.10738-3.1419.23676-4.5586 1.0547l-10.393 6c-3.7786 2.1816-5.1117 7.1503-2.9297 10.93 2.1816 3.7786 7.1503 5.1117 10.93 2.9297l10.393-6c3.7796-2.1822 5.1087-7.1521 2.9277-10.93-1.3629-2.3605-3.8057-3.8052-6.3691-3.9844zm47.379 0c-2.5634.1792-5.0063 1.6238-6.3691 3.9844-2.181 3.7776-.85187 8.7475 2.9277 10.93l10.393 6c3.7794 2.182 8.7481.8489 10.93-2.9297 2.182-3.7794.84891-8.7481-2.9297-10.93l-10.393-6c-1.4167-.81792-3.0225-1.1621-4.5586-1.0547zm-23.689 13.984c-4.3628 0-8 3.6372-8 8v12c0 4.3628 3.6372 8 8 8s8-3.6372 8-8v-12c0-4.3628-3.6372-8-8-8z" fill-opacity=".29412"/><path d="m52 8c-4.432 0-8 3.568-8 8v12 16.875a40 36 0 0 0 -20 31.125h28a12 12 0 0 0 12 12 12 12 0 0 0 12-12h28a40 36 0 0 0 -20-31.141v-20.859-8c0-4.432-3.568-8-8-8zm-11.969 78.006c-.76793-.053681-1.5596.1138-2.2793.5293l-10.393 6c-1.9191 1.108-2.5728 3.5457-1.4648 5.4648s3.5457 2.5728 5.4648 1.4648l10.393-6c1.9191-1.108 2.5709-3.5457 1.4629-5.4648-.6925-1.1994-1.9037-1.9047-3.1836-1.9941zm47.938 0c-1.2799.08947-2.4911.7947-3.1836 1.9941-1.108 1.9191-.45622 4.3568 1.4629 5.4648l10.393 6c1.9191 1.108 4.3568.45427 5.4648-1.4648s.45427-4.3568-1.4648-5.4648l-10.393-6c-.71967-.4155-1.5114-.58298-2.2793-.5293zm-23.969 13.994c-2.216 0-4 1.784-4 4v12c0 2.216 1.784 4 4 4s4-1.784 4-4v-12c0-2.216-1.784-4-4-4z" fill="#fefefe" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.1082"/></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m52 4c-6.5788 0-12 5.4212-12 12v26.625c-12.263 7.2822-19.978 19.75-20 33.369l-.005859 4.0059h28.578c1.7994 6.8632 8.0265 12 15.428 12s13.628-5.1368 15.428-12h28.576l-.00391-4.0039c-.01526-13.625-7.7323-26.099-20-33.385v-26.611c0-6.5788-5.4212-12-12-12zm-11.689 78.016c-1.536-.10738-3.1419.23676-4.5586 1.0547l-10.393 6c-3.7786 2.1816-5.1117 7.1503-2.9297 10.93 2.1816 3.7786 7.1503 5.1117 10.93 2.9297l10.393-6c3.7796-2.1822 5.1087-7.1521 2.9277-10.93-1.3629-2.3605-3.8057-3.8052-6.3691-3.9844zm47.379 0c-2.5634.1792-5.0063 1.6238-6.3691 3.9844-2.181 3.7776-.85187 8.7475 2.9277 10.93l10.393 6c3.7794 2.182 8.7481.8489 10.93-2.9297 2.182-3.7794.84891-8.7481-2.9297-10.93l-10.393-6c-1.4167-.81792-3.0225-1.1621-4.5586-1.0547zm-23.689 13.984c-4.3628 0-8 3.6372-8 8v12c0 4.3628 3.6372 8 8 8s8-3.6372 8-8v-12c0-4.3628-3.6372-8-8-8z" fill-opacity=".29412"/><path d="m52 8c-4.432 0-8 3.568-8 8v12 16.875a40 36 0 0 0 -20 31.125h28a12 12 0 0 0 12 12 12 12 0 0 0 12-12h28a40 36 0 0 0 -20-31.141v-20.859-8c0-4.432-3.568-8-8-8zm-11.969 78.006c-.76793-.053681-1.5596.1138-2.2793.5293l-10.393 6c-1.9191 1.108-2.5728 3.5457-1.4648 5.4648s3.5457 2.5728 5.4648 1.4648l10.393-6c1.9191-1.108 2.5709-3.5457 1.4629-5.4648-.6925-1.1994-1.9037-1.9047-3.1836-1.9941zm47.938 0c-1.2799.08947-2.4911.7947-3.1836 1.9941-1.108 1.9191-.45622 4.3568 1.4629 5.4648l10.393 6c1.9191 1.108 4.3568.45427 5.4648-1.4648s.45427-4.3568-1.4648-5.4648l-10.393-6c-.71967-.4155-1.5114-.58298-2.2793-.5293zm-23.969 13.994c-2.216 0-4 1.784-4 4v12c0 2.216 1.784 4 4 4s4-1.784 4-4v-12c0-2.216-1.784-4-4-4z" fill="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.1082"/></svg>
diff --git a/editor/icons/GizmoGIProbe.svg b/editor/icons/GizmoVoxelGI.svg
index ff3cafa1f5..ff3cafa1f5 100644
--- a/editor/icons/GizmoGIProbe.svg
+++ b/editor/icons/GizmoVoxelGI.svg
diff --git a/editor/icons/GraphEdit.svg b/editor/icons/GraphEdit.svg
index fbe7422f98..071c191532 100644
--- a/editor/icons/GraphEdit.svg
+++ b/editor/icons/GraphEdit.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m3 1a2 2 0 0 0 -2 2 2 2 0 0 0 1 1.7305v6.541a2 2 0 0 0 -1 1.7285 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -1-1.7305v-5.8555l4.793 4.793 1.4141-1.4141-4.793-4.793h5.8574a2 2 0 0 0 1.7285 1 2 2 0 0 0 2-2 2 2 0 0 0 -2-2 2 2 0 0 0 -1.7305 1h-6.541a2 2 0 0 0 -1.7285-1zm10.656 6.9297-.70703.70703 1.4141 1.4141.70703-.70703zm-1.4141 1.4141-3.8887 3.8887-.35352 1.7676 1.7676-.35352 3.8887-3.8887-1.4141-1.4141z" fill="#8eef97" transform="translate(0 1036.4)"/><ellipse cx="3" cy="1039.4" fill="#6e6e6e"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m3 1a2 2 0 0 0 -2 2 2 2 0 0 0 1 1.7305v6.541a2 2 0 0 0 -1 1.7285 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -1-1.7305v-5.8555l4.793 4.793 1.4141-1.4141-4.793-4.793h5.8574a2 2 0 0 0 1.7285 1 2 2 0 0 0 2-2 2 2 0 0 0 -2-2 2 2 0 0 0 -1.7305 1h-6.541a2 2 0 0 0 -1.7285-1zm10.656 6.9297-.70703.70703 1.4141 1.4141.70703-.70703zm-1.4141 1.4141-3.8887 3.8887-.35352 1.7676 1.7676-.35352 3.8887-3.8887-1.4141-1.4141z" fill="#8eef97" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/GraphNode.svg b/editor/icons/GraphNode.svg
index 061a81d951..b56fe6b609 100644
--- a/editor/icons/GraphNode.svg
+++ b/editor/icons/GraphNode.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m3 1a2 2 0 0 0 -2 2 2 2 0 0 0 1 1.7305v6.541a2 2 0 0 0 -1 1.7285 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -1-1.7305v-5.8555l4.0859 4.0859 1.4141-1.4141-4.0859-4.0859h5.8574a2 2 0 0 0 1.7285 1 2 2 0 0 0 2-2 2 2 0 0 0 -2-2 2 2 0 0 0 -1.7305 1h-6.541a2 2 0 0 0 -1.7285-1zm9.5 9a2.5 2.5 0 0 0 -2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.5-2.5 2.5 2.5 0 0 0 -2.5-2.5z" fill="#8eef97" transform="translate(0 1036.4)"/><ellipse cx="3" cy="1039.4" fill="#6e6e6e"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m3 1a2 2 0 0 0 -2 2 2 2 0 0 0 1 1.7305v6.541a2 2 0 0 0 -1 1.7285 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -1-1.7305v-5.8555l4.0859 4.0859 1.4141-1.4141-4.0859-4.0859h5.8574a2 2 0 0 0 1.7285 1 2 2 0 0 0 2-2 2 2 0 0 0 -2-2 2 2 0 0 0 -1.7305 1h-6.541a2 2 0 0 0 -1.7285-1zm9.5 9a2.5 2.5 0 0 0 -2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.5-2.5 2.5 2.5 0 0 0 -2.5-2.5z" fill="#8eef97" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/GuiDropdown.svg b/editor/icons/GuiDropdown.svg
index ef37cda6ff..9b9abe5b49 100644
--- a/editor/icons/GuiDropdown.svg
+++ b/editor/icons/GuiDropdown.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m4 1045.4 3 3 3-3" style="fill:none;stroke:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.58824;stroke-width:2" transform="translate(0 -1038.4)"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="m4 7 3 3 3-3" fill="none" stroke="#fff" stroke-opacity=".59" stroke-width="2"/></svg>
diff --git a/editor/icons/GuiScrollGrabber.svg b/editor/icons/GuiScrollGrabber.svg
index 935f9361dd..a8a0cf08c2 100644
--- a/editor/icons/GuiScrollGrabber.svg
+++ b/editor/icons/GuiScrollGrabber.svg
@@ -1 +1 @@
-<svg height="12" viewBox="0 0 12 11.999999" width="12" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" fill="#fff" fill-opacity=".294118" r="3"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" fill="#fff" fill-opacity=".29" r="3"/></svg>
diff --git a/editor/icons/GuiScrollGrabberHl.svg b/editor/icons/GuiScrollGrabberHl.svg
index cec53330f0..0388063d25 100644
--- a/editor/icons/GuiScrollGrabberHl.svg
+++ b/editor/icons/GuiScrollGrabberHl.svg
@@ -1 +1 @@
-<svg height="12" viewBox="0 0 12 11.999999" width="12" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" fill="#f9f9f9" fill-opacity=".73" r="3"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" fill="#f9f9f9" fill-opacity=".73" r="3"/></svg>
diff --git a/editor/icons/GuiScrollGrabberPressed.svg b/editor/icons/GuiScrollGrabberPressed.svg
index 13f8427d35..202b9c9412 100644
--- a/editor/icons/GuiScrollGrabberPressed.svg
+++ b/editor/icons/GuiScrollGrabberPressed.svg
@@ -1 +1 @@
-<svg height="12" viewBox="0 0 12 11.999999" width="12" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" fill="#afafaf" fill-opacity=".72941" r="3"/></svg>
+<svg height="12" viewBox="0 0 12 11.999999" width="12" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" fill="#b3b3b3" fill-opacity=".72941" r="3"/></svg>
diff --git a/editor/icons/GuiSliderGrabber.svg b/editor/icons/GuiSliderGrabber.svg
index ddd1b1d9b8..47342747bf 100644
--- a/editor/icons/GuiSliderGrabber.svg
+++ b/editor/icons/GuiSliderGrabber.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0 -7-7zm0 2a5 5 0 0 1 .5.025391 5 5 0 0 1 .49414.074219 5 5 0 0 1 .48438.12305 5 5 0 0 1 .46875.17188 5 5 0 0 1 .44922.2168 5 5 0 0 1 .42578.26172 5 5 0 0 1 .39844.30273 5 5 0 0 1 .36524.33984 5 5 0 0 1 .33008.37695 5 5 0 0 1 .29102.40625 5 5 0 0 1 .24805.43359 5 5 0 0 1 .20508.45508 5 5 0 0 1 .1582.47461 5 5 0 0 1 .10938.48828 5 5 0 0 1 .060547.49609 5 5 0 0 1 .011719.35352 5 5 0 0 1 -.025391.5 5 5 0 0 1 -.074218.49414 5 5 0 0 1 -.12305.48438 5 5 0 0 1 -.17188.46875 5 5 0 0 1 -.2168.44922 5 5 0 0 1 -.26172.42578 5 5 0 0 1 -.30273.39844 5 5 0 0 1 -.33984.36524 5 5 0 0 1 -.37695.33008 5 5 0 0 1 -.40625.29102 5 5 0 0 1 -.43359.24805 5 5 0 0 1 -.45508.20508 5 5 0 0 1 -.47461.1582 5 5 0 0 1 -.48828.10938 5 5 0 0 1 -.49609.060547 5 5 0 0 1 -.35352.011719 5 5 0 0 1 -.5-.025391 5 5 0 0 1 -.49414-.074218 5 5 0 0 1 -.48438-.12305 5 5 0 0 1 -.46875-.17188 5 5 0 0 1 -.44922-.2168 5 5 0 0 1 -.42578-.26172 5 5 0 0 1 -.39844-.30273 5 5 0 0 1 -.36523-.33984 5 5 0 0 1 -.33008-.37695 5 5 0 0 1 -.29102-.40625 5 5 0 0 1 -.24805-.43359 5 5 0 0 1 -.20508-.45508 5 5 0 0 1 -.1582-.47461 5 5 0 0 1 -.10938-.48828 5 5 0 0 1 -.060547-.49609 5 5 0 0 1 -.011719-.35352 5 5 0 0 1 .025391-.5 5 5 0 0 1 .074219-.49414 5 5 0 0 1 .12305-.48438 5 5 0 0 1 .17188-.46875 5 5 0 0 1 .2168-.44922 5 5 0 0 1 .26172-.42578 5 5 0 0 1 .30273-.39844 5 5 0 0 1 .33984-.36523 5 5 0 0 1 .37695-.33008 5 5 0 0 1 .40625-.29102 5 5 0 0 1 .43359-.24805 5 5 0 0 1 .45508-.20508 5 5 0 0 1 .47461-.1582 5 5 0 0 1 .48828-.10938 5 5 0 0 1 .49609-.060547 5 5 0 0 1 .35352-.011719z" fill="#e0e0e0" fill-opacity=".289256" transform="translate(0 1036.4)"/><circle cx="8" cy="1044.4" r="3" style="fill:#fff;fill-opacity:.58824;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.32549;stroke-width:3"/></g><circle cx="7.932204" cy="8" fill="#fff" fill-opacity=".78431" r="3" stroke-linejoin="round" stroke-opacity=".39216" stroke-width="3"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3.84 0-7 3.16-7 7s3.16 7 7 7 7-3.16 7-7-3.16-7-7-7zm0 2c.167 0 .334.009.5.025.166.017.331.042.494.075.164.033.325.074.485.123.159.049.315.106.468.172.154.064.303.137.449.216.147.08.289.168.426.262.138.094.271.195.399.303.127.107.249.22.365.34s.226.245.33.377c.104.13.201.266.291.406s.173.285.248.433c.076.149.144.3.205.455.061.156.114.314.158.475.045.161.081.324.11.488.028.165.048.33.06.496.008.118.012.236.012.354 0 .167-.009.334-.025.5-.017.166-.042.331-.075.494-.032.164-.074.325-.123.485-.049.159-.106.315-.172.468-.064.154-.137.303-.216.449-.08.147-.168.289-.262.426-.094.138-.195.271-.303.399-.107.127-.22.249-.34.365s-.245.226-.377.33c-.13.104-.266.201-.406.291s-.285.173-.433.248c-.149.076-.3.144-.455.205-.156.061-.314.114-.475.158-.161.045-.324.081-.488.11-.165.028-.33.048-.496.06-.118.008-.236.012-.354.012-.167 0-.334-.009-.5-.025-.166-.017-.331-.042-.494-.075-.164-.032-.325-.074-.484-.123-.16-.049-.316-.106-.469-.172-.153-.064-.303-.137-.449-.216-.147-.08-.289-.168-.426-.262-.138-.094-.271-.195-.399-.303-.127-.107-.249-.22-.365-.34s-.226-.245-.33-.377c-.104-.13-.201-.266-.291-.406s-.173-.285-.248-.433c-.076-.149-.144-.3-.205-.455-.061-.156-.114-.314-.158-.475-.045-.161-.081-.324-.11-.488-.028-.165-.048-.33-.06-.496-.008-.118-.012-.236-.012-.354 0-.167.009-.334.025-.5.017-.166.042-.331.075-.494.033-.164.074-.325.123-.484.049-.16.106-.316.172-.469.064-.153.137-.303.216-.449.08-.147.168-.289.262-.426.094-.138.195-.271.303-.399.107-.127.22-.249.34-.365s.246-.226.377-.33c.13-.104.266-.201.406-.291s.285-.173.433-.248c.149-.076.3-.144.456-.205.155-.061.313-.114.474-.158.161-.045.324-.081.488-.11.165-.028.33-.048.496-.06.118-.008.236-.012.354-.012z" fill="#e0e0e0" fill-opacity=".29" fill-rule="nonzero"/><g fill="#fff"><circle cx="8" cy="8" fill-opacity=".59" r="3"/><circle cx="7.932" cy="8" fill-opacity=".78" r="3"/></g></svg>
diff --git a/editor/icons/GuiSliderGrabberHl.svg b/editor/icons/GuiSliderGrabberHl.svg
index 3af977ae4a..b7be27af0f 100644
--- a/editor/icons/GuiSliderGrabberHl.svg
+++ b/editor/icons/GuiSliderGrabberHl.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0 -7-7zm0 2a5 5 0 0 1 .5.025391 5 5 0 0 1 .49414.074219 5 5 0 0 1 .48438.12305 5 5 0 0 1 .46875.17188 5 5 0 0 1 .44922.2168 5 5 0 0 1 .42578.26172 5 5 0 0 1 .39844.30273 5 5 0 0 1 .36524.33984 5 5 0 0 1 .33008.37695 5 5 0 0 1 .29102.40625 5 5 0 0 1 .24805.43359 5 5 0 0 1 .20508.45508 5 5 0 0 1 .1582.47461 5 5 0 0 1 .10938.48828 5 5 0 0 1 .060547.49609 5 5 0 0 1 .011719.35352 5 5 0 0 1 -.025391.5 5 5 0 0 1 -.074218.49414 5 5 0 0 1 -.12305.48438 5 5 0 0 1 -.17188.46875 5 5 0 0 1 -.2168.44922 5 5 0 0 1 -.26172.42578 5 5 0 0 1 -.30273.39844 5 5 0 0 1 -.33984.36524 5 5 0 0 1 -.37695.33008 5 5 0 0 1 -.40625.29102 5 5 0 0 1 -.43359.24805 5 5 0 0 1 -.45508.20508 5 5 0 0 1 -.47461.1582 5 5 0 0 1 -.48828.10938 5 5 0 0 1 -.49609.060547 5 5 0 0 1 -.35352.011719 5 5 0 0 1 -.5-.025391 5 5 0 0 1 -.49414-.074218 5 5 0 0 1 -.48438-.12305 5 5 0 0 1 -.46875-.17188 5 5 0 0 1 -.44922-.2168 5 5 0 0 1 -.42578-.26172 5 5 0 0 1 -.39844-.30273 5 5 0 0 1 -.36523-.33984 5 5 0 0 1 -.33008-.37695 5 5 0 0 1 -.29102-.40625 5 5 0 0 1 -.24805-.43359 5 5 0 0 1 -.20508-.45508 5 5 0 0 1 -.1582-.47461 5 5 0 0 1 -.10938-.48828 5 5 0 0 1 -.060547-.49609 5 5 0 0 1 -.011719-.35352 5 5 0 0 1 .025391-.5 5 5 0 0 1 .074219-.49414 5 5 0 0 1 .12305-.48438 5 5 0 0 1 .17188-.46875 5 5 0 0 1 .2168-.44922 5 5 0 0 1 .26172-.42578 5 5 0 0 1 .30273-.39844 5 5 0 0 1 .33984-.36523 5 5 0 0 1 .37695-.33008 5 5 0 0 1 .40625-.29102 5 5 0 0 1 .43359-.24805 5 5 0 0 1 .45508-.20508 5 5 0 0 1 .47461-.1582 5 5 0 0 1 .48828-.10938 5 5 0 0 1 .49609-.060547 5 5 0 0 1 .35352-.011719z" fill="#e0e0e0" transform="translate(0 1036.4)"/><circle cx="8" cy="1044.4" r="3" style="fill:#fff;fill-opacity:.58824;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.32549;stroke-width:3"/></g><circle cx="7.932204" cy="8" fill="#fff" fill-opacity=".78431" r="3" stroke-linejoin="round" stroke-opacity=".39216" stroke-width="3"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3.84 0-7 3.16-7 7s3.16 7 7 7 7-3.16 7-7-3.16-7-7-7zm0 2c.167 0 .334.009.5.025.166.017.331.042.494.075.164.033.325.074.485.123.159.049.315.106.468.172.154.064.303.137.449.216.147.08.289.168.426.262.138.094.271.195.399.303.127.107.249.22.365.34s.226.245.33.377c.104.13.201.266.291.406s.173.285.248.433c.076.149.144.3.205.455.061.156.114.314.158.475.045.161.081.324.11.488.028.165.048.33.06.496.008.118.012.236.012.354 0 .167-.009.334-.025.5-.017.166-.042.331-.075.494-.032.164-.074.325-.123.485-.049.159-.106.315-.172.468-.064.154-.137.303-.216.449-.08.147-.168.289-.262.426-.094.138-.195.271-.303.399-.107.127-.22.249-.34.365s-.245.226-.377.33c-.13.104-.266.201-.406.291s-.285.173-.433.248c-.149.076-.3.144-.455.205-.156.061-.314.114-.475.158-.161.045-.324.081-.488.11-.165.028-.33.048-.496.06-.118.008-.236.012-.354.012-.167 0-.334-.009-.5-.025-.166-.017-.331-.042-.494-.075-.164-.032-.325-.074-.484-.123-.16-.049-.316-.106-.469-.172-.153-.064-.303-.137-.449-.216-.147-.08-.289-.168-.426-.262-.138-.094-.271-.195-.399-.303-.127-.107-.249-.22-.365-.34s-.226-.245-.33-.377c-.104-.13-.201-.266-.291-.406s-.173-.285-.248-.433c-.076-.149-.144-.3-.205-.455-.061-.156-.114-.314-.158-.475-.045-.161-.081-.324-.11-.488-.028-.165-.048-.33-.06-.496-.008-.118-.012-.236-.012-.354 0-.167.009-.334.025-.5.017-.166.042-.331.075-.494.033-.164.074-.325.123-.484.049-.16.106-.316.172-.469.064-.153.137-.303.216-.449.08-.147.168-.289.262-.426.094-.138.195-.271.303-.399.107-.127.22-.249.34-.365s.246-.226.377-.33c.13-.104.266-.201.406-.291s.285-.173.433-.248c.149-.076.3-.144.456-.205.155-.061.313-.114.474-.158.161-.045.324-.081.488-.11.165-.028.33-.048.496-.06.118-.008.236-.012.354-.012z" fill="#e0e0e0" fill-rule="nonzero"/><g fill="#fff"><circle cx="8" cy="8" fill-opacity=".59" r="3"/><circle cx="7.932" cy="8" fill-opacity=".78" r="3"/></g></svg>
diff --git a/editor/icons/GuiToggleOn.svg b/editor/icons/GuiToggleOn.svg
index 37b47e8de4..0e20128730 100644
--- a/editor/icons/GuiToggleOn.svg
+++ b/editor/icons/GuiToggleOn.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 38 15.999999" width="38" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-4 0-7 3.0000002-7 7.0000002 0 3.9999998 3 6.9999998 7 6.9999998h22c4 0 7-3 7-6.9999998 0-4-3-7.0000002-7-7.0000002-7.333334 0-14.55609 0-22 0z" fill="#699ce8"/><circle cx="30" cy="8" fill="#fefefe" r="5"/></svg>
+<svg height="16" viewBox="0 0 38 15.999999" width="38" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-4 0-7 3.0000002-7 7.0000002 0 3.9999998 3 6.9999998 7 6.9999998h22c4 0 7-3 7-6.9999998 0-4-3-7.0000002-7-7.0000002-7.333334 0-14.55609 0-22 0z" fill="#699ce8"/><circle cx="30" cy="8" fill="#ffffff" r="5"/></svg>
diff --git a/editor/icons/GuiToggleOnMirrored.svg b/editor/icons/GuiToggleOnMirrored.svg
index fa7f602ee7..affbb5c7a8 100644
--- a/editor/icons/GuiToggleOnMirrored.svg
+++ b/editor/icons/GuiToggleOnMirrored.svg
@@ -1 +1 @@
-<svg height="26" width="42" xmlns="http://www.w3.org/2000/svg"><path d="m31 5c4.986 0 9 3.568 9 8s-4.014 8-9 8h-20c-4.986 0-9-3.568-9-8s4.014-8 9-8z" fill="#699ce8"/><circle cx="10" cy="13" fill="#fefefe" r="5"/></svg>
+<svg height="26" width="42" xmlns="http://www.w3.org/2000/svg"><path d="m31 5c4.986 0 9 3.568 9 8s-4.014 8-9 8h-20c-4.986 0-9-3.568-9-8s4.014-8 9-8z" fill="#699ce8"/><circle cx="10" cy="13" fill="#ffffff" r="5"/></svg>
diff --git a/editor/icons/GuiTreeArrowDown.svg b/editor/icons/GuiTreeArrowDown.svg
index 7b320152ff..ad8a625f18 100644
--- a/editor/icons/GuiTreeArrowDown.svg
+++ b/editor/icons/GuiTreeArrowDown.svg
@@ -1 +1 @@
-<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m3 1045.4 3 3 3-3" style="fill:none;stroke:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.39216;stroke-width:2" transform="translate(0 -1040.4)"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="m3 5 3 3 3-3" fill="none" stroke="#fff" stroke-opacity=".39" stroke-width="2"/></svg>
diff --git a/editor/icons/GuiTreeArrowLeft.svg b/editor/icons/GuiTreeArrowLeft.svg
index d0f7b36fab..2cc20c8459 100644
--- a/editor/icons/GuiTreeArrowLeft.svg
+++ b/editor/icons/GuiTreeArrowLeft.svg
@@ -1 +1 @@
-<svg height="12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m7 9-3-3 3-3" style="fill:none;stroke:#fff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.392"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="m7 9-3-3 3-3" fill="none" stroke="#fff" stroke-opacity=".39" stroke-width="2"/></svg>
diff --git a/editor/icons/GuiTreeArrowRight.svg b/editor/icons/GuiTreeArrowRight.svg
index cf1b5dac7c..14314bb365 100644
--- a/editor/icons/GuiTreeArrowRight.svg
+++ b/editor/icons/GuiTreeArrowRight.svg
@@ -1 +1 @@
-<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m4 1049.4 3-3-3-3" style="fill:none;stroke:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.39216;stroke-width:2" transform="translate(0 -1040.4)"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="m4 9 3-3-3-3" fill="none" stroke="#fff" stroke-opacity=".39" stroke-width="2"/></svg>
diff --git a/editor/icons/GuiTreeUpdown.svg b/editor/icons/GuiTreeUpdown.svg
index c6b9014e82..feff99f618 100644
--- a/editor/icons/GuiTreeUpdown.svg
+++ b/editor/icons/GuiTreeUpdown.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m6.9844 1.002a1.0001 1.0001 0 0 0 -.69141.29102l-3 3a1 1 0 0 0 0 1.4141 1 1 0 0 0 1.4141 0l2.293-2.293 2.293 2.293a1 1 0 0 0 1.4141 0 1 1 0 0 0 0-1.4141l-3-3a1.0001 1.0001 0 0 0 -.72266-.29102zm3 6.998a1 1 0 0 0 -.69141.29297l-2.293 2.293-2.293-2.293a1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l3 3a1.0001 1.0001 0 0 0 1.4141 0l3-3a1 1 0 0 0 0-1.4141 1 1 0 0 0 -.72266-.29297z" fill="#fff" fill-opacity=".58824"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="m6.984 1.002c-.259.004-.507.108-.691.291l-3 3c-.388.388-.388 1.026 0 1.414s1.026.388 1.414 0l2.293-2.293 2.293 2.293c.388.388 1.026.388 1.414 0s.388-1.026 0-1.414l-3-3c-.191-.191-.452-.296-.722-.291zm3 6.998c-.259.004-.507.109-.691.293l-2.293 2.293-2.293-2.293c-.191-.19-.451-.295-.721-.291-.26.003-.509.108-.693.291-.388.388-.388 1.026 0 1.414l3 3c.388.388 1.026.388 1.414 0l3-3c.388-.388.388-1.026 0-1.414-.191-.191-.452-.297-.723-.293z" fill="#fff" fill-opacity=".59" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/HFlowContainer.svg b/editor/icons/HFlowContainer.svg
new file mode 100644
index 0000000000..dedf6e54f2
--- /dev/null
+++ b/editor/icons/HFlowContainer.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.097 0-2 .903-2 2v10c0 1.097.903 2 2 2h10c1.097 0 2-.903 2-2v-10c0-1.097-.903-2-2-2zm0 2h10v10h-10zm2 1c-.554 0-1 .446-1 1s.446 1 1 1h2c.554 0 1-.446 1-1s-.446-1-1-1zm4.996 0c-.554 0-1 .446-1 1s.446 1 1 1h1.004c.554 0 1-.446 1-1s-.446-1-1-1zm-4.996 3c-.554 0-1 .446-1 1s.446 1 1 1 1-.446 1-1-.446-1-1-1zm3 0c-.554 0-1 .446-1 1s.446 1 1 1h3c.554 0 1-.446 1-1s-.446-1-1-1zm-3.004 3c-.554 0-1 .446-1 1s.446 1 1 1h4.004c.554 0 1-.446 1-1s-.446-1-1-1z" fill="#8eef97" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/Heart.svg b/editor/icons/Heart.svg
index 4f46908760..00edf90729 100644
--- a/editor/icons/Heart.svg
+++ b/editor/icons/Heart.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 14c-.1249999 0-.25-.046875-.34375-.140625l-4.875-4.703125c-.0625-.054687-1.7812496-1.6249999-1.7812496-3.5 0-2.2890626 1.3984372-3.6562499 3.7343748-3.6562499 1.3671873 0 2.6484373 1.390625 3.2656248 1.9999999.6171875-.6093749 1.8984375-1.9999999 3.265625-1.9999999 2.335938 0 3.734375 1.3671873 3.734375 3.6562499 0 1.8750001-1.71875 3.4453125-1.789062 3.5156251l-4.867188 4.6874999c-.09375.09375-.2187499.140625-.34375.140625z" fill="#ff7070"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 14c-.1249999 0-.25-.046875-.34375-.140625l-4.875-4.703125c-.0625-.054687-1.7812496-1.6249999-1.7812496-3.5 0-2.2890626 1.3984372-3.6562499 3.7343748-3.6562499 1.3671873 0 2.6484373 1.390625 3.2656248 1.9999999.6171875-.6093749 1.8984375-1.9999999 3.265625-1.9999999 2.335938 0 3.734375 1.3671873 3.734375 3.6562499 0 1.8750001-1.71875 3.4453125-1.789062 3.5156251l-4.867188 4.6874999c-.09375.09375-.2187499.140625-.34375.140625z" fill="#fc7f7f"/></svg>
diff --git a/editor/icons/HeightMapShape3D.svg b/editor/icons/HeightMapShape3D.svg
index 0ffff96850..e1b3af88e5 100644
--- a/editor/icons/HeightMapShape3D.svg
+++ b/editor/icons/HeightMapShape3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="8" y2="11"><stop offset="0" stop-color="#68b6ff"/><stop offset="1" stop-color="#a2d2ff"/></linearGradient><g transform="translate(0 -1)"><path d="m1 1044.4 7 3 7-3-7-3z" fill="#a2d2ff" fill-rule="evenodd" transform="translate(0 -1033.4)"/><path d="m3 11c1-1 2-2 2-4s1-3 3-3 3 1 3 3 1 3 2 4z" fill="url(#a)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="8" y2="11"><stop offset="0" stop-color="#5fb2ff"/><stop offset="1" stop-color="#a2d2ff"/></linearGradient><g transform="translate(0 -1)"><path d="m1 1044.4 7 3 7-3-7-3z" fill="#a2d2ff" fill-rule="evenodd" transform="translate(0 -1033.4)"/><path d="m3 11c1-1 2-2 2-4s1-3 3-3 3 1 3 3 1 3 2 4z" fill="url(#a)"/></g></svg>
diff --git a/editor/icons/Help.svg b/editor/icons/Help.svg
index 65f3100164..ace4e79bf3 100644
--- a/editor/icons/Help.svg
+++ b/editor/icons/Help.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5.0293 1c-.99969-.010925-2.0096.31165-3.0293 1v7c2.0172-1.3529 4.0167-1.3136 6 0 1.9833-1.3136 3.9828-1.3529 6 0v-7c-1.0197-.68835-2.0296-1.0109-3.0293-1-.6613.007227-1.3175.1735-1.9707.46289v4.5371h-1v-4c-.98156-.64465-1.971-.98908-2.9707-1zm-5.0293 9v6h2a3 3 0 0 0 3-3 3 3 0 0 0 -3-3zm5 3a3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0 -3-3 3 3 0 0 0 -3 3zm6 0a3 3 0 0 0 3 3h1v-2h-1a1 1 0 0 1 -1-1 1 1 0 0 1 1-1h1v-2h-1a3 3 0 0 0 -3 3zm-9-1a1 1 0 0 1 1 1 1 1 0 0 1 -1 1zm6 0a1 1 0 0 1 1 1 1 1 0 0 1 -1 1 1 1 0 0 1 -1-1 1 1 0 0 1 1-1z" style="fill:#e0e0e0;fill-opacity:.58824;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:.32549;stroke-width:2"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m5.029 1c-.999-.011-2.009.312-3.029 1v7c2.017-1.353 4.017-1.314 6 0 1.983-1.314 3.983-1.353 6 0v-7c-1.02-.688-2.03-1.011-3.029-1-.662.007-1.318.173-1.971.463v4.537h-1v-4c-.982-.645-1.971-.989-2.971-1zm-5.029 9v6h2c1.646 0 3-1.354 3-3s-1.354-3-3-3zm5 3c0 1.646 1.354 3 3 3s3-1.354 3-3-1.354-3-3-3-3 1.354-3 3zm6 0c0 1.646 1.354 3 3 3h1v-2h-1c-.549 0-1-.451-1-1s.451-1 1-1h1v-2h-1c-1.646 0-3 1.354-3 3zm-9-1c.549 0 1 .451 1 1s-.451 1-1 1zm6 0c.549 0 1 .451 1 1s-.451 1-1 1-1-.451-1-1 .451-1 1-1z" fill="#e0e0e0" fill-opacity=".59" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/ImmediateMesh.svg b/editor/icons/ImmediateMesh.svg
index 9521530876..f94ef8c323 100644
--- a/editor/icons/ImmediateMesh.svg
+++ b/editor/icons/ImmediateMesh.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 14.999999 14.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2v2h5v-2zm5 2v3h2v-3zm2-2v2h5v-2zm-2 7v3h2v-3zm-5 3v2h5v-2zm7 0v2h5v-2z" fill="#ffca5f" transform="scale(.93749994)"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2v2h5v-2zm5 2v3h2v-3zm2-2v2h5v-2zm-2 7v3h2v-3zm-5 3v2h5v-2zm7 0v2h5v-2z" fill="#ffca5f" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/ImportCheck.svg b/editor/icons/ImportCheck.svg
index 0e6b0a7105..d17506afea 100644
--- a/editor/icons/ImportCheck.svg
+++ b/editor/icons/ImportCheck.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1044.4 4 4 8-8" fill="none" stroke="#45ff8b" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1036.4)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1044.4 4 4 8-8" fill="none" stroke="#5fff97" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1036.4)"/></svg>
diff --git a/editor/icons/ImportFail.svg b/editor/icons/ImportFail.svg
index 6e34dfc405..582b244614 100644
--- a/editor/icons/ImportFail.svg
+++ b/editor/icons/ImportFail.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2.9902 1.9902a1.0001 1.0001 0 0 0 -.69727 1.7168l4.293 4.293-4.293 4.293a1.0001 1.0001 0 1 0 1.4141 1.4141l4.293-4.293 4.293 4.293a1.0001 1.0001 0 1 0 1.4141-1.4141l-4.293-4.293 4.293-4.293a1.0001 1.0001 0 0 0 -.72656-1.7148 1.0001 1.0001 0 0 0 -.6875.30078l-4.293 4.293-4.293-4.293a1.0001 1.0001 0 0 0 -.7168-.30273z" fill="#ff5d5d" fill-rule="evenodd"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2.9902 1.9902a1.0001 1.0001 0 0 0 -.69727 1.7168l4.293 4.293-4.293 4.293a1.0001 1.0001 0 1 0 1.4141 1.4141l4.293-4.293 4.293 4.293a1.0001 1.0001 0 1 0 1.4141-1.4141l-4.293-4.293 4.293-4.293a1.0001 1.0001 0 0 0 -.72656-1.7148 1.0001 1.0001 0 0 0 -.6875.30078l-4.293 4.293-4.293-4.293a1.0001 1.0001 0 0 0 -.7168-.30273z" fill="#ff5f5f" fill-rule="evenodd"/></svg>
diff --git a/editor/icons/KeyBlendShape.svg b/editor/icons/KeyBlendShape.svg
index 42f7341942..11932bd847 100644
--- a/editor/icons/KeyBlendShape.svg
+++ b/editor/icons/KeyBlendShape.svg
@@ -1,44 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="10"
- viewBox="0 0 10 10"
- width="10"
- version="1.1"
- id="svg80"
- sodipodi:docname="KeyBlendShape.svg"
- inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs84" />
- <sodipodi:namedview
- id="namedview82"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="false"
- inkscape:zoom="84.4"
- inkscape:cx="2.6599526"
- inkscape:cy="5.0059242"
- inkscape:window-width="1848"
- inkscape:window-height="1016"
- inkscape:window-x="72"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg80" />
- <rect
- fill="#3cf34e"
- height="6.1027"
- ry=".76286"
- transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 -1042.4)"
- width="6.1027"
- x="-740.13947"
- y="741.10779"
- id="rect78"
- style="fill:#5ad5c4;fill-opacity:1" />
-</svg>
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><rect fill="#5ad5c4" height="6.1027" ry=".76286" transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 -1042.4)" width="6.1027" x="-740.13947" y="741.10779"/></svg>
diff --git a/editor/icons/KeyInvalid.svg b/editor/icons/KeyInvalid.svg
index 4a04c1ee65..4cfc72dc3f 100644
--- a/editor/icons/KeyInvalid.svg
+++ b/editor/icons/KeyInvalid.svg
@@ -1 +1 @@
-<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m.46447 1046.2 2.1213 2.1213-2.1213 2.1213 1.4142 1.4142 2.1213-2.1213 2.1213 2.1213 1.4142-1.4142-2.1213-2.1213 2.1213-2.1213-1.4142-1.4142-2.1213 2.1213-2.1213-2.1213-1.4142 1.4142z" fill="#ff5d5d" transform="translate(0 -1044.4)"/></svg>
+<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m.46447 1046.2 2.1213 2.1213-2.1213 2.1213 1.4142 1.4142 2.1213-2.1213 2.1213 2.1213 1.4142-1.4142-2.1213-2.1213 2.1213-2.1213-1.4142-1.4142-2.1213 2.1213-2.1213-2.1213-1.4142 1.4142z" fill="#ff5f5f" transform="translate(0 -1044.4)"/></svg>
diff --git a/editor/icons/KeyTrackBlendShape.svg b/editor/icons/KeyTrackBlendShape.svg
index e82f0d6a6f..2594fad3d3 100644
--- a/editor/icons/KeyTrackBlendShape.svg
+++ b/editor/icons/KeyTrackBlendShape.svg
@@ -1,45 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="10"
- viewBox="0 0 10 10"
- width="10"
- version="1.1"
- id="svg12"
- sodipodi:docname="KeyTrackBlendShape.svg"
- inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs16" />
- <sodipodi:namedview
- id="namedview14"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="false"
- inkscape:zoom="29.839906"
- inkscape:cx="-3.5690461"
- inkscape:cy="9.0985541"
- inkscape:window-width="1848"
- inkscape:window-height="1016"
- inkscape:window-x="72"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg12" />
- <ellipse
- style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
- id="path921"
- cx="-0.88986981"
- cy="6.0959954"
- rx="1.2495773"
- ry="1.0867468" />
- <path
- id="path1910"
- style="color:#000000;fill:#5ad5c4;stroke-linejoin:round;-inkscape-stroke:none"
- d="m 4.5230825,1.1341567 c -2.1310744,0.017055 -3.86718737,1.7635044 -3.86718737,3.8984375 0,1.8778511 1.34348597,3.4523891 3.11718737,3.8164061 L 3.95277,7.5794693 C 2.7929991,7.3095662 1.9351921,6.2780435 1.9351921,5.0325942 c 0,-1.4262775 1.123493,-2.5732858 2.5390622,-2.6152344 v 0.017578 h 0.2011719 l 0.1796875,-1.28125 H 4.5230825 v -0.011719 c 0,-0.00263 -2.64e-5,-0.00518 0,-0.00781 z m 1.6816406,0.019531 -0.1796875,1.28125 h 1.3085937 c 0.078866,0 0.1230469,0.044181 0.1230469,0.1230469 v 4.9882815 c 0,0.07887 -0.044181,0.121093 -0.1230469,0.121094 H 5.2887075 L 5.10902,8.9486103 h 2.2246093 c 0.7663818,0 1.4042969,-0.635962 1.4042969,-1.402344 V 2.5579848 c 0,-0.7663818 -0.637915,-1.4042969 -1.4042969,-1.4042969 z" />
-</svg>
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><ellipse cx="-.88987" cy="6.095995" fill="none" rx="1.249577" ry="1.086747"/><path d="m4.5230825 1.1341567c-2.1310744.017055-3.86718737 1.7635044-3.86718737 3.8984375 0 1.8778511 1.34348597 3.4523891 3.11718737 3.8164061l.1796875-1.269531c-1.1597709-.2699031-2.0175779-1.3014258-2.0175779-2.5468751 0-1.4262775 1.123493-2.5732858 2.5390622-2.6152344v.017578h.2011719l.1796875-1.28125h-.3320312v-.011719c0-.00263-.0000264-.00518 0-.00781zm1.6816406.019531-.1796875 1.28125h1.3085937c.078866 0 .1230469.044181.1230469.1230469v4.9882815c0 .07887-.044181.121093-.1230469.121094h-2.0449218l-.1796875 1.2812502h2.2246093c.7663818 0 1.4042969-.635962 1.4042969-1.402344v-4.9882815c0-.7663818-.637915-1.4042969-1.4042969-1.4042969z" fill="#5ad5c4" stroke-linejoin="round"/></svg>
diff --git a/editor/icons/KeyTrackPosition.svg b/editor/icons/KeyTrackPosition.svg
index 05c6e88841..300b0439e5 100644
--- a/editor/icons/KeyTrackPosition.svg
+++ b/editor/icons/KeyTrackPosition.svg
@@ -1,47 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="10"
- viewBox="0 0 10 10"
- width="10"
- version="1.1"
- id="svg12"
- sodipodi:docname="KeyTrackPosition.svg"
- inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs16" />
- <sodipodi:namedview
- id="namedview14"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="false"
- inkscape:zoom="42.2"
- inkscape:cx="12.78436"
- inkscape:cy="6.1729858"
- inkscape:window-width="1848"
- inkscape:window-height="1016"
- inkscape:window-x="72"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg12" />
- <ellipse
- style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
- id="path921"
- cx="-0.88986981"
- cy="6.0959954"
- rx="1.2495773"
- ry="1.0867468" />
- <path
- d="M 4.949661,0.60426977 A 0.6444116,0.6444116 0 0 0 4.504153,0.79178767 L 3.215459,2.0804819 4.12663,2.9916532 4.95977,2.1585124 5.792911,2.9916532 6.704083,2.0804819 5.415388,0.79178767 A 0.6444116,0.6444116 0 0 0 4.949744,0.60426977 Z M 1.926771,3.3691634 0.638077,4.6578577 a 0.6444116,0.6444116 0 0 0 0,0.9111713 L 1.926771,6.8577233 2.837942,5.946552 2.004801,5.1134111 2.837942,4.2802702 1.926771,3.3690989 Z m 6.065948,0 -0.911171,0.9111713 0.83314,0.8331409 -0.83314,0.8331408 0.911171,0.9111714 1.288694,-1.2886944 a 0.6444116,0.6444116 0 0 0 0,-0.9111713 L 7.992719,3.3692278 Z M 4.959777,3.8247361 A 1.2886943,1.2886943 0 0 0 3.671083,5.1134305 1.2886943,1.2886943 0 0 0 4.959777,6.4021248 1.2886943,1.2886943 0 0 0 6.248471,5.1134305 1.2886943,1.2886943 0 0 0 4.959777,3.8247361 Z m -0.83314,3.4105296 -0.911172,0.9111715 1.288694,1.288694 a 0.6444116,0.6444116 0 0 0 0.911171,0 L 6.704025,8.1464372 5.792853,7.2352657 4.959712,8.0684062 4.126572,7.2352657 Z"
- fill="#e0e0e0"
- fill-opacity="0.99608"
- id="path1400"
- style="fill:#ea7940;fill-opacity:1;stroke-width:0.644347" />
-</svg>
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><ellipse cx="-.88987" cy="6.095995" fill="none" rx="1.249577" ry="1.086747"/><path d="m4.949661.60426977a.6444116.6444116 0 0 0 -.445508.1875179l-1.288694 1.28869423.911171.9111713.83314-.8331408.833141.8331408.911172-.9111713-1.288695-1.28869423a.6444116.6444116 0 0 0 -.465644-.1875179zm-3.02289 2.76489363-1.288694 1.2886943a.6444116.6444116 0 0 0 0 .9111713l1.288694 1.2886943.911171-.9111713-.833141-.8331409.833141-.8331409-.911171-.9111713zm6.065948 0-.911171.9111713.83314.8331409-.83314.8331408.911171.9111714 1.288694-1.2886944a.6444116.6444116 0 0 0 0-.9111713l-1.288694-1.2886943zm-3.032942.4555727a1.2886943 1.2886943 0 0 0 -1.288694 1.2886944 1.2886943 1.2886943 0 0 0 1.288694 1.2886943 1.2886943 1.2886943 0 0 0 1.288694-1.2886943 1.2886943 1.2886943 0 0 0 -1.288694-1.2886944zm-.83314 3.4105296-.911172.9111715 1.288694 1.288694a.6444116.6444116 0 0 0 .911171 0l1.288695-1.288694-.911172-.9111715-.833141.8331405-.83314-.8331405z" fill="#ea7940" stroke-width=".644347"/></svg>
diff --git a/editor/icons/KeyTrackRotation.svg b/editor/icons/KeyTrackRotation.svg
index d05e381eb2..cfaa9ef80b 100644
--- a/editor/icons/KeyTrackRotation.svg
+++ b/editor/icons/KeyTrackRotation.svg
@@ -1,47 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="10"
- viewBox="0 0 10 10"
- width="10"
- version="1.1"
- id="svg12"
- sodipodi:docname="KeyTrackRotation.svg"
- inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs16" />
- <sodipodi:namedview
- id="namedview14"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="false"
- inkscape:zoom="42.2"
- inkscape:cx="1.9075829"
- inkscape:cy="5.8175355"
- inkscape:window-width="1848"
- inkscape:window-height="1016"
- inkscape:window-x="72"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg12" />
- <ellipse
- style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
- id="path921"
- cx="-0.88986981"
- cy="6.0959954"
- rx="1.2495773"
- ry="1.0867468" />
- <path
- d="m 5.0711986,0.88214062 a 4.1086779,4.1086779 0 0 0 -0.178839,0.00115 4.1086779,4.1086779 0 0 0 -0.409265,0.033245 A 4.1086779,4.1086779 0 0 0 0.99001362,4.1883346 4.1086779,4.1086779 0 0 0 2.1467236,7.9244144 h -0.648877 v 1.1739077 h 2.347816 a 0.58701268,0.58701268 0 0 0 0.569756,-0.729119 l -0.586953,-2.3478164 -1.139514,0.2854534 0.165082,0.6580347 a 2.9347699,2.9347699 0 0 1 -0.769204,-1.9752178 2.9347699,2.9347699 0 0 1 2.93477,-2.93477 2.9347699,2.9347699 0 0 1 2.93477,2.93477 2.9347699,2.9347699 0 0 1 -0.860944,2.0738257 l 0.831127,0.8311266 A 4.1086779,4.1086779 0 0 0 8.7040866,3.1726236 4.1086779,4.1086779 0 0 0 5.0711336,0.88215292 Z m -0.05159,2.93359608 a 1.173908,1.173908 0 0 0 -1.173907,1.173908 1.173908,1.173908 0 0 0 1.173907,1.173908 1.173908,1.173908 0 0 0 1.173909,-1.173908 1.173908,1.173908 0 0 0 -1.173909,-1.173908 z"
- fill="#e0e0e0"
- fill-opacity="0.99608"
- id="path1165"
- style="fill:#ff2b88;fill-opacity:1;stroke-width:0.586954" />
-</svg>
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><ellipse cx="-.88987" cy="6.095995" fill="none" rx="1.249577" ry="1.086747"/><path d="m5.0711986.88214062a4.1086779 4.1086779 0 0 0 -.178839.00115 4.1086779 4.1086779 0 0 0 -.409265.033245 4.1086779 4.1086779 0 0 0 -3.49308098 3.27179898 4.1086779 4.1086779 0 0 0 1.15670998 3.7360798h-.648877v1.1739077h2.347816a.58701268.58701268 0 0 0 .569756-.729119l-.586953-2.3478164-1.139514.2854534.165082.6580347a2.9347699 2.9347699 0 0 1 -.769204-1.9752178 2.9347699 2.9347699 0 0 1 2.93477-2.93477 2.9347699 2.9347699 0 0 1 2.93477 2.93477 2.9347699 2.9347699 0 0 1 -.860944 2.0738257l.831127.8311266a4.1086779 4.1086779 0 0 0 .779534-4.7219857 4.1086779 4.1086779 0 0 0 -3.632953-2.29047068zm-.05159 2.93359608a1.173908 1.173908 0 0 0 -1.173907 1.173908 1.173908 1.173908 0 0 0 1.173907 1.173908 1.173908 1.173908 0 0 0 1.173909-1.173908 1.173908 1.173908 0 0 0 -1.173909-1.173908z" fill="#ff2b88" stroke-width=".586954"/></svg>
diff --git a/editor/icons/KeyTrackScale.svg b/editor/icons/KeyTrackScale.svg
index 9269ccbca2..73fe1cb5f0 100644
--- a/editor/icons/KeyTrackScale.svg
+++ b/editor/icons/KeyTrackScale.svg
@@ -1,47 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="10"
- viewBox="0 0 10 10"
- width="10"
- version="1.1"
- id="svg12"
- sodipodi:docname="KeyTrackScale.svg"
- inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs16" />
- <sodipodi:namedview
- id="namedview14"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="false"
- inkscape:zoom="42.2"
- inkscape:cx="1.9075829"
- inkscape:cy="5.8175355"
- inkscape:window-width="1848"
- inkscape:window-height="1016"
- inkscape:window-x="72"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg12" />
- <ellipse
- style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
- id="path921"
- cx="-0.88986981"
- cy="6.0959954"
- rx="1.2495773"
- ry="1.0867468" />
- <path
- d="M 5.5742494,0.94786723 A 0.58774585,0.58774585 0 0 0 4.9865036,1.535613 0.58774585,0.58774585 0 0 0 5.5742494,2.1233589 h 1.519852 L 6.334146,2.8833142 7.1652774,3.7144456 7.9252328,2.9544903 V 4.4743422 A 0.58774585,0.58774585 0 0 0 8.5129787,5.0620881 0.58774585,0.58774585 0 0 0 9.1007245,4.4743422 V 1.535613 A 0.58780462,0.58780462 0 0 0 8.5129787,0.94786723 Z M 4.9865036,3.8865964 A 1.1754917,1.1754917 0 0 0 3.8110119,5.0620881 1.1754917,1.1754917 0 0 0 4.9865036,6.2375798 1.1754917,1.1754917 0 0 0 6.1619953,5.0620881 1.1754917,1.1754917 0 0 0 4.9865036,3.8865964 Z M 1.4600285,5.0620881 A 0.58774585,0.58774585 0 0 0 0.8722826,5.6498339 V 8.5885638 A 0.58780462,0.58780462 0 0 0 1.4600285,9.1763092 H 4.3987577 A 0.58774585,0.58774585 0 0 0 4.9865036,8.5885638 0.58774585,0.58774585 0 0 0 4.3987577,8.0008173 H 2.8789057 L 3.6388611,7.2408619 2.8077297,6.4097305 2.0477743,7.1696859 V 5.6498339 A 0.58774585,0.58774585 0 0 0 1.4600285,5.0620881 Z"
- fill="#e0e0e0"
- fill-opacity="0.99608"
- id="path7"
- style="fill:#eac840;fill-opacity:1;stroke-width:0.587745" />
-</svg>
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><ellipse cx="-.88987" cy="6.095995" fill="none" rx="1.249577" ry="1.086747"/><path d="m5.5742494.94786723a.58774585.58774585 0 0 0 -.5877458.58774577.58774585.58774585 0 0 0 .5877458.5877459h1.519852l-.7599554.7599553.8311314.8311314.7599554-.7599553v1.5198519a.58774585.58774585 0 0 0 .5877459.5877459.58774585.58774585 0 0 0 .5877458-.5877459v-2.9387292a.58780462.58780462 0 0 0 -.5877458-.58774577zm-.5877458 2.93872917a1.1754917 1.1754917 0 0 0 -1.1754917 1.1754917 1.1754917 1.1754917 0 0 0 1.1754917 1.1754917 1.1754917 1.1754917 0 0 0 1.1754917-1.1754917 1.1754917 1.1754917 0 0 0 -1.1754917-1.1754917zm-3.5264751 1.1754917a.58774585.58774585 0 0 0 -.5877459.5877458v2.9387299a.58780462.58780462 0 0 0 .5877459.5877454h2.9387292a.58774585.58774585 0 0 0 .5877459-.5877454.58774585.58774585 0 0 0 -.5877459-.5877465h-1.519852l.7599554-.7599554-.8311314-.8311314-.7599554.7599554v-1.519852a.58774585.58774585 0 0 0 -.5877458-.5877458z" fill="#eac840" stroke-width=".587745"/></svg>
diff --git a/editor/icons/KeyXPosition.svg b/editor/icons/KeyXPosition.svg
index 5816a241c9..59c14cd820 100644
--- a/editor/icons/KeyXPosition.svg
+++ b/editor/icons/KeyXPosition.svg
@@ -1,43 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="10"
- viewBox="0 0 10 10"
- width="10"
- version="1.1"
- id="svg1678"
- sodipodi:docname="KeyXPosition.svg"
- inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs1682" />
- <sodipodi:namedview
- id="namedview1680"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="false"
- inkscape:zoom="84.4"
- inkscape:cx="4.9940758"
- inkscape:cy="5.0059242"
- inkscape:window-width="1848"
- inkscape:window-height="1016"
- inkscape:window-x="72"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg1678" />
- <rect
- fill="#ea7940"
- height="6.1027"
- ry=".76286"
- transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 -1042.4)"
- width="6.1027"
- x="-740.13947"
- y="741.10779"
- id="rect1676" />
-</svg>
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><rect fill="#ea7940" height="6.1027" ry=".76286" transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 -1042.4)" width="6.1027" x="-740.13947" y="741.10779"/></svg>
diff --git a/editor/icons/KeyXRotation.svg b/editor/icons/KeyXRotation.svg
index 6cd5a67ac0..4494c301bb 100644
--- a/editor/icons/KeyXRotation.svg
+++ b/editor/icons/KeyXRotation.svg
@@ -1,44 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="10"
- viewBox="0 0 10 10"
- width="10"
- version="1.1"
- id="svg1678"
- sodipodi:docname="KeyXRotation.svg"
- inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs1682" />
- <sodipodi:namedview
- id="namedview1680"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="false"
- inkscape:zoom="84.4"
- inkscape:cx="0.32582938"
- inkscape:cy="5.0059242"
- inkscape:window-width="1848"
- inkscape:window-height="1016"
- inkscape:window-x="72"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg1678" />
- <rect
- fill="#ea7940"
- height="6.1027002"
- ry="0.76286"
- transform="rotate(-45,-1258.2881,-521.2)"
- width="6.1030002"
- x="-740.13947"
- y="741.10779"
- id="rect1676"
- style="fill:#ee3c94;fill-opacity:1" />
-</svg>
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><rect fill="#ff2b88" height="6.1027" ry=".76286" transform="matrix(.70710678 -.70710678 .70710678 .70710678 .000003 -1042.399994)" width="6.103" x="-740.13947" y="741.10779"/></svg>
diff --git a/editor/icons/KeyXScale.svg b/editor/icons/KeyXScale.svg
index 588fa5d2f3..cb6011cd83 100644
--- a/editor/icons/KeyXScale.svg
+++ b/editor/icons/KeyXScale.svg
@@ -1,44 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="10"
- viewBox="0 0 10 10"
- width="10"
- version="1.1"
- id="svg1678"
- sodipodi:docname="KeyXScale.svg"
- inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs1682" />
- <sodipodi:namedview
- id="namedview1680"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="false"
- inkscape:zoom="84.4"
- inkscape:cx="2.6658768"
- inkscape:cy="5.0059242"
- inkscape:window-width="1473"
- inkscape:window-height="752"
- inkscape:window-x="122"
- inkscape:window-y="114"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg1678" />
- <rect
- fill="#ea7940"
- height="6.1027002"
- ry="0.76286"
- transform="rotate(-45,-1258.2881,-521.2)"
- width="6.1030002"
- x="-740.13947"
- y="741.10779"
- id="rect1676"
- style="fill:#dbbf4f;fill-opacity:1" />
-</svg>
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><rect fill="#eac840" height="6.1027" ry=".76286" transform="matrix(.70710678 -.70710678 .70710678 .70710678 .000003 -1042.399994)" width="6.103" x="-740.13947" y="741.10779"/></svg>
diff --git a/editor/icons/MaterialPreviewCube.svg b/editor/icons/MaterialPreviewCube.svg
index 29baa9f030..c4af05ffb0 100644
--- a/editor/icons/MaterialPreviewCube.svg
+++ b/editor/icons/MaterialPreviewCube.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><path d="m8 1-7 3v8l7 3 7-3v-8z" fill="#d5d5d5" transform="translate(0 1036.4)"/><path d="m1 1040.4 7 3 7-3-7-3z" fill="#fff"/><path d="m8 1051.4-7-3v-8l7 3z" fill="#e0e0e0"/><path d="m8 1051.4 7-3v-8l-7 3z" fill="#d5d5d5"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><path d="m8 1-7 3v8l7 3 7-3v-8z" fill="#d6d6d6" transform="translate(0 1036.4)"/><path d="m1 1040.4 7 3 7-3-7-3z" fill="#fff"/><path d="m8 1051.4-7-3v-8l7 3z" fill="#e0e0e0"/><path d="m8 1051.4 7-3v-8l-7 3z" fill="#d6d6d6"/></g></svg>
diff --git a/editor/icons/MaterialPreviewCubeOff.svg b/editor/icons/MaterialPreviewCubeOff.svg
index 14564c558e..e573e170ad 100644
--- a/editor/icons/MaterialPreviewCubeOff.svg
+++ b/editor/icons/MaterialPreviewCubeOff.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><path d="m8 1-7 3v8l7 3 7-3v-8z" fill="#d5d5d5" transform="translate(0 1036.4)"/><path d="m1 1040.4 7 3 7-3-7-3z" fill="#fff"/><path d="m8 1051.4-7-3v-8l7 3z" fill="#e0e0e0"/><path d="m8 1051.4 7-3v-8l-7 3z" fill="#d5d5d5"/><path d="m8 1037.4-7 3v8l7 3 7-3v-8z" fill-opacity=".23529"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><path d="m8 1-7 3v8l7 3 7-3v-8z" fill="#d6d6d6" transform="translate(0 1036.4)"/><path d="m1 1040.4 7 3 7-3-7-3z" fill="#fff"/><path d="m8 1051.4-7-3v-8l7 3z" fill="#e0e0e0"/><path d="m8 1051.4 7-3v-8l-7 3z" fill="#d6d6d6"/><path d="m8 1037.4-7 3v8l7 3 7-3v-8z" fill-opacity=".23529"/></g></svg>
diff --git a/editor/icons/MaterialPreviewLight1Off.svg b/editor/icons/MaterialPreviewLight1Off.svg
index 2f84612e82..f51b0bbcb2 100644
--- a/editor/icons/MaterialPreviewLight1Off.svg
+++ b/editor/icons/MaterialPreviewLight1Off.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v2h2v-2zm-3.2422 1.3438-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm8.4844 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-4.2422 1.6562a4 4 0 0 0 -4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0 -4-4zm-1 1h2v1 5h-1v-5h-1zm-6 2v2h2v-2zm12 0v2h2v-2zm-9.2422 3.8281-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm8.4844 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-5.2422 2.1719v2h2v-2z" fill-opacity=".23529"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v2h2v-2zm-3.2422 1.3438-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm8.4844 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-4.2422 1.6562a4 4 0 0 0 -4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0 -4-4zm-1 1h2v1 5h-1v-5h-1zm-6 2v2h2v-2zm12 0v2h2v-2zm-9.2422 3.8281-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm8.4844 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-5.2422 2.1719v2h2v-2z" fill="#e0e0e0"/><path d="m7 1v2h2v-2zm-3.2422 1.3438-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm8.4844 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-4.2422 1.6562a4 4 0 0 0 -4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0 -4-4zm-1 1h2v1 5h-1v-5h-1zm-6 2v2h2v-2zm12 0v2h2v-2zm-9.2422 3.8281-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm8.4844 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-5.2422 2.1719v2h2v-2z" fill-opacity=".23529"/></svg>
diff --git a/editor/icons/MaterialPreviewSphereOff.svg b/editor/icons/MaterialPreviewSphereOff.svg
index 52bb095210..e958e3f0a8 100644
--- a/editor/icons/MaterialPreviewSphereOff.svg
+++ b/editor/icons/MaterialPreviewSphereOff.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0 -7-7zm-2 2a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2z" fill-opacity=".23529"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0 -7-7zm-2 2a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2z" fill="#e0e0e0"/><path d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0 -7-7zm-2 2a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2z" fill-opacity=".23529"/></svg>
diff --git a/editor/icons/NavigationAgent2D.svg b/editor/icons/NavigationAgent2D.svg
index 28760be4a1..3f1d571a7e 100644
--- a/editor/icons/NavigationAgent2D.svg
+++ b/editor/icons/NavigationAgent2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 4.2333332 4.2333335" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-2.9999997.0000126-5 2-5 5s3.0000003 6 5 9c2-3 5.007143-6.0296693 5-9 0-3-2-4.9999874-5-5zm0 2.5a2.4999999 2.4999999 0 0 1 2.5 2.5 2.4999999 2.4999999 0 0 1 -2.5 2.5 2.4999999 2.4999999 0 0 1 -2.5-2.5 2.4999999 2.4999999 0 0 1 2.5-2.5z" fill="#e0e0e0" transform="scale(.26458333)"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3 0-5 2-5 5s3 6 5 9c2-3 5.007-6.03 5-9 0-3-2-5-5-5zm0 2.5c1.371 0 2.5 1.129 2.5 2.5s-1.129 2.5-2.5 2.5-2.5-1.129-2.5-2.5 1.129-2.5 2.5-2.5z" fill="#e0e0e0" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/NavigationAgent3D.svg b/editor/icons/NavigationAgent3D.svg
index da76adaa99..947b2129c3 100644
--- a/editor/icons/NavigationAgent3D.svg
+++ b/editor/icons/NavigationAgent3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 4.2333332 4.2333335" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="scale(.26458333)"><path d="m9 1c-1.3712923 0-2.308408.4294811-2.9394531 1.0742188-.6678822.6627728-1.3395938 1.3233299-2.0097657 1.984375-.0455468 1.7412784.7567781 4.3277129 2.3652344 4.84375.1781835.3171398.3844475.6487461.5839844.9765624v5.1210938l2-2c2-3 4-5.9999874 4-8s-1-4-4-4z" fill="#fff" fill-opacity=".392157"/><path d="m7 3c-3 0-4 1.9999874-4 4s2.0000003 5 4 8c2.0000001-3 4-5.9999874 4-8s-1-4-4-4zm0 2a1.9999999 1.9999999 0 0 1 2 2 1.9999999 1.9999999 0 0 1 -2 2 1.9999999 1.9999999 0 0 1 -2-2 1.9999999 1.9999999 0 0 1 2-2z" fill="#e0e0e0"/></g></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m9 1c-1.371 0-2.308.429-2.939 1.074-.668.663-1.34 1.324-2.01 1.985-.046 1.741.757 4.327 2.365 4.843.178.317.384.649.584.977v5.121l2-2c2-3 4-6 4-8s-1-4-4-4z" fill="#fff" fill-opacity=".39"/><path d="m7 3c-3 0-4 2-4 4s2 5 4 8c2-3 4-6 4-8s-1-4-4-4zm0 2c1.097 0 2 .903 2 2s-.903 2-2 2-2-.903-2-2 .903-2 2-2z" fill="#e0e0e0"/></g></svg>
diff --git a/editor/icons/NavigationObstacle2D.svg b/editor/icons/NavigationObstacle2D.svg
index fab41e2f43..8fcb5617dd 100644
--- a/editor/icons/NavigationObstacle2D.svg
+++ b/editor/icons/NavigationObstacle2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 4.2333332 4.2333335" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 .875c-.625 0-1.2499999.3749906-1.5 1.125l-2.9999999 10h8.9999999l-3-10c-.2499999-.7500094-.875-1.125-1.5-1.125zm-1.5 4.125h3l1 4h-5zm-4.5 8c-1 0-1 2 0 2h12c1 0 1-2 0-2z" fill="#e0e0e0" transform="scale(.26458333)"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 .875c-.625 0-1.25.375-1.5 1.125l-3 10h9l-3-10c-.25-.75-.875-1.125-1.5-1.125zm-1.5 4.125h3l1 4h-5zm-4.5 8c-1 0-1 2 0 2h12c1 0 1-2 0-2z" fill="#e0e0e0" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/NavigationObstacle3D.svg b/editor/icons/NavigationObstacle3D.svg
index 10b09107cd..c5e58eebf7 100644
--- a/editor/icons/NavigationObstacle3D.svg
+++ b/editor/icons/NavigationObstacle3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 4.2333332 4.2333335" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="scale(.26458333)"><path d="m4.6074219 8.3789062c-1.7979243.927604-3.60742192 2.0716858-3.6074219 2.6210938 0 .999987 6.0000005 4 7 4 1.0000006 0 7-3.000013 7-4 0-.549408-1.809498-1.6934898-3.607422-2.6210938l.607422 1.6210938c2 4.000025-9.9999999 4.000025-8 0z" fill="#fff" fill-opacity=".392157"/><path d="m8 .875c-.375 0-.7499997.3749906-1 1.125l-3 8c-1.9999998 4.000025 10 4.000025 8 0l-3-8c-.2499997-.7500094-.625-1.125-1-1.125zm-1.5 4.125c.9999999.4999937 2.0000001.4999937 3 0l1 3.5c-1.4999996.9999874-3.4999996.9999874-5 0z" fill="#e0e0e0"/></g></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m4.607 8.379c-1.798.928-3.607 2.072-3.607 2.621 0 1 6 4 7 4s7-3 7-4c0-.549-1.809-1.693-3.607-2.621l.607 1.621c2 4-10 4-8 0z" fill="#fff" fill-opacity=".39"/><path d="m8 .875c-.375 0-.75.375-1 1.125l-3 8c-2 4 10 4 8 0l-3-8c-.25-.75-.625-1.125-1-1.125zm-1.5 4.125c1 .5 2 .5 3 0l1 3.5c-1.5 1-3.5 1-5 0z" fill="#e0e0e0"/></g></svg>
diff --git a/editor/icons/NodeDisabled.svg b/editor/icons/NodeDisabled.svg
index b2d51fc4fb..f0cc586103 100644
--- a/editor/icons/NodeDisabled.svg
+++ b/editor/icons/NodeDisabled.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2a6 6 0 0 0 -6 6 6 6 0 0 0 6 6 6 6 0 0 0 6-6 6 6 0 0 0 -6-6zm0 2a4 4 0 0 1 4 4 4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 4-4z" fill="#999"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2a6 6 0 0 0 -6 6 6 6 0 0 0 6 6 6 6 0 0 0 6-6 6 6 0 0 0 -6-6zm0 2a4 4 0 0 1 4 4 4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 4-4z" fill="#919191"/></svg>
diff --git a/editor/icons/Notification.svg b/editor/icons/Notification.svg
index 1f1f9c3e15..15695e22a8 100644
--- a/editor/icons/Notification.svg
+++ b/editor/icons/Notification.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12.089506 1.2353795c-.498141-.2384823-1.095292-.027987-1.333775.4701537-.01372.028981-.02341.059557-.03428.089693-.01467.023016-.02777.046925-.04071.071459-.04899-.00527-.09728-.00862-.146087-.011473-1.4730257-.6255101-3.1777024.0153376-3.8738627 1.4563251l-1.7272425 3.6078572s-.3364181.7034345-.8079671 1.1268133c-.00105.0009371-.00239.00174-.00344.00268-.2721193.1337295-.5707545.185826-.8605632.0470816-.4981411-.2384824-1.0952924-.0279876-1.3337749.4701537-.01605.033526-.029907.066894-.041944.1011828-.018769.030371-.036749.06319-.052515.096122-.2384825.4981412-.027988 1.0952923.4701537 1.3337751l9.0196437 4.318106c.498141.238482 1.095292.02799 1.333775-.470154.01577-.03293.0301-.0675.04191-.1012.0192-.03086.0365-.06257.05255-.0961.238483-.498141.02799-1.095292-.470153-1.333775-.901965-.43181-.03834-2.235739-.03834-2.235739l1.727237-3.6078618c.715447-1.4944233.08396-3.2858776-1.410461-4.0013247.238482-.4981411.02799-1.0952926-.470154-1.333775zm-5.5145786 11.3714015c-.322341.673306-.037829 1.480435.6354753 1.802775.6733031.32234 1.4804334.03783 1.8027749-.635476z" fill="#e6e6e6"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12.089506 1.2353795c-.498141-.2384823-1.095292-.027987-1.333775.4701537-.01372.028981-.02341.059557-.03428.089693-.01467.023016-.02777.046925-.04071.071459-.04899-.00527-.09728-.00862-.146087-.011473-1.4730257-.6255101-3.1777024.0153376-3.8738627 1.4563251l-1.7272425 3.6078572s-.3364181.7034345-.8079671 1.1268133c-.00105.0009371-.00239.00174-.00344.00268-.2721193.1337295-.5707545.185826-.8605632.0470816-.4981411-.2384824-1.0952924-.0279876-1.3337749.4701537-.01605.033526-.029907.066894-.041944.1011828-.018769.030371-.036749.06319-.052515.096122-.2384825.4981412-.027988 1.0952923.4701537 1.3337751l9.0196437 4.318106c.498141.238482 1.095292.02799 1.333775-.470154.01577-.03293.0301-.0675.04191-.1012.0192-.03086.0365-.06257.05255-.0961.238483-.498141.02799-1.095292-.470153-1.333775-.901965-.43181-.03834-2.235739-.03834-2.235739l1.727237-3.6078618c.715447-1.4944233.08396-3.2858776-1.410461-4.0013247.238482-.4981411.02799-1.0952926-.470154-1.333775zm-5.5145786 11.3714015c-.322341.673306-.037829 1.480435.6354753 1.802775.6733031.32234 1.4804334.03783 1.8027749-.635476z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/NotificationDisabled.svg b/editor/icons/NotificationDisabled.svg
index 0e4465bee8..294682a42c 100644
--- a/editor/icons/NotificationDisabled.svg
+++ b/editor/icons/NotificationDisabled.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m11.705078 1.1386719c-.389281-.0180576-.770356.1928007-.949219.5664062-.01372.028981-.024286.0597078-.035156.0898438-.01467.023016-.026122.0477316-.039062.0722656-.04899-.00527-.097678-.0088657-.146485-.0117187-1.4730253-.6255102-3.1788394.0160437-3.8749998 1.4570312l-1.7265624 3.6074219s-.3370448.7035743-.8085938 1.1269531l-.0019531.0019531c-.2721193.1337295-.5715196.1856194-.8613281.046875-.4981413-.2384824-1.0955019-.0274382-1.3339844.4707031-.01605.0335262-.0289787.0672737-.0410156.1015626-.0187691.0303709-.0369684.0627711-.0527344.0957031-.2384825.4981412-.0293917 1.0955019.46875 1.3339841l.3398437.16211 10.8984379-6.9394535c-.263272-.3070418-.592225-.5660832-.980469-.7519531.238482-.4981411.027441-1.0935489-.470703-1.3320313-.124536-.0596206-.255006-.091637-.384766-.0976562zm2.435547 2.8652343a.94188849 1 0 0 0 -.566406.1386719l-12.1171878 7.7148439a.94188849 1 0 0 0 -.3222656 1.373047.94188849 1 0 0 0 1.2910156.341797l12.1171878-7.7148441a.94188849 1 0 0 0 .322265-1.3710938.94188849 1 0 0 0 -.724609-.4824219zm-.509766 3.2753907-7.3867184 4.7050781 5.0781254 2.431641c.498141.238482 1.095501.027442 1.333984-.470704.01577-.03293.031159-.067862.042969-.101562.0192-.03086.036684-.062173.052734-.095703.238483-.498141.02744-1.095501-.470703-1.333985-.901965-.431809-.039063-2.236328-.039062-2.236328zm-7.0566402 5.3281251c-.322341.673306-.0365856 1.480394.6367187 1.802734.6733031.32234 1.4803929.036588 1.8027344-.636718z" fill="#e6e6e6"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m11.705078 1.1386719c-.389281-.0180576-.770356.1928007-.949219.5664062-.01372.028981-.024286.0597078-.035156.0898438-.01467.023016-.026122.0477316-.039062.0722656-.04899-.00527-.097678-.0088657-.146485-.0117187-1.4730253-.6255102-3.1788394.0160437-3.8749998 1.4570312l-1.7265624 3.6074219s-.3370448.7035743-.8085938 1.1269531l-.0019531.0019531c-.2721193.1337295-.5715196.1856194-.8613281.046875-.4981413-.2384824-1.0955019-.0274382-1.3339844.4707031-.01605.0335262-.0289787.0672737-.0410156.1015626-.0187691.0303709-.0369684.0627711-.0527344.0957031-.2384825.4981412-.0293917 1.0955019.46875 1.3339841l.3398437.16211 10.8984379-6.9394535c-.263272-.3070418-.592225-.5660832-.980469-.7519531.238482-.4981411.027441-1.0935489-.470703-1.3320313-.124536-.0596206-.255006-.091637-.384766-.0976562zm2.435547 2.8652343a.94188849 1 0 0 0 -.566406.1386719l-12.1171878 7.7148439a.94188849 1 0 0 0 -.3222656 1.373047.94188849 1 0 0 0 1.2910156.341797l12.1171878-7.7148441a.94188849 1 0 0 0 .322265-1.3710938.94188849 1 0 0 0 -.724609-.4824219zm-.509766 3.2753907-7.3867184 4.7050781 5.0781254 2.431641c.498141.238482 1.095501.027442 1.333984-.470704.01577-.03293.031159-.067862.042969-.101562.0192-.03086.036684-.062173.052734-.095703.238483-.498141.02744-1.095501-.470703-1.333985-.901965-.431809-.039063-2.236328-.039062-2.236328zm-7.0566402 5.3281251c-.322341.673306-.0365856 1.480394.6367187 1.802734.6733031.32234 1.4803929.036588 1.8027344-.636718z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/OccluderPolygon2D.svg b/editor/icons/OccluderPolygon2D.svg
index ae5d2f4a1d..7ab4240d2f 100644
--- a/editor/icons/OccluderPolygon2D.svg
+++ b/editor/icons/OccluderPolygon2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><path d="m1 1045.4 6 6h8v-8l-6-6h-8z" fill="#3552b1"/><path d="m1 1037.4h8l-3 4 3 4h-8z" fill="#8da5f3" fill-opacity=".98824"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="translate(0 -1036.4)"><path d="m1 1045.4 6 6h8v-8l-6-6h-8z" fill="#4b70ea"/><path d="m1 1037.4h8l-3 4 3 4h-8z" fill="#8da5f3" fill-opacity=".98824"/></g></svg>
diff --git a/editor/icons/OverbrightIndicator.svg b/editor/icons/OverbrightIndicator.svg
index f618980d51..35ad59fbfb 100644
--- a/editor/icons/OverbrightIndicator.svg
+++ b/editor/icons/OverbrightIndicator.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m.5.5v10l10-10z" fill="#fff" stroke="#000"/><path d="m0 12 12-12h-1.714286l-10.285714 10.285714z" fill="#000003" stroke-width="2"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m.5.5v10l10-10z" fill="#fff" stroke="#000000"/><path d="m0 12 12-12h-1.714286l-10.285714 10.285714z" fill="#000003" stroke-width="2"/></svg>
diff --git a/editor/icons/PackedByteArray.svg b/editor/icons/PackedByteArray.svg
index 95534e4410..448e25c478 100644
--- a/editor/icons/PackedByteArray.svg
+++ b/editor/icons/PackedByteArray.svg
@@ -1 +1 @@
-<svg height="12" viewBox="0 0 16 12" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m0 0v12h4v-2h-2v-8h2v-2h-2zm12 0v2h2v8h-2v2h4v-12h-2z" fill="#e0e0e0"/><path d="m5 3a3 3 0 0 0 -3 3v3h2v-3a1 1 0 0 1 1-1h1v4h2a3 3 0 0 0 1-.17578v.17578h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4h-2v3a1 1 0 0 1 -1 1v-4h-2z" fill="#69ec9e"/><path d="m6 9v-6h2v4a1 1 0 0 0 1-1v-3h2v4a1 1 0 0 0 1-1v-3h2v3a3 3 0 0 1 -3 3h-2v-.1758a3 3 0 0 1 -1 .1758z" fill="#fff" fill-opacity=".39216"/></svg>
+<svg height="12" viewBox="0 0 16 12" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m0 0v12h4v-2h-2v-8h2v-2h-2zm12 0v2h2v8h-2v2h4v-12h-2z" fill="#e0e0e0"/><path d="m5 3a3 3 0 0 0 -3 3v3h2v-3a1 1 0 0 1 1-1h1v4h2a3 3 0 0 0 1-.17578v.17578h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4h-2v3a1 1 0 0 1 -1 1v-4h-2z" fill="#5fff97"/><path d="m6 9v-6h2v4a1 1 0 0 0 1-1v-3h2v4a1 1 0 0 0 1-1v-3h2v3a3 3 0 0 1 -3 3h-2v-.1758a3 3 0 0 1 -1 .1758z" fill="#fff" fill-opacity=".39216"/></svg>
diff --git a/editor/icons/PackedColorArray.svg b/editor/icons/PackedColorArray.svg
index 588002f188..6911ff1d34 100644
--- a/editor/icons/PackedColorArray.svg
+++ b/editor/icons/PackedColorArray.svg
@@ -1 +1 @@
-<svg height="12" viewBox="0 0 16 12" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m0 0v12h4v-2h-2v-8h2v-2zm12 0v2h2v8h-2v2h4v-12z" fill="#e0e0e0"/><path d="m6 3.5a3 3 0 0 0 -3 3 3 3 0 0 0 3 3h1v-2h-1a1 1 0 0 1 -1-1 1 1 0 0 1 1-1h1v-2z" fill="#ff4545"/><path d="m13 3.5a3 3 0 0 0 -3 3v3h2v-3a1 1 0 0 1 1-1z" fill="#70bfff"/><path d="m7 1.5v5a3 3 0 0 0 3 3v-2a1 1 0 0 1 -1-1v-5z" fill="#7aff70"/></svg>
+<svg height="12" viewBox="0 0 16 12" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m0 0v12h4v-2h-2v-8h2v-2zm12 0v2h2v8h-2v2h4v-12z" fill="#e0e0e0"/><path d="m6 3.5a3 3 0 0 0 -3 3 3 3 0 0 0 3 3h1v-2h-1a1 1 0 0 1 -1-1 1 1 0 0 1 1-1h1v-2z" fill="#ff4545"/><path d="m13 3.5a3 3 0 0 0 -3 3v3h2v-3a1 1 0 0 1 1-1z" fill="#45d7ff"/><path d="m7 1.5v5a3 3 0 0 0 3 3v-2a1 1 0 0 1 -1-1v-5z" fill="#80ff45"/></svg>
diff --git a/editor/icons/PageFirst.svg b/editor/icons/PageFirst.svg
index 76078691ef..ab5cd2c789 100644
--- a/editor/icons/PageFirst.svg
+++ b/editor/icons/PageFirst.svg
@@ -1,47 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="12"
- viewBox="0 0 12 12"
- width="12"
- version="1.1"
- id="svg4"
- sodipodi:docname="PageFirst.svg"
- inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs8" />
- <sodipodi:namedview
- id="namedview6"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="true"
- inkscape:zoom="74.25"
- inkscape:cx="18.053872"
- inkscape:cy="6.5252525"
- inkscape:window-width="3838"
- inkscape:window-height="1582"
- inkscape:window-x="0"
- inkscape:window-y="16"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg4">
- <inkscape:grid
- type="xygrid"
- id="grid989" />
- </sodipodi:namedview>
- <path
- d="M 6,9 3,6 6,3"
- style="fill:none;stroke:#e0e0e0;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
- id="path2" />
- <path
- d="M 9,9 V 3"
- style="fill:none;stroke:#e0e0e0;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
- id="path2211"
- sodipodi:nodetypes="cc" />
-</svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#e0e0e0" stroke-width="2"><path d="m6 9-3-3 3-3"/><path d="m9 9v-6"/></g></svg>
diff --git a/editor/icons/PageLast.svg b/editor/icons/PageLast.svg
index 17c874e8c9..0bc8504e11 100644
--- a/editor/icons/PageLast.svg
+++ b/editor/icons/PageLast.svg
@@ -1,47 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="12"
- viewBox="0 0 12 12"
- width="12"
- version="1.1"
- id="svg4"
- sodipodi:docname="PageLast.svg"
- inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs8" />
- <sodipodi:namedview
- id="namedview6"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="true"
- inkscape:zoom="74.25"
- inkscape:cx="18.053872"
- inkscape:cy="6.5252525"
- inkscape:window-width="3838"
- inkscape:window-height="1582"
- inkscape:window-x="0"
- inkscape:window-y="16"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg4">
- <inkscape:grid
- type="xygrid"
- id="grid989" />
- </sodipodi:namedview>
- <path
- d="m 6.0000414,9 3,-3 -3,-3"
- style="fill:none;stroke:#e0e0e0;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
- id="path2" />
- <path
- d="M 3.0000414,9 V 3"
- style="fill:none;stroke:#e0e0e0;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
- id="path2211"
- sodipodi:nodetypes="cc" />
-</svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#e0e0e0" stroke-width="2"><path d="m6 9 3-3-3-3"/><path d="m3 9v-6"/></g></svg>
diff --git a/editor/icons/PageNext.svg b/editor/icons/PageNext.svg
index 89ff6219bb..2c3d032d63 100644
--- a/editor/icons/PageNext.svg
+++ b/editor/icons/PageNext.svg
@@ -1,42 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="12"
- viewBox="0 0 12 12"
- width="12"
- version="1.1"
- id="svg4"
- sodipodi:docname="PageNext.svg"
- inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs8" />
- <sodipodi:namedview
- id="namedview6"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="true"
- inkscape:zoom="105.00536"
- inkscape:cx="4.5854803"
- inkscape:cy="5.9377923"
- inkscape:window-width="3838"
- inkscape:window-height="1582"
- inkscape:window-x="0"
- inkscape:window-y="16"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg4">
- <inkscape:grid
- type="xygrid"
- id="grid989" />
- </sodipodi:namedview>
- <path
- d="m 4.5000207,9 3,-3 -3,-3"
- style="fill:none;stroke:#e0e0e0;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
- id="path2" />
-</svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="m4.5 9 3-3-3-3" fill="none" stroke="#e0e0e0" stroke-width="2"/></svg>
diff --git a/editor/icons/PagePrevious.svg b/editor/icons/PagePrevious.svg
index a2fa84da0c..37adc85d7a 100644
--- a/editor/icons/PagePrevious.svg
+++ b/editor/icons/PagePrevious.svg
@@ -1,42 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- height="12"
- viewBox="0 0 12 12"
- width="12"
- version="1.1"
- id="svg4"
- sodipodi:docname="PagePrevious.svg"
- inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:svg="http://www.w3.org/2000/svg">
- <defs
- id="defs8" />
- <sodipodi:namedview
- id="namedview6"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageshadow="2"
- inkscape:pageopacity="0.0"
- inkscape:pagecheckerboard="0"
- showgrid="true"
- inkscape:zoom="105.00536"
- inkscape:cx="4.5854803"
- inkscape:cy="5.9377923"
- inkscape:window-width="3838"
- inkscape:window-height="1582"
- inkscape:window-x="0"
- inkscape:window-y="16"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg4">
- <inkscape:grid
- type="xygrid"
- id="grid989" />
- </sodipodi:namedview>
- <path
- d="m 7.4999793,9 -3,-3 3,-3"
- style="fill:none;stroke:#e0e0e0;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
- id="path2" />
-</svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="m7.5 9-3-3 3-3" fill="none" stroke="#e0e0e0" stroke-width="2"/></svg>
diff --git a/editor/icons/ParallaxBackground.svg b/editor/icons/ParallaxBackground.svg
index 9d13f3a65d..f188230fcc 100644
--- a/editor/icons/ParallaxBackground.svg
+++ b/editor/icons/ParallaxBackground.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><ellipse cx="3" cy="1039.4" fill="#6e6e6e"/><path d="m2 2a1 1 0 0 0 -1 1v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-10a1 1 0 0 0 -1-1zm0 1h12v10h-12zm5 2-3 3 3 3zm2 0v6l3-3z" fill="#e0e0e0" fill-opacity=".99608" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m2 2a1 1 0 0 0 -1 1v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-10a1 1 0 0 0 -1-1zm0 1h12v10h-12zm5 2-3 3 3 3zm2 0v6l3-3z" fill="#e0e0e0" fill-opacity=".99608" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/ParallaxLayer.svg b/editor/icons/ParallaxLayer.svg
index 0768e941ad..58968b77fb 100644
--- a/editor/icons/ParallaxLayer.svg
+++ b/editor/icons/ParallaxLayer.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><ellipse cx="3" cy="1039.4" fill="#6e6e6e"/><path d="m3 1c-1.1046 0-2 .89543-2 2v10c0 1.1046.89543 2 2 2h10c1.1046 0 2-.89543 2-2v-10c0-1.1046-.89543-2-2-2zm0 1h10c.55228.0000096.99999.44772 1 1v10c-.00001.55228-.44772.99999-1 1h-10c-.55228-.00001-.99999-.44772-1-1v-10c.0000096-.55228.44772-.99999 1-1zm4 3-3 3 3 3zm2 0v6l3-3z" fill="#8da5f3" fill-opacity=".98824" fill-rule="evenodd" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m3 1c-1.1046 0-2 .89543-2 2v10c0 1.1046.89543 2 2 2h10c1.1046 0 2-.89543 2-2v-10c0-1.1046-.89543-2-2-2zm0 1h10c.55228.0000096.99999.44772 1 1v10c-.00001.55228-.44772.99999-1 1h-10c-.55228-.00001-.99999-.44772-1-1v-10c.0000096-.55228.44772-.99999 1-1zm4 3-3 3 3 3zm2 0v6l3-3z" fill="#8da5f3" fill-opacity=".98824" fill-rule="evenodd" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/PlayOverlay.svg b/editor/icons/PlayOverlay.svg
index 9b3299d1b9..9ff59b1170 100644
--- a/editor/icons/PlayOverlay.svg
+++ b/editor/icons/PlayOverlay.svg
@@ -1 +1 @@
-<svg height="64" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><rect fill="#044b94" fill-opacity=".6" height="64" rx="5" width="64"/><path d="m16 16 32 16-32 16" fill="#f2f2f2"/></svg>
+<svg height="64" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><rect fill="#699ce8" fill-opacity=".75" height="64" rx="5" width="64"/><path d="m16 16 32 16-32 16" fill="#ffffff"/></svg>
diff --git a/editor/icons/RectangleShape2D.svg b/editor/icons/RectangleShape2D.svg
index f0d6c54dc9..2d6a503255 100644
--- a/editor/icons/RectangleShape2D.svg
+++ b/editor/icons/RectangleShape2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><rect fill="none" height="8" rx=".000017" stroke="#68b6ff" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" width="12" x="2" y="4"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><rect fill="none" height="8" rx=".000017" stroke="#5fb2ff" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" width="12" x="2" y="4"/></svg>
diff --git a/editor/icons/ReverseGradient.svg b/editor/icons/ReverseGradient.svg
index 12f80d12dd..55bc5a1678 100644
--- a/editor/icons/ReverseGradient.svg
+++ b/editor/icons/ReverseGradient.svg
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8"?><svg version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient id="b" x1=".26458" x2="3.9688" y1=".79375" y2=".79375" gradientTransform="scale(3.7795)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" offset="0"/><stop stop-color="#ccc" stop-opacity="0" offset="1"/></linearGradient><linearGradient id="a" x1=".26458" x2="3.9688" y1="3.4396" y2="3.4396" gradientTransform="matrix(3.7795 0 0 3.7795 -16 -1.1865e-7)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" offset="0"/><stop stop-color="#ccc" stop-opacity="0" offset="1"/></linearGradient></defs><g><rect x="1" y="1" width="14" height="4" ry="1" fill="url(#b)"/><rect transform="scale(-1,1)" x="-15" y="11" width="14" height="4" ry="1" fill="url(#a)" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.7795"/><path d="m6 6 2 4 2-4z" fill="#ccc"/></g></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a"><stop offset="0" stop-color="#e0e0e0"/><stop offset="1" stop-color="#e0e0e0" stop-opacity="0"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1=".99998" x2="15.00008" xlink:href="#a" y1="2.99998" y2="2.99998"/><linearGradient id="c" gradientTransform="matrix(-14.0001 0 0 14.0001 15 13)" gradientUnits="userSpaceOnUse" x1="0" x2="1" xlink:href="#a" y1="0" y2="0"/><path d="m1 1h14v4h-14z" fill="url(#b)"/><path d="m1 11h14v4h-14z" fill="url(#c)"/><path d="m6 6 2 4 2-4z" fill="#e0e0e0" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/Ruler.svg b/editor/icons/Ruler.svg
index 2f026ed365..caf2f7f15c 100644
--- a/editor/icons/Ruler.svg
+++ b/editor/icons/Ruler.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 4.2333 4.2333" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v7.5 6.5h14zm3 7 4 4h-4z" fill="#e0e0e0" transform="scale(.26458)"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v14h14zm3 7 4 4h-4z" fill="#e0e0e0" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/Script.svg b/editor/icons/Script.svg
index 2a47c67def..997a4b5e06 100644
--- a/editor/icons/Script.svg
+++ b/editor/icons/Script.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m6 1v1a1 1 0 0 0 -1 1v10h-1v-2h-2v2a1 1 0 0 0 .5.86523 1 1 0 0 0 .5.13477v1h7a2 2 0 0 0 2-2v-8h3v-2a2 2 0 0 0 -2-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/><path d="m6 1c-1.1046 0-2 .89543-2 2v7h-2-1v1 2c0 1.1046.89543 2 2 2s2-.89543 2-2v-10c0-.55228.44772-1 1-1s1 .44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-.89543-2-2-2zm-4 10h2v2c0 .55228-.44772 1-1 1s-1-.44772-1-1z" fill="#b4b4b4" transform="translate(0 1036.4)"/><circle cx="3" cy="1048.4" fill="#e0e0e0"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m6 1v1a1 1 0 0 0 -1 1v10h-1v-2h-2v2a1 1 0 0 0 .5.86523 1 1 0 0 0 .5.13477v1h7a2 2 0 0 0 2-2v-8h3v-2a2 2 0 0 0 -2-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/><path d="m6 1c-1.1046 0-2 .89543-2 2v7h-2-1v1 2c0 1.1046.89543 2 2 2s2-.89543 2-2v-10c0-.55228.44772-1 1-1s1 .44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-.89543-2-2-2zm-4 10h2v2c0 .55228-.44772 1-1 1s-1-.44772-1-1z" fill="#b3b3b3" transform="translate(0 1036.4)"/><circle cx="3" cy="1048.4" fill="#e0e0e0"/></g></svg>
diff --git a/editor/icons/ScriptCreate.svg b/editor/icons/ScriptCreate.svg
index 564dcddc2e..70e13e14c4 100644
--- a/editor/icons/ScriptCreate.svg
+++ b/editor/icons/ScriptCreate.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m6 1v1c-.55228 0-1 .44772-1 1v10h-1v-2h-2v2c.0002826.35698.19084.68674.5.86523.15194.088045.32439.13452.5.13477v1h5 1v-1h-1v-4h2v-2h2v-3h3v-2c0-1.1046-.89543-2-2-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/><path d="m6 1c-1.1046 0-2 .89543-2 2v7h-2-1v1 2c0 1.1046.89543 2 2 2s2-.89543 2-2v-10c0-.55228.44772-1 1-1s1 .44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-.89543-2-2-2zm-4 10h2v2c0 .55228-.44772 1-1 1s-1-.44772-1-1z" fill="#b4b4b4" transform="translate(0 1036.4)"/><circle cx="3" cy="1048.4" fill="#e0e0e0"/><path d="m13 1049.4h2v-2h-2v-2h-2v2h-2v2h2v2h2z" fill="#5fff97" fill-rule="evenodd"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m6 1v1c-.55228 0-1 .44772-1 1v10h-1v-2h-2v2c.0002826.35698.19084.68674.5.86523.15194.088045.32439.13452.5.13477v1h5 1v-1h-1v-4h2v-2h2v-3h3v-2c0-1.1046-.89543-2-2-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/><path d="m6 1c-1.1046 0-2 .89543-2 2v7h-2-1v1 2c0 1.1046.89543 2 2 2s2-.89543 2-2v-10c0-.55228.44772-1 1-1s1 .44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-.89543-2-2-2zm-4 10h2v2c0 .55228-.44772 1-1 1s-1-.44772-1-1z" fill="#b3b3b3" transform="translate(0 1036.4)"/><circle cx="3" cy="1048.4" fill="#e0e0e0"/><path d="m13 1049.4h2v-2h-2v-2h-2v2h-2v2h2v2h2z" fill="#5fff97" fill-rule="evenodd"/></g></svg>
diff --git a/editor/icons/ScriptExtend.svg b/editor/icons/ScriptExtend.svg
index 5aa39e21a9..96291e8aa2 100644
--- a/editor/icons/ScriptExtend.svg
+++ b/editor/icons/ScriptExtend.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m6 1v1c-.55228 0-1 .44772-1 1v10h-1v-2h-2v2c.0002826.35698.19084.68674.5.86523.15194.088045.32439.13452.5.13477v1h7c.73866 0 1.3763-.40437 1.7227-1h-3.7227v-4h4v-5h3v-2c0-1.1046-.89543-2-2-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/><path d="m6 1c-1.1046 0-2 .89543-2 2v7h-2-1v1 2c0 1.1046.89543 2 2 2s2-.89543 2-2v-10c0-.55228.44772-1 1-1s1 .44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-.89543-2-2-2zm-4 10h2v2c0 .55228-.44772 1-1 1s-1-.44772-1-1z" fill="#b4b4b4" transform="translate(0 1036.4)"/><circle cx="3" cy="1048.4" fill="#e0e0e0"/><path d="m16 1048.4-3-3v2h-4v2h4v2z" fill="#68b6ff" fill-rule="evenodd"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m6 1v1c-.55228 0-1 .44772-1 1v10h-1v-2h-2v2c.0002826.35698.19084.68674.5.86523.15194.088045.32439.13452.5.13477v1h7c.73866 0 1.3763-.40437 1.7227-1h-3.7227v-4h4v-5h3v-2c0-1.1046-.89543-2-2-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/><path d="m6 1c-1.1046 0-2 .89543-2 2v7h-2-1v1 2c0 1.1046.89543 2 2 2s2-.89543 2-2v-10c0-.55228.44772-1 1-1s1 .44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-.89543-2-2-2zm-4 10h2v2c0 .55228-.44772 1-1 1s-1-.44772-1-1z" fill="#b3b3b3" transform="translate(0 1036.4)"/><circle cx="3" cy="1048.4" fill="#e0e0e0"/><path d="m16 1048.4-3-3v2h-4v2h4v2z" fill="#5fb2ff" fill-rule="evenodd"/></g></svg>
diff --git a/editor/icons/ScriptRemove.svg b/editor/icons/ScriptRemove.svg
index 9af184c946..392d38e06d 100644
--- a/editor/icons/ScriptRemove.svg
+++ b/editor/icons/ScriptRemove.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m6 1v1c-.55228 0-1 .44772-1 1v10h-1v-2h-2v2c.0002826.35698.19084.68674.5.86523.15194.088045.32439.13452.5.13477v1h5.6348l-1.584-1.584 1.4141-1.4141-1.4141-1.416 3.5352-3.5352 1.4141 1.4141v-.46484-3h3v-2c0-1.1046-.89543-2-2-2h-7z" fill="#e0e0e0" transform="translate(0 1036.4)"/><path d="m6 1c-1.1046 0-2 .89543-2 2v7h-2-1v1 2c0 1.1046.89543 2 2 2s2-.89543 2-2v-10c0-.55228.44772-1 1-1s1 .44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-.89543-2-2-2zm-4 10h2v2c0 .55228-.44772 1-1 1s-1-.44772-1-1z" fill="#b4b4b4" transform="translate(0 1036.4)"/><circle cx="3" cy="1048.4" fill="#e0e0e0"/><path d="m13.414 1048.4 1.4142-1.4142-1.4142-1.4142-1.4142 1.4142-1.4142-1.4142-1.4142 1.4142 1.4142 1.4142-1.4142 1.4142 1.4142 1.4142 1.4142-1.4142 1.4142 1.4142 1.4142-1.4142z" fill="#ff5f5f" fill-rule="evenodd"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m6 1v1c-.55228 0-1 .44772-1 1v10h-1v-2h-2v2c.0002826.35698.19084.68674.5.86523.15194.088045.32439.13452.5.13477v1h5.6348l-1.584-1.584 1.4141-1.4141-1.4141-1.416 3.5352-3.5352 1.4141 1.4141v-.46484-3h3v-2c0-1.1046-.89543-2-2-2h-7z" fill="#e0e0e0" transform="translate(0 1036.4)"/><path d="m6 1c-1.1046 0-2 .89543-2 2v7h-2-1v1 2c0 1.1046.89543 2 2 2s2-.89543 2-2v-10c0-.55228.44772-1 1-1s1 .44772 1 1v1 1 1h1 4v-1h-4v-1-1c0-1.1046-.89543-2-2-2zm-4 10h2v2c0 .55228-.44772 1-1 1s-1-.44772-1-1z" fill="#b3b3b3" transform="translate(0 1036.4)"/><circle cx="3" cy="1048.4" fill="#e0e0e0"/><path d="m13.414 1048.4 1.4142-1.4142-1.4142-1.4142-1.4142 1.4142-1.4142-1.4142-1.4142 1.4142 1.4142 1.4142-1.4142 1.4142 1.4142 1.4142 1.4142-1.4142 1.4142 1.4142 1.4142-1.4142z" fill="#ff5f5f" fill-rule="evenodd"/></g></svg>
diff --git a/editor/icons/SegmentShape2D.svg b/editor/icons/SegmentShape2D.svg
index 3fcbd78625..b6763f7429 100644
--- a/editor/icons/SegmentShape2D.svg
+++ b/editor/icons/SegmentShape2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4 12-12" style="fill:none;stroke:#68b6ff;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-width:2" transform="translate(0 -1036.4)"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m2 14 12-12" fill="none" stroke="#5fb2ff" stroke-width="2"/></svg>
diff --git a/editor/icons/SeparationRayShape2D.svg b/editor/icons/SeparationRayShape2D.svg
index aa8cee1210..6966e75bc6 100644
--- a/editor/icons/SeparationRayShape2D.svg
+++ b/editor/icons/SeparationRayShape2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1a1 1 0 0 0 -1 1v9.5859l-1.293-1.293a1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l3 3a1.0001 1.0001 0 0 0 .0039062.003907 1 1 0 0 0 .050781.044921 1.0001 1.0001 0 0 0 .03125.027344 1 1 0 0 0 .048828.035156 1.0001 1.0001 0 0 0 .023438.015625 1 1 0 0 0 .076172.044922 1.0001 1.0001 0 0 0 .0058593.003906 1 1 0 0 0 .013672.007813 1.0001 1.0001 0 0 0 .078125.035156 1 1 0 0 0 .074219.025391 1.0001 1.0001 0 0 0 .025391.009766 1 1 0 0 0 .039062.009765 1.0001 1.0001 0 0 0 .068359.013672 1.0001 1.0001 0 0 0 .097656.011719 1.0001 1.0001 0 0 0 .0078125 0 1 1 0 0 0 .0625.003906 1 1 0 0 0 .015625-.001953 1.0001 1.0001 0 0 0 .083984-.003906 1 1 0 0 0 .015625-.001953 1.0001 1.0001 0 0 0 .083984-.013672 1.0001 1.0001 0 0 0 .052734-.013672 1 1 0 0 0 .058594-.015625 1.0001 1.0001 0 0 0 .078125-.029297 1 1 0 0 0 .013672-.00586 1.0001 1.0001 0 0 0 .076172-.037109 1 1 0 0 0 .013672-.007812 1.0001 1.0001 0 0 0 .072266-.044922 1 1 0 0 0 .011719-.007813 1.0001 1.0001 0 0 0 .068359-.052734 1 1 0 0 0 .011719-.009766 1.0001 1.0001 0 0 0 .050781-.046875l.0097657-.011719 2.9902-2.9883a1 1 0 0 0 0-1.4141 1 1 0 0 0 -1.4141 0l-1.293 1.293v-9.5859a1 1 0 0 0 -1-1z" fill="#68b6ff" fill-rule="evenodd"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1a1 1 0 0 0 -1 1v9.5859l-1.293-1.293a1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l3 3a1.0001 1.0001 0 0 0 .0039062.003907 1 1 0 0 0 .050781.044921 1.0001 1.0001 0 0 0 .03125.027344 1 1 0 0 0 .048828.035156 1.0001 1.0001 0 0 0 .023438.015625 1 1 0 0 0 .076172.044922 1.0001 1.0001 0 0 0 .0058593.003906 1 1 0 0 0 .013672.007813 1.0001 1.0001 0 0 0 .078125.035156 1 1 0 0 0 .074219.025391 1.0001 1.0001 0 0 0 .025391.009766 1 1 0 0 0 .039062.009765 1.0001 1.0001 0 0 0 .068359.013672 1.0001 1.0001 0 0 0 .097656.011719 1.0001 1.0001 0 0 0 .0078125 0 1 1 0 0 0 .0625.003906 1 1 0 0 0 .015625-.001953 1.0001 1.0001 0 0 0 .083984-.003906 1 1 0 0 0 .015625-.001953 1.0001 1.0001 0 0 0 .083984-.013672 1.0001 1.0001 0 0 0 .052734-.013672 1 1 0 0 0 .058594-.015625 1.0001 1.0001 0 0 0 .078125-.029297 1 1 0 0 0 .013672-.00586 1.0001 1.0001 0 0 0 .076172-.037109 1 1 0 0 0 .013672-.007812 1.0001 1.0001 0 0 0 .072266-.044922 1 1 0 0 0 .011719-.007813 1.0001 1.0001 0 0 0 .068359-.052734 1 1 0 0 0 .011719-.009766 1.0001 1.0001 0 0 0 .050781-.046875l.0097657-.011719 2.9902-2.9883a1 1 0 0 0 0-1.4141 1 1 0 0 0 -1.4141 0l-1.293 1.293v-9.5859a1 1 0 0 0 -1-1z" fill="#5fb2ff" fill-rule="evenodd"/></svg>
diff --git a/editor/icons/ShapeCast2D.svg b/editor/icons/ShapeCast2D.svg
index dcdba92f45..36065705b0 100644
--- a/editor/icons/ShapeCast2D.svg
+++ b/editor/icons/ShapeCast2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#a5b7f3"><path d="m7 1v9h-3l4 5 4-5h-3v-9z"/><circle cx="7.990566" cy="4.8202" r="4.009434"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#8da5f3"><path d="m7 1v9h-3l4 5 4-5h-3v-9z"/><circle cx="7.990566" cy="4.8202" r="4.009434"/></g></svg>
diff --git a/editor/icons/SnapGrid.svg b/editor/icons/SnapGrid.svg
index e3aea78162..6960d4d13d 100644
--- a/editor/icons/SnapGrid.svg
+++ b/editor/icons/SnapGrid.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 0v3h-3v2h3v4h-3v2h3v3h2v-9h9v-2h-3v-3h-2v3h-4v-3zm4 13v2h2v-2zm6 0v2h2v-2z" fill="#e0e0e0"/><path d="m11 7a4 4 0 0 0 -4 4v2h2v-2a2 2 0 0 1 2-2 2 2 0 0 1 2 2v2h2v-2a4 4 0 0 0 -4-4z" fill="#fff" fill-opacity=".68627"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 0v2h-2v1h2v4h-2v1h2v4h-2v1h2v2h1v-2h3v-1h-3v-4h4l1-1v-4h4v3h1v-3h2v-1h-2v-2h-1v2h-4v-2h-1v2h-4v-2zm1 3h4v4h-4zm4 10v2h2v-2zm6 0v2h2v-2z" fill="#e0e0e0"/><path d="m11 7a4 4 0 0 0 -4 4v2h2v-2a2 2 0 0 1 2-2 2 2 0 0 1 2 2v2h2v-2a4 4 0 0 0 -4-4z" fill="#fff" fill-opacity=".68627"/></svg>
diff --git a/editor/icons/SphereShape3D.svg b/editor/icons/SphereShape3D.svg
index cc526abcec..6aceee5864 100644
--- a/editor/icons/SphereShape3D.svg
+++ b/editor/icons/SphereShape3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#68b6ff" r="7"/><circle cx="6" cy="5" fill="#a2d2ff" r="2"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#5fb2ff" r="7"/><circle cx="6" cy="5" fill="#a2d2ff" r="2"/></svg>
diff --git a/editor/icons/StaticBody2D.svg b/editor/icons/StaticBody2D.svg
index ba61605522..359d4d858c 100644
--- a/editor/icons/StaticBody2D.svg
+++ b/editor/icons/StaticBody2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m29 1042.4h1v1h-1z" fill="#fefeff"/><path d="m3 1a2 2 0 0 0 -1.4141.58594 2 2 0 0 0 -.58594 1.4141v10a2 2 0 0 0 .58594 1.4141 2 2 0 0 0 1.4141.58594h10a2 2 0 0 0 2-2v-10a2 2 0 0 0 -2-2h-10zm0 1h10a1 1 0 0 1 1 1v10a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1-1v-10a1 1 0 0 1 1-1zm0 1v2h2v-2zm8 0v2h2v-2zm-8 8v2h2v-2zm8 0v2h2v-2z" fill="#8da5f3" fill-opacity=".98824" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m3 1a2 2 0 0 0 -1.4141.58594 2 2 0 0 0 -.58594 1.4141v10a2 2 0 0 0 .58594 1.4141 2 2 0 0 0 1.4141.58594h10a2 2 0 0 0 2-2v-10a2 2 0 0 0 -2-2h-10zm0 1h10a1 1 0 0 1 1 1v10a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1-1v-10a1 1 0 0 1 1-1zm0 1v2h2v-2zm8 0v2h2v-2zm-8 8v2h2v-2zm8 0v2h2v-2z" fill="#8da5f3" fill-opacity=".98824" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/StatusError.svg b/editor/icons/StatusError.svg
index a9639c8749..278d77d8bf 100644
--- a/editor/icons/StatusError.svg
+++ b/editor/icons/StatusError.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3.866 0-7 3.134-7 7s3.134 7 7 7 7-3.134 7-7-3.134-7-7-7zm-2.8281 2.7578 2.8281 2.8281 2.8281-2.8281 1.4141 1.4141-2.8281 2.8281 2.8281 2.8281-1.4141 1.4141-2.8281-2.8281-2.8281 2.8281-1.4141-1.4141 2.8281-2.8281-2.8281-2.8281 1.4141-1.4141z" fill="#ff5d5d"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3.866 0-7 3.134-7 7s3.134 7 7 7 7-3.134 7-7-3.134-7-7-7zm-2.8281 2.7578 2.8281 2.8281 2.8281-2.8281 1.4141 1.4141-2.8281 2.8281 2.8281 2.8281-1.4141 1.4141-2.8281-2.8281-2.8281 2.8281-1.4141-1.4141 2.8281-2.8281-2.8281-2.8281 1.4141-1.4141z" fill="#ff5f5f"/></svg>
diff --git a/editor/icons/StatusSuccess.svg b/editor/icons/StatusSuccess.svg
index 6e7988100f..d8a05fc329 100644
--- a/editor/icons/StatusSuccess.svg
+++ b/editor/icons/StatusSuccess.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3.866 0-7 3.134-7 7s3.134 7 7 7 7-3.134 7-7-3.134-7-7-7zm3.293 3.877 1.4141 1.4141-5.707 5.709-3.707-3.709 1.4141-1.4141 2.293 2.293z" fill="#45ff8b"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3.866 0-7 3.134-7 7s3.134 7 7 7 7-3.134 7-7-3.134-7-7-7zm3.293 3.877 1.4141 1.4141-5.707 5.709-3.707-3.709 1.4141-1.4141 2.293 2.293z" fill="#5fff97"/></svg>
diff --git a/editor/icons/TerrainMatchCorners.svg b/editor/icons/TerrainMatchCorners.svg
index b9dfcf67d2..0d5cfe710c 100644
--- a/editor/icons/TerrainMatchCorners.svg
+++ b/editor/icons/TerrainMatchCorners.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-width="70.7093"><path d="m15 1h-6v3h3v3h3z" fill="#ffa62a"/><path d="m1 1h6v3h-2.9999996l-.0000004 3h-3.0000004z" fill="#1aab1a"/><path d="m1 15h5.9999999v-3h-3v-3h-2.9999999z" fill="#ffa62a"/><path d="m15.000001 15h-6v-3h2.999999l.000001-2.9999997h3z" fill="#1aab1a"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-width="70.7093"><path d="m15 1h-6v3h3v3h3z" fill="#ffca5f"/><path d="m1 1h6v3h-2.9999996l-.0000004 3h-3.0000004z" fill="#77ce57"/><path d="m1 15h5.9999999v-3h-3v-3h-2.9999999z" fill="#ffca5f"/><path d="m15.000001 15h-6v-3h2.999999l.000001-2.9999997h3z" fill="#77ce57"/></g></svg>
diff --git a/editor/icons/TerrainMatchCornersAndSides.svg b/editor/icons/TerrainMatchCornersAndSides.svg
index 81153005bd..a54ad8fd7c 100644
--- a/editor/icons/TerrainMatchCornersAndSides.svg
+++ b/editor/icons/TerrainMatchCornersAndSides.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1h4v3h-1l-1 1-1-1h-1z" fill="#ffa62a" stroke-width="70.7093"/><path d="m1 15h4v-3h-1v-1h-3z" fill="#1aab1a" stroke-width="70.7093"/><path d="m6 15h4v-3h-1l-1-1-1 1h-1z" fill="#ffa62a" stroke-width="99.998"/><g stroke-width="70.7093"><path d="m1 10v-4h3v1l1 1-1 1v1z" fill="#ffa62a"/><path d="m15 10v-4h-3v1l-1 1 1 1v1z" fill="#ffa62a"/><g fill="#1aab1a"><path d="m15 15h-4v-3h1v-1h3z"/><path d="m15 1h-4v3h1v1h3z"/><path d="m1 1h4.0000004v3h-1v1h-3.0000004z"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1h4v3h-1l-1 1-1-1h-1z" fill="#ffca5f" stroke-width="70.7093"/><path d="m1 15h4v-3h-1v-1h-3z" fill="#77ce57" stroke-width="70.7093"/><path d="m6 15h4v-3h-1l-1-1-1 1h-1z" fill="#ffca5f" stroke-width="99.998"/><g stroke-width="70.7093"><path d="m1 10v-4h3v1l1 1-1 1v1z" fill="#ffca5f"/><path d="m15 10v-4h-3v1l-1 1 1 1v1z" fill="#ffca5f"/><g fill="#77ce57"><path d="m15 15h-4v-3h1v-1h3z"/><path d="m15 1h-4v3h1v1h3z"/><path d="m1 1h4.0000004v3h-1v1h-3.0000004z"/></g></g></svg>
diff --git a/editor/icons/TerrainMatchSides.svg b/editor/icons/TerrainMatchSides.svg
index 1e2ec75ea7..ca3ec872fa 100644
--- a/editor/icons/TerrainMatchSides.svg
+++ b/editor/icons/TerrainMatchSides.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-width="70.7093"><path d="m1 14v-12l3 3v2l1 1-1 1v2z" fill="#1aab1a"/><path d="m15 14v-12l-3 2.7057075v2l-1 1.0000001 1 1v1.9999994z" fill="#1aab1a"/><g fill="#ffa62a"><path d="m2 15h12l-3-3h-2l-1-1-1 1h-2z"/><path d="m14 1h-12l2.9999992 3h1.9999998l1.000001.9999999 1-.9999999h1.999999z"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-width="70.7093"><path d="m1 14v-12l3 3v2l1 1-1 1v2z" fill="#77ce57"/><path d="m15 14v-12l-3 2.7057075v2l-1 1.0000001 1 1v1.9999994z" fill="#77ce57"/><g fill="#ffca5f"><path d="m2 15h12l-3-3h-2l-1-1-1 1h-2z"/><path d="m14 1h-12l2.9999992 3h1.9999998l1.000001.9999999 1-.9999999h1.999999z"/></g></g></svg>
diff --git a/editor/icons/TextEdit.svg b/editor/icons/TextEdit.svg
index 67a5145373..a749c17c91 100644
--- a/editor/icons/TextEdit.svg
+++ b/editor/icons/TextEdit.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m29 1042.4h1v1h-1z" fill="#fefeff"/><path d="m3 1c-1.1046 0-2 .8954-2 2v10c0 1.1046.89543 2 2 2h10c1.1046 0 2-.8954 2-2v-10c0-1.1046-.89543-2-2-2zm0 2h10v10h-10zm1 1v4h1v-4z" fill="#8eef97" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m3 1c-1.1046 0-2 .8954-2 2v10c0 1.1046.89543 2 2 2h10c1.1046 0 2-.8954 2-2v-10c0-1.1046-.89543-2-2-2zm0 2h10v10h-10zm1 1v4h1v-4z" fill="#8eef97" transform="translate(0 1036.4)"/></g></svg>
diff --git a/editor/icons/Texture3D.svg b/editor/icons/Texture3D.svg
index 795dd62ba5..a313613b26 100644
--- a/editor/icons/Texture3D.svg
+++ b/editor/icons/Texture3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" transform="translate(-.359546 .287637)"><path d="m2 1c-.5522847 0-1 .4477153-1 1v12c0 .552285.4477153 1 1 1h12c.552285 0 1-.447715 1-1v-12c0-.5522847-.447715-1-1-1zm1 2h10v8h-10z" fill-opacity=".99608" transform="translate(.359546 -.287637)"/><g fill-opacity=".996078" stroke-width=".203212" transform="scale(.9167105 1.0908569)"><path d="m5.8175194 8.9717502q-.2194689 0-.4633233-.032514-.2438544-.0243854-.4714519-.0731562-.2275974-.0487709-.4145524-.1056703-.1869551-.0568993-.2926253-.1056702l.2357259-1.0079315q.2113405.089413.5364797.1950835.3332677.097542.8209765.097542.5608651 0 .8209764-.2113404.2601114-.2113405.2601114-.5689936 0-.219469-.097542-.3657816-.089413-.1544415-.2519826-.2438547-.1625696-.0975418-.3901671-.1300557-.2194689-.0406424-.4714518-.0406424h-.4714519v-.9754176h.5364797q.1788266 0 .3413962-.032514.1706981-.032514.3007537-.1056703.1300557-.081285.203212-.2113404.081285-.1381842.081285-.3413962 0-.1544411-.065028-.2682398-.0650278-.1137987-.1706981-.186955-.0975417-.0731563-.2357259-.1056702-.1300557-.0406424-.2682398-.0406424-.3495247 0-.6502784.1056702-.2926253.1056703-.5364797.2601114l-.4308095-.8860043q.1300557-.0812848.3007538-.1706981.1788266-.0894133.390167-.1625696.2113405-.0731563.4470664-.1219272.2438544-.048771.5120943-.048771.4958373 0 .8534904.1219272.3657816.1137987.6015075.3332677.2357259.2113405.3495246.5039657.1137987.2844968.1137987.625893 0 .3332677-.186955.6502784-.186955.3088822-.5039657.4714518.4389379.1788266.6746638.5364797.2438544.3495246.2438544.8453619 0 .3901671-.1300557.7234347-.1300557.3251393-.406424.5689937-.2763683.235726-.7071777.3739101-.422681.1300557-1.0079316.1300557z"/><path d="m10.502445 7.817506q.08941.00813.203212.016257.121927 0 .284497 0 .951032 0 1.406227-.4795803.463323-.4795803.463323-1.3249422 0-.8860044-.438938-1.3411992-.438938-.4551949-1.38997-.4551949-.130055 0-.26824.00813-.138184 0-.260111.016257zm3.665945-1.7882655q0 .7315631-.227598 1.2761713-.227597.5446082-.650278.9022613-.414553.3576531-1.01606.5364797-.601508.1788265-1.349328.1788265-.341396 0-.796591-.032514-.4551948-.0243853-.8941328-.1137986v-5.486724q.438938-.081285.9103898-.1056702.47958-.032514.820976-.032514.723435 0 1.308686.1625696.593379.1625696 1.01606.5120943.422681.3495246.650278.8941328.227598.5446081.227598 1.3086853z"/></g></g></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-rule="nonzero"><path d="m2 1c-.552 0-1 .448-1 1v12c0 .552.448 1 1 1h12c.552 0 1-.448 1-1v-12c0-.552-.448-1-1-1zm1 2h10v8h-10z"/><g fill-opacity=".99"><path d="m4.973 10.075c-.134 0-.275-.012-.424-.036-.149-.018-.293-.044-.432-.08-.14-.035-.266-.074-.381-.115-.114-.041-.203-.08-.268-.115l.216-1.1c.129.065.293.136.492.213.204.071.455.106.753.106.342 0 .593-.076.752-.23s.239-.361.239-.621c0-.159-.03-.292-.09-.399-.054-.112-.131-.201-.231-.266-.099-.071-.218-.118-.357-.142-.134-.029-.279-.044-.432-.044h-.433v-1.064h.492c.109 0 .214-.012.313-.036.104-.023.196-.062.276-.115.079-.059.141-.136.186-.23.05-.101.075-.225.075-.373 0-.112-.02-.21-.06-.292-.04-.083-.092-.151-.157-.204-.059-.054-.131-.092-.216-.116-.079-.029-.161-.044-.246-.044-.213 0-.412.038-.596.115-.178.077-.342.172-.491.284l-.395-.966c.079-.06.171-.122.275-.187.11-.065.229-.124.358-.177s.266-.098.41-.133c.149-.035.305-.053.469-.053.303 0 .564.044.783.133.223.083.407.204.551.363.144.154.251.337.321.55.069.207.104.435.104.683 0 .242-.057.479-.172.709-.114.225-.268.396-.462.515.269.13.475.325.619.585.149.254.223.561.223.922 0 .284-.039.547-.119.789-.079.237-.203.443-.372.621-.169.171-.385.307-.649.408-.258.094-.566.142-.924.142z"/><path d="m9.268 8.815c.055.006.117.012.186.018h.261c.581 0 1.011-.174 1.289-.523.284-.349.425-.831.425-1.445 0-.645-.134-1.132-.402-1.463-.269-.331-.693-.497-1.274-.497-.08 0-.162.003-.246.009-.085 0-.164.006-.239.018zm3.361-1.95c0 .532-.07.996-.209 1.392s-.338.724-.596.984c-.253.26-.564.455-.931.585-.368.13-.78.195-1.237.195-.209 0-.452-.011-.731-.035-.278-.018-.551-.059-.819-.124v-5.986c.268-.059.546-.097.834-.115.293-.023.544-.035.753-.035.442 0 .842.059 1.2.177.362.118.673.305.931.559s.457.579.596.975.209.872.209 1.428z"/></g></g></svg>
diff --git a/editor/icons/TimelineIndicator.svg b/editor/icons/TimelineIndicator.svg
index d63026b9e2..3f89206814 100644
--- a/editor/icons/TimelineIndicator.svg
+++ b/editor/icons/TimelineIndicator.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 0h10l-4 4h-2z" fill="#fefefe"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 0h10l-4 4h-2z" fill="#ffffff"/></svg>
diff --git a/editor/icons/ToolTriangle.svg b/editor/icons/ToolTriangle.svg
index 51dee03f60..a682e8a36b 100644
--- a/editor/icons/ToolTriangle.svg
+++ b/editor/icons/ToolTriangle.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-26.001 -1046.2683)"><path d="m27.695915 1056.3022s7.457627-8.0678 7.118644-7.8644 5.830509 11.7288 5.830509 11.7288z" fill="#e0e0e0"/><g fill="#4b4b4b" stroke="#e0e0e0" stroke-width=".512"><circle cx="34.662014" cy="1048.5903" r="1.607564"/><circle cx="39.933205" cy="1059.6581" r="1.607564"/><circle cx="28.17049" cy="1056.2683" r="1.607564"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-26.001 -1046.2683)"><path d="m27.695915 1056.3022s7.457627-8.0678 7.118644-7.8644 5.830509 11.7288 5.830509 11.7288z" fill="#e0e0e0"/><g fill="#414042" stroke="#e0e0e0" stroke-width=".512"><circle cx="34.662014" cy="1048.5903" r="1.607564"/><circle cx="39.933205" cy="1059.6581" r="1.607564"/><circle cx="28.17049" cy="1056.2683" r="1.607564"/></g></g></svg>
diff --git a/editor/icons/TransitionEndAutoBig.svg b/editor/icons/TransitionEndAutoBig.svg
index 22f3414d34..97774d7d6d 100644
--- a/editor/icons/TransitionEndAutoBig.svg
+++ b/editor/icons/TransitionEndAutoBig.svg
@@ -1 +1 @@
-<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><g fill="#77ce57" stroke="#41562e"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill-rule="evenodd" transform="matrix(1.4099529 0 0 1.4099529 -4.197589 -1462.5094)"/><rect height="14.194397" ry="1.075597" stroke-width="1.409953" width="4.325911" x="14.371336" y="3.007612"/></g></svg>
+<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><g fill="#77ce57" stroke="#414042"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill-rule="evenodd" transform="matrix(1.4099529 0 0 1.4099529 -4.197589 -1462.5094)"/><rect height="14.194397" ry="1.075597" stroke-width="1.409953" width="4.325911" x="14.371336" y="3.007612"/></g></svg>
diff --git a/editor/icons/TransitionEndBig.svg b/editor/icons/TransitionEndBig.svg
index 641f9c55d0..6cfcf44bf0 100644
--- a/editor/icons/TransitionEndBig.svg
+++ b/editor/icons/TransitionEndBig.svg
@@ -1 +1 @@
-<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" stroke="#424242"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill-rule="evenodd" stroke-width=".999944" transform="matrix(1.4203458 0 0 1.4203458 -4.29479 -1473.1325)"/><rect height="14.299023" ry="1.083525" stroke-width="1.420266" width="4.357798" x="14.411009" y="3.186887"/></g></svg>
+<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" stroke="#414042"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill-rule="evenodd" stroke-width=".999944" transform="matrix(1.4203458 0 0 1.4203458 -4.29479 -1473.1325)"/><rect height="14.299023" ry="1.083525" stroke-width="1.420266" width="4.357798" x="14.411009" y="3.186887"/></g></svg>
diff --git a/editor/icons/TransitionImmediateAutoBig.svg b/editor/icons/TransitionImmediateAutoBig.svg
index fe5e0903b5..36a49621eb 100644
--- a/editor/icons/TransitionImmediateAutoBig.svg
+++ b/editor/icons/TransitionImmediateAutoBig.svg
@@ -1 +1 @@
-<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill="#77ce57" fill-rule="evenodd" stroke="#41562e" transform="matrix(1.571031 0 0 1.571031 -2.725768 -1630.6239)"/></svg>
+<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill="#77ce57" fill-rule="evenodd" stroke="#414042" transform="matrix(1.571031 0 0 1.571031 -2.725768 -1630.6239)"/></svg>
diff --git a/editor/icons/TransitionImmediateBig.svg b/editor/icons/TransitionImmediateBig.svg
index 2365518cc3..aa79e63457 100644
--- a/editor/icons/TransitionImmediateBig.svg
+++ b/editor/icons/TransitionImmediateBig.svg
@@ -1 +1 @@
-<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill="#e0e0e0" fill-rule="evenodd" stroke="#404040" transform="matrix(1.571031 0 0 1.571031 -2.725768 -1630.6239)"/></svg>
+<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill="#e0e0e0" fill-rule="evenodd" stroke="#414042" transform="matrix(1.571031 0 0 1.571031 -2.725768 -1630.6239)"/></svg>
diff --git a/editor/icons/TransitionSyncAutoBig.svg b/editor/icons/TransitionSyncAutoBig.svg
index 27cb637667..440d4c4770 100644
--- a/editor/icons/TransitionSyncAutoBig.svg
+++ b/editor/icons/TransitionSyncAutoBig.svg
@@ -1 +1 @@
-<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><g fill="#77ce57" stroke="#41562e"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill-rule="evenodd" transform="matrix(1.4099529 0 0 1.4099529 2.175293 -1462.5094)"/><rect height="14.194397" ry="1.075597" stroke-width="1.409953" width="4.325911" x="1.625573" y="3.007612"/></g></svg>
+<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><g fill="#77ce57" stroke="#414042"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill-rule="evenodd" transform="matrix(1.4099529 0 0 1.4099529 2.175293 -1462.5094)"/><rect height="14.194397" ry="1.075597" stroke-width="1.409953" width="4.325911" x="1.625573" y="3.007612"/></g></svg>
diff --git a/editor/icons/TransitionSyncBig.svg b/editor/icons/TransitionSyncBig.svg
index 27ae519739..c6ef188e98 100644
--- a/editor/icons/TransitionSyncBig.svg
+++ b/editor/icons/TransitionSyncBig.svg
@@ -1 +1 @@
-<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" stroke="#424242"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill-rule="evenodd" stroke-width=".999944" transform="matrix(1.4203458 0 0 1.4203458 1.874702 -1473.1325)"/><rect height="14.299023" ry="1.083525" stroke-width="1.420266" width="4.357798" x="1.461856" y="3.186887"/></g></svg>
+<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" stroke="#414042"><path d="m4.9883 1039.4c-.5469.01-.98717.4511-.98828.998v8c.0001163.7986.89011 1.275 1.5547.8321l6-4c.59362-.3959.59362-1.2682 0-1.6641l-6-4c-.1678-.1111-.3652-.1689-.56641-.166z" fill-rule="evenodd" stroke-width=".999944" transform="matrix(1.4203458 0 0 1.4203458 1.874702 -1473.1325)"/><rect height="14.299023" ry="1.083525" stroke-width="1.420266" width="4.357798" x="1.461856" y="3.186887"/></g></svg>
diff --git a/editor/icons/VFlowContainer.svg b/editor/icons/VFlowContainer.svg
new file mode 100644
index 0000000000..ccb0bea883
--- /dev/null
+++ b/editor/icons/VFlowContainer.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.097 0-2 .903-2 2v10c0 1.097.903 2 2 2h10c1.097 0 2-.903 2-2v-10c0-1.097-.903-2-2-2zm0 2h10v10h-10zm7.998.998c-.554 0-1 .446-1 1v4.004c0 .554.446 1 1 1s1-.446 1-1v-4.004c0-.554-.446-1-1-1zm-6 .004c-.554 0-1 .446-1 1v2c0 .554.446 1 1 1s1-.446 1-1v-2c0-.554-.446-1-1-1zm3 0c-.554 0-1 .446-1 1s.446 1 1 1 1-.446 1-1-.446-1-1-1zm0 3c-.554 0-1 .446-1 1v3c0 .554.446 1 1 1s1-.446 1-1v-3c0-.554-.446-1-1-1zm-3 1.996c-.554 0-1 .446-1 1v1.004c0 .554.446 1 1 1s1-.446 1-1v-1.004c0-.554-.446-1-1-1z" fill="#8eef97" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/VisualShaderGraphTextureUniform.svg b/editor/icons/VisualShaderGraphTextureUniform.svg
index ed9e084fd3..c95a72601f 100644
--- a/editor/icons/VisualShaderGraphTextureUniform.svg
+++ b/editor/icons/VisualShaderGraphTextureUniform.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eae068"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eac968"/></svg>
diff --git a/editor/icons/VisualShaderNodeColorConstant.svg b/editor/icons/VisualShaderNodeColorConstant.svg
index cbc5b3a471..d327012a1f 100644
--- a/editor/icons/VisualShaderNodeColorConstant.svg
+++ b/editor/icons/VisualShaderNodeColorConstant.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".392157" transform="translate(0 -1038.3622)"><path d="m7 1039.3622a4.0000172 4.0000172 0 0 0 -4 4 4.0000172 4.0000172 0 0 0 .03906.5195 4.0000172 4.0000172 0 0 0 -2.039062 3.4805 4.0000172 4.0000172 0 0 0 4 4 4.0000172 4.0000172 0 0 0 1.998047-.541 4.0000172 4.0000172 0 0 0 2.001953.541 4.0000172 4.0000172 0 0 0 4-4 4.0000172 4.0000172 0 0 0 -2.037109-3.4824 4.0000172 4.0000172 0 0 0 .03711-.5176 4.0000172 4.0000172 0 0 0 -4-4z" fill="#fff"/><path d="m7 1040.3622a3 3 0 0 0 -3 3 3 3 0 0 0 .210937 1.1055 3 3 0 0 0 -2.210937 2.8945 3 3 0 0 0 3 3 3 3 0 0 0 2-.7676 3 3 0 0 0 2 .7676 3 3 0 0 0 3-3 3 3 0 0 0 -2.2148438-2.8906 3 3 0 0 0 .2148438-1.1094 3 3 0 0 0 -3-3z" fill="#fff"/><circle cx="7" cy="1043.3622" fill="#f00" r="3"/><circle cx="5" cy="1047.3622" fill="#00f" r="3"/><circle cx="9" cy="1047.3622" fill="#0f0" r="3"/><circle cx="7" cy="1043.3622" fill="#f00" r="3"/><circle cx="5" cy="1047.3622" fill="#00f" r="3"/><circle cx="9" cy="1047.3622" fill="#0f0" r="3"/></g></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".392157" transform="translate(0 -1038.3622)"><path d="m7 1039.3622a4.0000172 4.0000172 0 0 0 -4 4 4.0000172 4.0000172 0 0 0 .03906.5195 4.0000172 4.0000172 0 0 0 -2.039062 3.4805 4.0000172 4.0000172 0 0 0 4 4 4.0000172 4.0000172 0 0 0 1.998047-.541 4.0000172 4.0000172 0 0 0 2.001953.541 4.0000172 4.0000172 0 0 0 4-4 4.0000172 4.0000172 0 0 0 -2.037109-3.4824 4.0000172 4.0000172 0 0 0 .03711-.5176 4.0000172 4.0000172 0 0 0 -4-4z" fill="#fff"/><path d="m7 1040.3622a3 3 0 0 0 -3 3 3 3 0 0 0 .210937 1.1055 3 3 0 0 0 -2.210937 2.8945 3 3 0 0 0 3 3 3 3 0 0 0 2-.7676 3 3 0 0 0 2 .7676 3 3 0 0 0 3-3 3 3 0 0 0 -2.2148438-2.8906 3 3 0 0 0 .2148438-1.1094 3 3 0 0 0 -3-3z" fill="#ffffff"/><circle cx="7" cy="1043.3622" fill="#ff0000" r="3"/><circle cx="5" cy="1047.3622" fill="#0000ff" r="3"/><circle cx="9" cy="1047.3622" fill="#00ff00" r="3"/><circle cx="7" cy="1043.3622" fill="#ff0000" r="3"/><circle cx="5" cy="1047.3622" fill="#0000ff" r="3"/><circle cx="9" cy="1047.3622" fill="#00ff00" r="3"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeColorOp.svg b/editor/icons/VisualShaderNodeColorOp.svg
index 7b6cd8149b..005da8b6e1 100644
--- a/editor/icons/VisualShaderNodeColorOp.svg
+++ b/editor/icons/VisualShaderNodeColorOp.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><g fill="#fff"><path d="m4 1050.3622h6v-10h-6z" fill-rule="evenodd" stroke="#fff" stroke-linejoin="round" stroke-width="2"/><path d="m1 1041.3622h2v2h-2z"/><path d="m1 1047.3622h2v2h-2z"/><path d="m11 1044.3622h2v2h-2z"/></g><g fill-opacity=".862745"><path d="m5 1041.3622h4v2h-4z" fill="#ff4646"/><path d="m5 1044.3622h4v2h-4z" fill="#46ff46"/><path d="m5 1047.3622h4v2h-4z" fill="#4646ff"/></g></g></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><g fill="#ffffff"><path d="m4 1050.3622h6v-10h-6z" fill-rule="evenodd" stroke="#ffffff" stroke-linejoin="round" stroke-width="2"/><path d="m1 1041.3622h2v2h-2z"/><path d="m1 1047.3622h2v2h-2z"/><path d="m11 1044.3622h2v2h-2z"/></g><g fill-opacity=".862745"><path d="m5 1041.3622h4v2h-4z" fill="#ff0000"/><path d="m5 1044.3622h4v2h-4z" fill="#00ff00"/><path d="m5 1047.3622h4v2h-4z" fill="#0000ff"/></g></g></svg>
diff --git a/editor/icons/VisualShaderNodeColorUniform.svg b/editor/icons/VisualShaderNodeColorUniform.svg
index ce89b16583..db41e5eca3 100644
--- a/editor/icons/VisualShaderNodeColorUniform.svg
+++ b/editor/icons/VisualShaderNodeColorUniform.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m2 1038.3622c-1.10457 0-2 .8954-2 2v10c0 1.1046.89543 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2z" fill="#fff"/><g fill-opacity=".392157"><path d="m7 2a3 3 0 0 0 -3 3 3 3 0 0 0 .2109375 1.1054688 3 3 0 0 0 -2.2109375 2.8945312 3 3 0 0 0 3 3 3 3 0 0 0 2-.767578 3 3 0 0 0 2 .767578 3 3 0 0 0 3-3 3 3 0 0 0 -2.2148438-2.890625 3 3 0 0 0 .2148438-1.109375 3 3 0 0 0 -3-3z" fill="#fff" transform="translate(0 1038.3622)"/><circle cx="7" cy="1043.3622" fill="#f00" r="3"/><circle cx="5" cy="1047.3622" fill="#00f" r="3"/><circle cx="9" cy="1047.3622" fill="#0f0" r="3"/><circle cx="7" cy="1043.3622" fill="#f00" r="3"/><circle cx="5" cy="1047.3622" fill="#00f" r="3"/><circle cx="9" cy="1047.3622" fill="#0f0" r="3"/></g></g></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m2 1038.3622c-1.10457 0-2 .8954-2 2v10c0 1.1046.89543 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2z" fill="#fff"/><g fill-opacity=".392157"><path d="m7 2a3 3 0 0 0 -3 3 3 3 0 0 0 .2109375 1.1054688 3 3 0 0 0 -2.2109375 2.8945312 3 3 0 0 0 3 3 3 3 0 0 0 2-.767578 3 3 0 0 0 2 .767578 3 3 0 0 0 3-3 3 3 0 0 0 -2.2148438-2.890625 3 3 0 0 0 .2148438-1.109375 3 3 0 0 0 -3-3z" fill="#ffffff" transform="translate(0 1038.3622)"/><circle cx="7" cy="1043.3622" fill="#ff0000" r="3"/><circle cx="5" cy="1047.3622" fill="#0000ff" r="3"/><circle cx="9" cy="1047.3622" fill="#00ff00" r="3"/><circle cx="7" cy="1043.3622" fill="#ff0000" r="3"/><circle cx="5" cy="1047.3622" fill="#0000ff" r="3"/><circle cx="9" cy="1047.3622" fill="#00ff00" r="3"/></g></g></svg>
diff --git a/editor/icons/VisualShaderNodeCurveTexture.svg b/editor/icons/VisualShaderNodeCurveTexture.svg
index c0ee634ca4..e468acf8ca 100644
--- a/editor/icons/VisualShaderNodeCurveTexture.svg
+++ b/editor/icons/VisualShaderNodeCurveTexture.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m2 1049.3622c8 0 9 0 9-9" fill="none" stroke="#f6f6f6" stroke-linecap="round" stroke-width="2"/><path d="m11 4a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-5 5a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#68d0ea" transform="translate(0 1038.3622)"/></g></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m2 1049.3622c8 0 9 0 9-9" fill="none" stroke="#f9f9f9" stroke-linecap="round" stroke-width="2"/><path d="m11 4a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-5 5a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#5fb2ff" transform="translate(0 1038.3622)"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeCurveXYZTexture.svg b/editor/icons/VisualShaderNodeCurveXYZTexture.svg
index c0ee634ca4..e468acf8ca 100644
--- a/editor/icons/VisualShaderNodeCurveXYZTexture.svg
+++ b/editor/icons/VisualShaderNodeCurveXYZTexture.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m2 1049.3622c8 0 9 0 9-9" fill="none" stroke="#f6f6f6" stroke-linecap="round" stroke-width="2"/><path d="m11 4a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-5 5a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#68d0ea" transform="translate(0 1038.3622)"/></g></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m2 1049.3622c8 0 9 0 9-9" fill="none" stroke="#f9f9f9" stroke-linecap="round" stroke-width="2"/><path d="m11 4a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm-5 5a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2z" fill="#5fb2ff" transform="translate(0 1038.3622)"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeExpression.svg b/editor/icons/VisualShaderNodeExpression.svg
index 8a930d4078..710ba818b7 100644
--- a/editor/icons/VisualShaderNodeExpression.svg
+++ b/editor/icons/VisualShaderNodeExpression.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#ac73f1"><path d="m4.859536 3.0412379c-2.0539867 0-3.7190721 1.6650852-3.7190721 3.719072v6.1984521h2.4793814v-2.479381h2.4793814v-2.4793803h-2.4793814v-1.2396908c0-.6846622.5550285-1.2396907 1.2396907-1.2396907h1.2396907v-2.4793813z"/><path d="m7.5889175 3.0000003 2.5000005 4.9999997-2.5000005 5h2.5000005l1.135249-2.727 1.36475 2.727h2.499999l-2.499999-5 2.499999-4.9999997h-2.499999l-1.13525 2.7269998-1.364749-2.7269998zm7.4999985 9.9999997v-6.25z"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#cf68ea"><path d="m4.859536 3.0412379c-2.0539867 0-3.7190721 1.6650852-3.7190721 3.719072v6.1984521h2.4793814v-2.479381h2.4793814v-2.4793803h-2.4793814v-1.2396908c0-.6846622.5550285-1.2396907 1.2396907-1.2396907h1.2396907v-2.4793813z"/><path d="m7.5889175 3.0000003 2.5000005 4.9999997-2.5000005 5h2.5000005l1.135249-2.727 1.36475 2.727h2.499999l-2.499999-5 2.499999-4.9999997h-2.499999l-1.13525 2.7269998-1.364749-2.7269998zm7.4999985 9.9999997v-6.25z"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeInput.svg b/editor/icons/VisualShaderNodeInput.svg
index ec347100d7..0c4de2e681 100644
--- a/editor/icons/VisualShaderNodeInput.svg
+++ b/editor/icons/VisualShaderNodeInput.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><circle cx="7" cy="7" fill="#f6f6f6" r="6"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><circle cx="7" cy="7" fill="#f9f9f9" r="6"/></svg>
diff --git a/editor/icons/VisualShaderNodeTexture2DArrayUniform.svg b/editor/icons/VisualShaderNodeTexture2DArrayUniform.svg
index ed9e084fd3..c95a72601f 100644
--- a/editor/icons/VisualShaderNodeTexture2DArrayUniform.svg
+++ b/editor/icons/VisualShaderNodeTexture2DArrayUniform.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eae068"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eac968"/></svg>
diff --git a/editor/icons/VisualShaderNodeTexture3DUniform.svg b/editor/icons/VisualShaderNodeTexture3DUniform.svg
index ed9e084fd3..c95a72601f 100644
--- a/editor/icons/VisualShaderNodeTexture3DUniform.svg
+++ b/editor/icons/VisualShaderNodeTexture3DUniform.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eae068"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eac968"/></svg>
diff --git a/editor/icons/VisualShaderNodeTextureUniform.svg b/editor/icons/VisualShaderNodeTextureUniform.svg
index ed9e084fd3..c95a72601f 100644
--- a/editor/icons/VisualShaderNodeTextureUniform.svg
+++ b/editor/icons/VisualShaderNodeTextureUniform.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eae068"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eac968"/></svg>
diff --git a/editor/icons/VisualShaderNodeTextureUniformTriplanar.svg b/editor/icons/VisualShaderNodeTextureUniformTriplanar.svg
index ed9e084fd3..c95a72601f 100644
--- a/editor/icons/VisualShaderNodeTextureUniformTriplanar.svg
+++ b/editor/icons/VisualShaderNodeTextureUniformTriplanar.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eae068"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm0 2h10v10h-10zm9 2-4 4-2-2-2 3h8z" fill="#eac968"/></svg>
diff --git a/editor/icons/VisualShaderNodeTransformCompose.svg b/editor/icons/VisualShaderNodeTransformCompose.svg
index 6c7b28cda3..774f9e77b1 100644
--- a/editor/icons/VisualShaderNodeTransformCompose.svg
+++ b/editor/icons/VisualShaderNodeTransformCompose.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="1.000747" x2="13.014989" y1="1045.3622" y2="1045.3622"><stop offset="0" stop-color="#b8ea68"/><stop offset="1" stop-color="#ea686c"/></linearGradient><path d="m1.9909808 1039.3524a1.0001 1.0001 0 0 0 -.697265 1.7168l3.2929683 3.293h-2.5859373a1.0001 1.0001 0 1 0 0 2h2.5859373l-3.2929683 3.293a1.0001 1.0001 0 1 0 1.414062 1.414l4.7070313-4.707h4.5859379a1.0001 1.0001 0 1 0 0-2h-4.5859379l-4.7070313-4.707a1.0001 1.0001 0 0 0 -.716797-.3028z" fill="url(#a)" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="1.000747" x2="13.014989" y1="1045.3622" y2="1045.3622"><stop offset="0" stop-color="#77ce57"/><stop offset="1" stop-color="#ea686c"/></linearGradient><path d="m1.9909808 1039.3524a1.0001 1.0001 0 0 0 -.697265 1.7168l3.2929683 3.293h-2.5859373a1.0001 1.0001 0 1 0 0 2h2.5859373l-3.2929683 3.293a1.0001 1.0001 0 1 0 1.414062 1.414l4.7070313-4.707h4.5859379a1.0001 1.0001 0 1 0 0-2h-4.5859379l-4.7070313-4.707a1.0001 1.0001 0 0 0 -.716797-.3028z" fill="url(#a)" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
diff --git a/editor/icons/VisualShaderNodeTransformDecompose.svg b/editor/icons/VisualShaderNodeTransformDecompose.svg
index 276b3ea7c8..9594bcb263 100644
--- a/editor/icons/VisualShaderNodeTransformDecompose.svg
+++ b/editor/icons/VisualShaderNodeTransformDecompose.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(-1 0 0 1 13.999754 1038.3622)" gradientUnits="userSpaceOnUse" x1="1" x2="13.014242" y1="7" y2="7"><stop offset="0" stop-color="#b8ea68"/><stop offset="1" stop-color="#ea686c"/></linearGradient><path d="m12.00952 1039.3524a1.0001 1.0001 0 0 1 .697265 1.7168l-3.2929683 3.293h2.5859373a1.0001 1.0001 0 1 1 0 2h-2.5859373l3.2929683 3.293a1.0001 1.0001 0 1 1 -1.414062 1.414l-4.7070313-4.707h-4.5859377a1.0001 1.0001 0 1 1 0-2h4.5859377l4.7070313-4.707a1.0001 1.0001 0 0 1 .716797-.3028z" fill="url(#a)" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(-1 0 0 1 13.999754 1038.3622)" gradientUnits="userSpaceOnUse" x1="1" x2="13.014242" y1="7" y2="7"><stop offset="0" stop-color="#77ce57"/><stop offset="1" stop-color="#ea686c"/></linearGradient><path d="m12.00952 1039.3524a1.0001 1.0001 0 0 1 .697265 1.7168l-3.2929683 3.293h2.5859373a1.0001 1.0001 0 1 1 0 2h-2.5859373l3.2929683 3.293a1.0001 1.0001 0 1 1 -1.414062 1.414l-4.7070313-4.707h-4.5859377a1.0001 1.0001 0 1 1 0-2h4.5859377l4.7070313-4.707a1.0001 1.0001 0 0 1 .716797-.3028z" fill="url(#a)" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
diff --git a/editor/icons/VisualShaderNodeTransformVecMult.svg b/editor/icons/VisualShaderNodeTransformVecMult.svg
index fe133b6ffe..611f027402 100644
--- a/editor/icons/VisualShaderNodeTransformVecMult.svg
+++ b/editor/icons/VisualShaderNodeTransformVecMult.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m9 1042.3622 2 6 2-6" fill="none" stroke="#b8ea68" stroke-linejoin="round" stroke-width="2"/><circle cx="7" cy="1046.3622" fill="#b8ea68" r="1"/><path d="m1 1049.3621v-7l2 3 2-3v7" fill="none" stroke="#ea686c" stroke-linejoin="round" stroke-width="2"/></g></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1038.3622)"><path d="m9 1042.3622 2 6 2-6" fill="none" stroke="#77ce57" stroke-linejoin="round" stroke-width="2"/><circle cx="7" cy="1046.3622" fill="#77ce57" r="1"/><path d="m1 1049.3621v-7l2 3 2-3v7" fill="none" stroke="#ea686c" stroke-linejoin="round" stroke-width="2"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeVec3Uniform.svg b/editor/icons/VisualShaderNodeVec3Uniform.svg
index 6e0175230c..eed10289c0 100644
--- a/editor/icons/VisualShaderNodeVec3Uniform.svg
+++ b/editor/icons/VisualShaderNodeVec3Uniform.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954305-2 2v10c0 1.104569.8954305 2 2 2h10c1.104569 0 2-.895431 2-2v-10c0-1.1045695-.895431-2-2-2zm6 0 3 3-3 3v-2h-5v-2h5zm-3.65625 5.6289062.5136719.8574219 2.1425781 3.5703129 2.1425781-3.5703129.5136719-.8574219 1.714844 1.0292969-.513672.8574219-3.0000001 5c-.3885014.647055-1.3263424.647055-1.7148438 0l-3-5-.5136719-.8574219z" fill="#b8ea68"/><path d="m23 0v2h-5v2h5v2l3-3zm-3.65625 5.6289062-1.714844 1.0292969.513672.8574219 3 5c.388501.647056 1.326343.647056 1.714844 0l3-5 .513672-.8574219-1.714844-1.0292969-.513672.8574219-2.142578 3.5703129-2.142578-3.5703129z" fill-rule="evenodd"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954305-2 2v10c0 1.104569.8954305 2 2 2h10c1.104569 0 2-.895431 2-2v-10c0-1.1045695-.895431-2-2-2zm6 0 3 3-3 3v-2h-5v-2h5zm-3.65625 5.6289062.5136719.8574219 2.1425781 3.5703129 2.1425781-3.5703129.5136719-.8574219 1.714844 1.0292969-.513672.8574219-3.0000001 5c-.3885014.647055-1.3263424.647055-1.7148438 0l-3-5-.5136719-.8574219z" fill="#77ce57"/><path d="m23 0v2h-5v2h5v2l3-3zm-3.65625 5.6289062-1.714844 1.0292969.513672.8574219 3 5c.388501.647056 1.326343.647056 1.714844 0l3-5 .513672-.8574219-1.714844-1.0292969-.513672.8574219-2.142578 3.5703129-2.142578-3.5703129z" fill-rule="evenodd"/></svg>
diff --git a/editor/icons/VisualShaderNodeVectorCompose.svg b/editor/icons/VisualShaderNodeVectorCompose.svg
index 8e12ab2ff6..2c4c1fb8b9 100644
--- a/editor/icons/VisualShaderNodeVectorCompose.svg
+++ b/editor/icons/VisualShaderNodeVectorCompose.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="1" x2="13.014242" y1="7" y2="7"><stop offset="0" stop-color="#cf68ea"/><stop offset="1" stop-color="#b8ea68"/></linearGradient><path d="m1.9902344.99023438a1.0001 1.0001 0 0 0 -.6972656 1.71679682l3.2929687 3.2929688h-2.5859375a1.0001 1.0001 0 1 0 0 2h2.5859375l-3.2929687 3.292969a1.0001 1.0001 0 1 0 1.4140624 1.414062l4.7070313-4.707031h4.5859375a1.0001 1.0001 0 1 0 0-2h-4.5859375l-4.7070313-4.7070312a1.0001 1.0001 0 0 0 -.7167968-.30273442z" fill="url(#a)" fill-rule="evenodd"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="1" x2="13.014242" y1="7" y2="7"><stop offset="0" stop-color="#cf68ea"/><stop offset="1" stop-color="#77ce57"/></linearGradient><path d="m1.9902344.99023438a1.0001 1.0001 0 0 0 -.6972656 1.71679682l3.2929687 3.2929688h-2.5859375a1.0001 1.0001 0 1 0 0 2h2.5859375l-3.2929687 3.292969a1.0001 1.0001 0 1 0 1.4140624 1.414062l4.7070313-4.707031h4.5859375a1.0001 1.0001 0 1 0 0-2h-4.5859375l-4.7070313-4.7070312a1.0001 1.0001 0 0 0 -.7167968-.30273442z" fill="url(#a)" fill-rule="evenodd"/></svg>
diff --git a/editor/icons/VisualShaderNodeVectorDecompose.svg b/editor/icons/VisualShaderNodeVectorDecompose.svg
index 4bd2dc2138..5fb8661300 100644
--- a/editor/icons/VisualShaderNodeVectorDecompose.svg
+++ b/editor/icons/VisualShaderNodeVectorDecompose.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(-1 0 0 1 13.999754 1038.3622)" gradientUnits="userSpaceOnUse" x1="1" x2="13.014242" y1="7" y2="7"><stop offset="0" stop-color="#cf68ea"/><stop offset="1" stop-color="#b8ea68"/></linearGradient><path d="m12.00952 1039.3524a1.0001 1.0001 0 0 1 .697265 1.7168l-3.2929685 3.293h2.5859375a1.0001 1.0001 0 1 1 0 2h-2.5859375l3.2929685 3.293a1.0001 1.0001 0 1 1 -1.414062 1.414l-4.7070315-4.707h-4.5859375a1.0001 1.0001 0 1 1 0-2h4.5859375l4.7070315-4.707a1.0001 1.0001 0 0 1 .716797-.3028z" fill="url(#a)" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(-1 0 0 1 13.999754 1038.3622)" gradientUnits="userSpaceOnUse" x1="1" x2="13.014242" y1="7" y2="7"><stop offset="0" stop-color="#cf68ea"/><stop offset="1" stop-color="#77ce57"/></linearGradient><path d="m12.00952 1039.3524a1.0001 1.0001 0 0 1 .697265 1.7168l-3.2929685 3.293h2.5859375a1.0001 1.0001 0 1 1 0 2h-2.5859375l3.2929685 3.293a1.0001 1.0001 0 1 1 -1.414062 1.414l-4.7070315-4.707h-4.5859375a1.0001 1.0001 0 1 1 0-2h4.5859375l4.7070315-4.707a1.0001 1.0001 0 0 1 .716797-.3028z" fill="url(#a)" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
diff --git a/editor/icons/VisualShaderNodeVectorDistance.svg b/editor/icons/VisualShaderNodeVectorDistance.svg
index 74a46047bf..b7185f25fb 100644
--- a/editor/icons/VisualShaderNodeVectorDistance.svg
+++ b/editor/icons/VisualShaderNodeVectorDistance.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.3622 10-10" fill="none" stroke="#b8ea68" stroke-linecap="round" stroke-width="2" transform="translate(0 -1038.3622)"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.3622 10-10" fill="none" stroke="#77ce57" stroke-linecap="round" stroke-width="2" transform="translate(0 -1038.3622)"/></svg>
diff --git a/editor/icons/VisualShaderNodeVectorFunc.svg b/editor/icons/VisualShaderNodeVectorFunc.svg
index dcd4cee3e4..e452bc3d49 100644
--- a/editor/icons/VisualShaderNodeVectorFunc.svg
+++ b/editor/icons/VisualShaderNodeVectorFunc.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g fill="#b8ea68" transform="translate(0 -1038.3622)"><path d="m6 1042.3622h2v4.999982h-2z"/><path d="m9.0703125 1a3 3 0 0 0 -1.5703125.4023438 3 3 0 0 0 -1.5 2.5976562h2a1 1 0 0 1 1-1 1 1 0 0 1 1 1h2a3 3 0 0 0 -1.5-2.5976562 3 3 0 0 0 -1.4296875-.4023438z" transform="translate(0 1038.3622)"/><path d="m10 1042.3622h2v1.000017h-2z"/><path d="m2 10a3 3 0 0 0 1.5 2.597656 3 3 0 0 0 3 0 3 3 0 0 0 1.5-2.597656h-2a1 1 0 0 1 -1 1 1 1 0 0 1 -1-1z" transform="translate(0 1038.3622)"/><path d="m6-1048.3622h2v1.000017h-2z" transform="scale(1 -1)"/><path d="m4 1044.3622h6v2h-6z"/></g></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><g fill="#77ce57" transform="translate(0 -1038.3622)"><path d="m6 1042.3622h2v4.999982h-2z"/><path d="m9.0703125 1a3 3 0 0 0 -1.5703125.4023438 3 3 0 0 0 -1.5 2.5976562h2a1 1 0 0 1 1-1 1 1 0 0 1 1 1h2a3 3 0 0 0 -1.5-2.5976562 3 3 0 0 0 -1.4296875-.4023438z" transform="translate(0 1038.3622)"/><path d="m10 1042.3622h2v1.000017h-2z"/><path d="m2 10a3 3 0 0 0 1.5 2.597656 3 3 0 0 0 3 0 3 3 0 0 0 1.5-2.597656h-2a1 1 0 0 1 -1 1 1 1 0 0 1 -1-1z" transform="translate(0 1038.3622)"/><path d="m6-1048.3622h2v1.000017h-2z" transform="scale(1 -1)"/><path d="m4 1044.3622h6v2h-6z"/></g></svg>
diff --git a/editor/icons/VisualShaderNodeVectorLen.svg b/editor/icons/VisualShaderNodeVectorLen.svg
index 71faffdc3f..dce4890927 100644
--- a/editor/icons/VisualShaderNodeVectorLen.svg
+++ b/editor/icons/VisualShaderNodeVectorLen.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m8 1038.3614v2h-5v2h5v2l3-3zm-3.65625 5.6289-1.714844 1.0293.513672.8574 3 5c.388501.647 1.326343.647 1.714844 0l3-5 .513672-.8574-1.714844-1.0293-.513672.8574-2.142578 3.5703-2.142578-3.5703z" fill="#b8ea68" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m8 1038.3614v2h-5v2h5v2l3-3zm-3.65625 5.6289-1.714844 1.0293.513672.8574 3 5c.388501.647 1.326343.647 1.714844 0l3-5 .513672-.8574-1.714844-1.0293-.513672.8574-2.142578 3.5703-2.142578-3.5703z" fill="#77ce57" fill-rule="evenodd" transform="translate(0 -1038.3622)"/></svg>
diff --git a/editor/icons/WorldBoundaryShape2D.svg b/editor/icons/WorldBoundaryShape2D.svg
index f1dbe97c6f..70defbebd8 100644
--- a/editor/icons/WorldBoundaryShape2D.svg
+++ b/editor/icons/WorldBoundaryShape2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke="#68b6ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1036.4)"><path d="m1 1037.4 14 14" fill="#68b6ff" fill-rule="evenodd" stroke-opacity=".39216"/><g fill="none"><path d="m3 1039.4 10 10" stroke-opacity=".58824"/><path d="m5 1041.4 6 6"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke="#5fb2ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1036.4)"><path d="m1 1037.4 14 14" fill="#5fb2ff" fill-rule="evenodd" stroke-opacity=".39216"/><g fill="none"><path d="m3 1039.4 10 10" stroke-opacity=".58824"/><path d="m5 1041.4 6 6"/></g></g></svg>
diff --git a/editor/icons/editor_icons_builders.py b/editor/icons/editor_icons_builders.py
index d7145abe50..fb9a57c429 100644
--- a/editor/icons/editor_icons_builders.py
+++ b/editor/icons/editor_icons_builders.py
@@ -8,7 +8,7 @@ import os
from io import StringIO
from platform_methods import subprocess_main
-
+# See also `scene/resources/default_theme/default_theme_icons_builders.py`.
def make_editor_icons_action(target, source, env):
dst = target[0]
diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp
index 2cc534d96d..b40a810763 100644
--- a/editor/import/collada.cpp
+++ b/editor/import/collada.cpp
@@ -411,8 +411,9 @@ Vector<String> Collada::_read_string_array(XMLParser &parser) {
}
Transform3D Collada::_read_transform(XMLParser &parser) {
- if (parser.is_empty())
+ if (parser.is_empty()) {
return Transform3D();
+ }
Vector<String> array;
while (parser.read() == OK) {
diff --git a/editor/import/dynamicfont_import_settings.cpp b/editor/import/dynamicfont_import_settings.cpp
index 3151496bec..81b98c1d45 100644
--- a/editor/import/dynamicfont_import_settings.cpp
+++ b/editor/import/dynamicfont_import_settings.cpp
@@ -427,6 +427,7 @@ void DynamicFontImportSettings::_add_glyph_range_item(int32_t p_start, int32_t p
for (int i = 0; i < pages; i++) {
TreeItem *item = glyph_tree->create_item(glyph_root);
ERR_FAIL_NULL(item);
+ item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
item->set_text(0, _pad_zeros(String::num_int64(start, 16)) + " - " + _pad_zeros(String::num_int64(start + page_size, 16)));
item->set_text(1, p_name);
item->set_metadata(0, Vector2i(start, start + page_size));
@@ -435,6 +436,7 @@ void DynamicFontImportSettings::_add_glyph_range_item(int32_t p_start, int32_t p
if (remain > 0) {
TreeItem *item = glyph_tree->create_item(glyph_root);
ERR_FAIL_NULL(item);
+ item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
item->set_text(0, _pad_zeros(String::num_int64(start, 16)) + " - " + _pad_zeros(String::num_int64(p_end, 16)));
item->set_text(1, p_name);
item->set_metadata(0, Vector2i(start, p_end));
@@ -442,398 +444,6 @@ void DynamicFontImportSettings::_add_glyph_range_item(int32_t p_start, int32_t p
}
/*************************************************************************/
-/* Languages and scripts */
-/*************************************************************************/
-
-struct CodeInfo {
- String name;
- String code;
-};
-
-static CodeInfo langs[] = {
- { U"Custom", U"xx" },
- { U"-", U"-" },
- { U"Abkhazian", U"ab" },
- { U"Afar", U"aa" },
- { U"Afrikaans", U"af" },
- { U"Akan", U"ak" },
- { U"Albanian", U"sq" },
- { U"Amharic", U"am" },
- { U"Arabic", U"ar" },
- { U"Aragonese", U"an" },
- { U"Armenian", U"hy" },
- { U"Assamese", U"as" },
- { U"Avaric", U"av" },
- { U"Avestan", U"ae" },
- { U"Aymara", U"ay" },
- { U"Azerbaijani", U"az" },
- { U"Bambara", U"bm" },
- { U"Bashkir", U"ba" },
- { U"Basque", U"eu" },
- { U"Belarusian", U"be" },
- { U"Bengali", U"bn" },
- { U"Bihari", U"bh" },
- { U"Bislama", U"bi" },
- { U"Bosnian", U"bs" },
- { U"Breton", U"br" },
- { U"Bulgarian", U"bg" },
- { U"Burmese", U"my" },
- { U"Catalan", U"ca" },
- { U"Chamorro", U"ch" },
- { U"Chechen", U"ce" },
- { U"Chichewa", U"ny" },
- { U"Chinese", U"zh" },
- { U"Chuvash", U"cv" },
- { U"Cornish", U"kw" },
- { U"Corsican", U"co" },
- { U"Cree", U"cr" },
- { U"Croatian", U"hr" },
- { U"Czech", U"cs" },
- { U"Danish", U"da" },
- { U"Divehi", U"dv" },
- { U"Dutch", U"nl" },
- { U"Dzongkha", U"dz" },
- { U"English", U"en" },
- { U"Esperanto", U"eo" },
- { U"Estonian", U"et" },
- { U"Ewe", U"ee" },
- { U"Faroese", U"fo" },
- { U"Fijian", U"fj" },
- { U"Finnish", U"fi" },
- { U"French", U"fr" },
- { U"Fulah", U"ff" },
- { U"Galician", U"gl" },
- { U"Georgian", U"ka" },
- { U"German", U"de" },
- { U"Greek", U"el" },
- { U"Guarani", U"gn" },
- { U"Gujarati", U"gu" },
- { U"Haitian", U"ht" },
- { U"Hausa", U"ha" },
- { U"Hebrew", U"he" },
- { U"Herero", U"hz" },
- { U"Hindi", U"hi" },
- { U"Hiri Motu", U"ho" },
- { U"Hungarian", U"hu" },
- { U"Interlingua", U"ia" },
- { U"Indonesian", U"id" },
- { U"Interlingue", U"ie" },
- { U"Irish", U"ga" },
- { U"Igbo", U"ig" },
- { U"Inupiaq", U"ik" },
- { U"Ido", U"io" },
- { U"Icelandic", U"is" },
- { U"Italian", U"it" },
- { U"Inuktitut", U"iu" },
- { U"Japanese", U"ja" },
- { U"Javanese", U"jv" },
- { U"Kalaallisut", U"kl" },
- { U"Kannada", U"kn" },
- { U"Kanuri", U"kr" },
- { U"Kashmiri", U"ks" },
- { U"Kazakh", U"kk" },
- { U"Central Khmer", U"km" },
- { U"Kikuyu", U"ki" },
- { U"Kinyarwanda", U"rw" },
- { U"Kirghiz", U"ky" },
- { U"Komi", U"kv" },
- { U"Kongo", U"kg" },
- { U"Korean", U"ko" },
- { U"Kurdish", U"ku" },
- { U"Kuanyama", U"kj" },
- { U"Latin", U"la" },
- { U"Luxembourgish", U"lb" },
- { U"Ganda", U"lg" },
- { U"Limburgan", U"li" },
- { U"Lingala", U"ln" },
- { U"Lao", U"lo" },
- { U"Lithuanian", U"lt" },
- { U"Luba-Katanga", U"lu" },
- { U"Latvian", U"lv" },
- { U"Man", U"gv" },
- { U"Macedonian", U"mk" },
- { U"Malagasy", U"mg" },
- { U"Malay", U"ms" },
- { U"Malayalam", U"ml" },
- { U"Maltese", U"mt" },
- { U"Maori", U"mi" },
- { U"Marathi", U"mr" },
- { U"Marshallese", U"mh" },
- { U"Mongolian", U"mn" },
- { U"Nauru", U"na" },
- { U"Navajo", U"nv" },
- { U"North Ndebele", U"nd" },
- { U"Nepali", U"ne" },
- { U"Ndonga", U"ng" },
- { U"Norwegian Bokmål", U"nb" },
- { U"Norwegian Nynorsk", U"nn" },
- { U"Norwegian", U"no" },
- { U"Sichuan Yi, Nuosu", U"ii" },
- { U"South Ndebele", U"nr" },
- { U"Occitan", U"oc" },
- { U"Ojibwa", U"oj" },
- { U"Church Slavic", U"cu" },
- { U"Oromo", U"om" },
- { U"Oriya", U"or" },
- { U"Ossetian", U"os" },
- { U"Punjabi", U"pa" },
- { U"Pali", U"pi" },
- { U"Persian", U"fa" },
- { U"Polish", U"pl" },
- { U"Pashto", U"ps" },
- { U"Portuguese", U"pt" },
- { U"Quechua", U"qu" },
- { U"Romansh", U"rm" },
- { U"Rundi", U"rn" },
- { U"Romanian", U"ro" },
- { U"Russian", U"ru" },
- { U"Sanskrit", U"sa" },
- { U"Sardinian", U"sc" },
- { U"Sindhi", U"sd" },
- { U"Northern Sami", U"se" },
- { U"Samoan", U"sm" },
- { U"Sango", U"sg" },
- { U"Serbian", U"sr" },
- { U"Gaelic", U"gd" },
- { U"Shona", U"sn" },
- { U"Sinhala", U"si" },
- { U"Slovak", U"sk" },
- { U"Slovenian", U"sl" },
- { U"Somali", U"so" },
- { U"Southern Sotho", U"st" },
- { U"Spanish", U"es" },
- { U"Sundanese", U"su" },
- { U"Swahili", U"sw" },
- { U"Swati", U"ss" },
- { U"Swedish", U"sv" },
- { U"Tamil", U"ta" },
- { U"Telugu", U"te" },
- { U"Tajik", U"tg" },
- { U"Thai", U"th" },
- { U"Tigrinya", U"ti" },
- { U"Tibetan", U"bo" },
- { U"Turkmen", U"tk" },
- { U"Tagalog", U"tl" },
- { U"Tswana", U"tn" },
- { U"Tonga", U"to" },
- { U"Turkish", U"tr" },
- { U"Tsonga", U"ts" },
- { U"Tatar", U"tt" },
- { U"Twi", U"tw" },
- { U"Tahitian", U"ty" },
- { U"Uighur", U"ug" },
- { U"Ukrainian", U"uk" },
- { U"Urdu", U"ur" },
- { U"Uzbek", U"uz" },
- { U"Venda", U"ve" },
- { U"Vietnamese", U"vi" },
- { U"Volapük", U"vo" },
- { U"Walloon", U"wa" },
- { U"Welsh", U"cy" },
- { U"Wolof", U"wo" },
- { U"Western Frisian", U"fy" },
- { U"Xhosa", U"xh" },
- { U"Yiddish", U"yi" },
- { U"Yoruba", U"yo" },
- { U"Zhuang", U"za" },
- { U"Zulu", U"zu" },
- { String(), String() }
-};
-
-static CodeInfo scripts[] = {
- { U"Custom", U"Qaaa" },
- { U"-", U"-" },
- { U"Adlam", U"Adlm" },
- { U"Afaka", U"Afak" },
- { U"Caucasian Albanian", U"Aghb" },
- { U"Ahom", U"Ahom" },
- { U"Arabic", U"Arab" },
- { U"Imperial Aramaic", U"Armi" },
- { U"Armenian", U"Armn" },
- { U"Avestan", U"Avst" },
- { U"Balinese", U"Bali" },
- { U"Bamum", U"Bamu" },
- { U"Bassa Vah", U"Bass" },
- { U"Batak", U"Batk" },
- { U"Bengali", U"Beng" },
- { U"Bhaiksuki", U"Bhks" },
- { U"Blissymbols", U"Blis" },
- { U"Bopomofo", U"Bopo" },
- { U"Brahmi", U"Brah" },
- { U"Braille", U"Brai" },
- { U"Buginese", U"Bugi" },
- { U"Buhid", U"Buhd" },
- { U"Chakma", U"Cakm" },
- { U"Unified Canadian Aboriginal", U"Cans" },
- { U"Carian", U"Cari" },
- { U"Cham", U"Cham" },
- { U"Cherokee", U"Cher" },
- { U"Chorasmian", U"Chrs" },
- { U"Cirth", U"Cirt" },
- { U"Coptic", U"Copt" },
- { U"Cypro-Minoan", U"Cpmn" },
- { U"Cypriot", U"Cprt" },
- { U"Cyrillic", U"Cyrl" },
- { U"Devanagari", U"Deva" },
- { U"Dives Akuru", U"Diak" },
- { U"Dogra", U"Dogr" },
- { U"Deseret", U"Dsrt" },
- { U"Duployan", U"Dupl" },
- { U"Egyptian demotic", U"Egyd" },
- { U"Egyptian hieratic", U"Egyh" },
- { U"Egyptian hieroglyphs", U"Egyp" },
- { U"Elbasan", U"Elba" },
- { U"Elymaic", U"Elym" },
- { U"Ethiopic", U"Ethi" },
- { U"Khutsuri", U"Geok" },
- { U"Georgian", U"Geor" },
- { U"Glagolitic", U"Glag" },
- { U"Gunjala Gondi", U"Gong" },
- { U"Masaram Gondi", U"Gonm" },
- { U"Gothic", U"Goth" },
- { U"Grantha", U"Gran" },
- { U"Greek", U"Grek" },
- { U"Gujarati", U"Gujr" },
- { U"Gurmukhi", U"Guru" },
- { U"Hangul", U"Hang" },
- { U"Han", U"Hani" },
- { U"Hanunoo", U"Hano" },
- { U"Hatran", U"Hatr" },
- { U"Hebrew", U"Hebr" },
- { U"Hiragana", U"Hira" },
- { U"Anatolian Hieroglyphs", U"Hluw" },
- { U"Pahawh Hmong", U"Hmng" },
- { U"Nyiakeng Puachue Hmong", U"Hmnp" },
- { U"Old Hungarian", U"Hung" },
- { U"Indus", U"Inds" },
- { U"Old Italic", U"Ital" },
- { U"Javanese", U"Java" },
- { U"Jurchen", U"Jurc" },
- { U"Kayah Li", U"Kali" },
- { U"Katakana", U"Kana" },
- { U"Kharoshthi", U"Khar" },
- { U"Khmer", U"Khmr" },
- { U"Khojki", U"Khoj" },
- { U"Khitan large script", U"Kitl" },
- { U"Khitan small script", U"Kits" },
- { U"Kannada", U"Knda" },
- { U"Kpelle", U"Kpel" },
- { U"Kaithi", U"Kthi" },
- { U"Tai Tham", U"Lana" },
- { U"Lao", U"Laoo" },
- { U"Latin", U"Latn" },
- { U"Leke", U"Leke" },
- { U"Lepcha", U"Lepc" },
- { U"Limbu", U"Limb" },
- { U"Linear A", U"Lina" },
- { U"Linear B", U"Linb" },
- { U"Lisu", U"Lisu" },
- { U"Loma", U"Loma" },
- { U"Lycian", U"Lyci" },
- { U"Lydian", U"Lydi" },
- { U"Mahajani", U"Mahj" },
- { U"Makasar", U"Maka" },
- { U"Mandaic", U"Mand" },
- { U"Manichaean", U"Mani" },
- { U"Marchen", U"Marc" },
- { U"Mayan Hieroglyphs", U"Maya" },
- { U"Medefaidrin", U"Medf" },
- { U"Mende Kikakui", U"Mend" },
- { U"Meroitic Cursive", U"Merc" },
- { U"Meroitic Hieroglyphs", U"Mero" },
- { U"Malayalam", U"Mlym" },
- { U"Modi", U"Modi" },
- { U"Mongolian", U"Mong" },
- { U"Moon", U"Moon" },
- { U"Mro", U"Mroo" },
- { U"Meitei Mayek", U"Mtei" },
- { U"Multani", U"Mult" },
- { U"Myanmar (Burmese)", U"Mymr" },
- { U"Nandinagari", U"Nand" },
- { U"Old North Arabian", U"Narb" },
- { U"Nabataean", U"Nbat" },
- { U"Newa", U"Newa" },
- { U"Naxi Dongba", U"Nkdb" },
- { U"Nakhi Geba", U"Nkgb" },
- { U"N’Ko", U"Nkoo" },
- { U"Nüshu", U"Nshu" },
- { U"Ogham", U"Ogam" },
- { U"Ol Chiki", U"Olck" },
- { U"Old Turkic", U"Orkh" },
- { U"Oriya", U"Orya" },
- { U"Osage", U"Osge" },
- { U"Osmanya", U"Osma" },
- { U"Old Uyghur", U"Ougr" },
- { U"Palmyrene", U"Palm" },
- { U"Pau Cin Hau", U"Pauc" },
- { U"Proto-Cuneiform", U"Pcun" },
- { U"Proto-Elamite", U"Pelm" },
- { U"Old Permic", U"Perm" },
- { U"Phags-pa", U"Phag" },
- { U"Inscriptional Pahlavi", U"Phli" },
- { U"Psalter Pahlavi", U"Phlp" },
- { U"Book Pahlavi", U"Phlv" },
- { U"Phoenician", U"Phnx" },
- { U"Klingon", U"Piqd" },
- { U"Miao", U"Plrd" },
- { U"Inscriptional Parthian", U"Prti" },
- { U"Proto-Sinaitic", U"Psin" },
- { U"Ranjana", U"Ranj" },
- { U"Rejang", U"Rjng" },
- { U"Hanifi Rohingya", U"Rohg" },
- { U"Rongorongo", U"Roro" },
- { U"Runic", U"Runr" },
- { U"Samaritan", U"Samr" },
- { U"Sarati", U"Sara" },
- { U"Old South Arabian", U"Sarb" },
- { U"Saurashtra", U"Saur" },
- { U"SignWriting", U"Sgnw" },
- { U"Shavian", U"Shaw" },
- { U"Sharada", U"Shrd" },
- { U"Shuishu", U"Shui" },
- { U"Siddham", U"Sidd" },
- { U"Khudawadi", U"Sind" },
- { U"Sinhala", U"Sinh" },
- { U"Sogdian", U"Sogd" },
- { U"Old Sogdian", U"Sogo" },
- { U"Sora Sompeng", U"Sora" },
- { U"Soyombo", U"Soyo" },
- { U"Sundanese", U"Sund" },
- { U"Syloti Nagri", U"Sylo" },
- { U"Syriac", U"Syrc" },
- { U"Tagbanwa", U"Tagb" },
- { U"Takri", U"Takr" },
- { U"Tai Le", U"Tale" },
- { U"New Tai Lue", U"Talu" },
- { U"Tamil", U"Taml" },
- { U"Tangut", U"Tang" },
- { U"Tai Viet", U"Tavt" },
- { U"Telugu", U"Telu" },
- { U"Tengwar", U"Teng" },
- { U"Tifinagh", U"Tfng" },
- { U"Tagalog", U"Tglg" },
- { U"Thaana", U"Thaa" },
- { U"Thai", U"Thai" },
- { U"Tibetan", U"Tibt" },
- { U"Tirhuta", U"Tirh" },
- { U"Tangsa", U"Tnsa" },
- { U"Toto", U"Toto" },
- { U"Ugaritic", U"Ugar" },
- { U"Vai", U"Vaii" },
- { U"Visible Speech", U"Visp" },
- { U"Vithkuqi", U"Vith" },
- { U"Warang Citi", U"Wara" },
- { U"Wancho", U"Wcho" },
- { U"Woleai", U"Wole" },
- { U"Old Persian", U"Xpeo" },
- { U"Cuneiform", U"Xsux" },
- { U"Yezidi", U"Yezi" },
- { U"Yi", U"Yiii" },
- { U"Zanabazar Square", U"Zanb" },
- { String(), String() }
-};
-
-/*************************************************************************/
/* Page 1 callbacks: Rendering Options */
/*************************************************************************/
@@ -1048,6 +658,30 @@ void DynamicFontImportSettings::_glyph_selected() {
}
}
label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+
+ item = glyph_tree->get_selected();
+ ERR_FAIL_NULL(item);
+ Vector2i range = item->get_metadata(0);
+
+ int total_chars = range.y - range.x;
+ int selected_count = 0;
+ for (int i = range.x; i < range.y; i++) {
+ if (!font_main->has_char(i)) {
+ total_chars--;
+ }
+
+ if (selected_chars.has(i)) {
+ selected_count++;
+ }
+ }
+
+ if (selected_count == total_chars) {
+ item->set_checked(0, true);
+ } else if (selected_count > 0) {
+ item->set_indeterminate(0, true);
+ } else {
+ item->set_checked(0, false);
+ }
}
void DynamicFontImportSettings::_range_edited() {
@@ -1152,6 +786,10 @@ void DynamicFontImportSettings::_range_update(int32_t p_start, int32_t p_end) {
}
}
_edit_range(p_start, p_end);
+
+ TreeItem *item = glyph_tree->get_selected();
+ ERR_FAIL_NULL(item);
+ item->set_checked(0, !all_selected);
}
/*************************************************************************/
@@ -1159,19 +797,17 @@ void DynamicFontImportSettings::_range_update(int32_t p_start, int32_t p_end) {
/*************************************************************************/
void DynamicFontImportSettings::_lang_add() {
- menu_langs->set_position(lang_list->get_screen_position() + lang_list->get_local_mouse_position());
- menu_langs->reset_size();
- menu_langs->popup();
+ locale_select->popup_locale_dialog();
}
-void DynamicFontImportSettings::_lang_add_item(int p_option) {
+void DynamicFontImportSettings::_lang_add_item(const String &p_locale) {
TreeItem *lang_item = lang_list->create_item(lang_list_root);
ERR_FAIL_NULL(lang_item);
lang_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
lang_item->set_editable(0, true);
lang_item->set_checked(0, false);
- lang_item->set_text(1, langs[p_option].code);
+ lang_item->set_text(1, p_locale);
lang_item->set_editable(1, true);
lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
lang_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
@@ -1230,7 +866,7 @@ void DynamicFontImportSettings::_script_add_item(int p_option) {
script_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
script_item->set_editable(0, true);
script_item->set_checked(0, false);
- script_item->set_text(1, scripts[p_option].code);
+ script_item->set_text(1, script_codes[p_option]);
script_item->set_editable(1, true);
script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
script_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
@@ -1688,26 +1324,15 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
// Popup menus
- menu_langs = memnew(PopupMenu);
- menu_langs->set_name("Language");
- for (int i = 0; !langs[i].name.is_empty(); i++) {
- if (langs[i].name == "-") {
- menu_langs->add_separator();
- } else {
- menu_langs->add_item(langs[i].name + " (" + langs[i].code + ")", i);
- }
- }
- add_child(menu_langs);
- menu_langs->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_lang_add_item));
+ locale_select = memnew(EditorLocaleDialog);
+ locale_select->connect("locale_selected", callable_mp(this, &DynamicFontImportSettings::_lang_add_item));
+ add_child(locale_select);
menu_scripts = memnew(PopupMenu);
menu_scripts->set_name("Script");
- for (int i = 0; !scripts[i].name.is_empty(); i++) {
- if (scripts[i].name == "-") {
- menu_scripts->add_separator();
- } else {
- menu_scripts->add_item(scripts[i].name + " (" + scripts[i].code + ")", i);
- }
+ script_codes = TranslationServer::get_singleton()->get_all_scripts();
+ for (int i = 0; i < script_codes.size(); i++) {
+ menu_scripts->add_item(TranslationServer::get_singleton()->get_script_name(script_codes[i]) + " (" + script_codes[i] + ")", i);
}
add_child(menu_scripts);
menu_scripts->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_script_add_item));
diff --git a/editor/import/dynamicfont_import_settings.h b/editor/import/dynamicfont_import_settings.h
index 89665ae476..5d37f58b9b 100644
--- a/editor/import/dynamicfont_import_settings.h
+++ b/editor/import/dynamicfont_import_settings.h
@@ -33,6 +33,7 @@
#include "editor/editor_file_dialog.h"
#include "editor/editor_inspector.h"
+#include "editor/editor_locale_dialog.h"
#include "editor/import/resource_importer_dynamicfont.h"
@@ -67,6 +68,9 @@ class DynamicFontImportSettings : public ConfirmationDialog {
List<ResourceImporter::ImportOption> options_variations;
List<ResourceImporter::ImportOption> options_general;
+ EditorLocaleDialog *locale_select;
+ Vector<String> script_codes;
+
// Root layout
Label *label_warn = nullptr;
TabContainer *main_pages = nullptr;
@@ -122,7 +126,6 @@ class DynamicFontImportSettings : public ConfirmationDialog {
Button *add_script = nullptr;
Button *add_ot = nullptr;
- PopupMenu *menu_langs = nullptr;
PopupMenu *menu_scripts = nullptr;
PopupMenu *menu_ot = nullptr;
PopupMenu *menu_ot_ss = nullptr;
@@ -142,7 +145,7 @@ class DynamicFontImportSettings : public ConfirmationDialog {
Label *label_ot = nullptr;
void _lang_add();
- void _lang_add_item(int p_option);
+ void _lang_add_item(const String &p_locale);
void _lang_remove(Object *p_item, int p_column, int p_id);
void _script_add();
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 22b2bd1ed4..c1ae5be0bb 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -471,7 +471,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
bool local_xform_mirror = p_local_xform.basis.determinant() < 0;
if (p_morph_data) {
- //add morphie target
+ //add morph target
ERR_FAIL_COND_V(!p_morph_data->targets.has("MORPH_TARGET"), ERR_INVALID_DATA);
String mt = p_morph_data->targets["MORPH_TARGET"];
ERR_FAIL_COND_V(!p_morph_data->sources.has(mt), ERR_INVALID_DATA);
diff --git a/editor/import/resource_importer_bmfont.cpp b/editor/import/resource_importer_bmfont.cpp
index fa560e8eb1..8a655fbc0c 100644
--- a/editor/import/resource_importer_bmfont.cpp
+++ b/editor/import/resource_importer_bmfont.cpp
@@ -30,7 +30,6 @@
#include "resource_importer_bmfont.h"
-#include "core/io/image_loader.h"
#include "core/io/resource_saver.h"
String ResourceImporterBMFont::get_importer_name() const {
@@ -64,749 +63,14 @@ void ResourceImporterBMFont::get_import_options(const String &p_path, List<Impor
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
}
-void _convert_packed_8bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_sz) {
- int w = p_source->get_width();
- int h = p_source->get_height();
-
- PackedByteArray imgdata = p_source->get_data();
- const uint8_t *r = imgdata.ptr();
-
- PackedByteArray imgdata_r;
- imgdata_r.resize(w * h * 2);
- uint8_t *wr = imgdata_r.ptrw();
-
- PackedByteArray imgdata_g;
- imgdata_g.resize(w * h * 2);
- uint8_t *wg = imgdata_g.ptrw();
-
- PackedByteArray imgdata_b;
- imgdata_b.resize(w * h * 2);
- uint8_t *wb = imgdata_b.ptrw();
-
- PackedByteArray imgdata_a;
- imgdata_a.resize(w * h * 2);
- uint8_t *wa = imgdata_a.ptrw();
-
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- int ofs_src = (i * w + j) * 4;
- int ofs_dst = (i * w + j) * 2;
- wr[ofs_dst + 0] = 255;
- wr[ofs_dst + 1] = r[ofs_src + 0];
- wg[ofs_dst + 0] = 255;
- wg[ofs_dst + 1] = r[ofs_src + 1];
- wb[ofs_dst + 0] = 255;
- wb[ofs_dst + 1] = r[ofs_src + 2];
- wa[ofs_dst + 0] = 255;
- wa[ofs_dst + 1] = r[ofs_src + 3];
- }
- }
- Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
- r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
- Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
- r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
- Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
- r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
- Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
- r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
-}
-
-void _convert_packed_4bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_sz) {
- int w = p_source->get_width();
- int h = p_source->get_height();
-
- PackedByteArray imgdata = p_source->get_data();
- const uint8_t *r = imgdata.ptr();
-
- PackedByteArray imgdata_r;
- imgdata_r.resize(w * h * 2);
- uint8_t *wr = imgdata_r.ptrw();
-
- PackedByteArray imgdata_g;
- imgdata_g.resize(w * h * 2);
- uint8_t *wg = imgdata_g.ptrw();
-
- PackedByteArray imgdata_b;
- imgdata_b.resize(w * h * 2);
- uint8_t *wb = imgdata_b.ptrw();
-
- PackedByteArray imgdata_a;
- imgdata_a.resize(w * h * 2);
- uint8_t *wa = imgdata_a.ptrw();
-
- PackedByteArray imgdata_ro;
- imgdata_ro.resize(w * h * 2);
- uint8_t *wro = imgdata_ro.ptrw();
-
- PackedByteArray imgdata_go;
- imgdata_go.resize(w * h * 2);
- uint8_t *wgo = imgdata_go.ptrw();
-
- PackedByteArray imgdata_bo;
- imgdata_bo.resize(w * h * 2);
- uint8_t *wbo = imgdata_bo.ptrw();
-
- PackedByteArray imgdata_ao;
- imgdata_ao.resize(w * h * 2);
- uint8_t *wao = imgdata_ao.ptrw();
-
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- int ofs_src = (i * w + j) * 4;
- int ofs_dst = (i * w + j) * 2;
- wr[ofs_dst + 0] = 255;
- wro[ofs_dst + 0] = 255;
- if (r[ofs_src + 0] > 0x0F) {
- wr[ofs_dst + 1] = (r[ofs_src + 0] - 0x0F) * 2;
- wro[ofs_dst + 1] = 0;
- } else {
- wr[ofs_dst + 1] = 0;
- wro[ofs_dst + 1] = r[ofs_src + 0] * 2;
- }
- wg[ofs_dst + 0] = 255;
- wgo[ofs_dst + 0] = 255;
- if (r[ofs_src + 1] > 0x0F) {
- wg[ofs_dst + 1] = (r[ofs_src + 1] - 0x0F) * 2;
- wgo[ofs_dst + 1] = 0;
- } else {
- wg[ofs_dst + 1] = 0;
- wgo[ofs_dst + 1] = r[ofs_src + 1] * 2;
- }
- wb[ofs_dst + 0] = 255;
- wbo[ofs_dst + 0] = 255;
- if (r[ofs_src + 2] > 0x0F) {
- wb[ofs_dst + 1] = (r[ofs_src + 2] - 0x0F) * 2;
- wbo[ofs_dst + 1] = 0;
- } else {
- wb[ofs_dst + 1] = 0;
- wbo[ofs_dst + 1] = r[ofs_src + 2] * 2;
- }
- wa[ofs_dst + 0] = 255;
- wao[ofs_dst + 0] = 255;
- if (r[ofs_src + 3] > 0x0F) {
- wa[ofs_dst + 1] = (r[ofs_src + 3] - 0x0F) * 2;
- wao[ofs_dst + 1] = 0;
- } else {
- wa[ofs_dst + 1] = 0;
- wao[ofs_dst + 1] = r[ofs_src + 3] * 2;
- }
- }
- }
- Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
- r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
- Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
- r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
- Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
- r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
- Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
- r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
-
- Ref<Image> img_ro = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ro));
- r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 0, img_ro);
- Ref<Image> img_go = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_go));
- r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 1, img_go);
- Ref<Image> img_bo = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_bo));
- r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 2, img_bo);
- Ref<Image> img_ao = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ao));
- r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao);
-}
-
-void _convert_rgba_4bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_sz) {
- int w = p_source->get_width();
- int h = p_source->get_height();
-
- PackedByteArray imgdata = p_source->get_data();
- const uint8_t *r = imgdata.ptr();
-
- PackedByteArray imgdata_g;
- imgdata_g.resize(w * h * 4);
- uint8_t *wg = imgdata_g.ptrw();
-
- PackedByteArray imgdata_o;
- imgdata_o.resize(w * h * 4);
- uint8_t *wo = imgdata_o.ptrw();
-
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- int ofs = (i * w + j) * 4;
-
- if (r[ofs + 0] > 0x7F) {
- wg[ofs + 0] = r[ofs + 0];
- wo[ofs + 0] = 0;
- } else {
- wg[ofs + 0] = 0;
- wo[ofs + 0] = r[ofs + 0] * 2;
- }
- if (r[ofs + 1] > 0x7F) {
- wg[ofs + 1] = r[ofs + 1];
- wo[ofs + 1] = 0;
- } else {
- wg[ofs + 1] = 0;
- wo[ofs + 1] = r[ofs + 1] * 2;
- }
- if (r[ofs + 2] > 0x7F) {
- wg[ofs + 2] = r[ofs + 2];
- wo[ofs + 2] = 0;
- } else {
- wg[ofs + 2] = 0;
- wo[ofs + 2] = r[ofs + 2] * 2;
- }
- if (r[ofs + 3] > 0x7F) {
- wg[ofs + 3] = r[ofs + 3];
- wo[ofs + 3] = 0;
- } else {
- wg[ofs + 3] = 0;
- wo[ofs + 3] = r[ofs + 3] * 2;
- }
- }
- }
- Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_g));
- r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
-
- Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_o));
- r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o);
-}
-
-void _convert_mono_8bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) {
- int w = p_source->get_width();
- int h = p_source->get_height();
-
- PackedByteArray imgdata = p_source->get_data();
- const uint8_t *r = imgdata.ptr();
-
- int size = 4;
- if (p_source->get_format() == Image::FORMAT_L8) {
- size = 1;
- p_ch = 0;
- }
-
- PackedByteArray imgdata_g;
- imgdata_g.resize(w * h * 2);
- uint8_t *wg = imgdata_g.ptrw();
-
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- int ofs_src = (i * w + j) * size;
- int ofs_dst = (i * w + j) * 2;
- wg[ofs_dst + 0] = 255;
- wg[ofs_dst + 1] = r[ofs_src + p_ch];
- }
- }
- Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
- r_font->set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g);
-}
-
-void _convert_mono_4bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) {
- int w = p_source->get_width();
- int h = p_source->get_height();
-
- PackedByteArray imgdata = p_source->get_data();
- const uint8_t *r = imgdata.ptr();
-
- int size = 4;
- if (p_source->get_format() == Image::FORMAT_L8) {
- size = 1;
- p_ch = 0;
- }
-
- PackedByteArray imgdata_g;
- imgdata_g.resize(w * h * 2);
- uint8_t *wg = imgdata_g.ptrw();
-
- PackedByteArray imgdata_o;
- imgdata_o.resize(w * h * 2);
- uint8_t *wo = imgdata_o.ptrw();
-
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- int ofs_src = (i * w + j) * size;
- int ofs_dst = (i * w + j) * 2;
- wg[ofs_dst + 0] = 255;
- wo[ofs_dst + 0] = 255;
- if (r[ofs_src + p_ch] > 0x7F) {
- wg[ofs_dst + 1] = r[ofs_src + p_ch];
- wo[ofs_dst + 1] = 0;
- } else {
- wg[ofs_dst + 1] = 0;
- wo[ofs_dst + 1] = r[ofs_src + p_ch] * 2;
- }
- }
- }
- Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
- r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
-
- Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_o));
- r_font->set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o);
-}
-
Error ResourceImporterBMFont::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
print_verbose("Importing BMFont font from: " + p_source_file);
Ref<FontData> font;
font.instantiate();
- font->set_antialiased(false);
- font->set_multichannel_signed_distance_field(false);
- font->set_force_autohinter(false);
- font->set_hinting(TextServer::HINTING_NONE);
- font->set_oversampling(1.0f);
-
- FileAccessRef f = FileAccess::open(p_source_file, FileAccess::READ);
- if (f == nullptr) {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Cannot open font from file ") + "\"" + p_source_file + "\".");
- }
-
- int base_size = 16;
- int height = 0;
- int ascent = 0;
- int outline = 0;
- uint32_t st_flags = 0;
- String font_name;
-
- bool packed = false;
- uint8_t ch[4] = { 0, 0, 0, 0 }; // RGBA
- int first_gl_ch = -1;
- int first_ol_ch = -1;
- int first_cm_ch = -1;
-
- unsigned char magic[4];
- f->get_buffer((unsigned char *)&magic, 4);
- if (magic[0] == 'B' && magic[1] == 'M' && magic[2] == 'F') {
- // Binary BMFont file.
- ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(TTR("Version %d of BMFont is not supported."), (int)magic[3]));
-
- uint8_t block_type = f->get_8();
- uint32_t block_size = f->get_32();
- while (!f->eof_reached()) {
- uint64_t off = f->get_position();
- switch (block_type) {
- case 1: /* info */ {
- ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, TTR("Invalid BMFont info block size."));
- base_size = f->get_16();
- uint8_t flags = f->get_8();
- ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
- if (flags & (1 << 3)) {
- st_flags |= TextServer::FONT_BOLD;
- }
- if (flags & (1 << 2)) {
- st_flags |= TextServer::FONT_ITALIC;
- }
- f->get_8(); // non-unicode charset, skip
- f->get_16(); // stretch_h, skip
- f->get_8(); // aa, skip
- f->get_32(); // padding, skip
- f->get_16(); // spacing, skip
- outline = f->get_8();
- // font name
- PackedByteArray name_data;
- name_data.resize(block_size - 14);
- f->get_buffer(name_data.ptrw(), block_size - 14);
- font_name = String::utf8((const char *)name_data.ptr(), block_size - 14);
- font->set_fixed_size(base_size);
- } break;
- case 2: /* common */ {
- ERR_FAIL_COND_V_MSG(block_size != 15, ERR_CANT_CREATE, TTR("Invalid BMFont common block size."));
- height = f->get_16();
- ascent = f->get_16();
- f->get_32(); // scale, skip
- f->get_16(); // pages, skip
- uint8_t flags = f->get_8();
- packed = (flags & 0x01);
- ch[3] = f->get_8();
- ch[0] = f->get_8();
- ch[1] = f->get_8();
- ch[2] = f->get_8();
- for (int i = 0; i < 4; i++) {
- if (ch[i] == 0 && first_gl_ch == -1) {
- first_gl_ch = i;
- }
- if (ch[i] == 1 && first_ol_ch == -1) {
- first_ol_ch = i;
- }
- if (ch[i] == 2 && first_cm_ch == -1) {
- first_cm_ch = i;
- }
- }
- } break;
- case 3: /* pages */ {
- int page = 0;
- CharString cs;
- char32_t c = f->get_8();
- while (!f->eof_reached() && f->get_position() <= off + block_size) {
- if (c == '\0') {
- String base_dir = p_source_file.get_base_dir();
- String file = base_dir.plus_file(String::utf8(cs.ptr(), cs.length()));
- if (RenderingServer::get_singleton() != nullptr) {
- Ref<Image> img;
- img.instantiate();
- Error err = ImageLoader::load_image(file, img);
- ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\".");
-
- if (packed) {
- if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
- outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_packed_8bit(font, img, page, base_size);
- } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_packed_4bit(font, img, page, base_size);
- } else {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
- }
- } else {
- if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
- outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- font->set_texture_image(0, Vector2i(base_size, 0), page, img);
- } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_rgba_4bit(font, img, page, base_size);
- } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
- _convert_mono_8bit(font, img, page, first_ol_ch, base_size, 1);
- } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_mono_4bit(font, img, page, first_cm_ch, base_size, 1);
- } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
- outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
- } else {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
- }
- }
- }
- page++;
- cs = "";
- } else {
- cs += c;
- }
- c = f->get_8();
- }
- } break;
- case 4: /* chars */ {
- int char_count = block_size / 20;
- for (int i = 0; i < char_count; i++) {
- Vector2 advance;
- Vector2 size;
- Vector2 offset;
- Rect2 uv_rect;
-
- char32_t idx = f->get_32();
- uv_rect.position.x = (int16_t)f->get_16();
- uv_rect.position.y = (int16_t)f->get_16();
- uv_rect.size.width = (int16_t)f->get_16();
- size.width = uv_rect.size.width;
- uv_rect.size.height = (int16_t)f->get_16();
- size.height = uv_rect.size.height;
- offset.x = (int16_t)f->get_16();
- offset.y = (int16_t)f->get_16() - ascent;
- advance.x = (int16_t)f->get_16();
- if (advance.x < 0) {
- advance.x = size.width + 1;
- }
-
- int texture_idx = f->get_8();
- uint8_t channel = f->get_8();
-
- ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel."));
- int ch_off = 0;
- switch (channel) {
- case 1:
- ch_off = 2;
- break; // B
- case 2:
- ch_off = 1;
- break; // G
- case 4:
- ch_off = 0;
- break; // R
- case 8:
- ch_off = 3;
- break; // A
- default:
- ch_off = 0;
- break;
- }
- font->set_glyph_advance(0, base_size, idx, advance);
- font->set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
- font->set_glyph_size(0, Vector2i(base_size, 0), idx, size);
- font->set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, uv_rect);
- font->set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, texture_idx * (packed ? 4 : 1) + ch_off);
- if (outline > 0) {
- font->set_glyph_offset(0, Vector2i(base_size, 1), idx, offset);
- font->set_glyph_size(0, Vector2i(base_size, 1), idx, size);
- font->set_glyph_uv_rect(0, Vector2i(base_size, 1), idx, uv_rect);
- font->set_glyph_texture_idx(0, Vector2i(base_size, 1), idx, texture_idx * (packed ? 4 : 1) + ch_off);
- }
- }
- } break;
- case 5: /* kerning */ {
- int pair_count = block_size / 10;
- for (int i = 0; i < pair_count; i++) {
- Vector2i kpk;
- kpk.x = f->get_32();
- kpk.y = f->get_32();
- font->set_kerning(0, base_size, kpk, Vector2((int16_t)f->get_16(), 0));
- }
- } break;
- default: {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Invalid BMFont block type."));
- } break;
- }
- f->seek(off + block_size);
- block_type = f->get_8();
- block_size = f->get_32();
- }
-
- } else {
- // Text BMFont file.
- f->seek(0);
- while (true) {
- String line = f->get_line();
-
- int delimiter = line.find(" ");
- String type = line.substr(0, delimiter);
- int pos = delimiter + 1;
- Map<String, String> keys;
-
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
-
- while (pos < line.size()) {
- int eq = line.find("=", pos);
- if (eq == -1) {
- break;
- }
- String key = line.substr(pos, eq - pos);
- int end = -1;
- String value;
- if (line[eq + 1] == '"') {
- end = line.find("\"", eq + 2);
- if (end == -1) {
- break;
- }
- value = line.substr(eq + 2, end - 1 - eq - 1);
- pos = end + 1;
- } else {
- end = line.find(" ", eq + 1);
- if (end == -1) {
- end = line.size();
- }
- value = line.substr(eq + 1, end - eq);
- pos = end;
- }
-
- while (pos < line.size() && line[pos] == ' ') {
- pos++;
- }
-
- keys[key] = value;
- }
-
- if (type == "info") {
- if (keys.has("size")) {
- base_size = keys["size"].to_int();
- font->set_fixed_size(base_size);
- }
- if (keys.has("outline")) {
- outline = keys["outline"].to_int();
- }
- if (keys.has("bold")) {
- if (keys["bold"].to_int()) {
- st_flags |= TextServer::FONT_BOLD;
- }
- }
- if (keys.has("italic")) {
- if (keys["italic"].to_int()) {
- st_flags |= TextServer::FONT_ITALIC;
- }
- }
- if (keys.has("face")) {
- font_name = keys["face"];
- }
- ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
- } else if (type == "common") {
- if (keys.has("lineHeight")) {
- height = keys["lineHeight"].to_int();
- }
- if (keys.has("base")) {
- ascent = keys["base"].to_int();
- }
- if (keys.has("packed")) {
- packed = (keys["packed"].to_int() == 1);
- }
- if (keys.has("alphaChnl")) {
- ch[3] = keys["alphaChnl"].to_int();
- }
- if (keys.has("redChnl")) {
- ch[0] = keys["redChnl"].to_int();
- }
- if (keys.has("greenChnl")) {
- ch[1] = keys["greenChnl"].to_int();
- }
- if (keys.has("blueChnl")) {
- ch[2] = keys["blueChnl"].to_int();
- }
- for (int i = 0; i < 4; i++) {
- if (ch[i] == 0 && first_gl_ch == -1) {
- first_gl_ch = i;
- }
- if (ch[i] == 1 && first_ol_ch == -1) {
- first_ol_ch = i;
- }
- if (ch[i] == 2 && first_cm_ch == -1) {
- first_cm_ch = i;
- }
- }
- } else if (type == "page") {
- int page = 0;
- if (keys.has("id")) {
- page = keys["id"].to_int();
- }
- if (keys.has("file")) {
- String base_dir = p_source_file.get_base_dir();
- String file = base_dir.plus_file(keys["file"]);
- if (RenderingServer::get_singleton() != nullptr) {
- Ref<Image> img;
- img.instantiate();
- Error err = ImageLoader::load_image(file, img);
- ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\".");
- if (packed) {
- if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
- outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_packed_8bit(font, img, page, base_size);
- } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_packed_4bit(font, img, page, base_size);
- } else {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
- }
- } else {
- if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
- outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- font->set_texture_image(0, Vector2i(base_size, 0), page, img);
- } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_rgba_4bit(font, img, page, base_size);
- } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
- _convert_mono_8bit(font, img, page, first_ol_ch, base_size, 1);
- } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_mono_4bit(font, img, page, first_cm_ch, base_size, 1);
- } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
- outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
- _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
- } else {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
- }
- }
- }
- }
- } else if (type == "char") {
- char32_t idx = 0;
- Vector2 advance;
- Vector2 size;
- Vector2 offset;
- Rect2 uv_rect;
- int texture_idx = -1;
- uint8_t channel = 15;
-
- if (keys.has("id")) {
- idx = keys["id"].to_int();
- }
- if (keys.has("x")) {
- uv_rect.position.x = keys["x"].to_int();
- }
- if (keys.has("y")) {
- uv_rect.position.y = keys["y"].to_int();
- }
- if (keys.has("width")) {
- uv_rect.size.width = keys["width"].to_int();
- size.width = keys["width"].to_int();
- }
- if (keys.has("height")) {
- uv_rect.size.height = keys["height"].to_int();
- size.height = keys["height"].to_int();
- }
- if (keys.has("xoffset")) {
- offset.x = keys["xoffset"].to_int();
- }
- if (keys.has("yoffset")) {
- offset.y = keys["yoffset"].to_int() - ascent;
- }
- if (keys.has("page")) {
- texture_idx = keys["page"].to_int();
- }
- if (keys.has("xadvance")) {
- advance.x = keys["xadvance"].to_int();
- }
- if (advance.x < 0) {
- advance.x = size.width + 1;
- }
- if (keys.has("chnl")) {
- channel = keys["chnl"].to_int();
- }
-
- ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel."));
- int ch_off = 0;
- switch (channel) {
- case 1:
- ch_off = 2;
- break; // B
- case 2:
- ch_off = 1;
- break; // G
- case 4:
- ch_off = 0;
- break; // R
- case 8:
- ch_off = 3;
- break; // A
- default:
- ch_off = 0;
- break;
- }
- font->set_glyph_advance(0, base_size, idx, advance);
- font->set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
- font->set_glyph_size(0, Vector2i(base_size, 0), idx, size);
- font->set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, uv_rect);
- font->set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, texture_idx * (packed ? 4 : 1) + ch_off);
- if (outline > 0) {
- font->set_glyph_offset(0, Vector2i(base_size, 1), idx, offset);
- font->set_glyph_size(0, Vector2i(base_size, 1), idx, size);
- font->set_glyph_uv_rect(0, Vector2i(base_size, 1), idx, uv_rect);
- font->set_glyph_texture_idx(0, Vector2i(base_size, 1), idx, texture_idx * (packed ? 4 : 1) + ch_off);
- }
- } else if (type == "kerning") {
- Vector2i kpk;
- if (keys.has("first")) {
- kpk.x = keys["first"].to_int();
- }
- if (keys.has("second")) {
- kpk.y = keys["second"].to_int();
- }
- if (keys.has("amount")) {
- font->set_kerning(0, base_size, kpk, Vector2(keys["amount"].to_int(), 0));
- }
- }
-
- if (f->eof_reached()) {
- break;
- }
- }
- }
- font->set_font_name(font_name);
- font->set_font_style(st_flags);
- font->set_ascent(0, base_size, ascent);
- font->set_descent(0, base_size, height - ascent);
+ Error err = font->load_bitmap_font(p_source_file);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load font to file \"" + p_source_file + "\".");
int flg = ResourceSaver::SaverFlags::FLAG_BUNDLE_RESOURCES | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
if ((bool)p_options["compress"]) {
@@ -814,7 +78,7 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
}
print_verbose("Saving to: " + p_save_path + ".fontdata");
- Error err = ResourceSaver::save(p_save_path + ".fontdata", font, flg);
+ err = ResourceSaver::save(p_save_path + ".fontdata", font, flg);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save font to file \"" + p_save_path + ".res\".");
print_verbose("Done saving to: " + p_save_path + ".fontdata");
return OK;
diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp
index 448b318c64..f0ee14bdcb 100644
--- a/editor/import/resource_importer_csv_translation.cpp
+++ b/editor/import/resource_importer_csv_translation.cpp
@@ -99,8 +99,7 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const
Vector<Ref<Translation>> translations;
for (int i = 1; i < line.size(); i++) {
- String locale = line[i];
- ERR_FAIL_COND_V_MSG(!TranslationServer::is_locale_valid(locale), ERR_PARSE_ERROR, "Error importing CSV translation: '" + locale + "' is not a valid locale.");
+ String locale = TranslationServer::get_singleton()->standardize_locale(line[i]);
locales.push_back(locale);
Ref<Translation> translation;
diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index 5876d6df0b..d63366638e 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -450,12 +450,6 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
formats_imported.push_back("etc2");
}
- if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc")) {
- _save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, lossy, Image::COMPRESS_ETC2, csource, used_channels, mipmaps, true);
- r_platform_variants->push_back("pvrtc");
- formats_imported.push_back("pvrtc");
- }
-
if (!ok_on_pc) {
EditorNode::add_io_error("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correctly on PC.");
}
@@ -481,7 +475,6 @@ const char *ResourceImporterLayeredTexture::compression_formats[] = {
"s3tc",
"etc",
"etc2",
- "pvrtc",
nullptr
};
String ResourceImporterLayeredTexture::get_import_settings_string() const {
@@ -524,7 +517,7 @@ bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_p
String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
bool test = ProjectSettings::get_singleton()->get(setting_path);
if (test) {
- if (formats_imported.find(compression_formats[index]) == -1) {
+ if (!formats_imported.has(compression_formats[index])) {
valid = false;
break;
}
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index e801cd4553..0fefa0f3c4 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -282,7 +282,8 @@ bool ResourceImporterScene::get_option_visibility(const String &p_path, const St
}
}
- if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) < 3) {
+ if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) != 2) {
+ // Only display the lightmap texel size import option when using the Static Lightmaps light baking mode.
return false;
}
@@ -370,16 +371,17 @@ static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r
}
}
-Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map) {
- // children first
+Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, List<Pair<NodePath, Node *>> &r_node_renames) {
+ // Children first.
for (int i = 0; i < p_node->get_child_count(); i++) {
- Node *r = _pre_fix_node(p_node->get_child(i), p_root, collision_map);
+ Node *r = _pre_fix_node(p_node->get_child(i), p_root, collision_map, r_node_renames);
if (!r) {
- i--; //was erased
+ i--; // Was erased.
}
}
String name = p_node->get_name();
+ NodePath original_path = p_root->get_path_to(p_node); // Used to detect renames due to import hints.
bool isroot = p_node == p_root;
@@ -414,14 +416,21 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
}
if (Object::cast_to<AnimationPlayer>(p_node)) {
- //remove animations referencing non-importable nodes
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
+ // Node paths in animation tracks are relative to the following path (this is used to fix node paths below).
+ Node *ap_root = ap->get_node(ap->get_root());
+ NodePath path_prefix = p_root->get_path_to(ap_root);
+
+ bool nodes_were_renamed = r_node_renames.size() != 0;
+
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &E : anims) {
Ref<Animation> anim = ap->get_animation(E);
ERR_CONTINUE(anim.is_null());
+
+ // Remove animation tracks referencing non-importable nodes.
for (int i = 0; i < anim->get_track_count(); i++) {
NodePath path = anim->track_get_path(i);
@@ -435,6 +444,27 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
}
}
+ // Fix node paths in animations, in case nodes were renamed earlier due to import hints.
+ if (nodes_were_renamed) {
+ for (int i = 0; i < anim->get_track_count(); i++) {
+ NodePath path = anim->track_get_path(i);
+ // Convert track path to absolute node path without subnames (some manual work because we are not in the scene tree).
+ Vector<StringName> absolute_path_names = path_prefix.get_names();
+ absolute_path_names.append_array(path.get_names());
+ NodePath absolute_path(absolute_path_names, false);
+ absolute_path.simplify();
+ // Fix paths to renamed nodes.
+ for (const Pair<NodePath, Node *> &F : r_node_renames) {
+ if (F.first == absolute_path) {
+ NodePath new_path(ap_root->get_path_to(F.second).get_names(), path.get_subnames(), false);
+ print_verbose(vformat("Fix: Correcting node path in animation track: %s should be %s", path, new_path));
+ anim->track_set_path(i, new_path);
+ break; // Only one match is possible.
+ }
+ }
+ }
+ }
+
String animname = E;
const int loop_string_count = 3;
static const char *loop_strings[loop_string_count] = { "loop_mode", "loop", "cycle" };
@@ -452,13 +482,22 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
if (isroot) {
return p_node;
}
+
+ String fixed_name;
+ if (_teststr(name, "colonly")) {
+ fixed_name = _fixstr(name, "colonly");
+ } else if (_teststr(name, "convcolonly")) {
+ fixed_name = _fixstr(name, "convcolonly");
+ }
+
+ ERR_FAIL_COND_V(fixed_name.is_empty(), nullptr);
+
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
if (mi) {
Ref<ImporterMesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
Vector<Ref<Shape3D>> shapes;
- String fixed_name;
if (collision_map.has(mesh)) {
shapes = collision_map[mesh];
} else if (_teststr(name, "colonly")) {
@@ -469,14 +508,6 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
collision_map[mesh] = shapes;
}
- if (_teststr(name, "colonly")) {
- fixed_name = _fixstr(name, "colonly");
- } else if (_teststr(name, "convcolonly")) {
- fixed_name = _fixstr(name, "convcolonly");
- }
-
- ERR_FAIL_COND_V(fixed_name.is_empty(), nullptr);
-
if (shapes.size()) {
StaticBody3D *col = memnew(StaticBody3D);
col->set_transform(mi->get_transform());
@@ -492,11 +523,11 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
} else if (p_node->has_meta("empty_draw_type")) {
String empty_draw_type = String(p_node->get_meta("empty_draw_type"));
StaticBody3D *sb = memnew(StaticBody3D);
- sb->set_name(_fixstr(name, "colonly"));
+ sb->set_name(fixed_name);
Object::cast_to<Node3D>(sb)->set_transform(Object::cast_to<Node3D>(p_node)->get_transform());
p_node->replace_by(sb);
memdelete(p_node);
- p_node = nullptr;
+ p_node = sb;
CollisionShape3D *colshape = memnew(CollisionShape3D);
if (empty_draw_type == "CUBE") {
BoxShape3D *boxShape = memnew(BoxShape3D);
@@ -635,6 +666,14 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
}
}
+ if (p_node) {
+ NodePath new_path = p_root->get_path_to(p_node);
+ if (new_path != original_path) {
+ print_verbose(vformat("Fix: Renamed %s to %s", original_path, new_path));
+ r_node_renames.push_back({ original_path, p_node });
+ }
+ }
+
return p_node;
}
@@ -1438,7 +1477,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Dynamic,Static,Static Lightmaps", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 2));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI),Static Lightmaps (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
@@ -1622,7 +1661,7 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
} break;
case LIGHT_BAKE_STATIC:
case LIGHT_BAKE_STATIC_LIGHTMAPS: {
- mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_BAKED);
+ mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_STATIC);
} break;
}
@@ -1738,7 +1777,8 @@ void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, Ani
if (bone_idx == -1) {
continue;
}
- skel->get_bone_pose(bone_idx);
+ // Note that this is using get_bone_pose to update the bone pose cache.
+ _ALLOW_DISCARD_ skel->get_bone_pose(bone_idx);
loc = skel->get_bone_pose_position(bone_idx);
rot = skel->get_bone_pose_rotation(bone_idx);
scale = skel->get_bone_pose_scale(bone_idx);
@@ -1828,8 +1868,8 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file) {
}
Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
-
- _pre_fix_node(scene, scene, collision_map);
+ List<Pair<NodePath, Node *>> node_renames;
+ _pre_fix_node(scene, scene, collision_map, node_renames);
return scene;
}
@@ -1904,8 +1944,9 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
Set<Ref<ImporterMesh>> scanned_meshes;
Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
+ List<Pair<NodePath, Node *>> node_renames;
- _pre_fix_node(scene, scene, collision_map);
+ _pre_fix_node(scene, scene, collision_map, node_renames);
for (int i = 0; i < post_importer_plugins.size(); i++) {
post_importer_plugins.write[i]->pre_process(scene, p_options);
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index 00d095eac1..13b55b5754 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -159,9 +159,9 @@ class ResourceImporterScene : public ResourceImporter {
enum LightBakeMode {
LIGHT_BAKE_DISABLED,
- LIGHT_BAKE_DYNAMIC,
LIGHT_BAKE_STATIC,
- LIGHT_BAKE_STATIC_LIGHTMAPS
+ LIGHT_BAKE_STATIC_LIGHTMAPS,
+ LIGHT_BAKE_DYNAMIC,
};
enum MeshPhysicsMode {
@@ -261,7 +261,7 @@ public:
// Import scenes *after* everything else (such as textures).
virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; }
- Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map);
+ Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, List<Pair<NodePath, Node *>> &r_node_renames);
Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Set<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks);
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 59550a3ee3..69c705ed5a 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -211,14 +211,17 @@ void ResourceImporterTexture::get_import_options(const String &p_path, List<Impo
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), (p_preset == PRESET_3D ? true : false)));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "roughness/mode", PROPERTY_HINT_ENUM, "Detect,Disabled,Red,Green,Blue,Alpha,Gray"), 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "roughness/src_normal", PROPERTY_HINT_FILE, "*.bmp,*.dds,*.exr,*.jpeg,*.jpg,*.hdr,*.png,*.svg,*.svgz,*.tga,*.webp"), ""));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "roughness/src_normal", PROPERTY_HINT_FILE, "*.bmp,*.dds,*.exr,*.jpeg,*.jpg,*.hdr,*.png,*.svg,*.tga,*.webp"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/fix_alpha_border"), p_preset != PRESET_3D));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/premult_alpha"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/normal_map_invert_y"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/HDR_as_SRGB"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "detect_3d/compress_to", PROPERTY_HINT_ENUM, "Disabled,VRAM Compressed,Basis Universal"), (p_preset == PRESET_DETECT) ? 1 : 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0));
+
+ if (p_path.get_extension() == "svg") {
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0));
+ }
}
void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality) {
@@ -408,11 +411,14 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
int size_limit = p_options["process/size_limit"];
bool hdr_as_srgb = p_options["process/HDR_as_SRGB"];
int normal = p_options["compress/normal_map"];
- float scale = p_options["svg/scale"];
int hdr_compression = p_options["compress/hdr_compression"];
int bptc_ldr = p_options["compress/bptc_ldr"];
int roughness = p_options["roughness/mode"];
String normal_map = p_options["roughness/src_normal"];
+ float scale = 1.0;
+ if (p_options.has("svg/scale")) {
+ scale = p_options["svg/scale"];
+ }
Ref<Image> normal_image;
Image::RoughnessChannel roughness_channel = Image::ROUGHNESS_CHANNEL_R;
@@ -556,12 +562,6 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
formats_imported.push_back("etc");
}
- if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc")) {
- _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC1_4, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
- r_platform_variants->push_back("pvrtc");
- formats_imported.push_back("pvrtc");
- }
-
if (!ok_on_pc) {
EditorNode::add_io_error("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correctly on PC.");
}
@@ -586,7 +586,6 @@ const char *ResourceImporterTexture::compression_formats[] = {
"s3tc",
"etc",
"etc2",
- "pvrtc",
nullptr
};
String ResourceImporterTexture::get_import_settings_string() const {
@@ -629,7 +628,7 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co
String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
bool test = ProjectSettings::get_singleton()->get(setting_path);
if (test) {
- if (formats_imported.find(compression_formats[index]) == -1) {
+ if (!formats_imported.has(compression_formats[index])) {
valid = false;
break;
}
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index 10654cfe43..f809747410 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -91,6 +91,8 @@ public:
}
};
+ImportDock *ImportDock::singleton = nullptr;
+
void ImportDock::set_edit_path(const String &p_path) {
Ref<ConfigFile> config;
config.instantiate();
@@ -445,7 +447,7 @@ static bool _find_owners(EditorFileSystemDirectory *efsd, const String &p_path)
for (int i = 0; i < efsd->get_file_count(); i++) {
Vector<String> deps = efsd->get_file_deps(i);
- if (deps.find(p_path) != -1) {
+ if (deps.has(p_path)) {
return true;
}
}
@@ -606,6 +608,7 @@ void ImportDock::initialize_import_options() const {
}
ImportDock::ImportDock() {
+ singleton = this;
set_name("Import");
content = memnew(VBoxContainer);
@@ -687,5 +690,6 @@ ImportDock::ImportDock() {
}
ImportDock::~ImportDock() {
+ singleton = nullptr;
memdelete(params);
}
diff --git a/editor/import_dock.h b/editor/import_dock.h
index 33fc23f1b4..c5cdc4ac40 100644
--- a/editor/import_dock.h
+++ b/editor/import_dock.h
@@ -85,6 +85,12 @@ class ImportDock : public VBoxContainer {
ITEM_CLEAR_DEFAULT,
};
+private:
+ static ImportDock *singleton;
+
+public:
+ static ImportDock *get_singleton() { return singleton; }
+
protected:
static void _bind_methods();
void _notification(int p_what);
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index ccb287e433..e36c86fb10 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -33,6 +33,8 @@
#include "editor/editor_scale.h"
#include "editor/plugins/animation_player_editor_plugin.h"
+InspectorDock *InspectorDock::singleton = nullptr;
+
void InspectorDock::_menu_option(int p_option) {
_menu_option_confirm(p_option, false);
}
@@ -108,7 +110,7 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) {
Variant v = current->get(E->get().name);
REF ref = v;
RES res = ref;
- if (v.is_ref() && ref.is_valid() && res.is_valid()) {
+ if (v.is_ref_counted() && ref.is_valid() && res.is_valid()) {
// Valid resource which would be duplicated if action is confirmed.
resource_propnames.append(E->get().name);
}
@@ -145,7 +147,7 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) {
}
Variant v = current->get(prop_info.name);
- if (v.is_ref()) {
+ if (v.is_ref_counted()) {
REF ref = v;
if (ref.is_valid()) {
RES res = ref;
@@ -156,7 +158,7 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) {
res = duplicates[res];
current->set(prop_info.name, res);
- editor->get_inspector()->update_property(prop_info.name);
+ get_inspector_singleton()->update_property(prop_info.name);
}
}
}
@@ -382,20 +384,6 @@ void InspectorDock::_menu_expandall() {
inspector->expand_all_folding();
}
-void InspectorDock::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_value_key(p_keyed, p_value, p_advance);
-}
-
-void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Transform3D &p_key) {
- Node3D *s = Object::cast_to<Node3D>(sp);
- if (!s) {
- return;
- }
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_POSITION_3D, p_key.origin);
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_ROTATION_3D, p_key.basis.get_rotation_quaternion());
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_SCALE_3D, p_key.basis.get_scale());
-}
-
void InspectorDock::_warning_pressed() {
warning_dialog->popup_centered();
}
@@ -406,6 +394,7 @@ Container *InspectorDock::get_addon_area() {
void InspectorDock::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
@@ -415,6 +404,7 @@ void InspectorDock::_notification(int p_what) {
resource_load_button->set_icon(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")));
resource_save_button->set_icon(get_theme_icon(SNAME("Save"), SNAME("EditorIcons")));
resource_extra_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
+ open_docs_button->set_icon(get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons")));
PopupMenu *resource_extra_popup = resource_extra_button->get_popup();
resource_extra_popup->set_item_icon(resource_extra_popup->get_item_index(RESOURCE_EDIT_CLIPBOARD), get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")));
@@ -430,6 +420,7 @@ void InspectorDock::_notification(int p_what) {
history_menu->set_icon(get_theme_icon(SNAME("History"), SNAME("EditorIcons")));
object_menu->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons")));
+ search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons")));
warning->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
} break;
@@ -437,9 +428,6 @@ void InspectorDock::_notification(int p_what) {
}
void InspectorDock::_bind_methods() {
- ClassDB::bind_method("update_keying", &InspectorDock::update_keying);
- ClassDB::bind_method("_transform_keyed", &InspectorDock::_transform_keyed); // Still used by some connect_compat.
-
ClassDB::bind_method("_unref_resource", &InspectorDock::_unref_resource);
ClassDB::bind_method("_paste_resource", &InspectorDock::_paste_resource);
ClassDB::bind_method("_copy_resource", &InspectorDock::_copy_resource);
@@ -544,25 +532,9 @@ void InspectorDock::go_back() {
_edit_back();
}
-void InspectorDock::update_keying() {
- bool valid = false;
-
- if (AnimationPlayerEditor::get_singleton()->get_track_editor()->has_keying()) {
- EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
- if (editor_history->get_path_size() >= 1) {
- Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0));
- if (Object::cast_to<Node>(obj)) {
- valid = true;
- }
- }
- }
-
- inspector->set_keying(valid);
-}
-
InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
+ singleton = this;
set_name("Inspector");
- set_theme(p_editor->get_gui_base()->get_theme());
editor = p_editor;
editor_data = &p_editor_data;
@@ -573,7 +545,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
resource_new_button = memnew(Button);
resource_new_button->set_flat(true);
resource_new_button->set_tooltip(TTR("Create a new resource in memory and edit it."));
- resource_new_button->set_icon(get_theme_icon(SNAME("New"), SNAME("EditorIcons")));
general_options_hb->add_child(resource_new_button);
resource_new_button->connect("pressed", callable_mp(this, &InspectorDock::_new_resource));
resource_new_button->set_focus_mode(Control::FOCUS_NONE);
@@ -581,14 +552,12 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
resource_load_button = memnew(Button);
resource_load_button->set_flat(true);
resource_load_button->set_tooltip(TTR("Load an existing resource from disk and edit it."));
- resource_load_button->set_icon(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")));
general_options_hb->add_child(resource_load_button);
resource_load_button->connect("pressed", callable_mp(this, &InspectorDock::_open_resource_selector));
resource_load_button->set_focus_mode(Control::FOCUS_NONE);
resource_save_button = memnew(MenuButton);
resource_save_button->set_tooltip(TTR("Save the currently edited resource."));
- resource_save_button->set_icon(get_theme_icon(SNAME("Save"), SNAME("EditorIcons")));
general_options_hb->add_child(resource_save_button);
resource_save_button->get_popup()->add_item(TTR("Save"), RESOURCE_SAVE);
resource_save_button->get_popup()->add_item(TTR("Save As..."), RESOURCE_SAVE_AS);
@@ -597,7 +566,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
resource_save_button->set_disabled(true);
resource_extra_button = memnew(MenuButton);
- resource_extra_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
resource_extra_button->set_tooltip(TTR("Extra resource options."));
general_options_hb->add_child(resource_extra_button);
resource_extra_button->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_resource_extra_popup));
@@ -614,11 +582,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
backward_button = memnew(Button);
backward_button->set_flat(true);
general_options_hb->add_child(backward_button);
- if (is_layout_rtl()) {
- backward_button->set_icon(get_theme_icon(SNAME("Forward"), SNAME("EditorIcons")));
- } else {
- backward_button->set_icon(get_theme_icon(SNAME("Back"), SNAME("EditorIcons")));
- }
backward_button->set_tooltip(TTR("Go to the previous edited object in history."));
backward_button->set_disabled(true);
backward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_back));
@@ -626,18 +589,12 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
forward_button = memnew(Button);
forward_button->set_flat(true);
general_options_hb->add_child(forward_button);
- if (is_layout_rtl()) {
- forward_button->set_icon(get_theme_icon(SNAME("Back"), SNAME("EditorIcons")));
- } else {
- forward_button->set_icon(get_theme_icon(SNAME("Forward"), SNAME("EditorIcons")));
- }
forward_button->set_tooltip(TTR("Go to the next edited object in history."));
forward_button->set_disabled(true);
forward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_forward));
history_menu = memnew(MenuButton);
history_menu->set_tooltip(TTR("History of recently edited objects."));
- history_menu->set_icon(get_theme_icon(SNAME("History"), SNAME("EditorIcons")));
general_options_hb->add_child(history_menu);
history_menu->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_history));
history_menu->get_popup()->connect("id_pressed", callable_mp(this, &InspectorDock::_select_history));
@@ -652,7 +609,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
open_docs_button->set_flat(true);
open_docs_button->set_disabled(true);
open_docs_button->set_tooltip(TTR("Open documentation for this object."));
- open_docs_button->set_icon(get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons")));
open_docs_button->set_shortcut(ED_SHORTCUT("property_editor/open_help", TTR("Open Documentation")));
subresource_hb->add_child(open_docs_button);
open_docs_button->connect("pressed", callable_mp(this, &InspectorDock::_menu_option), varray(OBJECT_REQUEST_HELP));
@@ -668,13 +624,11 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
search = memnew(LineEdit);
search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search->set_placeholder(TTR("Filter properties"));
- search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
search->set_clear_button_enabled(true);
property_tools_hb->add_child(search);
object_menu = memnew(MenuButton);
object_menu->set_shortcut_context(this);
- object_menu->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons")));
property_tools_hb->add_child(object_menu);
object_menu->set_tooltip(TTR("Manage object properties."));
object_menu->get_popup()->connect("id_pressed", callable_mp(this, &InspectorDock::_menu_option));
@@ -682,8 +636,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
warning = memnew(Button);
add_child(warning);
warning->set_text(TTR("Changes may be lost!"));
- warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons")));
- warning->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
warning->set_clip_text(true);
warning->hide();
warning->connect("pressed", callable_mp(this, &InspectorDock::_warning_pressed));
@@ -734,8 +686,8 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
inspector->set_use_filter(true); // TODO: check me
inspector->connect("resource_selected", callable_mp(this, &InspectorDock::_resource_selected));
- inspector->connect("property_keyed", callable_mp(this, &InspectorDock::_property_keyed));
}
InspectorDock::~InspectorDock() {
+ singleton = nullptr;
}
diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h
index 94e4f67348..9dd3fa2070 100644
--- a/editor/inspector_dock.h
+++ b/editor/inspector_dock.h
@@ -117,8 +117,12 @@ class InspectorDock : public VBoxContainer {
void _select_history(int p_idx);
void _prepare_history();
- void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance);
- void _transform_keyed(Object *sp, const String &p_sub, const Transform3D &p_key);
+private:
+ static InspectorDock *singleton;
+
+public:
+ static InspectorDock *get_singleton() { return singleton; }
+ static EditorInspector *get_inspector_singleton() { return singleton->inspector; }
protected:
static void _bind_methods();
@@ -126,7 +130,6 @@ protected:
public:
void go_back();
- void update_keying();
void edit_resource(const Ref<Resource> &p_resource);
void open_resource(const String &p_type);
void clear();
diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp
index a902b070f4..1e9e2fc09b 100644
--- a/editor/localization_editor.cpp
+++ b/editor/localization_editor.cpp
@@ -32,6 +32,7 @@
#include "core/string/translation.h"
#include "editor_node.h"
+#include "editor_scale.h"
#include "editor_translation_parser.h"
#include "pot_generator.h"
#include "scene/gui/control.h"
@@ -175,10 +176,27 @@ void LocalizationEditor::_translation_res_select() {
if (updating_translations) {
return;
}
-
call_deferred(SNAME("update_translations"));
}
+void LocalizationEditor::_translation_res_option_popup(bool p_arrow_clicked) {
+ TreeItem *ed = translation_remap_options->get_edited();
+ ERR_FAIL_COND(!ed);
+
+ locale_select->set_locale(ed->get_tooltip(1));
+ locale_select->popup_locale_dialog();
+}
+
+void LocalizationEditor::_translation_res_option_selected(const String &p_locale) {
+ TreeItem *ed = translation_remap_options->get_edited();
+ ERR_FAIL_COND(!ed);
+
+ ed->set_text(1, TranslationServer::get_singleton()->get_locale_name(p_locale));
+ ed->set_tooltip(1, p_locale);
+
+ LocalizationEditor::_translation_res_option_changed();
+}
+
void LocalizationEditor::_translation_res_option_changed() {
if (updating_translations) {
return;
@@ -198,20 +216,11 @@ void LocalizationEditor::_translation_res_option_changed() {
String key = k->get_metadata(0);
int idx = ed->get_metadata(0);
String path = ed->get_metadata(1);
- int which = ed->get_range(1);
-
- Vector<String> langs = TranslationServer::get_all_locales();
-
- ERR_FAIL_INDEX(which, langs.size());
+ String locale = ed->get_tooltip(1);
ERR_FAIL_COND(!remaps.has(key));
PackedStringArray r = remaps[key];
- ERR_FAIL_INDEX(idx, r.size());
- if (translation_locales_idxs_remap.size() > which) {
- r.set(idx, path + ":" + langs[translation_locales_idxs_remap[which]]);
- } else {
- r.set(idx, path + ":" + langs[which]);
- }
+ r.set(idx, path + ":" + locale);
remaps[key] = r;
updating_translations = true;
@@ -289,86 +298,6 @@ void LocalizationEditor::_translation_res_option_delete(Object *p_item, int p_co
undo_redo->commit_action();
}
-void LocalizationEditor::_translation_filter_option_changed() {
- int sel_id = translation_locale_filter_mode->get_selected_id();
- TreeItem *t = translation_filter->get_edited();
- String locale = t->get_tooltip(0);
- bool checked = t->is_checked(0);
-
- Variant prev;
- Array f_locales_all;
-
- if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter")) {
- f_locales_all = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter");
- prev = f_locales_all;
-
- if (f_locales_all.size() != 2) {
- f_locales_all.clear();
- f_locales_all.append(sel_id);
- f_locales_all.append(Array());
- }
- } else {
- f_locales_all.append(sel_id);
- f_locales_all.append(Array());
- }
-
- Array f_locales = f_locales_all[1];
- int l_idx = f_locales.find(locale);
-
- if (checked) {
- if (l_idx == -1) {
- f_locales.append(locale);
- }
- } else {
- if (l_idx != -1) {
- f_locales.remove_at(l_idx);
- }
- }
-
- f_locales.sort();
-
- undo_redo->create_action(TTR("Changed Locale Filter"));
- undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", f_locales_all);
- undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", prev);
- undo_redo->add_do_method(this, "update_translations");
- undo_redo->add_undo_method(this, "update_translations");
- undo_redo->add_do_method(this, "emit_signal", localization_changed);
- undo_redo->add_undo_method(this, "emit_signal", localization_changed);
- undo_redo->commit_action();
-}
-
-void LocalizationEditor::_translation_filter_mode_changed(int p_mode) {
- int sel_id = translation_locale_filter_mode->get_selected_id();
-
- Variant prev;
- Array f_locales_all;
-
- if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter")) {
- f_locales_all = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter");
- prev = f_locales_all;
-
- if (f_locales_all.size() != 2) {
- f_locales_all.clear();
- f_locales_all.append(sel_id);
- f_locales_all.append(Array());
- } else {
- f_locales_all[0] = sel_id;
- }
- } else {
- f_locales_all.append(sel_id);
- f_locales_all.append(Array());
- }
-
- undo_redo->create_action(TTR("Changed Locale Filter Mode"));
- undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", f_locales_all);
- undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", prev);
- undo_redo->add_do_method(this, "update_translations");
- undo_redo->add_undo_method(this, "update_translations");
- undo_redo->add_do_method(this, "emit_signal", localization_changed);
- undo_redo->add_undo_method(this, "emit_signal", localization_changed);
- undo_redo->commit_action();
-}
-
void LocalizationEditor::_pot_add(const PackedStringArray &p_paths) {
PackedStringArray pot_translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations_pot_files");
for (int i = 0; i < p_paths.size(); i++) {
@@ -452,64 +381,6 @@ void LocalizationEditor::update_translations() {
}
}
- Vector<String> langs = TranslationServer::get_all_locales();
- Vector<String> names = TranslationServer::get_all_locale_names();
-
- // Update filter tab
- Array l_filter_all;
-
- bool is_arr_empty = true;
- if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter")) {
- l_filter_all = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter");
-
- if (l_filter_all.size() == 2) {
- translation_locale_filter_mode->select(l_filter_all[0]);
- is_arr_empty = false;
- }
- }
- if (is_arr_empty) {
- l_filter_all.append(0);
- l_filter_all.append(Array());
- translation_locale_filter_mode->select(0);
- }
-
- int filter_mode = l_filter_all[0];
- Array l_filter = l_filter_all[1];
-
- int s = names.size();
- bool is_short_list_when_show_all_selected = filter_mode == SHOW_ALL_LOCALES && translation_filter_treeitems.size() < s;
- bool is_full_list_when_show_only_selected = filter_mode == SHOW_ONLY_SELECTED_LOCALES && translation_filter_treeitems.size() == s;
- bool should_recreate_locales_list = is_short_list_when_show_all_selected || is_full_list_when_show_only_selected;
-
- if (!translation_locales_list_created || should_recreate_locales_list) {
- translation_locales_list_created = true;
- translation_filter->clear();
- root = translation_filter->create_item(nullptr);
- translation_filter->set_hide_root(true);
- translation_filter_treeitems.clear();
- for (int i = 0; i < s; i++) {
- String n = names[i];
- String l = langs[i];
- bool is_checked = l_filter.has(l);
- if (filter_mode == SHOW_ONLY_SELECTED_LOCALES && !is_checked) {
- continue;
- }
-
- TreeItem *t = translation_filter->create_item(root);
- t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- t->set_text(0, vformat("[%s] %s", l, n));
- t->set_editable(0, true);
- t->set_tooltip(0, l);
- t->set_checked(0, is_checked);
- translation_filter_treeitems.push_back(t);
- }
- } else {
- for (int i = 0; i < translation_filter_treeitems.size(); i++) {
- TreeItem *t = translation_filter_treeitems[i];
- t->set_checked(0, l_filter.has(t->get_tooltip(0)));
- }
- }
-
// Update translation remaps.
String remap_selected;
if (translation_remap->get_selected()) {
@@ -524,32 +395,6 @@ void LocalizationEditor::update_translations() {
translation_remap_options->set_hide_root(true);
translation_res_option_add_button->set_disabled(true);
- translation_locales_idxs_remap.clear();
- translation_locales_idxs_remap.resize(l_filter.size());
- int fl_idx_count = translation_locales_idxs_remap.size();
-
- String langnames = "";
- int l_idx = 0;
- for (int i = 0; i < names.size(); i++) {
- if (filter_mode == SHOW_ONLY_SELECTED_LOCALES && fl_idx_count != 0) {
- if (l_filter.size() > 0) {
- if (l_filter.find(langs[i]) != -1) {
- if (langnames.length() > 0) {
- langnames += ",";
- }
- langnames += vformat("[%s] %s", langs[i], names[i]);
- translation_locales_idxs_remap.write[l_idx] = i;
- l_idx++;
- }
- }
- } else {
- if (i > 0) {
- langnames += ",";
- }
- langnames += vformat("[%s] %s", langs[i], names[i]);
- }
- }
-
if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) {
Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps");
List<Variant> rk;
@@ -584,21 +429,11 @@ void LocalizationEditor::update_translations() {
t2->set_tooltip(0, path);
t2->set_metadata(0, j);
t2->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), 0, false, TTR("Remove"));
- t2->set_cell_mode(1, TreeItem::CELL_MODE_RANGE);
- t2->set_text(1, langnames);
+ t2->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM);
+ t2->set_text(1, TranslationServer::get_singleton()->get_locale_name(locale));
t2->set_editable(1, true);
t2->set_metadata(1, path);
- int idx = langs.find(locale);
- if (idx < 0) {
- idx = 0;
- }
-
- int f_idx = translation_locales_idxs_remap.find(idx);
- if (f_idx != -1 && fl_idx_count > 0 && filter_mode == SHOW_ONLY_SELECTED_LOCALES) {
- t2->set_range(1, f_idx);
- } else {
- t2->set_range(1, idx);
- }
+ t2->set_tooltip(1, locale);
}
}
}
@@ -637,9 +472,6 @@ LocalizationEditor::LocalizationEditor() {
updating_translations = false;
localization_changed = "localization_changed";
- translation_locales_idxs_remap = Vector<int>();
- translation_locales_list_created = false;
-
TabContainer *translations = memnew(TabContainer);
translations->set_tab_alignment(TabContainer::ALIGNMENT_LEFT);
translations->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -669,6 +501,10 @@ LocalizationEditor::LocalizationEditor() {
translation_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tmc->add_child(translation_list);
+ locale_select = memnew(EditorLocaleDialog);
+ locale_select->connect("locale_selected", callable_mp(this, &LocalizationEditor::_translation_res_option_selected));
+ add_child(locale_select);
+
translation_file_open = memnew(EditorFileDialog);
translation_file_open->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILES);
translation_file_open->connect("files_selected", callable_mp(this, &LocalizationEditor::_translation_add));
@@ -731,10 +567,11 @@ LocalizationEditor::LocalizationEditor() {
translation_remap_options->set_column_expand(0, true);
translation_remap_options->set_column_clip_content(0, true);
translation_remap_options->set_column_expand(1, false);
- translation_remap_options->set_column_clip_content(1, true);
- translation_remap_options->set_column_custom_minimum_width(1, 200);
+ translation_remap_options->set_column_clip_content(1, false);
+ translation_remap_options->set_column_custom_minimum_width(1, 250);
translation_remap_options->connect("item_edited", callable_mp(this, &LocalizationEditor::_translation_res_option_changed));
translation_remap_options->connect("button_pressed", callable_mp(this, &LocalizationEditor::_translation_res_option_delete));
+ translation_remap_options->connect("custom_popup_edited", callable_mp(this, &LocalizationEditor::_translation_res_option_popup));
tmc->add_child(translation_remap_options);
translation_res_option_file_open_dialog = memnew(EditorFileDialog);
@@ -745,32 +582,6 @@ LocalizationEditor::LocalizationEditor() {
{
VBoxContainer *tvb = memnew(VBoxContainer);
- tvb->set_name(TTR("Locales Filter"));
- translations->add_child(tvb);
-
- VBoxContainer *tmc = memnew(VBoxContainer);
- tmc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- tvb->add_child(tmc);
-
- translation_locale_filter_mode = memnew(OptionButton);
- translation_locale_filter_mode->add_item(TTR("Show All Locales"), SHOW_ALL_LOCALES);
- translation_locale_filter_mode->add_item(TTR("Show Selected Locales Only"), SHOW_ONLY_SELECTED_LOCALES);
- translation_locale_filter_mode->select(0);
- translation_locale_filter_mode->connect("item_selected", callable_mp(this, &LocalizationEditor::_translation_filter_mode_changed));
- tmc->add_margin_child(TTR("Filter mode:"), translation_locale_filter_mode);
-
- Label *l = memnew(Label(TTR("Locales:")));
- l->set_theme_type_variation("HeaderSmall");
- tmc->add_child(l);
- translation_filter = memnew(Tree);
- translation_filter->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- translation_filter->set_columns(1);
- translation_filter->connect("item_edited", callable_mp(this, &LocalizationEditor::_translation_filter_option_changed));
- tmc->add_child(translation_filter);
- }
-
- {
- VBoxContainer *tvb = memnew(VBoxContainer);
tvb->set_name(TTR("POT Generation"));
translations->add_child(tvb);
diff --git a/editor/localization_editor.h b/editor/localization_editor.h
index 4c77aca397..cad07dd336 100644
--- a/editor/localization_editor.h
+++ b/editor/localization_editor.h
@@ -33,18 +33,15 @@
#include "core/object/undo_redo.h"
#include "editor_file_dialog.h"
+#include "editor_locale_dialog.h"
#include "scene/gui/tree.h"
class LocalizationEditor : public VBoxContainer {
GDCLASS(LocalizationEditor, VBoxContainer);
- enum LocaleFilter {
- SHOW_ALL_LOCALES,
- SHOW_ONLY_SELECTED_LOCALES,
- };
-
Tree *translation_list;
+ EditorLocaleDialog *locale_select;
EditorFileDialog *translation_file_open;
Button *translation_res_option_add_button;
@@ -52,11 +49,6 @@ class LocalizationEditor : public VBoxContainer {
EditorFileDialog *translation_res_option_file_open_dialog;
Tree *translation_remap;
Tree *translation_remap_options;
- Tree *translation_filter;
- bool translation_locales_list_created;
- OptionButton *translation_locale_filter_mode;
- Vector<TreeItem *> translation_filter_treeitems;
- Vector<int> translation_locales_idxs_remap;
Tree *translation_pot_list;
EditorFileDialog *pot_file_open_dialog;
@@ -78,9 +70,8 @@ class LocalizationEditor : public VBoxContainer {
void _translation_res_option_add(const PackedStringArray &p_paths);
void _translation_res_option_changed();
void _translation_res_option_delete(Object *p_item, int p_column, int p_button);
-
- void _translation_filter_option_changed();
- void _translation_filter_mode_changed(int p_mode);
+ void _translation_res_option_popup(bool p_arrow_clicked);
+ void _translation_res_option_selected(const String &p_locale);
void _pot_add(const PackedStringArray &p_paths);
void _pot_delete(Object *p_item, int p_column, int p_button);
diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp
index 59fc473d73..c61380684a 100644
--- a/editor/multi_node_edit.cpp
+++ b/editor/multi_node_edit.cpp
@@ -87,8 +87,8 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
ur->add_undo_property(n, name, n->get(name));
}
- ur->add_do_method(EditorNode::get_singleton()->get_inspector(), "refresh");
- ur->add_undo_method(EditorNode::get_singleton()->get_inspector(), "refresh");
+ ur->add_do_method(InspectorDock::get_inspector_singleton(), "refresh");
+ ur->add_undo_method(InspectorDock::get_inspector_singleton(), "refresh");
ur->commit_action();
return true;
diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp
index d8f16b367a..1246ebe0dd 100644
--- a/editor/node_dock.cpp
+++ b/editor/node_dock.cpp
@@ -134,3 +134,7 @@ NodeDock::NodeDock() {
select_a_node->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
add_child(select_a_node);
}
+
+NodeDock::~NodeDock() {
+ singleton = nullptr;
+}
diff --git a/editor/node_dock.h b/editor/node_dock.h
index b35be8de8a..4c814ab65f 100644
--- a/editor/node_dock.h
+++ b/editor/node_dock.h
@@ -48,13 +48,17 @@ class NodeDock : public VBoxContainer {
Label *select_a_node;
+private:
+ static NodeDock *singleton;
+
+public:
+ static NodeDock *get_singleton() { return singleton; }
+
protected:
static void _bind_methods();
void _notification(int p_what);
public:
- static NodeDock *singleton;
-
void set_node(Node *p_node);
void show_groups();
@@ -63,6 +67,7 @@ public:
void update_lists();
NodeDock();
+ ~NodeDock();
};
#endif // NODE_DOCK_H
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index 348ef4ecc7..c6bde4c98a 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -147,12 +147,15 @@ void AbstractPolygon2DEditor::_menu_option(int p_option) {
void AbstractPolygon2DEditor::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ button_create->set_icon(get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons")));
+ button_edit->set_icon(get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
+ button_delete->set_icon(get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
+ } break;
case NOTIFICATION_READY: {
disable_polygon_editing(false, String());
- button_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons")));
- button_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
- button_delete->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
button_edit->set_pressed(true);
get_tree()->connect("node_removed", callable_mp(this, &AbstractPolygon2DEditor::_node_removed));
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index c0029312a7..f9df8db419 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -180,7 +180,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
selected_point = -1;
for (int i = 0; i < points.size(); i++) {
- if (making_triangle.find(i) != -1) {
+ if (making_triangle.has(i)) {
continue;
}
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 9ebdede4e9..e46c81b77e 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -40,6 +40,7 @@
#include "scene/gui/menu_button.h"
#include "scene/gui/panel.h"
#include "scene/gui/progress_bar.h"
+#include "scene/gui/view_panner.h"
#include "scene/main/window.h"
void AnimationNodeBlendTreeEditor::add_custom_type(const String &p_name, const Ref<Script> &p_script) {
@@ -103,7 +104,7 @@ void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_propert
}
void AnimationNodeBlendTreeEditor::_update_graph() {
- if (updating) {
+ if (updating || blend_tree.is_null()) {
return;
}
@@ -732,6 +733,11 @@ void AnimationNodeBlendTreeEditor::_removed_from_graph() {
}
void AnimationNodeBlendTreeEditor::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+ graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
+ graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning")));
+ }
+
if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
@@ -892,6 +898,9 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima
}
void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node) {
+ if (le == nullptr) {
+ return; // The text_submitted signal triggered the graph update and freed the LineEdit.
+ }
_node_renamed(le->call("get_text"), p_node);
}
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h
index 8e63e39fd5..1e55cc8598 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.h
+++ b/editor/plugins/animation_blend_tree_editor_plugin.h
@@ -75,7 +75,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
String type;
Ref<Script> script;
int input_port_count;
- AddOption(const String &p_name = String(), const String &p_type = String(), bool p_input_port_count = 0) :
+ AddOption(const String &p_name = String(), const String &p_type = String(), int p_input_port_count = 0) :
name(p_name),
type(p_type),
input_port_count(p_input_port_count) {
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 4ce9f40a5e..83c2b53241 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -301,7 +301,6 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
autoplay->set_pressed(current == player->get_autoplay());
AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying();
- EditorNode::get_singleton()->update_keying();
_animation_key_editor_seek(timeline_position, false);
}
@@ -829,7 +828,6 @@ void AnimationPlayerEditor::_update_player() {
if (!player) {
AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying();
- EditorNode::get_singleton()->update_keying();
return;
}
@@ -967,16 +965,7 @@ void AnimationPlayerEditor::_animation_duplicate() {
return;
}
- Ref<Animation> new_anim = memnew(Animation);
- List<PropertyInfo> plist;
- anim->get_property_list(&plist);
- for (const PropertyInfo &E : plist) {
- if (E.usage & PROPERTY_USAGE_STORAGE) {
- new_anim->set(E.name, anim->get(E.name));
- }
- }
- new_anim->set_path("");
-
+ Ref<Animation> new_anim = _animation_clone(anim);
String new_name = current;
while (player->has_animation(new_name)) {
new_name = new_name + " (copy)";
@@ -1000,6 +989,44 @@ void AnimationPlayerEditor::_animation_duplicate() {
}
}
+Ref<Animation> AnimationPlayerEditor::_animation_clone(Ref<Animation> p_anim) {
+ Ref<Animation> new_anim = memnew(Animation);
+ List<PropertyInfo> plist;
+ p_anim->get_property_list(&plist);
+
+ for (const PropertyInfo &E : plist) {
+ if (E.usage & PROPERTY_USAGE_STORAGE) {
+ new_anim->set(E.name, p_anim->get(E.name));
+ }
+ }
+ new_anim->set_path("");
+
+ return new_anim;
+}
+
+void AnimationPlayerEditor::_animation_paste(Ref<Animation> p_anim) {
+ String name = p_anim->get_name();
+ if (name.is_empty()) {
+ name = TTR("Pasted Animation");
+ }
+
+ int idx = 1;
+ String base = name;
+ while (player->has_animation(name)) {
+ idx++;
+ name = base + " " + itos(idx);
+ }
+
+ undo_redo->create_action(TTR("Paste Animation"));
+ undo_redo->add_do_method(player, "add_animation", name, p_anim);
+ undo_redo->add_undo_method(player, "remove_animation", name);
+ undo_redo->add_do_method(this, "_animation_player_changed", player);
+ undo_redo->add_undo_method(this, "_animation_player_changed", player);
+ undo_redo->commit_action();
+
+ _select_anim_by_name(name);
+}
+
void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set, bool p_timeline_only) {
if (updating || !player || player->is_playing()) {
return;
@@ -1135,31 +1162,22 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
case TOOL_PASTE_ANIM: {
Ref<Animation> anim2 = EditorSettings::get_singleton()->get_resource_clipboard();
if (!anim2.is_valid()) {
- error_dialog->set_text(TTR("No animation resource on clipboard!"));
+ error_dialog->set_text(TTR("No animation resource in clipboard!"));
error_dialog->popup_centered();
return;
}
-
- String name = anim2->get_name();
- if (name.is_empty()) {
- name = TTR("Pasted Animation");
- }
-
- int idx = 1;
- String base = name;
- while (player->has_animation(name)) {
- idx++;
- name = base + " " + itos(idx);
+ Ref<Animation> new_anim = _animation_clone(anim2);
+ _animation_paste(new_anim);
+ } break;
+ case TOOL_PASTE_ANIM_REF: {
+ Ref<Animation> anim2 = EditorSettings::get_singleton()->get_resource_clipboard();
+ if (!anim2.is_valid()) {
+ error_dialog->set_text(TTR("No animation resource in clipboard!"));
+ error_dialog->popup_centered();
+ return;
}
- undo_redo->create_action(TTR("Paste Animation"));
- undo_redo->add_do_method(player, "add_animation", name, anim2);
- undo_redo->add_undo_method(player, "remove_animation", name);
- undo_redo->add_do_method(this, "_animation_player_changed", player);
- undo_redo->add_undo_method(this, "_animation_player_changed", player);
- undo_redo->commit_action();
-
- _select_anim_by_name(name);
+ _animation_paste(anim2);
} break;
case TOOL_EDIT_RESOURCE: {
if (!animation->get_item_count()) {
@@ -1487,7 +1505,7 @@ void AnimationPlayerEditor::_stop_onion_skinning() {
}
void AnimationPlayerEditor::_pin_pressed() {
- EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->update_tree();
+ SceneTreeDock::get_singleton()->get_tree_editor()->update_tree();
}
void AnimationPlayerEditor::_bind_methods() {
@@ -1587,6 +1605,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
tool_anim->get_popup()->add_separator();
tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/copy_animation", TTR("Copy")), TOOL_COPY_ANIM);
tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/paste_animation", TTR("Paste")), TOOL_PASTE_ANIM);
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/paste_animation_as_reference", TTR("Paste As Reference")), TOOL_PASTE_ANIM_REF);
tool_anim->get_popup()->add_separator();
tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/duplicate_animation", TTR("Duplicate")), TOOL_DUPLICATE_ANIM);
tool_anim->get_popup()->add_separator();
@@ -1774,11 +1793,39 @@ AnimationPlayerEditor::~AnimationPlayerEditor() {
void AnimationPlayerEditorPlugin::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
+ Node3DEditor::get_singleton()->connect("transform_key_request", callable_mp(this, &AnimationPlayerEditorPlugin::_transform_key_request));
+ InspectorDock::get_inspector_singleton()->connect("property_keyed", callable_mp(this, &AnimationPlayerEditorPlugin::_property_keyed));
+ anim_editor->get_track_editor()->connect("keying_changed", callable_mp(this, &AnimationPlayerEditorPlugin::_update_keying));
+ InspectorDock::get_inspector_singleton()->connect("edited_object_changed", callable_mp(anim_editor->get_track_editor(), &AnimationTrackEditor::update_keying));
set_force_draw_over_forwarding_enabled();
} break;
}
}
+void AnimationPlayerEditorPlugin::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) {
+ if (!anim_editor->get_track_editor()->has_keying()) {
+ return;
+ }
+ anim_editor->get_track_editor()->insert_value_key(p_keyed, p_value, p_advance);
+}
+
+void AnimationPlayerEditorPlugin::_transform_key_request(Object *sp, const String &p_sub, const Transform3D &p_key) {
+ if (!anim_editor->get_track_editor()->has_keying()) {
+ return;
+ }
+ Node3D *s = Object::cast_to<Node3D>(sp);
+ if (!s) {
+ return;
+ }
+ anim_editor->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_POSITION_3D, p_key.origin);
+ anim_editor->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_ROTATION_3D, p_key.basis.get_rotation_quaternion());
+ anim_editor->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_SCALE_3D, p_key.basis.get_scale());
+}
+
+void AnimationPlayerEditorPlugin::_update_keying() {
+ InspectorDock::get_inspector_singleton()->set_keying(anim_editor->get_track_editor()->has_keying());
+}
+
void AnimationPlayerEditorPlugin::edit(Object *p_object) {
anim_editor->set_undo_redo(&get_undo_redo());
if (!p_object) {
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index 4e7ea46c1d..06dca11aff 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -60,6 +60,7 @@ class AnimationPlayerEditor : public VBoxContainer {
TOOL_REMOVE_ANIM,
TOOL_COPY_ANIM,
TOOL_PASTE_ANIM,
+ TOOL_PASTE_ANIM_REF,
TOOL_EDIT_RESOURCE
};
@@ -183,6 +184,8 @@ class AnimationPlayerEditor : public VBoxContainer {
void _animation_blend();
void _animation_edit();
void _animation_duplicate();
+ Ref<Animation> _animation_clone(const Ref<Animation> p_anim);
+ void _animation_paste(const Ref<Animation> p_anim);
void _animation_resource_edit();
void _scale_changed(const String &p_scale);
void _save_animation(String p_file);
@@ -252,6 +255,10 @@ class AnimationPlayerEditorPlugin : public EditorPlugin {
protected:
void _notification(int p_what);
+ void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance);
+ void _transform_key_request(Object *sp, const String &p_sub, const Transform3D &p_key);
+ void _update_keying();
+
public:
virtual Dictionary get_state() const override { return anim_editor->get_state(); }
virtual void set_state(const Dictionary &p_state) override { anim_editor->set_state(p_state); }
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index 94990636da..649fc53b3a 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -755,7 +755,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
//now scroll it to draw
state_machine_draw->draw_style_box(sb, nr.node);
- if (playing && (blend_from == name || current == name || travel_path.find(name) != -1)) {
+ if (playing && (blend_from == name || current == name || travel_path.has(name))) {
state_machine_draw->draw_style_box(playing_overlay, nr.node);
}
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 49bef4acd5..7199f69f0b 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -193,7 +193,8 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const
void EditorAssetLibraryItemDescription::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
previews_bg->add_theme_style_override("panel", previews->get_theme_stylebox(SNAME("normal"), SNAME("TextEdit")));
} break;
}
@@ -368,19 +369,19 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int
download_error->set_text(TTR("Asset Download Error:") + "\n" + error_text);
download_error->popup_centered();
// Let the user retry the download.
- retry->show();
+ retry_button->show();
return;
}
- install->set_disabled(false);
- status->set_text(TTR("Success!"));
+ install_button->set_disabled(false);
+ status->set_text(TTR("Ready to install!"));
// Make the progress bar invisible but don't reflow other Controls around it.
progress->set_modulate(Color(0, 0, 0, 0));
set_process(false);
// Automatically prompt for installation once the download is completed.
- _install();
+ install();
}
void EditorAssetLibraryItemDownload::configure(const String &p_title, int p_asset_id, const Ref<Texture2D> &p_preview, const String &p_download_url, const String &p_sha256_hash) {
@@ -397,10 +398,11 @@ void EditorAssetLibraryItemDownload::configure(const String &p_title, int p_asse
void EditorAssetLibraryItemDownload::_notification(int p_what) {
switch (p_what) {
- // FIXME: The editor crashes if 'NOTICATION_THEME_CHANGED' is used.
- case NOTIFICATION_ENTER_TREE: {
- add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
- dismiss->set_normal_texture(get_theme_icon(SNAME("Close"), SNAME("EditorIcons")));
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("AssetLib")));
+ status->add_theme_color_override("font_color", get_theme_color(SNAME("status_color"), SNAME("AssetLib")));
+ dismiss_button->set_normal_texture(get_theme_icon(SNAME("dismiss"), SNAME("AssetLib")));
} break;
case NOTIFICATION_PROCESS: {
// Make the progress bar visible again when retrying the download.
@@ -460,7 +462,11 @@ void EditorAssetLibraryItemDownload::_close() {
queue_delete();
}
-void EditorAssetLibraryItemDownload::_install() {
+bool EditorAssetLibraryItemDownload::can_install() const {
+ return !install_button->is_disabled();
+}
+
+void EditorAssetLibraryItemDownload::install() {
String file = download->get_download_file();
if (external_install) {
@@ -474,7 +480,7 @@ void EditorAssetLibraryItemDownload::_install() {
void EditorAssetLibraryItemDownload::_make_request() {
// Hide the Retry button if we've just pressed it.
- retry->hide();
+ retry_button->hide();
download->cancel_request();
download->set_download_file(EditorPaths::get_singleton()->get_cache_dir().plus_file("tmp_asset_" + itos(asset_id)) + ".zip");
@@ -492,9 +498,14 @@ void EditorAssetLibraryItemDownload::_bind_methods() {
}
EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() {
+ panel = memnew(PanelContainer);
+ add_child(panel);
+
HBoxContainer *hb = memnew(HBoxContainer);
- add_child(hb);
+ panel->add_child(hb);
icon = memnew(TextureRect);
+ icon->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
+ icon->set_v_size_flags(0);
hb->add_child(icon);
VBoxContainer *vb = memnew(VBoxContainer);
@@ -507,9 +518,9 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() {
title_hb->add_child(title);
title->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- dismiss = memnew(TextureButton);
- dismiss->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::_close));
- title_hb->add_child(dismiss);
+ dismiss_button = memnew(TextureButton);
+ dismiss_button->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::_close));
+ title_hb->add_child(dismiss_button);
title->set_clip_text(true);
@@ -517,7 +528,6 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() {
status = memnew(Label(TTR("Idle")));
vb->add_child(status);
- status->add_theme_color_override("font_color", Color(0.5, 0.5, 0.5));
progress = memnew(ProgressBar);
vb->add_child(progress);
@@ -525,32 +535,32 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() {
vb->add_child(hb2);
hb2->add_spacer();
- install = memnew(Button);
- install->set_text(TTR("Install..."));
- install->set_disabled(true);
- install->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::_install));
+ install_button = memnew(Button);
+ install_button->set_text(TTR("Install..."));
+ install_button->set_disabled(true);
+ install_button->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::install));
- retry = memnew(Button);
- retry->set_text(TTR("Retry"));
- retry->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::_make_request));
+ retry_button = memnew(Button);
+ retry_button->set_text(TTR("Retry"));
+ retry_button->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::_make_request));
// Only show the Retry button in case of a failure.
- retry->hide();
+ retry_button->hide();
- hb2->add_child(retry);
- hb2->add_child(install);
+ hb2->add_child(retry_button);
+ hb2->add_child(install_button);
set_custom_minimum_size(Size2(310, 0) * EDSCALE);
download = memnew(HTTPRequest);
- add_child(download);
+ panel->add_child(download);
download->connect("request_completed", callable_mp(this, &EditorAssetLibraryItemDownload::_http_download_completed));
setup_http_request(download);
download_error = memnew(AcceptDialog);
- add_child(download_error);
+ panel->add_child(download_error);
download_error->set_title(TTR("Download Error"));
asset_installer = memnew(EditorAssetInstaller);
- add_child(asset_installer);
+ panel->add_child(asset_installer);
asset_installer->connect("confirmed", callable_mp(this, &EditorAssetLibraryItemDownload::_close));
prev_status = -1;
@@ -562,11 +572,15 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() {
void EditorAssetLibrary::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
+ error_label->raise();
+ } break;
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
error_tr->set_texture(get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
- filter->set_clear_button_enabled(true);
-
- error_label->raise();
+ library_scroll_bg->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ downloads_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ error_label->add_theme_color_override("color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
@@ -596,16 +610,9 @@ void EditorAssetLibrary::_notification(int p_what) {
}
} break;
- case NOTIFICATION_THEME_CHANGED: {
- library_scroll_bg->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
- downloads_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
- error_tr->set_texture(get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
- filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
- filter->set_clear_button_enabled(true);
- } break;
-
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
_update_repository_options();
+ setup_http_request(request);
} break;
}
}
@@ -640,14 +647,10 @@ void EditorAssetLibrary::unhandled_key_input(const Ref<InputEvent> &p_event) {
void EditorAssetLibrary::_install_asset() {
ERR_FAIL_COND(!description);
- for (int i = 0; i < downloads_hb->get_child_count(); i++) {
- EditorAssetLibraryItemDownload *d = Object::cast_to<EditorAssetLibraryItemDownload>(downloads_hb->get_child(i));
- if (d && d->get_asset_id() == description->get_asset_id()) {
- if (EditorNode::get_singleton() != nullptr) {
- EditorNode::get_singleton()->show_warning(TTR("Download for this asset is already in progress!"));
- }
- return;
- }
+ EditorAssetLibraryItemDownload *d = _get_asset_in_progress(description->get_asset_id());
+ if (d) {
+ d->install();
+ return;
}
EditorAssetLibraryItemDownload *download = memnew(EditorAssetLibraryItemDownload);
@@ -1265,6 +1268,20 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
description->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["cost"], r["version"], r["version_string"], r["description"], r["download_url"], r["browse_url"], r["download_hash"]);
+ EditorAssetLibraryItemDownload *download_item = _get_asset_in_progress(description->get_asset_id());
+ if (download_item) {
+ if (download_item->can_install()) {
+ description->get_ok_button()->set_text(TTR("Install"));
+ description->get_ok_button()->set_disabled(false);
+ } else {
+ description->get_ok_button()->set_text(TTR("Downloading..."));
+ description->get_ok_button()->set_disabled(true);
+ }
+ } else {
+ description->get_ok_button()->set_text(TTR("Download"));
+ description->get_ok_button()->set_disabled(false);
+ }
+
if (r.has("icon_url") && !r["icon_url"].operator String().is_empty()) {
_request_image(description->get_instance_id(), r["icon_url"], IMAGE_QUEUE_ICON, 0);
}
@@ -1322,6 +1339,17 @@ void EditorAssetLibrary::_manage_plugins() {
ProjectSettingsEditor::get_singleton()->set_plugins_page();
}
+EditorAssetLibraryItemDownload *EditorAssetLibrary::_get_asset_in_progress(int p_asset_id) const {
+ for (int i = 0; i < downloads_hb->get_child_count(); i++) {
+ EditorAssetLibraryItemDownload *d = Object::cast_to<EditorAssetLibraryItemDownload>(downloads_hb->get_child(i));
+ if (d && d->get_asset_id() == p_asset_id) {
+ return d;
+ }
+ }
+
+ return nullptr;
+}
+
void EditorAssetLibrary::_install_external_asset(String p_zip_path, String p_title) {
emit_signal(SNAME("install_asset"), p_zip_path, p_title);
}
@@ -1354,6 +1382,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
} else {
filter->set_placeholder(TTR("Search assets (excluding templates, projects, and demos)"));
}
+ filter->set_clear_button_enabled(true);
search_hb->add_child(filter);
filter->set_h_size_flags(Control::SIZE_EXPAND_FILL);
filter->connect("text_changed", callable_mp(this, &EditorAssetLibrary::_search_text_changed));
@@ -1495,7 +1524,6 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
error_hb = memnew(HBoxContainer);
library_main->add_child(error_hb);
error_label = memnew(Label);
- error_label->add_theme_color_override("color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
error_hb->add_child(error_label);
error_tr = memnew(TextureRect);
error_tr->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index d797608c24..29d26411f3 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -39,6 +39,7 @@
#include "scene/gui/grid_container.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/link_button.h"
+#include "scene/gui/margin_container.h"
#include "scene/gui/option_button.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/progress_bar.h"
@@ -126,15 +127,16 @@ public:
EditorAssetLibraryItemDescription();
};
-class EditorAssetLibraryItemDownload : public PanelContainer {
- GDCLASS(EditorAssetLibraryItemDownload, PanelContainer);
+class EditorAssetLibraryItemDownload : public MarginContainer {
+ GDCLASS(EditorAssetLibraryItemDownload, MarginContainer);
+ PanelContainer *panel;
TextureRect *icon;
Label *title;
ProgressBar *progress;
- Button *install;
- Button *retry;
- TextureButton *dismiss;
+ Button *install_button;
+ Button *retry_button;
+ TextureButton *dismiss_button;
AcceptDialog *download_error;
HTTPRequest *download;
@@ -151,7 +153,6 @@ class EditorAssetLibraryItemDownload : public PanelContainer {
EditorAssetInstaller *asset_installer;
void _close();
- void _install();
void _make_request();
void _http_download_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
@@ -163,6 +164,10 @@ public:
void set_external_install(bool p_enable) { external_install = p_enable; }
int get_asset_id() { return asset_id; }
void configure(const String &p_title, int p_asset_id, const Ref<Texture2D> &p_preview, const String &p_download_url, const String &p_sha256_hash);
+
+ bool can_install() const;
+ void install();
+
EditorAssetLibraryItemDownload();
};
@@ -286,6 +291,7 @@ class EditorAssetLibrary : public PanelContainer {
void _api_request(const String &p_request, RequestType p_request_type, const String &p_arguments = "");
void _http_request_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
void _filter_debounce_timer_timeout();
+ EditorAssetLibraryItemDownload *_get_asset_in_progress(int p_asset_id) const;
void _repository_changed(int p_repository_id);
void _support_toggled(int p_support);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index cb84e7ea65..d496804bf2 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -51,6 +51,7 @@
#include "scene/gui/grid_container.h"
#include "scene/gui/nine_patch_rect.h"
#include "scene/gui/subviewport_container.h"
+#include "scene/gui/view_panner.h"
#include "scene/main/canvas_layer.h"
#include "scene/main/window.h"
#include "scene/resources/packed_scene.h"
@@ -881,10 +882,39 @@ void CanvasItemEditor::_selection_menu_hide() {
}
void CanvasItemEditor::_add_node_pressed(int p_result) {
- if (p_result == AddNodeOption::ADD_NODE) {
- editor->get_scene_tree_dock()->open_add_child_dialog();
- } else if (p_result == AddNodeOption::ADD_INSTANCE) {
- editor->get_scene_tree_dock()->open_instance_child_dialog();
+ List<Node *> nodes_to_move;
+
+ switch (p_result) {
+ case ADD_NODE: {
+ SceneTreeDock::get_singleton()->open_add_child_dialog();
+ } break;
+ case ADD_INSTANCE: {
+ SceneTreeDock::get_singleton()->open_instance_child_dialog();
+ } break;
+ case ADD_PASTE: {
+ nodes_to_move = SceneTreeDock::get_singleton()->paste_nodes();
+ [[fallthrough]];
+ }
+ case ADD_MOVE: {
+ if (p_result == ADD_MOVE) {
+ nodes_to_move = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list();
+ }
+ if (nodes_to_move.is_empty()) {
+ return;
+ }
+
+ undo_redo->create_action(TTR("Move Node(s) to Position"));
+ for (Node *node : nodes_to_move) {
+ CanvasItem *ci = Object::cast_to<CanvasItem>(node);
+ if (ci) {
+ Transform2D xform = ci->get_global_transform_with_canvas().affine_inverse() * ci->get_transform();
+ undo_redo->add_do_method(ci, "_edit_set_position", xform.xform(node_create_position));
+ undo_redo->add_undo_method(ci, "_edit_set_position", ci->_edit_get_position());
+ }
+ }
+ undo_redo->commit_action();
+ _reset_create_position();
+ } break;
}
}
@@ -1116,77 +1146,15 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve
}
bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bool p_already_accepted) {
- Ref<InputEventMouseButton> b = p_event;
- if (b.is_valid() && !p_already_accepted) {
- const bool pan_on_scroll = bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan")) && !b->is_ctrl_pressed();
-
- if (pan_on_scroll) {
- // Perform horizontal scrolling first so we can check for Shift being held.
- if (b->is_pressed() &&
- (b->get_button_index() == MouseButton::WHEEL_LEFT || (b->is_shift_pressed() && b->get_button_index() == MouseButton::WHEEL_UP))) {
- // Pan left
- view_offset.x -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
- update_viewport();
- return true;
- }
-
- if (b->is_pressed() &&
- (b->get_button_index() == MouseButton::WHEEL_RIGHT || (b->is_shift_pressed() && b->get_button_index() == MouseButton::WHEEL_DOWN))) {
- // Pan right
- view_offset.x += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
- update_viewport();
- return true;
- }
- }
-
- if (b->is_pressed() && b->get_button_index() == MouseButton::WHEEL_DOWN) {
- // Scroll or pan down
- if (pan_on_scroll) {
- view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
- update_viewport();
- } else {
- zoom_widget->set_zoom_by_increments(-1, Input::get_singleton()->is_key_pressed(Key::ALT));
- if (!Math::is_equal_approx(b->get_factor(), 1.0f)) {
- // Handle high-precision (analog) scrolling.
- zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * b->get_factor() + 1.f));
- }
- _zoom_on_position(zoom_widget->get_zoom(), b->get_position());
- }
- return true;
- }
-
- if (b->is_pressed() && b->get_button_index() == MouseButton::WHEEL_UP) {
- // Scroll or pan up
- if (pan_on_scroll) {
- view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
- update_viewport();
- } else {
- zoom_widget->set_zoom_by_increments(1, Input::get_singleton()->is_key_pressed(Key::ALT));
- if (!Math::is_equal_approx(b->get_factor(), 1.0f)) {
- // Handle high-precision (analog) scrolling.
- zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * b->get_factor() + 1.f));
- }
- _zoom_on_position(zoom_widget->get_zoom(), b->get_position());
- }
- return true;
- }
-
- if (!panning) {
- if (b->is_pressed() &&
- (b->get_button_index() == MouseButton::MIDDLE ||
- (b->get_button_index() == MouseButton::LEFT && tool == TOOL_PAN) ||
- (b->get_button_index() == MouseButton::LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_panning") && pan_pressed))) {
- // Pan the viewport
- panning = true;
- }
- }
+ panner->set_force_drag(tool == TOOL_PAN);
+ bool panner_active = panner->gui_input(p_event, warped_panning ? viewport->get_global_rect() : Rect2());
+ if (panner->is_panning() != pan_pressed) {
+ pan_pressed = panner->is_panning();
+ _update_cursor();
+ }
- if (panning) {
- if (!b->is_pressed() && (pan_on_scroll || (b->get_button_index() != MouseButton::WHEEL_DOWN && b->get_button_index() != MouseButton::WHEEL_UP))) {
- // Stop panning the viewport (for any mouse button press except zooming)
- panning = false;
- }
- }
+ if (panner_active) {
+ return true;
}
Ref<InputEventKey> k = p_event;
@@ -1214,44 +1182,6 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
_update_zoom(16.0 * MAX(1, EDSCALE));
}
}
-
- bool is_pan_key = pan_view_shortcut.is_valid() && pan_view_shortcut->matches_event(p_event);
-
- if (is_pan_key && (EditorSettings::get_singleton()->get("editors/2d/simple_panning") || drag_type != DRAG_NONE)) {
- if (!panning) {
- if (k->is_pressed() && !k->is_echo()) {
- //Pan the viewport
- panning = true;
- }
- } else {
- if (!k->is_pressed()) {
- // Stop panning the viewport (for any mouse button press)
- panning = false;
- }
- }
- }
-
- if (is_pan_key && pan_pressed != k->is_pressed()) {
- pan_pressed = k->is_pressed();
- _update_cursor();
- }
- }
-
- Ref<InputEventMouseMotion> m = p_event;
- if (m.is_valid()) {
- if (panning) {
- // Pan the viewport
- Point2i relative;
- if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) {
- relative = Input::get_singleton()->warp_mouse_motion(m, viewport->get_global_rect());
- } else {
- relative = m->get_relative();
- }
- view_offset.x -= relative.x / zoom;
- view_offset.y -= relative.y / zoom;
- update_viewport();
- return true;
- }
}
Ref<InputEventMagnifyGesture> magnify_gesture = p_event;
@@ -1277,7 +1207,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
}
// Pan gesture
- const Vector2 delta = (int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom) * pan_gesture->get_delta();
+ const Vector2 delta = (pan_speed / zoom) * pan_gesture->get_delta();
view_offset.x += delta.x;
view_offset.y += delta.y;
update_viewport();
@@ -1287,6 +1217,25 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
return false;
}
+void CanvasItemEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) {
+ _pan_callback(-p_scroll_vec * pan_speed);
+}
+
+void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec) {
+ view_offset.x -= p_scroll_vec.x / zoom;
+ view_offset.y -= p_scroll_vec.y / zoom;
+ update_viewport();
+}
+
+void CanvasItemEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) {
+ zoom_widget->set_zoom_by_increments(-1, p_alt);
+ if (!Math::is_equal_approx(p_scroll_vec.y, (real_t)1.0)) {
+ // Handle high-precision (analog) scrolling.
+ zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * p_scroll_vec.y + 1.f));
+ }
+ _zoom_on_position(zoom_widget->get_zoom(), p_origin);
+}
+
bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> m = p_event;
Ref<InputEventMouseButton> b = p_event;
@@ -2274,14 +2223,30 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
}
if (b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::RIGHT) {
+ add_node_menu->clear();
+ add_node_menu->add_icon_item(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), TTR("Add Node Here"), ADD_NODE);
+ add_node_menu->add_icon_item(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), TTR("Instantiate Scene Here"), ADD_INSTANCE);
+ for (Node *node : SceneTreeDock::get_singleton()->get_node_clipboard()) {
+ if (Object::cast_to<CanvasItem>(node)) {
+ add_node_menu->add_icon_item(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), TTR("Paste Node(s) Here"), ADD_PASTE);
+ break;
+ }
+ }
+ for (Node *node : EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list()) {
+ if (Object::cast_to<CanvasItem>(node)) {
+ add_node_menu->add_icon_item(get_theme_icon(SNAME("ToolMove"), SNAME("EditorIcons")), TTR("Move Node(s) Here"), ADD_MOVE);
+ break;
+ }
+ }
+
add_node_menu->reset_size();
- add_node_menu->set_position(get_screen_transform().xform(get_local_mouse_position()));
+ add_node_menu->set_position(get_screen_transform().xform(b->get_position()));
add_node_menu->popup();
- node_create_position = transform.affine_inverse().xform((get_local_mouse_position()));
+ node_create_position = transform.affine_inverse().xform(b->get_position());
return true;
}
- if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && tool == TOOL_SELECT) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && tool == TOOL_SELECT && !panner->is_panning()) {
// Single item selection
Point2 click = transform.affine_inverse().xform(b->get_position());
@@ -2498,31 +2463,34 @@ bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) {
void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
bool accepted = false;
- if (EditorSettings::get_singleton()->get("editors/2d/simple_panning") || !pan_pressed) {
+ Ref<InputEventMouseButton> mb = p_event;
+ bool release_lmb = (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT); // Required to properly release some stuff (e.g. selection box) while panning.
+
+ if (EditorSettings::get_singleton()->get("editors/panning/simple_panning") || !pan_pressed || release_lmb) {
if ((accepted = _gui_input_rulers_and_guides(p_event))) {
- //printf("Rulers and guides\n");
+ // print_line("Rulers and guides");
} else if ((accepted = editor->get_editor_plugins_over()->forward_gui_input(p_event))) {
- //printf("Plugin\n");
+ // print_line("Plugin");
} else if ((accepted = _gui_input_open_scene_on_double_click(p_event))) {
- //printf("Open scene on double click\n");
+ // print_line("Open scene on double click");
} else if ((accepted = _gui_input_scale(p_event))) {
- //printf("Set scale\n");
+ // print_line("Set scale");
} else if ((accepted = _gui_input_pivot(p_event))) {
- //printf("Set pivot\n");
+ // print_line("Set pivot");
} else if ((accepted = _gui_input_resize(p_event))) {
- //printf("Resize\n");
+ // print_line("Resize");
} else if ((accepted = _gui_input_rotate(p_event))) {
- //printf("Rotate\n");
+ // print_line("Rotate");
} else if ((accepted = _gui_input_move(p_event))) {
- //printf("Move\n");
+ // print_line("Move");
} else if ((accepted = _gui_input_anchors(p_event))) {
- //printf("Anchors\n");
+ // print_line("Anchors");
} else if ((accepted = _gui_input_select(p_event))) {
- //printf("Selection\n");
+ // print_line("Selection");
} else if ((accepted = _gui_input_ruler_tool(p_event))) {
- //printf("Measure\n");
+ // print_line("Measure");
} else {
- //printf("Not accepted\n");
+ // print_line("Not accepted");
}
}
@@ -2539,7 +2507,7 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
_update_cursor();
// Grab focus
- if (!viewport->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) {
+ if (!viewport->has_focus() && (!get_viewport()->gui_get_focus_owner() || !get_viewport()->gui_get_focus_owner()->is_text_field())) {
viewport->call_deferred(SNAME("grab_focus"));
}
}
@@ -3517,7 +3485,7 @@ void CanvasItemEditor::_draw_axis() {
Color area_axis_color = EditorSettings::get_singleton()->get("editors/2d/viewport_border_color");
- Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
Vector2 screen_endpoints[4] = {
transform.xform(Vector2(0, 0)),
@@ -3935,6 +3903,10 @@ void CanvasItemEditor::_notification(int p_what) {
anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignWide"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_PRESET_WIDE);
anchor_mode_button->set_icon(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")));
+
+ panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/2d_editor_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
+ pan_speed = int(EditorSettings::get_singleton()->get("editors/panning/2d_editor_pan_speed"));
+ warped_panning = bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning"));
}
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
@@ -3977,7 +3949,7 @@ void CanvasItemEditor::_selection_changed() {
void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
Array selection = editor_selection->get_selected_nodes();
- if (selection.size() != 1 || (Node *)selection[0] != p_canvas_item) {
+ if (selection.size() != 1 || Object::cast_to<Node>(selection[0]) != p_canvas_item) {
drag_type = DRAG_NONE;
// Clear the selection
@@ -4010,7 +3982,7 @@ void CanvasItemEditor::_update_scrollbars() {
Size2 vmin = v_scroll->get_minimum_size();
// Get the visible frame.
- Size2 screen_rect = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ Size2 screen_rect = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
Rect2 local_rect = Rect2(Point2(), viewport->get_size() - Size2(vmin.width, hmin.height));
// Calculate scrollable area.
@@ -5129,8 +5101,22 @@ void CanvasItemEditor::remove_control_from_menu_panel(Control *p_control) {
hbc_context_menu->remove_child(p_control);
}
-HSplitContainer *CanvasItemEditor::get_palette_split() {
- return palette_split;
+void CanvasItemEditor::add_control_to_left_panel(Control *p_control) {
+ left_panel_split->add_child(p_control);
+ left_panel_split->move_child(p_control, 0);
+}
+
+void CanvasItemEditor::add_control_to_right_panel(Control *p_control) {
+ right_panel_split->add_child(p_control);
+ right_panel_split->move_child(p_control, 1);
+}
+
+void CanvasItemEditor::remove_control_from_left_panel(Control *p_control) {
+ left_panel_split->remove_child(p_control);
+}
+
+void CanvasItemEditor::remove_control_from_right_panel(Control *p_control) {
+ right_panel_split->remove_child(p_control);
}
VSplitContainer *CanvasItemEditor::get_bottom_split() {
@@ -5191,7 +5177,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
dragged_guide_index = -1;
is_hovering_h_guide = false;
is_hovering_v_guide = false;
- panning = false;
pan_pressed = false;
ruler_tool_active = false;
@@ -5207,8 +5192,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
editor_selection->connect("selection_changed", callable_mp((CanvasItem *)this, &CanvasItem::update));
editor_selection->connect("selection_changed", callable_mp(this, &CanvasItemEditor::_selection_changed));
- editor->get_scene_tree_dock()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created));
- editor->get_scene_tree_dock()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position));
+ SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created));
+ SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position));
editor->call_deferred(SNAME("connect"), "play_pressed", Callable(this, "_update_override_camera_button"), make_binds(true));
editor->call_deferred(SNAME("connect"), "stop_pressed", Callable(this, "_update_override_camera_button"), make_binds(false));
@@ -5221,12 +5206,16 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
add_child(bottom_split);
bottom_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- palette_split = memnew(HSplitContainer);
- bottom_split->add_child(palette_split);
- palette_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ left_panel_split = memnew(HSplitContainer);
+ bottom_split->add_child(left_panel_split);
+ left_panel_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ right_panel_split = memnew(HSplitContainer);
+ left_panel_split->add_child(right_panel_split);
+ right_panel_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
viewport_scrollable = memnew(Control);
- palette_split->add_child(viewport_scrollable);
+ right_panel_split->add_child(viewport_scrollable);
viewport_scrollable->set_mouse_filter(MOUSE_FILTER_PASS);
viewport_scrollable->set_clip_contents(true);
viewport_scrollable->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -5242,6 +5231,14 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
controls_vb = memnew(VBoxContainer);
controls_vb->set_begin(Point2(5, 5));
+ zoom_widget = memnew(EditorZoomWidget);
+ controls_vb->add_child(zoom_widget);
+ zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
+ zoom_widget->connect("zoom_changed", callable_mp(this, &CanvasItemEditor::_update_zoom));
+
+ panner.instantiate();
+ panner->set_callbacks(callable_mp(this, &CanvasItemEditor::_scroll_callback), callable_mp(this, &CanvasItemEditor::_pan_callback), callable_mp(this, &CanvasItemEditor::_zoom_callback));
+
viewport = memnew(CanvasItemEditorViewport(p_editor, this));
viewport_scrollable->add_child(viewport);
viewport->set_mouse_filter(MOUSE_FILTER_PASS);
@@ -5250,6 +5247,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
viewport->set_focus_mode(FOCUS_ALL);
viewport->connect("draw", callable_mp(this, &CanvasItemEditor::_draw_viewport));
viewport->connect("gui_input", callable_mp(this, &CanvasItemEditor::_gui_input_viewport));
+ viewport->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
h_scroll = memnew(HScrollBar);
viewport->add_child(h_scroll);
@@ -5263,11 +5261,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
viewport->add_child(controls_vb);
- zoom_widget = memnew(EditorZoomWidget);
- controls_vb->add_child(zoom_widget);
- zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
- zoom_widget->connect("zoom_changed", callable_mp(this, &CanvasItemEditor::_update_zoom));
-
updating_scroll = false;
// Add some margin to the left for better aesthetics.
@@ -5600,13 +5593,10 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
add_node_menu = memnew(PopupMenu);
add_child(add_node_menu);
- add_node_menu->add_icon_item(editor->get_scene_tree_dock()->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), TTR("Add Node Here"));
- add_node_menu->add_icon_item(editor->get_scene_tree_dock()->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), TTR("Instance Scene Here"));
add_node_menu->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_add_node_pressed));
multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), Key::KP_MULTIPLY);
divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), Key::KP_DIVIDE);
- pan_view_shortcut = ED_SHORTCUT("canvas_item_editor/pan_view", TTR("Pan View"), Key::SPACE);
skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true);
singleton = this;
@@ -6112,7 +6102,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte
target_node = nullptr;
editor = p_node;
- editor_data = editor->get_scene_tree_dock()->get_editor_data();
+ editor_data = SceneTreeDock::get_singleton()->get_editor_data();
canvas_item_editor = p_canvas_item_editor;
preview_node = memnew(Control);
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 8bba5130d4..9fa44bfb25 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -42,6 +42,7 @@
#include "scene/main/canvas_item.h"
class CanvasItemEditorViewport;
+class ViewPanner;
class CanvasItemEditorSelectedItem : public Object {
GDCLASS(CanvasItemEditorSelectedItem, Object);
@@ -83,6 +84,8 @@ public:
enum AddNodeOption {
ADD_NODE,
ADD_INSTANCE,
+ ADD_PASTE,
+ ADD_MOVE,
};
private:
@@ -276,7 +279,6 @@ private:
bool key_pos;
bool key_rot;
bool key_scale;
- bool panning;
bool pan_pressed;
bool ruler_tool_active;
@@ -402,7 +404,13 @@ private:
Ref<Shortcut> set_pivot_shortcut;
Ref<Shortcut> multiply_grid_step_shortcut;
Ref<Shortcut> divide_grid_step_shortcut;
- Ref<Shortcut> pan_view_shortcut;
+
+ Ref<ViewPanner> panner;
+ bool warped_panning = true;
+ int pan_speed = 20;
+ void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
+ void _pan_callback(Vector2 p_scroll_vec);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt);
bool _is_node_locked(const Node *p_node);
bool _is_node_movable(const Node *p_node, bool p_popup_warning = false);
@@ -527,7 +535,8 @@ private:
void _update_override_camera_button(bool p_game_running);
- HSplitContainer *palette_split;
+ HSplitContainer *left_panel_split;
+ HSplitContainer *right_panel_split;
VSplitContainer *bottom_split;
void _update_context_menu_stylebox();
@@ -571,7 +580,12 @@ public:
void add_control_to_menu_panel(Control *p_control);
void remove_control_from_menu_panel(Control *p_control);
- HSplitContainer *get_palette_split();
+ void add_control_to_left_panel(Control *p_control);
+ void remove_control_from_left_panel(Control *p_control);
+
+ void add_control_to_right_panel(Control *p_control);
+ void remove_control_from_right_panel(Control *p_control);
+
VSplitContainer *get_bottom_split();
Control *get_viewport_control() { return viewport; }
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index 6b93a1872d..06be9d678f 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -110,9 +110,9 @@ void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Convert to CPUParticles2D"));
- ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", particles, cpu_particles, true, false);
+ ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", particles, cpu_particles, true, false);
ur->add_do_reference(cpu_particles);
- ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, particles, false, false);
+ ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, particles, false, false);
ur->add_undo_reference(particles);
ur->commit_action();
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index 0057566603..087b0a26b7 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -269,9 +269,9 @@ void GPUParticles3DEditor::_menu_option(int p_option) {
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Convert to CPUParticles3D"));
- ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, cpu_particles, true, false);
+ ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, cpu_particles, true, false);
ur->add_do_reference(cpu_particles);
- ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, node, false, false);
+ ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, node, false, false);
ur->add_undo_reference(node);
ur->commit_action();
diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp
index d82d0c6ffc..e47381b8a0 100644
--- a/editor/plugins/mesh_library_editor_plugin.cpp
+++ b/editor/plugins/mesh_library_editor_plugin.cpp
@@ -136,9 +136,11 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
continue;
}
- //Transform3D shape_transform = sb->shape_owner_get_transform(E);
-
- //shape_transform.set_origin(shape_transform.get_origin() - phys_offset);
+ Transform3D shape_transform;
+ if (p_apply_xforms) {
+ shape_transform = mi->get_transform();
+ }
+ shape_transform *= sb->get_transform() * sb->shape_owner_get_transform(E);
for (int k = 0; k < sb->shape_owner_get_shape_count(E); k++) {
Ref<Shape3D> collision = sb->shape_owner_get_shape(E, k);
@@ -147,7 +149,7 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
}
MeshLibrary::ShapeData shape_data;
shape_data.shape = collision;
- shape_data.local_transform = sb->get_transform() * sb->shape_owner_get_transform(E);
+ shape_data.local_transform = shape_transform;
collisions.push_back(shape_data);
}
}
@@ -226,7 +228,7 @@ void MeshLibraryEditor::_menu_cbk(int p_option) {
mesh_library->create_item(mesh_library->get_last_unused_item_id());
} break;
case MENU_OPTION_REMOVE_ITEM: {
- String p = editor->get_inspector()->get_selected_path();
+ String p = InspectorDock::get_inspector_singleton()->get_selected_path();
if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/") >= 3) {
to_erase = p.get_slice("/", 3).to_int();
cd_remove->set_text(vformat(TTR("Remove item %d?"), to_erase));
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 6ea8fba9b5..0c0188e8d1 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1203,7 +1203,7 @@ Transform3D Node3DEditorViewport::_compute_transform(TransformMode p_mode, const
}
void Node3DEditorViewport::_surface_mouse_enter() {
- if (!surface->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) {
+ if (!surface->has_focus() && (!get_viewport()->gui_get_focus_owner() || !get_viewport()->gui_get_focus_owner()->is_text_field())) {
surface->grab_focus();
}
}
@@ -2733,7 +2733,7 @@ void Node3DEditorViewport::_notification(int p_what) {
_update_freelook(delta);
- Node *scene_root = editor->get_scene_tree_dock()->get_editor_data()->get_edited_scene_root();
+ Node *scene_root = SceneTreeDock::get_singleton()->get_editor_data()->get_edited_scene_root();
if (previewing_cinema && scene_root != nullptr) {
Camera3D *cam = scene_root->get_viewport()->get_camera_3d();
if (cam != nullptr && cam != previewing) {
@@ -3064,7 +3064,7 @@ void Node3DEditorViewport::_draw() {
Math::round(2 * EDSCALE));
}
if (previewing) {
- Size2 ss = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ Size2 ss = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
float aspect = ss.aspect();
Size2 s = get_size();
@@ -4284,7 +4284,7 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_
target_node = root_node;
} else {
// Create a root node so we can add child nodes to it.
- EditorNode::get_singleton()->get_scene_tree_dock()->add_root_node(memnew(Node3D));
+ SceneTreeDock::get_singleton()->add_root_node(memnew(Node3D));
target_node = get_tree()->get_edited_scene_root();
}
} else {
@@ -4311,7 +4311,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
index = p_index;
editor = p_editor;
- editor_data = editor->get_scene_tree_dock()->get_editor_data();
+ editor_data = SceneTreeDock::get_singleton()->get_editor_data();
editor_selection = editor->get_editor_selection();
undo_redo = editor->get_undo_redo();
@@ -6623,7 +6623,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
// For snapping to be performed, there must be solid geometry under at least one of the selected nodes.
// We need to check this before snapping to register the undo/redo action only if needed.
for (int i = 0; i < keys.size(); i++) {
- Node *node = keys[i];
+ Node *node = Object::cast_to<Node>(keys[i]);
Node3D *sp = Object::cast_to<Node3D>(node);
Dictionary d = snap_data[node];
Vector3 from = d["from"];
@@ -6645,7 +6645,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
// Perform snapping if at least one node can be snapped
for (int i = 0; i < keys.size(); i++) {
- Node *node = keys[i];
+ Node *node = Object::cast_to<Node>(keys[i]);
Node3D *sp = Object::cast_to<Node3D>(node);
Dictionary d = snap_data[node];
Vector3 from = d["from"];
@@ -6704,7 +6704,7 @@ void Node3DEditor::_add_sun_to_scene(bool p_already_added_environment) {
Node *base = get_tree()->get_edited_scene_root();
if (!base) {
// Create a root node so we can add child nodes to it.
- EditorNode::get_singleton()->get_scene_tree_dock()->add_root_node(memnew(Node3D));
+ SceneTreeDock::get_singleton()->add_root_node(memnew(Node3D));
base = get_tree()->get_edited_scene_root();
}
ERR_FAIL_COND(!base);
@@ -6732,7 +6732,7 @@ void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) {
Node *base = get_tree()->get_edited_scene_root();
if (!base) {
// Create a root node so we can add child nodes to it.
- EditorNode::get_singleton()->get_scene_tree_dock()->add_root_node(memnew(Node3D));
+ SceneTreeDock::get_singleton()->add_root_node(memnew(Node3D));
base = get_tree()->get_edited_scene_root();
}
ERR_FAIL_COND(!base);
@@ -6790,7 +6790,7 @@ void Node3DEditor::_notification(int p_what) {
get_tree()->connect("node_removed", callable_mp(this, &Node3DEditor::_node_removed));
get_tree()->connect("node_added", callable_mp(this, &Node3DEditor::_node_added));
- EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->connect("node_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons));
+ SceneTreeDock::get_singleton()->get_tree_editor()->connect("node_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons));
editor_selection->connect("selection_changed", callable_mp(this, &Node3DEditor::_selection_changed));
editor->connect("stop_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button), make_binds(false));
@@ -6880,8 +6880,46 @@ VSplitContainer *Node3DEditor::get_shader_split() {
return shader_split;
}
-HSplitContainer *Node3DEditor::get_palette_split() {
- return palette_split;
+void Node3DEditor::add_control_to_left_panel(Control *p_control) {
+ left_panel_split->add_child(p_control);
+ left_panel_split->move_child(p_control, 0);
+}
+
+void Node3DEditor::add_control_to_right_panel(Control *p_control) {
+ right_panel_split->add_child(p_control);
+ right_panel_split->move_child(p_control, 1);
+}
+
+void Node3DEditor::remove_control_from_left_panel(Control *p_control) {
+ left_panel_split->remove_child(p_control);
+}
+
+void Node3DEditor::remove_control_from_right_panel(Control *p_control) {
+ right_panel_split->remove_child(p_control);
+}
+
+void Node3DEditor::move_control_to_left_panel(Control *p_control) {
+ ERR_FAIL_NULL(p_control);
+ if (p_control->get_parent() == left_panel_split) {
+ return;
+ }
+
+ ERR_FAIL_COND(p_control->get_parent() != right_panel_split);
+ right_panel_split->remove_child(p_control);
+
+ add_control_to_left_panel(p_control);
+}
+
+void Node3DEditor::move_control_to_right_panel(Control *p_control) {
+ ERR_FAIL_NULL(p_control);
+ if (p_control->get_parent() == right_panel_split) {
+ return;
+ }
+
+ ERR_FAIL_COND(p_control->get_parent() != left_panel_split);
+ left_panel_split->remove_child(p_control);
+
+ add_control_to_right_panel(p_control);
}
void Node3DEditor::_request_gizmo(Object *p_obj) {
@@ -7534,13 +7572,17 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
/* REST OF MENU */
- palette_split = memnew(HSplitContainer);
- palette_split->set_v_size_flags(SIZE_EXPAND_FILL);
- vbc->add_child(palette_split);
+ left_panel_split = memnew(HSplitContainer);
+ left_panel_split->set_v_size_flags(SIZE_EXPAND_FILL);
+ vbc->add_child(left_panel_split);
+
+ right_panel_split = memnew(HSplitContainer);
+ right_panel_split->set_v_size_flags(SIZE_EXPAND_FILL);
+ left_panel_split->add_child(right_panel_split);
shader_split = memnew(VSplitContainer);
shader_split->set_h_size_flags(SIZE_EXPAND_FILL);
- palette_split->add_child(shader_split);
+ right_panel_split->add_child(shader_split);
viewport_base = memnew(Node3DEditorViewportContainer);
shader_split->add_child(viewport_base);
viewport_base->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -8001,7 +8043,6 @@ Node3DEditorPlugin::Node3DEditorPlugin(EditorNode *p_node) {
editor->get_main_control()->add_child(spatial_editor);
spatial_editor->hide();
- spatial_editor->connect("transform_key_request", Callable(editor->get_inspector_dock(), "_transform_keyed"));
}
Node3DEditorPlugin::~Node3DEditorPlugin() {
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index da560f4d83..20a782c8a8 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -527,7 +527,8 @@ private:
Node3DEditorViewportContainer *viewport_base;
Node3DEditorViewport *viewports[VIEWPORTS_COUNT];
VSplitContainer *shader_split;
- HSplitContainer *palette_split;
+ HSplitContainer *left_panel_split;
+ HSplitContainer *right_panel_split;
/////
@@ -801,8 +802,16 @@ public:
void add_control_to_menu_panel(Control *p_control);
void remove_control_from_menu_panel(Control *p_control);
+ void add_control_to_left_panel(Control *p_control);
+ void remove_control_from_left_panel(Control *p_control);
+
+ void add_control_to_right_panel(Control *p_control);
+ void remove_control_from_right_panel(Control *p_control);
+
+ void move_control_to_left_panel(Control *p_control);
+ void move_control_to_right_panel(Control *p_control);
+
VSplitContainer *get_shader_split();
- HSplitContainer *get_palette_split();
Node3D *get_single_selected_node() { return selected; }
bool is_current_selected_gizmo(const EditorNode3DGizmo *p_gizmo);
diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
index c50673559c..702bc4a8ce 100644
--- a/editor/plugins/path_2d_editor_plugin.cpp
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -38,14 +38,13 @@
void Path2DEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
- //button_create->set_icon( get_icon("Edit","EditorIcons"));
- //button_edit->set_icon( get_icon("MovePoint","EditorIcons"));
- //set_pressed_button(button_edit);
- //button_edit->set_pressed(true);
-
- } break;
- case NOTIFICATION_PHYSICS_PROCESS: {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ curve_edit->set_icon(get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
+ curve_edit_curve->set_icon(get_theme_icon(SNAME("CurveCurve"), SNAME("EditorIcons")));
+ curve_create->set_icon(get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons")));
+ curve_del->set_icon(get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
+ curve_close->set_icon(get_theme_icon(SNAME("CurveClose"), SNAME("EditorIcons")));
} break;
}
}
@@ -532,41 +531,41 @@ Path2DEditor::Path2DEditor(EditorNode *p_editor) {
sep = memnew(VSeparator);
base_hb->add_child(sep);
+
curve_edit = memnew(Button);
curve_edit->set_flat(true);
- curve_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
curve_edit->set_toggle_mode(true);
curve_edit->set_focus_mode(Control::FOCUS_NONE);
curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Click: Add Point") + "\n" + TTR("Left Click: Split Segment (in curve)") + "\n" + TTR("Right Click: Delete Point"));
curve_edit->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected), varray(MODE_EDIT));
base_hb->add_child(curve_edit);
+
curve_edit_curve = memnew(Button);
curve_edit_curve->set_flat(true);
- curve_edit_curve->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveCurve"), SNAME("EditorIcons")));
curve_edit_curve->set_toggle_mode(true);
curve_edit_curve->set_focus_mode(Control::FOCUS_NONE);
curve_edit_curve->set_tooltip(TTR("Select Control Points (Shift+Drag)"));
curve_edit_curve->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected), varray(MODE_EDIT_CURVE));
base_hb->add_child(curve_edit_curve);
+
curve_create = memnew(Button);
curve_create->set_flat(true);
- curve_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons")));
curve_create->set_toggle_mode(true);
curve_create->set_focus_mode(Control::FOCUS_NONE);
curve_create->set_tooltip(TTR("Add Point (in empty space)"));
curve_create->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected), varray(MODE_CREATE));
base_hb->add_child(curve_create);
+
curve_del = memnew(Button);
curve_del->set_flat(true);
- curve_del->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
curve_del->set_toggle_mode(true);
curve_del->set_focus_mode(Control::FOCUS_NONE);
curve_del->set_tooltip(TTR("Delete Point"));
curve_del->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected), varray(MODE_DELETE));
base_hb->add_child(curve_del);
+
curve_close = memnew(Button);
curve_close->set_flat(true);
- curve_close->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveClose"), SNAME("EditorIcons")));
curve_close->set_focus_mode(Control::FOCUS_NONE);
curve_close->set_tooltip(TTR("Close Curve"));
curve_close->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected), varray(ACTION_CLOSE));
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index cb62dcdccc..7cc926f06c 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -539,12 +539,29 @@ void Path3DEditorPlugin::_handle_option_pressed(int p_option) {
}
}
+void Path3DEditorPlugin::_update_theme() {
+ // TODO: Split the EditorPlugin instance from the UI instance and connect this properly.
+ // See the 2D path editor for inspiration.
+ curve_edit->set_icon(Node3DEditor::get_singleton()->get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
+ curve_create->set_icon(Node3DEditor::get_singleton()->get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons")));
+ curve_del->set_icon(Node3DEditor::get_singleton()->get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
+ curve_close->set_icon(Node3DEditor::get_singleton()->get_theme_icon(SNAME("CurveClose"), SNAME("EditorIcons")));
+}
+
void Path3DEditorPlugin::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- curve_create->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed), make_binds(0));
- curve_edit->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed), make_binds(1));
- curve_del->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed), make_binds(2));
- curve_close->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_close_curve));
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ curve_create->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed), make_binds(0));
+ curve_edit->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed), make_binds(1));
+ curve_del->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed), make_binds(2));
+ curve_close->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_close_curve));
+
+ _update_theme();
+ } break;
+
+ case NOTIFICATION_READY: {
+ Node3DEditor::get_singleton()->connect("theme_changed", callable_mp(this, &Path3DEditorPlugin::_update_theme));
+ } break;
}
}
@@ -567,33 +584,33 @@ Path3DEditorPlugin::Path3DEditorPlugin(EditorNode *p_node) {
sep = memnew(VSeparator);
sep->hide();
Node3DEditor::get_singleton()->add_control_to_menu_panel(sep);
+
curve_edit = memnew(Button);
curve_edit->set_flat(true);
- curve_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
curve_edit->set_toggle_mode(true);
curve_edit->hide();
curve_edit->set_focus_mode(Control::FOCUS_NONE);
curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point"));
Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_edit);
+
curve_create = memnew(Button);
curve_create->set_flat(true);
- curve_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons")));
curve_create->set_toggle_mode(true);
curve_create->hide();
curve_create->set_focus_mode(Control::FOCUS_NONE);
curve_create->set_tooltip(TTR("Add Point (in empty space)") + "\n" + TTR("Split Segment (in curve)"));
Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_create);
+
curve_del = memnew(Button);
curve_del->set_flat(true);
- curve_del->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
curve_del->set_toggle_mode(true);
curve_del->hide();
curve_del->set_focus_mode(Control::FOCUS_NONE);
curve_del->set_tooltip(TTR("Delete Point"));
Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_del);
+
curve_close = memnew(Button);
curve_close->set_flat(true);
- curve_close->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveClose"), SNAME("EditorIcons")));
curve_close->hide();
curve_close->set_focus_mode(Control::FOCUS_NONE);
curve_close->set_tooltip(TTR("Close Curve"));
diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h
index adda648868..b877e2ae17 100644
--- a/editor/plugins/path_3d_editor_plugin.h
+++ b/editor/plugins/path_3d_editor_plugin.h
@@ -80,6 +80,8 @@ class Path3DEditorPlugin : public EditorPlugin {
Path3D *path;
+ void _update_theme();
+
void _mode_changed(int p_idx);
void _close_curve();
void _handle_option_pressed(int p_option);
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index e272b96778..b116f8ff6d 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -38,6 +38,8 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "scene/2d/skeleton_2d.h"
+#include "scene/gui/scroll_container.h"
+#include "scene/gui/view_panner.h"
Node2D *Polygon2DEditor::_get_node() const {
return node;
@@ -63,9 +65,8 @@ int Polygon2DEditor::_get_polygon_count() const {
void Polygon2DEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
- case NOTIFICATION_THEME_CHANGED: {
- uv_edit_draw->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
- bone_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ uv_panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
} break;
case NOTIFICATION_READY: {
button_uv->set_icon(get_theme_icon(SNAME("Uv"), SNAME("EditorIcons")));
@@ -88,6 +89,11 @@ void Polygon2DEditor::_notification(int p_what) {
uv_vscroll->set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE);
uv_hscroll->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE);
+ [[fallthrough]];
+ }
+ case NOTIFICATION_THEME_CHANGED: {
+ uv_edit_draw->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ bone_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible()) {
@@ -440,6 +446,11 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
return;
}
+ if (uv_panner->gui_input(p_input)) {
+ accept_event();
+ return;
+ }
+
Transform2D mtx;
mtx.elements[2] = -uv_draw_ofs;
mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom));
@@ -462,7 +473,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_move_current = uv_mode;
if (uv_move_current == UV_MODE_CREATE) {
if (!uv_create) {
- points_prev.resize(0);
+ points_prev.clear();
Vector2 tuv = mtx.affine_inverse().xform(snap_point(mb->get_position()));
points_prev.push_back(tuv);
uv_create_to = tuv;
@@ -671,7 +682,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
}
polygon_create.clear();
- } else if (polygon_create.find(closest) == -1) {
+ } else if (!polygon_create.has(closest)) {
//add temporarily if not exists
polygon_create.push_back(closest);
}
@@ -767,23 +778,13 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
}
uv_edit_draw->update();
-
- } else if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) {
- uv_zoom->set_value(uv_zoom->get_value() / (1 - (0.1 * mb->get_factor())));
- } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed()) {
- uv_zoom->set_value(uv_zoom->get_value() * (1 - (0.1 * mb->get_factor())));
}
}
Ref<InputEventMouseMotion> mm = p_input;
if (mm.is_valid()) {
- if ((mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE || Input::get_singleton()->is_key_pressed(Key::SPACE)) {
- Vector2 drag = mm->get_relative();
- uv_hscroll->set_value(uv_hscroll->get_value() - drag.x);
- uv_vscroll->set_value(uv_vscroll->get_value() - drag.y);
-
- } else if (uv_drag) {
+ if (uv_drag) {
Vector2 uv_drag_to = mm->get_position();
uv_drag_to = snap_point(uv_drag_to); // FIXME: Only works correctly with 'UV_MODE_EDIT_POINT', it's imprecise with the rest.
Vector2 drag = mtx.affine_inverse().xform(uv_drag_to) - mtx.affine_inverse().xform(uv_drag_from);
@@ -925,6 +926,23 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
}
}
+void Polygon2DEditor::_uv_scroll_callback(Vector2 p_scroll_vec, bool p_alt) {
+ _uv_pan_callback(-p_scroll_vec * 32);
+}
+
+void Polygon2DEditor::_uv_pan_callback(Vector2 p_scroll_vec) {
+ uv_hscroll->set_value(uv_hscroll->get_value() - p_scroll_vec.x);
+ uv_vscroll->set_value(uv_vscroll->get_value() - p_scroll_vec.y);
+}
+
+void Polygon2DEditor::_uv_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) {
+ if (p_scroll_vec.y < 0) {
+ uv_zoom->set_value(uv_zoom->get_value() / (1 - (0.1 * Math::abs(p_scroll_vec.y))));
+ } else {
+ uv_zoom->set_value(uv_zoom->get_value() * (1 - (0.1 * Math::abs(p_scroll_vec.y))));
+ }
+}
+
void Polygon2DEditor::_uv_scroll_changed(real_t) {
if (updating_uv_scroll) {
return;
@@ -1448,8 +1466,13 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
bone_scroll_vb = memnew(VBoxContainer);
bone_scroll->add_child(bone_scroll_vb);
+ uv_panner.instantiate();
+ uv_panner->set_callbacks(callable_mp(this, &Polygon2DEditor::_uv_scroll_callback), callable_mp(this, &Polygon2DEditor::_uv_pan_callback), callable_mp(this, &Polygon2DEditor::_uv_zoom_callback));
+
uv_edit_draw->connect("draw", callable_mp(this, &Polygon2DEditor::_uv_draw));
uv_edit_draw->connect("gui_input", callable_mp(this, &Polygon2DEditor::_uv_input));
+ uv_edit_draw->connect("focus_exited", callable_mp(uv_panner.ptr(), &ViewPanner::release_pan_key));
+ uv_edit_draw->set_focus_mode(FOCUS_CLICK);
uv_draw_zoom = 1.0;
point_drag_index = -1;
uv_drag = false;
diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h
index a04179dcad..0f10b6b645 100644
--- a/editor/plugins/polygon_2d_editor_plugin.h
+++ b/editor/plugins/polygon_2d_editor_plugin.h
@@ -32,7 +32,9 @@
#define POLYGON_2D_EDITOR_PLUGIN_H
#include "editor/plugins/abstract_polygon_2d_editor.h"
-#include "scene/gui/scroll_container.h"
+
+class ViewPanner;
+class ScrollContainer;
class Polygon2DEditor : public AbstractPolygon2DEditor {
GDCLASS(Polygon2DEditor, AbstractPolygon2DEditor);
@@ -78,6 +80,11 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
MenuButton *uv_menu;
TextureRect *uv_icon_zoom;
+ Ref<ViewPanner> uv_panner;
+ void _uv_scroll_callback(Vector2 p_scroll_vec, bool p_alt);
+ void _uv_pan_callback(Vector2 p_scroll_vec);
+ void _uv_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt);
+
VBoxContainer *bone_scroll_main_vb;
ScrollContainer *bone_scroll;
VBoxContainer *bone_scroll_vb;
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 03ed0e0ef2..f1f67eeea0 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -677,6 +677,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->set_item_disabled(recent_scripts->get_item_id(recent_scripts->get_item_count() - 1), rc.is_empty());
recent_scripts->set_as_minsize();
}
@@ -1421,7 +1422,7 @@ void ScriptEditor::_menu_option(int p_option) {
path = path.get_slice("::", 0); // Show the scene instead.
}
- FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
+ FileSystemDock *file_system_dock = FileSystemDock::get_singleton();
file_system_dock->navigate_to_path(path);
// Ensure that the FileSystem dock is visible.
TabContainer *tab_container = (TabContainer *)file_system_dock->get_parent_control();
@@ -1540,6 +1541,51 @@ void ScriptEditor::_show_save_theme_as_dialog() {
file_dialog->set_title(TTR("Save Theme As..."));
}
+bool ScriptEditor::_has_docs_tab() const {
+ const int child_count = tab_container->get_child_count();
+ for (int i = 0; i < child_count; i++) {
+ if (Object::cast_to<EditorHelp>(tab_container->get_child(i))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScriptEditor::_has_script_tab() const {
+ const int child_count = tab_container->get_child_count();
+ for (int i = 0; i < child_count; i++) {
+ if (Object::cast_to<ScriptEditorBase>(tab_container->get_child(i))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void ScriptEditor::_prepare_file_menu() {
+ PopupMenu *menu = file_menu->get_popup();
+ const bool current_is_doc = _get_current_editor() == nullptr;
+
+ menu->set_item_disabled(menu->get_item_index(FILE_REOPEN_CLOSED), previous_scripts.is_empty());
+
+ menu->set_item_disabled(menu->get_item_index(FILE_SAVE), current_is_doc);
+ menu->set_item_disabled(menu->get_item_index(FILE_SAVE_AS), current_is_doc);
+ menu->set_item_disabled(menu->get_item_index(FILE_SAVE_ALL), !_has_script_tab());
+
+ menu->set_item_disabled(menu->get_item_index(FILE_TOOL_RELOAD_SOFT), current_is_doc);
+ menu->set_item_disabled(menu->get_item_index(FILE_COPY_PATH), current_is_doc);
+ menu->set_item_disabled(menu->get_item_index(SHOW_IN_FILE_SYSTEM), current_is_doc);
+
+ menu->set_item_disabled(menu->get_item_index(WINDOW_PREV), history_pos <= 0);
+ menu->set_item_disabled(menu->get_item_index(WINDOW_NEXT), history_pos >= history.size() - 1);
+
+ menu->set_item_disabled(menu->get_item_index(FILE_CLOSE), tab_container->get_child_count() < 1);
+ menu->set_item_disabled(menu->get_item_index(CLOSE_ALL), tab_container->get_child_count() < 1);
+ menu->set_item_disabled(menu->get_item_index(CLOSE_OTHER_TABS), tab_container->get_child_count() <= 1);
+ menu->set_item_disabled(menu->get_item_index(CLOSE_DOCS), !_has_docs_tab());
+
+ menu->set_item_disabled(menu->get_item_index(FILE_RUN), current_is_doc);
+}
+
void ScriptEditor::_tab_changed(int p_which) {
ensure_select_current();
}
@@ -1551,13 +1597,14 @@ void ScriptEditor::_notification(int p_what) {
editor->connect("script_add_function_request", callable_mp(this, &ScriptEditor::_add_callback));
editor->connect("resource_saved", callable_mp(this, &ScriptEditor::_res_saved_callback));
editor->connect("scene_saved", callable_mp(this, &ScriptEditor::_scene_saved_callback));
- editor->get_filesystem_dock()->connect("files_moved", callable_mp(this, &ScriptEditor::_files_moved));
- editor->get_filesystem_dock()->connect("file_removed", callable_mp(this, &ScriptEditor::_file_removed));
+ FileSystemDock::get_singleton()->connect("files_moved", callable_mp(this, &ScriptEditor::_files_moved));
+ FileSystemDock::get_singleton()->connect("file_removed", callable_mp(this, &ScriptEditor::_file_removed));
script_list->connect("item_selected", callable_mp(this, &ScriptEditor::_script_selected));
members_overview->connect("item_selected", callable_mp(this, &ScriptEditor::_members_overview_selected));
help_overview->connect("item_selected", callable_mp(this, &ScriptEditor::_help_overview_selected));
- script_split->connect("dragged", callable_mp(this, &ScriptEditor::_script_split_dragged));
+ script_split->connect("dragged", callable_mp(this, &ScriptEditor::_split_dragged));
+ list_split->connect("dragged", callable_mp(this, &ScriptEditor::_split_dragged));
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ScriptEditor::_editor_settings_changed));
EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &ScriptEditor::_filesystem_changed));
@@ -1595,7 +1642,7 @@ void ScriptEditor::_notification(int p_what) {
case NOTIFICATION_READY: {
get_tree()->connect("tree_changed", callable_mp(this, &ScriptEditor::_tree_changed));
- editor->get_inspector_dock()->connect("request_help", callable_mp(this, &ScriptEditor::_help_class_open));
+ InspectorDock::get_singleton()->connect("request_help", callable_mp(this, &ScriptEditor::_help_class_open));
editor->connect("request_help_search", callable_mp(this, &ScriptEditor::_help_search));
} break;
@@ -1771,6 +1818,7 @@ struct _ScriptEditorItemData {
String name;
String sort_key;
Ref<Texture2D> icon;
+ bool tool = false;
int index = 0;
String tooltip;
bool used = false;
@@ -1831,7 +1879,7 @@ void ScriptEditor::_update_members_overview() {
for (int i = 0; i < functions.size(); i++) {
String filter = filter_methods->get_text();
String name = functions[i].get_slice(":", 0);
- if (filter.is_empty() || filter.is_subsequence_ofi(name)) {
+ if (filter.is_empty() || filter.is_subsequence_ofn(name)) {
members_overview->add_item(name);
members_overview->set_item_metadata(members_overview->get_item_count() - 1, functions[i].get_slice(":", 1).to_int() - 1);
}
@@ -1894,6 +1942,7 @@ void ScriptEditor::_update_script_colors() {
int hist_size = EditorSettings::get_singleton()->get("text_editor/script_list/script_temperature_history_size");
Color hot_color = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
+ hot_color.set_s(hot_color.get_s() * 0.9);
Color cold_color = get_theme_color(SNAME("font_color"), SNAME("Editor"));
for (int i = 0; i < script_list->get_item_count(); i++) {
@@ -1953,6 +2002,7 @@ void ScriptEditor::_update_script_names() {
se->set_meta("_edit_res_path", path);
}
String name = se->get_name();
+ Ref<Script> scr = se->get_edited_resource();
_ScriptEditorItemData sd;
sd.icon = icon;
@@ -1962,6 +2012,9 @@ void ScriptEditor::_update_script_names() {
sd.used = used.has(se->get_edited_resource());
sd.category = 0;
sd.ref = se;
+ if (scr.is_valid()) {
+ sd.tool = scr->is_tool();
+ }
switch (sort_by) {
case SORT_BY_NAME: {
@@ -2076,13 +2129,19 @@ void ScriptEditor::_update_script_names() {
Vector<_ScriptEditorItemData> sedata_filtered;
for (int i = 0; i < sedata.size(); i++) {
String filter = filter_scripts->get_text();
- if (filter.is_empty() || filter.is_subsequence_ofi(sedata[i].name)) {
+ if (filter.is_empty() || filter.is_subsequence_ofn(sedata[i].name)) {
sedata_filtered.push_back(sedata[i]);
}
}
+ Color tool_color = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
+ tool_color.set_s(tool_color.get_s() * 1.5);
for (int i = 0; i < sedata_filtered.size(); i++) {
script_list->add_item(sedata_filtered[i].name, sedata_filtered[i].icon);
+ if (sedata_filtered[i].tool) {
+ script_list->set_item_icon_modulate(script_list->get_item_count() - 1, tool_color);
+ }
+
int index = script_list->get_item_count() - 1;
script_list->set_item_tooltip(index, sedata_filtered[i].tooltip);
script_list->set_item_metadata(index, sedata_filtered[i].index); /* Saving as metadata the script's index in the tab container and not the filtered one */
@@ -2110,8 +2169,6 @@ void ScriptEditor::_update_script_names() {
_update_members_overview_visibility();
_update_help_overview_visibility();
_update_script_colors();
-
- file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(FILE_REOPEN_CLOSED), previous_scripts.is_empty());
}
void ScriptEditor::_update_script_connections() {
@@ -2335,7 +2392,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
// If we delete a script within the filesystem, the original resource path
// is lost, so keep it as metadata to figure out the exact tab to delete.
se->set_meta("_edit_res_path", p_resource->get_path());
- se->set_tooltip_request_func("_get_debug_tooltip", this);
+ se->set_tooltip_request_func(callable_mp(this, &ScriptEditor::_get_debug_tooltip));
if (se->get_edit_menu()) {
se->get_edit_menu()->hide();
menu_hb->add_child(se->get_edit_menu());
@@ -2753,7 +2810,7 @@ void ScriptEditor::_tree_changed() {
call_deferred(SNAME("_update_script_connections"));
}
-void ScriptEditor::_script_split_dragged(float) {
+void ScriptEditor::_split_dragged(float) {
_save_layout();
}
@@ -2782,6 +2839,7 @@ Variant ScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
if (!preview_icon.is_null()) {
TextureRect *tf = memnew(TextureRect);
tf->set_texture(preview_icon);
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
drag_preview->add_child(tf);
}
Label *label = memnew(Label(preview_name));
@@ -2802,7 +2860,7 @@ bool ScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data
}
if (String(d["type"]) == "script_list_element") {
- Node *node = d["script_list_element"];
+ Node *node = Object::cast_to<Node>(d["script_list_element"]);
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
if (se) {
@@ -2875,7 +2933,7 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co
}
if (String(d["type"]) == "script_list_element") {
- Node *node = d["script_list_element"];
+ Node *node = Object::cast_to<Node>(d["script_list_element"]);
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
EditorHelp *eh = Object::cast_to<EditorHelp>(node);
@@ -3055,6 +3113,12 @@ void ScriptEditor::_make_script_list_context_menu() {
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_sort"), WINDOW_SORT);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/toggle_scripts_panel"), TOGGLE_SCRIPTS_PANEL);
+ context_menu->set_item_disabled(context_menu->get_item_index(CLOSE_ALL), tab_container->get_child_count() <= 0);
+ context_menu->set_item_disabled(context_menu->get_item_index(CLOSE_OTHER_TABS), tab_container->get_child_count() <= 1);
+ context_menu->set_item_disabled(context_menu->get_item_index(WINDOW_MOVE_UP), tab_container->get_current_tab() <= 0);
+ context_menu->set_item_disabled(context_menu->get_item_index(WINDOW_MOVE_DOWN), tab_container->get_current_tab() >= tab_container->get_child_count() - 1);
+ context_menu->set_item_disabled(context_menu->get_item_index(WINDOW_SORT), tab_container->get_child_count() <= 1);
+
context_menu->set_position(get_screen_position() + get_local_mouse_position());
context_menu->reset_size();
context_menu->popup();
@@ -3136,8 +3200,12 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
tab_container->get_child(i)->set_meta("__editor_pass", Variant());
}
- if (p_layout->has_section_key("ScriptEditor", "split_offset")) {
- script_split->set_split_offset(p_layout->get_value("ScriptEditor", "split_offset"));
+ if (p_layout->has_section_key("ScriptEditor", "script_split_offset")) {
+ script_split->set_split_offset(p_layout->get_value("ScriptEditor", "script_split_offset"));
+ }
+
+ if (p_layout->has_section_key("ScriptEditor", "list_split_offset")) {
+ list_split->set_split_offset(p_layout->get_value("ScriptEditor", "list_split_offset"));
}
// Remove any deleted editors that have been removed between launches.
@@ -3190,7 +3258,8 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
p_layout->set_value("ScriptEditor", "open_scripts", scripts);
p_layout->set_value("ScriptEditor", "open_help", helps);
- p_layout->set_value("ScriptEditor", "split_offset", script_split->get_split_offset());
+ p_layout->set_value("ScriptEditor", "script_split_offset", script_split->get_split_offset());
+ p_layout->set_value("ScriptEditor", "list_split_offset", list_split->get_split_offset());
// Save the cache.
script_editor_cache->save(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("script_editor_cache.cfg"));
@@ -3423,7 +3492,7 @@ void ScriptEditor::_open_script_request(const String &p_path) {
void ScriptEditor::register_syntax_highlighter(const Ref<EditorSyntaxHighlighter> &p_syntax_highlighter) {
ERR_FAIL_COND(p_syntax_highlighter.is_null());
- if (syntax_highlighters.find(p_syntax_highlighter) == -1) {
+ if (!syntax_highlighters.has(p_syntax_highlighter)) {
syntax_highlighters.push_back(p_syntax_highlighter);
}
}
@@ -3443,7 +3512,7 @@ void ScriptEditor::register_create_script_editor_function(CreateScriptEditorFunc
}
void ScriptEditor::_script_changed() {
- NodeDock::singleton->update_lists();
+ NodeDock::get_singleton()->update_lists();
}
void ScriptEditor::_on_find_in_files_requested(String text) {
@@ -3535,7 +3604,6 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_goto_script_line2", &ScriptEditor::_goto_script_line2);
ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path);
- ClassDB::bind_method("_get_debug_tooltip", &ScriptEditor::_get_debug_tooltip);
ClassDB::bind_method("_update_script_connections", &ScriptEditor::_update_script_connections);
ClassDB::bind_method("_help_class_open", &ScriptEditor::_help_class_open);
ClassDB::bind_method("_live_auto_reload_running_scripts", &ScriptEditor::_live_auto_reload_running_scripts);
@@ -3678,7 +3746,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
ED_SHORTCUT("script_editor/next_script", TTR("Next Script"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::PERIOD);
ED_SHORTCUT("script_editor/prev_script", TTR("Previous Script"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::COMMA);
set_process_input(true);
- set_process_unhandled_input(true);
+ set_process_unhandled_key_input(true);
file_menu = memnew(MenuButton);
file_menu->set_text(TTR("File"));
@@ -3686,8 +3754,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
file_menu->set_shortcut_context(this);
menu_hb->add_child(file_menu);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script...")), FILE_NEW);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New Text File...")), FILE_NEW_TEXTFILE);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script..."), KeyModifierMask::CMD | Key::N), FILE_NEW);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New Text File..."), KeyModifierMask::CMD | 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 | KeyModifierMask::SHIFT | Key::T), FILE_REOPEN_CLOSED);
file_menu->get_popup()->add_submenu_item(TTR("Open Recent"), "RecentScripts", FILE_OPEN_RECENT);
@@ -3737,6 +3805,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/toggle_scripts_panel", TTR("Toggle Scripts Panel"), KeyModifierMask::CMD | Key::BACKSLASH), TOGGLE_SCRIPTS_PANEL);
file_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptEditor::_menu_option));
+ file_menu->get_popup()->connect("about_to_popup", callable_mp(this, &ScriptEditor::_prepare_file_menu));
script_search_menu = memnew(MenuButton);
script_search_menu->set_text(TTR("Search"));
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index ca409e15ca..d754f1a378 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -165,7 +165,7 @@ public:
virtual bool show_members_overview() = 0;
- virtual void set_tooltip_request_func(String p_method, Object *p_obj) = 0;
+ virtual void set_tooltip_request_func(const Callable &p_toolip_callback) = 0;
virtual Control *get_edit_menu() = 0;
virtual void clear_edit_menu() = 0;
virtual void set_find_replace_bar(FindReplaceBar *p_bar) = 0;
@@ -315,6 +315,9 @@ class ScriptEditor : public PanelContainer {
void _menu_option(int p_option);
void _theme_option(int p_option);
void _show_save_theme_as_dialog();
+ bool _has_docs_tab() const;
+ bool _has_script_tab() const;
+ void _prepare_file_menu();
Tree *disk_changed_list;
ConfirmationDialog *disk_changed;
@@ -415,7 +418,7 @@ class ScriptEditor : public PanelContainer {
void _tree_changed();
- void _script_split_dragged(float);
+ void _split_dragged(float);
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 97a882c383..09af73520b 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -392,8 +392,17 @@ String ScriptTextEditor::get_name() {
}
Ref<Texture2D> ScriptTextEditor::get_theme_icon() {
- if (get_parent_control() && get_parent_control()->has_theme_icon(script->get_class(), "EditorIcons")) {
- return get_parent_control()->get_theme_icon(script->get_class(), "EditorIcons");
+ if (get_parent_control()) {
+ String icon_name = script->get_class();
+ if (script->is_built_in()) {
+ icon_name += "Internal";
+ }
+
+ if (get_parent_control()->has_theme_icon(icon_name, "EditorIcons")) {
+ return get_parent_control()->get_theme_icon(icon_name, "EditorIcons");
+ } else if (get_parent_control()->has_theme_icon(script->get_class(), "EditorIcons")) {
+ return get_parent_control()->get_theme_icon(script->get_class(), "EditorIcons");
+ }
}
return Ref<Texture2D>();
@@ -404,12 +413,14 @@ void ScriptTextEditor::_validate_script() {
String text = te->get_text();
List<String> fnc;
- Set<int> safe_lines;
- List<ScriptLanguage::Warning> warnings;
- List<ScriptLanguage::ScriptError> errors;
+
+ warnings.clear();
+ errors.clear();
+ safe_lines.clear();
if (!script->get_language()->validate(text, script->get_path(), &fnc, &errors, &warnings, &safe_lines)) {
- String error_text = TTR("Error at ") + "(" + itos(errors[0].line) + "," + itos(errors[0].column) + "): " + errors[0].message;
+ // TRANSLATORS: Script error pointing to a line and column number.
+ String error_text = vformat(TTR("Error at (%d, %d):"), errors[0].line, errors[0].column) + " " + errors[0].message;
code_editor->set_error(error_text);
code_editor->set_error_pos(errors[0].line - 1, errors[0].column - 1);
script_is_valid = false;
@@ -428,7 +439,14 @@ void ScriptTextEditor::_validate_script() {
script_is_valid = true;
}
_update_connected_methods();
+ _update_warnings();
+ _update_errors();
+
+ emit_signal(SNAME("name_changed"));
+ emit_signal(SNAME("edited_script_changed"));
+}
+void ScriptTextEditor::_update_warnings() {
int warning_nb = warnings.size();
warnings_panel->clear();
@@ -456,7 +474,6 @@ void ScriptTextEditor::_validate_script() {
}
}
- code_editor->set_error_count(errors.size());
code_editor->set_warning_count(warning_nb);
if (has_connections_table) {
@@ -480,6 +497,10 @@ void ScriptTextEditor::_validate_script() {
warnings_panel->pop(); // Cell.
}
warnings_panel->pop(); // Table.
+}
+
+void ScriptTextEditor::_update_errors() {
+ code_editor->set_error_count(errors.size());
errors_panel->clear();
errors_panel->push_table(2);
@@ -498,6 +519,7 @@ void ScriptTextEditor::_validate_script() {
}
errors_panel->pop(); // Table
+ CodeEdit *te = code_editor->get_text_editor();
bool highlight_safe = EDITOR_DEF("text_editor/appearance/gutters/highlight_type_safe_lines", true);
bool last_is_safe = false;
for (int i = 0; i < te->get_line_count(); i++) {
@@ -527,9 +549,6 @@ void ScriptTextEditor::_validate_script() {
te->set_line_gutter_item_color(i, 1, default_line_number_color);
}
}
-
- emit_signal(SNAME("name_changed"));
- emit_signal(SNAME("edited_script_changed"));
}
void ScriptTextEditor::_update_bookmark_list() {
@@ -1314,6 +1333,11 @@ void ScriptTextEditor::_change_syntax_highlighter(int p_idx) {
void ScriptTextEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED:
+ if (is_visible_in_tree()) {
+ _update_warnings();
+ _update_errors();
+ }
+ [[fallthrough]];
case NOTIFICATION_ENTER_TREE: {
code_editor->get_text_editor()->set_gutter_width(connection_gutter, code_editor->get_text_editor()->get_line_height());
} break;
@@ -1366,8 +1390,10 @@ void ScriptTextEditor::clear_breakpoints() {
code_editor->get_text_editor()->clear_breakpointed_lines();
}
-void ScriptTextEditor::set_tooltip_request_func(String p_method, Object *p_obj) {
- code_editor->get_text_editor()->set_tooltip_request_func(p_obj, p_method, this);
+void ScriptTextEditor::set_tooltip_request_func(const Callable &p_toolip_callback) {
+ Variant args[1] = { this };
+ const Variant *argp[] = { &args[0] };
+ code_editor->get_text_editor()->set_tooltip_request_func(p_toolip_callback.bind(argp, 1));
}
void ScriptTextEditor::set_debugger_active(bool p_active) {
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 6e67444489..1e2284b403 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -62,6 +62,9 @@ class ScriptTextEditor : public ScriptEditorBase {
bool editor_enabled = false;
Vector<String> functions;
+ List<ScriptLanguage::Warning> warnings;
+ List<ScriptLanguage::ScriptError> errors;
+ Set<int> safe_lines;
List<Connection> missing_connections;
@@ -154,6 +157,8 @@ protected:
void _breakpoint_toggled(int p_row);
void _validate_script(); // No longer virtual.
+ void _update_warnings();
+ void _update_errors();
void _update_bookmark_list();
void _bookmark_item_pressed(int p_idx);
@@ -233,7 +238,7 @@ public:
virtual bool show_members_overview() override;
- virtual void set_tooltip_request_func(String p_method, Object *p_obj) override;
+ virtual void set_tooltip_request_func(const Callable &p_toolip_callback) override;
virtual void set_debugger_active(bool p_active) override;
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index afecada1db..4bbeb33406 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -50,6 +50,20 @@ static bool saved_treat_warning_as_errors = false;
static Map<ShaderWarning::Code, bool> saved_warnings;
static uint32_t saved_warning_flags = 0U;
+void ShaderTextEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ if (is_visible_in_tree()) {
+ _load_theme_settings();
+ if (warnings.size() > 0 && last_compile_result == OK) {
+ warnings_panel->clear();
+ _update_warning_panel();
+ }
+ }
+ } break;
+ }
+}
+
Ref<Shader> ShaderTextEditor::get_edited_shader() const {
return shader;
}
@@ -243,9 +257,9 @@ void ShaderTextEditor::_validate_script() {
sl.enable_warning_checking(saved_warnings_enabled);
sl.set_warning_flags(saved_warning_flags);
- Error err = sl.compile(code, info);
+ last_compile_result = sl.compile(code, info);
- if (err != OK) {
+ if (last_compile_result != OK) {
String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text();
set_error(error_text);
set_error_pos(sl.get_error_line() - 1, 0);
@@ -260,14 +274,14 @@ void ShaderTextEditor::_validate_script() {
set_error("");
}
- if (warnings.size() > 0 || err != OK) {
+ if (warnings.size() > 0 || last_compile_result != OK) {
warnings_panel->clear();
}
warnings.clear();
for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) {
warnings.push_back(E->get());
}
- if (warnings.size() > 0 && err == OK) {
+ if (warnings.size() > 0 && last_compile_result == OK) {
warnings.sort_custom<WarningsComparator>();
_update_warning_panel();
} else {
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index db2d1438b3..9196ded921 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -55,11 +55,13 @@ class ShaderTextEditor : public CodeTextEditor {
RichTextLabel *warnings_panel = nullptr;
Ref<Shader> shader;
List<ShaderWarning> warnings;
+ Error last_compile_result = Error::OK;
void _check_shader_mode();
void _update_warning_panel();
protected:
+ void _notification(int p_what);
static void _bind_methods();
virtual void _load_theme_settings() override;
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index e1b27cb045..5dd24983ff 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -149,6 +149,9 @@ void BoneTransformEditor::set_target(const String &p_prop) {
void BoneTransformEditor::_property_keyed(const String &p_path, bool p_advance) {
AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
+ if (!te->has_keying()) {
+ return;
+ }
PackedStringArray split = p_path.split("/");
if (split.size() == 3 && split[0] == "bones") {
int bone_idx = split[1].to_int();
@@ -380,7 +383,7 @@ void Skeleton3DEditor::create_physical_skeleton() {
if (!bones_infos[parent].physical_bone) {
bones_infos.write[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos);
- ur->create_action(TTR("Create physical bones"));
+ ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL);
ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone);
ur->add_do_reference(bones_infos[parent].physical_bone);
ur->add_undo_method(skeleton, "remove_child", bones_infos[parent].physical_bone);
diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp
index 1eac651ed6..85ff20dd23 100644
--- a/editor/plugins/sprite_2d_editor_plugin.cpp
+++ b/editor/plugins/sprite_2d_editor_plugin.cpp
@@ -337,9 +337,9 @@ void Sprite2DEditor::_convert_to_mesh_2d_node() {
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Convert to Mesh2D"));
- ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, mesh_instance, true, false);
+ ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, mesh_instance, true, false);
ur->add_do_reference(mesh_instance);
- ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", mesh_instance, node, false, false);
+ ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", mesh_instance, node, false, false);
ur->add_undo_reference(node);
ur->commit_action();
}
@@ -395,9 +395,9 @@ void Sprite2DEditor::_convert_to_polygon_2d_node() {
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Convert to Polygon2D"));
- ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, polygon_2d_instance, true, false);
+ ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, polygon_2d_instance, true, false);
ur->add_do_reference(polygon_2d_instance);
- ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", polygon_2d_instance, node, false, false);
+ ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", polygon_2d_instance, node, false, false);
ur->add_undo_reference(node);
ur->commit_action();
}
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 2da4f80751..014fa0e7a5 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -222,28 +222,14 @@ void SpriteFramesEditor::_sheet_add_frames() {
int fc = frames->get_frame_count(edited_anim);
- Point2 src_origin;
- Rect2 src_region(Point2(), texture_size);
-
- AtlasTexture *src_atlas = Object::cast_to<AtlasTexture>(*split_sheet_preview->get_texture());
- if (src_atlas && src_atlas->get_atlas().is_valid()) {
- src_origin = src_atlas->get_region().position - src_atlas->get_margin().position;
- src_region = src_atlas->get_region();
- }
-
for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) {
int idx = E->get();
Point2 frame_coords(idx % frame_count_x, idx / frame_count_x);
- Rect2 frame(frame_coords * frame_size + src_origin, frame_size);
- Rect2 region = frame.intersection(src_region);
- Rect2 margin(region == Rect2() ? Point2() : region.position - frame.position, frame.size - region.size);
-
Ref<AtlasTexture> at;
at.instantiate();
at->set_atlas(split_sheet_preview->get_texture());
- at->set_region(region);
- at->set_margin(margin);
+ at->set_region(Rect2(frame_coords * frame_size, frame_size));
undo_redo->add_do_method(frames, "add_frame", edited_anim, at, -1);
undo_redo->add_undo_method(frames, "remove_frame", edited_anim, fc);
@@ -835,19 +821,30 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) {
for (int i = 0; i < frames->get_frame_count(edited_anim); i++) {
String name;
- Ref<Texture2D> icon;
+ Ref<Texture2D> frame = frames->get_frame(edited_anim, i);
- if (frames->get_frame(edited_anim, i).is_null()) {
+ if (frame.is_null()) {
name = itos(i) + ": " + TTR("(empty)");
-
} else {
- name = itos(i) + ": " + frames->get_frame(edited_anim, i)->get_name();
- icon = frames->get_frame(edited_anim, i);
+ name = itos(i) + ": " + frame->get_name();
}
- tree->add_item(name, icon);
- if (frames->get_frame(edited_anim, i).is_valid()) {
- tree->set_item_tooltip(tree->get_item_count() - 1, frames->get_frame(edited_anim, i)->get_path());
+ tree->add_item(name, frame);
+ if (frame.is_valid()) {
+ String tooltip = frame->get_path();
+
+ // Frame is often saved as an AtlasTexture subresource within a scene/resource file,
+ // thus its path might be not what the user is looking for. So we're also showing
+ // subsequent source texture paths.
+ String prefix = String::utf8("┖╴");
+ Ref<AtlasTexture> at = frame;
+ while (at.is_valid() && at->get_atlas().is_valid()) {
+ tooltip += "\n" + prefix + at->get_atlas()->get_path();
+ prefix = " " + prefix;
+ at = at->get_atlas();
+ }
+
+ tree->set_item_tooltip(tree->get_item_count() - 1, tooltip);
}
if (sel == i) {
tree->select(tree->get_item_count() - 1);
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 12d13571f8..940f269803 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -272,8 +272,10 @@ void TextEditor::update_settings() {
code_editor->update_editor_settings();
}
-void TextEditor::set_tooltip_request_func(String p_method, Object *p_obj) {
- code_editor->get_text_editor()->set_tooltip_request_func(p_obj, p_method, this);
+void TextEditor::set_tooltip_request_func(const Callable &p_toolip_callback) {
+ Variant args[1] = { this };
+ const Variant *argp[] = { &args[0] };
+ code_editor->get_text_editor()->set_tooltip_request_func(p_toolip_callback.bind(argp, 1));
}
Control *TextEditor::get_edit_menu() {
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index d3fb0c0a16..d03385d79e 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -135,7 +135,7 @@ public:
virtual bool show_members_overview() override;
virtual bool can_lose_focus_on_node_selection() override { return true; }
virtual void set_debugger_active(bool p_active) override;
- virtual void set_tooltip_request_func(String p_method, Object *p_obj) override;
+ virtual void set_tooltip_request_func(const Callable &p_toolip_callback) override;
virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
void update_toggle_scripts_button() override;
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 900bf4ef57..662c0126ec 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -35,6 +35,7 @@
#include "core/os/keyboard.h"
#include "editor/editor_scale.h"
#include "scene/gui/check_box.h"
+#include "scene/gui/view_panner.h"
void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to) {
Vector2 line = (to - from).normalized() * 10;
@@ -259,6 +260,10 @@ void TextureRegionEditor::_region_draw() {
}
void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
+ if (panner->gui_input(p_input)) {
+ return;
+ }
+
Transform2D mtx;
mtx.elements[2] = -draw_ofs * draw_zoom;
mtx.scale_basis(Vector2(draw_zoom, draw_zoom));
@@ -281,7 +286,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid()) {
if (mb->get_button_index() == MouseButton::LEFT) {
- if (mb->is_pressed()) {
+ if (mb->is_pressed() && !panner->is_panning()) {
if (node_ninepatch || obj_styleBox.is_valid()) {
edited_margin = -1;
float margins[4] = { 0 };
@@ -400,7 +405,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
}
}
- } else if (drag) {
+ } else if (!mb->is_pressed() && drag) {
if (edited_margin >= 0) {
undo_redo->create_action(TTR("Set Margin"));
static Side side[4] = { SIDE_TOP, SIDE_BOTTOM, SIDE_LEFT, SIDE_RIGHT };
@@ -461,21 +466,13 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
drag_index = -1;
}
}
- } else if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) {
- _zoom_on_position(draw_zoom * ((0.95 + (0.05 * mb->get_factor())) / 0.95), mb->get_position());
- } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed()) {
- _zoom_on_position(draw_zoom * (1 - (0.05 * mb->get_factor())), mb->get_position());
}
}
Ref<InputEventMouseMotion> mm = p_input;
if (mm.is_valid()) {
- if ((mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE || Input::get_singleton()->is_key_pressed(Key::SPACE)) {
- Vector2 dragged(mm->get_relative().x / draw_zoom, mm->get_relative().y / draw_zoom);
- hscroll->set_value(hscroll->get_value() - dragged.x);
- vscroll->set_value(vscroll->get_value() - dragged.y);
- } else if (drag) {
+ if (drag) {
if (edited_margin >= 0) {
float new_margin = 0;
@@ -605,6 +602,24 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
}
}
+void TextureRegionEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) {
+ _pan_callback(-p_scroll_vec * 32);
+}
+
+void TextureRegionEditor::_pan_callback(Vector2 p_scroll_vec) {
+ p_scroll_vec /= draw_zoom;
+ hscroll->set_value(hscroll->get_value() - p_scroll_vec.x);
+ vscroll->set_value(vscroll->get_value() - p_scroll_vec.y);
+}
+
+void TextureRegionEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) {
+ if (p_scroll_vec.y < 0) {
+ _zoom_on_position(draw_zoom * ((0.95 + (0.05 * Math::abs(p_scroll_vec.y))) / 0.95), p_origin);
+ } else {
+ _zoom_on_position(draw_zoom * (1 - (0.05 * Math::abs(p_scroll_vec.y))), p_origin);
+ }
+}
+
void TextureRegionEditor::_scroll_changed(float) {
if (updating_scroll) {
return;
@@ -802,6 +817,10 @@ void TextureRegionEditor::_notification(int p_what) {
vscroll->set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE);
hscroll->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE);
+ [[fallthrough]];
+ }
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (snap_mode == SNAP_AUTOSLICE && is_visible() && autoslice_is_dirty) {
@@ -1058,11 +1077,16 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) {
hb_grid->hide();
+ panner.instantiate();
+ panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_scroll_callback), callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback));
+
edit_draw = memnew(Panel);
add_child(edit_draw);
edit_draw->set_v_size_flags(SIZE_EXPAND_FILL);
edit_draw->connect("draw", callable_mp(this, &TextureRegionEditor::_region_draw));
edit_draw->connect("gui_input", callable_mp(this, &TextureRegionEditor::_region_input));
+ edit_draw->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
+ edit_draw->set_focus_mode(FOCUS_CLICK);
draw_zoom = 1.0;
edit_draw->set_clip_contents(true);
diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h
index bffc6fd9bf..d78ad3891c 100644
--- a/editor/plugins/texture_region_editor_plugin.h
+++ b/editor/plugins/texture_region_editor_plugin.h
@@ -40,6 +40,8 @@
#include "scene/resources/style_box.h"
#include "scene/resources/texture.h"
+class ViewPanner;
+
class TextureRegionEditor : public VBoxContainer {
GDCLASS(TextureRegionEditor, VBoxContainer);
@@ -98,6 +100,11 @@ class TextureRegionEditor : public VBoxContainer {
Vector2 drag_from;
int drag_index;
+ Ref<ViewPanner> panner;
+ void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
+ void _pan_callback(Vector2 p_scroll_vec);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt);
+
void _set_snap_mode(int p_mode);
void _set_snap_off_x(float p_val);
void _set_snap_off_y(float p_val);
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 91c17399c2..aaa09237cf 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -81,8 +81,6 @@ void ThemeItemImportTree::_update_items_tree() {
bool is_matching_filter = (filter_text.is_empty() || type_name.findn(filter_text) > -1);
bool has_filtered_items = false;
- bool any_checked = false;
- bool any_checked_with_data = false;
for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
Theme::DataType dt = (Theme::DataType)i;
@@ -178,9 +176,6 @@ void ThemeItemImportTree::_update_items_tree() {
break; // Can't happen, but silences warning.
}
- bool data_type_any_checked = false;
- bool data_type_any_checked_with_data = false;
-
filtered_names.sort_custom<StringName::AlphCompare>();
for (const StringName &F : filtered_names) {
TreeItem *item_node = import_items_tree->create_item(data_type_node);
@@ -194,20 +189,11 @@ void ThemeItemImportTree::_update_items_tree() {
item_node->set_editable(IMPORT_ITEM_DATA, true);
_restore_selected_item(item_node);
- if (item_node->is_checked(IMPORT_ITEM)) {
- data_type_any_checked = true;
- any_checked = true;
- }
- if (item_node->is_checked(IMPORT_ITEM_DATA)) {
- data_type_any_checked_with_data = true;
- any_checked_with_data = true;
- }
+ item_node->propagate_check(IMPORT_ITEM, false);
+ item_node->propagate_check(IMPORT_ITEM_DATA, false);
item_list->push_back(item_node);
}
-
- data_type_node->set_checked(IMPORT_ITEM, data_type_any_checked);
- data_type_node->set_checked(IMPORT_ITEM_DATA, data_type_any_checked && data_type_any_checked_with_data);
}
// Remove the item if it doesn't match the filter in any way.
@@ -221,9 +207,6 @@ void ThemeItemImportTree::_update_items_tree() {
if (!filter_text.is_empty() && has_filtered_items) {
type_node->set_collapsed(false);
}
-
- type_node->set_checked(IMPORT_ITEM, any_checked);
- type_node->set_checked(IMPORT_ITEM_DATA, any_checked && any_checked_with_data);
}
if (color_amount > 0) {
@@ -471,23 +454,26 @@ void ThemeItemImportTree::_tree_item_edited() {
if (is_checked) {
if (edited_column == IMPORT_ITEM_DATA) {
edited_item->set_checked(IMPORT_ITEM, true);
+ edited_item->propagate_check(IMPORT_ITEM);
}
-
- _select_all_subitems(edited_item, (edited_column == IMPORT_ITEM_DATA));
} else {
if (edited_column == IMPORT_ITEM) {
edited_item->set_checked(IMPORT_ITEM_DATA, false);
+ edited_item->propagate_check(IMPORT_ITEM_DATA);
}
-
- _deselect_all_subitems(edited_item, (edited_column == IMPORT_ITEM));
}
-
- _update_parent_items(edited_item);
- _store_selected_item(edited_item);
-
+ edited_item->propagate_check(edited_column);
updating_tree = false;
}
+void ThemeItemImportTree::_check_propagated_to_tree_item(Object *p_obj, int p_column) {
+ TreeItem *item = Object::cast_to<TreeItem>(p_obj);
+ // Skip "category" tree items by checking for children.
+ if (item && !item->get_first_child()) {
+ _store_selected_item(item);
+ }
+}
+
void ThemeItemImportTree::_select_all_subitems(TreeItem *p_root_item, bool p_select_with_data) {
TreeItem *child_item = p_root_item->get_first_child();
while (child_item) {
@@ -516,32 +502,6 @@ void ThemeItemImportTree::_deselect_all_subitems(TreeItem *p_root_item, bool p_d
}
}
-void ThemeItemImportTree::_update_parent_items(TreeItem *p_root_item) {
- TreeItem *parent_item = p_root_item->get_parent();
- if (!parent_item) {
- return;
- }
-
- bool any_checked = false;
- bool any_checked_with_data = false;
-
- TreeItem *child_item = parent_item->get_first_child();
- while (child_item) {
- if (child_item->is_checked(IMPORT_ITEM)) {
- any_checked = true;
- }
- if (child_item->is_checked(IMPORT_ITEM_DATA)) {
- any_checked_with_data = true;
- }
-
- child_item = child_item->get_next();
- }
-
- parent_item->set_checked(IMPORT_ITEM, any_checked);
- parent_item->set_checked(IMPORT_ITEM_DATA, any_checked && any_checked_with_data);
- _update_parent_items(parent_item);
-}
-
void ThemeItemImportTree::_select_all_items_pressed() {
if (updating_tree) {
return;
@@ -629,7 +589,7 @@ void ThemeItemImportTree::_select_all_data_type_pressed(int p_data_type) {
}
child_item->set_checked(IMPORT_ITEM, true);
- _update_parent_items(child_item);
+ child_item->propagate_check(IMPORT_ITEM, false);
_store_selected_item(child_item);
}
@@ -685,7 +645,8 @@ void ThemeItemImportTree::_select_full_data_type_pressed(int p_data_type) {
child_item->set_checked(IMPORT_ITEM, true);
child_item->set_checked(IMPORT_ITEM_DATA, true);
- _update_parent_items(child_item);
+ child_item->propagate_check(IMPORT_ITEM, false);
+ child_item->propagate_check(IMPORT_ITEM_DATA, false);
_store_selected_item(child_item);
}
@@ -741,7 +702,8 @@ void ThemeItemImportTree::_deselect_all_data_type_pressed(int p_data_type) {
child_item->set_checked(IMPORT_ITEM, false);
child_item->set_checked(IMPORT_ITEM_DATA, false);
- _update_parent_items(child_item);
+ child_item->propagate_check(IMPORT_ITEM, false);
+ child_item->propagate_check(IMPORT_ITEM_DATA, false);
_store_selected_item(child_item);
}
@@ -937,6 +899,7 @@ ThemeItemImportTree::ThemeItemImportTree() {
import_items_tree->set_h_size_flags(Control::SIZE_EXPAND_FILL);
import_main_hb->add_child(import_items_tree);
import_items_tree->connect("item_edited", callable_mp(this, &ThemeItemImportTree::_tree_item_edited));
+ import_items_tree->connect("check_propagated_to_item", callable_mp(this, &ThemeItemImportTree::_check_propagated_to_tree_item));
import_items_tree->set_columns(3);
import_items_tree->set_column_titles_visible(true);
@@ -2113,7 +2076,7 @@ void ThemeTypeDialog::_update_add_type_options(const String &p_filter) {
Vector<StringName> unique_names;
for (const StringName &E : names) {
// Filter out undesired values.
- if (!p_filter.is_subsequence_ofi(String(E))) {
+ if (!p_filter.is_subsequence_ofn(String(E))) {
continue;
}
@@ -2263,7 +2226,7 @@ void ThemeTypeEditor::_update_type_list() {
}
updating = true;
- Control *focused = get_focus_owner();
+ Control *focused = get_viewport()->gui_get_focus_owner();
if (focused) {
if (focusables.has(focused)) {
// If focus is currently on one of the internal property editors, don't update.
@@ -3506,7 +3469,7 @@ void ThemeEditor::_add_preview_tab(ThemeEditorPreview *p_preview_tab, const Stri
preview_tabs->add_tab(p_preview_name, p_icon);
preview_tabs_content->add_child(p_preview_tab);
- preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("close"), SNAME("TabBar")));
+ preview_tabs->set_tab_button_icon(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("close"), SNAME("TabBar")));
p_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
preview_tabs->set_current_tab(preview_tabs->get_tab_count() - 1);
@@ -3637,7 +3600,7 @@ ThemeEditor::ThemeEditor() {
preview_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
preview_tabbar_hb->add_child(preview_tabs);
preview_tabs->connect("tab_changed", callable_mp(this, &ThemeEditor::_change_preview_tab));
- preview_tabs->connect("tab_rmb_clicked", callable_mp(this, &ThemeEditor::_remove_preview_tab));
+ preview_tabs->connect("tab_button_pressed", callable_mp(this, &ThemeEditor::_remove_preview_tab));
HBoxContainer *add_preview_button_hb = memnew(HBoxContainer);
preview_tabbar_hb->add_child(add_preview_button_hb);
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index 4c6b16a68c..c00ce3ae65 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -149,9 +149,9 @@ class ThemeItemImportTree : public VBoxContainer {
void _update_total_selected(Theme::DataType p_data_type);
void _tree_item_edited();
+ void _check_propagated_to_tree_item(Object *p_obj, int p_column);
void _select_all_subitems(TreeItem *p_root_item, bool p_select_with_data);
void _deselect_all_subitems(TreeItem *p_root_item, bool p_deselect_completely);
- void _update_parent_items(TreeItem *p_root_item);
void _select_all_items_pressed();
void _select_full_items_pressed();
diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp
index fc4764f61e..677c9759c2 100644
--- a/editor/plugins/tiles/atlas_merging_dialog.cpp
+++ b/editor/plugins/tiles/atlas_merging_dialog.cpp
@@ -81,7 +81,7 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
}
// Copy the properties.
- TileData *original_tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+ TileData *original_tile_data = atlas_source->get_tile_data(tile_id, alternative_id);
List<PropertyInfo> properties;
original_tile_data->get_property_list(&properties);
for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 24ede3b85e..35496795e0 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -48,7 +48,7 @@ void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) {
}
}
-void TileAtlasView::_scroll_callback(Vector2 p_scroll_vec) {
+void TileAtlasView::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) {
_pan_callback(-p_scroll_vec * 32);
}
@@ -58,7 +58,7 @@ void TileAtlasView::_pan_callback(Vector2 p_scroll_vec) {
_update_zoom_and_panning(true);
}
-void TileAtlasView::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin) {
+void TileAtlasView::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) {
zoom_widget->set_zoom_by_increments(-p_scroll_vec.y * 2);
emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning);
_update_zoom_and_panning(true);
@@ -83,7 +83,7 @@ Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
- bool transposed = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(tile_id, alternative_id))->get_transpose();
+ bool transposed = tile_set_atlas_source->get_tile_data(tile_id, alternative_id)->get_transpose();
line_size.x += transposed ? texture_region_size.y : texture_region_size.x;
line_size.y = MAX(line_size.y, transposed ? texture_region_size.x : texture_region_size.y);
}
@@ -345,7 +345,7 @@ void TileAtlasView::_draw_alternatives() {
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);
- TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id));
+ TileData *tile_data = tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id);
bool transposed = tile_data->get_transpose();
// Update the y to max value.
@@ -473,7 +473,7 @@ void TileAtlasView::_update_alternative_tiles_rect_cache() {
int line_height = 0;
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
- TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(tile_id, alternative_id));
+ TileData *tile_data = tile_set_atlas_source->get_tile_data(tile_id, alternative_id);
bool transposed = tile_data->get_transpose();
current.size = transposed ? Vector2i(texture_region_size.y, texture_region_size.x) : texture_region_size;
@@ -524,7 +524,7 @@ void TileAtlasView::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED:
- panner->set_control_scheme((ViewPanner::ControlScheme)EDITOR_GET("interface/editors/sub_editor_panning_scheme").operator int());
+ panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
break;
case NOTIFICATION_READY:
@@ -540,9 +540,6 @@ void TileAtlasView::_bind_methods() {
TileAtlasView::TileAtlasView() {
set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
- panner.instantiate();
- panner->set_callbacks(callable_mp(this, &TileAtlasView::_scroll_callback), callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback));
-
Panel *panel = memnew(Panel);
panel->set_clip_contents(true);
panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
@@ -566,10 +563,16 @@ TileAtlasView::TileAtlasView() {
button_center_view->set_tooltip(TTR("Center View"));
add_child(button_center_view);
+ panner.instantiate();
+ panner->set_callbacks(callable_mp(this, &TileAtlasView::_scroll_callback), callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback));
+ panner->set_enable_rmb(true);
+
center_container = memnew(CenterContainer);
center_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
center_container->set_anchors_preset(Control::PRESET_CENTER);
center_container->connect("gui_input", callable_mp(this, &TileAtlasView::gui_input));
+ center_container->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
+ center_container->set_focus_mode(FOCUS_CLICK);
panel->add_child(center_container);
missing_source_label = memnew(Label);
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
index 6a0e0ae820..37ef7d6a2a 100644
--- a/editor/plugins/tiles/tile_atlas_view.h
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -67,9 +67,9 @@ private:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
Ref<ViewPanner> panner;
- void _scroll_callback(Vector2 p_scroll_vec);
+ void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
void _pan_callback(Vector2 p_scroll_vec);
- void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt);
Map<Vector2, Map<int, Rect2i>> alternative_tiles_rect_cache;
void _update_alternative_tiles_rect_cache();
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 1b28e84ca2..12003738e7 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -60,7 +60,7 @@ TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) {
if (atlas_source) {
ERR_FAIL_COND_V(!atlas_source->has_tile(p_cell.get_atlas_coords()), nullptr);
ERR_FAIL_COND_V(!atlas_source->has_alternative_tile(p_cell.get_atlas_coords(), p_cell.alternative_tile), nullptr);
- td = Object::cast_to<TileData>(atlas_source->get_tile_data(p_cell.get_atlas_coords(), p_cell.alternative_tile));
+ td = atlas_source->get_tile_data(p_cell.get_atlas_coords(), p_cell.alternative_tile);
}
return td;
@@ -713,20 +713,21 @@ void GenericTilePolygonEditor::set_multiple_polygon_mode(bool p_multiple_polygon
void GenericTilePolygonEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY:
- button_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons")));
- button_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
- button_delete->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
- button_center_view->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CenterView"), SNAME("EditorIcons")));
- button_pixel_snap->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Snap"), SNAME("EditorIcons")));
- button_advanced_menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ button_create->set_icon(get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons")));
+ button_edit->set_icon(get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
+ button_delete->set_icon(get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
+ button_center_view->set_icon(get_theme_icon(SNAME("CenterView"), SNAME("EditorIcons")));
+ button_pixel_snap->set_icon(get_theme_icon(SNAME("Snap"), SNAME("EditorIcons")));
+ button_advanced_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
PopupMenu *p = button_advanced_menu->get_popup();
p->set_item_icon(p->get_item_index(ROTATE_RIGHT), get_theme_icon(SNAME("RotateRight"), SNAME("EditorIcons")));
p->set_item_icon(p->get_item_index(ROTATE_LEFT), get_theme_icon(SNAME("RotateLeft"), SNAME("EditorIcons")));
p->set_item_icon(p->get_item_index(FLIP_HORIZONTALLY), get_theme_icon(SNAME("MirrorX"), SNAME("EditorIcons")));
p->set_item_icon(p->get_item_index(FLIP_VERTICALLY), get_theme_icon(SNAME("MirrorY"), SNAME("EditorIcons")));
- break;
+ } break;
}
}
@@ -837,7 +838,7 @@ Variant TileDataDefaultEditor::_get_painted_value() {
}
void TileDataDefaultEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
Variant value = tile_data->get(property);
dummy_object->set(property, value);
@@ -847,13 +848,13 @@ void TileDataDefaultEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_at
}
void TileDataDefaultEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
tile_data->set(property, p_value);
}
Variant TileDataDefaultEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND_V(!tile_data, Variant());
return tile_data->get(property);
}
@@ -1148,6 +1149,7 @@ void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, String p
property_editor->set_label(p_label);
}
property_editor->connect("property_changed", callable_mp(this, &TileDataDefaultEditor::_property_value_changed).unbind(1));
+ property_editor->set_tooltip(p_property);
property_editor->update_property();
add_child(property_editor);
}
@@ -1268,7 +1270,7 @@ Variant TileDataOcclusionShapeEditor::_get_painted_value() {
}
void TileDataOcclusionShapeEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
Ref<OccluderPolygon2D> occluder_polygon = tile_data->get_occluder(occlusion_layer);
@@ -1280,7 +1282,7 @@ void TileDataOcclusionShapeEditor::_set_painted_value(TileSetAtlasSource *p_tile
}
void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
Ref<OccluderPolygon2D> occluder_polygon = p_value;
tile_data->set_occluder(occlusion_layer, occluder_polygon);
@@ -1289,7 +1291,7 @@ void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atl
}
Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND_V(!tile_data, Variant());
return tile_data->get_occluder(occlusion_layer);
}
@@ -1356,6 +1358,7 @@ void TileDataCollisionEditor::_polygons_changed() {
one_way_property_editor->set_label(one_way_property);
one_way_property_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1));
one_way_property_editor->connect("selected", callable_mp(this, &TileDataCollisionEditor::_property_selected));
+ one_way_property_editor->set_tooltip(one_way_property_editor->get_edited_property());
one_way_property_editor->update_property();
add_child(one_way_property_editor);
property_editors[one_way_property] = one_way_property_editor;
@@ -1367,6 +1370,7 @@ void TileDataCollisionEditor::_polygons_changed() {
one_way_margin_property_editor->set_label(one_way_margin_property);
one_way_margin_property_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1));
one_way_margin_property_editor->connect("selected", callable_mp(this, &TileDataCollisionEditor::_property_selected));
+ one_way_margin_property_editor->set_tooltip(one_way_margin_property_editor->get_edited_property());
one_way_margin_property_editor->update_property();
add_child(one_way_margin_property_editor);
property_editors[one_way_margin_property] = one_way_margin_property_editor;
@@ -1409,7 +1413,7 @@ Variant TileDataCollisionEditor::_get_painted_value() {
}
void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
polygon_editor->clear_polygons();
@@ -1435,7 +1439,7 @@ void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_
}
void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
Dictionary dict = p_value;
@@ -1454,7 +1458,7 @@ void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_so
}
Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND_V(!tile_data, Variant());
Dictionary dict;
@@ -1527,6 +1531,7 @@ TileDataCollisionEditor::TileDataCollisionEditor() {
linear_velocity_editor->set_label("linear_velocity");
linear_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1));
linear_velocity_editor->connect("selected", callable_mp(this, &TileDataCollisionEditor::_property_selected));
+ linear_velocity_editor->set_tooltip(linear_velocity_editor->get_edited_property());
linear_velocity_editor->update_property();
add_child(linear_velocity_editor);
property_editors["linear_velocity"] = linear_velocity_editor;
@@ -1536,6 +1541,7 @@ TileDataCollisionEditor::TileDataCollisionEditor() {
angular_velocity_editor->set_label("angular_velocity");
angular_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1));
angular_velocity_editor->connect("selected", callable_mp(this, &TileDataCollisionEditor::_property_selected));
+ angular_velocity_editor->set_tooltip(angular_velocity_editor->get_edited_property());
angular_velocity_editor->update_property();
add_child(angular_velocity_editor);
property_editors["angular_velocity"] = angular_velocity_editor;
@@ -1654,7 +1660,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
hovered_coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_pos);
hovered_coords = p_tile_set_atlas_source->get_tile_at_coords(hovered_coords);
if (hovered_coords != TileSetSource::INVALID_ATLAS_COORDS) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(hovered_coords, 0));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(hovered_coords, 0);
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(hovered_coords);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, 0);
@@ -1693,7 +1699,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i coords = p_tile_set_atlas_source->get_tile_id(i);
if (coords != hovered_coords) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
if (tile_data->get_terrain_set() != int(dummy_object->get("terrain_set"))) {
// Dimming
p_canvas_item->draw_set_transform_matrix(p_transform);
@@ -1767,7 +1773,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
Vector2i coords = Vector2i(x, y);
coords = p_tile_set_atlas_source->get_tile_at_coords(coords);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
if (tile_data->get_terrain_set() == terrain_set) {
TileMapCell cell;
cell.source_id = 0;
@@ -1828,7 +1834,7 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
hovered_coords = Vector2i(hovered.x, hovered.y);
hovered_alternative = hovered.z;
if (hovered_coords != TileSetSource::INVALID_ATLAS_COORDS) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(hovered_coords, hovered_alternative));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(hovered_coords, hovered_alternative);
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(hovered_coords, hovered_alternative);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, hovered_alternative);
@@ -1869,7 +1875,7 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
for (int j = 1; j < p_tile_set_atlas_source->get_alternative_tiles_count(coords); j++) {
int alternative_tile = p_tile_set_atlas_source->get_alternative_tile_id(coords, j);
if (coords != hovered_coords || alternative_tile != hovered_alternative) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile);
if (tile_data->get_terrain_set() != int(dummy_object->get("terrain_set"))) {
// Dimming
p_canvas_item->draw_set_transform_matrix(p_transform);
@@ -1913,7 +1919,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
cell.alternative_tile = 0;
// Save the old terrain_set and terrains bits.
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
if (!drag_modified.has(cell)) {
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
@@ -1943,7 +1949,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
cell.set_atlas_coords(coords);
cell.alternative_tile = 0;
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
if (tile_data->get_terrain_set() == terrain_set) {
// Save the old terrain_set and terrains bits.
if (!drag_modified.has(cell)) {
@@ -1985,7 +1991,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position());
coords = p_tile_set_atlas_source->get_tile_at_coords(coords);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
@@ -2009,7 +2015,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
coords = p_tile_set_atlas_source->get_tile_at_coords(coords);
TileData *tile_data = nullptr;
if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
- tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
+ tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
}
int terrain_set = int(dummy_object->get("terrain_set"));
int terrain = int(dummy_object->get("terrain"));
@@ -2126,7 +2132,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
undo_redo->create_action(TTR("Painting Terrain Set"));
for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) {
Vector2i coords = E->get().get_atlas_coords();
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->get().alternative_tile), tile_data->get_terrain_set());
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->get().alternative_tile), drag_painted_value);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
@@ -2192,7 +2198,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Vector2i coords = Vector2i(x, y);
coords = p_tile_set_atlas_source->get_tile_at_coords(coords);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
if (tile_data->get_terrain_set() == terrain_set) {
TileMapCell cell;
cell.source_id = 0;
@@ -2213,7 +2219,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
undo_redo->create_action(TTR("Painting Terrain"));
for (Set<TileMapCell>::Element *E = edited.front(); E; E = E->next()) {
Vector2i coords = E->get().get_atlas_coords();
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
@@ -2254,7 +2260,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
cell.source_id = 0;
cell.set_atlas_coords(coords);
cell.alternative_tile = alternative_tile;
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile);
if (!drag_modified.has(cell)) {
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
@@ -2286,7 +2292,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
cell.alternative_tile = alternative_tile;
// Save the old terrain_set and terrains bits.
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile);
if (tile_data->get_terrain_set() == terrain_set) {
if (!drag_modified.has(cell)) {
Dictionary dict;
@@ -2328,7 +2334,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
int alternative_tile = tile.z;
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile);
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
@@ -2355,7 +2361,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
Vector2i coords = Vector2i(tile.x, tile.y);
int alternative_tile = tile.z;
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile);
if (terrain_set == -1 || !tile_data || tile_data->get_terrain_set() != terrain_set) {
drag_type = DRAG_TYPE_PAINT_TERRAIN_SET;
@@ -2505,6 +2511,7 @@ TileDataTerrainsEditor::TileDataTerrainsEditor() {
terrain_set_property_editor->set_object_and_property(dummy_object, "terrain_set");
terrain_set_property_editor->set_label("Terrain Set");
terrain_set_property_editor->connect("property_changed", callable_mp(this, &TileDataTerrainsEditor::_property_value_changed).unbind(1));
+ terrain_set_property_editor->set_tooltip(terrain_set_property_editor->get_edited_property());
add_child(terrain_set_property_editor);
terrain_property_editor = memnew(EditorPropertyEnum);
@@ -2533,7 +2540,7 @@ Variant TileDataNavigationEditor::_get_painted_value() {
}
void TileDataNavigationEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer);
@@ -2547,7 +2554,7 @@ void TileDataNavigationEditor::_set_painted_value(TileSetAtlasSource *p_tile_set
}
void TileDataNavigationEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND(!tile_data);
Ref<NavigationPolygon> navigation_polygon = p_value;
tile_data->set_navigation_polygon(navigation_layer, navigation_polygon);
@@ -2556,7 +2563,7 @@ void TileDataNavigationEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_s
}
Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
- TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
+ TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile);
ERR_FAIL_COND_V(!tile_data, Variant());
return tile_data->get_navigation_polygon(navigation_layer);
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index aa92920722..89027b174d 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -124,6 +124,10 @@ void TileMapEditorTilesPlugin::_tab_changed() {
void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
// Update the sources.
int old_current = sources_list->get_current();
+ int old_source = -1;
+ if (old_current > -1) {
+ old_source = sources_list->get_item_metadata(old_current);
+ }
sources_list->clear();
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
@@ -136,9 +140,12 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
return;
}
- for (int i = 0; i < tile_set->get_source_count(); i++) {
- int source_id = tile_set->get_source_id(i);
+ if (!tile_set->has_source(old_source)) {
+ old_source = -1;
+ }
+ List<int> source_ids = TilesEditorPlugin::get_singleton()->get_sorted_sources(tile_set);
+ for (const int &source_id : source_ids) {
TileSetSource *source = *tile_set->get_source(source_id);
Ref<Texture2D> texture;
@@ -157,7 +164,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
if (texture.is_valid()) {
item_text = vformat("%s (ID: %d)", texture->get_path().get_file(), source_id);
} else {
- item_text = vformat("No Texture Atlas Source (ID: %d)", source_id);
+ item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
}
}
}
@@ -180,20 +187,25 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
}
sources_list->add_item(item_text, texture);
- sources_list->set_item_metadata(i, source_id);
+ sources_list->set_item_metadata(sources_list->get_item_count() - 1, source_id);
}
if (sources_list->get_item_count() > 0) {
- if (old_current > 0) {
- // Keep the current selected item if needed.
- sources_list->set_current(CLAMP(old_current, 0, sources_list->get_item_count() - 1));
+ if (old_source >= 0) {
+ for (int i = 0; i < sources_list->get_item_count(); i++) {
+ if ((int)sources_list->get_item_metadata(i) == old_source) {
+ sources_list->set_current(i);
+ sources_list->ensure_current_is_visible();
+ break;
+ }
+ }
} else {
sources_list->set_current(0);
}
sources_list->emit_signal(SNAME("item_selected"), sources_list->get_current());
}
- // Synchronize
+ // Synchronize the lists.
TilesEditorPlugin::get_singleton()->set_sources_lists_current(sources_list->get_current());
}
@@ -439,6 +451,7 @@ void TileMapEditorTilesPlugin::_scenes_list_nothing_selected() {
}
void TileMapEditorTilesPlugin::_update_theme() {
+ source_sort_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
select_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons")));
paint_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
line_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons")));
@@ -835,9 +848,9 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
// Fade out the border of the grid.
float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
- float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
+ float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)(pos_in_rect.x + 1)), 0.0f, 1.0f);
float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
- float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
+ float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)(pos_in_rect.y + 1)), 0.0f, 1.0f);
float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
Transform2D tile_xform;
@@ -864,7 +877,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
// Get tile data.
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E.value.get_atlas_coords(), E.value.alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(E.value.get_atlas_coords(), E.value.alternative_tile);
// Compute the offset
Rect2i source_rect = atlas_source->get_tile_texture_region(E.value.get_atlas_coords());
@@ -936,7 +949,7 @@ TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(Ref<TileMapPattern> p_pa
TileSetSource *source = *tile_set->get_source(source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(atlas_coords, alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(atlas_coords, alternative_tile);
ERR_FAIL_COND_V(!tile_data, TileMapCell());
sum += tile_data->get_probability();
} else {
@@ -955,7 +968,7 @@ TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(Ref<TileMapPattern> p_pa
TileSetSource *source = *tile_set->get_source(source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
- current += Object::cast_to<TileData>(atlas_source->get_tile_data(atlas_coords, alternative_tile))->get_probability();
+ current += atlas_source->get_tile_data(atlas_coords, alternative_tile)->get_probability();
} else {
current += 1.0;
}
@@ -1950,6 +1963,14 @@ void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer
tile_map_layer = p_tile_map_layer;
}
+void TileMapEditorTilesPlugin::_set_source_sort(int p_sort) {
+ for (int i = 0; i != TilesEditorPlugin::SOURCE_SORT_MAX; i++) {
+ source_sort_button->get_popup()->set_item_checked(i, (i == (int)p_sort));
+ }
+ TilesEditorPlugin::get_singleton()->set_sorting_option(p_sort);
+ _update_tile_set_sources_list();
+}
+
void TileMapEditorTilesPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileMapEditorTilesPlugin::_scene_thumbnail_done);
ClassDB::bind_method(D_METHOD("_set_tile_map_selection", "selection"), &TileMapEditorTilesPlugin::_set_tile_map_selection);
@@ -2106,17 +2127,44 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
atlas_sources_split_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tiles_bottom_panel->add_child(atlas_sources_split_container);
+ VBoxContainer *split_container_left_side = memnew(VBoxContainer);
+ split_container_left_side->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ split_container_left_side->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ split_container_left_side->set_stretch_ratio(0.25);
+ split_container_left_side->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
+ atlas_sources_split_container->add_child(split_container_left_side);
+
+ HBoxContainer *sources_bottom_actions = memnew(HBoxContainer);
+ sources_bottom_actions->set_alignment(HBoxContainer::ALIGNMENT_END);
+
+ source_sort_button = memnew(MenuButton);
+ source_sort_button->set_flat(true);
+ source_sort_button->set_tooltip(TTR("Sort sources"));
+
+ PopupMenu *p = source_sort_button->get_popup();
+ p->connect("id_pressed", callable_mp(this, &TileMapEditorTilesPlugin::_set_source_sort));
+ p->add_radio_check_item(TTR("Sort by ID (Ascending)"), TilesEditorPlugin::SOURCE_SORT_ID);
+ p->add_radio_check_item(TTR("Sort by ID (Descending)"), TilesEditorPlugin::SOURCE_SORT_ID_REVERSE);
+ p->add_radio_check_item(TTR("Sort by Name (Ascending)"), TilesEditorPlugin::SOURCE_SORT_NAME);
+ p->add_radio_check_item(TTR("Sort by Name (Descending)"), TilesEditorPlugin::SOURCE_SORT_NAME_REVERSE);
+ p->set_item_checked(TilesEditorPlugin::SOURCE_SORT_ID, true);
+ sources_bottom_actions->add_child(source_sort_button);
+
sources_list = memnew(ItemList);
sources_list->set_fixed_icon_size(Size2i(60, 60) * EDSCALE);
sources_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ sources_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
sources_list->set_stretch_ratio(0.25);
sources_list->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1));
sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_source_display).unbind(1));
sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
- sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list));
- atlas_sources_split_container->add_child(sources_list);
+ sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list, source_sort_button));
+ sources_list->add_user_signal(MethodInfo("sort_request"));
+ sources_list->connect("sort_request", callable_mp(this, &TileMapEditorTilesPlugin::_update_tile_set_sources_list));
+ split_container_left_side->add_child(sources_list);
+ split_container_left_side->add_child(sources_bottom_actions);
// Tile atlas source.
tile_atlas_view = memnew(TileAtlasView);
@@ -2427,7 +2475,7 @@ Set<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p
Ref<TileSetSource> source = tile_set->get_source(source_cell.source_id);
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(source_cell.get_atlas_coords(), source_cell.alternative_tile));
+ tile_data = atlas_source->get_tile_data(source_cell.get_atlas_coords(), source_cell.alternative_tile);
}
if (!tile_data) {
return Set<Vector2i>();
@@ -2458,7 +2506,7 @@ Set<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p
Ref<TileSetSource> source = tile_set->get_source(tile_map->get_cell_source_id(tile_map_layer, coords));
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords)));
+ tile_data = atlas_source->get_tile_data(tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords));
}
if (tile_data) {
candidate_pattern = tile_data->get_terrains_pattern();
@@ -2503,7 +2551,7 @@ Set<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p
Ref<TileSetSource> source = tile_set->get_source(tile_map->get_cell_source_id(tile_map_layer, coords));
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords)));
+ tile_data = atlas_source->get_tile_data(tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords));
}
if (tile_data) {
candidate_pattern = tile_data->get_terrains_pattern();
@@ -2569,7 +2617,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile));
+ tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
}
if (tile_data) {
@@ -2948,9 +2996,9 @@ void TileMapEditorTerrainsPlugin::forward_canvas_draw_over_viewport(Control *p_o
// Fade out the border of the grid.
float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
- float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
+ float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)(pos_in_rect.x + 1)), 0.0f, 1.0f);
float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
- float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
+ float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)(pos_in_rect.y + 1)), 0.0f, 1.0f);
float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
Transform2D tile_xform;
@@ -3010,7 +3058,7 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) {
int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+ TileData *tile_data = atlas_source->get_tile_data(tile_id, alternative_id);
int terrain_set = tile_data->get_terrain_set();
if (terrain_set >= 0) {
ERR_FAIL_INDEX(terrain_set, (int)per_terrain_terrains_patterns.size());
@@ -3138,7 +3186,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
if (tile_data->get_probability() > max_probability) {
icon = atlas_source->get_texture();
region = atlas_source->get_tile_texture_region(cell.get_atlas_coords());
@@ -3833,9 +3881,9 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
// Fade out the border of the grid.
float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
- float right_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.x, (float)(displayed_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
+ float right_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.x, (float)(displayed_rect.size.x - fading), (float)(pos_in_rect.x + 1)), 0.0f, 1.0f);
float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
- float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
+ float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)(pos_in_rect.y + 1)), 0.0f, 1.0f);
float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
Transform2D tile_xform;
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index b1bee03211..6fa0d01612 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -145,6 +145,7 @@ private:
Label *invalid_source_label;
ItemList *sources_list;
+ MenuButton *source_sort_button;
Ref<Texture2D> missing_atlas_texture_icon;
void _update_tile_set_sources_list();
@@ -170,6 +171,7 @@ private:
void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
void _update_atlas_view();
+ void _set_source_sort(int p_sort);
// Scenes collection sources.
ItemList *scene_tiles_list;
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index c4cc9745ee..e708b83440 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -274,7 +274,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
const int &alternative = E->get().alternative;
bool valid = false;
- TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative);
ERR_FAIL_COND_V(!tile_data, false);
tile_data->set(p_name, p_value, &valid);
@@ -359,7 +359,7 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na
const Vector2i &coords = E->get().tile;
const int &alternative = E->get().alternative;
- TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative);
ERR_FAIL_COND_V(!tile_data, false);
bool valid = false;
@@ -432,7 +432,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro
const Vector2i &coords = E->get().tile;
const int &alternative = E->get().alternative;
- TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative);
ERR_FAIL_COND(!tile_data);
List<PropertyInfo> list;
@@ -486,7 +486,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::edit(TileSetAtlasSource *p_
const int &alternative = E->get().alternative;
if (tile_set_atlas_source && tile_set_atlas_source->has_tile(coords) && tile_set_atlas_source->has_alternative_tile(coords, alternative)) {
- TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative);
if (tile_data->is_connected(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed))) {
tile_data->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
}
@@ -502,7 +502,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::edit(TileSetAtlasSource *p_
const int &alternative = E->get().alternative;
if (tile_set_atlas_source->has_tile(coords) && tile_set_atlas_source->has_alternative_tile(coords, alternative)) {
- TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative);
if (!tile_data->is_connected(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed))) {
tile_data->connect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
}
@@ -2609,7 +2609,7 @@ void EditorPropertyTilePolygon::update_property() {
// Set the background
Vector2i coords = atlas_tile_proxy_object->get_edited_tiles().front()->get().tile;
int alternative = atlas_tile_proxy_object->get_edited_tiles().front()->get().alternative;
- TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ TileData *tile_data = tile_set_atlas_source->get_tile_data(coords, alternative);
generic_tile_polygon_editor->set_background(tile_set_atlas_source->get_texture(), tile_set_atlas_source->get_tile_texture_region(coords), tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate());
// Reset the polygons.
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index ef8d423724..be261927ee 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -137,9 +137,8 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
sources_list->clear();
// Update the atlas sources.
- for (int i = 0; i < tile_set->get_source_count(); i++) {
- int source_id = tile_set->get_source_id(i);
-
+ List<int> source_ids = TilesEditorPlugin::get_singleton()->get_sorted_sources(tile_set);
+ for (const int &source_id : source_ids) {
TileSetSource *source = *tile_set->get_source(source_id);
Ref<Texture2D> texture;
@@ -156,9 +155,9 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
texture = atlas_source->get_texture();
if (item_text.is_empty()) {
if (texture.is_valid()) {
- item_text = vformat("%s (ID:%d)", texture->get_path().get_file(), source_id);
+ item_text = vformat("%s (ID: %d)", texture->get_path().get_file(), source_id);
} else {
- item_text = vformat(TTR("No Texture Atlas Source (ID:%d)"), source_id);
+ item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
}
}
}
@@ -168,20 +167,20 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
if (scene_collection_source) {
texture = get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
if (item_text.is_empty()) {
- item_text = vformat(TTR("Scene Collection Source (ID:%d)"), source_id);
+ item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
}
}
// Use default if not valid.
if (item_text.is_empty()) {
- item_text = vformat(TTR("Unknown Type Source (ID:%d)"), source_id);
+ item_text = vformat(TTR("Unknown Type Source (ID: %d)"), source_id);
}
if (!texture.is_valid()) {
texture = missing_texture_texture;
}
sources_list->add_item(item_text, texture);
- sources_list->set_item_metadata(i, source_id);
+ sources_list->set_item_metadata(sources_list->get_item_count() - 1, source_id);
}
// Set again the current selected item if needed.
@@ -189,6 +188,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
for (int i = 0; i < sources_list->get_item_count(); i++) {
if ((int)sources_list->get_item_metadata(i) == to_select) {
sources_list->set_current(i);
+ sources_list->ensure_current_is_visible();
if (old_selected != to_select) {
sources_list->emit_signal(SNAME("item_selected"), sources_list->get_current());
}
@@ -312,12 +312,29 @@ void TileSetEditor::_sources_advanced_menu_id_pressed(int p_id_pressed) {
}
}
+void TileSetEditor::_set_source_sort(int p_sort) {
+ TilesEditorPlugin::get_singleton()->set_sorting_option(p_sort);
+ for (int i = 0; i != TilesEditorPlugin::SOURCE_SORT_MAX; i++) {
+ source_sort_button->get_popup()->set_item_checked(i, (i == (int)p_sort));
+ }
+
+ int old_selected = TileSet::INVALID_SOURCE;
+ if (sources_list->get_current() >= 0) {
+ int source_id = sources_list->get_item_metadata(sources_list->get_current());
+ if (tile_set->has_source(source_id)) {
+ old_selected = source_id;
+ }
+ }
+ _update_sources_list(old_selected);
+}
+
void TileSetEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED:
sources_delete_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
sources_add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ source_sort_button->set_icon(get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
sources_advanced_menu_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
missing_texture_texture = get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons"));
break;
@@ -465,7 +482,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_
Vector2i tile_id = tas->get_tile_id(j);
for (int k = 0; k < tas->get_alternative_tiles_count(tile_id); k++) {
int alternative_id = tas->get_alternative_tile_id(tile_id, k);
- TileData *tile_data = Object::cast_to<TileData>(tas->get_tile_data(tile_id, alternative_id));
+ TileData *tile_data = tas->get_tile_data(tile_id, alternative_id);
ERR_FAIL_COND(!tile_data);
// Actually saving stuff.
@@ -584,7 +601,7 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
Vector2i tile_id = tas->get_tile_id(j);
for (int k = 0; k < tas->get_alternative_tiles_count(tile_id); k++) {
int alternative_id = tas->get_alternative_tile_id(tile_id, k);
- TileData *tile_data = Object::cast_to<TileData>(tas->get_tile_data(tile_id, alternative_id));
+ TileData *tile_data = tas->get_tile_data(tile_id, alternative_id);
ERR_FAIL_COND(!tile_data);
if (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") {
@@ -670,13 +687,27 @@ TileSetEditor::TileSetEditor() {
split_container_left_side->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
split_container->add_child(split_container_left_side);
+ source_sort_button = memnew(MenuButton);
+ source_sort_button->set_flat(true);
+ source_sort_button->set_tooltip(TTR("Sort sources"));
+
+ PopupMenu *p = source_sort_button->get_popup();
+ p->connect("id_pressed", callable_mp(this, &TileSetEditor::_set_source_sort));
+ p->add_radio_check_item(TTR("Sort by ID (Ascending)"), TilesEditorPlugin::SOURCE_SORT_ID);
+ p->add_radio_check_item(TTR("Sort by ID (Descending)"), TilesEditorPlugin::SOURCE_SORT_ID_REVERSE);
+ p->add_radio_check_item(TTR("Sort by Name (Ascending)"), TilesEditorPlugin::SOURCE_SORT_NAME);
+ p->add_radio_check_item(TTR("Sort by Name (Descending)"), TilesEditorPlugin::SOURCE_SORT_NAME_REVERSE);
+ p->set_item_checked(TilesEditorPlugin::SOURCE_SORT_ID, true);
+
sources_list = memnew(ItemList);
sources_list->set_fixed_icon_size(Size2i(60, 60) * EDSCALE);
sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
sources_list->set_v_size_flags(SIZE_EXPAND_FILL);
sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected));
sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
- sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list));
+ sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list, source_sort_button));
+ sources_list->add_user_signal(MethodInfo("sort_request"));
+ sources_list->connect("sort_request", callable_mp(this, &TileSetEditor::_update_sources_list), varray(-1));
sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
sources_list->set_drag_forwarding(this);
split_container_left_side->add_child(sources_list);
@@ -704,6 +735,7 @@ TileSetEditor::TileSetEditor() {
sources_advanced_menu_button->get_popup()->add_item(TTR("Manage Tile Proxies"));
sources_advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetEditor::_sources_advanced_menu_id_pressed));
sources_bottom_actions->add_child(sources_advanced_menu_button);
+ sources_bottom_actions->add_child(source_sort_button);
atlas_merging_dialog = memnew(AtlasMergingDialog);
add_child(atlas_merging_dialog);
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index 98ebbae02f..a21c951c42 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -67,6 +67,7 @@ private:
// Sources management.
Button *sources_delete_button;
MenuButton *sources_add_button;
+ MenuButton *source_sort_button;
MenuButton *sources_advanced_menu_button;
ItemList *sources_list;
Ref<Texture2D> missing_texture_texture;
@@ -74,6 +75,7 @@ private:
void _source_delete_pressed();
void _source_add_id_pressed(int p_id_pressed);
void _sources_advanced_menu_id_pressed(int p_id_pressed);
+ void _set_source_sort(int p_sort);
AtlasMergingDialog *atlas_merging_dialog;
TileProxiesManagerDialog *tile_proxies_manager_dialog;
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index f99fcb3675..4c644f33d4 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -113,7 +113,7 @@ void TilesEditorPlugin::_thread() {
tile_map->set_scale(scale);
tile_map->set_position(-(scale * encompassing_rect.get_center()) + thumbnail_size2 / 2);
- // Add the viewport at the lasst moment to avoid rendering too early.
+ // Add the viewport at the last moment to avoid rendering too early.
EditorNode::get_singleton()->add_child(viewport);
RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<TilesEditorPlugin *>(this), &TilesEditorPlugin::_preview_frame_started), Vector<Variant>(), Object::CONNECT_ONESHOT);
@@ -166,8 +166,6 @@ void TilesEditorPlugin::_update_editors() {
editor_node->hide_bottom_panel();
}
}
- tileset_editor_button->set_visible(tile_set.is_valid());
- tilemap_editor_button->set_visible(tile_map);
}
void TilesEditorPlugin::_notification(int p_what) {
@@ -218,15 +216,29 @@ void TilesEditorPlugin::set_sources_lists_current(int p_current) {
atlas_sources_lists_current = p_current;
}
-void TilesEditorPlugin::synchronize_sources_list(Object *p_current) {
- ItemList *item_list = Object::cast_to<ItemList>(p_current);
+void TilesEditorPlugin::synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button) {
+ ItemList *item_list = Object::cast_to<ItemList>(p_current_list);
+ MenuButton *sorting_button = Object::cast_to<MenuButton>(p_current_sort_button);
ERR_FAIL_COND(!item_list);
+ ERR_FAIL_COND(!sorting_button);
+
+ if (sorting_button->is_visible_in_tree()) {
+ for (int i = 0; i != SOURCE_SORT_MAX; i++) {
+ sorting_button->get_popup()->set_item_checked(i, (i == (int)source_sort));
+ }
+ }
if (item_list->is_visible_in_tree()) {
if (atlas_sources_lists_current < 0 || atlas_sources_lists_current >= item_list->get_item_count()) {
item_list->deselect_all();
} else {
+ // Make sure the selection is not overwritten after sorting.
+ int atlas_sources_lists_current_mem = atlas_sources_lists_current;
+ item_list->emit_signal(SNAME("sort_request"));
+ atlas_sources_lists_current = atlas_sources_lists_current_mem;
+
item_list->set_current(atlas_sources_lists_current);
+ item_list->ensure_current_is_visible();
item_list->emit_signal(SNAME("item_selected"), atlas_sources_lists_current);
}
}
@@ -246,6 +258,87 @@ void TilesEditorPlugin::synchronize_atlas_view(Object *p_current) {
}
}
+void TilesEditorPlugin::set_sorting_option(int p_option) {
+ source_sort = p_option;
+}
+
+List<int> TilesEditorPlugin::get_sorted_sources(const Ref<TileSet> tile_set) const {
+ SourceNameComparator::tile_set = tile_set;
+ List<int> source_ids;
+
+ for (int i = 0; i < tile_set->get_source_count(); i++) {
+ source_ids.push_back(tile_set->get_source_id(i));
+ }
+
+ switch (source_sort) {
+ case SOURCE_SORT_ID_REVERSE:
+ // Already sorted.
+ source_ids.reverse();
+ break;
+ case SOURCE_SORT_NAME:
+ source_ids.sort_custom<SourceNameComparator>();
+ break;
+ case SOURCE_SORT_NAME_REVERSE:
+ source_ids.sort_custom<SourceNameComparator>();
+ source_ids.reverse();
+ break;
+ default: // SOURCE_SORT_ID
+ break;
+ }
+
+ SourceNameComparator::tile_set.unref();
+ return source_ids;
+}
+
+Ref<TileSet> TilesEditorPlugin::SourceNameComparator::tile_set;
+
+bool TilesEditorPlugin::SourceNameComparator::operator()(const int &p_a, const int &p_b) const {
+ String name_a;
+ String name_b;
+
+ {
+ TileSetSource *source = *tile_set->get_source(p_a);
+
+ if (!source->get_name().is_empty()) {
+ name_a = source->get_name();
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ Ref<Texture2D> texture = atlas_source->get_texture();
+ if (name_a.is_empty() && texture.is_valid()) {
+ name_a = texture->get_path().get_file();
+ }
+ }
+
+ if (name_a.is_empty()) {
+ name_a = itos(p_a);
+ }
+ }
+
+ {
+ TileSetSource *source = *tile_set->get_source(p_b);
+
+ if (!source->get_name().is_empty()) {
+ name_b = source->get_name();
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ Ref<Texture2D> texture = atlas_source->get_texture();
+ if (name_b.is_empty() && texture.is_valid()) {
+ name_b = texture->get_path().get_file();
+ }
+ }
+
+ if (name_b.is_empty()) {
+ name_b = itos(p_b);
+ }
+ }
+
+ return NaturalNoCaseComparator()(name_a, name_b);
+}
+
void TilesEditorPlugin::edit(Object *p_object) {
// Disconnect to changes.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index 59eb79480e..487436b98a 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -43,6 +43,15 @@ class TilesEditorPlugin : public EditorPlugin {
static TilesEditorPlugin *singleton;
+public:
+ enum SourceSortOption {
+ SOURCE_SORT_ID = 0,
+ SOURCE_SORT_ID_REVERSE,
+ SOURCE_SORT_NAME,
+ SOURCE_SORT_NAME_REVERSE,
+ SOURCE_SORT_MAX
+ };
+
private:
EditorNode *editor_node;
@@ -65,6 +74,14 @@ private:
void _tile_map_changed();
+ // Source sorting.
+ int source_sort = SOURCE_SORT_ID;
+
+ struct SourceNameComparator {
+ static Ref<TileSet> tile_set;
+ bool operator()(const int &p_a, const int &p_b) const;
+ };
+
// Patterns preview generation.
struct QueueItem {
Ref<TileSet> tile_set;
@@ -97,11 +114,15 @@ public:
// To synchronize the atlas sources lists.
void set_sources_lists_current(int p_current);
- void synchronize_sources_list(Object *p_current);
+ void synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button);
void set_atlas_view_transform(float p_zoom, Vector2 p_scroll);
void synchronize_atlas_view(Object *p_current);
+ // Sorting.
+ void set_sorting_option(int p_option);
+ List<int> get_sorted_sources(const Ref<TileSet> tile_set) const;
+
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index f05ff72e5d..9ab53bf5c3 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -42,6 +42,7 @@
#include "scene/animation/animation_player.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel.h"
+#include "scene/gui/view_panner.h"
#include "scene/main/window.h"
#include "scene/resources/visual_shader_nodes.h"
#include "scene/resources/visual_shader_particle_nodes.h"
@@ -201,6 +202,10 @@ void VisualShaderGraphPlugin::set_input_port_default_value(VisualShader::Type p_
case Variant::FLOAT: {
button->set_text(String::num(p_value, 4));
} break;
+ case Variant::VECTOR2: {
+ Vector2 v = p_value;
+ button->set_text(String::num(v.x, 3) + "," + String::num(v.y, 3));
+ } break;
case Variant::VECTOR3: {
Vector3 v = p_value;
button->set_text(String::num(v.x, 3) + "," + String::num(v.y, 3) + "," + String::num(v.z, 3));
@@ -351,15 +356,17 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
if (!graph_plugin) {
return;
}
+ Shader::Mode mode = visual_shader->get_mode();
Control *offset;
static Ref<StyleBoxEmpty> label_style = make_empty_stylebox(2, 1, 2, 1);
- static const Color type_color[6] = {
+ static const Color type_color[7] = {
Color(0.38, 0.85, 0.96), // scalar (float)
Color(0.49, 0.78, 0.94), // scalar (int)
- Color(0.84, 0.49, 0.93), // vector
+ Color(0.74, 0.57, 0.95), // vector2
+ Color(0.84, 0.49, 0.93), // vector3
Color(0.55, 0.65, 0.94), // boolean
Color(0.96, 0.66, 0.43), // transform
Color(1.0, 1.0, 0.0), // sampler
@@ -595,8 +602,15 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
int output_port_count = 0;
for (int i = 0; i < vsnode->get_output_port_count(); i++) {
if (vsnode->_is_output_port_expanded(i)) {
- if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
- output_port_count += 3;
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ output_port_count += 2;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ output_port_count += 3;
+ } break;
+ default:
+ break;
}
}
output_port_count++;
@@ -606,10 +620,23 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
int expanded_port_counter = 0;
for (int i = 0, j = 0; i < max_ports; i++, j++) {
- if (expanded_type == VisualShaderNode::PORT_TYPE_VECTOR && expanded_port_counter >= 3) {
- expanded_type = VisualShaderNode::PORT_TYPE_SCALAR;
- expanded_port_counter = 0;
- i -= 3;
+ switch (expanded_type) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ if (expanded_port_counter >= 2) {
+ expanded_type = VisualShaderNode::PORT_TYPE_SCALAR;
+ expanded_port_counter = 0;
+ i -= 2;
+ }
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ if (expanded_port_counter >= 3) {
+ expanded_type = VisualShaderNode::PORT_TYPE_SCALAR;
+ expanded_port_counter = 0;
+ i -= 3;
+ }
+ } break;
+ default:
+ break;
}
if (vsnode->is_port_separator(i)) {
@@ -680,7 +707,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
hb->add_child(type_box);
type_box->add_item(TTR("Float"));
type_box->add_item(TTR("Int"));
- type_box->add_item(TTR("Vector"));
+ type_box->add_item(TTR("Vector2"));
+ type_box->add_item(TTR("Vector3"));
type_box->add_item(TTR("Boolean"));
type_box->add_item(TTR("Transform"));
type_box->add_item(TTR("Sampler"));
@@ -707,9 +735,9 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
label->add_theme_style_override("normal", label_style); //more compact
hb->add_child(label);
- if (vsnode->get_input_port_default_hint(i) != "" && !port_left_used) {
+ if (vsnode->is_input_port_default(i, mode) && !port_left_used) {
Label *hint_label = memnew(Label);
- hint_label->set_text("[" + vsnode->get_input_port_default_hint(i) + "]");
+ hint_label->set_text(TTR("[default]"));
hint_label->add_theme_color_override("font_color", editor->get_theme_color(SNAME("font_readonly_color"), SNAME("TextEdit")));
hint_label->add_theme_style_override("normal", label_style);
hb->add_child(hint_label);
@@ -741,7 +769,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
hb->add_child(type_box);
type_box->add_item(TTR("Float"));
type_box->add_item(TTR("Int"));
- type_box->add_item(TTR("Vector"));
+ type_box->add_item(TTR("Vector2"));
+ type_box->add_item(TTR("Vector3"));
type_box->add_item(TTR("Boolean"));
type_box->add_item(TTR("Transform"));
type_box->select(group_node->get_output_port_type(i));
@@ -803,31 +832,55 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
node->set_slot(idx, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]);
if (vsnode->_is_output_port_expanded(i)) {
- if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
- port_offset++;
- valid_left = (i + 1) < vsnode->get_input_port_count();
- port_left = VisualShaderNode::PORT_TYPE_SCALAR;
- if (valid_left) {
- port_left = vsnode->get_input_port_type(i + 1);
- }
- node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]);
- port_offset++;
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ port_offset++;
+ valid_left = (i + 1) < vsnode->get_input_port_count();
+ port_left = VisualShaderNode::PORT_TYPE_SCALAR;
+ if (valid_left) {
+ port_left = vsnode->get_input_port_type(i + 1);
+ }
+ node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]);
+ port_offset++;
- valid_left = (i + 2) < vsnode->get_input_port_count();
- port_left = VisualShaderNode::PORT_TYPE_SCALAR;
- if (valid_left) {
- port_left = vsnode->get_input_port_type(i + 2);
- }
- node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]);
- port_offset++;
+ valid_left = (i + 2) < vsnode->get_input_port_count();
+ port_left = VisualShaderNode::PORT_TYPE_SCALAR;
+ if (valid_left) {
+ port_left = vsnode->get_input_port_type(i + 2);
+ }
+ node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]);
+
+ expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_2D;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ port_offset++;
+ valid_left = (i + 1) < vsnode->get_input_port_count();
+ port_left = VisualShaderNode::PORT_TYPE_SCALAR;
+ if (valid_left) {
+ port_left = vsnode->get_input_port_type(i + 1);
+ }
+ node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]);
+ port_offset++;
- valid_left = (i + 3) < vsnode->get_input_port_count();
- port_left = VisualShaderNode::PORT_TYPE_SCALAR;
- if (valid_left) {
- port_left = vsnode->get_input_port_type(i + 3);
- }
- node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[2]);
- expanded_type = VisualShaderNode::PORT_TYPE_VECTOR;
+ valid_left = (i + 2) < vsnode->get_input_port_count();
+ port_left = VisualShaderNode::PORT_TYPE_SCALAR;
+ if (valid_left) {
+ port_left = vsnode->get_input_port_type(i + 2);
+ }
+ node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]);
+ port_offset++;
+
+ valid_left = (i + 3) < vsnode->get_input_port_count();
+ port_left = VisualShaderNode::PORT_TYPE_SCALAR;
+ if (valid_left) {
+ port_left = vsnode->get_input_port_type(i + 3);
+ }
+ node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[2]);
+
+ expanded_type = VisualShaderNode::PORT_TYPE_VECTOR;
+ } break;
+ default:
+ break;
}
}
}
@@ -840,7 +893,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
offset->set_custom_minimum_size(Size2(0, 4 * EDSCALE));
node->add_child(offset);
- String error = vsnode->get_warning(visual_shader->get_mode(), p_type);
+ String error = vsnode->get_warning(mode, p_type);
if (!error.is_empty()) {
Label *error_label = memnew(Label);
error_label->add_theme_color_override("font_color", editor->get_theme_color(SNAME("error_color"), SNAME("Editor")));
@@ -1028,7 +1081,7 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) {
}
void VisualShaderEditor::add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin) {
- if (plugins.find(p_plugin) != -1) {
+ if (plugins.has(p_plugin)) {
return;
}
plugins.push_back(p_plugin);
@@ -1257,7 +1310,9 @@ void VisualShaderEditor::_update_options_menu() {
if (input.is_valid()) {
input->set_shader_mode(visual_shader->get_mode());
input->set_shader_type(visual_shader->get_shader_type());
- input->set_input_name(add_options[i].sub_func_str);
+ if (!add_options[i].ops.is_empty() && add_options[i].ops[0].get_type() == Variant::STRING) {
+ input->set_input_name((String)add_options[i].ops[0]);
+ }
}
Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(vsn.ptr());
@@ -1374,6 +1429,9 @@ void VisualShaderEditor::_update_options_menu() {
case VisualShaderNode::PORT_TYPE_SCALAR_INT:
item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons")));
break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D:
+ item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")));
+ break;
case VisualShaderNode::PORT_TYPE_VECTOR:
item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")));
break;
@@ -1472,6 +1530,7 @@ void VisualShaderEditor::_update_uniforms(bool p_update_refs) {
if (uniform.is_valid()) {
Ref<VisualShaderNodeFloatUniform> float_uniform = vsnode;
Ref<VisualShaderNodeIntUniform> int_uniform = vsnode;
+ Ref<VisualShaderNodeVec2Uniform> vec2_uniform = vsnode;
Ref<VisualShaderNodeVec3Uniform> vec3_uniform = vsnode;
Ref<VisualShaderNodeColorUniform> color_uniform = vsnode;
Ref<VisualShaderNodeBooleanUniform> bool_uniform = vsnode;
@@ -1484,8 +1543,10 @@ void VisualShaderEditor::_update_uniforms(bool p_update_refs) {
uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_INT;
} else if (bool_uniform.is_valid()) {
uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_BOOLEAN;
+ } else if (vec2_uniform.is_valid()) {
+ uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_VECTOR2;
} else if (vec3_uniform.is_valid()) {
- uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_VECTOR;
+ uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_VECTOR3;
} else if (transform_uniform.is_valid()) {
uniform_type = VisualShaderNodeUniformRef::UniformType::UNIFORM_TYPE_TRANSFORM;
} else if (color_uniform.is_valid()) {
@@ -1724,8 +1785,15 @@ void VisualShaderEditor::_expand_output_port(int p_node, int p_port, bool p_expa
undo_redo->add_undo_method(node.ptr(), "_set_output_port_expanded", p_port, !p_expand);
int type_size = 0;
- if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_VECTOR) {
- type_size = 3;
+ switch (node->get_output_port_type(p_port)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ type_size = 2;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ type_size = 3;
+ } break;
+ default:
+ break;
}
List<VisualShader::Connection> conns;
@@ -2173,13 +2241,36 @@ void VisualShaderEditor::_edit_port_default_input(Object *p_button, int p_node,
editing_port = p_port;
}
-void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
+void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, const Vector<Variant> &p_ops) {
+ // INPUT
+ {
+ VisualShaderNodeInput *input = Object::cast_to<VisualShaderNodeInput>(p_node);
+
+ if (input) {
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::STRING);
+ input->set_input_name((String)p_ops[0]);
+ return;
+ }
+ }
+
+ // FLOAT_CONST
+ {
+ VisualShaderNodeFloatConstant *float_const = Object::cast_to<VisualShaderNodeFloatConstant>(p_node);
+
+ if (float_const) {
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::FLOAT);
+ float_const->set_constant((float)p_ops[0]);
+ return;
+ }
+ }
+
// FLOAT_OP
{
VisualShaderNodeFloatOp *floatOp = Object::cast_to<VisualShaderNodeFloatOp>(p_node);
if (floatOp) {
- floatOp->set_operator((VisualShaderNodeFloatOp::Operator)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ floatOp->set_operator((VisualShaderNodeFloatOp::Operator)(int)p_ops[0]);
return;
}
}
@@ -2189,7 +2280,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeFloatFunc *floatFunc = Object::cast_to<VisualShaderNodeFloatFunc>(p_node);
if (floatFunc) {
- floatFunc->set_function((VisualShaderNodeFloatFunc::Function)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ floatFunc->set_function((VisualShaderNodeFloatFunc::Function)(int)p_ops[0]);
return;
}
}
@@ -2199,7 +2291,10 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeVectorOp *vecOp = Object::cast_to<VisualShaderNodeVectorOp>(p_node);
if (vecOp) {
- vecOp->set_operator((VisualShaderNodeVectorOp::Operator)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ ERR_FAIL_COND(p_ops[1].get_type() != Variant::INT);
+ vecOp->set_operator((VisualShaderNodeVectorOp::Operator)(int)p_ops[0]);
+ vecOp->set_op_type((VisualShaderNodeVectorOp::OpType)(int)p_ops[1]);
return;
}
}
@@ -2209,7 +2304,10 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeVectorFunc *vecFunc = Object::cast_to<VisualShaderNodeVectorFunc>(p_node);
if (vecFunc) {
- vecFunc->set_function((VisualShaderNodeVectorFunc::Function)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ ERR_FAIL_COND(p_ops[1].get_type() != Variant::INT);
+ vecFunc->set_function((VisualShaderNodeVectorFunc::Function)(int)p_ops[0]);
+ vecFunc->set_op_type((VisualShaderNodeVectorFunc::OpType)(int)p_ops[1]);
return;
}
}
@@ -2219,7 +2317,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeColorOp *colorOp = Object::cast_to<VisualShaderNodeColorOp>(p_node);
if (colorOp) {
- colorOp->set_operator((VisualShaderNodeColorOp::Operator)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ colorOp->set_operator((VisualShaderNodeColorOp::Operator)(int)p_ops[0]);
return;
}
}
@@ -2229,7 +2328,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeColorFunc *colorFunc = Object::cast_to<VisualShaderNodeColorFunc>(p_node);
if (colorFunc) {
- colorFunc->set_function((VisualShaderNodeColorFunc::Function)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ colorFunc->set_function((VisualShaderNodeColorFunc::Function)(int)p_ops[0]);
return;
}
}
@@ -2239,7 +2339,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeIntOp *intOp = Object::cast_to<VisualShaderNodeIntOp>(p_node);
if (intOp) {
- intOp->set_operator((VisualShaderNodeIntOp::Operator)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ intOp->set_operator((VisualShaderNodeIntOp::Operator)(int)p_ops[0]);
return;
}
}
@@ -2249,7 +2350,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeIntFunc *intFunc = Object::cast_to<VisualShaderNodeIntFunc>(p_node);
if (intFunc) {
- intFunc->set_function((VisualShaderNodeIntFunc::Function)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ intFunc->set_function((VisualShaderNodeIntFunc::Function)(int)p_ops[0]);
return;
}
}
@@ -2259,7 +2361,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeTransformOp *matOp = Object::cast_to<VisualShaderNodeTransformOp>(p_node);
if (matOp) {
- matOp->set_operator((VisualShaderNodeTransformOp::Operator)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ matOp->set_operator((VisualShaderNodeTransformOp::Operator)(int)p_ops[0]);
return;
}
}
@@ -2269,17 +2372,41 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeTransformFunc *matFunc = Object::cast_to<VisualShaderNodeTransformFunc>(p_node);
if (matFunc) {
- matFunc->set_function((VisualShaderNodeTransformFunc::Function)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ matFunc->set_function((VisualShaderNodeTransformFunc::Function)(int)p_ops[0]);
+ return;
+ }
+ }
+
+ // VECTOR_COMPOSE
+ {
+ VisualShaderNodeVectorCompose *vecCompose = Object::cast_to<VisualShaderNodeVectorCompose>(p_node);
+
+ if (vecCompose) {
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ vecCompose->set_op_type((VisualShaderNodeVectorCompose::OpType)(int)p_ops[0]);
+ return;
+ }
+ }
+
+ // VECTOR_DECOMPOSE
+ {
+ VisualShaderNodeVectorDecompose *vecDecompose = Object::cast_to<VisualShaderNodeVectorDecompose>(p_node);
+
+ if (vecDecompose) {
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ vecDecompose->set_op_type((VisualShaderNodeVectorDecompose::OpType)(int)p_ops[0]);
return;
}
}
- //UV_FUNC
+ // UV_FUNC
{
VisualShaderNodeUVFunc *uvFunc = Object::cast_to<VisualShaderNodeUVFunc>(p_node);
if (uvFunc) {
- uvFunc->set_function((VisualShaderNodeUVFunc::Function)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ uvFunc->set_function((VisualShaderNodeUVFunc::Function)(int)p_ops[0]);
return;
}
}
@@ -2289,7 +2416,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeIs *is = Object::cast_to<VisualShaderNodeIs>(p_node);
if (is) {
- is->set_function((VisualShaderNodeIs::Function)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ is->set_function((VisualShaderNodeIs::Function)(int)p_ops[0]);
return;
}
}
@@ -2299,24 +2427,32 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeCompare *cmp = Object::cast_to<VisualShaderNodeCompare>(p_node);
if (cmp) {
- cmp->set_function((VisualShaderNodeCompare::Function)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ cmp->set_function((VisualShaderNodeCompare::Function)(int)p_ops[0]);
return;
}
}
- // DERIVATIVE
+ // DISTANCE
{
- VisualShaderNodeScalarDerivativeFunc *sderFunc = Object::cast_to<VisualShaderNodeScalarDerivativeFunc>(p_node);
+ VisualShaderNodeVectorDistance *dist = Object::cast_to<VisualShaderNodeVectorDistance>(p_node);
- if (sderFunc) {
- sderFunc->set_function((VisualShaderNodeScalarDerivativeFunc::Function)p_op_idx);
+ if (dist) {
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ dist->set_op_type((VisualShaderNodeVectorDistance::OpType)(int)p_ops[0]);
return;
}
+ }
- VisualShaderNodeVectorDerivativeFunc *vderFunc = Object::cast_to<VisualShaderNodeVectorDerivativeFunc>(p_node);
+ // DERIVATIVE
+ {
+ VisualShaderNodeDerivativeFunc *derFunc = Object::cast_to<VisualShaderNodeDerivativeFunc>(p_node);
- if (vderFunc) {
- vderFunc->set_function((VisualShaderNodeVectorDerivativeFunc::Function)p_op_idx);
+ if (derFunc) {
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ ERR_FAIL_COND(p_ops[1].get_type() != Variant::INT);
+ derFunc->set_function((VisualShaderNodeDerivativeFunc::Function)(int)p_ops[0]);
+ derFunc->set_op_type((VisualShaderNodeDerivativeFunc::OpType)(int)p_ops[1]);
return;
}
}
@@ -2326,7 +2462,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeMix *mix = Object::cast_to<VisualShaderNodeMix>(p_node);
if (mix) {
- mix->set_op_type((VisualShaderNodeMix::OpType)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ mix->set_op_type((VisualShaderNodeMix::OpType)(int)p_ops[0]);
return;
}
}
@@ -2336,7 +2473,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeClamp *clampFunc = Object::cast_to<VisualShaderNodeClamp>(p_node);
if (clampFunc) {
- clampFunc->set_op_type((VisualShaderNodeClamp::OpType)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ clampFunc->set_op_type((VisualShaderNodeClamp::OpType)(int)p_ops[0]);
return;
}
}
@@ -2346,7 +2484,28 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeSwitch *switchFunc = Object::cast_to<VisualShaderNodeSwitch>(p_node);
if (switchFunc) {
- switchFunc->set_op_type((VisualShaderNodeSwitch::OpType)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ switchFunc->set_op_type((VisualShaderNodeSwitch::OpType)(int)p_ops[0]);
+ return;
+ }
+ }
+
+ // FACEFORWARD
+ {
+ VisualShaderNodeFaceForward *faceForward = Object::cast_to<VisualShaderNodeFaceForward>(p_node);
+ if (faceForward) {
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ faceForward->set_op_type((VisualShaderNodeFaceForward::OpType)(int)p_ops[0]);
+ return;
+ }
+ }
+
+ // LENGTH
+ {
+ VisualShaderNodeVectorLen *length = Object::cast_to<VisualShaderNodeVectorLen>(p_node);
+ if (length) {
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ length->set_op_type((VisualShaderNodeVectorLen::OpType)(int)p_ops[0]);
return;
}
}
@@ -2356,7 +2515,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeSmoothStep *smoothStepFunc = Object::cast_to<VisualShaderNodeSmoothStep>(p_node);
if (smoothStepFunc) {
- smoothStepFunc->set_op_type((VisualShaderNodeSmoothStep::OpType)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ smoothStepFunc->set_op_type((VisualShaderNodeSmoothStep::OpType)(int)p_ops[0]);
return;
}
}
@@ -2366,7 +2526,8 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeStep *stepFunc = Object::cast_to<VisualShaderNodeStep>(p_node);
if (stepFunc) {
- stepFunc->set_op_type((VisualShaderNodeStep::OpType)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ stepFunc->set_op_type((VisualShaderNodeStep::OpType)(int)p_ops[0]);
return;
}
}
@@ -2376,12 +2537,13 @@ void VisualShaderEditor::_setup_node(VisualShaderNode *p_node, int p_op_idx) {
VisualShaderNodeMultiplyAdd *fmaFunc = Object::cast_to<VisualShaderNodeMultiplyAdd>(p_node);
if (fmaFunc) {
- fmaFunc->set_op_type((VisualShaderNodeMultiplyAdd::OpType)p_op_idx);
+ ERR_FAIL_COND(p_ops[0].get_type() != Variant::INT);
+ fmaFunc->set_op_type((VisualShaderNodeMultiplyAdd::OpType)(int)p_ops[0]);
}
}
}
-void VisualShaderEditor::_add_node(int p_idx, int p_op_idx, String p_resource_path, int p_node_idx) {
+void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, String p_resource_path, int p_node_idx) {
ERR_FAIL_INDEX(p_idx, add_options.size());
VisualShader::Type type = get_current_shader_type();
@@ -2393,25 +2555,9 @@ void VisualShaderEditor::_add_node(int p_idx, int p_op_idx, String p_resource_pa
if (!is_custom && !add_options[p_idx].type.is_empty()) {
VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instantiate(add_options[p_idx].type));
ERR_FAIL_COND(!vsn);
-
- VisualShaderNodeFloatConstant *constant = Object::cast_to<VisualShaderNodeFloatConstant>(vsn);
-
- if (constant) {
- if ((int)add_options[p_idx].value != -1) {
- constant->set_constant(add_options[p_idx].value);
- }
- } else {
- if (p_op_idx != -1) {
- VisualShaderNodeInput *input = Object::cast_to<VisualShaderNodeInput>(vsn);
-
- if (input) {
- input->set_input_name(add_options[p_idx].sub_func_str);
- } else {
- _setup_node(vsn, p_op_idx);
- }
- }
+ if (!p_ops.is_empty()) {
+ _setup_node(vsn, p_ops);
}
-
VisualShaderNodeUniformRef *uniform_ref = Object::cast_to<VisualShaderNodeUniformRef>(vsn);
if (uniform_ref && to_node != -1 && to_slot != -1) {
@@ -2499,6 +2645,9 @@ void VisualShaderEditor::_add_node(int p_idx, int p_op_idx, String p_resource_pa
case VisualShaderNode::PORT_TYPE_SCALAR_INT:
initial_expression_code = "output0 = 1;";
break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D:
+ initial_expression_code = "output0 = vec2(1.0, 1.0);";
+ break;
case VisualShaderNode::PORT_TYPE_VECTOR:
initial_expression_code = "output0 = vec3(1.0, 1.0, 1.0);";
break;
@@ -2909,6 +3058,25 @@ void VisualShaderEditor::_convert_constants_to_uniforms(bool p_vice_versa) {
}
}
+ // vec2
+ if (!caught) {
+ if (!p_vice_versa) {
+ Ref<VisualShaderNodeVec2Constant> vec2_const = Object::cast_to<VisualShaderNodeVec2Constant>(node.ptr());
+ if (vec2_const.is_valid()) {
+ _replace_node(type_id, node_id, "VisualShaderNodeVec2Constant", "VisualShaderNodeVec2Uniform");
+ var = vec2_const->get_constant();
+ caught = true;
+ }
+ } else {
+ Ref<VisualShaderNodeVec2Uniform> vec2_uniform = Object::cast_to<VisualShaderNodeVec2Uniform>(node.ptr());
+ if (vec2_uniform.is_valid()) {
+ _replace_node(type_id, node_id, "VisualShaderNodeVec2Uniform", "VisualShaderNodeVec2Constant");
+ var = vec2_uniform->get_default_value();
+ caught = true;
+ }
+ }
+ }
+
// vec3
if (!caught) {
if (!p_vice_versa) {
@@ -3222,7 +3390,8 @@ void VisualShaderEditor::_notification(int p_what) {
}
if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
- graph->set_panning_scheme((GraphEdit::PanningScheme)EDITOR_GET("interface/editors/sub_editor_panning_scheme").operator int());
+ graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
+ graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning")));
}
if (p_what == NOTIFICATION_DRAG_BEGIN) {
@@ -3693,7 +3862,7 @@ void VisualShaderEditor::_member_create() {
TreeItem *item = members->get_selected();
if (item != nullptr && item->has_meta("id")) {
int idx = members->get_selected()->get_meta("id");
- _add_node(idx, add_options[idx].sub_func);
+ _add_node(idx, add_options[idx].ops);
members_dialog->hide();
}
}
@@ -3796,11 +3965,6 @@ Variant VisualShaderEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f
Dictionary d;
d["id"] = id;
- if (op.sub_func == -1) {
- d["sub_func"] = op.sub_func_str;
- } else {
- d["sub_func"] = op.sub_func;
- }
Label *label = memnew(Label);
label->set_text(it->get_text(0));
@@ -3833,7 +3997,7 @@ void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
int idx = d["id"];
saved_node_pos = p_point;
saved_node_pos_dirty = true;
- _add_node(idx, add_options[idx].sub_func);
+ _add_node(idx, add_options[idx].ops);
} else if (d.has("files")) {
undo_redo->create_action(TTR("Add Node(s) to Visual Shader"));
@@ -3858,33 +4022,33 @@ void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
}
if (idx != -1) {
- _add_node(idx, -1, arr[i], i);
+ _add_node(idx, {}, arr[i], i);
}
}
} else if (type == "CurveTexture") {
saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE);
saved_node_pos_dirty = true;
- _add_node(curve_node_option_idx, -1, arr[i], i);
+ _add_node(curve_node_option_idx, {}, arr[i], i);
} else if (type == "CurveXYZTexture") {
saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE);
saved_node_pos_dirty = true;
- _add_node(curve_xyz_node_option_idx, -1, arr[i], i);
+ _add_node(curve_xyz_node_option_idx, {}, arr[i], i);
} else if (ClassDB::get_parent_class(type) == "Texture2D") {
saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE);
saved_node_pos_dirty = true;
- _add_node(texture2d_node_option_idx, -1, arr[i], i);
+ _add_node(texture2d_node_option_idx, {}, arr[i], i);
} else if (type == "Texture2DArray") {
saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE);
saved_node_pos_dirty = true;
- _add_node(texture2d_array_node_option_idx, -1, arr[i], i);
+ _add_node(texture2d_array_node_option_idx, {}, arr[i], i);
} else if (ClassDB::get_parent_class(type) == "Texture3D") {
saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE);
saved_node_pos_dirty = true;
- _add_node(texture3d_node_option_idx, -1, arr[i], i);
+ _add_node(texture3d_node_option_idx, {}, arr[i], i);
} else if (type == "Cubemap") {
saved_node_pos = p_point + Vector2(0, i * 250 * EDSCALE);
saved_node_pos_dirty = true;
- _add_node(cubemap_node_option_idx, -1, arr[i], i);
+ _add_node(cubemap_node_option_idx, {}, arr[i], i);
}
}
}
@@ -4017,6 +4181,7 @@ VisualShaderEditor::VisualShaderEditor() {
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR);
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR_INT);
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_BOOLEAN);
+ graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR_2D);
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR);
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_TRANSFORM);
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SAMPLER);
@@ -4036,18 +4201,27 @@ VisualShaderEditor::VisualShaderEditor() {
graph->connect("visibility_changed", callable_mp(this, &VisualShaderEditor::_visibility_changed));
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR_INT);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR_2D);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_BOOLEAN);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_SCALAR_INT);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_SCALAR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_VECTOR_2D);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_VECTOR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_BOOLEAN);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_SCALAR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_SCALAR_INT);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_VECTOR_2D);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_BOOLEAN);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_SCALAR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_SCALAR_INT);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR_2D);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_BOOLEAN);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_SCALAR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_SCALAR_INT);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR_2D);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_BOOLEAN);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM);
@@ -4272,94 +4446,99 @@ VisualShaderEditor::VisualShaderEditor() {
// COLOR
- add_options.push_back(AddOption("ColorFunc", "Color", "Common", "VisualShaderNodeColorFunc", TTR("Color function."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("ColorOp", "Color", "Common", "VisualShaderNodeColorOp", TTR("Color operator."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ColorFunc", "Color", "Common", "VisualShaderNodeColorFunc", TTR("Color function."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ColorOp", "Color", "Common", "VisualShaderNodeColorOp", TTR("Color operator."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Grayscale", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), VisualShaderNodeColorFunc::FUNC_GRAYSCALE, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts HSV vector to RGB equivalent."), VisualShaderNodeVectorFunc::FUNC_HSV2RGB, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts RGB vector to HSV equivalent."), VisualShaderNodeVectorFunc::FUNC_RGB2HSV, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Sepia", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), VisualShaderNodeColorFunc::FUNC_SEPIA, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Grayscale", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), { VisualShaderNodeColorFunc::FUNC_GRAYSCALE }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts HSV vector to RGB equivalent."), { VisualShaderNodeVectorFunc::FUNC_HSV2RGB }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts RGB vector to HSV equivalent."), { VisualShaderNodeVectorFunc::FUNC_RGB2HSV }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Sepia", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), { VisualShaderNodeColorFunc::FUNC_SEPIA }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Burn", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), VisualShaderNodeColorOp::OP_BURN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Darken", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Darken operator."), VisualShaderNodeColorOp::OP_DARKEN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Difference", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Difference operator."), VisualShaderNodeColorOp::OP_DIFFERENCE, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Dodge", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Dodge operator."), VisualShaderNodeColorOp::OP_DODGE, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("HardLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("HardLight operator."), VisualShaderNodeColorOp::OP_HARD_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Lighten", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Lighten operator."), VisualShaderNodeColorOp::OP_LIGHTEN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Overlay", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Overlay operator."), VisualShaderNodeColorOp::OP_OVERLAY, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Screen", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Screen operator."), VisualShaderNodeColorOp::OP_SCREEN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("SoftLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("SoftLight operator."), VisualShaderNodeColorOp::OP_SOFT_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Burn", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), { VisualShaderNodeColorOp::OP_BURN }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Darken", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Darken operator."), { VisualShaderNodeColorOp::OP_DARKEN }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Difference", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Difference operator."), { VisualShaderNodeColorOp::OP_DIFFERENCE }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Dodge", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Dodge operator."), { VisualShaderNodeColorOp::OP_DODGE }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("HardLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("HardLight operator."), { VisualShaderNodeColorOp::OP_HARD_LIGHT }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Lighten", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Lighten operator."), { VisualShaderNodeColorOp::OP_LIGHTEN }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Overlay", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Overlay operator."), { VisualShaderNodeColorOp::OP_OVERLAY }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Screen", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Screen operator."), { VisualShaderNodeColorOp::OP_SCREEN }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("SoftLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("SoftLight operator."), { VisualShaderNodeColorOp::OP_SOFT_LIGHT }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("ColorConstant", "Color", "Variables", "VisualShaderNodeColorConstant", TTR("Color constant."), -1, -1));
- add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform."), -1, -1));
+ add_options.push_back(AddOption("ColorConstant", "Color", "Variables", "VisualShaderNodeColorConstant", TTR("Color constant.")));
+ add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform.")));
+
+ // COMMON
+
+ add_options.push_back(AddOption("DerivativeFunc", "Common", "", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) Derivative function."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
// CONDITIONAL
const String &compare_func_desc = TTR("Returns the boolean result of the %s comparison between two parameters.");
- add_options.push_back(AddOption("Equal", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Equal (==)")), VisualShaderNodeCompare::FUNC_EQUAL, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("GreaterThan", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than (>)")), VisualShaderNodeCompare::FUNC_GREATER_THAN, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("GreaterThanEqual", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than or Equal (>=)")), VisualShaderNodeCompare::FUNC_GREATER_THAN_EQUAL, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("If", "Conditional", "Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("IsInf", "Conditional", "Functions", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between INF and a scalar parameter."), VisualShaderNodeIs::FUNC_IS_INF, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("IsNaN", "Conditional", "Functions", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between NaN and a scalar parameter."), VisualShaderNodeIs::FUNC_IS_NAN, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("LessThan", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than (<)")), VisualShaderNodeCompare::FUNC_LESS_THAN, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("LessThanEqual", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than or Equal (<=)")), VisualShaderNodeCompare::FUNC_LESS_THAN_EQUAL, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("NotEqual", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Not Equal (!=)")), VisualShaderNodeCompare::FUNC_NOT_EQUAL, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("Switch", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated vector if the provided boolean value is true or false."), VisualShaderNodeSwitch::OP_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("SwitchBool", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated boolean if the provided boolean value is true or false."), VisualShaderNodeSwitch::OP_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("SwitchFloat", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated floating-point scalar if the provided boolean value is true or false."), VisualShaderNodeSwitch::OP_TYPE_FLOAT, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("SwitchInt", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated integer scalar if the provided boolean value is true or false."), VisualShaderNodeSwitch::OP_TYPE_INT, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("SwitchTransform", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated transform if the provided boolean value is true or false."), VisualShaderNodeSwitch::OP_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM));
-
- add_options.push_back(AddOption("Compare", "Conditional", "Common", "VisualShaderNodeCompare", TTR("Returns the boolean result of the comparison between two parameters."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("Is", "Conditional", "Common", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between INF (or NaN) and a scalar parameter."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN));
-
- add_options.push_back(AddOption("BooleanConstant", "Conditional", "Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("BooleanUniform", "Conditional", "Variables", "VisualShaderNodeBooleanUniform", TTR("Boolean uniform."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("Equal", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Equal (==)")), { VisualShaderNodeCompare::FUNC_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("GreaterThan", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than (>)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("GreaterThanEqual", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than or Equal (>=)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("If", "Conditional", "Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("IsInf", "Conditional", "Functions", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between INF and a scalar parameter."), { VisualShaderNodeIs::FUNC_IS_INF }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("IsNaN", "Conditional", "Functions", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between NaN and a scalar parameter."), { VisualShaderNodeIs::FUNC_IS_NAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("LessThan", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than (<)")), { VisualShaderNodeCompare::FUNC_LESS_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("LessThanEqual", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than or Equal (<=)")), { VisualShaderNodeCompare::FUNC_LESS_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("NotEqual", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Not Equal (!=)")), { VisualShaderNodeCompare::FUNC_NOT_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("Switch", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 3D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Switch2D", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 2D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("SwitchBool", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated boolean if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_BOOLEAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("SwitchFloat", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated floating-point scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_FLOAT }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("SwitchInt", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated integer scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_INT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("SwitchTransform", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated transform if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_TRANSFORM }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+
+ add_options.push_back(AddOption("Compare", "Conditional", "Common", "VisualShaderNodeCompare", TTR("Returns the boolean result of the comparison between two parameters."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("Is", "Conditional", "Common", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between INF (or NaN) and a scalar parameter."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN));
+
+ add_options.push_back(AddOption("BooleanConstant", "Conditional", "Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("BooleanUniform", "Conditional", "Variables", "VisualShaderNodeBooleanUniform", TTR("Boolean uniform."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN));
// INPUT
const String input_param_shader_modes = TTR("'%s' input parameter for all shader modes.");
- // SPATIAL-FOR-ALL
+ // NODE3D-FOR-ALL
- add_options.push_back(AddOption("Camera", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "camera"), "camera", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("InvCamera", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_camera"), "inv_camera", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("InvProjection", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_projection"), "inv_projection", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Normal", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "normal"), "normal", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("OutputIsSRGB", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "output_is_srgb"), "output_is_srgb", VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Projection", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "projection"), "projection", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("UV", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "uv"), "uv", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("UV2", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "uv2"), "uv2", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("ViewportSize", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "viewport_size"), "viewport_size", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("World", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "world"), "world", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Camera", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "camera"), { "camera" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("InvCamera", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_camera"), { "inv_camera" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("InvProjection", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_projection"), { "inv_projection" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Normal", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "normal"), { "normal" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("OutputIsSRGB", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "output_is_srgb"), { "output_is_srgb" }, VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Projection", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "projection"), { "projection" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("UV", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "uv"), { "uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("UV2", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "uv2"), { "uv2" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("ViewportSize", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "viewport_size"), { "viewport_size" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("World", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "world"), { "world" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
// CANVASITEM-FOR-ALL
- add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("TexturePixelSize", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "texture_pixel_size"), "texture_pixel_size", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("UV", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "uv"), "uv", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("TexturePixelSize", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "texture_pixel_size"), { "texture_pixel_size" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, -1, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("UV", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "uv"), { "uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, -1, Shader::MODE_CANVAS_ITEM));
// PARTICLES-FOR-ALL
- add_options.push_back(AddOption("Active", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), "active", VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("AttractorForce", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "attractor_force"), "attractor_force", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Custom", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("CustomAlpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Delta", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("EmissionTransform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Index", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("LifeTime", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Restart", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), "restart", VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Transform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Velocity", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Active", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), { "active" }, VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("AttractorForce", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "attractor_force"), { "attractor_force" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Custom", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), { "custom" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("CustomAlpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), { "custom_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Delta", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), { "delta" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("EmissionTransform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), { "emission_transform" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Index", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "index"), { "index" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("LifeTime", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "lifetime"), { "lifetime" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Restart", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), { "restart" }, VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Transform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), { "transform" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Velocity", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), { "velocity" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
/////////////////
@@ -4381,370 +4560,428 @@ VisualShaderEditor::VisualShaderEditor() {
// NODE3D INPUTS
- add_options.push_back(AddOption("Alpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Binormal", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal"), "binormal", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("InstanceId", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_id"), "instance_id", VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom"), "instance_custom", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("InstanceCustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom_alpha"), "instance_custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("ModelView", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "modelview"), "modelview", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "point_size"), "point_size", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Tangent", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "tangent"), "tangent", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex"), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
-
- add_options.push_back(AddOption("Alpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Binormal", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal"), "binormal", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("DepthTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "depth_texture"), "depth_texture", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("FrontFacing", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "front_facing"), "front_facing", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "point_coord"), "point_coord", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("ScreenTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_texture"), "screen_texture", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_uv"), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Tangent", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "tangent"), "tangent", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex"), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("View", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view"), "view", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
-
- add_options.push_back(AddOption("Albedo", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "albedo"), "albedo", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Attenuation", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "attenuation"), "attenuation", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Backlight", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "backlight"), "backlight", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Diffuse", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "diffuse"), "diffuse", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light"), "light", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color"), "light_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Metallic", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "metallic"), "metallic", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Roughness", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "roughness"), "roughness", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Specular", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "specular"), "specular", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("View", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view"), "view", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Alpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Binormal", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("InstanceId", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_id"), { "instance_id" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("InstanceCustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom_alpha"), { "instance_custom_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("ModelView", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "modelview"), { "modelview" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "point_size"), { "point_size" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Tangent", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "tangent"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+
+ add_options.push_back(AddOption("Alpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Binormal", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("DepthTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "depth_texture"), { "depth_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("FrontFacing", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "front_facing"), { "front_facing" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "point_coord"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("ScreenTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_texture"), { "screen_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_uv"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Tangent", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "tangent"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("View", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+
+ add_options.push_back(AddOption("Albedo", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "albedo"), { "albedo" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Attenuation", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "attenuation"), { "attenuation" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Backlight", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "backlight"), { "backlight" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Diffuse", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "diffuse"), { "diffuse" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light"), { "light" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color"), { "light_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Metallic", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "metallic"), { "metallic" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Roughness", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "roughness"), { "roughness" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Specular", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "specular"), { "specular" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("View", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
// CANVASITEM INPUTS
- add_options.push_back(AddOption("AtLightPass", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "at_light_pass"), "at_light_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Canvas", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "canvas"), "canvas", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom"), "instance_custom", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("InstanceCustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom_alpha"), "instance_custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "point_size"), "point_size", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Screen", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "screen"), "screen", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "vertex"), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("World", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "world"), "world", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
-
- add_options.push_back(AddOption("AtLightPass", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "at_light_pass"), "at_light_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("NormalTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "normal_texture"), "normal_texture", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "point_coord"), "point_coord", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("ScreenPixelSize", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_pixel_size"), "screen_pixel_size", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("ScreenTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_texture"), "screen_texture", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv"), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("SpecularShininess", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess"), "specular_shininess", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("SpecularShininessAlpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess_alpha"), "specular_shininess_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("SpecularShininessTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "specular_shininess_texture"), "specular_shininess_texture", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Texture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "texture"), "texture", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "vertex"), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
-
- add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light"), "light", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("LightAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_alpha"), "light_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color"), "light_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("LightColorAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color_alpha"), "light_color_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("LightPosition", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_position"), "light_position", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("LightVertex", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "light_vertex"), "light_vertex", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Normal", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "normal"), "normal", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("PointCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "point_coord"), "point_coord", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("ScreenUV", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv"), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Shadow", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "shadow"), "shadow", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("ShadowAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "shadow_alpha"), "shadow_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("SpecularShininess", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess"), "specular_shininess", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("SpecularShininessAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess_alpha"), "specular_shininess_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Texture", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "texture"), "texture", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("AtLightPass", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "at_light_pass"), { "at_light_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Canvas", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "canvas"), { "canvas" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("InstanceCustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom_alpha"), { "instance_custom_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "point_size"), { "point_size" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Screen", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "screen"), { "screen" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "vertex"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("World", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "world"), { "world" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
+
+ add_options.push_back(AddOption("AtLightPass", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "at_light_pass"), { "at_light_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("NormalTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "normal_texture"), { "normal_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "point_coord"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("ScreenPixelSize", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_pixel_size"), { "screen_pixel_size" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("ScreenTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_texture"), { "screen_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("SpecularShininess", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess"), { "specular_shininess" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("SpecularShininessAlpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess_alpha"), { "specular_shininess_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("SpecularShininessTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "specular_shininess_texture"), { "specular_shininess_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Texture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "texture"), { "texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "vertex"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+
+ add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light"), { "light" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("LightAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_alpha"), { "light_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color"), { "light_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("LightColorAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color_alpha"), { "light_color_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("LightPosition", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_position"), { "light_position" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("LightVertex", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "light_vertex"), { "light_vertex" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Normal", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "normal"), { "normal" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("PointCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "point_coord"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("ScreenUV", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Shadow", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "shadow"), { "shadow" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("ShadowAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "shadow_alpha"), { "shadow_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("SpecularShininess", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess"), { "specular_shininess" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("SpecularShininessAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess_alpha"), { "specular_shininess_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Texture", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "texture"), { "texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
// SKY INPUTS
- add_options.push_back(AddOption("AtCubeMapPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_cubemap_pass"), "at_cubemap_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("AtHalfResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_half_res_pass"), "at_half_res_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("AtQuarterResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_quarter_res_pass"), "at_quarter_res_pass", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("EyeDir", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "eyedir"), "eyedir", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("HalfResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_color"), "half_res_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("HalfResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_alpha"), "half_res_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light0Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_color"), "light0_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light0Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_direction"), "light0_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light0Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_enabled"), "light0_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light0Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_energy"), "light0_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light1Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_color"), "light1_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light1Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_direction"), "light1_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light1Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_enabled"), "light1_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light1Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_energy"), "light1_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light2Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_color"), "light2_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light2Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_direction"), "light2_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light2Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_enabled"), "light2_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light2Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_energy"), "light2_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light3Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_color"), "light3_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light3Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_direction"), "light3_direction", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light3Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_enabled"), "light3_enabled", VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light3Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_energy"), "light3_energy", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Position", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "position"), "position", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("QuarterResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_color"), "quarter_res_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("QuarterResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_alpha"), "quarter_res_alpha", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Radiance", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "radiance"), "radiance", VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("ScreenUV", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "screen_uv"), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("SkyCoords", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "sky_coords"), "sky_coords", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Time", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("AtCubeMapPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_cubemap_pass"), { "at_cubemap_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("AtHalfResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_half_res_pass"), { "at_half_res_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("AtQuarterResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_quarter_res_pass"), { "at_quarter_res_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("EyeDir", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "eyedir"), { "eyedir" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("HalfResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_color"), { "half_res_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("HalfResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_alpha"), { "half_res_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light0Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_color"), { "light0_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light0Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_direction"), { "light0_direction" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light0Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_enabled"), { "light0_enabled" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light0Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_energy"), { "light0_energy" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light1Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_color"), { "light1_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light1Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_direction"), { "light1_direction" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light1Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_enabled"), { "light1_enabled" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light1Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_energy"), { "light1_energy" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light2Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_color"), { "light2_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light2Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_direction"), { "light2_direction" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light2Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_enabled"), { "light2_enabled" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light2Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_energy"), { "light2_energy" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light3Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_color"), { "light3_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light3Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_direction"), { "light3_direction" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light3Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_enabled"), { "light3_enabled" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light3Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_energy"), { "light3_energy" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Position", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "position"), { "position" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("QuarterResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_color"), { "quarter_res_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("QuarterResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_alpha"), { "quarter_res_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Radiance", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "radiance"), { "radiance" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("ScreenUV", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "screen_uv"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("SkyCoords", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "sky_coords"), { "sky_coords" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Time", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "time"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
// FOG INPUTS
- add_options.push_back(AddOption("WorldPosition", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "world_position"), "world_position", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
- add_options.push_back(AddOption("ObjectPosition", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "object_position"), "object_position", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
- add_options.push_back(AddOption("UVW", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "uvw"), "uvw", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
- add_options.push_back(AddOption("Extents", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "extents"), "extents", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
- add_options.push_back(AddOption("Transform", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_FOG, Shader::MODE_FOG));
- add_options.push_back(AddOption("SDF", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "sdf"), "sdf", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
- add_options.push_back(AddOption("Time", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("WorldPosition", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "world_position"), { "world_position" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("ObjectPosition", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "object_position"), { "object_position" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("UVW", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "uvw"), { "uvw" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("Extents", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "extents"), { "extents" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("SDF", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "sdf"), { "sdf" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("Time", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "time"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
// PARTICLES INPUTS
- add_options.push_back(AddOption("CollisionDepth", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_depth"), "collision_depth", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("CollisionNormal", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_normal"), "collision_normal", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("CollisionDepth", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_depth"), { "collision_depth" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("CollisionNormal", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_normal"), { "collision_normal" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
// PARTICLES
- add_options.push_back(AddOption("EmitParticle", "Particles", "", "VisualShaderNodeParticleEmit", "", -1, -1, TYPE_FLAGS_PROCESS | TYPE_FLAGS_PROCESS_CUSTOM | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("ParticleAccelerator", "Particles", "", "VisualShaderNodeParticleAccelerator", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("ParticleRandomness", "Particles", "", "VisualShaderNodeParticleRandomness", "", -1, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("MultiplyByAxisAngle", "Particles", "Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", "A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters.", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("EmitParticle", "Particles", "", "VisualShaderNodeParticleEmit", "", {}, -1, TYPE_FLAGS_PROCESS | TYPE_FLAGS_PROCESS_CUSTOM | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("ParticleAccelerator", "Particles", "", "VisualShaderNodeParticleAccelerator", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("ParticleRandomness", "Particles", "", "VisualShaderNodeParticleRandomness", "", {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("MultiplyByAxisAngle", "Particles", "Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", TTR("A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters."), {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("BoxEmitter", "Particles", "Emitters", "VisualShaderNodeParticleBoxEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("MeshEmitter", "Particles", "Emitters", "VisualShaderNodeParticleMeshEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("RingEmitter", "Particles", "Emitters", "VisualShaderNodeParticleRingEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("SphereEmitter", "Particles", "Emitters", "VisualShaderNodeParticleSphereEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("BoxEmitter", "Particles", "Emitters", "VisualShaderNodeParticleBoxEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("MeshEmitter", "Particles", "Emitters", "VisualShaderNodeParticleMeshEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("RingEmitter", "Particles", "Emitters", "VisualShaderNodeParticleRingEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("SphereEmitter", "Particles", "Emitters", "VisualShaderNodeParticleSphereEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("ConeVelocity", "Particles", "Velocity", "VisualShaderNodeParticleConeVelocity", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("ConeVelocity", "Particles", "Velocity", "VisualShaderNodeParticleConeVelocity", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
// SCALAR
- add_options.push_back(AddOption("FloatFunc", "Scalar", "Common", "VisualShaderNodeFloatFunc", TTR("Float function."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("IntFunc", "Scalar", "Common", "VisualShaderNodeIntFunc", TTR("Integer function."), -1, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("FloatOp", "Scalar", "Common", "VisualShaderNodeFloatOp", TTR("Float operator."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("IntOp", "Scalar", "Common", "VisualShaderNodeIntOp", TTR("Integer operator."), -1, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("FloatFunc", "Scalar", "Common", "VisualShaderNodeFloatFunc", TTR("Float function."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("FloatOp", "Scalar", "Common", "VisualShaderNodeFloatOp", TTR("Float operator."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("IntFunc", "Scalar", "Common", "VisualShaderNodeIntFunc", TTR("Integer function."), {}, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("IntOp", "Scalar", "Common", "VisualShaderNodeIntOp", TTR("Integer operator."), {}, VisualShaderNode::PORT_TYPE_SCALAR_INT));
// CONSTANTS
for (int i = 0; i < MAX_FLOAT_CONST_DEFS; i++) {
- add_options.push_back(AddOption(float_constant_defs[i].name, "Scalar", "Constants", "VisualShaderNodeFloatConstant", float_constant_defs[i].desc, -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, float_constant_defs[i].value));
+ add_options.push_back(AddOption(float_constant_defs[i].name, "Scalar", "Constants", "VisualShaderNodeFloatConstant", float_constant_defs[i].desc, { float_constant_defs[i].value }, VisualShaderNode::PORT_TYPE_SCALAR));
}
// FUNCTIONS
- add_options.push_back(AddOption("Abs", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the absolute value of the parameter."), VisualShaderNodeFloatFunc::FUNC_ABS, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Abs", "Scalar", "Functions", "VisualShaderNodeIntFunc", TTR("Returns the absolute value of the parameter."), VisualShaderNodeIntFunc::FUNC_ABS, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("ACos", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the arc-cosine of the parameter."), VisualShaderNodeFloatFunc::FUNC_ACOS, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("ACosH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), VisualShaderNodeFloatFunc::FUNC_ACOSH, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("ASin", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the arc-sine of the parameter."), VisualShaderNodeFloatFunc::FUNC_ASIN, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("ASinH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), VisualShaderNodeFloatFunc::FUNC_ASINH, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("ATan", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the arc-tangent of the parameter."), VisualShaderNodeFloatFunc::FUNC_ATAN, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("ATan2", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the arc-tangent of the parameters."), VisualShaderNodeFloatOp::OP_ATAN2, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("ATanH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), VisualShaderNodeFloatFunc::FUNC_ATANH, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("BitwiseNOT", "Scalar", "Functions", "VisualShaderNodeIntFunc", TTR("Returns the result of bitwise NOT (~a) operation on the integer."), VisualShaderNodeIntFunc::FUNC_BITWISE_NOT, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("Ceil", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), VisualShaderNodeFloatFunc::FUNC_CEIL, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Clamp", "Scalar", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), VisualShaderNodeClamp::OP_TYPE_FLOAT, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Clamp", "Scalar", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), VisualShaderNodeClamp::OP_TYPE_INT, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("Cos", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the cosine of the parameter."), VisualShaderNodeFloatFunc::FUNC_COS, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("CosH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the hyperbolic cosine of the parameter."), VisualShaderNodeFloatFunc::FUNC_COSH, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Degrees", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Converts a quantity in radians to degrees."), VisualShaderNodeFloatFunc::FUNC_DEGREES, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Exp", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-e Exponential."), VisualShaderNodeFloatFunc::FUNC_EXP, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Exp2", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-2 Exponential."), VisualShaderNodeFloatFunc::FUNC_EXP2, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Floor", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer less than or equal to the parameter."), VisualShaderNodeFloatFunc::FUNC_FLOOR, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Fract", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Computes the fractional part of the argument."), VisualShaderNodeFloatFunc::FUNC_FRAC, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("InverseSqrt", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse of the square root of the parameter."), VisualShaderNodeFloatFunc::FUNC_INVERSE_SQRT, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Log", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Natural logarithm."), VisualShaderNodeFloatFunc::FUNC_LOG, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Log2", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-2 logarithm."), VisualShaderNodeFloatFunc::FUNC_LOG2, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Max", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the greater of two values."), VisualShaderNodeFloatOp::OP_MAX, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Min", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the lesser of two values."), VisualShaderNodeFloatOp::OP_MIN, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Mix", "Scalar", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two scalars."), VisualShaderNodeMix::OP_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("MultiplyAdd", "Scalar", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on scalars."), VisualShaderNodeMultiplyAdd::OP_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeFloatFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeIntFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeIntFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("OneMinus", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("1.0 - scalar"), VisualShaderNodeFloatFunc::FUNC_ONEMINUS, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Pow", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeFloatOp::OP_POW, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Radians", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeFloatFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Reciprocal", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("1.0 / scalar"), VisualShaderNodeFloatFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Round", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer to the parameter."), VisualShaderNodeFloatFunc::FUNC_ROUND, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("RoundEven", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest even integer to the parameter."), VisualShaderNodeFloatFunc::FUNC_ROUNDEVEN, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Saturate", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Clamps the value between 0.0 and 1.0."), VisualShaderNodeFloatFunc::FUNC_SATURATE, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Sign", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Extracts the sign of the parameter."), VisualShaderNodeFloatFunc::FUNC_SIGN, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Sign", "Scalar", "Functions", "VisualShaderNodeIntFunc", TTR("Extracts the sign of the parameter."), VisualShaderNodeIntFunc::FUNC_SIGN, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("Sin", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the sine of the parameter."), VisualShaderNodeFloatFunc::FUNC_SIN, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("SinH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the hyperbolic sine of the parameter."), VisualShaderNodeFloatFunc::FUNC_SINH, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Sqrt", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the square root of the parameter."), VisualShaderNodeFloatFunc::FUNC_SQRT, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("SmoothStep", "Scalar", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), VisualShaderNodeSmoothStep::OP_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Step", "Scalar", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), VisualShaderNodeStep::OP_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Tan", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the tangent of the parameter."), VisualShaderNodeFloatFunc::FUNC_TAN, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("TanH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the hyperbolic tangent of the parameter."), VisualShaderNodeFloatFunc::FUNC_TANH, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Trunc", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the truncated value of the parameter."), VisualShaderNodeFloatFunc::FUNC_TRUNC, VisualShaderNode::PORT_TYPE_SCALAR));
-
- add_options.push_back(AddOption("Add", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Sums two floating-point scalars."), VisualShaderNodeFloatOp::OP_ADD, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Add", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Sums two integer scalars."), VisualShaderNodeIntOp::OP_ADD, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("BitwiseAND", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise AND (a & b) operation for two integers."), VisualShaderNodeIntOp::OP_BITWISE_AND, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("BitwiseLeftShift", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise left shift (a << b) operation on the integer."), VisualShaderNodeIntOp::OP_BITWISE_LEFT_SHIFT, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("BitwiseOR", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise OR (a | b) operation for two integers."), VisualShaderNodeIntOp::OP_BITWISE_OR, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("BitwiseRightShift", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise right shift (a >> b) operation on the integer."), VisualShaderNodeIntOp::OP_BITWISE_RIGHT_SHIFT, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("BitwiseXOR", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise XOR (a ^ b) operation on the integer."), VisualShaderNodeIntOp::OP_BITWISE_XOR, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("Divide", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Divides two floating-point scalars."), VisualShaderNodeFloatOp::OP_DIV, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Divide", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Divides two integer scalars."), VisualShaderNodeIntOp::OP_DIV, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("Multiply", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Multiplies two floating-point scalars."), VisualShaderNodeFloatOp::OP_MUL, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Multiply", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Multiplies two integer scalars."), VisualShaderNodeIntOp::OP_MUL, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("Remainder", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Returns the remainder of the two floating-point scalars."), VisualShaderNodeFloatOp::OP_MOD, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Remainder", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the remainder of the two integer scalars."), VisualShaderNodeIntOp::OP_MOD, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("Subtract", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Subtracts two floating-point scalars."), VisualShaderNodeFloatOp::OP_SUB, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Subtract", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Subtracts two integer scalars."), VisualShaderNodeIntOp::OP_SUB, VisualShaderNode::PORT_TYPE_SCALAR_INT));
-
- add_options.push_back(AddOption("FloatConstant", "Scalar", "Variables", "VisualShaderNodeFloatConstant", TTR("Scalar floating-point constant."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("IntConstant", "Scalar", "Variables", "VisualShaderNodeIntConstant", TTR("Scalar integer constant."), -1, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("FloatUniform", "Scalar", "Variables", "VisualShaderNodeFloatUniform", TTR("Scalar floating-point uniform."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("IntUniform", "Scalar", "Variables", "VisualShaderNodeIntUniform", TTR("Scalar integer uniform."), -1, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Abs", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the absolute value of the parameter."), { VisualShaderNodeFloatFunc::FUNC_ABS }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Abs", "Scalar", "Functions", "VisualShaderNodeIntFunc", TTR("Returns the absolute value of the parameter."), { VisualShaderNodeIntFunc::FUNC_ABS }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("ACos", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the arc-cosine of the parameter."), { VisualShaderNodeFloatFunc::FUNC_ACOS }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("ACosH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), { VisualShaderNodeFloatFunc::FUNC_ACOSH }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("ASin", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the arc-sine of the parameter."), { VisualShaderNodeFloatFunc::FUNC_ASIN }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("ASinH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), { VisualShaderNodeFloatFunc::FUNC_ASINH }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("ATan", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the arc-tangent of the parameter."), { VisualShaderNodeFloatFunc::FUNC_ATAN }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("ATan2", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the arc-tangent of the parameters."), { VisualShaderNodeFloatOp::OP_ATAN2 }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("ATanH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), { VisualShaderNodeFloatFunc::FUNC_ATANH }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("BitwiseNOT", "Scalar", "Functions", "VisualShaderNodeIntFunc", TTR("Returns the result of bitwise NOT (~a) operation on the integer."), { VisualShaderNodeIntFunc::FUNC_BITWISE_NOT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Ceil", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), { VisualShaderNodeFloatFunc::FUNC_CEIL }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Clamp", "Scalar", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), { VisualShaderNodeClamp::OP_TYPE_FLOAT }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Clamp", "Scalar", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), { VisualShaderNodeClamp::OP_TYPE_INT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Cos", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the cosine of the parameter."), { VisualShaderNodeFloatFunc::FUNC_COS }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("CosH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeFloatFunc::FUNC_COSH }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Degrees", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeFloatFunc::FUNC_DEGREES }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("DFdX", "Scalar", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("DFdY", "Scalar", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("Exp", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-e Exponential."), { VisualShaderNodeFloatFunc::FUNC_EXP }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Exp2", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-2 Exponential."), { VisualShaderNodeFloatFunc::FUNC_EXP2 }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Floor", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeFloatFunc::FUNC_FLOOR }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Fract", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeFloatFunc::FUNC_FRAC }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("InverseSqrt", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeFloatFunc::FUNC_INVERSE_SQRT }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Log", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Natural logarithm."), { VisualShaderNodeFloatFunc::FUNC_LOG }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Log2", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-2 logarithm."), { VisualShaderNodeFloatFunc::FUNC_LOG2 }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Max", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the greater of two values."), { VisualShaderNodeFloatOp::OP_MAX }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Min", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the lesser of two values."), { VisualShaderNodeFloatOp::OP_MIN }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Mix", "Scalar", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two scalars."), { VisualShaderNodeMix::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("MultiplyAdd", "Scalar", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on scalars."), { VisualShaderNodeMultiplyAdd::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeFloatFunc::FUNC_NEGATE }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeIntFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeIntFunc::FUNC_NEGATE }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("OneMinus", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("1.0 - scalar"), { VisualShaderNodeFloatFunc::FUNC_ONEMINUS }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Pow", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeFloatOp::OP_POW }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Radians", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeFloatFunc::FUNC_RADIANS }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Reciprocal", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("1.0 / scalar"), { VisualShaderNodeFloatFunc::FUNC_RECIPROCAL }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Round", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeFloatFunc::FUNC_ROUND }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("RoundEven", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest even integer to the parameter."), { VisualShaderNodeFloatFunc::FUNC_ROUNDEVEN }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Saturate", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Clamps the value between 0.0 and 1.0."), { VisualShaderNodeFloatFunc::FUNC_SATURATE }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Sign", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Extracts the sign of the parameter."), { VisualShaderNodeFloatFunc::FUNC_SIGN }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Sign", "Scalar", "Functions", "VisualShaderNodeIntFunc", TTR("Extracts the sign of the parameter."), { VisualShaderNodeIntFunc::FUNC_SIGN }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Sin", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the sine of the parameter."), { VisualShaderNodeFloatFunc::FUNC_SIN }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("SinH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the hyperbolic sine of the parameter."), { VisualShaderNodeFloatFunc::FUNC_SINH }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Sqrt", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the square root of the parameter."), { VisualShaderNodeFloatFunc::FUNC_SQRT }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("SmoothStep", "Scalar", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Step", "Scalar", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Sum", "Scalar", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("Tan", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeFloatFunc::FUNC_TAN }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("TanH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeFloatFunc::FUNC_TANH }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Trunc", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeFloatFunc::FUNC_TRUNC }, VisualShaderNode::PORT_TYPE_SCALAR));
+
+ add_options.push_back(AddOption("Add", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Sums two floating-point scalars."), { VisualShaderNodeFloatOp::OP_ADD }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Add", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Sums two integer scalars."), { VisualShaderNodeIntOp::OP_ADD }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseAND", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise AND (a & b) operation for two integers."), { VisualShaderNodeIntOp::OP_BITWISE_AND }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseLeftShift", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise left shift (a << b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_LEFT_SHIFT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseOR", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise OR (a | b) operation for two integers."), { VisualShaderNodeIntOp::OP_BITWISE_OR }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseRightShift", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise right shift (a >> b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_RIGHT_SHIFT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseXOR", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise XOR (a ^ b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_XOR }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Divide", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Divides two floating-point scalars."), { VisualShaderNodeFloatOp::OP_DIV }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Divide", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Divides two integer scalars."), { VisualShaderNodeIntOp::OP_DIV }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Multiply", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Multiplies two floating-point scalars."), { VisualShaderNodeFloatOp::OP_MUL }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Multiply", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Multiplies two integer scalars."), { VisualShaderNodeIntOp::OP_MUL }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Remainder", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Returns the remainder of the two floating-point scalars."), { VisualShaderNodeFloatOp::OP_MOD }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Remainder", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the remainder of the two integer scalars."), { VisualShaderNodeIntOp::OP_MOD }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Subtract", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Subtracts two floating-point scalars."), { VisualShaderNodeFloatOp::OP_SUB }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Subtract", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Subtracts two integer scalars."), { VisualShaderNodeIntOp::OP_SUB }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+
+ add_options.push_back(AddOption("FloatConstant", "Scalar", "Variables", "VisualShaderNodeFloatConstant", TTR("Scalar floating-point constant."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("IntConstant", "Scalar", "Variables", "VisualShaderNodeIntConstant", TTR("Scalar integer constant."), {}, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("FloatUniform", "Scalar", "Variables", "VisualShaderNodeFloatUniform", TTR("Scalar floating-point uniform."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("IntUniform", "Scalar", "Variables", "VisualShaderNodeIntUniform", TTR("Scalar integer uniform."), {}, VisualShaderNode::PORT_TYPE_SCALAR_INT));
// SDF
{
- add_options.push_back(AddOption("ScreenUVToSDF", "SDF", "", "VisualShaderNodeScreenUVToSDF", TTR("Converts screen UV to a SDF."), -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("SDFRaymarch", "SDF", "", "VisualShaderNodeSDFRaymarch", TTR("Casts a ray against the screen SDF and returns the distance travelled."), -1, -1, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("SDFToScreenUV", "SDF", "", "VisualShaderNodeSDFToScreenUV", TTR("Converts a SDF to screen UV."), -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("TextureSDF", "SDF", "", "VisualShaderNodeTextureSDF", TTR("Performs a SDF texture lookup."), -1, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("TextureSDFNormal", "SDF", "", "VisualShaderNodeTextureSDFNormal", TTR("Performs a SDF normal texture lookup."), -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("ScreenUVToSDF", "SDF", "", "VisualShaderNodeScreenUVToSDF", TTR("Converts screen UV to a SDF."), {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("SDFRaymarch", "SDF", "", "VisualShaderNodeSDFRaymarch", TTR("Casts a ray against the screen SDF and returns the distance travelled."), {}, -1, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("SDFToScreenUV", "SDF", "", "VisualShaderNodeSDFToScreenUV", TTR("Converts a SDF to screen UV."), {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("TextureSDF", "SDF", "", "VisualShaderNodeTextureSDF", TTR("Performs a SDF texture lookup."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("TextureSDFNormal", "SDF", "", "VisualShaderNodeTextureSDFNormal", TTR("Performs a SDF normal texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
}
// TEXTURES
- add_options.push_back(AddOption("UVFunc", "Textures", "Common", "VisualShaderNodeUVFunc", TTR("Function to be applied on texture coordinates."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("UVFunc", "Textures", "Common", "VisualShaderNodeUVFunc", TTR("Function to be applied on texture coordinates."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D));
cubemap_node_option_idx = add_options.size();
- add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubemap", TTR("Perform the cubic texture lookup."), -1, -1));
+ add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubemap", TTR("Perform the cubic texture lookup.")));
curve_node_option_idx = add_options.size();
- add_options.push_back(AddOption("CurveTexture", "Textures", "Functions", "VisualShaderNodeCurveTexture", TTR("Perform the curve texture lookup."), -1, -1));
+ add_options.push_back(AddOption("CurveTexture", "Textures", "Functions", "VisualShaderNodeCurveTexture", TTR("Perform the curve texture lookup.")));
curve_xyz_node_option_idx = add_options.size();
- add_options.push_back(AddOption("CurveXYZTexture", "Textures", "Functions", "VisualShaderNodeCurveXYZTexture", TTR("Perform the three components curve texture lookup."), -1, -1));
+ add_options.push_back(AddOption("CurveXYZTexture", "Textures", "Functions", "VisualShaderNodeCurveXYZTexture", TTR("Perform the three components curve texture lookup.")));
texture2d_node_option_idx = add_options.size();
- add_options.push_back(AddOption("Texture2D", "Textures", "Functions", "VisualShaderNodeTexture", TTR("Perform the 2D texture lookup."), -1, -1));
+ add_options.push_back(AddOption("Texture2D", "Textures", "Functions", "VisualShaderNodeTexture", TTR("Perform the 2D texture lookup.")));
texture2d_array_node_option_idx = add_options.size();
- add_options.push_back(AddOption("Texture2DArray", "Textures", "Functions", "VisualShaderNodeTexture2DArray", TTR("Perform the 2D-array texture lookup."), -1, -1, -1, -1, -1));
+ add_options.push_back(AddOption("Texture2DArray", "Textures", "Functions", "VisualShaderNodeTexture2DArray", TTR("Perform the 2D-array texture lookup.")));
texture3d_node_option_idx = add_options.size();
- add_options.push_back(AddOption("Texture3D", "Textures", "Functions", "VisualShaderNodeTexture3D", TTR("Perform the 3D texture lookup."), -1, -1));
- add_options.push_back(AddOption("UVPanning", "Textures", "Functions", "VisualShaderNodeUVFunc", TTR("Apply panning function on texture coordinates."), VisualShaderNodeUVFunc::FUNC_PANNING, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("UVScaling", "Textures", "Functions", "VisualShaderNodeUVFunc", TTR("Apply scaling function on texture coordinates."), VisualShaderNodeUVFunc::FUNC_SCALING, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Texture3D", "Textures", "Functions", "VisualShaderNodeTexture3D", TTR("Perform the 3D texture lookup.")));
+ add_options.push_back(AddOption("UVPanning", "Textures", "Functions", "VisualShaderNodeUVFunc", TTR("Apply panning function on texture coordinates."), { VisualShaderNodeUVFunc::FUNC_PANNING }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("UVScaling", "Textures", "Functions", "VisualShaderNodeUVFunc", TTR("Apply scaling function on texture coordinates."), { VisualShaderNodeUVFunc::FUNC_SCALING }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("CubeMapUniform", "Textures", "Variables", "VisualShaderNodeCubemapUniform", TTR("Cubic texture uniform lookup."), -1, -1));
- add_options.push_back(AddOption("TextureUniform", "Textures", "Variables", "VisualShaderNodeTextureUniform", TTR("2D texture uniform lookup."), -1, -1));
- add_options.push_back(AddOption("TextureUniformTriplanar", "Textures", "Variables", "VisualShaderNodeTextureUniformTriplanar", TTR("2D texture uniform lookup with triplanar."), -1, -1, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Texture2DArrayUniform", "Textures", "Variables", "VisualShaderNodeTexture2DArrayUniform", TTR("2D array of textures uniform lookup."), -1, -1, -1, -1, -1));
- add_options.push_back(AddOption("Texture3DUniform", "Textures", "Variables", "VisualShaderNodeTexture3DUniform", TTR("3D texture uniform lookup."), -1, -1, -1, -1, -1));
+ add_options.push_back(AddOption("CubeMapUniform", "Textures", "Variables", "VisualShaderNodeCubemapUniform", TTR("Cubic texture uniform lookup.")));
+ add_options.push_back(AddOption("TextureUniform", "Textures", "Variables", "VisualShaderNodeTextureUniform", TTR("2D texture uniform lookup.")));
+ add_options.push_back(AddOption("TextureUniformTriplanar", "Textures", "Variables", "VisualShaderNodeTextureUniformTriplanar", TTR("2D texture uniform lookup with triplanar."), {}, -1, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Texture2DArrayUniform", "Textures", "Variables", "VisualShaderNodeTexture2DArrayUniform", TTR("2D array of textures uniform lookup.")));
+ add_options.push_back(AddOption("Texture3DUniform", "Textures", "Variables", "VisualShaderNodeTexture3DUniform", TTR("3D texture uniform lookup.")));
// TRANSFORM
- add_options.push_back(AddOption("TransformFunc", "Transform", "Common", "VisualShaderNodeTransformFunc", TTR("Transform function."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("TransformOp", "Transform", "Common", "VisualShaderNodeTransformOp", TTR("Transform operator."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("TransformFunc", "Transform", "Common", "VisualShaderNodeTransformFunc", TTR("Transform function."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("TransformOp", "Transform", "Common", "VisualShaderNodeTransformOp", TTR("Transform operator."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("OuterProduct", "Transform", "Composition", "VisualShaderNodeOuterProduct", TTR("Calculate the outer product of a pair of vectors.\n\nOuterProduct treats the first parameter 'c' as a column vector (matrix with one column) and the second parameter 'r' as a row vector (matrix with one row) and does a linear algebraic matrix multiply 'c * r', yielding a matrix whose number of rows is the number of components in 'c' and whose number of columns is the number of components in 'r'."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("TransformCompose", "Transform", "Composition", "VisualShaderNodeTransformCompose", TTR("Composes transform from four vectors."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("OuterProduct", "Transform", "Composition", "VisualShaderNodeOuterProduct", TTR("Calculate the outer product of a pair of vectors.\n\nOuterProduct treats the first parameter 'c' as a column vector (matrix with one column) and the second parameter 'r' as a row vector (matrix with one row) and does a linear algebraic matrix multiply 'c * r', yielding a matrix whose number of rows is the number of components in 'c' and whose number of columns is the number of components in 'r'."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("TransformCompose", "Transform", "Composition", "VisualShaderNodeTransformCompose", TTR("Composes transform from four vectors."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM));
add_options.push_back(AddOption("TransformDecompose", "Transform", "Composition", "VisualShaderNodeTransformDecompose", TTR("Decomposes transform to four vectors.")));
- add_options.push_back(AddOption("Determinant", "Transform", "Functions", "VisualShaderNodeDeterminant", TTR("Calculates the determinant of a transform."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("GetBillboardMatrix", "Transform", "Functions", "VisualShaderNodeBillboard", TTR("Calculates how the object should face the camera to be applied on Model View Matrix output port for 3D objects."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Inverse", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the inverse of a transform."), VisualShaderNodeTransformFunc::FUNC_INVERSE, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("Transpose", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the transpose of a transform."), VisualShaderNodeTransformFunc::FUNC_TRANSPOSE, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("Determinant", "Transform", "Functions", "VisualShaderNodeDeterminant", TTR("Calculates the determinant of a transform."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("GetBillboardMatrix", "Transform", "Functions", "VisualShaderNodeBillboard", TTR("Calculates how the object should face the camera to be applied on Model View Matrix output port for 3D objects."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Inverse", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the inverse of a transform."), { VisualShaderNodeTransformFunc::FUNC_INVERSE }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("Transpose", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the transpose of a transform."), { VisualShaderNodeTransformFunc::FUNC_TRANSPOSE }, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("Add", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Sums two transforms."), VisualShaderNodeTransformOp::OP_ADD, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("Divide", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Divides two transforms."), VisualShaderNodeTransformOp::OP_A_DIV_B, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("Multiply", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Multiplies two transforms."), VisualShaderNodeTransformOp::OP_AxB, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("MultiplyComp", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Performs per-component multiplication of two transforms."), VisualShaderNodeTransformOp::OP_AxB_COMP, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("Subtract", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Subtracts two transforms."), VisualShaderNodeTransformOp::OP_A_MINUS_B, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("TransformVectorMult", "Transform", "Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Add", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Sums two transforms."), { VisualShaderNodeTransformOp::OP_ADD }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("Divide", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Divides two transforms."), { VisualShaderNodeTransformOp::OP_A_DIV_B }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("Multiply", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Multiplies two transforms."), { VisualShaderNodeTransformOp::OP_AxB }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("MultiplyComp", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Performs per-component multiplication of two transforms."), { VisualShaderNodeTransformOp::OP_AxB_COMP }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("Subtract", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Subtracts two transforms."), { VisualShaderNodeTransformOp::OP_A_MINUS_B }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("TransformVectorMult", "Transform", "Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("TransformConstant", "Transform", "Variables", "VisualShaderNodeTransformConstant", TTR("Transform constant."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("TransformUniform", "Transform", "Variables", "VisualShaderNodeTransformUniform", TTR("Transform uniform."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("TransformConstant", "Transform", "Variables", "VisualShaderNodeTransformConstant", TTR("Transform constant."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("TransformUniform", "Transform", "Variables", "VisualShaderNodeTransformUniform", TTR("Transform uniform."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM));
// VECTOR
- add_options.push_back(AddOption("VectorFunc", "Vector", "Common", "VisualShaderNodeVectorFunc", TTR("Vector function."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("VectorOp", "Vector", "Common", "VisualShaderNodeVectorOp", TTR("Vector operator."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
-
- add_options.push_back(AddOption("VectorCompose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes vector from three scalars."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("VectorDecompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes vector to three scalars.")));
-
- add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), VisualShaderNodeVectorFunc::FUNC_CEIL, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), VisualShaderNodeClamp::OP_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Cross", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Calculates the cross product of two vectors."), VisualShaderNodeVectorOp::OP_CROSS, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Distance", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Dot", "Vector", "Functions", "VisualShaderNodeDotProduct", TTR("Calculates the dot product of two vectors."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns the vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), VisualShaderNodeVectorOp::OP_MAX, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), VisualShaderNodeVectorOp::OP_MIN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors."), VisualShaderNodeMix::OP_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("MixS", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), VisualShaderNodeMix::OP_TYPE_VECTOR_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("MultiplyAdd", "Vector", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeVectorOp::OP_POW, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest even integer to the parameter."), VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), VisualShaderNodeSmoothStep::OP_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), VisualShaderNodeStep::OP_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), VisualShaderNodeStep::OP_TYPE_VECTOR_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNode::PORT_TYPE_VECTOR));
-
- add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds vector to vector."), VisualShaderNodeVectorOp::OP_ADD, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides vector by vector."), VisualShaderNodeVectorOp::OP_DIV, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies vector by vector."), VisualShaderNodeVectorOp::OP_MUL, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two vectors."), VisualShaderNodeVectorOp::OP_MOD, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts vector from vector."), VisualShaderNodeVectorOp::OP_SUB, VisualShaderNode::PORT_TYPE_VECTOR));
-
- add_options.push_back(AddOption("VectorConstant", "Vector", "Variables", "VisualShaderNodeVec3Constant", TTR("Vector constant."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("VectorUniform", "Vector", "Variables", "VisualShaderNodeVec3Uniform", TTR("Vector uniform."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("VectorFunc", "Vector", "Common", "VisualShaderNodeVectorFunc", TTR("Vector function."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("VectorOp", "Vector", "Common", "VisualShaderNodeVectorOp", TTR("Vector operator."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("VectorCompose", "Vector", "Common", "VisualShaderNodeVectorCompose", TTR("Composes vector from three scalars.")));
+ add_options.push_back(AddOption("VectorDecompose", "Vector", "Common", "VisualShaderNodeVectorDecompose", TTR("Decomposes vector to three scalars.")));
+
+ add_options.push_back(AddOption("Vector2Compose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes 2D vector from three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Vector2Decompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes 2D vector to three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_2D }));
+ add_options.push_back(AddOption("Vector3Compose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes 3D vector from three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Vector3Decompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes 3D vector to three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_3D }));
+
+ add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), { VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), { VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_CEIL }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_CEIL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), { VisualShaderNodeClamp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), { VisualShaderNodeClamp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Cross", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Calculates the cross product of two vectors."), { VisualShaderNodeVectorOp::OP_CROSS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("DFdX", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("DFdX", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("DFdY", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("DFdY", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("Distance2D", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), { VisualShaderNodeVectorDistance::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Distance3D", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), { VisualShaderNodeVectorDistance::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Dot", "Vector", "Functions", "VisualShaderNodeDotProduct", TTR("Calculates the dot product of two vectors."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns the vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), { VisualShaderNodeFaceForward::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns the vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), { VisualShaderNodeFaceForward::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Fresnel", "Vector", "Functions", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), { VisualShaderNodeVectorOp::OP_MAX, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), { VisualShaderNodeVectorOp::OP_MAX, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), { VisualShaderNodeVectorOp::OP_MIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), { VisualShaderNodeVectorOp::OP_MIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors."), { VisualShaderNodeMix::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors."), { VisualShaderNodeMix::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("MixS", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("MixS", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("MultiplyAdd", "Vector", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("MultiplyAdd", "Vector", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), { VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest even integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest even integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), { VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), { VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Sum", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("Sum", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+
+ add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds 2D vector to 2D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds 3D vector to 3D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 2D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 3D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 2D vector from 2D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 3D vector from 3D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+
+ add_options.push_back(AddOption("Vector2Constant", "Vector", "Variables", "VisualShaderNodeVec2Constant", TTR("2D vector constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Vector3Constant", "Vector", "Variables", "VisualShaderNodeVec3Constant", TTR("3D vector constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Vector2Uniform", "Vector", "Variables", "VisualShaderNodeVec2Uniform", TTR("2D vector uniform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Vector3Uniform", "Vector", "Variables", "VisualShaderNodeVec3Uniform", TTR("3D vector uniform."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
// SPECIAL
add_options.push_back(AddOption("Comment", "Special", "", "VisualShaderNodeComment", TTR("A rectangular area with a description string for better graph organization.")));
add_options.push_back(AddOption("Expression", "Special", "", "VisualShaderNodeExpression", TTR("Custom Godot Shader Language expression, with custom amount of input and output ports. This is a direct injection of code into the vertex/fragment/light function, do not use it to write the function declarations inside.")));
- add_options.push_back(AddOption("Fresnel", "Special", "", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("GlobalExpression", "Special", "", "VisualShaderNodeGlobalExpression", TTR("Custom Godot Shader Language expression, which is placed on top of the resulted shader. You can place various function definitions inside and call it later in the Expressions. You can also declare varyings, uniforms and constants.")));
add_options.push_back(AddOption("UniformRef", "Special", "", "VisualShaderNodeUniformRef", TTR("A reference to an existing uniform.")));
- add_options.push_back(AddOption("ScalarDerivativeFunc", "Special", "Common", "VisualShaderNodeScalarDerivativeFunc", TTR("(Fragment/Light mode only) Scalar derivative function."), -1, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, -1, true));
- add_options.push_back(AddOption("VectorDerivativeFunc", "Special", "Common", "VisualShaderNodeVectorDerivativeFunc", TTR("(Fragment/Light mode only) Vector derivative function."), -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, -1, true));
-
- add_options.push_back(AddOption("DdX", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), VisualShaderNodeVectorDerivativeFunc::FUNC_X, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, -1, true));
- add_options.push_back(AddOption("DdXS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Derivative in 'x' using local differencing."), VisualShaderNodeScalarDerivativeFunc::FUNC_X, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, -1, true));
- add_options.push_back(AddOption("DdY", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), VisualShaderNodeVectorDerivativeFunc::FUNC_Y, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, -1, true));
- add_options.push_back(AddOption("DdYS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Derivative in 'y' using local differencing."), VisualShaderNodeScalarDerivativeFunc::FUNC_Y, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, -1, true));
- add_options.push_back(AddOption("Sum", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), VisualShaderNodeVectorDerivativeFunc::FUNC_SUM, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, -1, true));
- add_options.push_back(AddOption("SumS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(Fragment/Light mode only) (Scalar) Sum of absolute derivative in 'x' and 'y'."), VisualShaderNodeScalarDerivativeFunc::FUNC_SUM, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, -1, true));
custom_node_option_idx = add_options.size();
/////////////////////////////////////////////////////////////////////
@@ -4829,9 +5066,10 @@ public:
void setup(const Ref<VisualShaderNodeInput> &p_input) {
input = p_input;
- Ref<Texture2D> type_icon[6] = {
+ Ref<Texture2D> type_icon[7] = {
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons")),
+ EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")),
@@ -4877,10 +5115,11 @@ public:
void setup(const Ref<VisualShaderNodeUniformRef> &p_uniform_ref) {
uniform_ref = p_uniform_ref;
- Ref<Texture2D> type_icon[7] = {
+ Ref<Texture2D> type_icon[8] = {
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")),
+ EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Color"), SNAME("EditorIcons")),
@@ -4967,7 +5206,7 @@ public:
}
void _open_inspector(RES p_resource) {
- EditorNode::get_singleton()->get_inspector()->edit(p_resource.ptr());
+ InspectorDock::get_inspector_singleton()->edit(p_resource.ptr());
}
bool updating;
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index 2b837ef4a0..c5037853cd 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -247,44 +247,25 @@ class VisualShaderEditor : public VBoxContainer {
String category;
String type;
String description;
- int sub_func = 0;
- String sub_func_str;
+ Vector<Variant> ops;
Ref<Script> script;
int mode = 0;
int return_type = 0;
int func = 0;
- float value = 0;
bool highend = false;
bool is_custom = false;
int temp_idx = 0;
- AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_sub_category = String(), const String &p_type = String(), const String &p_description = String(), int p_sub_func = -1, int p_return_type = -1, int p_mode = -1, int p_func = -1, float p_value = -1, bool p_highend = false) {
+ AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_sub_category = String(), const String &p_type = String(), const String &p_description = String(), const Vector<Variant> &p_ops = Vector<Variant>(), int p_return_type = -1, int p_mode = -1, int p_func = -1, bool p_highend = false) {
name = p_name;
type = p_type;
category = p_category + "/" + p_sub_category;
description = p_description;
- sub_func = p_sub_func;
+ ops = p_ops;
return_type = p_return_type;
mode = p_mode;
func = p_func;
- value = p_value;
highend = p_highend;
- is_custom = false;
- }
-
- AddOption(const String &p_name, const String &p_category, const String &p_sub_category, const String &p_type, const String &p_description, const String &p_sub_func, int p_return_type = -1, int p_mode = -1, int p_func = -1, float p_value = -1, bool p_highend = false) {
- name = p_name;
- type = p_type;
- category = p_category + "/" + p_sub_category;
- description = p_description;
- sub_func = 0;
- sub_func_str = p_sub_func;
- return_type = p_return_type;
- mode = p_mode;
- func = p_func;
- value = p_value;
- highend = p_highend;
- is_custom = false;
}
};
struct _OptionComparator {
@@ -307,8 +288,8 @@ class VisualShaderEditor : public VBoxContainer {
void _draw_color_over_button(Object *obj, Color p_color);
- void _setup_node(VisualShaderNode *p_node, int p_op_idx);
- void _add_node(int p_idx, int p_op_idx = -1, String p_resource_path = "", int p_node_idx = -1);
+ void _setup_node(VisualShaderNode *p_node, const Vector<Variant> &p_ops);
+ void _add_node(int p_idx, const Vector<Variant> &p_ops, String p_resource_path = "", int p_node_idx = -1);
void _update_options_menu();
void _set_mode(int p_which);
diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp
index 1fd47b67c5..cef29032b2 100644
--- a/editor/plugins/voxel_gi_editor_plugin.cpp
+++ b/editor/plugins/voxel_gi_editor_plugin.cpp
@@ -115,7 +115,7 @@ EditorProgress *VoxelGIEditorPlugin::tmp_progress = nullptr;
void VoxelGIEditorPlugin::bake_func_begin(int p_steps) {
ERR_FAIL_COND(tmp_progress != nullptr);
- tmp_progress = memnew(EditorProgress("bake_gi", TTR("Bake GI Probe"), p_steps));
+ tmp_progress = memnew(EditorProgress("bake_gi", TTR("Bake VoxelGI"), p_steps));
}
void VoxelGIEditorPlugin::bake_func_step(int p_step, const String &p_description) {
@@ -149,7 +149,7 @@ VoxelGIEditorPlugin::VoxelGIEditorPlugin(EditorNode *p_node) {
bake = memnew(Button);
bake->set_flat(true);
bake->set_icon(editor->get_gui_base()->get_theme_icon(SNAME("Bake"), SNAME("EditorIcons")));
- bake->set_text(TTR("Bake GI Probe"));
+ bake->set_text(TTR("Bake VoxelGI"));
bake->connect("pressed", callable_mp(this, &VoxelGIEditorPlugin::_bake));
bake_hb->add_child(bake);
diff --git a/editor/project_export.cpp b/editor/project_export.cpp
index 9bd8c1e227..f39a494df8 100644
--- a/editor/project_export.cpp
+++ b/editor/project_export.cpp
@@ -752,12 +752,10 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem
p_item->set_metadata(0, p_dir->get_path());
bool used = false;
- bool checked = true;
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
TreeItem *subdir = include_files->create_item(p_item);
if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_only_scenes)) {
used = true;
- checked = checked && subdir->is_checked(0);
} else {
memdelete(subdir);
}
@@ -782,12 +780,10 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem
file->set_editable(0, true);
file->set_checked(0, current->has_export_file(path));
file->set_metadata(0, path);
- checked = checked && file->is_checked(0);
+ file->propagate_check(0);
used = true;
}
-
- p_item->set_checked(0, checked);
return used;
}
@@ -806,54 +802,24 @@ void ProjectExportDialog::_tree_changed() {
return;
}
- String path = item->get_metadata(0);
- bool added = item->is_checked(0);
+ item->propagate_check(0);
+}
- if (path.ends_with("/")) {
- _check_dir_recursive(item, added);
- } else {
+void ProjectExportDialog::_check_propagated_to_item(Object *p_obj, int column) {
+ Ref<EditorExportPreset> current = get_current_preset();
+ if (current.is_null()) {
+ return;
+ }
+ TreeItem *item = Object::cast_to<TreeItem>(p_obj);
+ String path = item->get_metadata(0);
+ if (item && !path.ends_with("/")) {
+ bool added = item->is_checked(0);
if (added) {
current->add_export_file(path);
} else {
current->remove_export_file(path);
}
}
- _refresh_parent_checks(item); // Makes parent folder checked if all files/folders are checked.
-}
-
-void ProjectExportDialog::_check_dir_recursive(TreeItem *p_dir, bool p_checked) {
- for (TreeItem *child = p_dir->get_first_child(); child; child = child->get_next()) {
- String path = child->get_metadata(0);
-
- child->set_checked(0, p_checked);
- if (path.ends_with("/")) {
- _check_dir_recursive(child, p_checked);
- } else {
- if (p_checked) {
- get_current_preset()->add_export_file(path);
- } else {
- get_current_preset()->remove_export_file(path);
- }
- }
- }
-}
-
-void ProjectExportDialog::_refresh_parent_checks(TreeItem *p_item) {
- TreeItem *parent = p_item->get_parent();
- if (!parent) {
- return;
- }
-
- bool checked = true;
- for (TreeItem *child = parent->get_first_child(); child; child = child->get_next()) {
- checked = checked && child->is_checked(0);
- if (!checked) {
- break;
- }
- }
- parent->set_checked(0, checked);
-
- _refresh_parent_checks(parent);
}
void ProjectExportDialog::_export_pck_zip() {
@@ -1126,6 +1092,7 @@ ProjectExportDialog::ProjectExportDialog() {
include_files = memnew(Tree);
include_margin->add_child(include_files);
include_files->connect("item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed));
+ include_files->connect("check_propagated_to_item", callable_mp(this, &ProjectExportDialog::_check_propagated_to_item));
include_filters = memnew(LineEdit);
resources_vb->add_margin_child(
diff --git a/editor/project_export.h b/editor/project_export.h
index af7ec083c4..3d90a0d3ff 100644
--- a/editor/project_export.h
+++ b/editor/project_export.h
@@ -124,8 +124,7 @@ private:
void _fill_resource_tree();
bool _fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> &current, bool p_only_scenes);
void _tree_changed();
- void _check_dir_recursive(TreeItem *p_dir, bool p_checked);
- void _refresh_parent_checks(TreeItem *p_item);
+ void _check_propagated_to_item(Object *p_obj, int column);
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 1bf6243bcc..bedae4f8a9 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "editor/editor_export.h"
+#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
@@ -47,7 +48,8 @@ void ProjectSettingsEditor::popup_project_settings() {
}
_add_feature_overrides();
- inspector->update_category_list();
+ general_settings_inspector->update_category_list();
+ set_process_unhandled_input(true);
localization_editor->update_translations();
autoload_settings->update_autoload();
@@ -74,7 +76,7 @@ void ProjectSettingsEditor::_setting_edited(const String &p_name) {
void ProjectSettingsEditor::_advanced_toggled(bool p_button_pressed) {
EditorSettings::get_singleton()->set_project_metadata("project_settings", "advanced_mode", p_button_pressed);
- inspector->set_restrict_to_basic_settings(!p_button_pressed);
+ general_settings_inspector->set_restrict_to_basic_settings(!p_button_pressed);
}
void ProjectSettingsEditor::_setting_selected(const String &p_path) {
@@ -82,7 +84,7 @@ void ProjectSettingsEditor::_setting_selected(const String &p_path) {
return;
}
- property_box->set_text(inspector->get_current_section() + "/" + p_path);
+ property_box->set_text(general_settings_inspector->get_current_section() + "/" + p_path);
_update_property_box(); // set_text doesn't trigger text_changed
}
@@ -99,13 +101,13 @@ void ProjectSettingsEditor::_add_setting() {
undo_redo->add_do_property(ps, setting, value);
undo_redo->add_undo_property(ps, setting, ps->has_setting(setting) ? ps->get(setting) : Variant());
- undo_redo->add_do_method(inspector, "update_category_list");
- undo_redo->add_undo_method(inspector, "update_category_list");
+ undo_redo->add_do_method(general_settings_inspector, "update_category_list");
+ undo_redo->add_undo_method(general_settings_inspector, "update_category_list");
undo_redo->add_do_method(this, "queue_save");
undo_redo->add_undo_method(this, "queue_save");
undo_redo->commit_action();
- inspector->set_current_section(setting.get_slice("/", 1));
+ general_settings_inspector->set_current_section(setting.get_slice("/", 1));
add_button->release_focus();
}
@@ -120,8 +122,8 @@ void ProjectSettingsEditor::_delete_setting() {
undo_redo->add_undo_method(ps, "set", setting, value);
undo_redo->add_undo_method(ps, "set_order", setting, order);
- undo_redo->add_do_method(inspector, "update_category_list");
- undo_redo->add_undo_method(inspector, "update_category_list");
+ undo_redo->add_do_method(general_settings_inspector, "update_category_list");
+ undo_redo->add_undo_method(general_settings_inspector, "update_category_list");
undo_redo->add_do_method(this, "queue_save");
undo_redo->add_undo_method(this, "queue_save");
@@ -200,6 +202,44 @@ void ProjectSettingsEditor::_select_type(Variant::Type p_type) {
type_box->select(type_box->get_item_index(p_type));
}
+void ProjectSettingsEditor::unhandled_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
+ const Ref<InputEventKey> k = p_event;
+
+ if (k.is_valid() && k->is_pressed()) {
+ bool handled = false;
+
+ if (ED_IS_SHORTCUT("ui_undo", p_event)) {
+ String action = undo_redo->get_current_action_name();
+ if (!action.is_empty()) {
+ EditorNode::get_log()->add_message("Undo: " + action, EditorLog::MSG_TYPE_EDITOR);
+ }
+ undo_redo->undo();
+ handled = true;
+ }
+
+ if (ED_IS_SHORTCUT("ui_redo", p_event)) {
+ undo_redo->redo();
+ String action = undo_redo->get_current_action_name();
+ if (!action.is_empty()) {
+ EditorNode::get_log()->add_message("Redo: " + action, EditorLog::MSG_TYPE_EDITOR);
+ }
+ handled = true;
+ }
+
+ if (k->get_keycode_with_modifiers() == (KeyModifierMask::CMD | Key::F)) {
+ search_box->grab_focus();
+ search_box->select_all();
+ handled = true;
+ }
+
+ if (handled) {
+ set_input_as_handled();
+ }
+ }
+}
+
String ProjectSettingsEditor::_get_setting_name() const {
String name = property_box->get_text().strip_edges();
if (name.find("/") == -1) {
@@ -215,7 +255,6 @@ void ProjectSettingsEditor::_add_feature_overrides() {
presets.insert("s3tc");
presets.insert("etc");
presets.insert("etc2");
- presets.insert("pvrtc");
presets.insert("debug");
presets.insert("release");
presets.insert("editor");
@@ -464,7 +503,15 @@ void ProjectSettingsEditor::_update_action_map_editor() {
actions.push_back(action_info);
}
- action_map->update_action_list(actions);
+ action_map_editor->update_action_list(actions);
+}
+
+void ProjectSettingsEditor::_update_theme() {
+ search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ restart_close_button->set_icon(get_theme_icon(SNAME("Close"), SNAME("EditorIcons")));
+ restart_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ restart_icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
+ restart_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
}
void ProjectSettingsEditor::_notification(int p_what) {
@@ -475,22 +522,13 @@ void ProjectSettingsEditor::_notification(int p_what) {
}
} break;
case NOTIFICATION_ENTER_TREE: {
- inspector->edit(ps);
-
- search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
- search_box->set_clear_button_enabled(true);
-
- restart_close_button->set_icon(get_theme_icon(SNAME("Close"), SNAME("EditorIcons")));
- restart_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
- restart_icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
- restart_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
-
+ general_settings_inspector->edit(ps);
_update_action_map_editor();
+ _update_theme();
} break;
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
- search_box->set_clear_button_enabled(true);
- } break;
+ case NOTIFICATION_THEME_CHANGED:
+ _update_theme();
+ break;
}
}
@@ -524,6 +562,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
search_box = memnew(LineEdit);
search_box->set_placeholder(TTR("Filter Settings"));
+ search_box->set_clear_button_enabled(true);
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search_bar->add_child(search_box);
@@ -570,14 +609,14 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
del_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_delete_setting));
header->add_child(del_button);
- inspector = memnew(SectionedInspector);
- inspector->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo());
- inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- inspector->register_search_box(search_box);
- inspector->get_inspector()->connect("property_selected", callable_mp(this, &ProjectSettingsEditor::_setting_selected));
- inspector->get_inspector()->connect("property_edited", callable_mp(this, &ProjectSettingsEditor::_setting_edited));
- inspector->get_inspector()->connect("restart_requested", callable_mp(this, &ProjectSettingsEditor::_editor_restart_request));
- general_editor->add_child(inspector);
+ general_settings_inspector = memnew(SectionedInspector);
+ general_settings_inspector->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo());
+ general_settings_inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ general_settings_inspector->register_search_box(search_box);
+ general_settings_inspector->get_inspector()->connect("property_selected", callable_mp(this, &ProjectSettingsEditor::_setting_selected));
+ general_settings_inspector->get_inspector()->connect("property_edited", callable_mp(this, &ProjectSettingsEditor::_setting_edited));
+ general_settings_inspector->get_inspector()->connect("restart_requested", callable_mp(this, &ProjectSettingsEditor::_editor_restart_request));
+ general_editor->add_child(general_settings_inspector);
restart_container = memnew(PanelContainer);
general_editor->add_child(restart_container);
@@ -605,14 +644,14 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
restart_close_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_editor_restart_close));
restart_hb->add_child(restart_close_button);
- action_map = memnew(ActionMapEditor);
- action_map->set_name(TTR("Input Map"));
- action_map->connect("action_added", callable_mp(this, &ProjectSettingsEditor::_action_added));
- action_map->connect("action_edited", callable_mp(this, &ProjectSettingsEditor::_action_edited));
- action_map->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed));
- action_map->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed));
- action_map->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered));
- tab_container->add_child(action_map);
+ action_map_editor = memnew(ActionMapEditor);
+ action_map_editor->set_name(TTR("Input Map"));
+ action_map_editor->connect("action_added", callable_mp(this, &ProjectSettingsEditor::_action_added));
+ action_map_editor->connect("action_edited", callable_mp(this, &ProjectSettingsEditor::_action_edited));
+ action_map_editor->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed));
+ action_map_editor->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed));
+ action_map_editor->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered));
+ tab_container->add_child(action_map_editor);
localization_editor = memnew(LocalizationEditor);
localization_editor->set_name(TTR("Localization"));
@@ -648,7 +687,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
advanced->set_pressed(true);
}
- inspector->set_restrict_to_basic_settings(!use_advanced);
+ general_settings_inspector->set_restrict_to_basic_settings(!use_advanced);
import_defaults_editor = memnew(ImportDefaultsEditor);
import_defaults_editor->set_name(TTR("Import Defaults"));
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index 26af73c54e..d48c2b76ca 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -50,8 +50,8 @@ class ProjectSettingsEditor : public AcceptDialog {
Timer *timer;
TabContainer *tab_container;
- SectionedInspector *inspector;
- ActionMapEditor *action_map;
+ SectionedInspector *general_settings_inspector;
+ ActionMapEditor *action_map_editor;
LocalizationEditor *localization_editor;
EditorAutoloadSettings *autoload_settings;
ShaderGlobalsEditor *shaders_global_variables_editor;
@@ -81,6 +81,8 @@ class ProjectSettingsEditor : public AcceptDialog {
void _feature_selected(int p_index);
void _select_type(Variant::Type p_type);
+ virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
+
String _get_setting_name() const;
void _setting_edited(const String &p_name);
void _setting_selected(const String &p_path);
@@ -99,6 +101,7 @@ class ProjectSettingsEditor : public AcceptDialog {
void _action_renamed(const String &p_old_name, const String &p_new_name);
void _action_reordered(const String &p_action_name, const String &p_relative_to, bool p_before);
void _update_action_map_editor();
+ void _update_theme();
protected:
void _notification(int p_what);
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index 481ff1a781..9d894afa6f 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -208,19 +208,19 @@ void CustomPropertyEditor::_menu_option(int p_which) {
} break;
case OBJ_MENU_NEW_SCRIPT: {
if (Object::cast_to<Node>(owner)) {
- EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(owner), false);
+ SceneTreeDock::get_singleton()->open_script_dialog(Object::cast_to<Node>(owner), false);
}
} break;
case OBJ_MENU_EXTEND_SCRIPT: {
if (Object::cast_to<Node>(owner)) {
- EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(owner), true);
+ SceneTreeDock::get_singleton()->open_script_dialog(Object::cast_to<Node>(owner), true);
}
} break;
case OBJ_MENU_SHOW_IN_FILE_SYSTEM: {
RES r = v;
- FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
+ FileSystemDock *file_system_dock = FileSystemDock::get_singleton();
file_system_dock->navigate_to_path(r->get_path());
// Ensure that the FileSystem dock is visible.
TabContainer *tab_container = (TabContainer *)file_system_dock->get_parent_control();
@@ -506,12 +506,16 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
} break;
case Variant::STRING: {
- if (hint == PROPERTY_HINT_FILE || hint == PROPERTY_HINT_GLOBAL_FILE) {
+ if (hint == PROPERTY_HINT_LOCALE_ID) {
+ List<String> names;
+ names.push_back(TTR("Locale..."));
+ names.push_back(TTR("Clear"));
+ config_action_buttons(names);
+ } else if (hint == PROPERTY_HINT_FILE || hint == PROPERTY_HINT_GLOBAL_FILE) {
List<String> names;
names.push_back(TTR("File..."));
names.push_back(TTR("Clear"));
config_action_buttons(names);
-
} else if (hint == PROPERTY_HINT_DIR || hint == PROPERTY_HINT_GLOBAL_DIR) {
List<String> names;
names.push_back(TTR("Dir..."));
@@ -1034,6 +1038,14 @@ void CustomPropertyEditor::_file_selected(String p_file) {
}
}
+void CustomPropertyEditor::_locale_selected(String p_locale) {
+ if (type == Variant::STRING && hint == PROPERTY_HINT_LOCALE_ID) {
+ v = p_locale;
+ emit_signal(SNAME("variant_changed"));
+ hide();
+ }
+}
+
void CustomPropertyEditor::_type_create_selected(int p_idx) {
if (type == Variant::INT || type == Variant::FLOAT) {
float newval = 0;
@@ -1177,7 +1189,8 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
case Variant::STRING: {
if (hint == PROPERTY_HINT_MULTILINE_TEXT) {
hide();
-
+ } else if (hint == PROPERTY_HINT_LOCALE_ID) {
+ locale->popup_locale_dialog();
} else if (hint == PROPERTY_HINT_FILE || hint == PROPERTY_HINT_GLOBAL_FILE) {
if (p_which == 0) {
if (hint == PROPERTY_HINT_FILE) {
@@ -1243,7 +1256,7 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
if (owner->is_class("Node") && (v.get_type() == Variant::NODE_PATH) && Object::cast_to<Node>(owner)->has_node(v)) {
Node *target_node = Object::cast_to<Node>(owner)->get_node(v);
EditorNode::get_singleton()->get_editor_selection()->clear();
- EditorNode::get_singleton()->get_scene_tree_dock()->set_selected(target_node);
+ SceneTreeDock::get_singleton()->set_selected(target_node);
}
hide();
@@ -1821,6 +1834,12 @@ CustomPropertyEditor::CustomPropertyEditor() {
file->connect("file_selected", callable_mp(this, &CustomPropertyEditor::_file_selected));
file->connect("dir_selected", callable_mp(this, &CustomPropertyEditor::_file_selected));
+ locale = memnew(EditorLocaleDialog);
+ value_vbox->add_child(locale);
+ locale->hide();
+
+ locale->connect("locale_selected", callable_mp(this, &CustomPropertyEditor::_locale_selected));
+
error = memnew(ConfirmationDialog);
error->set_title(TTR("Error!"));
value_vbox->add_child(error);
diff --git a/editor/property_editor.h b/editor/property_editor.h
index 9d88aaf26d..298acb3c01 100644
--- a/editor/property_editor.h
+++ b/editor/property_editor.h
@@ -32,6 +32,7 @@
#define PROPERTY_EDITOR_H
#include "editor/editor_file_dialog.h"
+#include "editor/editor_locale_dialog.h"
#include "editor/scene_tree_editor.h"
#include "scene/gui/button.h"
#include "scene/gui/check_box.h"
@@ -97,6 +98,7 @@ class CustomPropertyEditor : public PopupPanel {
PopupMenu *menu;
SceneTreeDialog *scene_tree;
EditorFileDialog *file;
+ EditorLocaleDialog *locale;
ConfirmationDialog *error;
String name;
Variant::Type type;
@@ -136,6 +138,7 @@ class CustomPropertyEditor : public PopupPanel {
void _text_edit_changed();
void _file_selected(String p_file);
+ void _locale_selected(String p_locale);
void _modified(String p_string);
real_t _parse_real_expression(String text);
diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp
index 3b0cbfdde9..7c0683c37b 100644
--- a/editor/property_selector.cpp
+++ b/editor/property_selector.cpp
@@ -185,7 +185,7 @@ void PropertySelector::_update_search() {
continue;
}
- if (type_filter.size() && type_filter.find(E.type) == -1) {
+ if (type_filter.size() && !type_filter.has(E.type)) {
continue;
}
diff --git a/editor/quick_open.cpp b/editor/quick_open.cpp
index 118c016c6d..2a8ca67fe6 100644
--- a/editor/quick_open.cpp
+++ b/editor/quick_open.cpp
@@ -84,7 +84,7 @@ void EditorQuickOpen::_update_search() {
// Filter possible candidates.
Vector<Entry> entries;
for (int i = 0; i < files.size(); i++) {
- if (empty_search || search_text.is_subsequence_ofi(files[i])) {
+ if (empty_search || search_text.is_subsequence_ofn(files[i])) {
Entry r;
r.path = files[i];
r.score = empty_search ? 0 : _score_path(search_text, files[i].to_lower());
diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp
index 0e34d200f2..c6a4c0d86a 100644
--- a/editor/rename_dialog.cpp
+++ b/editor/rename_dialog.cpp
@@ -284,6 +284,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und
vbc->add_child(lbl_preview_title);
lbl_preview = memnew(Label);
+ lbl_preview->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
vbc->add_child(lbl_preview);
// ---- Dialog related
@@ -336,7 +337,7 @@ void RenameDialog::_bind_methods() {
}
void RenameDialog::_update_substitute() {
- LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(scene_tree_editor->get_focus_owner());
+ LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(scene_tree_editor->get_viewport()->gui_get_focus_owner());
bool is_main_field = _is_main_field(focus_owner_line_edit);
but_insert_name->set_disabled(!is_main_field);
@@ -362,7 +363,7 @@ void RenameDialog::_post_popup() {
Array selected_node_list = editor_selection->get_selected_nodes();
ERR_FAIL_COND(selected_node_list.size() == 0);
- preview_node = selected_node_list[0];
+ preview_node = Object::cast_to<Node>(selected_node_list[0]);
_update_preview();
_update_substitute();
@@ -527,7 +528,7 @@ String RenameDialog::_postprocess(const String &subject) {
// To Lowercase
result = result.to_lower();
} else if (case_id == 2) {
- // To Upercase
+ // To Uppercase
result = result.to_upper();
}
@@ -631,7 +632,7 @@ bool RenameDialog::_is_main_field(LineEdit *line_edit) {
}
void RenameDialog::_insert_text(String text) {
- LineEdit *focus_owner = Object::cast_to<LineEdit>(scene_tree_editor->get_focus_owner());
+ LineEdit *focus_owner = Object::cast_to<LineEdit>(scene_tree_editor->get_viewport()->gui_get_focus_owner());
if (_is_main_field(focus_owner)) {
focus_owner->selection_delete();
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 2e72b17651..9b18d3a491 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -78,7 +78,7 @@ void SceneTreeDock::input(const Ref<InputEvent> &p_event) {
void SceneTreeDock::unhandled_key_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- if (get_focus_owner() && get_focus_owner()->is_text_field()) {
+ if (get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) {
return;
}
@@ -361,8 +361,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
tree->edit_selected();
}
} break;
- case TOOL_NEW:
- case TOOL_REPARENT_TO_NEW_NODE: {
+ case TOOL_REPARENT_TO_NEW_NODE:
+ if (!_validate_no_foreign()) {
+ break;
+ }
+ [[fallthrough]];
+ case TOOL_NEW: {
if (!profile_allow_editing) {
break;
}
@@ -441,8 +445,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
+ bool was_empty = false;
if (!node_clipboard.is_empty()) {
_clear_clipboard();
+ } else {
+ was_empty = true;
}
clipboard_source_scene = editor->get_edited_scene()->get_scene_file_path();
@@ -460,81 +467,13 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (p_tool == TOOL_CUT) {
_delete_confirm(true);
}
- } break;
- case TOOL_PASTE: {
- if (node_clipboard.is_empty() || !edited_scene) {
- break;
- }
-
- bool has_cycle = false;
- if (!edited_scene->get_scene_file_path().is_empty()) {
- for (Node *E : node_clipboard) {
- if (edited_scene->get_scene_file_path() == E->get_scene_file_path()) {
- has_cycle = true;
- break;
- }
- }
- }
-
- if (has_cycle) {
- current_option = -1;
- accept->set_text(TTR("Can't paste root node into the same scene."));
- accept->popup_centered();
- break;
- }
-
- Node *paste_parent = edited_scene;
- List<Node *> selection = editor_selection->get_selected_node_list();
- if (selection.size() > 0) {
- paste_parent = selection.back()->get();
- }
-
- Node *owner = paste_parent->get_owner();
- if (!owner) {
- owner = paste_parent;
- }
-
- editor_data->get_undo_redo().create_action(TTR("Paste Node(s)"));
- editor_data->get_undo_redo().add_do_method(editor_selection, "clear");
-
- Map<RES, RES> resource_remap;
- String target_scene = editor->get_edited_scene()->get_scene_file_path();
- if (target_scene != clipboard_source_scene) {
- if (!clipboard_resource_remap.has(target_scene)) {
- Map<RES, RES> remap;
- for (Node *E : node_clipboard) {
- _create_remap_for_node(E, remap);
- }
- clipboard_resource_remap[target_scene] = remap;
- }
- resource_remap = clipboard_resource_remap[target_scene];
- }
-
- for (Node *node : node_clipboard) {
- Map<const Node *, Node *> duplimap;
-
- Node *dup = node->duplicate_from_editor(duplimap, resource_remap);
- ERR_CONTINUE(!dup);
-
- editor_data->get_undo_redo().add_do_method(paste_parent, "add_child", dup, true);
-
- for (KeyValue<const Node *, Node *> &E2 : duplimap) {
- Node *d = E2.value;
- editor_data->get_undo_redo().add_do_method(d, "set_owner", owner);
- }
-
- editor_data->get_undo_redo().add_do_method(dup, "set_owner", owner);
- editor_data->get_undo_redo().add_do_method(editor_selection, "add_node", dup);
- editor_data->get_undo_redo().add_undo_method(paste_parent, "remove_child", dup);
- editor_data->get_undo_redo().add_do_reference(dup);
-
- if (node_clipboard.size() == 1) {
- editor_data->get_undo_redo().add_do_method(editor, "push_item", dup);
- }
+ if (was_empty) {
+ _update_create_root_dialog();
}
-
- editor_data->get_undo_redo().commit_action();
+ } break;
+ case TOOL_PASTE: {
+ paste_nodes();
} break;
case TOOL_REPLACE: {
if (!profile_allow_editing) {
@@ -1104,7 +1043,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (node) {
node->set_scene_inherited_state(Ref<SceneState>());
scene_tree->update_tree();
- EditorNode::get_singleton()->get_inspector()->update_tree();
+ InspectorDock::get_inspector_singleton()->update_tree();
}
}
} break;
@@ -1302,6 +1241,12 @@ void SceneTreeDock::_notification(int p_what) {
button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
button_custom->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_NEW, false));
+ button_clipboard = memnew(Button);
+ node_shortcuts->add_child(button_clipboard);
+ button_clipboard->set_text(TTR("Paste From Clipboard"));
+ button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")));
+ button_clipboard->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_PASTE, false));
+
node_shortcuts->add_spacer();
create_root_dialog->add_child(node_shortcuts);
_update_create_root_dialog();
@@ -1326,6 +1271,7 @@ void SceneTreeDock::_notification(int p_what) {
button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons")));
button_ui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons")));
button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")));
filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
filter->set_clear_button_enabled(true);
@@ -1777,7 +1723,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
Node *validate = new_parent;
while (validate) {
- ERR_FAIL_COND_MSG(p_nodes.find(validate) != -1, "Selection changed at some point. Can't reparent.");
+ ERR_FAIL_COND_MSG(p_nodes.has(validate), "Selection changed at some point. Can't reparent.");
validate = validate->get_parent();
}
@@ -2121,7 +2067,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
// Fixes the EditorHistory from still offering deleted notes
EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
editor_history->cleanup_history();
- EditorNode::get_singleton()->get_inspector_dock()->call("_prepare_history");
+ InspectorDock::get_singleton()->call("_prepare_history");
}
void SceneTreeDock::_update_script_button() {
@@ -2621,6 +2567,10 @@ void SceneTreeDock::_script_dropped(String p_file, NodePath p_to) {
}
void SceneTreeDock::_nodes_dragged(Array p_nodes, NodePath p_to, int p_type) {
+ if (!_validate_no_foreign()) {
+ return;
+ }
+
List<Node *> selection = editor_selection->get_selected_node_list();
if (selection.is_empty()) {
@@ -2732,10 +2682,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
}
if (profile_allow_editing) {
- menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/cut_node"), TOOL_CUT);
- menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY);
+ menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCut"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/cut_node"), TOOL_CUT);
+ menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY);
if (selection.size() == 1 && !node_clipboard.is_empty()) {
- menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE);
+ menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE);
}
menu->add_separator();
}
@@ -2859,15 +2809,9 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
menu->popup();
}
-void SceneTreeDock::_open_tree_menu() {
- menu->clear();
-
- menu->add_check_item(TTR("Auto Expand to Selected"), TOOL_AUTO_EXPAND);
- menu->set_item_checked(menu->get_item_idx_from_text(TTR("Auto Expand to Selected")), EditorSettings::get_singleton()->get("docks/scene_tree/auto_expand_to_selected"));
-
- menu->reset_size();
- menu->set_position(get_screen_position() + get_local_mouse_position());
- menu->popup();
+void SceneTreeDock::_update_tree_menu() {
+ PopupMenu *tree_menu = button_tree_menu->get_popup();
+ tree_menu->set_item_checked(tree_menu->get_item_idx_from_text(TTR("Auto Expand to Selected")), EditorSettings::get_singleton()->get("docks/scene_tree/auto_expand_to_selected"));
}
void SceneTreeDock::_filter_changed(const String &p_filter) {
@@ -3014,6 +2958,112 @@ void SceneTreeDock::open_instance_child_dialog() {
_tool_selected(TOOL_INSTANTIATE, true);
}
+List<Node *> SceneTreeDock::paste_nodes() {
+ List<Node *> pasted_nodes;
+
+ if (node_clipboard.is_empty()) {
+ return pasted_nodes;
+ }
+
+ bool has_cycle = false;
+ if (edited_scene && !edited_scene->get_scene_file_path().is_empty()) {
+ for (Node *E : node_clipboard) {
+ if (edited_scene->get_scene_file_path() == E->get_scene_file_path()) {
+ has_cycle = true;
+ break;
+ }
+ }
+ }
+
+ if (has_cycle) {
+ current_option = -1;
+ accept->set_text(TTR("Can't paste root node into the same scene."));
+ accept->popup_centered();
+ return pasted_nodes;
+ }
+
+ Node *paste_parent = edited_scene;
+ List<Node *> selection = editor_selection->get_selected_node_list();
+ if (selection.size() > 0) {
+ paste_parent = selection.back()->get();
+ }
+
+ Node *owner = nullptr;
+ if (paste_parent) {
+ owner = paste_parent->get_owner();
+ }
+ if (!owner) {
+ owner = paste_parent;
+ }
+
+ UndoRedo &ur = editor_data->get_undo_redo();
+ ur.create_action(TTR("Paste Node(s)"));
+ ur.add_do_method(editor_selection, "clear");
+
+ Map<RES, RES> resource_remap;
+ String target_scene;
+ if (edited_scene) {
+ target_scene = edited_scene->get_scene_file_path();
+ }
+ if (target_scene != clipboard_source_scene) {
+ if (!clipboard_resource_remap.has(target_scene)) {
+ Map<RES, RES> remap;
+ for (Node *E : node_clipboard) {
+ _create_remap_for_node(E, remap);
+ }
+ clipboard_resource_remap[target_scene] = remap;
+ }
+ resource_remap = clipboard_resource_remap[target_scene];
+ }
+
+ for (Node *node : node_clipboard) {
+ Map<const Node *, Node *> duplimap;
+
+ Node *dup = node->duplicate_from_editor(duplimap, resource_remap);
+ ERR_CONTINUE(!dup);
+
+ pasted_nodes.push_back(dup);
+
+ if (!paste_parent) {
+ paste_parent = dup;
+ owner = dup;
+ ur.add_do_method(editor, "set_edited_scene", dup);
+ } else {
+ ur.add_do_method(paste_parent, "add_child", dup, true);
+ }
+
+ for (KeyValue<const Node *, Node *> &E2 : duplimap) {
+ Node *d = E2.value;
+ if (d != dup) {
+ ur.add_do_method(d, "set_owner", owner);
+ }
+ }
+
+ if (dup != owner) {
+ ur.add_do_method(dup, "set_owner", owner);
+ }
+ ur.add_do_method(editor_selection, "add_node", dup);
+
+ if (dup == paste_parent) {
+ ur.add_undo_method(editor, "set_edited_scene", (Object *)nullptr);
+ } else {
+ ur.add_undo_method(paste_parent, "remove_child", dup);
+ }
+ ur.add_do_reference(dup);
+
+ if (node_clipboard.size() == 1) {
+ ur.add_do_method(editor, "push_item", dup);
+ }
+ }
+
+ ur.commit_action();
+ return pasted_nodes;
+}
+
+List<Node *> SceneTreeDock::get_node_clipboard() const {
+ return node_clipboard;
+}
+
void SceneTreeDock::add_remote_tree_editor(Control *p_remote) {
ERR_FAIL_COND(remote_tree != nullptr);
add_child(p_remote);
@@ -3113,6 +3163,7 @@ void SceneTreeDock::_update_create_root_dialog() {
beginner_nodes->show();
favorite_nodes->hide();
}
+ button_clipboard->set_visible(!node_clipboard.is_empty());
}
}
@@ -3167,7 +3218,7 @@ void SceneTreeDock::_create_remap_for_node(Node *p_node, Map<RES, RES> &r_remap)
}
Variant v = p_node->get(E.name);
- if (v.is_ref()) {
+ if (v.is_ref_counted()) {
RES res = v;
if (res.is_valid()) {
if (!states_stack_ready) {
@@ -3205,7 +3256,7 @@ void SceneTreeDock::_create_remap_for_resource(RES p_resource, Map<RES, RES> &r_
}
Variant v = p_resource->get(E.name);
- if (v.is_ref()) {
+ if (v.is_ref_counted()) {
RES res = v;
if (res.is_valid()) {
if (res->is_built_in() && !r_remap.has(res)) {
@@ -3317,11 +3368,15 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
filter_hbc->add_child(button_detach_script);
button_detach_script->hide();
- button_tree_menu = memnew(Button);
+ button_tree_menu = memnew(MenuButton);
button_tree_menu->set_flat(true);
- button_tree_menu->connect("pressed", callable_mp(this, &SceneTreeDock::_open_tree_menu));
+ button_tree_menu->connect("about_to_popup", callable_mp(this, &SceneTreeDock::_update_tree_menu));
filter_hbc->add_child(button_tree_menu);
+ PopupMenu *tree_menu = button_tree_menu->get_popup();
+ tree_menu->add_check_item(TTR("Auto Expand to Selected"), TOOL_AUTO_EXPAND);
+ tree_menu->connect("id_pressed", callable_mp(this, &SceneTreeDock::_tool_selected), make_binds(false));
+
button_hb = memnew(HBoxContainer);
vbc->add_child(button_hb);
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index ffaf34cfdc..3639e66233 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -117,12 +117,13 @@ class SceneTreeDock : public VBoxContainer {
Button *button_instance;
Button *button_create_script;
Button *button_detach_script;
- Button *button_tree_menu;
+ MenuButton *button_tree_menu;
Button *button_2d;
Button *button_3d;
Button *button_ui;
Button *button_custom;
+ Button *button_clipboard;
HBoxContainer *button_hb;
Button *edit_local, *edit_remote;
@@ -241,7 +242,7 @@ class SceneTreeDock : public VBoxContainer {
void _quick_open();
void _tree_rmb(const Vector2 &p_menu_pos);
- void _open_tree_menu();
+ void _update_tree_menu();
void _filter_changed(const String &p_filter);
@@ -263,12 +264,17 @@ class SceneTreeDock : public VBoxContainer {
bool profile_allow_editing;
bool profile_allow_script_editing;
- static SceneTreeDock *singleton;
static void _update_configuration_warning();
bool _update_node_path(Node *p_root_node, NodePath &r_node_path, Map<Node *, NodePath> *p_renames) const;
bool _check_node_path_recursive(Node *p_root_node, Variant &r_variant, Map<Node *, NodePath> *p_renames) const;
+private:
+ static SceneTreeDock *singleton;
+
+public:
+ static SceneTreeDock *get_singleton() { return singleton; }
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -308,6 +314,9 @@ public:
void open_add_child_dialog();
void open_instance_child_dialog();
+ List<Node *> paste_nodes();
+ List<Node *> get_node_clipboard() const;
+
ScriptCreateDialog *get_script_create_dialog() { return script_create_dialog; }
SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data);
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index c1ceba27b3..c755bca64f 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -133,8 +133,8 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
set_selected(n);
- NodeDock::singleton->get_parent()->call("set_current_tab", NodeDock::singleton->get_index());
- NodeDock::singleton->show_connections();
+ NodeDock::get_singleton()->get_parent()->call("set_current_tab", NodeDock::get_singleton()->get_index());
+ NodeDock::get_singleton()->show_connections();
} else if (p_id == BUTTON_GROUPS) {
editor_selection->clear();
@@ -142,8 +142,8 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
set_selected(n);
- NodeDock::singleton->get_parent()->call("set_current_tab", NodeDock::singleton->get_index());
- NodeDock::singleton->show_groups();
+ NodeDock::get_singleton()->get_parent()->call("set_current_tab", NodeDock::get_singleton()->get_index());
+ NodeDock::get_singleton()->show_groups();
}
}
@@ -411,7 +411,7 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent, bool p_scroll
item->set_as_cursor(0);
}
- bool keep = (filter.is_subsequence_ofi(String(p_node->get_name())));
+ bool keep = (filter.is_subsequence_ofn(String(p_node->get_name())));
for (int i = 0; i < p_node->get_child_count(); i++) {
bool child_keep = _add_nodes(p_node->get_child(i), item, p_scroll_to_selected);
@@ -1256,6 +1256,10 @@ void SceneTreeDialog::popup_scenetree_dialog() {
popup_centered_clamped(Size2(350, 700) * EDSCALE);
}
+void SceneTreeDialog::_update_theme() {
+ filter->set_right_icon(tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+}
+
void SceneTreeDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
@@ -1265,8 +1269,10 @@ void SceneTreeDialog::_notification(int p_what) {
} break;
case NOTIFICATION_ENTER_TREE: {
connect("confirmed", callable_mp(this, &SceneTreeDialog::_select));
- filter->set_right_icon(tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
- filter->set_clear_button_enabled(true);
+ _update_theme();
+ } break;
+ case NOTIFICATION_THEME_CHANGED: {
+ _update_theme();
} break;
case NOTIFICATION_EXIT_TREE: {
disconnect("confirmed", callable_mp(this, &SceneTreeDialog::_select));
@@ -1303,6 +1309,7 @@ SceneTreeDialog::SceneTreeDialog() {
filter = memnew(LineEdit);
filter->set_h_size_flags(Control::SIZE_EXPAND_FILL);
filter->set_placeholder(TTR("Filter nodes"));
+ filter->set_clear_button_enabled(true);
filter->add_theme_constant_override("minimum_character_width", 0);
filter->connect("text_changed", callable_mp(this, &SceneTreeDialog::_filter_changed));
vbc->add_child(filter);
diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h
index 7fb1451934..39fe64b828 100644
--- a/editor/scene_tree_editor.h
+++ b/editor/scene_tree_editor.h
@@ -174,6 +174,7 @@ class SceneTreeDialog : public ConfirmationDialog {
void _select();
void _cancel();
void _filter_changed(const String &p_filter);
+ void _update_theme();
protected:
void _notification(int p_what);
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 2098fa2c85..360ba5780c 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -656,14 +656,18 @@ void ScriptCreateDialog::_update_dialog() {
if (is_new_script_created) {
class_name->set_editable(true);
class_name->set_placeholder(TTR("Allowed: a-z, A-Z, 0-9, _ and ."));
- class_name->set_placeholder_alpha(0.3);
+ Color placeholder_color = class_name->get_theme_color("font_placeholder_color");
+ placeholder_color.a = 0.3;
+ class_name->add_theme_color_override("font_placeholder_color", placeholder_color);
} else {
class_name->set_editable(false);
}
} else {
class_name->set_editable(false);
class_name->set_placeholder(TTR("N/A"));
- class_name->set_placeholder_alpha(1);
+ Color placeholder_color = class_name->get_theme_color("font_placeholder_color");
+ placeholder_color.a = 1;
+ class_name->add_theme_color_override("font_placeholder_color", placeholder_color);
class_name->set_text("");
}
diff --git a/gles3_builders.py b/gles3_builders.py
index 4f9247c938..15446d345f 100644
--- a/gles3_builders.py
+++ b/gles3_builders.py
@@ -502,7 +502,7 @@ def build_gles3_header(filename, include, class_suffix, output_attribs):
for i in range(len(header_data.specialization_names)):
defval = header_data.specialization_values[i].strip()
if defval.upper() == "TRUE" or defval == "1":
- defal = "true"
+ defval = "true"
else:
defval = "false"
diff --git a/main/main.cpp b/main/main.cpp
index 9393e8d99d..a632059849 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -325,6 +325,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print("].\n");
OS::get_singleton()->print(" --rendering-driver <driver> Rendering driver (depends on display driver).\n");
+ OS::get_singleton()->print(" --gpu-index <device_index> Use a specific GPU (run with --verbose to get available device list).\n");
OS::get_singleton()->print(" --text-driver <driver> Text driver (Fonts, BiDi, shaping)\n");
@@ -793,6 +794,14 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "-w" || I->get() == "--windowed") { // force windowed window
init_windowed = true;
+ } else if (I->get() == "--gpu-index") {
+ if (I->next()) {
+ Engine::singleton->gpu_idx = I->next()->get().to_int();
+ N = I->next()->next();
+ } else {
+ OS::get_singleton()->print("Missing gpu index argument, aborting.\n");
+ goto error;
+ }
} else if (I->get() == "--vk-layers") {
Engine::singleton->use_validation_layers = true;
#ifdef DEBUG_ENABLED
@@ -1297,47 +1306,49 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// always convert to lower case for consistency in the code
rendering_driver = rendering_driver.to_lower();
- GLOBAL_DEF_BASIC("display/window/size/width", 1024);
- ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/width",
- PropertyInfo(Variant::INT, "display/window/size/width",
+ GLOBAL_DEF_BASIC("display/window/size/viewport_width", 1024);
+ ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/viewport_width",
+ PropertyInfo(Variant::INT, "display/window/size/viewport_width",
PROPERTY_HINT_RANGE,
- "0,7680,or_greater")); // 8K resolution
- GLOBAL_DEF_BASIC("display/window/size/height", 600);
- ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/height",
- PropertyInfo(Variant::INT, "display/window/size/height",
+ "0,7680,1,or_greater")); // 8K resolution
+
+ GLOBAL_DEF_BASIC("display/window/size/viewport_height", 600);
+ ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/viewport_height",
+ PropertyInfo(Variant::INT, "display/window/size/viewport_height",
PROPERTY_HINT_RANGE,
- "0,4320,or_greater")); // 8K resolution
+ "0,4320,1,or_greater")); // 8K resolution
+
GLOBAL_DEF_BASIC("display/window/size/resizable", true);
GLOBAL_DEF_BASIC("display/window/size/borderless", false);
GLOBAL_DEF_BASIC("display/window/size/fullscreen", false);
GLOBAL_DEF("display/window/size/always_on_top", false);
- GLOBAL_DEF("display/window/size/test_width", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/test_width",
+ GLOBAL_DEF("display/window/size/window_width_override", 0);
+ ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/window_width_override",
PropertyInfo(Variant::INT,
- "display/window/size/test_width",
+ "display/window/size/window_width_override",
PROPERTY_HINT_RANGE,
- "0,7680,or_greater")); // 8K resolution
- GLOBAL_DEF("display/window/size/test_height", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/test_height",
+ "0,7680,1,or_greater")); // 8K resolution
+ GLOBAL_DEF("display/window/size/window_height_override", 0);
+ ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/window_height_override",
PropertyInfo(Variant::INT,
- "display/window/size/test_height",
+ "display/window/size/window_height_override",
PROPERTY_HINT_RANGE,
- "0,4320,or_greater")); // 8K resolution
+ "0,4320,1,or_greater")); // 8K resolution
if (use_custom_res) {
if (!force_res) {
- window_size.width = GLOBAL_GET("display/window/size/width");
- window_size.height = GLOBAL_GET("display/window/size/height");
-
- if (globals->has_setting("display/window/size/test_width") &&
- globals->has_setting("display/window/size/test_height")) {
- int tw = globals->get("display/window/size/test_width");
- if (tw > 0) {
- window_size.width = tw;
+ window_size.width = GLOBAL_GET("display/window/size/viewport_width");
+ window_size.height = GLOBAL_GET("display/window/size/viewport_height");
+
+ 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");
+ if (desired_width > 0) {
+ window_size.width = desired_width;
}
- int th = globals->get("display/window/size/test_height");
- if (th > 0) {
- window_size.height = th;
+ int desired_height = globals->get("display/window/size/window_height_override");
+ if (desired_height > 0) {
+ window_size.height = desired_height;
}
}
}
@@ -1785,7 +1796,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
GLOBAL_DEF("application/config/icon", String());
ProjectSettings::get_singleton()->set_custom_property_info("application/config/icon",
PropertyInfo(Variant::STRING, "application/config/icon",
- PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"));
+ PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"));
GLOBAL_DEF("application/config/macos_native_icon", String());
ProjectSettings::get_singleton()->set_custom_property_info("application/config/macos_native_icon",
@@ -2082,6 +2093,7 @@ bool Main::start() {
GLOBAL_DEF("mono/runtime/unhandled_exception_policy", 0);
#endif
+ Error err;
DocTools doc;
doc.generate(doc_base);
@@ -2103,34 +2115,42 @@ bool Main::start() {
// Create the module documentation directory if it doesn't exist
DirAccess *da = DirAccess::create_for_path(path);
- da->make_dir_recursive(path);
+ err = da->make_dir_recursive(path);
memdelete(da);
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Error: Can't create directory: " + path + ": " + itos(err));
- docsrc.load_classes(path);
print_line("Loading docs from: " + path);
+ err = docsrc.load_classes(path);
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Error loading docs from: " + path + ": " + itos(err));
}
}
String index_path = doc_tool_path.plus_file("doc/classes");
// Create the main documentation directory if it doesn't exist
DirAccess *da = DirAccess::create_for_path(index_path);
- da->make_dir_recursive(index_path);
+ err = da->make_dir_recursive(index_path);
memdelete(da);
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Error: Can't create index directory: " + index_path + ": " + itos(err));
- docsrc.load_classes(index_path);
+ print_line("Loading classes from: " + index_path);
+ err = docsrc.load_classes(index_path);
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Error loading classes from: " + index_path + ": " + itos(err));
checked_paths.insert(index_path);
- print_line("Loading docs from: " + index_path);
print_line("Merging docs...");
doc.merge_from(docsrc);
+
for (Set<String>::Element *E = checked_paths.front(); E; E = E->next()) {
print_line("Erasing old docs at: " + E->get());
- DocTools::erase_classes(E->get());
+ err = DocTools::erase_classes(E->get());
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Error erasing old docs at: " + E->get() + ": " + itos(err));
}
print_line("Generating new docs...");
- doc.save_classes(index_path, doc_data_classes);
+ err = doc.save_classes(index_path, doc_data_classes);
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Error saving new docs:" + itos(err));
+ OS::get_singleton()->set_exit_code(EXIT_SUCCESS);
return false;
}
@@ -2330,8 +2350,8 @@ bool Main::start() {
String stretch_mode = GLOBAL_DEF_BASIC("display/window/stretch/mode", "disabled");
String stretch_aspect = GLOBAL_DEF_BASIC("display/window/stretch/aspect", "keep");
- Size2i stretch_size = Size2i(GLOBAL_DEF_BASIC("display/window/size/width", 0),
- GLOBAL_DEF_BASIC("display/window/size/height", 0));
+ Size2i stretch_size = Size2i(GLOBAL_DEF_BASIC("display/window/size/viewport_width", 0),
+ GLOBAL_DEF_BASIC("display/window/size/viewport_height", 0));
real_t stretch_scale = GLOBAL_DEF_BASIC("display/window/stretch/scale", 1.0);
Window::ContentScaleMode cs_sm = Window::CONTENT_SCALE_MODE_DISABLED;
@@ -2540,13 +2560,6 @@ bool Main::start() {
}
if (project_manager || editor) {
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CONSOLE_WINDOW)) {
- // Hide console window if requested (Windows-only).
- bool hide_console = EditorSettings::get_singleton()->get_setting(
- "interface/editor/hide_console_window");
- DisplayServer::get_singleton()->console_set_visible(!hide_console);
- }
-
// Load SSL Certificates from Editor Settings (or builtin)
Crypto::load_default_certificates(
EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String());
diff --git a/methods.py b/methods.py
index 3331e159c7..fbd304ddde 100644
--- a/methods.py
+++ b/methods.py
@@ -454,45 +454,39 @@ def no_verbose(sys, env):
# Colors are disabled in non-TTY environments such as pipes. This means
# that if output is redirected to a file, it will not contain color codes
if sys.stdout.isatty():
- colors["cyan"] = "\033[96m"
- colors["purple"] = "\033[95m"
- colors["blue"] = "\033[94m"
- colors["green"] = "\033[92m"
- colors["yellow"] = "\033[93m"
- colors["red"] = "\033[91m"
- colors["end"] = "\033[0m"
+ colors["blue"] = "\033[0;94m"
+ colors["bold_blue"] = "\033[1;94m"
+ colors["reset"] = "\033[0m"
else:
- colors["cyan"] = ""
- colors["purple"] = ""
colors["blue"] = ""
- colors["green"] = ""
- colors["yellow"] = ""
- colors["red"] = ""
- colors["end"] = ""
+ colors["bold_blue"] = ""
+ colors["reset"] = ""
- compile_source_message = "{}Compiling {}==> {}$SOURCE{}".format(
- colors["blue"], colors["purple"], colors["yellow"], colors["end"]
+ # There is a space before "..." to ensure that source file names can be
+ # Ctrl + clicked in the VS Code terminal.
+ compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
- java_compile_source_message = "{}Compiling {}==> {}$SOURCE{}".format(
- colors["blue"], colors["purple"], colors["yellow"], colors["end"]
+ java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
- compile_shared_source_message = "{}Compiling shared {}==> {}$SOURCE{}".format(
- colors["blue"], colors["purple"], colors["yellow"], colors["end"]
+ compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
- link_program_message = "{}Linking Program {}==> {}$TARGET{}".format(
- colors["red"], colors["purple"], colors["yellow"], colors["end"]
+ link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
- link_library_message = "{}Linking Static Library {}==> {}$TARGET{}".format(
- colors["red"], colors["purple"], colors["yellow"], colors["end"]
+ link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
- ranlib_library_message = "{}Ranlib Library {}==> {}$TARGET{}".format(
- colors["red"], colors["purple"], colors["yellow"], colors["end"]
+ ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
- link_shared_library_message = "{}Linking Shared Library {}==> {}$TARGET{}".format(
- colors["red"], colors["purple"], colors["yellow"], colors["end"]
+ link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
- java_library_message = "{}Creating Java Archive {}==> {}$TARGET{}".format(
- colors["red"], colors["purple"], colors["yellow"], colors["end"]
+ java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
)
env.Append(CXXCOMSTR=[compile_source_message])
@@ -780,9 +774,10 @@ def generate_vs_project(env, num_jobs):
env.vs_incs.append(str(header))
module_configs = ModuleConfigs()
- import modules.mono.build_scripts.mono_reg_utils as mono_reg
if env.get("module_mono_enabled"):
+ import modules.mono.build_scripts.mono_reg_utils as mono_reg
+
mono_root = env.get("mono_prefix") or mono_reg.find_mono_root_dir(env["bits"])
if mono_root:
module_configs.add_mode(
diff --git a/misc/dist/html/editor.html b/misc/dist/html/editor.html
index 8b077a5725..a681a2a1c3 100644
--- a/misc/dist/html/editor.html
+++ b/misc/dist/html/editor.html
@@ -9,8 +9,8 @@
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="application-name" content="Godot" />
<meta name="apple-mobile-web-app-title" content="Godot" />
- <meta name="theme-color" content="#478cbf" />
- <meta name="msapplication-navbutton-color" content="#478cbf" />
+ <meta name="theme-color" content="#202531" />
+ <meta name="msapplication-navbutton-color" content="#202531" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="msapplication-starturl" content="/latest" />
<meta property="og:site_name" content="Godot Engine Web Editor" />
diff --git a/misc/dist/html/manifest.json b/misc/dist/html/manifest.json
index 0ca27b3742..adc8106e2a 100644
--- a/misc/dist/html/manifest.json
+++ b/misc/dist/html/manifest.json
@@ -6,7 +6,7 @@
"start_url": "./godot.tools.html",
"display": "standalone",
"orientation": "landscape",
- "theme_color": "#478cbf",
+ "theme_color": "#202531",
"icons": [
{
"src": "favicon.png",
diff --git a/misc/dist/html/offline.html b/misc/dist/html/offline.html
index 000c21b4d3..5cfc3362d9 100644
--- a/misc/dist/html/offline.html
+++ b/misc/dist/html/offline.html
@@ -4,6 +4,8 @@
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
+ <meta name="theme-color" content="#202531" />
+ <meta name="msapplication-navbutton-color" content="#202531" />
<title>You are offline</title>
<style>
html {
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index fd69725a21..5ba45c0c13 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -308,6 +308,7 @@
CODE_SIGN_ENTITLEMENTS = "$binary/$binary.entitlements";
CODE_SIGN_IDENTITY = "$code_sign_identity_debug";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_debug";
+ CODE_SIGN_STYLE = "$code_sign_style_debug";
CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)";
DEVELOPMENT_TEAM = $team_id;
INFOPLIST_FILE = "$binary/$binary-Info.plist";
@@ -338,6 +339,7 @@
CODE_SIGN_ENTITLEMENTS = "$binary/$binary.entitlements";
CODE_SIGN_IDENTITY = "$code_sign_identity_release";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_release";
+ CODE_SIGN_STYLE = "$code_sign_style_release";
CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)";
DEVELOPMENT_TEAM = $team_id;
INFOPLIST_FILE = "$binary/$binary-Info.plist";
diff --git a/misc/dist/osx_template.app/Contents/Info.plist b/misc/dist/osx_template.app/Contents/Info.plist
index 8e221df946..a087550290 100644
--- a/misc/dist/osx_template.app/Contents/Info.plist
+++ b/misc/dist/osx_template.app/Contents/Info.plist
@@ -24,10 +24,7 @@
<string>$signature</string>
<key>CFBundleVersion</key>
<string>$version</string>
- <key>NSMicrophoneUsageDescription</key>
- <string>$microphone_usage_description</string>
- <key>NSCameraUsageDescription</key>
- <string>$camera_usage_description</string>
+$usage_descriptions
<key>NSHumanReadableCopyright</key>
<string>$copyright</string>
<key>CFBundleSupportedPlatforms</key>
@@ -46,6 +43,6 @@
<string>10.12</string>
</dict>
<key>NSHighResolutionCapable</key>
- $highres
+$highres
</dict>
</plist>
diff --git a/misc/scripts/black_format.sh b/misc/scripts/black_format.sh
index 2ad9a23832..99343f1c5a 100755
--- a/misc/scripts/black_format.sh
+++ b/misc/scripts/black_format.sh
@@ -6,28 +6,20 @@ set -uo pipefail
# Apply black.
echo -e "Formatting Python files..."
-PY_FILES=$(find \( -path "./.git" \
- -o -path "./thirdparty" \
- \) -prune \
- -o \( -name "SConstruct" \
- -o -name "SCsub" \
- -o -name "*.py" \
- \) -print)
+PY_FILES=$(git ls-files -- '*SConstruct' '*SCsub' '*.py' ':!:.git/*' ':!:thirdparty/*')
black -l 120 $PY_FILES
-git diff --color > patch.patch
+diff=$(git diff --color)
# If no patch has been generated all is OK, clean up, and exit.
-if [ ! -s patch.patch ] ; then
+if [ -z "$diff" ] ; then
printf "Files in this commit comply with the black style rules.\n"
- rm -f patch.patch
exit 0
fi
# A patch has been created, notify the user, clean up, and exit.
printf "\n*** The following differences were found between the code "
printf "and the formatting rules:\n\n"
-cat patch.patch
+echo "$diff"
printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\n"
-rm -f patch.patch
exit 1
diff --git a/misc/scripts/clang_format.sh b/misc/scripts/clang_format.sh
index b0020da597..13b5ab79b6 100755
--- a/misc/scripts/clang_format.sh
+++ b/misc/scripts/clang_format.sh
@@ -4,55 +4,36 @@
# This is the primary script responsible for fixing style violations.
set -uo pipefail
-IFS=$'\n\t'
-CLANG_FORMAT_FILE_EXTS=(".c" ".h" ".cpp" ".hpp" ".cc" ".hh" ".cxx" ".m" ".mm" ".inc" ".java" ".glsl")
+# Loops through all code files tracked by Git.
+git ls-files -- '*.c' '*.h' '*.cpp' '*.hpp' '*.cc' '*.hh' '*.cxx' '*.m' '*.mm' '*.inc' '*.java' '*.glsl' \
+ ':!:.git/*' ':!:thirdparty/*' ':!:platform/android/java/lib/src/com/google/*' ':!:*-so_wrap.*' |
+while read -r f; do
+ # Run clang-format.
+ clang-format --Wno-error=unknown -i "$f"
-# Loops through all text files tracked by Git.
-git grep -zIl '' |
-while IFS= read -rd '' f; do
- # Exclude some files.
- if [[ "$f" == "thirdparty"* ]]; then
+ # Fix copyright headers, but not all files get them.
+ if [[ "$f" == *"inc" ]]; then
continue
- elif [[ "$f" == "platform/android/java/lib/src/com/google"* ]]; then
+ elif [[ "$f" == *"glsl" ]]; then
continue
- elif [[ "$f" == *"-so_wrap."* ]]; then
+ elif [[ "$f" == "platform/android/java/lib/src/org/godotengine/godot/input/InputManager"* ]]; then
continue
fi
- for extension in ${CLANG_FORMAT_FILE_EXTS[@]}; do
- if [[ "$f" == *"$extension" ]]; then
- # Run clang-format.
- clang-format --Wno-error=unknown -i "$f"
- # Fix copyright headers, but not all files get them.
- if [[ "$f" == *"inc" ]]; then
- continue 2
- elif [[ "$f" == *"glsl" ]]; then
- continue 2
- elif [[ "$f" == *"theme_data.h" ]]; then
- continue 2
- elif [[ "$f" == "platform/android/java/lib/src/org/godotengine/godot/input/InputManager"* ]]; then
- continue 2
- fi
- python misc/scripts/copyright_headers.py "$f"
- continue 2
- fi
- done
+ python misc/scripts/copyright_headers.py "$f"
done
-git diff --color > patch.patch
+diff=$(git diff --color)
# If no patch has been generated all is OK, clean up, and exit.
-if [ ! -s patch.patch ] ; then
- printf "Files in this commit comply with the clang-format style rules.\n"
- rm -f patch.patch
+if [ -z "$diff" ] ; then
+ printf "Files in this commit comply with the clang-tidy style rules.\n"
exit 0
fi
# A patch has been created, notify the user, clean up, and exit.
-printf "\n*** The following differences were found between the code "
-printf "and the formatting rules:\n\n"
-cat patch.patch
-printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\n"
-rm -f patch.patch
+printf "\n*** The following changes have been made to comply with the formatting rules:\n\n"
+echo "$diff"
+printf "\n*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\n"
exit 1
diff --git a/misc/scripts/clang_tidy.sh b/misc/scripts/clang_tidy.sh
new file mode 100755
index 0000000000..e49f6ac9f4
--- /dev/null
+++ b/misc/scripts/clang_tidy.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+# This script runs clang-tidy on all relevant files in the repo.
+# This is more thorough than clang-format and thus slower; it should only be run manually.
+
+set -uo pipefail
+
+# Loops through all code files tracked by Git.
+git ls-files -- '*.c' '*.h' '*.cpp' '*.hpp' '*.cc' '*.hh' '*.cxx' '*.m' '*.mm' '*.inc' '*.java' '*.glsl' \
+ ':!:.git/*' ':!:thirdparty/*' ':!:platform/android/java/lib/src/com/google/*' ':!:*-so_wrap.*' |
+while read -r f; do
+ # Run clang-tidy.
+ clang-tidy --quiet --fix "$f" &> /dev/null
+
+ # Run clang-format. This also fixes the output of clang-tidy.
+ clang-format --Wno-error=unknown -i "$f"
+done
+
+diff=$(git diff --color)
+
+# If no patch has been generated all is OK, clean up, and exit.
+if [ -z "$diff" ] ; then
+ printf "Files in this commit comply with the clang-tidy style rules.\n"
+ exit 0
+fi
+
+# A patch has been created, notify the user, clean up, and exit.
+printf "\n*** The following changes have been made to comply with the formatting rules:\n\n"
+echo "$diff"
+printf "\n*** Please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\n"
+exit 1
diff --git a/misc/scripts/file_format.sh b/misc/scripts/file_format.sh
index e66bc88bc0..0c7235817d 100755
--- a/misc/scripts/file_format.sh
+++ b/misc/scripts/file_format.sh
@@ -47,10 +47,10 @@ while IFS= read -rd '' f; do
perl -i -ple 's/\s*$//g' "$f"
done
-git diff --color > patch.patch
+diff=$(git diff --color)
# If no patch has been generated all is OK, clean up, and exit.
-if [ ! -s patch.patch ] ; then
+if [ -z "$diff" ] ; then
printf "Files in this commit comply with the formatting rules.\n"
rm -f patch.patch
exit 0
@@ -59,7 +59,6 @@ fi
# A patch has been created, notify the user, clean up, and exit.
printf "\n*** The following differences were found between the code "
printf "and the formatting rules:\n\n"
-cat patch.patch
+echo "$diff"
printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\n"
-rm -f patch.patch
exit 1
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 41b4682c84..fbddedbe55 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -1451,8 +1451,8 @@ Ref<Material> CSGCylinder3D::get_material() const {
CSGCylinder3D::CSGCylinder3D() {
// defaults
- radius = 1.0;
- height = 1.0;
+ radius = 0.5;
+ height = 2.0;
sides = 8;
cone = false;
smooth_faces = true;
@@ -1671,8 +1671,8 @@ Ref<Material> CSGTorus3D::get_material() const {
CSGTorus3D::CSGTorus3D() {
// defaults
- inner_radius = 2.0;
- outer_radius = 3.0;
+ inner_radius = 0.5;
+ outer_radius = 1.0;
sides = 8;
ring_sides = 6;
smooth_faces = true;
@@ -1694,7 +1694,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
}
int shape_sides = shape_polygon.size();
Vector<int> shape_faces = Geometry2D::triangulate_polygon(shape_polygon);
- ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon");
+ ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon. Make sure the polygon doesn't have any intersecting edges.");
// Get polygon enclosing Rect2.
Rect2 shape_rect(shape_polygon[0], Vector2());
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index eed995a40e..6da9893368 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -239,7 +239,7 @@ class CSGBox3D : public CSGPrimitive3D {
virtual CSGBrush *_build_brush() override;
Ref<Material> material;
- Vector3 size = Vector3(2, 2, 2);
+ Vector3 size = Vector3(1, 1, 1);
protected:
static void _bind_methods();
diff --git a/modules/csg/doc_classes/CSGBox3D.xml b/modules/csg/doc_classes/CSGBox3D.xml
index d64e58ae4d..4b479ed42e 100644
--- a/modules/csg/doc_classes/CSGBox3D.xml
+++ b/modules/csg/doc_classes/CSGBox3D.xml
@@ -12,7 +12,7 @@
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the box.
</member>
- <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(2, 2, 2)">
+ <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(1, 1, 1)">
The box's width, height and depth.
</member>
</members>
diff --git a/modules/csg/doc_classes/CSGCylinder3D.xml b/modules/csg/doc_classes/CSGCylinder3D.xml
index 40e989bfb3..1fe2025bab 100644
--- a/modules/csg/doc_classes/CSGCylinder3D.xml
+++ b/modules/csg/doc_classes/CSGCylinder3D.xml
@@ -12,13 +12,13 @@
<member name="cone" type="bool" setter="set_cone" getter="is_cone" default="false">
If [code]true[/code] a cone is created, the [member radius] will only apply to one side.
</member>
- <member name="height" type="float" setter="set_height" getter="get_height" default="1.0">
+ <member name="height" type="float" setter="set_height" getter="get_height" default="2.0">
The height of the cylinder.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the cylinder.
</member>
- <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
The radius of the cylinder.
</member>
<member name="sides" type="int" setter="set_sides" getter="get_sides" default="8">
diff --git a/modules/csg/doc_classes/CSGMesh3D.xml b/modules/csg/doc_classes/CSGMesh3D.xml
index 2810343139..42fcb7bd2b 100644
--- a/modules/csg/doc_classes/CSGMesh3D.xml
+++ b/modules/csg/doc_classes/CSGMesh3D.xml
@@ -4,7 +4,7 @@
A CSG Mesh shape that uses a mesh resource.
</brief_description>
<description>
- This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces.
+ This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces. See also [CSGPolygon3D] for drawing 2D extruded polygons to be used as CSG nodes.
</description>
<tutorials>
</tutorials>
diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml
index ecbb7962d1..5a49eebc7b 100644
--- a/modules/csg/doc_classes/CSGPolygon3D.xml
+++ b/modules/csg/doc_classes/CSGPolygon3D.xml
@@ -4,7 +4,7 @@
Extrudes a 2D polygon shape to create a 3D mesh.
</brief_description>
<description>
- An array of 2D points is extruded to quickly and easily create a variety of 3D meshes.
+ An array of 2D points is extruded to quickly and easily create a variety of 3D meshes. See also [CSGMesh3D] for using 3D meshes as CSG nodes.
</description>
<tutorials>
</tutorials>
@@ -46,7 +46,8 @@
When [member mode] is [constant MODE_PATH], this is the distance along the path, in meters, the texture coordinates will tile. When set to 0, texture coordinates will match geometry exactly with no tiling.
</member>
<member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array(0, 0, 0, 1, 1, 1, 1, 0)">
- The point array that defines the 2D polygon that is extruded.
+ The point array that defines the 2D polygon that is extruded. This can be a convex or concave polygon with 3 or more points. The polygon must [i]not[/i] have any intersecting edges. Otherwise, triangulation will fail and no mesh will be generated.
+ [b]Note:[/b] If only 1 or 2 points are defined in [member polygon], no mesh will be generated.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="false">
If [code]true[/code], applies smooth shading to the extrusions.
diff --git a/modules/csg/doc_classes/CSGTorus3D.xml b/modules/csg/doc_classes/CSGTorus3D.xml
index 91ee63a4c9..2c0eef8f09 100644
--- a/modules/csg/doc_classes/CSGTorus3D.xml
+++ b/modules/csg/doc_classes/CSGTorus3D.xml
@@ -9,13 +9,13 @@
<tutorials>
</tutorials>
<members>
- <member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius" default="2.0">
+ <member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius" default="0.5">
The inner radius of the torus.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the torus.
</member>
- <member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius" default="3.0">
+ <member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius" default="1.0">
The outer radius of the torus.
</member>
<member name="ring_sides" type="int" setter="set_ring_sides" getter="get_ring_sides" default="6">
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index c79d449d41..7d5557d197 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -132,8 +132,39 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
// Compress image data and (if required) mipmaps.
const bool mipmaps = r_img->has_mipmaps();
- const int width = r_img->get_width();
- const int height = r_img->get_height();
+ int width = r_img->get_width();
+ int height = r_img->get_height();
+
+ /*
+ The first mipmap level of a compressed texture must be a multiple of 4. Quote from D3D11.3 spec:
+
+ BC format surfaces are always multiples of full blocks, each block representing 4x4 pixels.
+ For mipmaps, the top level map is required to be a multiple of 4 size in all dimensions.
+ The sizes for the lower level maps are computed as they are for all mipmapped surfaces,
+ and thus may not be a multiple of 4, for example a top level map of 20 results in a second level
+ map size of 10. For these cases, there is a differing 'physical' size and a 'virtual' size.
+ The virtual size is that computed for each mip level without adjustment, which is 10 for the example.
+ The physical size is the virtual size rounded up to the next multiple of 4, which is 12 for the example,
+ and this represents the actual memory size. The sampling hardware will apply texture address
+ processing based on the virtual size (using, for example, border color if specified for accesses
+ beyond 10), and thus for the example case will not access the 11th and 12th row of the resource.
+ So for mipmap chains when an axis becomes < 4 in size, only texels 'a','b','e','f'
+ are used for a 2x2 map, and texel 'a' is used for 1x1. Note that this is similar to, but distinct from,
+ the surface pitch, which can encompass additional padding beyond the physical surface size.
+ */
+ int next_width = width <= 2 ? width : (width + 3) & ~3;
+ int next_height = height <= 2 ? height : (height + 3) & ~3;
+ if (next_width != width || next_height != height) {
+ r_img->resize(next_width, next_height, Image::INTERPOLATE_LANCZOS);
+ width = r_img->get_width();
+ height = r_img->get_height();
+ }
+ // ERR_FAIL_COND(width % 4 != 0 || height % 4 != 0); // FIXME: No longer guaranteed.
+ // Multiple-of-4 should be guaranteed by above.
+ // However, power-of-two 3d textures will create Nx2 and Nx1 mipmap levels,
+ // which are individually compressed Image objects that violate the above rule.
+ // Hence, we allow Nx1 and Nx2 images through without forcing to multiple-of-4.
+
const uint8_t *src_read = r_img->get_data().ptr();
print_verbose(vformat("ETCPAK: Encoding image size %dx%d to format %s.", width, height, Image::get_format_name(target_format)));
@@ -144,24 +175,48 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
uint8_t *dest_write = dest_data.ptrw();
int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
+ Vector<uint32_t> padded_src;
for (int i = 0; i < mip_count + 1; i++) {
// Get write mip metrics for target image.
- int mip_w, mip_h;
- int mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, mip_w, mip_h);
+ int orig_mip_w, orig_mip_h;
+ int mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, orig_mip_w, orig_mip_h);
// Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer).
ERR_FAIL_COND(mip_ofs % 8 != 0);
uint64_t *dest_mip_write = (uint64_t *)&dest_write[mip_ofs];
// Block size. Align stride to multiple of 4 (RGBA8).
- mip_w = (mip_w + 3) & ~3;
- mip_h = (mip_h + 3) & ~3;
+ int mip_w = (orig_mip_w + 3) & ~3;
+ int mip_h = (orig_mip_h + 3) & ~3;
const uint32_t blocks = mip_w * mip_h / 16;
// Get mip data from source image for reading.
int src_mip_ofs = r_img->get_mipmap_offset(i);
const uint32_t *src_mip_read = (const uint32_t *)&src_read[src_mip_ofs];
+ // Pad textures to nearest block by smearing.
+ if (mip_w != orig_mip_w || mip_h != orig_mip_h) {
+ padded_src.resize(mip_w * mip_h);
+ uint32_t *ptrw = padded_src.ptrw();
+ int x = 0, y = 0;
+ for (y = 0; y < orig_mip_h; y++) {
+ for (x = 0; x < orig_mip_w; x++) {
+ ptrw[mip_w * y + x] = src_mip_read[orig_mip_w * y + x];
+ }
+ // First, smear in x.
+ for (; x < mip_w; x++) {
+ ptrw[mip_w * y + x] = ptrw[mip_w * y + x - 1];
+ }
+ }
+ // Then, smear in y.
+ for (; y < mip_h; y++) {
+ for (x = 0; x < mip_w; x++) {
+ ptrw[mip_w * y + x] = ptrw[mip_w * y + x - mip_w];
+ }
+ }
+ // Override the src_mip_read pointer to our temporary Vector.
+ src_mip_read = padded_src.ptr();
+ }
if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2 || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
diff --git a/modules/fbx/fbx_parser/FBXAnimation.cpp b/modules/fbx/fbx_parser/FBXAnimation.cpp
index 8c43aac8f6..8627c95012 100644
--- a/modules/fbx/fbx_parser/FBXAnimation.cpp
+++ b/modules/fbx/fbx_parser/FBXAnimation.cpp
@@ -130,7 +130,7 @@ AnimationCurve::~AnimationCurve() {
AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name,
const Document &doc, const char *const *target_prop_whitelist /*= nullptr*/,
size_t whitelist_size /*= 0*/) :
- Object(id, element, name), target(), doc(doc) {
+ Object(id, element, name), doc(doc) {
// find target node
const char *whitelist[] = { "Model", "NodeAttribute", "Deformer" };
const std::vector<const Connection *> &conns = doc.GetConnectionsBySourceSequenced(ID(), whitelist, 3);
diff --git a/modules/fbx/fbx_parser/FBXDeformer.cpp b/modules/fbx/fbx_parser/FBXDeformer.cpp
index b888afd90e..a2b216ab09 100644
--- a/modules/fbx/fbx_parser/FBXDeformer.cpp
+++ b/modules/fbx/fbx_parser/FBXDeformer.cpp
@@ -104,7 +104,7 @@ Constraint::~Constraint() {
// ------------------------------------------------------------------------------------------------
Cluster::Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
- Deformer(id, element, doc, name), valid_transformAssociateModel(false) {
+ Deformer(id, element, doc, name) {
const ScopePtr sc = GetRequiredScope(element);
// for( auto element : sc.Elements())
// {
@@ -177,7 +177,7 @@ Cluster::~Cluster() {
// ------------------------------------------------------------------------------------------------
Skin::Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
- Deformer(id, element, doc, name), accuracy(0.0f) {
+ Deformer(id, element, doc, name) {
const ScopePtr sc = GetRequiredScope(element);
// keep this it is used for debugging and any FBX format changes
diff --git a/modules/fbx/fbx_parser/FBXParser.cpp b/modules/fbx/fbx_parser/FBXParser.cpp
index d8ccb4179c..e345b7fc18 100644
--- a/modules/fbx/fbx_parser/FBXParser.cpp
+++ b/modules/fbx/fbx_parser/FBXParser.cpp
@@ -235,7 +235,7 @@ Scope::~Scope() {
// ------------------------------------------------------------------------------------------------
Parser::Parser(const TokenList &tokens, bool is_binary) :
- corrupt(false), tokens(tokens), cursor(tokens.begin()), is_binary(is_binary) {
+ tokens(tokens), cursor(tokens.begin()), is_binary(is_binary) {
root = new_Scope(*this, true);
scopes.push_back(root);
}
diff --git a/modules/fbx/fbx_parser/FBXProperties.cpp b/modules/fbx/fbx_parser/FBXProperties.cpp
index 531f0743d6..7cbb3a2eda 100644
--- a/modules/fbx/fbx_parser/FBXProperties.cpp
+++ b/modules/fbx/fbx_parser/FBXProperties.cpp
@@ -145,8 +145,7 @@ std::string PeekPropertyName(const Element &element) {
} // namespace
// ------------------------------------------------------------------------------------------------
-PropertyTable::PropertyTable() :
- element(nullptr) {
+PropertyTable::PropertyTable() {
}
// Is used when dealing with FBX Objects not metadata.
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index eea898475b..879291c2e0 100644
--- a/modules/gdnative/include/nativescript/godot_nativescript.h
+++ b/modules/gdnative/include/nativescript/godot_nativescript.h
@@ -79,6 +79,7 @@ typedef enum {
GODOT_PROPERTY_HINT_PROPERTY_OF_BASE_TYPE, ///< a property of a base type
GODOT_PROPERTY_HINT_PROPERTY_OF_INSTANCE, ///< a property of an instance
GODOT_PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base
+ GODOT_PROPERTY_HINT_LOCALE_ID,
GODOT_PROPERTY_HINT_MAX,
} godot_nativescript_property_hint;
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 6425123e63..5cc295bbab 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -60,7 +60,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
bool in_keyword = false;
bool in_word = false;
bool in_function_name = false;
+ bool in_lambda = false;
bool in_variable_declaration = false;
+ bool in_signal_declaration = false;
bool in_function_args = false;
bool in_member_variable = false;
bool in_node_path = false;
@@ -105,12 +107,15 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
/* color regions */
if (is_a_symbol || in_region != -1) {
int from = j;
- for (; from < line_length; from++) {
- if (str[from] == '\\') {
- from++;
- continue;
+
+ if (in_region == -1) {
+ for (; from < line_length; from++) {
+ if (str[from] == '\\') {
+ from++;
+ continue;
+ }
+ break;
}
- break;
}
if (from != line_length) {
@@ -142,6 +147,12 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
/* check if it's the whole line */
if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) {
+ if (from + end_key_length > line_length) {
+ // If it's key length and there is a '\', dont skip to highlight esc chars.
+ if (str.find("\\", from) >= 0) {
+ break;
+ }
+ }
prev_color = color_regions[in_region].color;
highlighter_info["color"] = color_regions[c].color;
color_map[j] = highlighter_info;
@@ -161,13 +172,25 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
/* if we are in one find the end key */
if (in_region != -1) {
+ Color region_color = color_regions[in_region].color;
+ if (in_node_path && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
+ region_color = node_path_color;
+ }
+
+ prev_color = region_color;
+ highlighter_info["color"] = region_color;
+ color_map[j] = highlighter_info;
+
/* search the line */
int region_end_index = -1;
int end_key_length = color_regions[in_region].end_key.length();
const char32_t *end_key = color_regions[in_region].end_key.get_data();
for (; from < line_length; from++) {
if (line_length - from < end_key_length) {
- break;
+ // Don't break if '\' to highlight esc chars.
+ if (str.find("\\", from) < 0) {
+ break;
+ }
}
if (!is_symbol(str[from])) {
@@ -175,7 +198,16 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
if (str[from] == '\\') {
+ Dictionary escape_char_highlighter_info;
+ escape_char_highlighter_info["color"] = symbol_color;
+ color_map[from] = escape_char_highlighter_info;
+
from++;
+
+ Dictionary region_continue_highlighter_info;
+ prev_color = region_color;
+ region_continue_highlighter_info["color"] = region_color;
+ color_map[from + 1] = region_continue_highlighter_info;
continue;
}
@@ -192,10 +224,6 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
}
- prev_color = color_regions[in_region].color;
- highlighter_info["color"] = color_regions[in_region].color;
- color_map[j] = highlighter_info;
-
previous_type = REGION;
previous_text = "";
previous_column = j;
@@ -289,20 +317,36 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
if (!in_function_name && in_word && !in_keyword) {
- int k = j;
- while (k < str.length() && !is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
- k++;
- }
+ if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::SIGNAL)) {
+ in_signal_declaration = true;
+ } else {
+ int k = j;
+ while (k < str.length() && !is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k++;
+ }
- // check for space between name and bracket
- while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
- k++;
- }
+ // check for space between name and bracket
+ while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ k++;
+ }
- if (str[k] == '(') {
- in_function_name = true;
- } else if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::VAR)) {
- in_variable_declaration = true;
+ if (str[k] == '(') {
+ in_function_name = true;
+ } else if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::VAR)) {
+ in_variable_declaration = true;
+ }
+
+ // Check for lambda.
+ if (in_function_name && previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
+ k = j - 1;
+ while (k > 0 && (str[k] == '\t' || str[k] == ' ')) {
+ k--;
+ }
+
+ if (str[k] == ':') {
+ in_lambda = true;
+ }
+ }
}
}
@@ -348,7 +392,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
in_variable_declaration = false;
+ in_signal_declaration = false;
in_function_name = false;
+ in_lambda = false;
in_member_variable = false;
}
@@ -376,10 +422,14 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
} else if (in_member_variable) {
next_type = MEMBER;
color = member_color;
+ } else if (in_signal_declaration) {
+ next_type = SIGNAL;
+
+ color = member_color;
} else if (in_function_name) {
next_type = FUNCTION;
- if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
+ if (!in_lambda && previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
color = function_definition_color;
} else {
color = function_color;
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index ac4995bee7..1ae0d72896 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -58,6 +58,7 @@ private:
SYMBOL,
NUMBER,
FUNCTION,
+ SIGNAL,
KEYWORD,
MEMBER,
IDENTIFIER,
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 3a79190149..d11174227a 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1586,7 +1586,7 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
if (p_match_pattern->dictionary[i].key) {
reduce_expression(p_match_pattern->dictionary[i].key);
if (!p_match_pattern->dictionary[i].key->is_constant) {
- push_error(R"(Expression in dictionary pattern key must be a constant.)", p_match_pattern->expression);
+ push_error(R"(Expression in dictionary pattern key must be a constant.)", p_match_pattern->dictionary[i].key);
}
}
@@ -2641,7 +2641,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
GDScriptParser::DataType result;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.kind = GDScriptParser::DataType::ENUM_VALUE;
- result.builtin_type = base.builtin_type;
+ result.is_constant = true;
+ result.builtin_type = Variant::INT;
result.native_type = base.native_type;
result.enum_type = name;
p_identifier->set_datatype(result);
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 8623122edc..82aa14795e 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -688,6 +688,7 @@ void GDScriptByteCodeGenerator::write_ternary_false_expr(const Address &p_expr)
void GDScriptByteCodeGenerator::write_end_ternary() {
patch_jump(ternary_jump_skip_pos.back()->get());
ternary_jump_skip_pos.pop_back();
+ ternary_result.pop_back();
}
void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) {
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 4ac5a4a60e..6ada7d36f5 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -122,6 +122,10 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP
}
if (singleton->parser_map.has(p_path)) {
ref = Ref<GDScriptParserRef>(singleton->parser_map[p_path]);
+ if (ref.is_null()) {
+ r_error = ERR_INVALID_DATA;
+ return ref;
+ }
} else {
if (!FileAccess::exists(p_path)) {
r_error = ERR_FILE_NOT_FOUND;
@@ -133,7 +137,6 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP
ref->path = p_path;
singleton->parser_map[p_path] = ref.ptr();
}
-
r_error = ref->raise_status(p_status);
return ref;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 117ca68c18..ca125d3a07 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -2019,7 +2019,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
codegen.generator->start_parameters();
for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
- GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value, true);
+ GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value);
if (r_error) {
memdelete(codegen.generator);
return nullptr;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index f1877df326..9424de9d22 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -248,7 +248,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
// If the return value is a GDScriptFunctionState reference,
// then the function did await again after resuming.
- if (ret.is_ref()) {
+ if (ret.is_ref_counted()) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
completed = false;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 432d31f78f..460bd85a86 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -2105,7 +2105,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
while (p_precedence <= get_rule(current.type)->precedence) {
- if (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) {
+ if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL)) {
return previous_operand;
}
// Also switch multiline mode on here for infix operators.
@@ -2415,6 +2415,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
push_error("Assignment is not allowed inside an expression.");
return parse_expression(false); // Return the following expression.
}
+ if (p_previous_operand == nullptr) {
+ return parse_expression(false); // Return the following expression.
+ }
#ifdef DEBUG_ENABLED
VariableNode *source_variable = nullptr;
@@ -3497,7 +3500,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint_string = hint_string;
- // This is called after tne analyzer is done finding the type, so this should be set here.
+ // This is called after the analyzer is done finding the type, so this should be set here.
DataType export_type = variable->get_datatype();
if (p_annotation->name == "@export") {
@@ -4236,7 +4239,11 @@ void GDScriptParser::TreePrinter::print_get_node(GetNodeNode *p_get_node) {
}
void GDScriptParser::TreePrinter::print_identifier(IdentifierNode *p_identifier) {
- push_text(p_identifier->name);
+ if (p_identifier != nullptr) {
+ push_text(p_identifier->name);
+ } else {
+ push_text("<invalid identifier>");
+ }
}
void GDScriptParser::TreePrinter::print_if(IfNode *p_if, bool p_is_elif) {
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 05ea061798..9977b88aa1 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -786,6 +786,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
}
String result;
+ char32_t prev = 0;
+ int prev_pos = 0;
for (;;) {
// Consume actual string.
@@ -852,9 +854,11 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
case '\\':
escaped = '\\';
break;
- case 'u':
+ case 'U':
+ case 'u': {
// Hexadecimal sequence.
- for (int i = 0; i < 4; i++) {
+ int hex_len = (code == 'U') ? 6 : 4;
+ for (int j = 0; j < hex_len; j++) {
if (_is_at_end()) {
return make_error("Unterminated string.");
}
@@ -886,7 +890,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
_advance();
}
- break;
+ } break;
case '\r':
if (_peek() != '\n') {
// Carriage return without newline in string. (???)
@@ -909,11 +913,53 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
valid_escape = false;
break;
}
+ // Parse UTF-16 pair.
+ if (valid_escape) {
+ if ((escaped & 0xfffffc00) == 0xd800) {
+ if (prev == 0) {
+ prev = escaped;
+ prev_pos = column - 2;
+ continue;
+ } else {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = column - 2;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ valid_escape = false;
+ prev = 0;
+ }
+ } else if ((escaped & 0xfffffc00) == 0xdc00) {
+ if (prev == 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired trail surrogate");
+ error.start_column = column - 2;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ valid_escape = false;
+ } else {
+ escaped = (prev << 10UL) + escaped - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev = 0;
+ }
+ }
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
+ }
if (valid_escape) {
result += escaped;
}
} else if (ch == quote_char) {
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
_advance();
if (is_multiline) {
if (_peek() == quote_char && _peek(1) == quote_char) {
@@ -930,6 +976,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
break;
}
} else {
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
result += ch;
_advance();
if (ch == '\n') {
@@ -937,6 +990,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
}
}
}
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
// Make the literal.
Variant string;
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index e0facaf61d..95122714f9 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -1532,7 +1532,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
} else if (methodstr == "free") {
if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- if (base->is_ref()) {
+ if (base->is_ref_counted()) {
err_text = "Attempted to free a reference.";
OPCODE_BREAK;
} else if (base->get_type() == Variant::OBJECT) {
@@ -1620,7 +1620,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
} else if (methodstr == "free") {
if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- if (base->is_ref()) {
+ if (base->is_ref_counted()) {
err_text = "Attempted to free a reference.";
OPCODE_BREAK;
} else if (base->get_type() == Variant::OBJECT) {
diff --git a/modules/gdscript/icons/GDScriptInternal.svg b/modules/gdscript/icons/GDScriptInternal.svg
new file mode 100644
index 0000000000..fcabaafbd0
--- /dev/null
+++ b/modules/gdscript/icons/GDScriptInternal.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578c-.2364329.0758517-.4668872.16921-.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941c-.1119126.2211335-.2072287.4502818-.28516.68555l-2.2539.5625v2l2.2578.56445c.075942.2357685.1692993.465568.2793.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953c.2211335.111913.4502818.207229.68555.28516l.5625 2.2539h2l.56445-2.2578c.2357685-.07594.465568-.169299.6875-.2793l1.9902 1.1934 1.4141-1.4141-1.1953-1.9941c.111913-.221133.207229-.4502818.28516-.68555l2.2539-.5625v-2l-2.2578-.56445c-.075942-.2357685-.169299-.4655679-.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953c-.221133-.1119126-.4502818-.2072287-.68555-.28516l-.5625-2.2539zm1 5c1.1045695 0 2 .8954305 2 2s-.8954305 2-2 2-2-.8954305-2-2 .8954305-2 2-2z" fill="none" stroke="#e0e0e0"/></svg>
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index a944844226..ed879b088a 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -269,7 +269,7 @@ Array GDScriptWorkspace::symbol(const Dictionary &p_params) {
Vector<lsp::DocumentedSymbolInformation> script_symbols;
E.value->get_symbols().symbol_tree_as_list(E.key, script_symbols);
for (int i = 0; i < script_symbols.size(); ++i) {
- if (query.is_subsequence_ofi(script_symbols[i].name)) {
+ if (query.is_subsequence_ofn(script_symbols[i].name)) {
lsp::DocumentedSymbolInformation symbol = script_symbols[i];
symbol.location.uri = get_file_uri(symbol.location.uri);
arr.push_back(symbol.to_json());
@@ -585,7 +585,7 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
stack.push_back(owner_scene_node);
while (!stack.is_empty()) {
- current = stack.pop_back();
+ current = Object::cast_to<Node>(stack.pop_back());
Ref<GDScript> script = current->get_script();
if (script.is_valid() && script->get_path() == path) {
break;
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.gd b/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.gd
new file mode 100644
index 0000000000..05d9bd6a3d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.gd
@@ -0,0 +1,9 @@
+# https://github.com/godotengine/godot/issues/56702
+
+func test():
+ # somewhat obscure feature: referencing parameters in defaults, but only earlier ones!
+ ref_default("non-optional")
+
+
+func ref_default(nondefault1, defa=nondefault1, defb=defc, defc=1):
+ prints(nondefault1, nondefault2, defa, defb, defc)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.out b/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.out
new file mode 100644
index 0000000000..1d5b5bf393
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Identifier "defc" not declared in the current scope.
diff --git a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.gd b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.gd
new file mode 100644
index 0000000000..e9690ee93d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.gd
@@ -0,0 +1,2 @@
+func test():
+ $=$
diff --git a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out
new file mode 100644
index 0000000000..b3dc181a22
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expect node path as string or identifier after "$".
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
index 447d7e223c..5b0ea9df43 100644
--- a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
@@ -1,2 +1,2 @@
GDTEST_OK
-{2:4, a:1, b:2, with spaces:3}
+{a:1, b:2, with spaces:3, 2:4}
diff --git a/modules/gdscript/tests/scripts/runtime/features/params_default_values.gd b/modules/gdscript/tests/scripts/runtime/features/params_default_values.gd
new file mode 100644
index 0000000000..8156b4ec68
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/params_default_values.gd
@@ -0,0 +1,35 @@
+# https://github.com/godotengine/godot/issues/56702
+
+func test():
+ const_default()
+ func_result_default()
+ # calling again will run the initializer again,
+ # as the default is not evaluated at time of defining the function (as in python)
+ # but every time the function is called (as in C++)
+ func_result_default()
+ lots_of_defaults("non-optional")
+ # somewhat obscure feature: referencing earlier parameters
+ ref_default("non-optional", 42)
+
+
+func const_default(param=42):
+ print(param)
+
+
+var default_val := 0
+
+func get_default():
+ default_val += 1
+ return default_val
+
+
+func func_result_default(param=get_default()):
+ print(param)
+
+
+func lots_of_defaults(nondefault, one=1, two=2, three=get_default()):
+ prints(nondefault, one, two, three)
+
+
+func ref_default(nondefault1, nondefault2, defa=nondefault1, defb=nondefault2 - 1):
+ prints(nondefault1, nondefault2, defa, defb)
diff --git a/modules/gdscript/tests/scripts/runtime/features/params_default_values.out b/modules/gdscript/tests/scripts/runtime/features/params_default_values.out
new file mode 100644
index 0000000000..50e0885ae5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/params_default_values.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+42
+1
+2
+non-optional 1 2 3
+non-optional 42 non-optional 41
diff --git a/modules/glslang/glslang_resource_limits.h b/modules/glslang/glslang_resource_limits.h
index 05390f95ad..02d3daff07 100644
--- a/modules/glslang/glslang_resource_limits.h
+++ b/modules/glslang/glslang_resource_limits.h
@@ -132,15 +132,15 @@ const TBuiltInResource DefaultTBuiltInResource = {
/* .maxDualSourceDrawBuffersEXT = */ 1,
/* .limits = */ {
- /* .nonInductiveForLoops = */ 1,
- /* .whileLoops = */ 1,
- /* .doWhileLoops = */ 1,
- /* .generalUniformIndexing = */ 1,
- /* .generalAttributeMatrixVectorIndexing = */ 1,
- /* .generalVaryingIndexing = */ 1,
- /* .generalSamplerIndexing = */ 1,
- /* .generalVariableIndexing = */ 1,
- /* .generalConstantMatrixVectorIndexing = */ 1,
+ /* .nonInductiveForLoops = */ true,
+ /* .whileLoops = */ true,
+ /* .doWhileLoops = */ true,
+ /* .generalUniformIndexing = */ true,
+ /* .generalAttributeMatrixVectorIndexing = */ true,
+ /* .generalVaryingIndexing = */ true,
+ /* .generalSamplerIndexing = */ true,
+ /* .generalVariableIndexing = */ true,
+ /* .generalConstantMatrixVectorIndexing = */ true,
}
};
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 5a931ed839..51608273a1 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -6843,6 +6843,7 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> state, const String &p_pa
}
Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) {
+ ERR_FAIL_NULL_V(state, nullptr);
ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr);
GLTFNodeIndex gltf_root = state->root_nodes.write[0];
Node *gltf_root_node = state->get_scene_node(gltf_root);
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 885817caf1..9c28421a46 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -67,7 +67,7 @@
Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
- <method name="get_meshes">
+ <method name="get_meshes" qualifiers="const">
<return type="Array" />
<description>
Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space.
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index a861efcbf4..67deedf839 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -35,6 +35,7 @@
#include "scene/3d/light_3d.h"
#include "scene/resources/mesh_library.h"
#include "scene/resources/physics_material.h"
+#include "scene/resources/primitive_meshes.h"
#include "scene/resources/surface_tool.h"
#include "scene/scene_string_names.h"
#include "servers/navigation_server_3d.h"
@@ -197,6 +198,24 @@ bool GridMap::get_collision_mask_value(int p_layer_number) const {
return get_collision_mask() & (1 << (p_layer_number - 1));
}
+Array GridMap::get_collision_shapes() const {
+ Array shapes;
+ for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
+ Octant *g = E.value;
+ RID body = g->static_body;
+ Transform3D body_xform = PhysicsServer3D::get_singleton()->body_get_state(body, PhysicsServer3D::BODY_STATE_TRANSFORM);
+ int nshapes = PhysicsServer3D::get_singleton()->body_get_shape_count(body);
+ for (int i = 0; i < nshapes; i++) {
+ RID shape = PhysicsServer3D::get_singleton()->body_get_shape(body, i);
+ Transform3D xform = PhysicsServer3D::get_singleton()->body_get_shape_transform(body, i);
+ shapes.push_back(body_xform * xform);
+ shapes.push_back(shape);
+ }
+ }
+
+ return shapes;
+}
+
void GridMap::set_bake_navigation(bool p_bake_navigation) {
bake_navigation = p_bake_navigation;
_recreate_octant_data();
@@ -930,7 +949,7 @@ Array GridMap::get_used_cells() const {
return a;
}
-Array GridMap::get_meshes() {
+Array GridMap::get_meshes() const {
if (mesh_library.is_null()) {
return Array();
}
@@ -938,7 +957,7 @@ Array GridMap::get_meshes() {
Vector3 ofs = _get_offset();
Array meshes;
- for (KeyValue<IndexKey, Cell> &E : cell_map) {
+ for (const KeyValue<IndexKey, Cell> &E : cell_map) {
int id = E.value.item;
if (!mesh_library->has_item(id)) {
continue;
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index 546b530148..6cdc3b178d 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -229,6 +229,8 @@ public:
void set_physics_material(Ref<PhysicsMaterial> p_material);
Ref<PhysicsMaterial> get_physics_material() const;
+ Array get_collision_shapes() const;
+
void set_bake_navigation(bool p_bake_navigation);
bool is_baking_navigation();
@@ -265,7 +267,7 @@ public:
Array get_used_cells() const;
- Array get_meshes();
+ Array get_meshes() const;
void clear_baked_meshes();
void make_baked_meshes(bool p_gen_lightmap_uv = false, float p_lightmap_uv_texel_size = 0.1);
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 7abc8f7d7a..84510fc71e 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -889,7 +889,7 @@ void GridMapEditor::update_palette() {
name = "#" + itos(id);
}
- if (!filter.is_empty() && !filter.is_subsequence_ofi(name)) {
+ if (!filter.is_empty() && !filter.is_subsequence_ofn(name)) {
continue;
}
@@ -1028,6 +1028,13 @@ void GridMapEditor::_draw_grids(const Vector3 &cell_size) {
}
}
+void GridMapEditor::_update_theme() {
+ options->set_icon(get_theme_icon(SNAME("GridMap"), SNAME("EditorIcons")));
+ search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ mode_thumbnail->set_icon(get_theme_icon(SNAME("FileThumbnail"), SNAME("EditorIcons")));
+ mode_list->set_icon(get_theme_icon(SNAME("FileList"), SNAME("EditorIcons")));
+}
+
void GridMapEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -1048,6 +1055,7 @@ void GridMapEditor::_notification(int p_what) {
_update_selection_transform();
_update_paste_indicator();
+ _update_theme();
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -1088,8 +1096,7 @@ void GridMapEditor::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
- options->set_icon(get_theme_icon(SNAME("GridMap"), SNAME("EditorIcons")));
- search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ _update_theme();
} break;
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
@@ -1250,7 +1257,6 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
mode_thumbnail->set_flat(true);
mode_thumbnail->set_toggle_mode(true);
mode_thumbnail->set_pressed(true);
- mode_thumbnail->set_icon(p_editor->get_gui_base()->get_theme_icon(SNAME("FileThumbnail"), SNAME("EditorIcons")));
hb->add_child(mode_thumbnail);
mode_thumbnail->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode), varray(DISPLAY_THUMBNAIL));
@@ -1258,7 +1264,6 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
mode_list->set_flat(true);
mode_list->set_toggle_mode(true);
mode_list->set_pressed(false);
- mode_list->set_icon(p_editor->get_gui_base()->get_theme_icon(SNAME("FileList"), SNAME("EditorIcons")));
hb->add_child(mode_list);
mode_list->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode), varray(DISPLAY_LIST));
@@ -1454,10 +1459,10 @@ void GridMapEditorPlugin::_notification(int p_what) {
if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
switch ((int)EditorSettings::get_singleton()->get("editors/grid_map/editor_side")) {
case 0: { // Left.
- Node3DEditor::get_singleton()->get_palette_split()->move_child(grid_map_editor, 0);
+ Node3DEditor::get_singleton()->move_control_to_left_panel(grid_map_editor);
} break;
case 1: { // Right.
- Node3DEditor::get_singleton()->get_palette_split()->move_child(grid_map_editor, 1);
+ Node3DEditor::get_singleton()->move_control_to_right_panel(grid_map_editor);
} break;
}
}
@@ -1493,10 +1498,10 @@ GridMapEditorPlugin::GridMapEditorPlugin(EditorNode *p_node) {
grid_map_editor = memnew(GridMapEditor(editor));
switch ((int)EditorSettings::get_singleton()->get("editors/grid_map/editor_side")) {
case 0: { // Left.
- add_control_to_container(CONTAINER_SPATIAL_EDITOR_SIDE_LEFT, grid_map_editor);
+ Node3DEditor::get_singleton()->add_control_to_left_panel(grid_map_editor);
} break;
case 1: { // Right.
- add_control_to_container(CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT, grid_map_editor);
+ Node3DEditor::get_singleton()->add_control_to_right_panel(grid_map_editor);
} break;
}
grid_map_editor->hide();
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 7e9510e227..37298a1d80 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -201,6 +201,7 @@ class GridMapEditor : public VBoxContainer {
void _update_cursor_transform();
void _update_cursor_instance();
void _update_clip();
+ void _update_theme();
void _text_changed(const String &p_text);
void _sbox_input(const Ref<InputEvent> &p_ie);
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index 59854ad527..8cd23ffb24 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -181,6 +181,7 @@ void MobileVRInterface::set_position_from_sensors() {
orientation = rotate * orientation;
tracking_state = XRInterface::XR_NORMAL_TRACKING;
+ tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
};
///@TODO improve this, the magnetometer is very fidgety sometimes flipping the axis for no apparent reason (probably a bug on my part)
@@ -193,6 +194,7 @@ void MobileVRInterface::set_position_from_sensors() {
orientation = Basis(transform_quat);
tracking_state = XRInterface::XR_NORMAL_TRACKING;
+ tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
} else if (has_grav) {
// use gravity vector to make sure down is down...
// transform gravity into our world space
@@ -461,7 +463,7 @@ CameraMatrix MobileVRInterface::get_projection_for_view(uint32_t p_view, double
return eye;
};
-Vector<BlitToScreen> MobileVRInterface::commit_views(RID p_render_target, const Rect2 &p_screen_rect) {
+Vector<BlitToScreen> MobileVRInterface::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
_THREAD_SAFE_METHOD_
Vector<BlitToScreen> blit_to_screen;
@@ -512,7 +514,7 @@ void MobileVRInterface::process() {
if (head.is_valid()) {
// Set our head position, note in real space, reference frame and world scale is applied later
- head->set_pose("default", head_transform, Vector3(), Vector3());
+ head->set_pose("default", head_transform, Vector3(), Vector3(), tracking_confidence);
}
};
};
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index ac04763569..8ecca3a2ae 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -51,6 +51,7 @@ class MobileVRInterface : public XRInterface {
private:
bool initialized = false;
XRInterface::TrackingStatus tracking_state;
+ XRPose::TrackingConfidence tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_NONE;
// Just set some defaults for these. At some point we need to look at adding a lookup table for common device + headset combos and/or support reading cardboard QR codes
double eye_height = 1.85;
@@ -150,7 +151,7 @@ public:
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
- virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override;
+ virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override;
diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py
index 93a66ebf6f..43c1ec8f8a 100644
--- a/modules/mono/build_scripts/mono_reg_utils.py
+++ b/modules/mono/build_scripts/mono_reg_utils.py
@@ -96,10 +96,10 @@ def find_msbuild_tools_path_reg():
raise ValueError("Cannot find `installationPath` entry")
except ValueError as e:
print("Error reading output from vswhere: " + e.message)
- except OSError:
- pass # Fine, vswhere not found
- except (subprocess.CalledProcessError, OSError):
- pass
+ except subprocess.CalledProcessError as e:
+ print(e.output)
+ except OSError as e:
+ print(e)
# Try to find 14.0 in the Registry
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 3d02f638ec..26436e3ec0 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1168,8 +1168,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#ifdef TOOLS_ENABLED
// FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative.
if (Engine::get_singleton()->is_editor_hint()) {
- EditorNode::get_singleton()->get_inspector()->update_tree();
- NodeDock::singleton->update_lists();
+ InspectorDock::get_inspector_singleton()->update_tree();
+ NodeDock::get_singleton()->update_lists();
}
#endif
}
@@ -2995,6 +2995,7 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
CRASH_COND(p_script->native == nullptr);
p_script->valid = true;
+ p_script->reload_invalidated = false;
update_script_class_info(p_script);
@@ -3351,13 +3352,13 @@ MethodInfo CSharpScript::get_method_info(const StringName &p_method) const {
}
Error CSharpScript::reload(bool p_keep_state) {
- bool has_instances;
- {
- MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex);
- has_instances = instances.size();
+ if (!reload_invalidated) {
+ return OK;
}
- ERR_FAIL_COND_V(!p_keep_state && has_instances, ERR_ALREADY_IN_USE);
+ // In the case of C#, reload doesn't really do any script reloading.
+ // That's done separately via domain reloading.
+ reload_invalidated = false;
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -3544,6 +3545,7 @@ void CSharpScript::_update_name() {
void CSharpScript::_clear() {
tool = false;
valid = false;
+ reload_invalidated = true;
base = nullptr;
native = nullptr;
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 221bba3af9..2be588cac4 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -101,6 +101,7 @@ private:
bool tool = false;
bool valid = false;
+ bool reload_invalidated = false;
bool builtin;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index e9cf7911be..2dbc78ab77 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -147,7 +147,7 @@ namespace GodotTools.Build
Icon = GetThemeIcon("StatusError", "EditorIcons"),
ExpandIcon = false,
ToggleMode = true,
- Pressed = true,
+ ButtonPressed = true,
FocusMode = FocusModeEnum.None
};
_errorsBtn.Toggled += ErrorsToggled;
@@ -159,7 +159,7 @@ namespace GodotTools.Build
Icon = GetThemeIcon("NodeWarning", "EditorIcons"),
ExpandIcon = false,
ToggleMode = true,
- Pressed = true,
+ ButtonPressed = true,
FocusMode = FocusModeEnum.None
};
_warningsBtn.Toggled += WarningsToggled;
@@ -169,7 +169,7 @@ namespace GodotTools.Build
{
Text = "Show Output".TTR(),
ToggleMode = true,
- Pressed = true,
+ ButtonPressed = true,
FocusMode = FocusModeEnum.None
};
_viewLogBtn.Toggled += ViewLogToggled;
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 98c6881166..69960bdbeb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -428,7 +428,7 @@ namespace GodotTools
Shortcut = buildSolutionShortcut,
ShortcutInTooltip = true
};
- _toolBarBuildButton.PressedSignal += BuildSolutionPressed;
+ _toolBarBuildButton.Pressed += BuildSolutionPressed;
AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton);
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index a89dca6c34..eba0ea9a79 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -727,7 +727,7 @@ namespace Godot
/// <summary>
/// Check whether this string is a subsequence of the given string.
/// </summary>
- /// <seealso cref="IsSubsequenceOfI(string, string)"/>
+ /// <seealso cref="IsSubsequenceOfN(string, string)"/>
/// <param name="instance">The subsequence to search.</param>
/// <param name="text">The string that contains the subsequence.</param>
/// <param name="caseSensitive">If <see langword="true"/>, the check is case sensitive.</param>
@@ -779,7 +779,7 @@ namespace Godot
/// <param name="instance">The subsequence to search.</param>
/// <param name="text">The string that contains the subsequence.</param>
/// <returns>If the string is a subsequence of the given string.</returns>
- public static bool IsSubsequenceOfI(this string instance, string text)
+ public static bool IsSubsequenceOfN(this string instance, string text)
{
return instance.IsSubsequenceOf(text, caseSensitive: false);
}
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 02739f0480..c0cd18e29d 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -68,7 +68,14 @@ String _get_mono_user_dir() {
} else {
String settings_path;
+ // Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir.
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
+
+ // On macOS, look outside .app bundle, since .app bundle is read-only.
+ if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.plus_file("..").simplify_path().ends_with("Contents")) {
+ exe_dir = exe_dir.plus_file("../../..").simplify_path();
+ }
+
DirAccessRef d = DirAccess::create_for_path(exe_dir);
if (d->file_exists("._sc_") || d->file_exists("_sc_")) {
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 6cbdb645ca..52d5379e8b 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -140,12 +140,12 @@ void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, cons
}
}
-void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
+void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
if (Object::cast_to<MeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node);
Ref<Mesh> mesh = mesh_instance->get_mesh();
if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * mesh_instance->get_transform(), p_vertices, p_indices);
+ _add_mesh(mesh, p_navmesh_transform * mesh_instance->get_global_transform(), p_vertices, p_indices);
}
}
@@ -159,7 +159,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
n = multimesh->get_instance_count();
}
for (int i = 0; i < n; i++) {
- _add_mesh(mesh, p_accumulated_transform * multimesh->get_instance_transform(i), p_vertices, p_indices);
+ _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices);
}
}
}
@@ -171,7 +171,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
if (!meshes.is_empty()) {
Ref<Mesh> mesh = meshes[1];
if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_vertices, p_indices);
+ _add_mesh(mesh, p_navmesh_transform * csg_shape->get_global_transform(), p_vertices, p_indices);
}
}
}
@@ -186,7 +186,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
if (Object::cast_to<CollisionShape3D>(child)) {
CollisionShape3D *col_shape = Object::cast_to<CollisionShape3D>(child);
- Transform3D transform = p_accumulated_transform * static_body->get_transform() * col_shape->get_transform();
+ Transform3D transform = p_navmesh_transform * static_body->get_global_transform() * col_shape->get_transform();
Ref<Mesh> mesh;
Ref<Shape3D> s = col_shape->get_shape();
@@ -265,27 +265,108 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
}
#ifdef MODULE_GRIDMAP_ENABLED
- if (Object::cast_to<GridMap>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
- GridMap *gridmap_instance = Object::cast_to<GridMap>(p_node);
- Array meshes = gridmap_instance->get_meshes();
- Transform3D xform = gridmap_instance->get_transform();
- for (int i = 0; i < meshes.size(); i += 2) {
- Ref<Mesh> mesh = meshes[i + 1];
- if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices);
+ GridMap *gridmap = Object::cast_to<GridMap>(p_node);
+
+ if (gridmap) {
+ if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
+ Array meshes = gridmap->get_meshes();
+ Transform3D xform = gridmap->get_global_transform();
+ for (int i = 0; i < meshes.size(); i += 2) {
+ Ref<Mesh> mesh = meshes[i + 1];
+ if (mesh.is_valid()) {
+ _add_mesh(mesh, p_navmesh_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices);
+ }
}
}
- }
-#endif
- if (Object::cast_to<Node3D>(p_node)) {
- Node3D *spatial = Object::cast_to<Node3D>(p_node);
- p_accumulated_transform = p_accumulated_transform * spatial->get_transform();
+ if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES && (gridmap->get_collision_layer() & p_collision_mask)) {
+ Array shapes = gridmap->get_collision_shapes();
+ for (int i = 0; i < shapes.size(); i += 2) {
+ RID shape = shapes[i + 1];
+ PhysicsServer3D::ShapeType type = PhysicsServer3D::get_singleton()->shape_get_type(shape);
+ Variant data = PhysicsServer3D::get_singleton()->shape_get_data(shape);
+ Ref<Mesh> mesh;
+
+ switch (type) {
+ case PhysicsServer3D::SHAPE_SPHERE: {
+ real_t radius = data;
+ Ref<SphereMesh> sphere_mesh;
+ sphere_mesh.instantiate();
+ sphere_mesh->set_radius(radius);
+ sphere_mesh->set_height(radius * 2.0);
+ mesh = sphere_mesh;
+ } break;
+ case PhysicsServer3D::SHAPE_BOX: {
+ Vector3 extents = data;
+ Ref<BoxMesh> box_mesh;
+ box_mesh.instantiate();
+ box_mesh->set_size(2.0 * extents);
+ mesh = box_mesh;
+ } break;
+ case PhysicsServer3D::SHAPE_CAPSULE: {
+ Dictionary dict = data;
+ real_t radius = dict["radius"];
+ real_t height = dict["height"];
+ Ref<CapsuleMesh> capsule_mesh;
+ capsule_mesh.instantiate();
+ capsule_mesh->set_radius(radius);
+ capsule_mesh->set_height(height);
+ mesh = capsule_mesh;
+ } break;
+ case PhysicsServer3D::SHAPE_CYLINDER: {
+ Dictionary dict = data;
+ real_t radius = dict["radius"];
+ real_t height = dict["height"];
+ Ref<CylinderMesh> cylinder_mesh;
+ cylinder_mesh.instantiate();
+ cylinder_mesh->set_height(height);
+ cylinder_mesh->set_bottom_radius(radius);
+ cylinder_mesh->set_top_radius(radius);
+ mesh = cylinder_mesh;
+ } break;
+ case PhysicsServer3D::SHAPE_CONVEX_POLYGON: {
+ PackedVector3Array vertices = data;
+ Geometry3D::MeshData md;
+
+ Error err = ConvexHullComputer::convex_hull(vertices, md);
+
+ if (err == OK) {
+ PackedVector3Array faces;
+
+ for (int j = 0; j < md.faces.size(); ++j) {
+ Geometry3D::MeshData::Face face = md.faces[j];
+
+ for (int k = 2; k < face.indices.size(); ++k) {
+ faces.push_back(md.vertices[face.indices[0]]);
+ faces.push_back(md.vertices[face.indices[k - 1]]);
+ faces.push_back(md.vertices[face.indices[k]]);
+ }
+ }
+
+ _add_faces(faces, shapes[i], p_vertices, p_indices);
+ }
+ } break;
+ case PhysicsServer3D::SHAPE_CONCAVE_POLYGON: {
+ Dictionary dict = data;
+ PackedVector3Array faces = Variant(dict["faces"]);
+ _add_faces(faces, shapes[i], p_vertices, p_indices);
+ } break;
+ default: {
+ WARN_PRINT("Unsupported collision shape type.");
+ } break;
+ }
+
+ if (mesh.is_valid()) {
+ _add_mesh(mesh, shapes[i], p_vertices, p_indices);
+ }
+ }
+ }
}
+#endif
if (p_recurse_children) {
for (int i = 0; i < p_node->get_child_count(); i++) {
- _parse_geometry(p_accumulated_transform, p_node->get_child(i), p_vertices, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
+ _parse_geometry(p_navmesh_transform, p_node->get_child(i), p_vertices, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
}
}
}
@@ -530,7 +611,7 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
p_node->get_tree()->get_nodes_in_group(p_nav_mesh->get_source_group_name(), &parse_nodes);
}
- Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_transform().affine_inverse();
+ Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_global_transform().affine_inverse();
for (Node *E : parse_nodes) {
NavigationMesh::ParsedGeometryType geometry_type = p_nav_mesh->get_parsed_geometry_type();
uint32_t collision_mask = p_nav_mesh->get_collision_mask();
diff --git a/modules/navigation/navigation_mesh_generator.h b/modules/navigation/navigation_mesh_generator.h
index 1ffdd39aee..21f7a4941b 100644
--- a/modules/navigation/navigation_mesh_generator.h
+++ b/modules/navigation/navigation_mesh_generator.h
@@ -52,7 +52,7 @@ protected:
static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices);
static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
- static void _parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
+ static void _parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh);
static void _build_recast_navigation_mesh(
diff --git a/modules/pvr/SCsub b/modules/pvr/SCsub
deleted file mode 100644
index 36052cffed..0000000000
--- a/modules/pvr/SCsub
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-env_pvr = env_modules.Clone()
-
-# Thirdparty source files
-
-thirdparty_obj = []
-
-# Not unbundled so far since not widespread as shared library
-thirdparty_dir = "#thirdparty/pvrtccompressor/"
-thirdparty_sources = [
- "BitScale.cpp",
- "MortonTable.cpp",
- "PvrTcDecoder.cpp",
- "PvrTcEncoder.cpp",
- "PvrTcPacket.cpp",
-]
-thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
-env_pvr.Prepend(CPPPATH=[thirdparty_dir])
-
-env_thirdparty = env_pvr.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
-env.modules_sources += thirdparty_obj
-
-# Godot source files
-
-module_obj = []
-
-env_pvr.add_source_files(module_obj, "*.cpp")
-env.modules_sources += module_obj
-
-# Needed to force rebuilding the module files when the thirdparty library is updated.
-env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/pvr/config.py b/modules/pvr/config.py
deleted file mode 100644
index d22f9454ed..0000000000
--- a/modules/pvr/config.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def can_build(env, platform):
- return True
-
-
-def configure(env):
- pass
diff --git a/modules/pvr/image_compress_pvrtc.h b/modules/pvr/image_compress_pvrtc.h
deleted file mode 100644
index 80edb81363..0000000000
--- a/modules/pvr/image_compress_pvrtc.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*************************************************************************/
-/* image_compress_pvrtc.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef IMAGE_COMPRESS_PVRTC_H
-#define IMAGE_COMPRESS_PVRTC_H
-
-void _register_pvrtc_compress_func();
-
-#endif // IMAGE_COMPRESS_PVRTC_H
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
deleted file mode 100644
index ff66a101ab..0000000000
--- a/modules/pvr/texture_loader_pvr.cpp
+++ /dev/null
@@ -1,608 +0,0 @@
-/*************************************************************************/
-/* texture_loader_pvr.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "texture_loader_pvr.h"
-
-#include "core/io/file_access.h"
-
-static void _pvrtc_decompress(Image *p_img);
-
-enum PVRFLags {
- PVR_HAS_MIPMAPS = 0x00000100,
- PVR_TWIDDLED = 0x00000200,
- PVR_NORMAL_MAP = 0x00000400,
- PVR_BORDER = 0x00000800,
- PVR_CUBE_MAP = 0x00001000,
- PVR_FALSE_MIPMAPS = 0x00002000,
- PVR_VOLUME_TEXTURES = 0x00004000,
- PVR_HAS_ALPHA = 0x00008000,
- PVR_VFLIP = 0x00010000
-};
-
-RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- if (r_error) {
- *r_error = ERR_CANT_OPEN;
- }
-
- Error err;
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (!f) {
- return RES();
- }
-
- FileAccessRef faref(f);
-
- ERR_FAIL_COND_V(err, RES());
-
- if (r_error) {
- *r_error = ERR_FILE_CORRUPT;
- }
-
- uint32_t hsize = f->get_32();
-
- ERR_FAIL_COND_V(hsize != 52, RES());
- uint32_t height = f->get_32();
- uint32_t width = f->get_32();
- uint32_t mipmaps = f->get_32();
- uint32_t flags = f->get_32();
- uint32_t surfsize = f->get_32();
- f->seek(f->get_position() + 20); // bpp, rmask, gmask, bmask, amask
- uint8_t pvrid[5] = { 0, 0, 0, 0, 0 };
- f->get_buffer(pvrid, 4);
- ERR_FAIL_COND_V(String((char *)pvrid) != "PVR!", RES());
- f->get_32(); // surfcount
-
- /*
- print_line("height: "+itos(height));
- print_line("width: "+itos(width));
- print_line("mipmaps: "+itos(mipmaps));
- print_line("flags: "+itos(flags));
- print_line("surfsize: "+itos(surfsize));
- print_line("bpp: "+itos(bpp));
- print_line("rmask: "+itos(rmask));
- print_line("gmask: "+itos(gmask));
- print_line("bmask: "+itos(bmask));
- print_line("amask: "+itos(amask));
- print_line("surfcount: "+itos(surfcount));
- */
-
- Vector<uint8_t> data;
- data.resize(surfsize);
-
- ERR_FAIL_COND_V(data.size() == 0, RES());
-
- uint8_t *w = data.ptrw();
- f->get_buffer(&w[0], surfsize);
- err = f->get_error();
- ERR_FAIL_COND_V(err != OK, RES());
-
- Image::Format format = Image::FORMAT_MAX;
-
- switch (flags & 0xFF) {
- case 0x18:
- case 0xC:
- format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC1_2A : Image::FORMAT_PVRTC1_2;
- break;
- case 0x19:
- case 0xD:
- format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC1_4A : Image::FORMAT_PVRTC1_4;
- break;
- case 0x16:
- format = Image::FORMAT_L8;
- break;
- case 0x17:
- format = Image::FORMAT_LA8;
- break;
- case 0x20:
- case 0x80:
- case 0x81:
- format = Image::FORMAT_DXT1;
- break;
- case 0x21:
- case 0x22:
- case 0x82:
- case 0x83:
- format = Image::FORMAT_DXT3;
- break;
- case 0x23:
- case 0x24:
- case 0x84:
- case 0x85:
- format = Image::FORMAT_DXT5;
- break;
- case 0x4:
- case 0x15:
- format = Image::FORMAT_RGB8;
- break;
- case 0x5:
- case 0x12:
- format = Image::FORMAT_RGBA8;
- break;
- case 0x36:
- format = Image::FORMAT_ETC;
- break;
- default:
- ERR_FAIL_V_MSG(RES(), "Unsupported format in PVR texture: " + itos(flags & 0xFF) + ".");
- }
-
- Ref<Image> image = memnew(Image(width, height, mipmaps, format, data));
- ERR_FAIL_COND_V(image->is_empty(), RES());
-
- Ref<ImageTexture> texture = memnew(ImageTexture);
- texture->create_from_image(image);
-
- if (r_error) {
- *r_error = OK;
- }
-
- return texture;
-}
-
-void ResourceFormatPVR::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("pvr");
-}
-
-bool ResourceFormatPVR::handles_type(const String &p_type) const {
- return ClassDB::is_parent_class(p_type, "Texture2D");
-}
-
-String ResourceFormatPVR::get_resource_type(const String &p_path) const {
- if (p_path.get_extension().to_lower() == "pvr") {
- return "Texture2D";
- }
- return "";
-}
-
-ResourceFormatPVR::ResourceFormatPVR() {
- Image::_image_decompress_pvrtc = _pvrtc_decompress;
-}
-
-/////////////////////////////////////////////////////////
-
-//PVRTC decompressor, Based on PVRTC decompressor by IMGTEC.
-
-/////////////////////////////////////////////////////////
-
-#define PT_INDEX 2
-#define BLK_Y_SIZE 4
-#define BLK_X_MAX 8
-#define BLK_X_2BPP 8
-#define BLK_X_4BPP 4
-
-#define WRAP_COORD(Val, Size) ((Val) & ((Size)-1))
-
-/*
- Define an expression to either wrap or clamp large or small vals to the
- legal coordinate range
-*/
-#define LIMIT_COORD(Val, Size, p_tiled) \
- ((p_tiled) ? WRAP_COORD((Val), (Size)) : CLAMP((Val), 0, (Size)-1))
-
-struct PVRTCBlock {
- //blocks are 64 bits
- uint32_t data[2] = {};
-};
-
-_FORCE_INLINE_ bool is_po2(uint32_t p_input) {
- if (p_input == 0) {
- return false;
- }
- uint32_t minus1 = p_input - 1;
- return ((p_input | minus1) == (p_input ^ minus1)) ? true : false;
-}
-
-static void unpack_5554(const PVRTCBlock *p_block, int p_ab_colors[2][4]) {
- uint32_t raw_bits[2];
- raw_bits[0] = p_block->data[1] & (0xFFFE);
- raw_bits[1] = p_block->data[1] >> 16;
-
- for (int i = 0; i < 2; i++) {
- if (raw_bits[i] & (1 << 15)) {
- p_ab_colors[i][0] = (raw_bits[i] >> 10) & 0x1F;
- p_ab_colors[i][1] = (raw_bits[i] >> 5) & 0x1F;
- p_ab_colors[i][2] = raw_bits[i] & 0x1F;
- if (i == 0) {
- p_ab_colors[0][2] |= p_ab_colors[0][2] >> 4;
- }
- p_ab_colors[i][3] = 0xF;
- } else {
- p_ab_colors[i][0] = (raw_bits[i] >> (8 - 1)) & 0x1E;
- p_ab_colors[i][1] = (raw_bits[i] >> (4 - 1)) & 0x1E;
-
- p_ab_colors[i][0] |= p_ab_colors[i][0] >> 4;
- p_ab_colors[i][1] |= p_ab_colors[i][1] >> 4;
-
- p_ab_colors[i][2] = (raw_bits[i] & 0xF) << 1;
-
- if (i == 0) {
- p_ab_colors[0][2] |= p_ab_colors[0][2] >> 3;
- } else {
- p_ab_colors[0][2] |= p_ab_colors[0][2] >> 4;
- }
-
- p_ab_colors[i][3] = (raw_bits[i] >> 11) & 0xE;
- }
- }
-}
-
-static void unpack_modulations(const PVRTCBlock *p_block, const int p_2bit, int p_modulation[8][16], int p_modulation_modes[8][16], int p_x, int p_y) {
- int block_mod_mode = p_block->data[1] & 1;
- uint32_t modulation_bits = p_block->data[0];
-
- if (p_2bit && block_mod_mode) {
- for (int y = 0; y < BLK_Y_SIZE; y++) {
- for (int x = 0; x < BLK_X_2BPP; x++) {
- p_modulation_modes[y + p_y][x + p_x] = block_mod_mode;
-
- if (((x ^ y) & 1) == 0) {
- p_modulation[y + p_y][x + p_x] = modulation_bits & 3;
- modulation_bits >>= 2;
- }
- }
- }
-
- } else if (p_2bit) {
- for (int y = 0; y < BLK_Y_SIZE; y++) {
- for (int x = 0; x < BLK_X_2BPP; x++) {
- p_modulation_modes[y + p_y][x + p_x] = block_mod_mode;
-
- if (modulation_bits & 1) {
- p_modulation[y + p_y][x + p_x] = 0x3;
- } else {
- p_modulation[y + p_y][x + p_x] = 0x0;
- }
-
- modulation_bits >>= 1;
- }
- }
- } else {
- for (int y = 0; y < BLK_Y_SIZE; y++) {
- for (int x = 0; x < BLK_X_4BPP; x++) {
- p_modulation_modes[y + p_y][x + p_x] = block_mod_mode;
- p_modulation[y + p_y][x + p_x] = modulation_bits & 3;
- modulation_bits >>= 2;
- }
- }
- }
-
- ERR_FAIL_COND(modulation_bits != 0);
-}
-
-static void interpolate_colors(const int p_colorp[4], const int p_colorq[4], const int p_colorr[4], const int p_colors[4], bool p_2bit, const int x, const int y, int r_result[4]) {
- int u, v, uscale;
- int k;
-
- int tmp1, tmp2;
-
- int P[4], Q[4], R[4], S[4];
-
- for (k = 0; k < 4; k++) {
- P[k] = p_colorp[k];
- Q[k] = p_colorq[k];
- R[k] = p_colorr[k];
- S[k] = p_colors[k];
- }
-
- v = (y & 0x3) | ((~y & 0x2) << 1);
-
- if (p_2bit) {
- u = (x & 0x7) | ((~x & 0x4) << 1);
- } else {
- u = (x & 0x3) | ((~x & 0x2) << 1);
- }
-
- v = v - BLK_Y_SIZE / 2;
-
- if (p_2bit) {
- u = u - BLK_X_2BPP / 2;
- uscale = 8;
- } else {
- u = u - BLK_X_4BPP / 2;
- uscale = 4;
- }
-
- for (k = 0; k < 4; k++) {
- tmp1 = P[k] * uscale + u * (Q[k] - P[k]);
- tmp2 = R[k] * uscale + u * (S[k] - R[k]);
-
- tmp1 = tmp1 * 4 + v * (tmp2 - tmp1);
-
- r_result[k] = tmp1;
- }
-
- if (p_2bit) {
- for (k = 0; k < 3; k++) {
- r_result[k] >>= 2;
- }
-
- r_result[3] >>= 1;
- } else {
- for (k = 0; k < 3; k++) {
- r_result[k] >>= 1;
- }
- }
-
- for (k = 0; k < 4; k++) {
- ERR_FAIL_COND(r_result[k] >= 256);
- }
-
- for (k = 0; k < 3; k++) {
- r_result[k] += r_result[k] >> 5;
- }
-
- r_result[3] += r_result[3] >> 4;
-
- for (k = 0; k < 4; k++) {
- ERR_FAIL_COND(r_result[k] >= 256);
- }
-}
-
-static void get_modulation_value(int x, int y, const int p_2bit, const int p_modulation[8][16], const int p_modulation_modes[8][16], int *r_mod, int *p_dopt) {
- static const int rep_vals0[4] = { 0, 3, 5, 8 };
- static const int rep_vals1[4] = { 0, 4, 4, 8 };
-
- int mod_val;
-
- y = (y & 0x3) | ((~y & 0x2) << 1);
-
- if (p_2bit) {
- x = (x & 0x7) | ((~x & 0x4) << 1);
- } else {
- x = (x & 0x3) | ((~x & 0x2) << 1);
- }
-
- *p_dopt = 0;
-
- if (p_modulation_modes[y][x] == 0) {
- mod_val = rep_vals0[p_modulation[y][x]];
- } else if (p_2bit) {
- if (((x ^ y) & 1) == 0) {
- mod_val = rep_vals0[p_modulation[y][x]];
- } else if (p_modulation_modes[y][x] == 1) {
- mod_val = (rep_vals0[p_modulation[y - 1][x]] +
- rep_vals0[p_modulation[y + 1][x]] +
- rep_vals0[p_modulation[y][x - 1]] +
- rep_vals0[p_modulation[y][x + 1]] + 2) /
- 4;
- } else if (p_modulation_modes[y][x] == 2) {
- mod_val = (rep_vals0[p_modulation[y][x - 1]] +
- rep_vals0[p_modulation[y][x + 1]] + 1) /
- 2;
- } else {
- mod_val = (rep_vals0[p_modulation[y - 1][x]] +
- rep_vals0[p_modulation[y + 1][x]] + 1) /
- 2;
- }
- } else {
- mod_val = rep_vals1[p_modulation[y][x]];
-
- *p_dopt = p_modulation[y][x] == PT_INDEX;
- }
-
- *r_mod = mod_val;
-}
-
-static int disable_twiddling = 0;
-
-static uint32_t twiddle_uv(uint32_t p_height, uint32_t p_width, uint32_t p_y, uint32_t p_x) {
- uint32_t twiddled;
-
- uint32_t min_dimension;
- uint32_t max_value;
-
- uint32_t scr_bit_pos;
- uint32_t dst_bit_pos;
-
- int shift_count;
-
- ERR_FAIL_COND_V(p_y >= p_height, 0);
- ERR_FAIL_COND_V(p_x >= p_width, 0);
-
- ERR_FAIL_COND_V(!is_po2(p_height), 0);
- ERR_FAIL_COND_V(!is_po2(p_width), 0);
-
- if (p_height < p_width) {
- min_dimension = p_height;
- max_value = p_x;
- } else {
- min_dimension = p_width;
- max_value = p_y;
- }
-
- if (disable_twiddling) {
- return (p_y * p_width + p_x);
- }
-
- scr_bit_pos = 1;
- dst_bit_pos = 1;
- twiddled = 0;
- shift_count = 0;
-
- while (scr_bit_pos < min_dimension) {
- if (p_y & scr_bit_pos) {
- twiddled |= dst_bit_pos;
- }
-
- if (p_x & scr_bit_pos) {
- twiddled |= (dst_bit_pos << 1);
- }
-
- scr_bit_pos <<= 1;
- dst_bit_pos <<= 2;
- shift_count += 1;
- }
-
- max_value >>= shift_count;
-
- twiddled |= (max_value << (2 * shift_count));
-
- return twiddled;
-}
-
-static void decompress_pvrtc(PVRTCBlock *p_comp_img, const int p_2bit, const int p_width, const int p_height, const int p_tiled, unsigned char *p_dst) {
- int x, y;
- int i, j;
-
- int block_x, blk_y;
- int block_xp1, blk_yp1;
- int x_block_size;
- int block_width, block_height;
-
- int p_x, p_y;
-
- int p_modulation[8][16] = { { 0 } };
- int p_modulation_modes[8][16] = { { 0 } };
-
- int Mod, DoPT;
-
- unsigned int u_pos;
-
- // local neighbourhood of blocks
- PVRTCBlock *p_blocks[2][2];
-
- PVRTCBlock *prev[2][2] = { { nullptr, nullptr }, { nullptr, nullptr } };
-
- struct
- {
- int Reps[2][4];
- } colors5554[2][2];
-
- int ASig[4], BSig[4];
-
- int r_result[4];
-
- if (p_2bit) {
- x_block_size = BLK_X_2BPP;
- } else {
- x_block_size = BLK_X_4BPP;
- }
-
- block_width = MAX(2, p_width / x_block_size);
- block_height = MAX(2, p_height / BLK_Y_SIZE);
-
- for (y = 0; y < p_height; y++) {
- for (x = 0; x < p_width; x++) {
- block_x = (x - x_block_size / 2);
- blk_y = (y - BLK_Y_SIZE / 2);
-
- block_x = LIMIT_COORD(block_x, p_width, p_tiled);
- blk_y = LIMIT_COORD(blk_y, p_height, p_tiled);
-
- block_x /= x_block_size;
- blk_y /= BLK_Y_SIZE;
-
- block_xp1 = LIMIT_COORD(block_x + 1, block_width, p_tiled);
- blk_yp1 = LIMIT_COORD(blk_y + 1, block_height, p_tiled);
-
- p_blocks[0][0] = p_comp_img + twiddle_uv(block_height, block_width, blk_y, block_x);
- p_blocks[0][1] = p_comp_img + twiddle_uv(block_height, block_width, blk_y, block_xp1);
- p_blocks[1][0] = p_comp_img + twiddle_uv(block_height, block_width, blk_yp1, block_x);
- p_blocks[1][1] = p_comp_img + twiddle_uv(block_height, block_width, blk_yp1, block_xp1);
-
- if (memcmp(prev, p_blocks, 4 * sizeof(void *)) != 0) {
- p_y = 0;
- for (i = 0; i < 2; i++) {
- p_x = 0;
- for (j = 0; j < 2; j++) {
- unpack_5554(p_blocks[i][j], colors5554[i][j].Reps);
-
- unpack_modulations(
- p_blocks[i][j],
- p_2bit,
- p_modulation,
- p_modulation_modes,
- p_x, p_y);
-
- p_x += x_block_size;
- }
-
- p_y += BLK_Y_SIZE;
- }
-
- memcpy(prev, p_blocks, 4 * sizeof(void *));
- }
-
- interpolate_colors(
- colors5554[0][0].Reps[0],
- colors5554[0][1].Reps[0],
- colors5554[1][0].Reps[0],
- colors5554[1][1].Reps[0],
- p_2bit, x, y,
- ASig);
-
- interpolate_colors(
- colors5554[0][0].Reps[1],
- colors5554[0][1].Reps[1],
- colors5554[1][0].Reps[1],
- colors5554[1][1].Reps[1],
- p_2bit, x, y,
- BSig);
-
- get_modulation_value(x, y, p_2bit, (const int(*)[16])p_modulation, (const int(*)[16])p_modulation_modes,
- &Mod, &DoPT);
-
- for (i = 0; i < 4; i++) {
- r_result[i] = ASig[i] * 8 + Mod * (BSig[i] - ASig[i]);
- r_result[i] >>= 3;
- }
-
- if (DoPT) {
- r_result[3] = 0;
- }
-
- u_pos = (x + y * p_width) << 2;
- p_dst[u_pos + 0] = (uint8_t)r_result[0];
- p_dst[u_pos + 1] = (uint8_t)r_result[1];
- p_dst[u_pos + 2] = (uint8_t)r_result[2];
- p_dst[u_pos + 3] = (uint8_t)r_result[3];
- }
- }
-}
-
-static void _pvrtc_decompress(Image *p_img) {
- ERR_FAIL_COND(p_img->get_format() != Image::FORMAT_PVRTC1_2 && p_img->get_format() != Image::FORMAT_PVRTC1_2A && p_img->get_format() != Image::FORMAT_PVRTC1_4 && p_img->get_format() != Image::FORMAT_PVRTC1_4A);
-
- bool _2bit = (p_img->get_format() == Image::FORMAT_PVRTC1_2 || p_img->get_format() == Image::FORMAT_PVRTC1_2A);
-
- Vector<uint8_t> data = p_img->get_data();
- const uint8_t *r = data.ptr();
-
- Vector<uint8_t> newdata;
- newdata.resize(p_img->get_width() * p_img->get_height() * 4);
- uint8_t *w = newdata.ptrw();
-
- decompress_pvrtc((PVRTCBlock *)r, _2bit, p_img->get_width(), p_img->get_height(), 0, (unsigned char *)w);
-
- bool make_mipmaps = p_img->has_mipmaps();
- p_img->create(p_img->get_width(), p_img->get_height(), false, Image::FORMAT_RGBA8, newdata);
- if (make_mipmaps) {
- p_img->generate_mipmaps();
- }
-}
diff --git a/modules/svg/SCsub b/modules/svg/SCsub
index c7228a8d0b..bb03147731 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -9,16 +9,66 @@ env_svg = env_modules.Clone()
thirdparty_obj = []
-thirdparty_dir = "#thirdparty/nanosvg/"
+thirdparty_dir = "#thirdparty/thorvg/"
thirdparty_sources = [
- "nanosvg.cc",
+ "src/lib/tvgBezier.cpp",
+ "src/lib/tvgCanvas.cpp",
+ "src/lib/tvgFill.cpp",
+ "src/lib/tvgGlCanvas.cpp",
+ "src/lib/tvgInitializer.cpp",
+ "src/lib/tvgLinearGradient.cpp",
+ "src/lib/tvgLoader.cpp",
+ "src/lib/tvgLzw.cpp",
+ "src/lib/tvgPaint.cpp",
+ "src/lib/tvgPicture.cpp",
+ "src/lib/tvgRadialGradient.cpp",
+ "src/lib/tvgRender.cpp",
+ "src/lib/tvgSaver.cpp",
+ "src/lib/tvgScene.cpp",
+ "src/lib/tvgShape.cpp",
+ "src/lib/tvgSwCanvas.cpp",
+ "src/lib/tvgTaskScheduler.cpp",
+ "src/loaders/raw/tvgRawLoader.cpp",
+ "src/loaders/svg/tvgXmlParser.cpp",
+ "src/loaders/svg/tvgSvgUtil.cpp",
+ "src/loaders/svg/tvgSvgSceneBuilder.cpp",
+ "src/loaders/svg/tvgSvgPath.cpp",
+ "src/loaders/svg/tvgSvgLoader.cpp",
+ "src/loaders/tvg/tvgTvgBinInterpreter.cpp",
+ "src/loaders/tvg/tvgTvgLoader.cpp",
+ "src/loaders/jpg/tvgJpgLoader.cpp",
+ "src/loaders/jpg/tvgJpgd.cpp",
+ "src/loaders/external_png/tvgPngLoader.cpp",
+ "src/lib/sw_engine/tvgSwFill.cpp",
+ "src/lib/sw_engine/tvgSwImage.cpp",
+ "src/lib/sw_engine/tvgSwMath.cpp",
+ "src/lib/sw_engine/tvgSwMemPool.cpp",
+ "src/lib/sw_engine/tvgSwRaster.cpp",
+ "src/lib/sw_engine/tvgSwRenderer.cpp",
+ "src/lib/sw_engine/tvgSwRle.cpp",
+ "src/lib/sw_engine/tvgSwShape.cpp",
+ "src/lib/sw_engine/tvgSwStroke.cpp",
+ "src/savers/tvg/tvgTvgSaver.cpp",
]
+
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env_svg.Prepend(CPPPATH=[thirdparty_dir])
+env_svg.Prepend(CPPPATH=[thirdparty_dir + "inc"])
env_thirdparty = env_svg.Clone()
env_thirdparty.disable_warnings()
+env_thirdparty.Prepend(
+ CPPPATH=[
+ thirdparty_dir + "src/lib",
+ thirdparty_dir + "src/lib/sw_engine",
+ thirdparty_dir + "src/loaders/raw",
+ thirdparty_dir + "src/loaders/svg",
+ thirdparty_dir + "src/loaders/jpg",
+ thirdparty_dir + "src/loaders/png",
+ thirdparty_dir + "src/loaders/tvg",
+ thirdparty_dir + "src/savers/tvg",
+ ]
+)
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index bf43d7a4ad..96b83bf25a 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -30,132 +30,118 @@
#include "image_loader_svg.h"
-#include <nanosvg.h>
-#include <nanosvgrast.h>
-
-void SVGRasterizer::rasterize(NSVGimage *p_image, float p_tx, float p_ty, float p_scale, unsigned char *p_dst, int p_w, int p_h, int p_stride) {
- nsvgRasterize(rasterizer, p_image, p_tx, p_ty, p_scale, p_dst, p_w, p_h, p_stride);
-}
-
-SVGRasterizer::SVGRasterizer() {
- rasterizer = nsvgCreateRasterizer();
-}
-
-SVGRasterizer::~SVGRasterizer() {
- nsvgDeleteRasterizer(rasterizer);
-}
-
-SVGRasterizer ImageLoaderSVG::rasterizer;
-
-inline void change_nsvg_paint_color(NSVGpaint *p_paint, const uint32_t p_old, const uint32_t p_new) {
- if (p_paint->type == NSVG_PAINT_COLOR) {
- if (p_paint->color << 8 == p_old << 8) {
- p_paint->color = (p_paint->color & 0xFF000000) | (p_new & 0x00FFFFFF);
- }
- }
-
- if (p_paint->type == NSVG_PAINT_LINEAR_GRADIENT || p_paint->type == NSVG_PAINT_RADIAL_GRADIENT) {
- for (int stop_index = 0; stop_index < p_paint->gradient->nstops; stop_index++) {
- if (p_paint->gradient->stops[stop_index].color << 8 == p_old << 8) {
- p_paint->gradient->stops[stop_index].color = p_new;
+#include "core/os/memory.h"
+#include "core/variant/variant.h"
+
+#include <thorvg.h>
+
+void ImageLoaderSVG::_replace_color_property(const String &p_prefix, String &r_string) {
+ // Replace colors in the SVG based on what is configured in `replace_colors`.
+ // Used to change the colors of editor icons based on the used theme.
+ // The strings being replaced are typically of the form:
+ // fill="#5abbef"
+ // But can also be 3-letter codes, include alpha, be "none" or a named color
+ // string ("blue"). So we convert to Godot Color to compare with `replace_colors`.
+
+ const int prefix_len = p_prefix.length();
+ int pos = r_string.find(p_prefix);
+ while (pos != -1) {
+ pos += prefix_len; // Skip prefix.
+ int end_pos = r_string.find("\"", pos);
+ ERR_FAIL_COND_MSG(end_pos == -1, vformat("Malformed SVG string after property \"%s\".", p_prefix));
+ const String color_code = r_string.substr(pos, end_pos - pos);
+ if (color_code != "none" && !color_code.begins_with("url(")) {
+ const Color color = Color(color_code); // Handles both HTML codes and named colors.
+ if (replace_colors.has(color)) {
+ r_string = r_string.left(pos) + "#" + replace_colors[color].operator Color().to_html(false) + r_string.substr(end_pos);
}
}
+ // Search for other occurrences.
+ pos = r_string.find(p_prefix, pos);
}
}
-void ImageLoaderSVG::_convert_colors(NSVGimage *p_svg_image) {
- for (NSVGshape *shape = p_svg_image->shapes; shape != nullptr; shape = shape->next) {
- for (int i = 0; i < replace_colors.old_colors.size(); i++) {
- change_nsvg_paint_color(&(shape->stroke), replace_colors.old_colors[i], replace_colors.new_colors[i]);
- change_nsvg_paint_color(&(shape->fill), replace_colors.old_colors[i], replace_colors.new_colors[i]);
- }
- }
-}
+void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, bool p_convert_color) {
+ ERR_FAIL_COND(Math::is_zero_approx(p_scale));
-void ImageLoaderSVG::set_convert_colors(Dictionary *p_replace_color) {
- if (p_replace_color) {
- Dictionary replace_color = *p_replace_color;
- for (int i = 0; i < replace_color.keys().size(); i++) {
- Variant o_c = replace_color.keys()[i];
- Variant n_c = replace_color[replace_color.keys()[i]];
- if (o_c.get_type() == Variant::COLOR && n_c.get_type() == Variant::COLOR) {
- Color old_color = o_c;
- Color new_color = n_c;
- replace_colors.old_colors.push_back(old_color.to_abgr32());
- replace_colors.new_colors.push_back(new_color.to_abgr32());
- }
- }
- } else {
- replace_colors.old_colors.clear();
- replace_colors.new_colors.clear();
+ if (p_convert_color) {
+ _replace_color_property("stop-color=\"", p_string);
+ _replace_color_property("fill=\"", p_string);
+ _replace_color_property("stroke=\"", p_string);
}
-}
-Error ImageLoaderSVG::_create_image(Ref<Image> p_image, const Vector<uint8_t> *p_data, float p_scale, bool upsample, bool convert_colors) {
- NSVGimage *svg_image;
- const uint8_t *src_r = p_data->ptr();
- svg_image = nsvgParse((char *)src_r, "px", 96);
- if (svg_image == nullptr) {
- ERR_PRINT("SVG Corrupted");
- return ERR_FILE_CORRUPT;
- }
+ std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
+ PackedByteArray bytes = p_string.to_utf8_buffer();
- if (convert_colors) {
- _convert_colors(svg_image);
+ tvg::Result result = picture->load((const char *)bytes.ptr(), bytes.size(), "svg", true);
+ if (result != tvg::Result::Success) {
+ return;
}
+ float fw, fh;
+ picture->viewbox(nullptr, nullptr, &fw, &fh);
- const float upscale = upsample ? 2.0 : 1.0;
-
- const int w = (int)(svg_image->width * p_scale * upscale);
- ERR_FAIL_COND_V_MSG(w > Image::MAX_WIDTH, ERR_PARAMETER_RANGE_ERROR, vformat("Can't create image from SVG with scale %s, the resulting image size exceeds max width.", rtos(p_scale)));
-
- const int h = (int)(svg_image->height * p_scale * upscale);
- ERR_FAIL_COND_V_MSG(h > Image::MAX_HEIGHT, ERR_PARAMETER_RANGE_ERROR, vformat("Can't create image from SVG with scale %s, the resulting image size exceeds max height.", rtos(p_scale)));
-
- Vector<uint8_t> dst_image;
- dst_image.resize(w * h * 4);
+ uint32_t width = MIN(fw * p_scale, 16 * 1024);
+ uint32_t height = MIN(fh * p_scale, 16 * 1024);
+ picture->size(width, height);
- uint8_t *dw = dst_image.ptrw();
+ std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
+ // Note: memalloc here, be sure to memfree before any return.
+ uint32_t *buffer = (uint32_t *)memalloc(sizeof(uint32_t) * width * height);
- rasterizer.rasterize(svg_image, 0, 0, p_scale * upscale, (unsigned char *)dw, w, h, w * 4);
-
- p_image->create(w, h, false, Image::FORMAT_RGBA8, dst_image);
- if (upsample) {
- p_image->shrink_x2();
+ tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888_STRAIGHT);
+ if (res != tvg::Result::Success) {
+ memfree(buffer);
+ ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
}
- nsvgDelete(svg_image);
+ res = sw_canvas->push(move(picture));
+ if (res != tvg::Result::Success) {
+ memfree(buffer);
+ ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
+ }
- return OK;
-}
+ res = sw_canvas->draw();
+ if (res != tvg::Result::Success) {
+ memfree(buffer);
+ ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
+ }
-Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample, bool convert_colors) {
- size_t str_len = strlen(p_svg_str);
- Vector<uint8_t> src_data;
- src_data.resize(str_len + 1);
- uint8_t *src_w = src_data.ptrw();
- memcpy(src_w, p_svg_str, str_len + 1);
+ res = sw_canvas->sync();
+ if (res != tvg::Result::Success) {
+ memfree(buffer);
+ ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
+ }
- return _create_image(p_image, &src_data, p_scale, upsample, convert_colors);
-}
+ Vector<uint8_t> image;
+ image.resize(width * height * sizeof(uint32_t));
+
+ for (uint32_t y = 0; y < height; y++) {
+ for (uint32_t x = 0; x < width; x++) {
+ uint32_t n = buffer[y * width + x];
+ const size_t offset = sizeof(uint32_t) * width * y + sizeof(uint32_t) * x;
+ image.write[offset + 0] = (n >> 16) & 0xff;
+ image.write[offset + 1] = (n >> 8) & 0xff;
+ image.write[offset + 2] = n & 0xff;
+ image.write[offset + 3] = (n >> 24) & 0xff;
+ }
+ }
-Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
- uint64_t size = f->get_length();
- Vector<uint8_t> src_image;
- src_image.resize(size + 1);
- uint8_t *src_w = src_image.ptrw();
- f->get_buffer(src_w, size);
- src_w[size] = '\0';
+ res = sw_canvas->clear(true);
+ memfree(buffer);
- return _create_image(p_image, &src_image, p_scale, 1.0);
+ p_image->create(width, height, false, Image::FORMAT_RGBA8, image);
}
void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("svg");
- p_extensions->push_back("svgz");
}
-ImageLoaderSVG::ImageLoaderSVG() {
+Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) {
+ String svg = p_fileaccess->get_as_utf8_string();
+ create_image_from_string(p_image, svg, p_scale, false, false);
+ ERR_FAIL_COND_V(p_image->is_empty(), FAILED);
+ if (p_force_linear) {
+ p_image->srgb_to_linear();
+ }
+ return OK;
}
-
-ImageLoaderSVG::ReplaceColors ImageLoaderSVG::replace_colors;
diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h
index 03307c319e..d0bd71d92d 100644
--- a/modules/svg/image_loader_svg.h
+++ b/modules/svg/image_loader_svg.h
@@ -32,38 +32,18 @@
#define IMAGE_LOADER_SVG_H
#include "core/io/image_loader.h"
-#include "core/string/ustring.h"
-
-// Forward declare and include thirdparty headers in .cpp.
-struct NSVGrasterizer;
-struct NSVGimage;
-
-class SVGRasterizer {
- NSVGrasterizer *rasterizer;
-
-public:
- void rasterize(NSVGimage *p_image, float p_tx, float p_ty, float p_scale, unsigned char *p_dst, int p_w, int p_h, int p_stride);
-
- SVGRasterizer();
- ~SVGRasterizer();
-};
class ImageLoaderSVG : public ImageFormatLoader {
- static struct ReplaceColors {
- List<uint32_t> old_colors;
- List<uint32_t> new_colors;
- } replace_colors;
- static SVGRasterizer rasterizer;
- static void _convert_colors(NSVGimage *p_svg_image);
- static Error _create_image(Ref<Image> p_image, const Vector<uint8_t> *p_data, float p_scale, bool upsample, bool convert_colors = false);
+ Dictionary replace_colors;
+ void _replace_color_property(const String &p_prefix, String &r_string);
public:
- static void set_convert_colors(Dictionary *p_replace_color = nullptr);
- static Error create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample, bool convert_colors = false);
+ // Called by the editor to handle theme icon colors.
+ void set_replace_colors(Dictionary p_replace_colors) { replace_colors = p_replace_colors; }
+ void create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, bool p_convert_color);
- virtual Error load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale);
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- ImageLoaderSVG();
+ virtual Error load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) override;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const override;
};
#endif // IMAGE_LOADER_SVG_H
diff --git a/modules/svg/register_types.cpp b/modules/svg/register_types.cpp
index 1560af95c3..a4341c6f1e 100644
--- a/modules/svg/register_types.cpp
+++ b/modules/svg/register_types.cpp
@@ -32,13 +32,23 @@
#include "image_loader_svg.h"
+#include <thorvg.h>
+
static ImageLoaderSVG *image_loader_svg = nullptr;
void register_svg_types() {
+ tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
+ if (tvg::Initializer::init(tvgEngine, 0) != tvg::Result::Success) {
+ return;
+ }
image_loader_svg = memnew(ImageLoaderSVG);
ImageLoader::add_image_format_loader(image_loader_svg);
}
void unregister_svg_types() {
+ if (!image_loader_svg) {
+ return;
+ }
memdelete(image_loader_svg);
+ tvg::Initializer::term(tvg::CanvasEngine::Sw);
}
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index d6a96282f3..6a06619840 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -62,7 +62,6 @@ if env["builtin_harfbuzz"]:
#'src/hb-gobject-structs.cc',
"src/hb-icu.cc",
"src/hb-map.cc",
- "src/hb-ms-feature-ranges.cc",
"src/hb-number.cc",
"src/hb-ot-cff1-table.cc",
"src/hb-ot-cff2-table.cc",
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 6002dc80da..e50a5337cb 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -322,7 +322,7 @@ _FORCE_INLINE_ bool is_underscore(char32_t p_char) {
/*************************************************************************/
String TextServerAdvanced::interface_name = "ICU / HarfBuzz / Graphite";
-uint32_t TextServerAdvanced::interface_features = FEATURE_BIDI_LAYOUT | FEATURE_VERTICAL_LAYOUT | FEATURE_SHAPING | FEATURE_KASHIDA_JUSTIFICATION | FEATURE_BREAK_ITERATORS | FEATURE_USE_SUPPORT_DATA | FEATURE_FONT_VARIABLE;
+uint32_t TextServerAdvanced::interface_features = FEATURE_BIDI_LAYOUT | FEATURE_VERTICAL_LAYOUT | FEATURE_SHAPING | FEATURE_KASHIDA_JUSTIFICATION | FEATURE_BREAK_ITERATORS | FEATURE_USE_SUPPORT_DATA | FEATURE_FONT_VARIABLE | FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION;
bool TextServerAdvanced::has_feature(Feature p_feature) const {
return (interface_features & p_feature) == p_feature;
@@ -2901,7 +2901,7 @@ void TextServerAdvanced::font_set_global_oversampling(float p_oversampling) {
List<RID> text_bufs;
shaped_owner.get_owned_list(&text_bufs);
for (const RID &E : text_bufs) {
- invalidate(shaped_owner.get_or_null(E));
+ invalidate(shaped_owner.get_or_null(E), false);
}
}
}
@@ -2936,7 +2936,7 @@ int TextServerAdvanced::_convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int
return limit;
}
-void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped) {
+void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped, bool p_text) {
p_shaped->valid = false;
p_shaped->sort_valid = false;
p_shaped->line_breaks_valid = false;
@@ -2951,27 +2951,32 @@ void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *
p_shaped->glyphs_logical.clear();
p_shaped->overrun_trim_data = TrimData();
p_shaped->utf16 = Char16String();
- if (p_shaped->script_iter != nullptr) {
- memdelete(p_shaped->script_iter);
- p_shaped->script_iter = nullptr;
- }
for (int i = 0; i < p_shaped->bidi_iter.size(); i++) {
ubidi_close(p_shaped->bidi_iter[i]);
}
p_shaped->bidi_iter.clear();
+
+ if (p_text) {
+ if (p_shaped->script_iter != nullptr) {
+ memdelete(p_shaped->script_iter);
+ p_shaped->script_iter = nullptr;
+ }
+ p_shaped->break_ops_valid = false;
+ p_shaped->js_ops_valid = false;
+ }
}
void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
ShapedTextDataAdvanced *parent = shaped_owner.get_or_null(p_shaped->parent);
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) {
+ for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : parent->objects) {
if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) {
p_shaped->objects[E.key] = E.value;
}
}
- for (int k = 0; k < parent->spans.size(); k++) {
- ShapedTextDataAdvanced::Span span = parent->spans[k];
+ for (int i = 0; i < parent->spans.size(); i++) {
+ ShapedTextDataAdvanced::Span span = parent->spans[i];
if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
continue;
}
@@ -3004,7 +3009,7 @@ void TextServerAdvanced::shaped_text_clear(RID p_shaped) {
sd->spans.clear();
sd->objects.clear();
sd->bidi_override.clear();
- invalidate(sd);
+ invalidate(sd, true);
}
void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
@@ -3017,7 +3022,7 @@ void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Dir
full_copy(sd);
}
sd->direction = p_direction;
- invalidate(sd);
+ invalidate(sd, false);
}
}
@@ -3047,7 +3052,7 @@ void TextServerAdvanced::shaped_text_set_custom_punctuation(RID p_shaped, const
full_copy(sd);
}
sd->custom_punct = p_punct;
- invalidate(sd);
+ invalidate(sd, false);
}
}
@@ -3070,7 +3075,7 @@ void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Array
for (int i = 0; i < p_override.size(); i++) {
sd->bidi_override.push_back(p_override[i]);
}
- invalidate(sd);
+ invalidate(sd, false);
}
void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
@@ -3083,7 +3088,7 @@ void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::O
full_copy(sd);
}
sd->orientation = p_orientation;
- invalidate(sd);
+ invalidate(sd, false);
}
}
@@ -3095,7 +3100,7 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
ERR_FAIL_COND(sd->parent != RID());
if (sd->preserve_invalid != p_enabled) {
sd->preserve_invalid = p_enabled;
- invalidate(sd);
+ invalidate(sd, false);
}
}
@@ -3117,7 +3122,7 @@ void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_e
full_copy(sd);
}
sd->preserve_control = p_enabled;
- invalidate(sd);
+ invalidate(sd, false);
}
}
@@ -3137,7 +3142,41 @@ TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_sh
return sd->orientation;
}
-bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+int TextServerAdvanced::shaped_get_span_count(RID p_shaped) const {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0);
+ return sd->spans.size();
+}
+
+Variant TextServerAdvanced::shaped_get_span_meta(RID p_shaped, int p_index) const {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, Variant());
+ ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
+ return sd->spans[p_index].meta;
+}
+
+void TextServerAdvanced::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+ ERR_FAIL_INDEX(p_index, sd->spans.size());
+
+ ShapedTextDataAdvanced::Span &span = sd->spans.write[p_index];
+ bool changed = (span.font_size != p_size) || (span.features != p_opentype_features) || (p_fonts.size() != span.fonts.size());
+ if (!changed) {
+ for (int i = 0; i < p_fonts.size(); i++) {
+ changed = changed || (span.fonts[i] != p_fonts[i]);
+ }
+ }
+ if (changed) {
+ span.fonts = p_fonts;
+ span.font_size = p_size;
+ span.features = p_opentype_features;
+
+ invalidate(sd, false);
+ }
+}
+
+bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
ERR_FAIL_COND_V(p_size <= 0, false);
@@ -3162,11 +3201,12 @@ bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_te
span.font_size = p_size;
span.language = p_language;
span.features = p_opentype_features;
+ span.meta = p_meta;
sd->spans.push_back(span);
sd->text += p_text;
sd->end += p_text.length();
- invalidate(sd);
+ invalidate(sd, true);
return true;
}
@@ -3196,13 +3236,13 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con
sd->text += String::chr(0xfffc).repeat(p_length);
sd->end += p_length;
sd->objects[p_key] = obj;
- invalidate(sd);
+ invalidate(sd, true);
return true;
}
bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3222,7 +3262,7 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
Glyph gl = sd->glyphs[i];
Variant key;
if (gl.count == 1) {
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) {
if (E.value.pos == gl.start) {
key = E.key;
break;
@@ -3262,75 +3302,78 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
sd->width += gl.advance * gl.repeat;
}
}
+ _realign(sd);
+ }
+ return true;
+}
- // Align embedded objects to baseline.
- float full_ascent = sd->ascent;
- float full_descent = sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
- if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) {
- if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.y = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.y = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.y = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.y -= E.value.rect.size.y;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.y -= E.value.rect.size.y / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.y);
- full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
- } else {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.x = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.x = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.x = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.x -= E.value.rect.size.x;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.x -= E.value.rect.size.x / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.x);
- full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
+void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const {
+ // Align embedded objects to baseline.
+ float full_ascent = p_sd->ascent;
+ float full_descent = p_sd->descent;
+ for (KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) {
+ if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) {
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.y = -p_sd->ascent;
+ } break;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.y = 0;
+ } break;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.y = p_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
+ } break;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
+ } break;
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
+ } break;
+ }
+ full_ascent = MAX(full_ascent, -E.value.rect.position.y);
+ full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
+ } else {
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.x = -p_sd->ascent;
+ } break;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.x = 0;
+ } break;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.x = p_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
+ } break;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
+ } break;
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
+ } break;
}
+ full_ascent = MAX(full_ascent, -E.value.rect.position.x);
+ full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
- sd->ascent = full_ascent;
- sd->descent = full_descent;
}
- return true;
+ p_sd->ascent = full_ascent;
+ p_sd->descent = full_descent;
}
RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
@@ -3423,7 +3466,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) {
if (E.value.pos == gl.start) {
find_embedded = true;
key = E.key;
@@ -3466,72 +3509,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
}
}
- // Align embedded objects to baseline.
- float full_ascent = p_new_sd->ascent;
- float full_descent = p_new_sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_new_sd->objects) {
- if ((E.value.pos >= p_new_sd->start) && (E.value.pos < p_new_sd->end)) {
- if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.y = -p_new_sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.y = (-p_new_sd->ascent + p_new_sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.y = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.y = p_new_sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.y -= E.value.rect.size.y;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.y -= E.value.rect.size.y / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.y);
- full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
- } else {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.x = -p_new_sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.x = (-p_new_sd->ascent + p_new_sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.x = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.x = p_new_sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.x -= E.value.rect.size.x;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.x -= E.value.rect.size.x / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.x);
- full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
- }
- }
- }
- p_new_sd->ascent = full_ascent;
- p_new_sd->descent = full_descent;
+ _realign(p_new_sd);
}
p_new_sd->valid = true;
@@ -3968,40 +3946,43 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
const UChar *data = sd->utf16.ptr();
- HashMap<int, bool> breaks;
- UErrorCode err = U_ZERO_ERROR;
- int i = 0;
- while (i < sd->spans.size()) {
- String language = sd->spans[i].language;
- int r_start = sd->spans[i].start;
- while (i + 1 < sd->spans.size() && language == sd->spans[i + 1].language) {
- i++;
- }
- int r_end = sd->spans[i].end;
- UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
- if (U_FAILURE(err)) {
- // No data loaded - use fallback.
- for (int j = r_start; j < r_end; j++) {
- char32_t c = sd->text[j - sd->start];
- if (is_whitespace(c)) {
- breaks[j + 1] = false;
- }
- if (is_linebreak(c)) {
- breaks[j + 1] = true;
- }
+ if (!sd->break_ops_valid) {
+ sd->breaks.clear();
+ UErrorCode err = U_ZERO_ERROR;
+ int i = 0;
+ while (i < sd->spans.size()) {
+ String language = sd->spans[i].language;
+ int r_start = sd->spans[i].start;
+ while (i + 1 < sd->spans.size() && language == sd->spans[i + 1].language) {
+ i++;
}
- } else {
- while (ubrk_next(bi) != UBRK_DONE) {
- int pos = _convert_pos(sd, ubrk_current(bi)) + r_start;
- if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
- breaks[pos] = true;
- } else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
- breaks[pos] = false;
+ int r_end = sd->spans[i].end;
+ UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
+ if (U_FAILURE(err)) {
+ // No data loaded - use fallback.
+ for (int j = r_start; j < r_end; j++) {
+ char32_t c = sd->text[j - sd->start];
+ if (is_whitespace(c)) {
+ sd->breaks[j + 1] = false;
+ }
+ if (is_linebreak(c)) {
+ sd->breaks[j + 1] = true;
+ }
+ }
+ } else {
+ while (ubrk_next(bi) != UBRK_DONE) {
+ int pos = _convert_pos(sd, ubrk_current(bi)) + r_start;
+ if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
+ sd->breaks[pos] = true;
+ } else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
+ sd->breaks[pos] = false;
+ }
}
}
+ ubrk_close(bi);
+ i++;
}
- ubrk_close(bi);
- i++;
+ sd->break_ops_valid = true;
}
sd->sort_valid = false;
@@ -4013,7 +3994,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
int c_punct_size = sd->custom_punct.length();
const char32_t *c_punct = sd->custom_punct.ptr();
- for (i = 0; i < sd_size; i++) {
+ for (int i = 0; i < sd_size; i++) {
if (sd_glyphs[i].count > 0) {
char32_t c = ch[sd_glyphs[i].start - sd->start];
if (c == 0xfffc) {
@@ -4040,8 +4021,8 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
if (is_underscore(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
}
- if (breaks.has(sd_glyphs[i].end)) {
- if (breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
+ if (sd->breaks.has(sd_glyphs[i].end)) {
+ if (sd->breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
} else if (is_whitespace(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
@@ -4186,41 +4167,45 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
const UChar *data = sd->utf16.ptr();
int32_t data_size = sd->utf16.length();
- Map<int, bool> jstops;
+ if (!sd->js_ops_valid) {
+ sd->jstops.clear();
- // Use ICU word iterator and custom kashida detection.
- UErrorCode err = U_ZERO_ERROR;
- UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err);
- if (U_FAILURE(err)) {
- // No data - use fallback.
- int limit = 0;
- for (int i = 0; i < sd->text.length(); i++) {
- if (is_whitespace(data[i])) {
- int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start;
- if (ks != -1) {
- jstops[ks] = true;
- }
- limit = i + 1;
+ // Use ICU word iterator and custom kashida detection.
+ UErrorCode err = U_ZERO_ERROR;
+ UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err);
+ if (U_FAILURE(err)) {
+ // No data - use fallback.
+ int limit = 0;
+ for (int i = 0; i < sd->text.length(); i++) {
+ if (is_whitespace(data[i])) {
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start;
+ if (ks != -1) {
+ sd->jstops[ks] = true;
+ }
+ limit = i + 1;
+ }
}
- }
- int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start;
- if (ks != -1) {
- jstops[ks] = true;
- }
- } else {
- int limit = 0;
- while (ubrk_next(bi) != UBRK_DONE) {
- if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) {
- int i = _convert_pos(sd, ubrk_current(bi));
- jstops[i + sd->start] = false;
- int ks = _generate_kashida_justification_opportunies(sd->text, limit, i);
- if (ks != -1) {
- jstops[ks + sd->start] = true;
- }
- limit = i;
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start;
+ if (ks != -1) {
+ sd->jstops[ks] = true;
+ }
+ } else {
+ int limit = 0;
+ while (ubrk_next(bi) != UBRK_DONE) {
+ if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) {
+ int i = _convert_pos(sd, ubrk_current(bi));
+ sd->jstops[i + sd->start] = false;
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, i);
+ if (ks != -1) {
+ sd->jstops[ks + sd->start] = true;
+ }
+ limit = i;
+ }
}
+ ubrk_close(bi);
}
- ubrk_close(bi);
+
+ sd->js_ops_valid = true;
}
sd->sort_valid = false;
@@ -4228,18 +4213,18 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
Glyph *sd_glyphs = sd->glyphs.ptrw();
int sd_size = sd->glyphs.size();
- if (jstops.size() > 0) {
+ if (sd->jstops.size() > 0) {
for (int i = 0; i < sd_size; i++) {
if (sd_glyphs[i].count > 0) {
char32_t c = sd->text[sd_glyphs[i].start - sd->start];
if (c == 0x0640) {
sd_glyphs[i].flags |= GRAPHEME_IS_ELONGATION;
}
- if (jstops.has(sd_glyphs[i].start)) {
+ if (sd->jstops.has(sd_glyphs[i].start)) {
if (c == 0xfffc) {
continue;
}
- if (jstops[sd_glyphs[i].start]) {
+ if (sd->jstops[sd_glyphs[i].start]) {
if (c != 0x0640) {
if (sd_glyphs[i].font_rid != RID()) {
Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size);
@@ -4574,7 +4559,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
return true;
}
- invalidate(sd);
+ invalidate(sd, false);
if (sd->parent != RID()) {
shaped_text_shape(sd->parent);
ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent);
@@ -4733,70 +4718,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
}
}
- // Align embedded objects to baseline.
- float full_ascent = sd->ascent;
- float full_descent = sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
- if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.y = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.y = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.y = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.y -= E.value.rect.size.y;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.y -= E.value.rect.size.y / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.y);
- full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
- } else {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.x = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.x = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.x = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.x -= E.value.rect.size.x;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.x -= E.value.rect.size.x / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.x);
- full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
- }
- }
- sd->ascent = full_ascent;
- sd->descent = full_descent;
+ _realign(sd);
sd->valid = true;
return sd->valid;
}
@@ -4863,7 +4785,7 @@ Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const {
ERR_FAIL_COND_V(!sd, ret);
MutexLock lock(sd->mutex);
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) {
ret.push_back(E.key);
}
@@ -5229,6 +5151,40 @@ String TextServerAdvanced::strip_diacritics(const String &p_string) const {
return result;
}
+String TextServerAdvanced::string_to_upper(const String &p_string, const String &p_language) const {
+ // Convert to UTF-16.
+ Char16String utf16 = p_string.utf16();
+
+ Char16String upper;
+ UErrorCode err = U_ZERO_ERROR;
+ int32_t len = u_strToUpper(nullptr, 0, utf16.ptr(), -1, p_language.ascii().get_data(), &err);
+ ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, p_string, u_errorName(err));
+ upper.resize(len);
+ err = U_ZERO_ERROR;
+ u_strToUpper(upper.ptrw(), len, utf16.ptr(), -1, p_language.ascii().get_data(), &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), p_string, u_errorName(err));
+
+ // Convert back to UTF-32.
+ return String::utf16(upper.ptr(), len);
+}
+
+String TextServerAdvanced::string_to_lower(const String &p_string, const String &p_language) const {
+ // Convert to UTF-16.
+ Char16String utf16 = p_string.utf16();
+
+ Char16String lower;
+ UErrorCode err = U_ZERO_ERROR;
+ int32_t len = u_strToLower(nullptr, 0, utf16.ptr(), -1, p_language.ascii().get_data(), &err);
+ ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, p_string, u_errorName(err));
+ lower.resize(len);
+ err = U_ZERO_ERROR;
+ u_strToLower(lower.ptrw(), len, utf16.ptr(), -1, p_language.ascii().get_data(), &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), p_string, u_errorName(err));
+
+ // Convert back to UTF-32.
+ return String::utf16(lower.ptr(), len);
+}
+
TextServerAdvanced::TextServerAdvanced() {
_insert_num_systems_lang();
_insert_feature_sets();
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index d088219d91..145d740b68 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -242,6 +242,21 @@ class TextServerAdvanced : public TextServer {
// Shaped text cache data.
struct ShapedTextDataAdvanced : public ShapedTextData {
+ struct Span {
+ int start = -1;
+ int end = -1;
+
+ Vector<RID> fonts;
+ int font_size = 0;
+
+ Variant embedded_key;
+
+ String language;
+ Dictionary features;
+ Variant meta;
+ };
+ Vector<Span> spans;
+
/* Intermediate data */
Char16String utf16;
Vector<UBiDi *> bidi_iter;
@@ -249,6 +264,11 @@ class TextServerAdvanced : public TextServer {
ScriptIterator *script_iter = nullptr;
hb_buffer_t *hb_buffer = nullptr;
+ HashMap<int, bool> jstops;
+ HashMap<int, bool> breaks;
+ bool break_ops_valid = false;
+ bool js_ops_valid = false;
+
~ShapedTextDataAdvanced() {
for (int i = 0; i < bidi_iter.size(); i++) {
ubidi_close(bidi_iter[i]);
@@ -268,6 +288,7 @@ class TextServerAdvanced : public TextServer {
mutable RID_PtrOwner<FontDataAdvanced> font_owner;
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
+ void _realign(ShapedTextDataAdvanced *p_sd) const;
int _convert_pos(const ShapedTextDataAdvanced *p_sd, int p_pos) const;
int _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int p_pos) const;
bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int p_start, int p_length) const;
@@ -302,7 +323,7 @@ protected:
static void _bind_methods(){};
void full_copy(ShapedTextDataAdvanced *p_shaped);
- void invalidate(ShapedTextDataAdvanced *p_shaped);
+ void invalidate(ShapedTextDataAdvanced *p_shaped, bool p_text = false);
public:
virtual bool has_feature(Feature p_feature) const override;
@@ -482,10 +503,14 @@ public:
virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
- virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
+ virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override;
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
+ virtual int shaped_get_span_count(RID p_shaped) const override;
+ virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override;
+ virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override;
+
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
@@ -527,6 +552,9 @@ public:
virtual String strip_diacritics(const String &p_string) const override;
+ virtual String string_to_upper(const String &p_string, const String &p_language = "") const override;
+ virtual String string_to_lower(const String &p_string, const String &p_language = "") const override;
+
TextServerAdvanced();
~TextServerAdvanced();
};
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index c7a7c4aa70..dd520a2e40 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -32,6 +32,7 @@
#include "core/error/error_macros.h"
#include "core/string/print_string.h"
+#include "core/string/ucaps.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
@@ -90,7 +91,7 @@ void TextServerFallback::free(RID p_rid) {
font_owner.free(p_rid);
memdelete(fd);
} else if (shaped_owner.owns(p_rid)) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_rid);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_rid);
shaped_owner.free(p_rid);
memdelete(sd);
}
@@ -126,8 +127,9 @@ _FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) {
char tag[4];
uint32_t i;
- if (!p_str || !p_len || !*p_str)
+ if (!p_str || !p_len || !*p_str) {
return OT_TAG(0, 0, 0, 0);
+ }
if (p_len < 0 || p_len > 4) {
p_len = 4;
@@ -2059,7 +2061,7 @@ void TextServerFallback::font_set_global_oversampling(float p_oversampling) {
/* Shaped text buffer interface */
/*************************************************************************/
-void TextServerFallback::invalidate(ShapedTextData *p_shaped) {
+void TextServerFallback::invalidate(ShapedTextDataFallback *p_shaped) {
p_shaped->valid = false;
p_shaped->sort_valid = false;
p_shaped->line_breaks_valid = false;
@@ -2073,17 +2075,17 @@ void TextServerFallback::invalidate(ShapedTextData *p_shaped) {
p_shaped->glyphs_logical.clear();
}
-void TextServerFallback::full_copy(ShapedTextData *p_shaped) {
- ShapedTextData *parent = shaped_owner.get_or_null(p_shaped->parent);
+void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
+ ShapedTextDataFallback *parent = shaped_owner.get_or_null(p_shaped->parent);
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) {
+ for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : parent->objects) {
if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) {
p_shaped->objects[E.key] = E.value;
}
}
for (int k = 0; k < parent->spans.size(); k++) {
- ShapedTextData::Span span = parent->spans[k];
+ ShapedTextDataFallback::Span span = parent->spans[k];
if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
continue;
}
@@ -2097,7 +2099,7 @@ void TextServerFallback::full_copy(ShapedTextData *p_shaped) {
RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
- ShapedTextData *sd = memnew(ShapedTextData);
+ ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
sd->direction = p_direction;
sd->orientation = p_orientation;
@@ -2105,7 +2107,7 @@ RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, Te
}
void TextServerFallback::shaped_text_clear(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2134,7 +2136,7 @@ TextServer::Direction TextServerFallback::shaped_text_get_inferred_direction(RID
void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
_THREAD_SAFE_METHOD_
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
if (sd->custom_punct != p_punct) {
@@ -2148,13 +2150,13 @@ void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const
String TextServerFallback::shaped_text_get_custom_punctuation(RID p_shaped) const {
_THREAD_SAFE_METHOD_
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, String());
return sd->custom_punct;
}
void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2172,7 +2174,7 @@ void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Array
}
TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
MutexLock lock(sd->mutex);
@@ -2180,7 +2182,7 @@ TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_sh
}
void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
MutexLock lock(sd->mutex);
ERR_FAIL_COND(!sd);
@@ -2194,7 +2196,7 @@ void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
}
bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2202,7 +2204,7 @@ bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
}
void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2216,15 +2218,52 @@ void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_e
}
bool TextServerFallback::shaped_text_get_preserve_control(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
return sd->preserve_control;
}
-bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+int TextServerFallback::shaped_get_span_count(RID p_shaped) const {
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0);
+ return sd->spans.size();
+}
+
+Variant TextServerFallback::shaped_get_span_meta(RID p_shaped, int p_index) const {
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, Variant());
+ ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
+ return sd->spans[p_index].meta;
+}
+
+void TextServerFallback::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) {
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+ ERR_FAIL_INDEX(p_index, sd->spans.size());
+
+ ShapedTextDataFallback::Span &span = sd->spans.write[p_index];
+ span.fonts.clear();
+ // Pre-sort fonts, push fonts with the language support first.
+ Vector<RID> fonts_no_match;
+ int font_count = p_fonts.size();
+ for (int i = 0; i < font_count; i++) {
+ if (font_is_language_supported(p_fonts[i], span.language)) {
+ span.fonts.push_back(p_fonts[i]);
+ } else {
+ fonts_no_match.push_back(p_fonts[i]);
+ }
+ }
+ span.fonts.append_array(fonts_no_match);
+ span.font_size = p_size;
+ span.features = p_opentype_features;
+
+ sd->valid = false;
+}
+
+bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2242,7 +2281,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
full_copy(sd);
}
- ShapedTextData::Span span;
+ ShapedTextDataFallback::Span span;
span.start = sd->text.length();
span.end = span.start + p_text.length();
@@ -2261,6 +2300,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
ERR_FAIL_COND_V(span.fonts.is_empty(), false);
span.font_size = p_size;
span.language = p_language;
+ span.meta = p_meta;
sd->spans.push_back(span);
sd->text += p_text;
@@ -2271,7 +2311,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
}
bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2282,12 +2322,12 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con
full_copy(sd);
}
- ShapedTextData::Span span;
+ ShapedTextDataFallback::Span span;
span.start = sd->start + sd->text.length();
span.end = span.start + p_length;
span.embedded_key = p_key;
- ShapedTextData::EmbeddedObject obj;
+ ShapedTextDataFallback::EmbeddedObject obj;
obj.inline_align = p_inline_align;
obj.rect.size = p_size;
obj.pos = span.start;
@@ -2302,7 +2342,7 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con
}
bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2322,7 +2362,7 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
Glyph gl = sd->glyphs[i];
Variant key;
if (gl.count == 1) {
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
if (E.value.pos == gl.start) {
key = E.key;
break;
@@ -2362,79 +2402,82 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
sd->width += gl.advance * gl.repeat;
}
}
+ _realign(sd);
+ }
+ return true;
+}
- // Align embedded objects to baseline.
- float full_ascent = sd->ascent;
- float full_descent = sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
- if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) {
- if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.y = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.y = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.y = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.y -= E.value.rect.size.y;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.y -= E.value.rect.size.y / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.y);
- full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
- } else {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.x = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.x = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.x = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.x -= E.value.rect.size.x;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.x -= E.value.rect.size.x / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.x);
- full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
+void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {
+ // Align embedded objects to baseline.
+ float full_ascent = p_sd->ascent;
+ float full_descent = p_sd->descent;
+ for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : p_sd->objects) {
+ if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) {
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.y = -p_sd->ascent;
+ } break;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.y = 0;
+ } break;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.y = p_sd->descent;
+ } break;
}
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
+ } break;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
+ } break;
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
+ } break;
+ }
+ full_ascent = MAX(full_ascent, -E.value.rect.position.y);
+ full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
+ } else {
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.x = -p_sd->ascent;
+ } break;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.x = 0;
+ } break;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.x = p_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
+ } break;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
+ } break;
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
+ } break;
+ }
+ full_ascent = MAX(full_ascent, -E.value.rect.position.x);
+ full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
- sd->ascent = full_ascent;
- sd->descent = full_descent;
}
- return true;
+ p_sd->ascent = full_ascent;
+ p_sd->descent = full_descent;
}
RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
MutexLock lock(sd->mutex);
@@ -2448,7 +2491,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());
ERR_FAIL_COND_V(sd->end < p_start + p_length, RID());
- ShapedTextData *new_sd = memnew(ShapedTextData);
+ ShapedTextDataFallback *new_sd = memnew(ShapedTextDataFallback);
new_sd->parent = p_shaped;
new_sd->start = p_start;
new_sd->end = p_start + p_length;
@@ -2474,7 +2517,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
if (E.value.pos == gl.start) {
find_embedded = true;
key = E.key;
@@ -2518,7 +2561,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
// Align embedded objects to baseline.
float full_ascent = new_sd->ascent;
float full_descent = new_sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : new_sd->objects) {
+ for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : new_sd->objects) {
if ((E.value.pos >= new_sd->start) && (E.value.pos < new_sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
@@ -2588,7 +2631,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
}
RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
MutexLock lock(sd->mutex);
@@ -2596,7 +2639,7 @@ RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const {
}
float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, uint16_t /*JustificationFlag*/ p_jst_flags) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -2705,7 +2748,7 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width,
}
float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -2761,7 +2804,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat3
}
bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2817,7 +2860,7 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
}
bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2833,7 +2876,7 @@ bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
}
void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint16_t p_trim_flags) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped_line);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped_line);
ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
@@ -2861,9 +2904,9 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
return;
}
- Vector<ShapedTextData::Span> &spans = sd->spans;
+ Vector<ShapedTextDataFallback::Span> &spans = sd->spans;
if (sd->parent != RID()) {
- ShapedTextData *parent_sd = shaped_owner.get_or_null(sd->parent);
+ ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);
ERR_FAIL_COND(!parent_sd->valid);
spans = parent_sd->spans;
}
@@ -2985,39 +3028,39 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
}
int TextServerFallback::shaped_text_get_trim_pos(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextData invalid.");
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
return sd->overrun_trim_data.trim_pos;
}
int TextServerFallback::shaped_text_get_ellipsis_pos(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextData invalid.");
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
return sd->overrun_trim_data.ellipsis_pos;
}
const Glyph *TextServerFallback::shaped_text_get_ellipsis_glyphs(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextData invalid.");
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
return sd->overrun_trim_data.ellipsis_glyph_buf.ptr();
}
int TextServerFallback::shaped_text_get_ellipsis_glyph_count(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextData invalid.");
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
return sd->overrun_trim_data.ellipsis_glyph_buf.size();
}
bool TextServerFallback::shaped_text_shape(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3044,7 +3087,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
// "Shape" string.
for (int i = 0; i < sd->spans.size(); i++) {
- const ShapedTextData::Span &span = sd->spans[i];
+ const ShapedTextDataFallback::Span &span = sd->spans[i];
if (span.embedded_key != Variant()) {
// Embedded object.
if (sd->orientation == ORIENTATION_HORIZONTAL) {
@@ -3144,7 +3187,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
// Align embedded objects to baseline.
float full_ascent = sd->ascent;
float full_descent = sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
case INLINE_ALIGNMENT_TO_TOP: {
@@ -3210,7 +3253,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
}
bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3218,7 +3261,7 @@ bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const {
}
const Glyph *TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
@@ -3229,7 +3272,7 @@ const Glyph *TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const {
}
int TextServerFallback::shaped_text_get_glyph_count(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0);
MutexLock lock(sd->mutex);
@@ -3240,7 +3283,7 @@ int TextServerFallback::shaped_text_get_glyph_count(RID p_shaped) const {
}
const Glyph *TextServerFallback::shaped_text_sort_logical(RID p_shaped) {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
@@ -3252,7 +3295,7 @@ const Glyph *TextServerFallback::shaped_text_sort_logical(RID p_shaped) {
}
Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Vector2i());
MutexLock lock(sd->mutex);
@@ -3261,11 +3304,11 @@ Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const {
Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const {
Array ret;
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, ret);
MutexLock lock(sd->mutex);
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
ret.push_back(E.key);
}
@@ -3273,7 +3316,7 @@ Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const {
}
Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Rect2());
MutexLock lock(sd->mutex);
@@ -3285,7 +3328,7 @@ Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_ke
}
Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Size2());
MutexLock lock(sd->mutex);
@@ -3300,7 +3343,7 @@ Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
}
float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3311,7 +3354,7 @@ float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
}
float TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3322,7 +3365,7 @@ float TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
}
float TextServerFallback::shaped_text_get_width(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3333,7 +3376,7 @@ float TextServerFallback::shaped_text_get_width(RID p_shaped) const {
}
float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3345,7 +3388,7 @@ float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const
}
float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3356,6 +3399,34 @@ float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) cons
return sd->uthk;
}
+String TextServerFallback::string_to_upper(const String &p_string, const String &p_language) const {
+ String upper = p_string;
+
+ for (int i = 0; i < upper.size(); i++) {
+ const char32_t s = upper[i];
+ const char32_t t = _find_upper(s);
+ if (s != t) { // avoid copy on write
+ upper[i] = t;
+ }
+ }
+
+ return upper;
+}
+
+String TextServerFallback::string_to_lower(const String &p_string, const String &p_language) const {
+ String lower = p_string;
+
+ for (int i = 0; i < lower.size(); i++) {
+ const char32_t s = lower[i];
+ const char32_t t = _find_lower(s);
+ if (s != t) { // avoid copy on write
+ lower[i] = t;
+ }
+ }
+
+ return lower;
+}
+
TextServerFallback::TextServerFallback() {
_insert_feature_sets();
};
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index e8619e0825..be944cde58 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -203,17 +203,38 @@ class TextServerFallback : public TextServer {
}
}
+ // Shaped text cache data.
+
+ struct ShapedTextDataFallback : public ShapedTextData {
+ struct Span {
+ int start = -1;
+ int end = -1;
+
+ Vector<RID> fonts;
+ int font_size = 0;
+
+ Variant embedded_key;
+
+ String language;
+ Dictionary features;
+ Variant meta;
+ };
+ Vector<Span> spans;
+ };
+
// Common data.
float oversampling = 1.f;
mutable RID_PtrOwner<FontDataFallback> font_owner;
- mutable RID_PtrOwner<ShapedTextData> shaped_owner;
+ mutable RID_PtrOwner<ShapedTextDataFallback> shaped_owner;
+
+ void _realign(ShapedTextDataFallback *p_sd) const;
protected:
static void _bind_methods(){};
- void full_copy(ShapedTextData *p_shaped);
- void invalidate(ShapedTextData *p_shaped);
+ void full_copy(ShapedTextDataFallback *p_shaped);
+ void invalidate(ShapedTextDataFallback *p_shaped);
public:
virtual bool has_feature(Feature p_feature) const override;
@@ -391,10 +412,14 @@ public:
virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
- virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
+ virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override;
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
+ virtual int shaped_get_span_count(RID p_shaped) const override;
+ virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override;
+ virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override;
+
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
@@ -430,6 +455,9 @@ public:
virtual float shaped_text_get_underline_position(RID p_shaped) const override;
virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
+ virtual String string_to_upper(const String &p_string, const String &p_language = "") const override;
+ virtual String string_to_lower(const String &p_string, const String &p_language = "") const override;
+
TextServerFallback();
~TextServerFallback();
};
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 9d297fcb93..a9652cbba1 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -337,6 +337,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
Ref<Image> img;
img.instantiate();
img->create(w, h, false, Image::FORMAT_RGBA8);
+ texture->create_from_image(img);
} else {
/* tear down the partial theora setup */
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index 0436f90c4c..b00b5a2ddd 100644
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -42,6 +42,7 @@
#include "editor/editor_node.h"
#include "editor/editor_resource_preview.h"
#include "editor/editor_scale.h"
+#include "scene/gui/view_panner.h"
#include "scene/main/window.h"
#ifdef TOOLS_ENABLED
@@ -271,7 +272,7 @@ protected:
if (String(p_name) == "export") {
script->set_variable_export(var, p_value);
- EditorNode::get_singleton()->get_inspector()->update_tree();
+ InspectorDock::get_inspector_singleton()->update_tree();
return true;
}
@@ -1366,7 +1367,7 @@ void VisualScriptEditor::_create_function() {
}
void VisualScriptEditor::_add_node_dialog() {
- _generic_search(script->get_instance_base_type(), graph->get_global_position() + Vector2(55, 80), true);
+ _generic_search(graph->get_global_position() + Vector2(55, 80), true);
}
void VisualScriptEditor::_add_func_input() {
@@ -1442,7 +1443,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt
if (p_button == 1) {
// Ensure script base exists otherwise use custom base type.
ERR_FAIL_COND(script.is_null());
- new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), String(), true);
+ new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), true);
return;
} else if (p_button == 0) {
String name = _validate_name("new_function");
@@ -1948,14 +1949,14 @@ void VisualScriptEditor::_on_nodes_duplicate() {
}
}
-void VisualScriptEditor::_generic_search(String p_base_type, Vector2 pos, bool node_centered) {
+void VisualScriptEditor::_generic_search(Vector2 pos, bool node_centered) {
if (node_centered) {
port_action_pos = graph->get_size() / 2.0f;
} else {
port_action_pos = graph->get_viewport()->get_mouse_position() - graph->get_global_position();
}
- new_connect_node_select->select_from_visual_script(p_base_type, false, false); // neither connecting nor reset text
+ new_connect_node_select->select_from_visual_script(script, false); // do not reset text
// Ensure that the dialog fits inside the graph.
Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size();
@@ -1992,7 +1993,7 @@ void VisualScriptEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
}
}
if (is_empty_selection && clipboard->nodes.is_empty()) {
- _generic_search(script->get_instance_base_type(), mouse_up_position);
+ _generic_search();
} else {
popup_menu->set_item_disabled(int(EDIT_CUT_NODES), is_empty_selection);
popup_menu->set_item_disabled(int(EDIT_COPY_NODES), is_empty_selection);
@@ -2446,7 +2447,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
drop_position = pos;
drop_node = node;
drop_path = sn->get_path_to(node);
- new_connect_node_select->select_from_instance(node, "", false, node->get_class());
+ new_connect_node_select->select_from_instance(node, false);
}
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
@@ -2655,6 +2656,15 @@ String VisualScriptEditor::get_name() {
}
Ref<Texture2D> VisualScriptEditor::get_theme_icon() {
+ String icon_name = "VisualScript";
+ if (script->is_built_in()) {
+ icon_name += "Internal";
+ }
+
+ if (Control::has_theme_icon(icon_name, "EditorIcons")) {
+ return Control::get_theme_icon(icon_name, SNAME("EditorIcons"));
+ }
+
return Control::get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons"));
}
@@ -2837,7 +2847,7 @@ Control *VisualScriptEditor::get_base_editor() const {
return graph;
}
-void VisualScriptEditor::set_tooltip_request_func(String p_method, Object *p_obj) {
+void VisualScriptEditor::set_tooltip_request_func(const Callable &p_toolip_callback) {
}
Control *VisualScriptEditor::get_edit_menu() {
@@ -3225,19 +3235,34 @@ void VisualScriptEditor::_port_action_menu(int p_option) {
n->set_base_type("Object");
}
String type_string;
+ String base_script = "";
if (script->get_node(port_action_node)->get_output_value_port_count() > 0) {
type_string = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ VisualScriptFunctionCall *vsfc = Object::cast_to<VisualScriptFunctionCall>(*script->get_node(port_action_node));
+ if (vsfc) {
+ base_script = vsfc->get_base_script();
+ } else {
+ VisualScriptPropertyGet *vspg = Object::cast_to<VisualScriptPropertyGet>(*script->get_node(port_action_node));
+ if (vspg) {
+ base_script = vspg->get_base_script();
+ } else {
+ VisualScriptPropertySet *vsps = Object::cast_to<VisualScriptPropertySet>(*script->get_node(port_action_node));
+ if (vsps) {
+ base_script = vsps->get_base_script();
+ }
+ }
+ }
}
if (tg.type == Variant::OBJECT) {
if (tg.script.is_valid()) {
- new_connect_node_select->select_from_script(tg.script, "");
- } else if (!type_string.is_empty()) {
- new_connect_node_select->select_from_base_type(type_string);
+ new_connect_node_select->select_from_script(tg.script);
+ } else if (type_string != String()) {
+ new_connect_node_select->select_from_base_type(type_string, base_script);
} else {
- new_connect_node_select->select_from_base_type(n->get_base_type());
+ new_connect_node_select->select_from_base_type(n->get_base_type(), base_script);
}
} else if (tg.type == Variant::NIL) {
- new_connect_node_select->select_from_base_type("");
+ new_connect_node_select->select_from_base_type("", base_script);
} else {
new_connect_node_select->select_from_basic_type(tg.type);
}
@@ -3300,66 +3325,54 @@ void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<Visua
}
void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) {
+#ifdef OSX_ENABLED
+ bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::META);
+#else
+ bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
+#endif
Vector2 pos = _get_pos_in_graph(port_action_pos);
Set<int> vn;
+ bool port_node_exists = true;
if (drop_position != Vector2()) {
pos = drop_position;
}
drop_position = Vector2();
- bool port_node_exists = true;
-
- // if (func == StringName()) {
- // func = default_func;
- // port_node_exists = false;
- // }
-
- if (p_category == "visualscript") {
- Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text);
- Ref<VisualScriptNode> vnode_old;
- if (port_node_exists && p_connecting) {
- vnode_old = script->get_node(port_action_node);
- }
- int new_id = script->get_available_id();
+ Ref<VisualScriptNode> vnode;
+ Ref<VisualScriptNode> vnode_old;
+ if (port_node_exists && p_connecting) {
+ vnode_old = script->get_node(port_action_node);
+ }
- if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr()) && vnode_old.is_valid()) {
- Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type;
- Object::cast_to<VisualScriptOperator>(vnode_new.ptr())->set_typed(type);
- }
+ if (p_category.begins_with("VisualScriptNode")) {
+ Ref<VisualScriptNode> n = VisualScriptLanguage::singleton->create_node_from_name(p_text);
- if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr()) && vnode_old.is_valid()) {
+ if (Object::cast_to<VisualScriptTypeCast>(n.ptr()) && vnode_old.is_valid()) {
Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type;
String hint_name = vnode_old->get_output_value_port_info(port_action_output).hint_string;
if (type == Variant::OBJECT) {
- Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(hint_name);
+ Object::cast_to<VisualScriptTypeCast>(n.ptr())->set_base_type(hint_name);
} else if (type == Variant::NIL) {
- Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type("");
+ Object::cast_to<VisualScriptTypeCast>(n.ptr())->set_base_type("");
} else {
- Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(Variant::get_type_name(type));
+ Object::cast_to<VisualScriptTypeCast>(n.ptr())->set_base_type(Variant::get_type_name(type));
}
}
-
- undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode_new, pos);
- if (vnode_old.is_valid() && p_connecting) {
- connect_seq(vnode_old, vnode_new, new_id);
- connect_data(vnode_old, vnode_new, new_id);
- }
-
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
- return;
+ vnode = n;
}
- Ref<VisualScriptNode> vnode;
- Ref<VisualScriptPropertySet> script_prop_set;
-
- if (p_category == String("method")) {
+ if (p_category == String("Class") && !p_connecting) {
+ Ref<VisualScriptFunctionCall> n;
+ n.instantiate();
+ n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SINGLETON);
+ n->set_singleton("ClassDB");
+ n->set_function("instantiate");
+ // Did not find a way to edit the input port value
+ vnode = n;
+ } else if (p_category == String("class_method")) {
Ref<VisualScriptFunctionCall> n;
n.instantiate();
if (!drop_path.is_empty()) {
@@ -3377,96 +3390,151 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
}
}
vnode = n;
- } else if (p_category == String("set")) {
- Ref<VisualScriptPropertySet> n;
- n.instantiate();
- if (!drop_path.is_empty()) {
- if (drop_path == ".") {
- n->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF);
- } else {
- n->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
- n->set_base_path(drop_path);
+ } else if (p_category == String("class_property")) {
+ Vector<String> property_path = p_text.split(":");
+ if (held_ctrl) {
+ Ref<VisualScriptPropertySet> n;
+ n.instantiate();
+ n->set_property(property_path[1]);
+ if (!drop_path.is_empty()) {
+ if (drop_path == ".") {
+ n->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF);
+ } else {
+ n->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
+ n->set_base_path(drop_path);
+ }
}
- }
- if (drop_node) {
- n->set_base_type(drop_node->get_class());
- if (drop_node->get_script_instance()) {
- n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ if (drop_node) {
+ n->set_base_type(drop_node->get_class());
+ if (drop_node->get_script_instance()) {
+ n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ }
}
- }
- vnode = n;
- script_prop_set = n;
- } else if (p_category == String("get")) {
- Ref<VisualScriptPropertyGet> n;
- n.instantiate();
- n->set_property(p_text);
- if (!drop_path.is_empty()) {
- if (drop_path == ".") {
- n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF);
- } else {
- n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH);
- n->set_base_path(drop_path);
+ vnode = n;
+ } else {
+ Ref<VisualScriptPropertyGet> n;
+ n.instantiate();
+ n->set_property(property_path[1]);
+ if (!drop_path.is_empty()) {
+ if (drop_path == ".") {
+ n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF);
+ } else {
+ n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH);
+ n->set_base_path(drop_path);
+ }
}
- }
- if (drop_node) {
- n->set_base_type(drop_node->get_class());
- if (drop_node->get_script_instance()) {
- n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ if (drop_node) {
+ n->set_base_type(drop_node->get_class());
+ if (drop_node->get_script_instance()) {
+ n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ }
}
- }
- vnode = n;
- }
- drop_path = String();
- drop_node = nullptr;
-
- if (p_category == String("action")) {
- if (p_text == "VisualScriptCondition") {
- Ref<VisualScriptCondition> n;
- n.instantiate();
vnode = n;
}
- if (p_text == "VisualScriptSwitch") {
- Ref<VisualScriptSwitch> n;
- n.instantiate();
- vnode = n;
- } else if (p_text == "VisualScriptSequence") {
- Ref<VisualScriptSequence> n;
+ } else if (p_category == String("class_constant")) {
+ Vector<String> property_path = p_text.split(":");
+ if (ClassDB::class_exists(property_path[0])) {
+ Ref<VisualScriptClassConstant> n;
n.instantiate();
+ n->set_base_type(property_path[0]);
+ n->set_class_constant(property_path[1]);
vnode = n;
- } else if (p_text == "VisualScriptIterator") {
- Ref<VisualScriptIterator> n;
- n.instantiate();
- vnode = n;
- } else if (p_text == "VisualScriptWhile") {
- Ref<VisualScriptWhile> n;
- n.instantiate();
- vnode = n;
- } else if (p_text == "VisualScriptReturn") {
- Ref<VisualScriptReturn> n;
+ } else {
+ Ref<VisualScriptBasicTypeConstant> n;
n.instantiate();
+ if (property_path[0] == "Nil") {
+ n->set_basic_type(Variant::NIL);
+ } else if (property_path[0] == "bool") {
+ n->set_basic_type(Variant::BOOL);
+ } else if (property_path[0] == "int") {
+ n->set_basic_type(Variant::INT);
+ } else if (property_path[0] == "float") {
+ n->set_basic_type(Variant::FLOAT);
+ } else if (property_path[0] == "String") {
+ n->set_basic_type(Variant::STRING);
+ } else if (property_path[0] == "Vector2") {
+ n->set_basic_type(Variant::VECTOR2);
+ } else if (property_path[0] == "Vector2i") {
+ n->set_basic_type(Variant::VECTOR2I);
+ } else if (property_path[0] == "Rect2") {
+ n->set_basic_type(Variant::RECT2);
+ } else if (property_path[0] == "Rect2i") {
+ n->set_basic_type(Variant::RECT2I);
+ } else if (property_path[0] == "Transform2D") {
+ n->set_basic_type(Variant::TRANSFORM2D);
+ } else if (property_path[0] == "Vector3") {
+ n->set_basic_type(Variant::VECTOR3);
+ } else if (property_path[0] == "Vector3i") {
+ n->set_basic_type(Variant::VECTOR3I);
+ } else if (property_path[0] == "Plane") {
+ n->set_basic_type(Variant::PLANE);
+ } else if (property_path[0] == "ABB") {
+ n->set_basic_type(Variant::AABB);
+ } else if (property_path[0] == "Quaternion") {
+ n->set_basic_type(Variant::QUATERNION);
+ } else if (property_path[0] == "Basis") {
+ n->set_basic_type(Variant::BASIS);
+ } else if (property_path[0] == "Transform3D") {
+ n->set_basic_type(Variant::TRANSFORM3D);
+ } else if (property_path[0] == "Color") {
+ n->set_basic_type(Variant::COLOR);
+ } else if (property_path[0] == "RID") {
+ n->set_basic_type(Variant::RID);
+ } else if (property_path[0] == "Object") {
+ n->set_basic_type(Variant::OBJECT);
+ } else if (property_path[0] == "Callable") {
+ n->set_basic_type(Variant::CALLABLE);
+ } else if (property_path[0] == "Signal") {
+ n->set_basic_type(Variant::SIGNAL);
+ } else if (property_path[0] == "StringName") {
+ n->set_basic_type(Variant::STRING_NAME);
+ } else if (property_path[0] == "NodePath") {
+ n->set_basic_type(Variant::NODE_PATH);
+ } else if (property_path[0] == "Dictionary") {
+ n->set_basic_type(Variant::DICTIONARY);
+ } else if (property_path[0] == "Array") {
+ n->set_basic_type(Variant::ARRAY);
+ } else if (property_path[0] == "PackedByteArray") {
+ n->set_basic_type(Variant::PACKED_BYTE_ARRAY);
+ } else if (property_path[0] == "PackedInt32Array") {
+ n->set_basic_type(Variant::PACKED_INT32_ARRAY);
+ } else if (property_path[0] == "PackedInt64Array") {
+ n->set_basic_type(Variant::PACKED_INT64_ARRAY);
+ } else if (property_path[0] == "PackedFloat32Array") {
+ n->set_basic_type(Variant::PACKED_FLOAT32_ARRAY);
+ } else if (property_path[0] == "PackedStringArray") {
+ n->set_basic_type(Variant::PACKED_STRING_ARRAY);
+ } else if (property_path[0] == "PackedVector2Array") {
+ n->set_basic_type(Variant::PACKED_VECTOR2_ARRAY);
+ } else if (property_path[0] == "PackedVector3Array") {
+ n->set_basic_type(Variant::PACKED_VECTOR3_ARRAY);
+ } else if (property_path[0] == "PackedColorArray") {
+ n->set_basic_type(Variant::PACKED_COLOR_ARRAY);
+ }
+ n->set_basic_type_constant(property_path[1]);
vnode = n;
}
- }
- int new_id = script->get_available_id();
- undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- undo_redo->add_do_method(this, "_update_graph", new_id);
- undo_redo->add_undo_method(this, "_update_graph", new_id);
- undo_redo->commit_action();
+ } else if (p_category == String("class_signal")) {
+ Vector<String> property_path = p_text.split(":");
+ ERR_FAIL_COND(!(script->has_custom_signal(property_path[1]) || ClassDB::has_signal(script->get_instance_base_type(), property_path[1])));
- if (script_prop_set.is_valid()) {
- script_prop_set->set_property(p_text);
+ Ref<VisualScriptEmitSignal> n;
+ n.instantiate();
+ n->set_signal(property_path[1]);
+ vnode = n;
+ }
+ if (vnode == nullptr) {
+ print_error("Category not handled: " + p_category.quote());
}
- port_action_new_node = new_id;
-
- Ref<VisualScriptNode> vsn = script->get_node(port_action_new_node);
+ if (Object::cast_to<VisualScriptFunctionCall>(vnode.ptr()) && p_category != "Class") {
+ Vector<String> property_path = p_text.split(":");
+ String class_of_method = property_path[0];
+ String method_name = property_path[1];
- if (Object::cast_to<VisualScriptFunctionCall>(vsn.ptr())) {
- Ref<VisualScriptFunctionCall> vsfc = vsn;
- vsfc->set_function(p_text);
+ Ref<VisualScriptFunctionCall> vsfc = vnode;
+ vsfc->set_function(method_name);
if (port_node_exists && p_connecting) {
VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
@@ -3483,7 +3551,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
if (!base_type.is_empty() && hint == PROPERTY_HINT_TYPE_STRING) {
vsfc->set_base_type(base_type);
}
- if (p_text == "call" || p_text == "call_deferred") {
+ if (method_name == "call" || method_name == "call_deferred") {
vsfc->set_function(String(""));
}
}
@@ -3501,8 +3569,8 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
}
if (port_node_exists && p_connecting) {
- if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) {
- Ref<VisualScriptPropertySet> vsp = vsn;
+ if (Object::cast_to<VisualScriptPropertySet>(vnode.ptr())) {
+ Ref<VisualScriptPropertySet> vsp = vnode;
VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
if (tg.type == Variant::OBJECT) {
@@ -3531,8 +3599,8 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
}
}
- if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) {
- Ref<VisualScriptPropertyGet> vsp = vsn;
+ if (Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())) {
+ Ref<VisualScriptPropertyGet> vsp = vnode;
VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
if (tg.type == Variant::OBJECT) {
@@ -3560,13 +3628,85 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
}
}
}
+ if (vnode == nullptr) {
+ print_error("Not able to create node from category: \"" + p_category + "\" and text \"" + p_text + "\" Not created");
+ return;
+ }
+
+ int new_id = script->get_available_id();
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
+ undo_redo->add_do_method(this, "_update_graph", new_id);
+ undo_redo->add_undo_method(this, "_update_graph", new_id);
+ undo_redo->commit_action();
+
+ port_action_new_node = new_id;
+
+ String base_script = "";
+ String base_type = "";
if (port_node_exists) {
- Ref<VisualScriptNode> vnode_old = script->get_node(port_action_node);
+ if (vnode_old.is_valid()) {
+ if (Object::cast_to<VisualScriptTypeCast>(vnode_old.ptr())) {
+ base_type = Object::cast_to<VisualScriptTypeCast>(vnode_old.ptr())->get_base_type();
+ base_script = Object::cast_to<VisualScriptTypeCast>(vnode_old.ptr())->get_base_script();
+ } else if (Object::cast_to<VisualScriptFunctionCall>(vnode_old.ptr())) {
+ base_type = Object::cast_to<VisualScriptFunctionCall>(vnode_old.ptr())->get_base_type();
+ base_script = Object::cast_to<VisualScriptFunctionCall>(vnode_old.ptr())->get_base_script();
+ } else if (Object::cast_to<VisualScriptPropertySet>(vnode_old.ptr())) {
+ base_type = Object::cast_to<VisualScriptPropertySet>(vnode_old.ptr())->get_base_type();
+ base_script = Object::cast_to<VisualScriptPropertySet>(vnode_old.ptr())->get_base_script();
+ } else if (Object::cast_to<VisualScriptPropertyGet>(vnode_old.ptr())) {
+ base_type = Object::cast_to<VisualScriptPropertyGet>(vnode_old.ptr())->get_base_type();
+ base_script = Object::cast_to<VisualScriptPropertyGet>(vnode_old.ptr())->get_base_script();
+ }
+ }
+
+ Vector<String> property_path = p_text.split(":");
+ if (ClassDB::is_parent_class(script->get_instance_base_type(), property_path[0]) || script->get_path().ends_with(property_path[0].unquote())) {
+ if (!p_connecting) {
+ base_type = script->get_instance_base_type();
+ base_script = script->get_path();
+ }
+ } else {
+ base_type = property_path[0];
+ base_script = "";
+ }
+
+ if (drop_node) {
+ Ref<Script> script = drop_node->get_script();
+ if (script != nullptr) {
+ base_script = script->get_path();
+ }
+ }
+
if (vnode_old.is_valid() && p_connecting) {
+ if (base_type == "") {
+ base_type = property_path[0];
+ } else if (ClassDB::is_parent_class(property_path[0], base_type)) {
+ base_type = property_path[0];
+ }
connect_seq(vnode_old, vnode, port_action_new_node);
connect_data(vnode_old, vnode, port_action_new_node);
}
}
+ if (Object::cast_to<VisualScriptTypeCast>(vnode.ptr())) {
+ Object::cast_to<VisualScriptTypeCast>(vnode.ptr())->set_base_type(base_type);
+ Object::cast_to<VisualScriptTypeCast>(vnode.ptr())->set_base_script(base_script);
+ } else if (Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())) {
+ Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())->set_base_type(base_type);
+ Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())->set_base_script(base_script);
+ } else if (Object::cast_to<VisualScriptPropertySet>(vnode.ptr())) {
+ Object::cast_to<VisualScriptPropertySet>(vnode.ptr())->set_base_type(base_type);
+ Object::cast_to<VisualScriptPropertySet>(vnode.ptr())->set_base_script(base_script);
+ } else if (Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())) {
+ Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())->set_base_type(base_type);
+ Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())->set_base_script(base_script);
+ }
+
+ drop_path = String();
+ drop_node = nullptr;
+
_update_graph(port_action_new_node);
}
@@ -3616,7 +3756,7 @@ void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<Visual
}
void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting) {
- String name = p_text;
+ String name = p_text.substr(p_text.find_char(':') + 1);
if (script->has_function(name)) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Script already has function '%s'"), name));
return;
@@ -3773,7 +3913,8 @@ void VisualScriptEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- graph->set_panning_scheme((GraphEdit::PanningScheme)EDITOR_GET("interface/editors/sub_editor_panning_scheme").operator int());
+ graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
+ graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning")));
} break;
case NOTIFICATION_READY: {
@@ -3892,7 +4033,7 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_
void VisualScriptEditor::_menu_option(int p_what) {
switch (p_what) {
case EDIT_ADD_NODE: {
- _generic_search(script->get_instance_base_type(), mouse_up_position);
+ _generic_search();
} break;
case EDIT_DELETE_NODES: {
_on_nodes_delete();
@@ -3922,7 +4063,7 @@ void VisualScriptEditor::_menu_option(int p_what) {
} break;
case EDIT_FIND_NODE_TYPE: {
- _generic_search(script->get_instance_base_type());
+ _generic_search();
} break;
case EDIT_COPY_NODES: {
_on_nodes_copy();
diff --git a/modules/visual_script/editor/visual_script_editor.h b/modules/visual_script/editor/visual_script_editor.h
index 90e4fb9d56..f1b01aa6dc 100644
--- a/modules/visual_script/editor/visual_script_editor.h
+++ b/modules/visual_script/editor/visual_script_editor.h
@@ -85,55 +85,55 @@ class VisualScriptEditor : public ScriptEditorBase {
MEMBER_SIGNAL
};
- VBoxContainer *members_section;
- MenuButton *edit_menu;
+ VBoxContainer *members_section = nullptr;
+ MenuButton *edit_menu = nullptr;
Ref<VisualScript> script;
- Button *base_type_select;
+ Button *base_type_select = nullptr;
- LineEdit *func_name_box;
- ScrollContainer *func_input_scroll;
- VBoxContainer *func_input_vbox;
- ConfirmationDialog *function_create_dialog;
+ LineEdit *func_name_box = nullptr;
+ ScrollContainer *func_input_scroll = nullptr;
+ VBoxContainer *func_input_vbox = nullptr;
+ ConfirmationDialog *function_create_dialog = nullptr;
- GraphEdit *graph;
- HBoxContainer *status_bar;
- Button *toggle_scripts_button;
+ GraphEdit *graph = nullptr;
+ HBoxContainer *status_bar = nullptr;
+ Button *toggle_scripts_button = nullptr;
- VisualScriptEditorSignalEdit *signal_editor;
+ VisualScriptEditorSignalEdit *signal_editor = nullptr;
- AcceptDialog *edit_signal_dialog;
- EditorInspector *edit_signal_edit;
+ AcceptDialog *edit_signal_dialog = nullptr;
+ EditorInspector *edit_signal_edit = nullptr;
- VisualScriptPropertySelector *method_select;
- VisualScriptPropertySelector *new_connect_node_select;
- VisualScriptPropertySelector *new_virtual_method_select;
+ VisualScriptPropertySelector *method_select = nullptr;
+ VisualScriptPropertySelector *new_connect_node_select = nullptr;
+ VisualScriptPropertySelector *new_virtual_method_select = nullptr;
- VisualScriptEditorVariableEdit *variable_editor;
+ VisualScriptEditorVariableEdit *variable_editor = nullptr;
- AcceptDialog *edit_variable_dialog;
- EditorInspector *edit_variable_edit;
+ AcceptDialog *edit_variable_dialog = nullptr;
+ EditorInspector *edit_variable_edit = nullptr;
- CustomPropertyEditor *default_value_edit;
+ CustomPropertyEditor *default_value_edit = nullptr;
- UndoRedo *undo_redo;
+ UndoRedo *undo_redo = nullptr;
- Tree *members;
- AcceptDialog *function_name_edit;
- LineEdit *function_name_box;
+ Tree *members = nullptr;
+ AcceptDialog *function_name_edit = nullptr;
+ LineEdit *function_name_box = nullptr;
- Label *hint_text;
- Timer *hint_text_timer;
+ Label *hint_text = nullptr;
+ Timer *hint_text_timer = nullptr;
- Label *select_func_text;
+ Label *select_func_text = nullptr;
bool updating_graph = false;
void _show_hint(const String &p_hint);
void _hide_timer();
- CreateDialog *select_base_type;
+ CreateDialog *select_base_type = nullptr;
struct VirtualInMenu {
String name;
@@ -241,7 +241,7 @@ class VisualScriptEditor : public ScriptEditorBase {
bool node_has_sequence_connections(int p_id);
- void _generic_search(String p_base_type = "", Vector2 pos = Vector2(), bool node_centered = false);
+ void _generic_search(Vector2 pos = Vector2(), bool node_centered = false);
virtual void input(const Ref<InputEvent> &p_event) override;
void _graph_gui_input(const Ref<InputEvent> &p_event);
@@ -328,7 +328,7 @@ public:
virtual void update_settings() override;
virtual bool show_members_overview() override;
virtual void set_debugger_active(bool p_active) override;
- virtual void set_tooltip_request_func(String p_method, Object *p_obj) override;
+ virtual void set_tooltip_request_func(const Callable &p_toolip_callback) override;
virtual Control *get_edit_menu() override;
virtual void clear_edit_menu() override;
virtual void set_find_replace_bar(FindReplaceBar *p_bar) override { p_bar->hide(); }; // Not needed here.
diff --git a/modules/visual_script/editor/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp
index c88d10dabd..1059d126bc 100644
--- a/modules/visual_script/editor/visual_script_property_selector.cpp
+++ b/modules/visual_script/editor/visual_script_property_selector.cpp
@@ -37,13 +37,28 @@
#include "../visual_script_nodes.h"
#include "core/os/keyboard.h"
#include "editor/doc_tools.h"
+#include "editor/editor_feature_profile.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "scene/main/node.h"
#include "scene/main/window.h"
-void VisualScriptPropertySelector::_text_changed(const String &p_newtext) {
- _update_search();
+void VisualScriptPropertySelector::_update_icons() {
+ search_box->set_right_icon(results_tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ search_box->set_clear_button_enabled(true);
+ search_box->add_theme_icon_override("right_icon", results_tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+
+ search_visual_script_nodes->set_icon(results_tree->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")));
+ search_classes->set_icon(results_tree->get_theme_icon(SNAME("Object"), SNAME("EditorIcons")));
+ search_methods->set_icon(results_tree->get_theme_icon(SNAME("MemberMethod"), SNAME("EditorIcons")));
+ search_operators->set_icon(results_tree->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ search_signals->set_icon(results_tree->get_theme_icon(SNAME("MemberSignal"), SNAME("EditorIcons")));
+ search_constants->set_icon(results_tree->get_theme_icon(SNAME("MemberConstant"), SNAME("EditorIcons")));
+ search_properties->set_icon(results_tree->get_theme_icon(SNAME("MemberProperty"), SNAME("EditorIcons")));
+ search_theme_items->set_icon(results_tree->get_theme_icon(SNAME("MemberTheme"), SNAME("EditorIcons")));
+
+ case_sensitive_button->set_icon(results_tree->get_theme_icon(SNAME("MatchCase"), SNAME("EditorIcons")));
+ hierarchy_button->set_icon(results_tree->get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons")));
}
void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) {
@@ -55,24 +70,8 @@ void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) {
case Key::DOWN:
case Key::PAGEUP:
case Key::PAGEDOWN: {
- search_options->gui_input(k);
+ results_tree->gui_input(k);
search_box->accept_event();
-
- TreeItem *root = search_options->get_root();
- if (!root->get_first_child()) {
- break;
- }
-
- TreeItem *current = search_options->get_selected();
-
- TreeItem *item = search_options->get_next_selected(root);
- while (item) {
- item->deselect(0);
- item = search_options->get_next_selected(item);
- }
-
- current->select(0);
-
} break;
default:
break;
@@ -80,654 +79,1191 @@ void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) {
}
}
-void VisualScriptPropertySelector::_update_search() {
- set_title(TTR("Search VisualScript"));
-
- search_options->clear();
- help_bit->set_text("");
-
- TreeItem *root = search_options->create_item();
- bool found = false;
- StringName base = base_type;
- List<StringName> base_list;
- while (base) {
- base_list.push_back(base);
- base = ClassDB::get_parent_class_nocheck(base);
- }
-
- for (const StringName &E : base_list) {
- List<MethodInfo> methods;
- List<PropertyInfo> props;
- TreeItem *category = nullptr;
- Ref<Texture2D> type_icons[Variant::VARIANT_MAX] = {
- vbc->get_theme_icon(SNAME("Variant"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("int"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("float"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("String"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Vector2i"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Rect2"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Rect2i"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Vector3i"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Transform2D"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Plane"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Quaternion"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("AABB"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Basis"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Color"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("StringName"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("NodePath"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("RID"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("MiniObject"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Callable"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Signal"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Dictionary"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedByteArray"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedInt32Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedInt64Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedFloat32Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedFloat64Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedStringArray"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedVector2Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedVector3Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedColorArray"), SNAME("EditorIcons"))
- };
- {
- String b = String(E);
- category = search_options->create_item(root);
- if (category) {
- category->set_text(0, b.replace_first("*", ""));
- category->set_selectable(0, false);
- Ref<Texture2D> icon;
- String rep = b.replace("*", "");
- icon = EditorNode::get_singleton()->get_class_icon(rep);
- category->set_icon(0, icon);
- }
- }
- if (properties || seq_connect) {
- if (instance) {
- instance->get_property_list(&props, true);
- } else {
- Object *obj = ObjectDB::get_instance(script);
- if (Object::cast_to<Script>(obj)) {
- Object::cast_to<Script>(obj)->get_script_property_list(&props);
- } else {
- ClassDB::get_property_list(E, &props, true);
- }
- }
- for (const PropertyInfo &F : props) {
- if (!(F.usage & PROPERTY_USAGE_EDITOR) && !(F.usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) {
- continue;
- }
+void VisualScriptPropertySelector::_update_results_i(int p_int) {
+ _update_results();
+}
- if (type_filter.size() && type_filter.find(F.type) == -1) {
- continue;
- }
+void VisualScriptPropertySelector::_update_results_s(String p_string) {
+ _update_results();
+}
- // capitalize() also converts underscore to space, we'll match again both possible styles
- String get_text_raw = String(vformat(TTR("Get %s"), F.name));
- String get_text = get_text_raw.capitalize();
- String set_text_raw = String(vformat(TTR("Set %s"), F.name));
- String set_text = set_text_raw.capitalize();
- String input = search_box->get_text().capitalize();
-
- if (input.is_empty() || get_text_raw.findn(input) != -1 || get_text.findn(input) != -1) {
- TreeItem *item = search_options->create_item(category ? category : root);
- item->set_text(0, get_text);
- item->set_metadata(0, F.name);
- item->set_icon(0, type_icons[F.type]);
- item->set_metadata(1, "get");
- item->set_collapsed(true);
- item->set_selectable(0, true);
- item->set_selectable(1, false);
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
- }
+void VisualScriptPropertySelector::_update_results() {
+ _update_icons();
+ search_runner = Ref<SearchRunner>(memnew(SearchRunner(this, results_tree)));
+ set_process(true);
+}
+
+void VisualScriptPropertySelector::_confirmed() {
+ TreeItem *ti = results_tree->get_selected();
+ if (!ti) {
+ return;
+ }
+ emit_signal(SNAME("selected"), ti->get_metadata(0), ti->get_metadata(1), connecting);
+ set_visible(false);
+}
- if (input.is_empty() || set_text_raw.findn(input) != -1 || set_text.findn(input) != -1) {
- TreeItem *item = search_options->create_item(category ? category : root);
- item->set_text(0, set_text);
- item->set_metadata(0, F.name);
- item->set_icon(0, type_icons[F.type]);
- item->set_metadata(1, "set");
- item->set_selectable(0, true);
- item->set_selectable(1, false);
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
+void VisualScriptPropertySelector::_item_selected() {
+ if (results_tree->get_selected()->has_meta("description")) {
+ help_bit->set_text(results_tree->get_selected()->get_meta("description"));
+ } else {
+ help_bit->set_text("No description available");
+ }
+}
+
+void VisualScriptPropertySelector::_hide_requested() {
+ _cancel_pressed(); // From AcceptDialog.
+}
+
+void VisualScriptPropertySelector::_notification(int p_what) {
+ switch (p_what) {
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ _update_icons();
+ } break;
+ case NOTIFICATION_ENTER_TREE: {
+ connect("confirmed", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
+ } break;
+ case NOTIFICATION_PROCESS: {
+ // Update background search.
+ if (search_runner.is_valid()) {
+ if (search_runner->work()) {
+ // Search done.
+ get_ok_button()->set_disabled(!results_tree->get_selected());
+
+ search_runner = Ref<SearchRunner>();
+ set_process(false);
}
- }
- }
- {
- if (type != Variant::NIL) {
- Variant v;
- Callable::CallError ce;
- Variant::construct(type, v, nullptr, 0, ce);
- v.get_method_list(&methods);
} else {
- Object *obj = ObjectDB::get_instance(script);
- if (Object::cast_to<Script>(obj)) {
- Object::cast_to<Script>(obj)->get_script_method_list(&methods);
- }
-
- ClassDB::get_method_list(E, &methods, true, true);
- }
- }
- for (List<MethodInfo>::Element *M = methods.front(); M; M = M->next()) {
- String name = M->get().name.get_slice(":", 0);
- if (name.begins_with("_") && !(M->get().flags & METHOD_FLAG_VIRTUAL)) {
- continue;
+ // if one is valid
+ set_process(false);
}
+ } break;
+ }
+}
- if (virtuals_only && !(M->get().flags & METHOD_FLAG_VIRTUAL)) {
- continue;
- }
+void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const bool p_virtuals_only, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select method from base type"));
+ base_type = p_base;
+ base_script = "";
+ type = Variant::NIL;
+ connecting = p_connecting;
- if (!virtuals_only && (M->get().flags & METHOD_FLAG_VIRTUAL)) {
- continue;
- }
+ if (clear_text) {
+ if (p_virtuals_only) {
+ search_box->set_text("._"); // show all _methods
+ search_box->set_caret_column(2);
+ } else {
+ search_box->set_text("."); // show all methods
+ search_box->set_caret_column(1);
+ }
+ }
- MethodInfo mi = M->get();
- String desc_arguments;
- if (mi.arguments.size() > 0) {
- desc_arguments = "(";
- for (int i = 0; i < mi.arguments.size(); i++) {
- if (i > 0) {
- desc_arguments += ", ";
- }
- if (mi.arguments[i].type == Variant::NIL) {
- desc_arguments += "var";
- } else if (mi.arguments[i].name.find(":") != -1) {
- desc_arguments += mi.arguments[i].name.get_slice(":", 1);
- mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0);
- } else {
- desc_arguments += Variant::get_type_name(mi.arguments[i].type);
- }
- }
- desc_arguments += ")";
- }
- String desc_raw = mi.name + desc_arguments;
- String desc = desc_raw.capitalize().replace("( ", "(");
+ search_visual_script_nodes->set_pressed(false);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(false);
+ search_signals->set_pressed(false);
+ search_constants->set_pressed(false);
+ search_properties->set_pressed(false);
+ search_theme_items->set_pressed(false);
- if (!search_box->get_text().is_empty() &&
- name.findn(search_box->get_text()) == -1 &&
- desc.findn(search_box->get_text()) == -1 &&
- desc_raw.findn(search_box->get_text()) == -1) {
- continue;
- }
+ scope_combo->select(2); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated"
- TreeItem *item = search_options->create_item(category ? category : root);
- item->set_text(0, desc);
- item->set_icon(0, vbc->get_theme_icon(SNAME("MemberMethod"), SNAME("EditorIcons")));
- item->set_metadata(0, name);
- item->set_selectable(0, true);
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
- item->set_metadata(1, "method");
- item->set_collapsed(true);
- item->set_selectable(1, false);
+ _update_results();
+}
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
- }
+void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_base_script, bool p_virtuals_only, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select from base type"));
+ base_type = p_base;
+ base_script = p_base_script.lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ type = Variant::NIL;
+ connecting = p_connecting;
- if (category && category->get_first_child() == nullptr) {
- memdelete(category); //old category was unused
+ if (clear_text) {
+ if (p_virtuals_only) {
+ search_box->set_text("_");
+ } else {
+ search_box->set_text(" ");
}
}
- if (properties) {
- if (!seq_connect && !visual_script_generic) {
- get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box);
- get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box);
- get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box);
- get_visual_node_names("functions/deconstruct/" + Variant::get_type_name(type), Set<String>(), found, root, search_box);
- get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box);
- if (type == Variant::INT) {
- get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box);
- }
- if (type == Variant::BOOL) {
- get_visual_node_names("operators/logic/", Set<String>(), found, root, search_box);
- }
- if (type == Variant::BOOL || type == Variant::INT || type == Variant::FLOAT || type == Variant::VECTOR2 || type == Variant::VECTOR3) {
- get_visual_node_names("operators/math/", Set<String>(), found, root, search_box);
- }
- }
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(false);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(false);
+ search_signals->set_pressed(true);
+ search_constants->set_pressed(false);
+ search_properties->set_pressed(true);
+ search_theme_items->set_pressed(false);
+
+ // When class is Input only show inheritors
+ scope_combo->select(0); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated"
+
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+ _update_results();
+}
+
+void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select from script"));
+ ERR_FAIL_COND(p_script.is_null());
+
+ base_type = p_script->get_instance_base_type();
+ base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ type = Variant::NIL;
+ script = p_script->get_instance_id();
+ connecting = p_connecting;
+
+ if (clear_text) {
+ search_box->set_text("");
}
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(false);
+ search_classes->set_pressed(true);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(true);
+ search_signals->set_pressed(true);
+ search_constants->set_pressed(true);
+ search_properties->set_pressed(true);
+ search_theme_items->set_pressed(false);
+
+ scope_combo->select(2); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated"
+
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+ _update_results();
+}
- if (seq_connect && !visual_script_generic) {
- String text = search_box->get_text();
- create_visualscript_item(String("VisualScriptCondition"), root, text, String("Condition"));
- create_visualscript_item(String("VisualScriptSwitch"), root, text, String("Switch"));
- create_visualscript_item(String("VisualScriptSequence"), root, text, String("Sequence"));
- create_visualscript_item(String("VisualScriptIterator"), root, text, String("Iterator"));
- create_visualscript_item(String("VisualScriptWhile"), root, text, String("While"));
- create_visualscript_item(String("VisualScriptReturn"), root, text, String("Return"));
- get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box);
- get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box);
+void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select from basic type"));
+ ERR_FAIL_COND(p_type == Variant::NIL);
+ base_type = Variant::get_type_name(p_type);
+ base_script = "";
+ type = p_type;
+ connecting = p_connecting;
+
+ if (clear_text) {
+ search_box->set_text(" ");
+ }
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(false);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(true);
+ search_signals->set_pressed(false);
+ search_constants->set_pressed(true);
+ search_properties->set_pressed(true);
+ search_theme_items->set_pressed(false);
+
+ scope_combo->select(2); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated" //id5 "Search All"
+
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+
+ _update_results();
+}
+
+void VisualScriptPropertySelector::select_from_action(const String &p_type, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select from action"));
+ base_type = p_type;
+ base_script = "";
+ type = Variant::NIL;
+ connecting = p_connecting;
+
+ if (clear_text) {
+ search_box->set_text("");
}
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(true);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(false);
+ search_operators->set_pressed(false);
+ search_signals->set_pressed(false);
+ search_constants->set_pressed(false);
+ search_properties->set_pressed(false);
+ search_theme_items->set_pressed(false);
+
+ scope_combo->select(0); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated" //id5 "Search All"
+
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+ _update_results();
+}
+
+void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select from instance"));
+ base_type = p_instance->get_class();
- if ((properties || seq_connect) && visual_script_generic) {
- get_visual_node_names("", Set<String>(), found, root, search_box);
+ const Ref<Script> &p_script = p_instance->get_script();
+ if (p_script == nullptr) {
+ base_script = "";
+ } else {
+ base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
}
- TreeItem *selected_item = search_options->search_item_text(search_box->get_text());
- if (!found && selected_item != nullptr) {
- selected_item->select(0);
- found = true;
+ type = Variant::NIL;
+ connecting = p_connecting;
+
+ if (clear_text) {
+ search_box->set_text(" ");
}
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(false);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(false);
+ search_signals->set_pressed(true);
+ search_constants->set_pressed(true);
+ search_properties->set_pressed(true);
+ search_theme_items->set_pressed(false);
- get_ok_button()->set_disabled(root->get_first_child() == nullptr);
+ scope_combo->select(2); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated" //id5 "Search All"
+
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+ _update_results();
}
-void VisualScriptPropertySelector::create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text) {
- if (search_input.is_empty() || text.findn(search_input) != -1) {
- TreeItem *item = search_options->create_item(root);
- item->set_text(0, text);
- item->set_icon(0, vbc->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")));
- item->set_metadata(0, name);
- item->set_metadata(1, "action");
- item->set_selectable(0, true);
- item->set_collapsed(true);
- item->set_selectable(1, false);
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
+void VisualScriptPropertySelector::select_from_visual_script(const Ref<Script> &p_script, bool clear_text) {
+ set_title(TTR("Select from visual script"));
+ base_type = p_script->get_instance_base_type();
+ if (p_script == nullptr) {
+ base_script = "";
+ } else {
+ base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ }
+ type = Variant::NIL;
+ connecting = false;
+
+ if (clear_text) {
+ search_box->set_text(" ");
}
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(true);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(false);
+ search_signals->set_pressed(true);
+ search_constants->set_pressed(true);
+ search_properties->set_pressed(true);
+ search_theme_items->set_pressed(false);
+
+ scope_combo->select(2); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated" //id5 "Search All"
+
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+ _update_results();
+}
+
+void VisualScriptPropertySelector::show_window(float p_screen_ratio) {
+ popup_centered_ratio(p_screen_ratio);
}
-void VisualScriptPropertySelector::get_visual_node_names(const String &root_filter, const Set<String> &p_modifiers, bool &found, TreeItem *const root, LineEdit *const search_box) {
- Map<String, TreeItem *> path_cache;
+void VisualScriptPropertySelector::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "category"), PropertyInfo(Variant::BOOL, "connecting")));
+}
- List<String> fnodes;
- VisualScriptLanguage::singleton->get_registered_node_names(&fnodes);
+VisualScriptPropertySelector::VisualScriptPropertySelector() {
+ virtuals_only = false;
- for (const String &E : fnodes) {
- if (!E.begins_with(root_filter)) {
- continue;
- }
- Vector<String> path = E.split("/");
-
- // check if the name has the filter
- bool in_filter = false;
- Vector<String> tx_filters = search_box->get_text().split(" ");
- for (int i = 0; i < tx_filters.size(); i++) {
- if (tx_filters[i].is_empty()) {
- in_filter = true;
- } else {
- in_filter = false;
- }
- if (E.findn(tx_filters[i]) != -1) {
- in_filter = true;
- break;
- }
- }
- if (!in_filter) {
- continue;
- }
+ vbox = memnew(VBoxContainer);
+ add_child(vbox);
+
+ HBoxContainer *hbox = memnew(HBoxContainer);
+ hbox->set_alignment(hbox->ALIGNMENT_CENTER);
+ vbox->add_child(hbox);
+
+ case_sensitive_button = memnew(Button);
+ case_sensitive_button->set_flat(true);
+ case_sensitive_button->set_tooltip(TTR("Case Sensitive"));
+ case_sensitive_button->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ case_sensitive_button->set_toggle_mode(true);
+ case_sensitive_button->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(case_sensitive_button);
+
+ hierarchy_button = memnew(Button);
+ hierarchy_button->set_flat(true);
+ hierarchy_button->set_tooltip(TTR("Show Hierarchy"));
+ hierarchy_button->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ hierarchy_button->set_toggle_mode(true);
+ hierarchy_button->set_pressed(true);
+ hierarchy_button->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(hierarchy_button);
+
+ hbox->add_child(memnew(VSeparator));
+
+ search_visual_script_nodes = memnew(Button);
+ search_visual_script_nodes->set_flat(true);
+ search_visual_script_nodes->set_tooltip(TTR("Search Visual Script Nodes"));
+ search_visual_script_nodes->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_visual_script_nodes->set_toggle_mode(true);
+ search_visual_script_nodes->set_pressed(true);
+ search_visual_script_nodes->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_visual_script_nodes);
+
+ search_classes = memnew(Button);
+ search_classes->set_flat(true);
+ search_classes->set_tooltip(TTR("Search Classes"));
+ search_classes->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_classes->set_toggle_mode(true);
+ search_classes->set_pressed(true);
+ search_classes->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_classes);
+
+ search_operators = memnew(Button);
+ search_operators->set_flat(true);
+ search_operators->set_tooltip(TTR("Search Operators"));
+ search_operators->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_operators->set_toggle_mode(true);
+ search_operators->set_pressed(true);
+ search_operators->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_operators);
+
+ hbox->add_child(memnew(VSeparator));
+
+ search_methods = memnew(Button);
+ search_methods->set_flat(true);
+ search_methods->set_tooltip(TTR("Search Methods"));
+ search_methods->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_methods->set_toggle_mode(true);
+ search_methods->set_pressed(true);
+ search_methods->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_methods);
+
+ search_signals = memnew(Button);
+ search_signals->set_flat(true);
+ search_signals->set_tooltip(TTR("Search Signals"));
+ search_signals->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_signals->set_toggle_mode(true);
+ search_signals->set_pressed(true);
+ search_signals->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_signals);
+
+ search_constants = memnew(Button);
+ search_constants->set_flat(true);
+ search_constants->set_tooltip(TTR("Search Constants"));
+ search_constants->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_constants->set_toggle_mode(true);
+ search_constants->set_pressed(true);
+ search_constants->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_constants);
+
+ search_properties = memnew(Button);
+ search_properties->set_flat(true);
+ search_properties->set_tooltip(TTR("Search Properties"));
+ search_properties->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_properties->set_toggle_mode(true);
+ search_properties->set_pressed(true);
+ search_properties->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_properties);
+
+ search_theme_items = memnew(Button);
+ search_theme_items->set_flat(true);
+ search_theme_items->set_tooltip(TTR("Search Theme Items"));
+ search_theme_items->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_theme_items->set_toggle_mode(true);
+ search_theme_items->set_pressed(true);
+ search_theme_items->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_theme_items);
+
+ scope_combo = memnew(OptionButton);
+ scope_combo->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
+ scope_combo->set_tooltip(TTR("Select the search limits"));
+ scope_combo->set_stretch_ratio(0); // Fixed width.
+ scope_combo->add_item(TTR("Search Related"), SCOPE_RELATED);
+ scope_combo->add_separator();
+ scope_combo->add_item(TTR("Search Base"), SCOPE_BASE);
+ scope_combo->add_item(TTR("Search Inheriters"), SCOPE_INHERITERS);
+ scope_combo->add_item(TTR("Search Unrelated"), SCOPE_UNRELATED);
+ scope_combo->add_item(TTR("Search All"), SCOPE_ALL);
+ scope_combo->connect("item_selected", callable_mp(this, &VisualScriptPropertySelector::_update_results_i));
+ hbox->add_child(scope_combo);
- bool in_modifier = p_modifiers.is_empty();
- for (Set<String>::Element *F = p_modifiers.front(); F && in_modifier; F = F->next()) {
- if (E.findn(F->get()) != -1) {
- in_modifier = true;
- }
- }
- if (!in_modifier) {
- continue;
- }
+ search_box = memnew(LineEdit);
+ search_box->set_tooltip(TTR("Enter \" \" to show all filterd options\nEnter \".\" to show all filterd methods, operators and constructors\nUse CTRL_KEY to drop property setters"));
+ search_box->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
+ search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ search_box->connect("text_changed", callable_mp(this, &VisualScriptPropertySelector::_update_results_s));
+ search_box->connect("gui_input", callable_mp(this, &VisualScriptPropertySelector::_sbox_input));
+ register_text_enter(search_box);
+ vbox->add_child(search_box);
+
+ results_tree = memnew(Tree);
+ results_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ results_tree->set_hide_root(true);
+ results_tree->set_hide_folding(false);
+ results_tree->set_columns(2);
+ results_tree->set_column_title(0, TTR("Name"));
+ results_tree->set_column_clip_content(0, true);
+ results_tree->set_column_title(1, TTR("Member Type"));
+ results_tree->set_column_expand(1, false);
+ results_tree->set_column_custom_minimum_width(1, 150 * EDSCALE);
+ results_tree->set_column_clip_content(1, true);
+ results_tree->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
+ results_tree->set_select_mode(Tree::SELECT_ROW);
+ results_tree->connect("item_activated", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
+ results_tree->connect("item_selected", callable_mp(this, &VisualScriptPropertySelector::_item_selected));
+ vbox->add_child(results_tree);
- TreeItem *item = search_options->create_item(root);
- Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(E);
- Ref<VisualScriptOperator> vnode_operator = vnode;
- String type_name;
- if (vnode_operator.is_valid()) {
- String type;
- if (path.size() >= 2) {
- type = path[1];
- }
- type_name = type.capitalize() + " ";
+ help_bit = memnew(EditorHelpBit);
+ vbox->add_child(help_bit);
+ help_bit->connect("request_hide", callable_mp(this, &VisualScriptPropertySelector::_hide_requested));
+ get_ok_button()->set_text(TTR("Open"));
+ get_ok_button()->set_disabled(true);
+ set_hide_on_ok(false);
+}
+
+bool VisualScriptPropertySelector::SearchRunner::_is_class_disabled_by_feature_profile(const StringName &p_class) {
+ Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
+ if (profile.is_null()) {
+ return false;
+ }
+
+ StringName class_name = p_class;
+ while (class_name != StringName()) {
+ if (!ClassDB::class_exists(class_name)) {
+ return false;
}
- Ref<VisualScriptFunctionCall> vnode_function_call = vnode;
- if (vnode_function_call.is_valid()) {
- String basic_type = Variant::get_type_name(vnode_function_call->get_basic_type());
- type_name = basic_type.capitalize() + " ";
+
+ if (profile->is_class_disabled(class_name)) {
+ return true;
}
- Ref<VisualScriptConstructor> vnode_constructor = vnode;
- if (vnode_constructor.is_valid()) {
- type_name = "Construct ";
+ class_name = ClassDB::get_parent_class(class_name);
+ }
+
+ return false;
+}
+
+bool VisualScriptPropertySelector::SearchRunner::_is_class_disabled_by_scope(const StringName &p_class) {
+ bool is_base_script = false;
+ if (p_class == selector_ui->base_script) {
+ is_base_script = true;
+ }
+ bool is_base = false;
+ if (selector_ui->base_type == p_class) {
+ is_base = true;
+ }
+ bool is_parent = false;
+ if ((ClassDB::is_parent_class(selector_ui->base_type, p_class)) && !is_base) {
+ is_parent = true;
+ }
+
+ bool is_inheriter = false;
+ List<StringName> inheriters;
+ ClassDB::get_inheriters_from_class(selector_ui->base_type, &inheriters);
+ if (inheriters.find(p_class)) {
+ is_inheriter = true;
+ }
+
+ if (scope_flags & SCOPE_BASE) {
+ if (is_base_script || is_base || is_parent) {
+ return false;
}
- Ref<VisualScriptDeconstruct> vnode_deconstruct = vnode;
- if (vnode_deconstruct.is_valid()) {
- type_name = "Deconstruct ";
+ }
+ if (scope_flags & SCOPE_INHERITERS) {
+ if (is_base_script || is_base || is_inheriter) {
+ return false;
}
- Vector<String> desc = path[path.size() - 1].replace("(", " ").replace(")", " ").replace(",", " ").split(" ");
- for (int i = 0; i < desc.size(); i++) {
- desc.write[i] = desc[i].capitalize();
- if (desc[i].ends_with(",")) {
- desc.write[i] = desc[i].replace(",", ", ");
- }
+ }
+ // if (scope_flags & SCOPE_RELATED) {
+ // /* code */
+ // }
+ if (scope_flags & SCOPE_UNRELATED) {
+ if (!is_base_script && !is_base && !is_inheriter) {
+ return false;
}
-
- item->set_text(0, type_name + String("").join(desc));
- item->set_icon(0, vbc->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")));
- item->set_selectable(0, true);
- item->set_metadata(0, E);
- item->set_selectable(0, true);
- item->set_metadata(1, "visualscript");
- item->set_selectable(1, false);
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
}
+ return true;
}
-void VisualScriptPropertySelector::_confirmed() {
- TreeItem *ti = search_options->get_selected();
- if (!ti) {
- return;
+bool VisualScriptPropertySelector::SearchRunner::_slice() {
+ bool phase_done = false;
+ switch (phase) {
+ case PHASE_INIT:
+ phase_done = _phase_init();
+ break;
+ case PHASE_MATCH_CLASSES_INIT:
+ phase_done = _phase_match_classes_init();
+ break;
+ case PHASE_NODE_CLASSES_INIT:
+ phase_done = _phase_node_classes_init();
+ break;
+ case PHASE_NODE_CLASSES_BUILD:
+ phase_done = _phase_node_classes_build();
+ break;
+ case PHASE_MATCH_CLASSES:
+ phase_done = _phase_match_classes();
+ break;
+ case PHASE_CLASS_ITEMS_INIT:
+ phase_done = _phase_class_items_init();
+ break;
+ case PHASE_CLASS_ITEMS:
+ phase_done = _phase_class_items();
+ break;
+ case PHASE_MEMBER_ITEMS_INIT:
+ phase_done = _phase_member_items_init();
+ break;
+ case PHASE_MEMBER_ITEMS:
+ phase_done = _phase_member_items();
+ break;
+ case PHASE_SELECT_MATCH:
+ phase_done = _phase_select_match();
+ break;
+ case PHASE_MAX:
+ return true;
+ default:
+ WARN_PRINT("Invalid or unhandled phase in EditorHelpSearch::Runner, aborting search.");
+ return true;
+ };
+
+ if (phase_done) {
+ phase++;
}
- emit_signal(SNAME("selected"), ti->get_metadata(0), ti->get_metadata(1), ti->get_metadata(2));
- set_visible(false);
+ return false;
}
-void VisualScriptPropertySelector::_item_selected() {
- help_bit->set_text("");
-
- TreeItem *item = search_options->get_selected();
- if (!item) {
- return;
+bool VisualScriptPropertySelector::SearchRunner::_phase_init() {
+ search_flags = 0; // selector_ui->filter_combo->get_selected_id();
+ if (selector_ui->search_visual_script_nodes->is_pressed()) {
+ search_flags |= SEARCH_VISUAL_SCRIPT_NODES;
}
- String name = item->get_metadata(0);
-
- String class_type;
- if (type != Variant::NIL) {
- class_type = Variant::get_type_name(type);
-
- } else {
- class_type = base_type;
+ if (selector_ui->search_classes->is_pressed()) {
+ search_flags |= SEARCH_CLASSES;
}
+ // if (selector_ui->search_constructors->is_pressed()) {
+ search_flags |= SEARCH_CONSTRUCTORS;
+ // }
+ if (selector_ui->search_methods->is_pressed()) {
+ search_flags |= SEARCH_METHODS;
+ }
+ if (selector_ui->search_operators->is_pressed()) {
+ search_flags |= SEARCH_OPERATORS;
+ }
+ if (selector_ui->search_signals->is_pressed()) {
+ search_flags |= SEARCH_SIGNALS;
+ }
+ if (selector_ui->search_constants->is_pressed()) {
+ search_flags |= SEARCH_CONSTANTS;
+ }
+ if (selector_ui->search_properties->is_pressed()) {
+ search_flags |= SEARCH_PROPERTIES;
+ }
+ if (selector_ui->search_theme_items->is_pressed()) {
+ search_flags |= SEARCH_THEME_ITEMS;
+ }
+ if (selector_ui->case_sensitive_button->is_pressed()) {
+ search_flags |= SEARCH_CASE_SENSITIVE;
+ }
+ if (selector_ui->hierarchy_button->is_pressed()) {
+ search_flags |= SEARCH_SHOW_HIERARCHY;
+ }
+ scope_flags = selector_ui->scope_combo->get_selected_id();
- DocTools *dd = EditorHelp::get_doc_data();
- String text;
-
- String at_class = class_type;
+ return true;
+}
- while (!at_class.is_empty()) {
- Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class);
- if (E) {
- for (int i = 0; i < E->get().properties.size(); i++) {
- if (E->get().properties[i].name == name) {
- text = DTR(E->get().properties[i].description);
+bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes_init() {
+ combined_docs = EditorHelp::get_doc_data()->class_list;
+ matches.clear();
+ matched_item = nullptr;
+ match_highest_score = 0;
+
+ if (
+ (selector_ui->base_script.unquote() != "") &&
+ (selector_ui->base_script.unquote() != ".") &&
+ !combined_docs.has(selector_ui->base_script)) {
+ String file_path = "res://" + selector_ui->base_script.unquote(); // EditorHelp::get_doc_data().name to filepath
+ Ref<Script> script;
+ script = ResourceLoader::load(file_path);
+ if (!script.is_null()) {
+ DocData::ClassDoc class_doc = DocData::ClassDoc();
+
+ class_doc.name = selector_ui->base_script;
+
+ class_doc.inherits = script->get_instance_base_type();
+ class_doc.brief_description = ".vs files not suported by EditorHelp::get_doc_data()";
+ class_doc.description = "";
+
+ Object *obj = ObjectDB::get_instance(script->get_instance_id());
+ if (Object::cast_to<Script>(obj)) {
+ List<MethodInfo> methods;
+ Object::cast_to<Script>(obj)->get_script_method_list(&methods);
+ for (List<MethodInfo>::Element *M = methods.front(); M; M = M->next()) {
+ class_doc.methods.push_back(_get_method_doc(M->get()));
}
- }
- }
- at_class = ClassDB::get_parent_class_nocheck(at_class);
- }
- at_class = class_type;
+ List<MethodInfo> signals;
+ Object::cast_to<Script>(obj)->get_script_signal_list(&signals);
+ for (List<MethodInfo>::Element *S = signals.front(); S; S = S->next()) {
+ class_doc.signals.push_back(_get_method_doc(S->get()));
+ }
- while (!at_class.is_empty()) {
- Map<String, DocData::ClassDoc>::Element *C = dd->class_list.find(at_class);
- if (C) {
- for (int i = 0; i < C->get().methods.size(); i++) {
- if (C->get().methods[i].name == name) {
- text = DTR(C->get().methods[i].description);
+ List<PropertyInfo> propertys;
+ Object::cast_to<Script>(obj)->get_script_property_list(&propertys);
+ for (List<PropertyInfo>::Element *P = propertys.front(); P; P = P->next()) {
+ DocData::PropertyDoc pd = DocData::PropertyDoc();
+ pd.name = P->get().name;
+ pd.type = Variant::get_type_name(P->get().type);
+ class_doc.properties.push_back(pd);
}
}
+ combined_docs.insert(class_doc.name, class_doc);
}
+ }
+ iterator_doc = combined_docs.front();
+ return true;
+}
+
+bool VisualScriptPropertySelector::SearchRunner::_phase_node_classes_init() {
+ VisualScriptLanguage::singleton->get_registered_node_names(&vs_nodes);
+ _add_class_doc("functions", "", "");
+ _add_class_doc("operators", "", "");
+ return true;
+}
- at_class = ClassDB::get_parent_class_nocheck(at_class);
+bool VisualScriptPropertySelector::SearchRunner::_phase_node_classes_build() {
+ if (vs_nodes.is_empty()) {
+ return true;
}
- Vector<String> functions = name.rsplit("/", false);
- at_class = functions.size() > 3 ? functions[functions.size() - 2] : class_type;
- Map<String, DocData::ClassDoc>::Element *T = dd->class_list.find(at_class);
- if (T) {
- for (int i = 0; i < T->get().methods.size(); i++) {
- if (T->get().methods[i].name == functions[functions.size() - 1]) {
- text = DTR(T->get().methods[i].description);
+ String registerd_node_name = vs_nodes[0];
+ vs_nodes.pop_front();
+
+ Vector<String> path = registerd_node_name.split("/");
+ if (path[0] == "constants") {
+ _add_class_doc(registerd_node_name, "", "constants");
+ } else if (path[0] == "custom") {
+ _add_class_doc(registerd_node_name, "", "custom");
+ } else if (path[0] == "data") {
+ _add_class_doc(registerd_node_name, "", "data");
+ } else if (path[0] == "flow_control") {
+ _add_class_doc(registerd_node_name, "", "flow_control");
+ } else if (path[0] == "functions") {
+ if (path[1] == "built_in") {
+ _add_class_doc(registerd_node_name, "functions", "built_in");
+ } else if (path[1] == "by_type") {
+ if (search_flags & SEARCH_CLASSES) {
+ _add_class_doc(registerd_node_name, path[2], "by_type_class");
}
+ } else if (path[1] == "constructors") {
+ if (search_flags & SEARCH_CLASSES) {
+ _add_class_doc(registerd_node_name, path[2].substr(0, path[2].find_char('(')), "constructors_class");
+ }
+ } else if (path[1] == "deconstruct") {
+ _add_class_doc(registerd_node_name, "", "deconstruct");
+ } else if (path[1] == "wait") {
+ _add_class_doc(registerd_node_name, "functions", "yield");
+ } else {
+ _add_class_doc(registerd_node_name, "functions", "");
+ }
+ } else if (path[0] == "index") {
+ _add_class_doc(registerd_node_name, "", "index");
+ } else if (path[0] == "operators") {
+ if (path[1] == "bitwise") {
+ _add_class_doc(registerd_node_name, "operators", "bitwise");
+ } else if (path[1] == "compare") {
+ _add_class_doc(registerd_node_name, "operators", "compare");
+ } else if (path[1] == "logic") {
+ _add_class_doc(registerd_node_name, "operators", "logic");
+ } else if (path[1] == "math") {
+ _add_class_doc(registerd_node_name, "operators", "math");
+ } else {
+ _add_class_doc(registerd_node_name, "operators", "");
}
}
+ return false;
+}
- List<String> *names = memnew(List<String>);
- VisualScriptLanguage::singleton->get_registered_node_names(names);
- if (names->find(name) != nullptr) {
- Ref<VisualScriptOperator> operator_node = VisualScriptLanguage::singleton->create_node_from_name(name);
- if (operator_node.is_valid()) {
- Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(operator_node->get_class_name());
- if (F) {
- text = Variant::get_operator_name(operator_node->get_operator());
+bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes() {
+ DocData::ClassDoc &class_doc = iterator_doc->value();
+ if (
+ (!_is_class_disabled_by_feature_profile(class_doc.name) && !_is_class_disabled_by_scope(class_doc.name)) ||
+ _match_visual_script(class_doc)) {
+ if (class_doc.inherits == "VisualScriptCustomNode") {
+ class_doc.script_path = "res://" + class_doc.name.unquote();
+ Ref<Script> script = ResourceLoader::load(class_doc.script_path);
+ Ref<VisualScriptCustomNode> vsn;
+ vsn.instantiate();
+ vsn->set_script(script);
+ class_doc.name = vsn->get_caption();
+ if (combined_docs.has(vsn->get_category())) {
+ class_doc.inherits = vsn->get_category();
+ } else if (combined_docs.has("VisualScriptNode/" + vsn->get_category())) {
+ class_doc.inherits = "VisualScriptNode/" + vsn->get_category();
+ } else if (combined_docs.has("VisualScriptCustomNode/" + vsn->get_category())) {
+ class_doc.inherits = "VisualScriptCustomNode/" + vsn->get_category();
+ } else {
+ class_doc.inherits = "";
}
+ class_doc.category = "VisualScriptCustomNode/" + vsn->get_category();
+ class_doc.brief_description = "";
+ class_doc.constructors.clear();
+ class_doc.methods.clear();
+ class_doc.operators.clear();
+ class_doc.signals.clear();
+ class_doc.constants.clear();
+ class_doc.enums.clear();
+ class_doc.properties.clear();
+ class_doc.theme_properties.clear();
}
- Ref<VisualScriptTypeCast> typecast_node = VisualScriptLanguage::singleton->create_node_from_name(name);
- if (typecast_node.is_valid()) {
- Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(typecast_node->get_class_name());
- if (F) {
- text = DTR(F->get().description);
+
+ matches[class_doc.name] = ClassMatch();
+ ClassMatch &match = matches[class_doc.name];
+
+ match.category = class_doc.category;
+ match.doc = &class_doc;
+ // Match class name.
+ if (search_flags & SEARCH_CLASSES || _match_visual_script(class_doc)) {
+ if (term == "") {
+ match.name = !_match_is_hidden(class_doc);
+ } else {
+ match.name = _match_string(term, class_doc.name);
}
+ // match.name = term == "" || _match_string(term, class_doc.name);
}
- Ref<VisualScriptBuiltinFunc> builtin_node = VisualScriptLanguage::singleton->create_node_from_name(name);
- if (builtin_node.is_valid()) {
- Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(builtin_node->get_class_name());
- if (F) {
- for (int i = 0; i < F->get().constants.size(); i++) {
- if (F->get().constants[i].value.to_int() == int(builtin_node->get_func())) {
- text = DTR(F->get().constants[i].description);
+ // Match members if the term is long enough.
+ if (term.length() >= 0) {
+ if (search_flags & SEARCH_CONSTRUCTORS) {
+ for (int i = 0; i < class_doc.constructors.size(); i++) {
+ String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.constructors[i].name : class_doc.constructors[i].name.to_lower();
+ if (method_name.find(term) > -1 ||
+ term == " " ||
+ (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
+ (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+ (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
+ match.constructors.push_back(const_cast<DocData::MethodDoc *>(&class_doc.constructors[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_METHODS) {
+ for (int i = 0; i < class_doc.methods.size(); i++) {
+ String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.methods[i].name : class_doc.methods[i].name.to_lower();
+ if (method_name.find(term) > -1 ||
+ term == " " ||
+ (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
+ (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+ (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
+ match.methods.push_back(const_cast<DocData::MethodDoc *>(&class_doc.methods[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_OPERATORS) {
+ for (int i = 0; i < class_doc.operators.size(); i++) {
+ String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.operators[i].name : class_doc.operators[i].name.to_lower();
+ if (method_name.find(term) > -1 ||
+ term == " " ||
+ (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
+ (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+ (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
+ match.operators.push_back(const_cast<DocData::MethodDoc *>(&class_doc.operators[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_SIGNALS) {
+ for (int i = 0; i < class_doc.signals.size(); i++) {
+ if (_match_string(term, class_doc.signals[i].name) ||
+ term == " ") {
+ match.signals.push_back(const_cast<DocData::MethodDoc *>(&class_doc.signals[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_CONSTANTS) {
+ for (int i = 0; i < class_doc.constants.size(); i++) {
+ if (_match_string(term, class_doc.constants[i].name) ||
+ term == " ") {
+ match.constants.push_back(const_cast<DocData::ConstantDoc *>(&class_doc.constants[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_PROPERTIES) {
+ for (int i = 0; i < class_doc.properties.size(); i++) {
+ if (_match_string(term, class_doc.properties[i].name) ||
+ term == " " ||
+ _match_string(term, class_doc.properties[i].getter) ||
+ _match_string(term, class_doc.properties[i].setter)) {
+ match.properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.properties[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_THEME_ITEMS) {
+ for (int i = 0; i < class_doc.theme_properties.size(); i++) {
+ if (_match_string(term, class_doc.theme_properties[i].name) ||
+ term == " ") {
+ match.theme_properties.push_back(const_cast<DocData::ThemeItemDoc *>(&class_doc.theme_properties[i]));
}
}
}
}
}
- memdelete(names);
+ iterator_doc = iterator_doc->next();
+ return !iterator_doc;
+}
- if (text.is_empty()) {
- return;
- }
+bool VisualScriptPropertySelector::SearchRunner::_phase_class_items_init() {
+ results_tree->clear();
+ iterator_match = matches.front();
- help_bit->set_text(text);
-}
+ root_item = results_tree->create_item();
+ class_items.clear();
-void VisualScriptPropertySelector::_hide_requested() {
- _cancel_pressed(); // From AcceptDialog.
+ return true;
}
-void VisualScriptPropertySelector::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- connect("confirmed", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
+bool VisualScriptPropertySelector::SearchRunner::_phase_class_items() {
+ if (!iterator_match) {
+ return true;
}
-}
-void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, const bool p_virtuals_only, const bool p_connecting, bool clear_text) {
- base_type = p_base;
- selected = p_current;
- type = Variant::NIL;
- properties = false;
- instance = nullptr;
- virtuals_only = p_virtuals_only;
+ ClassMatch &match = iterator_match->value();
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
+ if (search_flags & SEARCH_SHOW_HIERARCHY) {
+ if (match.required()) {
+ _create_class_hierarchy(match);
+ }
} else {
- search_box->select_all();
+ if (match.name) {
+ _create_class_item(root_item, match.doc, true);
+ }
}
- search_box->grab_focus();
- connecting = p_connecting;
- _update_search();
+ iterator_match = iterator_match->next();
+ return !iterator_match;
}
-void VisualScriptPropertySelector::set_type_filter(const Vector<Variant::Type> &p_type_filter) {
- type_filter = p_type_filter;
+bool VisualScriptPropertySelector::SearchRunner::_phase_member_items_init() {
+ iterator_match = matches.front();
+
+ return true;
}
-void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only, bool p_seq_connect, const bool p_connecting, bool clear_text) {
- base_type = p_base;
- selected = p_current;
- type = Variant::NIL;
- properties = true;
- visual_script_generic = false;
- instance = nullptr;
- virtuals_only = p_virtuals_only;
+bool VisualScriptPropertySelector::SearchRunner::_phase_member_items() {
+ if (!iterator_match) {
+ return true;
+ }
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
+ ClassMatch &match = iterator_match->value();
+
+ TreeItem *parent = (search_flags & SEARCH_SHOW_HIERARCHY) ? class_items[match.doc->name] : root_item;
+ bool constructor_created = false;
+ for (int i = 0; i < match.methods.size(); i++) {
+ String text = match.methods[i]->name;
+ if (!constructor_created) {
+ if (match.doc->name == match.methods[i]->name) {
+ text += " " + TTR("(constructors)");
+ constructor_created = true;
+ }
+ } else {
+ if (match.doc->name == match.methods[i]->name) {
+ continue;
+ }
+ }
+ _create_method_item(parent, match.doc, text, match.methods[i]);
+ }
+ for (int i = 0; i < match.signals.size(); i++) {
+ _create_signal_item(parent, match.doc, match.signals[i]);
+ }
+ for (int i = 0; i < match.constants.size(); i++) {
+ _create_constant_item(parent, match.doc, match.constants[i]);
+ }
+ for (int i = 0; i < match.properties.size(); i++) {
+ _create_property_item(parent, match.doc, match.properties[i]);
+ }
+ for (int i = 0; i < match.theme_properties.size(); i++) {
+ _create_theme_property_item(parent, match.doc, match.theme_properties[i]);
+ }
+
+ iterator_match = iterator_match->next();
+ return !iterator_match;
+}
+
+bool VisualScriptPropertySelector::SearchRunner::_phase_select_match() {
+ if (matched_item) {
+ matched_item->select(0);
+ }
+ return true;
+}
+
+bool VisualScriptPropertySelector::SearchRunner::_match_string(const String &p_term, const String &p_string) const {
+ if (search_flags & SEARCH_CASE_SENSITIVE) {
+ return p_string.find(p_term) > -1;
} else {
- search_box->select_all();
+ return p_string.findn(p_term) > -1;
+ }
+}
+
+bool VisualScriptPropertySelector::SearchRunner::_match_visual_script(DocData::ClassDoc &class_doc) {
+ if (class_doc.category.ends_with("_class")) {
+ if (class_doc.category.begins_with("VisualScript") && search_flags & SEARCH_CLASSES) {
+ if (matches.has(class_doc.inherits)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (class_doc.category.begins_with("VisualScript") && search_flags & SEARCH_VISUAL_SCRIPT_NODES) {
+ return true;
+ }
+ if (class_doc.name.begins_with("operators") && search_flags & SEARCH_OPERATORS) {
+ return true;
+ }
+ if (class_doc.category.begins_with("VisualScriptNode/deconstruct")) {
+ if (class_doc.name.find(selector_ui->base_type, 0) > -1) {
+ return true;
+ }
}
- search_box->grab_focus();
- seq_connect = p_seq_connect;
- connecting = p_connecting;
- _update_search();
+ return false;
}
-void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const String &p_current, const bool p_connecting, bool clear_text) {
- ERR_FAIL_COND(p_script.is_null());
+bool VisualScriptPropertySelector::SearchRunner::_match_is_hidden(DocData::ClassDoc &class_doc) {
+ if (class_doc.category.begins_with("VisualScript")) {
+ if (class_doc.name.begins_with("flow_control")) {
+ return false;
+ } else if (class_doc.name.begins_with("operators")) {
+ return !(search_flags & SEARCH_OPERATORS);
+ } else if (class_doc.name.begins_with("functions/built_in/print")) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
- base_type = p_script->get_instance_base_type();
- selected = p_current;
- type = Variant::NIL;
- script = p_script->get_instance_id();
- properties = true;
- visual_script_generic = false;
- instance = nullptr;
- virtuals_only = false;
+void VisualScriptPropertySelector::SearchRunner::_match_item(TreeItem *p_item, const String &p_text) {
+ float inverse_length = 1.f / float(p_text.length());
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
- } else {
- search_box->select_all();
+ // Favor types where search term is a substring close to the start of the type.
+ float w = 0.5f;
+ int pos = p_text.findn(term);
+ float score = (pos > -1) ? 1.0f - w * MIN(1, 3 * pos * inverse_length) : MAX(0.f, .9f - w);
+
+ // Favor shorter items: they resemble the search term more.
+ w = 0.1f;
+ score *= (1 - w) + w * (term.length() * inverse_length);
+
+ if (match_highest_score == 0 || score > match_highest_score) {
+ matched_item = p_item;
+ match_highest_score = score;
}
- search_box->grab_focus();
- seq_connect = false;
- connecting = p_connecting;
+}
- _update_search();
+void VisualScriptPropertySelector::SearchRunner::_add_class_doc(String class_name, String inherits, String category) {
+ DocData::ClassDoc class_doc = DocData::ClassDoc();
+ class_doc.name = class_name;
+ class_doc.inherits = inherits;
+ class_doc.category = "VisualScriptNode/" + category;
+ class_doc.brief_description = category;
+ combined_docs.insert(class_doc.name, class_doc);
}
-void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const String &p_current, const bool p_connecting, bool clear_text) {
- ERR_FAIL_COND(p_type == Variant::NIL);
- base_type = "";
- selected = p_current;
- type = p_type;
- properties = true;
- visual_script_generic = false;
- instance = nullptr;
- virtuals_only = false;
+DocData::MethodDoc VisualScriptPropertySelector::SearchRunner::_get_method_doc(MethodInfo method_info) {
+ DocData::MethodDoc method_doc = DocData::MethodDoc();
+ method_doc.name = method_info.name;
+ method_doc.return_type = Variant::get_type_name(method_info.return_val.type);
+ method_doc.description = "No description available";
+ for (List<PropertyInfo>::Element *P = method_info.arguments.front(); P; P = P->next()) {
+ DocData::ArgumentDoc argument_doc = DocData::ArgumentDoc();
+ argument_doc.name = P->get().name;
+ argument_doc.type = Variant::get_type_name(P->get().type);
+ method_doc.arguments.push_back(argument_doc);
+ }
+ return method_doc;
+}
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
- } else {
- search_box->select_all();
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_class_hierarchy(const ClassMatch &p_match) {
+ if (class_items.has(p_match.doc->name)) {
+ return class_items[p_match.doc->name];
+ }
+
+ // Ensure parent nodes are created first.
+ TreeItem *parent = root_item;
+ if (p_match.doc->inherits != "") {
+ if (class_items.has(p_match.doc->inherits)) {
+ parent = class_items[p_match.doc->inherits];
+ } else if (matches.has(p_match.doc->inherits)) {
+ ClassMatch &base_match = matches[p_match.doc->inherits];
+ parent = _create_class_hierarchy(base_match);
+ }
}
- search_box->grab_focus();
- seq_connect = false;
- connecting = p_connecting;
- _update_search();
+ TreeItem *class_item = _create_class_item(parent, p_match.doc, !p_match.name);
+ class_items[p_match.doc->name] = class_item;
+ return class_item;
}
-void VisualScriptPropertySelector::select_from_action(const String &p_type, const String &p_current, const bool p_connecting, bool clear_text) {
- base_type = p_type;
- selected = p_current;
- type = Variant::NIL;
- properties = false;
- visual_script_generic = false;
- instance = nullptr;
- virtuals_only = false;
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
+ Ref<Texture2D> icon = empty_icon;
+ String text_0 = p_doc->name;
+ String text_1 = "Class";
+
+ String what = "Class";
+ String details = p_doc->name;
+ if (p_doc->category.begins_with("VisualScriptCustomNode/")) {
+ Vector<String> path = p_doc->name.split("/");
+ icon = ui_service->get_theme_icon("VisualScript", "EditorIcons");
+ text_0 = path[path.size() - 1];
+ text_1 = "VisualScriptCustomNode";
+ what = "VisualScriptCustomNode";
+ details = "CustomNode";
+ } else if (p_doc->category.begins_with("VisualScriptNode/")) {
+ Vector<String> path = p_doc->name.split("/");
+ icon = ui_service->get_theme_icon("VisualScript", "EditorIcons");
+ text_0 = path[path.size() - 1];
+ if (p_doc->category.begins_with("VisualScriptNode/deconstruct")) {
+ text_0 = "deconstruct " + text_0;
+ }
+ text_1 = "VisualScriptNode";
+ what = "VisualScriptNode";
+ details = p_doc->name;
+
+ if (path.size() == 1) {
+ if (path[0] == "functions" || path[0] == "operators") {
+ text_1 = "VisualScript";
+ p_gray = true;
+ what = "no_result";
+ details = "";
+ }
+ }
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
} else {
- search_box->select_all();
+ if (p_doc->name.is_quoted()) {
+ text_0 = p_doc->name.unquote().get_file();
+ if (ui_service->has_theme_icon(p_doc->inherits, "EditorIcons")) {
+ icon = ui_service->get_theme_icon(p_doc->inherits, "EditorIcons");
+ }
+ } else if (ui_service->has_theme_icon(p_doc->name, "EditorIcons")) {
+ icon = ui_service->get_theme_icon(p_doc->name, "EditorIcons");
+ } else if (ClassDB::class_exists(p_doc->name) && ClassDB::is_parent_class(p_doc->name, "Object")) {
+ icon = ui_service->get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
+ }
}
- search_box->grab_focus();
- seq_connect = true;
- connecting = p_connecting;
+ String tooltip = p_doc->brief_description.strip_edges();
+
+ TreeItem *item = results_tree->create_item(p_parent);
+ item->set_icon(0, icon);
+ item->set_text(0, text_0);
+ item->set_text(1, TTR(text_1));
+ item->set_tooltip(0, tooltip);
+ item->set_tooltip(1, tooltip);
+ item->set_metadata(0, details);
+ item->set_metadata(1, what);
+ if (p_gray) {
+ item->set_custom_color(0, disabled_color);
+ item->set_custom_color(1, disabled_color);
+ }
+
+ _match_item(item, p_doc->name);
- _update_search();
+ return item;
}
-void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const String &p_current, const bool p_connecting, const String &p_basetype, bool clear_text) {
- base_type = p_basetype;
- selected = p_current;
- type = Variant::NIL;
- properties = true;
- visual_script_generic = false;
- instance = p_instance;
- virtuals_only = false;
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) {
+ String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
+ for (int i = 0; i < p_doc->arguments.size(); i++) {
+ const DocData::ArgumentDoc &arg = p_doc->arguments[i];
+ tooltip += arg.type + " " + arg.name;
+ if (arg.default_value != "") {
+ tooltip += " = " + arg.default_value;
+ }
+ if (i < p_doc->arguments.size() - 1) {
+ tooltip += ", ";
+ }
+ }
+ tooltip += ")";
+ return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, p_text, TTRC("Method"), "method", tooltip, p_doc->description);
+}
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
- } else {
- search_box->select_all();
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) {
+ String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
+ for (int i = 0; i < p_doc->arguments.size(); i++) {
+ const DocData::ArgumentDoc &arg = p_doc->arguments[i];
+ tooltip += arg.type + " " + arg.name;
+ if (arg.default_value != "") {
+ tooltip += " = " + arg.default_value;
+ }
+ if (i < p_doc->arguments.size() - 1) {
+ tooltip += ", ";
+ }
}
- search_box->grab_focus();
- seq_connect = false;
- connecting = p_connecting;
+ tooltip += ")";
+ return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, p_doc->name, TTRC("Signal"), "signal", tooltip, p_doc->description);
+}
- _update_search();
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc) {
+ String tooltip = p_class_doc->name + "." + p_doc->name;
+ return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, p_doc->name, TTRC("Constant"), "constant", tooltip, p_doc->description);
}
-void VisualScriptPropertySelector::select_from_visual_script(const String &p_base, const bool p_connecting, bool clear_text) {
- base_type = p_base;
- selected = "";
- type = Variant::NIL;
- properties = true;
- visual_script_generic = true;
- instance = nullptr;
- virtuals_only = false;
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc) {
+ String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
+ tooltip += "\n " + p_class_doc->name + "." + p_doc->setter + "(value) setter";
+ tooltip += "\n " + p_class_doc->name + "." + p_doc->getter + "() getter";
+ return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_doc->name, p_doc->name, TTRC("Property"), "property", tooltip, p_doc->description);
+}
+
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc) {
+ String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
+ return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_doc->name, p_doc->name, TTRC("Theme Property"), "theme_item", tooltip, p_doc->description);
+}
+
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_description) {
+ Ref<Texture2D> icon;
+ String text;
+ if (search_flags & SEARCH_SHOW_HIERARCHY) {
+ icon = ui_service->get_theme_icon(p_icon, SNAME("EditorIcons"));
+ text = p_text;
} else {
- search_box->select_all();
+ icon = ui_service->get_theme_icon(p_icon, SNAME("EditorIcons"));
+ text = p_class_name + "." + p_text;
}
- search_box->grab_focus();
- connecting = p_connecting;
- _update_search();
-}
+ TreeItem *item = results_tree->create_item(p_parent);
+ item->set_icon(0, icon);
+ item->set_text(0, text);
+ item->set_text(1, TTRGET(p_type));
+ item->set_tooltip(0, p_tooltip);
+ item->set_tooltip(1, p_tooltip);
+ item->set_metadata(0, p_class_name + ":" + p_name);
+ item->set_metadata(1, "class_" + p_metatype);
+ item->set_meta("description", p_description);
-void VisualScriptPropertySelector::show_window(float p_screen_ratio) {
- popup_centered_ratio(p_screen_ratio);
+ _match_item(item, p_name);
+
+ return item;
}
-void VisualScriptPropertySelector::_bind_methods() {
- ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "category"), PropertyInfo(Variant::BOOL, "connecting")));
+bool VisualScriptPropertySelector::SearchRunner::work(uint64_t slot) {
+ // Return true when the search has been completed, otherwise false.
+ const uint64_t until = OS::get_singleton()->get_ticks_usec() + slot;
+ while (!_slice()) {
+ if (OS::get_singleton()->get_ticks_usec() > until) {
+ return false;
+ }
+ }
+ return true;
}
-VisualScriptPropertySelector::VisualScriptPropertySelector() {
- vbc = memnew(VBoxContainer);
- add_child(vbc);
- //set_child_rect(vbc);
- search_box = memnew(LineEdit);
- vbc->add_margin_child(TTR("Search:"), search_box);
- search_box->connect("text_changed", callable_mp(this, &VisualScriptPropertySelector::_text_changed));
- search_box->connect("gui_input", callable_mp(this, &VisualScriptPropertySelector::_sbox_input));
- search_options = memnew(Tree);
- vbc->add_margin_child(TTR("Matches:"), search_options, true);
- get_ok_button()->set_text(TTR("Open"));
- get_ok_button()->set_disabled(true);
- register_text_enter(search_box);
- set_hide_on_ok(false);
- search_options->connect("item_activated", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
- search_options->connect("cell_selected", callable_mp(this, &VisualScriptPropertySelector::_item_selected));
- search_options->set_hide_root(true);
- search_options->set_hide_folding(true);
- virtuals_only = false;
- seq_connect = false;
- help_bit = memnew(EditorHelpBit);
- vbc->add_margin_child(TTR("Description:"), help_bit);
- help_bit->connect("request_hide", callable_mp(this, &VisualScriptPropertySelector::_hide_requested));
- search_options->set_columns(3);
- search_options->set_column_expand(1, false);
- search_options->set_column_expand(2, false);
+VisualScriptPropertySelector::SearchRunner::SearchRunner(VisualScriptPropertySelector *p_selector_ui, Tree *p_results_tree) :
+ selector_ui(p_selector_ui),
+ ui_service(p_selector_ui->vbox),
+ results_tree(p_results_tree),
+ term(p_selector_ui->search_box->get_text()),
+ empty_icon(ui_service->get_theme_icon(SNAME("ArrowRight"), SNAME("EditorIcons"))),
+ disabled_color(ui_service->get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))) {
}
diff --git a/modules/visual_script/editor/visual_script_property_selector.h b/modules/visual_script/editor/visual_script_property_selector.h
index 9e065548a0..3970c4473e 100644
--- a/modules/visual_script/editor/visual_script_property_selector.h
+++ b/modules/visual_script/editor/visual_script_property_selector.h
@@ -31,6 +31,7 @@
#ifndef VISUALSCRIPT_PROPERTYSELECTOR_H
#define VISUALSCRIPT_PROPERTYSELECTOR_H
+#include "../visual_script.h"
#include "editor/editor_help.h"
#include "editor/property_editor.h"
#include "scene/gui/rich_text_label.h"
@@ -38,15 +39,56 @@
class VisualScriptPropertySelector : public ConfirmationDialog {
GDCLASS(VisualScriptPropertySelector, ConfirmationDialog);
+ enum SearchFlags {
+ SEARCH_CLASSES = 1 << 0,
+ SEARCH_CONSTRUCTORS = 1 << 1,
+ SEARCH_METHODS = 1 << 2,
+ SEARCH_OPERATORS = 1 << 3,
+ SEARCH_SIGNALS = 1 << 4,
+ SEARCH_CONSTANTS = 1 << 5,
+ SEARCH_PROPERTIES = 1 << 6,
+ SEARCH_THEME_ITEMS = 1 << 7,
+ SEARCH_VISUAL_SCRIPT_NODES = 1 << 8,
+ SEARCH_ALL = SEARCH_CLASSES | SEARCH_CONSTRUCTORS | SEARCH_METHODS | SEARCH_OPERATORS | SEARCH_SIGNALS | SEARCH_CONSTANTS | SEARCH_PROPERTIES | SEARCH_THEME_ITEMS,
+ SEARCH_CASE_SENSITIVE = 1 << 29,
+ SEARCH_SHOW_HIERARCHY = 1 << 30,
+ };
+
+ enum ScopeFlags {
+ SCOPE_BASE = 1 << 0,
+ SCOPE_INHERITERS = 1 << 1,
+ SCOPE_UNRELATED = 1 << 2,
+ SCOPE_RELATED = SCOPE_BASE | SCOPE_INHERITERS,
+ SCOPE_ALL = SCOPE_BASE | SCOPE_INHERITERS | SCOPE_UNRELATED
+ };
+
LineEdit *search_box;
- Tree *search_options;
- void _text_changed(const String &p_newtext);
- void _sbox_input(const Ref<InputEvent> &p_ie);
- void _update_search();
+ Button *case_sensitive_button;
+ Button *hierarchy_button;
+
+ Button *search_visual_script_nodes;
+ Button *search_classes;
+ Button *search_operators;
+
+ Button *search_methods;
+ Button *search_signals;
+ Button *search_constants;
+ Button *search_properties;
+ Button *search_theme_items;
- void create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text);
- void get_visual_node_names(const String &root_filter, const Set<String> &p_modifiers, bool &found, TreeItem *const root, LineEdit *const search_box);
+ OptionButton *scope_combo;
+ Tree *results_tree;
+
+ class SearchRunner;
+ Ref<SearchRunner> search_runner;
+
+ void _update_icons();
+
+ void _sbox_input(const Ref<InputEvent> &p_ie);
+ void _update_results_i(int p_int);
+ void _update_results_s(String p_string);
+ void _update_results();
void _confirmed();
void _item_selected();
@@ -60,32 +102,118 @@ class VisualScriptPropertySelector : public ConfirmationDialog {
String selected;
Variant::Type type;
String base_type;
+ String base_script;
ObjectID script;
Object *instance;
bool virtuals_only;
- bool seq_connect;
- VBoxContainer *vbc;
-
- Vector<Variant::Type> type_filter;
+ VBoxContainer *vbox;
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- void select_method_from_base_type(const String &p_base, const String &p_current = "", const bool p_virtuals_only = false, const bool p_connecting = true, bool clear_text = true);
- void select_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false, bool p_seq_connect = false, const bool p_connecting = true, bool clear_text = true);
- void select_from_script(const Ref<Script> &p_script, const String &p_current = "", const bool p_connecting = true, bool clear_text = true);
- void select_from_basic_type(Variant::Type p_type, const String &p_current = "", const bool p_connecting = true, bool clear_text = true);
- void select_from_action(const String &p_type, const String &p_current = "", const bool p_connecting = true, bool clear_text = true);
- void select_from_instance(Object *p_instance, const String &p_current = "", const bool p_connecting = true, const String &p_basetype = "", bool clear_text = true);
- void select_from_visual_script(const String &p_base, const bool p_connecting = true, bool clear_text = true);
+ void select_method_from_base_type(const String &p_base, const bool p_virtuals_only = false, const bool p_connecting = true, bool clear_text = true);
+ void select_from_base_type(const String &p_base, const String &p_base_script = "", bool p_virtuals_only = false, const bool p_connecting = true, bool clear_text = true);
+ void select_from_script(const Ref<Script> &p_script, const bool p_connecting = true, bool clear_text = true);
+ void select_from_basic_type(Variant::Type p_type, const bool p_connecting = true, bool clear_text = true);
+ void select_from_action(const String &p_type, const bool p_connecting = true, bool clear_text = true);
+ void select_from_instance(Object *p_instance, const bool p_connecting = true, bool clear_text = true);
+ void select_from_visual_script(const Ref<Script> &p_script, bool clear_text = true);
void show_window(float p_screen_ratio);
- void set_type_filter(const Vector<Variant::Type> &p_type_filter);
-
VisualScriptPropertySelector();
};
+class VisualScriptPropertySelector::SearchRunner : public RefCounted {
+ enum Phase {
+ PHASE_INIT,
+ PHASE_MATCH_CLASSES_INIT,
+ PHASE_NODE_CLASSES_INIT,
+ PHASE_NODE_CLASSES_BUILD,
+ PHASE_MATCH_CLASSES,
+ PHASE_CLASS_ITEMS_INIT,
+ PHASE_CLASS_ITEMS,
+ PHASE_MEMBER_ITEMS_INIT,
+ PHASE_MEMBER_ITEMS,
+ PHASE_SELECT_MATCH,
+ PHASE_MAX
+ };
+ int phase = 0;
+
+ struct ClassMatch {
+ DocData::ClassDoc *doc;
+ bool name = false;
+ String category = "";
+ Vector<DocData::MethodDoc *> constructors;
+ Vector<DocData::MethodDoc *> methods;
+ Vector<DocData::MethodDoc *> operators;
+ Vector<DocData::MethodDoc *> signals;
+ Vector<DocData::ConstantDoc *> constants;
+ Vector<DocData::PropertyDoc *> properties;
+ Vector<DocData::ThemeItemDoc *> theme_properties;
+
+ bool required() {
+ return name || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size();
+ }
+ };
+
+ VisualScriptPropertySelector *selector_ui;
+ Control *ui_service;
+ Tree *results_tree;
+ String term;
+ int search_flags;
+ int scope_flags;
+
+ Ref<Texture2D> empty_icon;
+ Color disabled_color;
+
+ Map<String, DocData::ClassDoc>::Element *iterator_doc = nullptr;
+ Map<String, ClassMatch> matches;
+ Map<String, ClassMatch>::Element *iterator_match = nullptr;
+ TreeItem *root_item = nullptr;
+ Map<String, TreeItem *> class_items;
+ TreeItem *matched_item = nullptr;
+ float match_highest_score = 0;
+
+ Map<String, DocData::ClassDoc> combined_docs;
+ List<String> vs_nodes;
+
+ bool _is_class_disabled_by_feature_profile(const StringName &p_class);
+ bool _is_class_disabled_by_scope(const StringName &p_class);
+
+ bool _slice();
+ bool _phase_init();
+ bool _phase_match_classes_init();
+ bool _phase_node_classes_init();
+ bool _phase_node_classes_build();
+ bool _phase_match_classes();
+ bool _phase_class_items_init();
+ bool _phase_class_items();
+ bool _phase_member_items_init();
+ bool _phase_member_items();
+ bool _phase_select_match();
+
+ bool _match_string(const String &p_term, const String &p_string) const;
+ bool _match_visual_script(DocData::ClassDoc &class_doc);
+ bool _match_is_hidden(DocData::ClassDoc &class_doc);
+ void _match_item(TreeItem *p_item, const String &p_text);
+ void _add_class_doc(String class_name, String inherits, String category);
+ DocData::MethodDoc _get_method_doc(MethodInfo method_info);
+ TreeItem *_create_class_hierarchy(const ClassMatch &p_match);
+ TreeItem *_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray);
+ TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc);
+ TreeItem *_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc);
+ TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc);
+ TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc);
+ TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc);
+ TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_description);
+
+public:
+ bool work(uint64_t slot = 100000);
+
+ SearchRunner(VisualScriptPropertySelector *p_selector_ui, Tree *p_results_tree);
+};
+
#endif // VISUALSCRIPT_PROPERTYSELECTOR_H
diff --git a/modules/visual_script/icons/VisualScript.svg b/modules/visual_script/icons/VisualScript.svg
index 2352ba5d87..bc698247c9 100644
--- a/modules/visual_script/icons/VisualScript.svg
+++ b/modules/visual_script/icons/VisualScript.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><ellipse cx="3" cy="1039.4" fill="#6e6e6e"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><ellipse cx="3" cy="1039.4" fill="#e0e0e0"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/></g></svg>
diff --git a/modules/visual_script/icons/VisualScriptInternal.svg b/modules/visual_script/icons/VisualScriptInternal.svg
new file mode 100644
index 0000000000..8ab39ad929
--- /dev/null
+++ b/modules/visual_script/icons/VisualScriptInternal.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="3" cy="3.000024" fill="#e0e0e0" r="0"/><path d="m11 10a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0"/><path d="m3 10v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4z" fill="#e0e0e0"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2z" fill="none" stroke="#e0e0e0"/></svg>
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 99e75f9289..fd55796a66 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -964,7 +964,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
return;
}
- if (p_inputs[0]->is_ref()) {
+ if (p_inputs[0]->is_ref_counted()) {
REF r = *p_inputs[0];
if (!r.is_valid()) {
return;
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index 2abbd19e12..17a3566ed2 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -328,6 +328,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
};
case '"': {
String str;
+ char32_t prev = 0;
while (true) {
char32_t ch = GET_CHAR();
@@ -364,9 +365,11 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
case 'r':
res = 13;
break;
+ case 'U':
case 'u': {
- // hex number
- for (int j = 0; j < 4; j++) {
+ // Hexadecimal sequence.
+ int hex_len = (next == 'U') ? 6 : 4;
+ for (int j = 0; j < hex_len; j++) {
char32_t c = GET_CHAR();
if (c == 0) {
@@ -403,12 +406,46 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} break;
}
+ // Parse UTF-16 pair.
+ if ((res & 0xfffffc00) == 0xd800) {
+ if (prev == 0) {
+ prev = res;
+ continue;
+ } else {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ } else if ((res & 0xfffffc00) == 0xdc00) {
+ if (prev == 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired trail surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ } else {
+ res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev = 0;
+ }
+ }
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += res;
-
} else {
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += ch;
}
}
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
r_token.type = TK_CONSTANT;
r_token.value = str;
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index cc18d48dd8..ef6c1ecdb9 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -912,7 +912,7 @@ int VisualScriptPropertySet::get_output_sequence_port_count() const {
}
bool VisualScriptPropertySet::has_input_sequence_port() const {
- return 1;
+ return true;
}
Node *VisualScriptPropertySet::_get_base_node() const {
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index 618fe14137..416a674435 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -33,8 +33,7 @@
<method name="create_data_channel">
<return type="WebRTCDataChannel" />
<argument index="0" name="label" type="String" />
- <argument index="1" name="options" type="Dictionary" default="{
-}" />
+ <argument index="1" name="options" type="Dictionary" default="{}" />
<description>
Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [code]label[/code] and optionally configured via the [code]options[/code] dictionary. This method can only be called when the connection is in state [constant STATE_NEW].
There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]negotiated[/code] option set to [code]true[/code].
@@ -70,8 +69,7 @@
</method>
<method name="initialize">
<return type="int" enum="Error" />
- <argument index="0" name="configuration" type="Dictionary" default="{
-}" />
+ <argument index="0" name="configuration" type="Dictionary" default="{}" />
<description>
Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [code]options[/code] can be passed to configure the peer connection.
Valid [code]options[/code] are:
@@ -119,7 +117,7 @@
</methods>
<signals>
<signal name="data_channel_received">
- <argument index="0" name="channel" type="Object" />
+ <argument index="0" name="channel" type="WebRTCDataChannel" />
<description>
Emitted when a new in-band channel is received, i.e. when the channel was created with [code]negotiated: false[/code] (default).
The object will be an instance of [WebRTCDataChannel]. You must keep a reference of it or it will be closed automatically. See [method create_data_channel].
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index 54d4f57eef..e6a8dcc31a 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -38,11 +38,11 @@
#include "webrtc_peer_connection_extension.h"
void register_webrtc_types() {
-#define _SET_HINT(NAME, _VAL_, _MAX_) \
- GLOBAL_DEF(NAME, _VAL_); \
+#define SET_HINT(NAME, _VAL_, _MAX_) \
+ GLOBAL_DEF(NAME, _VAL_); \
ProjectSettings::get_singleton()->set_custom_property_info(NAME, PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater"));
- _SET_HINT(WRTC_IN_BUF, 64, 4096);
+ SET_HINT(WRTC_IN_BUF, 64, 4096);
ClassDB::register_custom_instance_class<WebRTCPeerConnection>();
GDREGISTER_CLASS(WebRTCPeerConnectionExtension);
@@ -51,6 +51,8 @@ void register_webrtc_types() {
GDREGISTER_CLASS(WebRTCDataChannelExtension);
GDREGISTER_CLASS(WebRTCMultiplayerPeer);
+
+#undef SET_HINT
}
void unregister_webrtc_types() {}
diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index 96cf518c06..7fdf26d3cd 100644
--- a/modules/webrtc/webrtc_peer_connection.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -69,7 +69,7 @@ void WebRTCPeerConnection::_bind_methods() {
ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
- ADD_SIGNAL(MethodInfo("data_channel_received", PropertyInfo(Variant::OBJECT, "channel")));
+ ADD_SIGNAL(MethodInfo("data_channel_received", PropertyInfo(Variant::OBJECT, "channel", PROPERTY_HINT_RESOURCE_TYPE, "WebRTCDataChannel")));
BIND_ENUM_CONSTANT(STATE_NEW);
BIND_ENUM_CONSTANT(STATE_CONNECTING);
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index bccbf88417..be1c75c354 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/modules/websocket/wsl_client.cpp
@@ -124,17 +124,17 @@ bool WSLClient::_verify_headers(String &r_protocol) {
}
}
-#define _WSL_CHECK(NAME, VALUE) \
+#define WSL_CHECK(NAME, VALUE) \
ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
"Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
-#define _WSL_CHECK_NC(NAME, VALUE) \
+#define WSL_CHECK_NC(NAME, VALUE) \
ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME] != VALUE, false, \
"Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
- _WSL_CHECK("connection", "upgrade");
- _WSL_CHECK("upgrade", "websocket");
- _WSL_CHECK_NC("sec-websocket-accept", WSLPeer::compute_key_response(_key));
-#undef _WSL_CHECK_NC
-#undef _WSL_CHECK
+ WSL_CHECK("connection", "upgrade");
+ WSL_CHECK("upgrade", "websocket");
+ WSL_CHECK_NC("sec-websocket-accept", WSLPeer::compute_key_response(_key));
+#undef WSL_CHECK_NC
+#undef WSL_CHECK
if (_protocols.size() == 0) {
// We didn't request a custom protocol
ERR_FAIL_COND_V(headers.has("sec-websocket-protocol"), false);
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
index 31175c5779..eadd7ef7ac 100644
--- a/modules/websocket/wsl_server.cpp
+++ b/modules/websocket/wsl_server.cpp
@@ -58,17 +58,17 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols, St
headers[name] = value;
}
}
-#define _WSL_CHECK(NAME, VALUE) \
+#define WSL_CHECK(NAME, VALUE) \
ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
"Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
-#define _WSL_CHECK_EX(NAME) \
+#define WSL_CHECK_EX(NAME) \
ERR_FAIL_COND_V_MSG(!headers.has(NAME), false, "Missing header '" + String(NAME) + "'.");
- _WSL_CHECK("upgrade", "websocket");
- _WSL_CHECK("sec-websocket-version", "13");
- _WSL_CHECK_EX("sec-websocket-key");
- _WSL_CHECK_EX("connection");
-#undef _WSL_CHECK_EX
-#undef _WSL_CHECK
+ WSL_CHECK("upgrade", "websocket");
+ WSL_CHECK("sec-websocket-version", "13");
+ WSL_CHECK_EX("sec-websocket-key");
+ WSL_CHECK_EX("connection");
+#undef WSL_CHECK_EX
+#undef WSL_CHECK
key = headers["sec-websocket-key"];
if (headers.has("sec-websocket-protocol")) {
Vector<String> protos = headers["sec-websocket-protocol"].split(",");
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
index 8eb0d8ff90..86b857f72c 100644
--- a/modules/webxr/webxr_interface_js.cpp
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -385,7 +385,7 @@ CameraMatrix WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p
return eye;
}
-Vector<BlitToScreen> WebXRInterfaceJS::commit_views(RID p_render_target, const Rect2 &p_screen_rect) {
+Vector<BlitToScreen> WebXRInterfaceJS::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
Vector<BlitToScreen> blit_to_screen;
if (!initialized) {
diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h
index 8eddfbe484..31858194f6 100644
--- a/modules/webxr/webxr_interface_js.h
+++ b/modules/webxr/webxr_interface_js.h
@@ -88,7 +88,7 @@ public:
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
- virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override;
+ virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override;
diff --git a/platform/android/SCsub b/platform/android/SCsub
index ecc72019e5..d031d14499 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -18,6 +18,7 @@ android_files = [
"jni_utils.cpp",
"android_keys_utils.cpp",
"display_server_android.cpp",
+ "plugin/godot_plugin_jni.cpp",
"vulkan/vulkan_context_android.cpp",
]
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index 7efdc620df..246ec6b198 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -39,10 +39,7 @@ void AndroidInputHandler::process_joy_event(AndroidInputHandler::JoypadEvent p_e
Input::get_singleton()->joy_button(p_event.device, (JoyButton)p_event.index, p_event.pressed);
break;
case JOY_EVENT_AXIS:
- Input::JoyAxisValue value;
- value.min = -1;
- value.value = p_event.value;
- Input::get_singleton()->joy_axis(p_event.device, (JoyAxis)p_event.index, value);
+ Input::get_singleton()->joy_axis(p_event.device, (JoyAxis)p_event.index, p_event.value);
break;
case JOY_EVENT_HAT:
Input::get_singleton()->joy_hat(p_event.device, p_event.hat);
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 5b9ca44180..3d0dabc56e 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -47,7 +47,6 @@ DisplayServerAndroid *DisplayServerAndroid::get_singleton() {
bool DisplayServerAndroid::has_feature(Feature p_feature) const {
switch (p_feature) {
- //case FEATURE_CONSOLE_WINDOW:
case FEATURE_CURSOR_SHAPE:
//case FEATURE_CUSTOM_CURSOR_SHAPE:
//case FEATURE_GLOBAL_MENU:
@@ -96,6 +95,17 @@ String DisplayServerAndroid::clipboard_get() const {
}
}
+bool DisplayServerAndroid::clipboard_has() const {
+ GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
+ ERR_FAIL_COND_V(!godot_java, false);
+
+ if (godot_java->has_has_clipboard()) {
+ return godot_java->has_clipboard();
+ } else {
+ return DisplayServer::clipboard_has();
+ }
+}
+
void DisplayServerAndroid::screen_set_keep_on(bool p_enable) {
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
ERR_FAIL_COND(!godot_java);
@@ -243,6 +253,24 @@ DisplayServer::WindowID DisplayServerAndroid::get_window_at_screen_position(cons
return MAIN_WINDOW_ID;
}
+int64_t DisplayServerAndroid::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
+ ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, 0);
+ switch (p_handle_type) {
+ case DISPLAY_HANDLE: {
+ return 0; // Not supported.
+ }
+ case WINDOW_HANDLE: {
+ return (int64_t)((OS_Android *)OS::get_singleton())->get_godot_java()->get_activity();
+ }
+ case WINDOW_VIEW: {
+ return 0; // Not supported.
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
void DisplayServerAndroid::window_attach_instance_id(ObjectID p_instance, DisplayServer::WindowID p_window) {
window_attached_instance_id = p_instance;
}
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index 8538b6e660..e52e07bf1a 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -93,6 +93,7 @@ public:
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
+ virtual bool clipboard_has() const override;
virtual void screen_set_keep_on(bool p_enable) override;
virtual bool screen_is_kept_on() const override;
@@ -123,6 +124,9 @@ public:
virtual Vector<WindowID> get_window_list() const override;
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
+
+ virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
+
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index aa44183329..61d2f897ef 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -975,20 +975,6 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
}
}
- if (tname == "meta-data" && attrname == "name" && value == "xr_mode_metadata_name") {
- // Update the meta-data 'android:name' attribute based on the selected XR mode.
- if (xr_mode_index == XR_MODE_OPENXR) {
- string_table.write[attr_value] = "com.samsung.android.vr.application.mode";
- }
- }
-
- if (tname == "meta-data" && attrname == "value" && value == "xr_mode_metadata_value") {
- // Update the meta-data 'android:value' attribute based on the selected XR mode.
- if (xr_mode_index == XR_MODE_OPENXR) {
- string_table.write[attr_value] = "vr_only";
- }
- }
-
if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_metadata_name") {
if (xr_mode_index == XR_MODE_OPENXR && hand_tracking_index > XR_HAND_TRACKING_NONE) {
string_table.write[attr_value] = "com.oculus.handtracking.frequency";
@@ -1514,7 +1500,7 @@ String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, R
}
if (scale_splash) {
- Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
int width, height;
if (screen_size.width > screen_size.height) {
// scale horizontally
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index 287b669fd1..babd8173d0 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -278,7 +278,6 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_
" android:requestLegacyExternalStorage=\"%s\"\n"
" tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n"
" tools:ignore=\"GoogleAppIndexingWarning\">\n\n"
- " <meta-data tools:node=\"remove\" android:name=\"xr_mode_metadata_name\" />\n"
" <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_metadata_name\" />\n",
bool_to_string(p_preset->get("user_data_backup/allow")),
bool_to_string(p_preset->get("package/classify_as_game")),
@@ -286,8 +285,6 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_
bool_to_string(p_has_storage_permission));
if (uses_xr) {
- manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.samsung.android.vr.application.mode\" android:value=\"vr_only\" />\n";
-
bool hand_tracking_enabled = (int)(p_preset->get("xr_features/hand_tracking")) > XR_HAND_TRACKING_NONE;
if (hand_tracking_enabled) {
int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency");
@@ -296,6 +293,8 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_
" <meta-data tools:node=\"replace\" android:name=\"com.oculus.handtracking.frequency\" android:value=\"%s\" />\n",
hand_tracking_frequency);
}
+ } else {
+ manifest_application_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.supportedDevices\" />\n";
}
manifest_application_text += _get_activity_tag(p_preset);
manifest_application_text += " </application>\n";
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 3924aacccd..4c4501729d 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -33,11 +33,6 @@
<!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. -->
<!-- Do these changes in the export preset. Adding new ones is fine. -->
- <!-- XR mode metadata. This is modified by the exporter based on the selected xr mode. DO NOT CHANGE the values here. -->
- <meta-data
- android:name="xr_mode_metadata_name"
- android:value="xr_mode_metadata_value" />
-
<!-- XR hand tracking metadata -->
<!-- This is modified by the exporter based on the selected xr mode. DO NOT CHANGE the values here. -->
<!-- Removed at export time if the xr mode is not VR or hand tracking is disabled. -->
@@ -45,6 +40,12 @@
android:name="xr_hand_tracking_metadata_name"
android:value="xr_hand_tracking_metadata_value"/>
+ <!-- Supported Meta devices -->
+ <!-- This is removed by the exporter if the xr mode is not VR. -->
+ <meta-data
+ android:name="com.oculus.supportedDevices"
+ android:value="all" />
+
<activity
android:name=".GodotApp"
android:label="@string/godot_project_name_string"
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 3fbbd8fbcc..78848c109a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -660,10 +660,14 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
}
+ public boolean hasClipboard() {
+ return mClipboard.hasPrimaryClip();
+ }
+
public String getClipboard() {
String copiedText = "";
- if (mClipboard.getPrimaryClip() != null) {
+ if (mClipboard.hasPrimaryClip()) {
ClipData.Item item = mClipboard.getPrimaryClip().getItemAt(0);
copiedText = item.getText().toString();
}
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 115264d7ee..5beec6b611 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -66,6 +66,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I");
_get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
_set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
+ _has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z");
_request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");
_request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z");
_get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;");
@@ -248,6 +249,21 @@ void GodotJavaWrapper::set_clipboard(const String &p_text) {
}
}
+bool GodotJavaWrapper::has_has_clipboard() {
+ return _has_clipboard != 0;
+}
+
+bool GodotJavaWrapper::has_clipboard() {
+ if (_has_clipboard) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND_V(env == nullptr, false);
+
+ return env->CallBooleanMethod(godot_instance, _has_clipboard);
+ } else {
+ return false;
+ }
+}
+
bool GodotJavaWrapper::request_permission(const String &p_name) {
if (_request_permission) {
JNIEnv *env = get_jni_env();
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 4d058ac426..42ae91480f 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -58,6 +58,7 @@ private:
jmethodID _get_GLES_version_code = 0;
jmethodID _get_clipboard = 0;
jmethodID _set_clipboard = 0;
+ jmethodID _has_clipboard = 0;
jmethodID _request_permission = 0;
jmethodID _request_permissions = 0;
jmethodID _get_granted_permissions = 0;
@@ -92,6 +93,8 @@ public:
String get_clipboard();
bool has_set_clipboard();
void set_clipboard(const String &p_text);
+ bool has_has_clipboard();
+ bool has_clipboard();
bool request_permission(const String &p_name);
bool request_permissions();
Vector<String> get_granted_permissions() const;
diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp
index 2207eec18d..48aeb3d070 100644
--- a/platform/android/plugin/godot_plugin_jni.cpp
+++ b/platform/android/plugin/godot_plugin_jni.cpp
@@ -126,7 +126,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitS
env->DeleteLocalRef(j_param);
};
- singleton->emit_signal(SNAME(signal_name), args, count);
+ singleton->emit_signal(StringName(signal_name), args, count);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jclass clazz, jobjectArray gdnlib_paths) {
diff --git a/platform/iphone/display_server_iphone.h b/platform/iphone/display_server_iphone.h
index de04bc88e3..6434483641 100644
--- a/platform/iphone/display_server_iphone.h
+++ b/platform/iphone/display_server_iphone.h
@@ -135,6 +135,8 @@ public:
virtual WindowID
get_window_at_screen_position(const Point2i &p_position) const override;
+ virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
+
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
diff --git a/platform/iphone/display_server_iphone.mm b/platform/iphone/display_server_iphone.mm
index 3047d91a4d..48bda89fc3 100644
--- a/platform/iphone/display_server_iphone.mm
+++ b/platform/iphone/display_server_iphone.mm
@@ -293,7 +293,6 @@ void DisplayServerIPhone::update_gyroscope(float p_x, float p_y, float p_z) {
bool DisplayServerIPhone::has_feature(Feature p_feature) const {
switch (p_feature) {
- // case FEATURE_CONSOLE_WINDOW:
// case FEATURE_CURSOR_SHAPE:
// case FEATURE_CUSTOM_CURSOR_SHAPE:
// case FEATURE_GLOBAL_MENU:
@@ -408,6 +407,24 @@ DisplayServer::WindowID DisplayServerIPhone::get_window_at_screen_position(const
return MAIN_WINDOW_ID;
}
+int64_t DisplayServerIPhone::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
+ ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, 0);
+ switch (p_handle_type) {
+ case DISPLAY_HANDLE: {
+ return 0; // Not supported.
+ }
+ case WINDOW_HANDLE: {
+ return (int64_t)AppDelegate.viewController;
+ }
+ case WINDOW_VIEW: {
+ return (int64_t)AppDelegate.viewController.godotView;
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
void DisplayServerIPhone::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
window_attached_instance_id = p_instance;
}
diff --git a/platform/iphone/export/export_plugin.cpp b/platform/iphone/export/export_plugin.cpp
index 0ad68086a7..fe00b1a3cd 100644
--- a/platform/iphone/export/export_plugin.cpp
+++ b/platform/iphone/export/export_plugin.cpp
@@ -32,11 +32,8 @@
void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
- r_features->push_back("pvrtc");
- if (driver == "vulkan") {
- // FIXME: Review if this is correct.
- r_features->push_back("etc2");
- }
+ // Vulkan and OpenGL ES 3.0 both mandate ETC2 support.
+ r_features->push_back("etc2");
Vector<String> architectures = _get_preset_architectures(p_preset);
for (int i = 0; i < architectures.size(); ++i) {
@@ -86,7 +83,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), "iPhone Developer"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), ""));
@@ -144,17 +141,17 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "icons/generate_missing"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with Retina display
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with Retina HD display
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with Retina display
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with retina display
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with retina HD display
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with retina display
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with Retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0));
@@ -163,8 +160,6 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color()));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "launch_screens/generate_missing"), false));
-
for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), ""));
}
@@ -183,6 +178,10 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
"scaleAspectFill",
"scaleToFill"
};
+ String dbg_sign_id = p_preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "iPhone Developer" : p_preset->get("application/code_sign_identity_debug");
+ String rel_sign_id = p_preset->get("application/code_sign_identity_release").operator String().is_empty() ? "iPhone Distribution" : p_preset->get("application/code_sign_identity_release");
+ bool dbg_manual = !p_preset->get("application/provisioning_profile_uuid_debug").operator String().is_empty() || (dbg_sign_id != "iPhone Developer");
+ bool rel_manual = !p_preset->get("application/provisioning_profile_uuid_release").operator String().is_empty() || (rel_sign_id != "iPhone Distribution");
String str;
String strnew;
str.parse_utf8((const char *)pfile.ptr(), pfile.size());
@@ -223,13 +222,25 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n";
} else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) {
strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n";
+ } else if (lines[i].find("$code_sign_style_debug") != -1) {
+ if (dbg_manual) {
+ strnew += lines[i].replace("$code_sign_style_debug", "Manual") + "\n";
+ } else {
+ strnew += lines[i].replace("$code_sign_style_debug", "Automatic") + "\n";
+ }
+ } else if (lines[i].find("$code_sign_style_release") != -1) {
+ if (rel_manual) {
+ strnew += lines[i].replace("$code_sign_style_release", "Manual") + "\n";
+ } else {
+ strnew += lines[i].replace("$code_sign_style_release", "Automatic") + "\n";
+ }
} else if (lines[i].find("$provisioning_profile_uuid") != -1) {
String uuid = p_debug ? p_preset->get("application/provisioning_profile_uuid_debug") : p_preset->get("application/provisioning_profile_uuid_release");
strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n";
} else if (lines[i].find("$code_sign_identity_debug") != -1) {
- strnew += lines[i].replace("$code_sign_identity_debug", p_preset->get("application/code_sign_identity_debug")) + "\n";
+ strnew += lines[i].replace("$code_sign_identity_debug", dbg_sign_id) + "\n";
} else if (lines[i].find("$code_sign_identity_release") != -1) {
- strnew += lines[i].replace("$code_sign_identity_release", p_preset->get("application/code_sign_identity_release")) + "\n";
+ strnew += lines[i].replace("$code_sign_identity_release", rel_sign_id) + "\n";
} else if (lines[i].find("$additional_plist_content") != -1) {
strnew += lines[i].replace("$additional_plist_content", p_config.plist_content) + "\n";
} else if (lines[i].find("$godot_archs") != -1) {
@@ -466,26 +477,26 @@ struct IconInfo {
const char *actual_size_side;
const char *scale;
const char *unscaled_size;
- bool is_required = false;
};
static const IconInfo icon_infos[] = {
- { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", true },
- { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true },
-
- { "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", true },
- { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true },
-
- { "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false },
-
- { "optional_icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false },
-
- { "optional_icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false },
-
- { "optional_icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false },
-
- { "optional_icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false },
- { "optional_icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false }
+ // Home screen on iPhone
+ { "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60" },
+ { "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40" },
+ { "icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60" },
+
+ // Home screen on iPad
+ { "icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76" },
+ { "icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76" },
+ { "icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5" },
+
+ // App Store
+ { "icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024" },
+
+ // Spotlight
+ { "icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40" },
+ { "icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40" },
+ { "icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40" }
};
Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) {
@@ -500,35 +511,23 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
int side_size = String(info.actual_size_side).to_int();
String icon_path = p_preset->get(info.preset_key);
if (icon_path.length() == 0) {
- if ((bool)p_preset->get("icons/generate_missing")) {
- // Resize main app icon
- icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
- Ref<Image> img = memnew(Image);
- Error err = ImageLoader::load_image(icon_path, img);
- if (err != OK) {
- ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
- return ERR_UNCONFIGURED;
- }
- img->resize(side_size, side_size);
- err = img->save_png(p_iconset_dir + info.export_name);
- if (err) {
- String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
- ERR_PRINT(err_str.utf8().get_data());
- return err;
- }
- } else {
- if (info.is_required) {
- String err_str = String("Required icon (") + info.preset_key + ") is not specified in the preset.";
- ERR_PRINT(err_str);
- return ERR_UNCONFIGURED;
- } else {
- String err_str = String("Icon (") + info.preset_key + ") is not specified in the preset.";
- WARN_PRINT(err_str);
- }
- continue;
+ // Resize main app icon
+ icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
+ Ref<Image> img = memnew(Image);
+ Error err = ImageLoader::load_image(icon_path, img);
+ if (err != OK) {
+ ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
+ return ERR_UNCONFIGURED;
+ }
+ img->resize(side_size, side_size);
+ err = img->save_png(p_iconset_dir + info.export_name);
+ if (err) {
+ String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
+ ERR_PRINT(err_str.utf8().get_data());
+ return err;
}
} else {
- // Load custom icon
+ // Load custom icon and resize if required
Ref<Image> img = memnew(Image);
Error err = ImageLoader::load_image(icon_path, img);
if (err != OK) {
@@ -536,11 +535,13 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
return ERR_UNCONFIGURED;
}
if (img->get_width() != side_size || img->get_height() != side_size) {
- ERR_PRINT("Invalid icon size (" + String(info.preset_key) + "): '" + icon_path + "'.");
- return ERR_UNCONFIGURED;
+ WARN_PRINT("Icon (" + String(info.preset_key) + "): '" + icon_path + "' has incorrect size (" + String::num_int64(img->get_width()) + "x" + String::num_int64(img->get_height()) + ") and was automatically resized to " + String::num_int64(side_size) + "x" + String::num_int64(side_size) + ".");
+ img->resize(side_size, side_size);
+ err = img->save_png(p_iconset_dir + info.export_name);
+ } else {
+ err = da->copy(icon_path, p_iconset_dir + info.export_name);
}
- err = da->copy(icon_path, p_iconset_dir + info.export_name);
if (err) {
memdelete(da);
String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
@@ -652,8 +653,13 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp
for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
LoadingScreenInfo info = loading_screen_infos[i];
String loading_screen_file = p_preset->get(info.preset_key);
+
+ Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color");
+ String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
+ bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize");
+
if (loading_screen_file.size() > 0) {
- // Load custom loading screens
+ // Load custom loading screens, and resize if required.
Ref<Image> img = memnew(Image);
Error err = ImageLoader::load_image(loading_screen_file, img);
if (err != OK) {
@@ -661,22 +667,31 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp
return ERR_UNCONFIGURED;
}
if (img->get_width() != info.width || img->get_height() != info.height) {
- ERR_PRINT("Invalid loading screen size (" + String(info.preset_key) + "): '" + loading_screen_file + "'.");
- return ERR_UNCONFIGURED;
+ WARN_PRINT("Loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "' has incorrect size (" + String::num_int64(img->get_width()) + "x" + String::num_int64(img->get_height()) + ") and was automatically resized to " + String::num_int64(info.width) + "x" + String::num_int64(info.height) + ".");
+ float aspect_ratio = (float)img->get_width() / (float)img->get_height();
+ if (boot_logo_scale) {
+ if (info.height * aspect_ratio <= info.width) {
+ img->resize(info.height * aspect_ratio, info.height);
+ } else {
+ img->resize(info.width, info.width / aspect_ratio);
+ }
+ }
+ Ref<Image> new_img = memnew(Image);
+ new_img->create(info.width, info.height, false, Image::FORMAT_RGBA8);
+ new_img->fill(boot_bg_color);
+ _blend_and_rotate(new_img, img, false);
+ err = new_img->save_png(p_dest_dir + info.export_name);
+ } else {
+ err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
}
- err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
if (err) {
memdelete(da);
String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path '" + loading_screen_file + "'.";
ERR_PRINT(err_str.utf8().get_data());
return err;
}
- } else if ((bool)p_preset->get("launch_screens/generate_missing")) {
+ } else {
// Generate loading screen from the splash screen
- Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color");
- String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
- bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize");
-
Ref<Image> img = memnew(Image);
img->create(info.width, info.height, false, Image::FORMAT_RGBA8);
img->fill(boot_bg_color);
@@ -716,9 +731,6 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp
String err_str = String("Failed to export loading screen (") + info.preset_key + ") from splash screen.";
WARN_PRINT(err_str.utf8().get_data());
}
- } else {
- String err_str = String("No loading screen (") + info.preset_key + ") specified.";
- WARN_PRINT(err_str.utf8().get_data());
}
}
memdelete(da);
@@ -774,10 +786,18 @@ Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) {
if (p_file.ends_with(".dylib")) {
CodesignData *data = (CodesignData *)p_userdata;
print_line(String("Signing ") + p_file);
+
+ String sign_id;
+ if (data->debug) {
+ sign_id = data->preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "iPhone Developer" : data->preset->get("application/code_sign_identity_debug");
+ } else {
+ sign_id = data->preset->get("application/code_sign_identity_release").operator String().is_empty() ? "iPhone Distribution" : data->preset->get("application/code_sign_identity_release");
+ }
+
List<String> codesign_args;
codesign_args.push_back("-f");
codesign_args.push_back("-s");
- codesign_args.push_back(data->preset->get(data->debug ? "application/code_sign_identity_debug" : "application/code_sign_identity_release"));
+ codesign_args.push_back(sign_id);
codesign_args.push_back(p_file);
return OS::get_singleton()->execute("codesign", codesign_args);
}
@@ -1684,6 +1704,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
archive_args.push_back("-destination");
archive_args.push_back("generic/platform=iOS");
archive_args.push_back("archive");
+ archive_args.push_back("-allowProvisioningUpdates");
archive_args.push_back("-archivePath");
archive_args.push_back(archive_path);
String archive_str;
@@ -1754,19 +1775,7 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset
valid = false;
}
- for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {
- IconInfo info = icon_infos[i];
- String icon_path = p_preset->get(info.preset_key);
- if (icon_path.length() == 0) {
- if (info.is_required) {
- err += TTR("Required icon is not specified in the preset.") + "\n";
- valid = false;
- }
- break;
- }
- }
-
- String etc_error = test_etc2_or_pvrtc();
+ const String etc_error = test_etc2();
if (!etc_error.is_empty()) {
valid = false;
err += etc_error;
diff --git a/platform/iphone/joypad_iphone.mm b/platform/iphone/joypad_iphone.mm
index 2630b42da0..f45f4da5a8 100644
--- a/platform/iphone/joypad_iphone.mm
+++ b/platform/iphone/joypad_iphone.mm
@@ -287,24 +287,22 @@ void JoypadIPhone::start_processing() {
gamepad.dpad.right.isPressed);
};
- Input::JoyAxisValue jx;
- jx.min = -1;
if (element == gamepad.leftThumbstick) {
- jx.value = gamepad.leftThumbstick.xAxis.value;
- Input::get_singleton()->joy_axis(joy_id, JoyAxis::LEFT_X, jx);
- jx.value = -gamepad.leftThumbstick.yAxis.value;
- Input::get_singleton()->joy_axis(joy_id, JoyAxis::LEFT_Y, jx);
+ float value = gamepad.leftThumbstick.xAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::LEFT_X, value);
+ value = -gamepad.leftThumbstick.yAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::LEFT_Y, value);
} else if (element == gamepad.rightThumbstick) {
- jx.value = gamepad.rightThumbstick.xAxis.value;
- Input::get_singleton()->joy_axis(joy_id, JoyAxis::RIGHT_X, jx);
- jx.value = -gamepad.rightThumbstick.yAxis.value;
- Input::get_singleton()->joy_axis(joy_id, JoyAxis::RIGHT_Y, jx);
+ float value = gamepad.rightThumbstick.xAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::RIGHT_X, value);
+ value = -gamepad.rightThumbstick.yAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::RIGHT_Y, value);
} else if (element == gamepad.leftTrigger) {
- jx.value = gamepad.leftTrigger.value;
- Input::get_singleton()->joy_axis(joy_id, JoyAxis::TRIGGER_LEFT, jx);
+ float value = gamepad.leftTrigger.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::TRIGGER_LEFT, value);
} else if (element == gamepad.rightTrigger) {
- jx.value = gamepad.rightTrigger.value;
- Input::get_singleton()->joy_axis(joy_id, JoyAxis::TRIGGER_RIGHT, jx);
+ float value = gamepad.rightTrigger.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::TRIGGER_RIGHT, value);
};
};
} else if (controller.microGamepad != nil) {
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index b57f3b3f16..b6be44fbb2 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -89,10 +89,10 @@ def configure(env):
if env["tools"]:
if not env["threads_enabled"]:
- print("Threads must be enabled to build the editor. Please add the 'threads_enabled=yes' option")
- sys.exit(255)
+ print('Note: Forcing "threads_enabled=yes" as it is required for the web editor.')
+ env["threads_enabled"] = "yes"
if env["initial_memory"] < 64:
- print("Editor build requires at least 64MiB of initial memory. Forcing it.")
+ print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
env["initial_memory"] = 64
env.Append(CCFLAGS=["-frtti"])
elif env["builtin_icu"]:
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
index f98e0c4c5f..2842fc2f5e 100644
--- a/platform/javascript/display_server_javascript.cpp
+++ b/platform/javascript/display_server_javascript.cpp
@@ -137,7 +137,6 @@ int DisplayServerJavaScript::mouse_button_callback(int p_pressed, int p_button,
DisplayServerJavaScript *ds = get_singleton();
Point2 pos(p_x, p_y);
- Input::get_singleton()->set_mouse_position(pos);
Ref<InputEventMouseButton> ev;
ev.instantiate();
ev->set_position(pos);
@@ -219,7 +218,6 @@ void DisplayServerJavaScript::mouse_move_callback(double p_x, double p_y, double
}
Point2 pos(p_x, p_y);
- Input::get_singleton()->set_mouse_position(pos);
Ref<InputEventMouseMotion> ev;
ev.instantiate();
dom2godot_mod(ev, p_modifiers);
@@ -229,7 +227,6 @@ void DisplayServerJavaScript::mouse_move_callback(double p_x, double p_y, double
ev->set_global_position(pos);
ev->set_relative(Vector2(p_rel_x, p_rel_y));
- Input::get_singleton()->set_mouse_position(ev->get_position());
ev->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
Input::get_singleton()->parse_input_event(ev);
@@ -558,24 +555,16 @@ void DisplayServerJavaScript::process_joypads() {
continue;
}
for (int b = 0; b < s_btns_num; b++) {
- float value = s_btns[b];
// Buttons 6 and 7 in the standard mapping need to be
// axis to be handled as JoyAxis::TRIGGER by Godot.
if (s_standard && (b == 6 || b == 7)) {
- Input::JoyAxisValue joy_axis;
- joy_axis.min = 0;
- joy_axis.value = value;
- JoyAxis a = b == 6 ? JoyAxis::TRIGGER_LEFT : JoyAxis::TRIGGER_RIGHT;
- input->joy_axis(idx, a, joy_axis);
+ input->joy_axis(idx, (JoyAxis)b, s_btns[b]);
} else {
- input->joy_button(idx, (JoyButton)b, value);
+ input->joy_button(idx, (JoyButton)b, s_btns[b]);
}
}
for (int a = 0; a < s_axes_num; a++) {
- Input::JoyAxisValue joy_axis;
- joy_axis.min = -1;
- joy_axis.value = s_axes[a];
- input->joy_axis(idx, (JoyAxis)a, joy_axis);
+ input->joy_axis(idx, (JoyAxis)a, s_axes[a]);
}
}
}
@@ -746,7 +735,6 @@ DisplayServerJavaScript::~DisplayServerJavaScript() {
bool DisplayServerJavaScript::has_feature(Feature p_feature) const {
switch (p_feature) {
- //case FEATURE_CONSOLE_WINDOW:
//case FEATURE_GLOBAL_MENU:
//case FEATURE_HIDPI:
//case FEATURE_IME:
diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp
index 81ffae82bf..db0d506cdf 100644
--- a/platform/javascript/export/export_plugin.cpp
+++ b/platform/javascript/export/export_plugin.cpp
@@ -333,9 +333,9 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/display", PROPERTY_HINT_ENUM, "Fullscreen,Standalone,Minimal UI,Browser"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/orientation", PROPERTY_HINT_ENUM, "Any,Landscape,Portrait"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_144x144", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_180x180", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_512x512", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_144x144", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_180x180", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_512x512", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "progressive_web_app/background_color", PROPERTY_HINT_COLOR_NO_ALPHA), Color()));
}
diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp
index 57416ebe48..c946302862 100644
--- a/platform/javascript/http_client_javascript.cpp
+++ b/platform/javascript/http_client_javascript.cpp
@@ -87,6 +87,11 @@ Error HTTPClientJavaScript::request(Method p_method, const String &p_url, const
ERR_FAIL_COND_V(port < 0, ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER);
+ Error err = verify_headers(p_headers);
+ if (err) {
+ return err;
+ }
+
String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + p_url;
Vector<CharString> keeper;
Vector<const char *> c_strings;
@@ -143,7 +148,7 @@ Error HTTPClientJavaScript::get_response_headers(List<String> *r_response) {
return OK;
}
-int HTTPClientJavaScript::get_response_body_length() const {
+int64_t HTTPClientJavaScript::get_response_body_length() const {
return godot_js_fetch_body_length_get(js_id);
}
diff --git a/platform/javascript/http_client_javascript.h b/platform/javascript/http_client_javascript.h
index d8f23fe694..096aa6a153 100644
--- a/platform/javascript/http_client_javascript.h
+++ b/platform/javascript/http_client_javascript.h
@@ -95,7 +95,7 @@ public:
bool is_response_chunked() const override;
int get_response_code() const override;
Error get_response_headers(List<String> *r_response) override;
- int get_response_body_length() const override;
+ int64_t get_response_body_length() const override;
PackedByteArray read_response_body_chunk() override;
void set_blocking_mode(bool p_enable) override;
bool is_blocking_mode_enabled() const override;
diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js
index ba61b14eb7..5628ddbab5 100644
--- a/platform/javascript/js/engine/config.js
+++ b/platform/javascript/js/engine/config.js
@@ -225,33 +225,34 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
*/
Config.prototype.update = function (opts) {
const config = opts || {};
- function parse(key, def) {
+ const me = this;
+ function parse(key) {
if (typeof (config[key]) === 'undefined') {
- return def;
+ return me[key];
}
return config[key];
}
// Module config
- this.unloadAfterInit = parse('unloadAfterInit', this.unloadAfterInit);
- this.onPrintError = parse('onPrintError', this.onPrintError);
- this.onPrint = parse('onPrint', this.onPrint);
- this.onProgress = parse('onProgress', this.onProgress);
+ this.unloadAfterInit = parse('unloadAfterInit');
+ this.onPrintError = parse('onPrintError');
+ this.onPrint = parse('onPrint');
+ this.onProgress = parse('onProgress');
// Godot config
- this.canvas = parse('canvas', this.canvas);
- this.executable = parse('executable', this.executable);
- this.mainPack = parse('mainPack', this.mainPack);
- this.locale = parse('locale', this.locale);
- this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy);
- this.persistentPaths = parse('persistentPaths', this.persistentPaths);
- this.persistentDrops = parse('persistentDrops', this.persistentDrops);
- this.experimentalVK = parse('experimentalVK', this.experimentalVK);
- this.focusCanvas = parse('focusCanvas', this.focusCanvas);
- this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
- this.fileSizes = parse('fileSizes', this.fileSizes);
- this.args = parse('args', this.args);
- this.onExecute = parse('onExecute', this.onExecute);
- this.onExit = parse('onExit', this.onExit);
+ this.canvas = parse('canvas');
+ this.executable = parse('executable');
+ this.mainPack = parse('mainPack');
+ this.locale = parse('locale');
+ this.canvasResizePolicy = parse('canvasResizePolicy');
+ this.persistentPaths = parse('persistentPaths');
+ this.persistentDrops = parse('persistentDrops');
+ this.experimentalVK = parse('experimentalVK');
+ this.focusCanvas = parse('focusCanvas');
+ this.gdnativeLibs = parse('gdnativeLibs');
+ this.fileSizes = parse('fileSizes');
+ this.args = parse('args');
+ this.onExecute = parse('onExecute');
+ this.onExit = parse('onExit');
};
/**
diff --git a/platform/javascript/js/libs/library_godot_input.js b/platform/javascript/js/libs/library_godot_input.js
index 7a4d0d8126..1e64c260f8 100644
--- a/platform/javascript/js/libs/library_godot_input.js
+++ b/platform/javascript/js/libs/library_godot_input.js
@@ -87,7 +87,7 @@ const GodotInputGamepads = {
},
init: function (onchange) {
- GodotEventListeners.samples = [];
+ GodotInputGamepads.samples = [];
function add(pad) {
const guid = GodotInputGamepads.get_guid(pad);
const c_id = GodotRuntime.allocString(pad.id);
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index b0eea54aa9..39eea13ca1 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -107,11 +107,11 @@ void OS_JavaScript::finalize() {
// Miscellaneous
-Error OS_JavaScript::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+Error OS_JavaScript::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
return create_process(p_path, p_arguments);
}
-Error OS_JavaScript::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
+Error OS_JavaScript::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
Array args;
for (const String &E : p_arguments) {
args.push_back(E);
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index c12088bfbe..3404fe9dcf 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -70,8 +70,8 @@ public:
MainLoop *get_main_loop() const override;
bool main_loop_iterate();
- Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) override;
- Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+ Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
Error kill(const ProcessID &p_pid) override;
int get_process_id() const override;
int get_processor_count() const override;
diff --git a/platform/javascript/package-lock.json b/platform/javascript/package-lock.json
index 1bc11c7ccf..35f864f01a 100644
--- a/platform/javascript/package-lock.json
+++ b/platform/javascript/package-lock.json
@@ -1,8 +1,3009 @@
{
"name": "godot",
"version": "1.0.0",
- "lockfileVersion": 1,
+ "lockfileVersion": 2,
"requires": true,
+ "packages": {
+ "": {
+ "name": "godot",
+ "version": "1.0.0",
+ "license": "MIT",
+ "devDependencies": {
+ "eslint": "^7.28.0",
+ "eslint-config-airbnb-base": "^14.2.1",
+ "eslint-plugin-import": "^2.23.4",
+ "jsdoc": "^3.6.7",
+ "serve": "^13.0.2"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.12.11",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+ "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz",
+ "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
+ "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.14.5",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.5.tgz",
+ "integrity": "sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz",
+ "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.1.1",
+ "espree": "^7.3.0",
+ "globals": "^13.9.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
+ "dev": true
+ },
+ "node_modules/@zeit/schemas": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz",
+ "integrity": "sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==",
+ "dev": true
+ },
+ "node_modules/accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+ "dev": true,
+ "dependencies": {
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
+ "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
+ "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": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-align": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.1.0"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/arch": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
+ "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/arg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz",
+ "integrity": "sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==",
+ "dev": true
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz",
+ "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.2",
+ "get-intrinsic": "^1.1.1",
+ "is-string": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz",
+ "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "node_modules/boxen": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
+ "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-align": "^3.0.0",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.1.0",
+ "cli-boxes": "^2.2.1",
+ "string-width": "^4.2.2",
+ "type-fest": "^0.20.2",
+ "widest-line": "^3.1.0",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/catharsis": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
+ "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.15"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/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/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/chalk/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/chalk/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-boxes": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
+ "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/clipboardy": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
+ "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==",
+ "dev": true,
+ "dependencies": {
+ "arch": "^2.1.1",
+ "execa": "^1.0.0",
+ "is-wsl": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "node_modules/compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": ">= 1.43.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/compression": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz",
+ "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
+ "dev": true,
+ "dependencies": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.14",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.1",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/compression/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/compression/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "node_modules/confusing-browser-globals": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz",
+ "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==",
+ "dev": true
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "node_modules/define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "dependencies": {
+ "object-keys": "^1.0.12"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/enquirer": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-colors": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/entities": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
+ "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==",
+ "dev": true
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.18.3",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz",
+ "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.2",
+ "is-callable": "^1.2.3",
+ "is-negative-zero": "^2.0.1",
+ "is-regex": "^1.1.3",
+ "is-string": "^1.0.6",
+ "object-inspect": "^1.10.3",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.2",
+ "string.prototype.trimend": "^1.0.4",
+ "string.prototype.trimstart": "^1.0.4",
+ "unbox-primitive": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz",
+ "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "7.12.11",
+ "@eslint/eslintrc": "^0.4.2",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "enquirer": "^2.3.5",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^2.1.0",
+ "eslint-visitor-keys": "^2.0.0",
+ "espree": "^7.3.1",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.1.2",
+ "globals": "^13.6.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.0.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "progress": "^2.0.0",
+ "regexpp": "^3.1.0",
+ "semver": "^7.2.1",
+ "strip-ansi": "^6.0.0",
+ "strip-json-comments": "^3.1.0",
+ "table": "^6.0.9",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-airbnb-base": {
+ "version": "14.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz",
+ "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==",
+ "dev": true,
+ "dependencies": {
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0",
+ "eslint-plugin-import": "^2.22.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
+ "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^2.6.9",
+ "resolve": "^1.13.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz",
+ "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7",
+ "pkg-dir": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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-import": {
+ "version": "2.23.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz",
+ "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==",
+ "dev": true,
+ "dependencies": {
+ "array-includes": "^3.1.3",
+ "array.prototype.flat": "^1.2.4",
+ "debug": "^2.6.9",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.4",
+ "eslint-module-utils": "^2.6.1",
+ "find-up": "^2.0.0",
+ "has": "^1.0.3",
+ "is-core-module": "^2.4.0",
+ "minimatch": "^3.0.4",
+ "object.values": "^1.1.3",
+ "pkg-up": "^2.0.0",
+ "read-pkg-up": "^3.0.0",
+ "resolve": "^1.20.0",
+ "tsconfig-paths": "^3.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/espree": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+ "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^7.4.0",
+ "acorn-jsx": "^5.3.1",
+ "eslint-visitor-keys": "^1.3.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esquery/node_modules/estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/execa/node_modules/cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "dependencies": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ },
+ "engines": {
+ "node": ">=4.8"
+ }
+ },
+ "node_modules/execa/node_modules/path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/execa/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/execa/node_modules/shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/execa/node_modules/shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/execa/node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "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",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "node_modules/fast-url-parser": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz",
+ "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^1.3.2"
+ }
+ },
+ "node_modules/fast-url-parser/node_modules/punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ },
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz",
+ "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==",
+ "dev": true
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "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/functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+ "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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/globals": {
+ "version": "13.9.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz",
+ "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
+ "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.1",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+ "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hosted-git-info": {
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+ "dev": true
+ },
+ "node_modules/ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "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/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "node_modules/is-bigint": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz",
+ "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz",
+ "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
+ "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
+ "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz",
+ "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true,
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
+ "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz",
+ "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz",
+ "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz",
+ "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==",
+ "dev": true,
+ "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-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/js2xmlparser": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.1.tgz",
+ "integrity": "sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==",
+ "dev": true,
+ "dependencies": {
+ "xmlcreate": "^2.0.3"
+ }
+ },
+ "node_modules/jsdoc": {
+ "version": "3.6.7",
+ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz",
+ "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.9.4",
+ "bluebird": "^3.7.2",
+ "catharsis": "^0.9.0",
+ "escape-string-regexp": "^2.0.0",
+ "js2xmlparser": "^4.0.1",
+ "klaw": "^3.0.0",
+ "markdown-it": "^10.0.0",
+ "markdown-it-anchor": "^5.2.7",
+ "marked": "^2.0.3",
+ "mkdirp": "^1.0.4",
+ "requizzle": "^0.2.3",
+ "strip-json-comments": "^3.1.0",
+ "taffydb": "2.6.2",
+ "underscore": "~1.13.1"
+ },
+ "bin": {
+ "jsdoc": "jsdoc.js"
+ },
+ "engines": {
+ "node": ">=8.15.0"
+ }
+ },
+ "node_modules/jsdoc/node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "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",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/klaw": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz",
+ "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/linkify-it": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
+ "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
+ "dev": true,
+ "dependencies": {
+ "uc.micro": "^1.0.1"
+ }
+ },
+ "node_modules/load-json-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
+ "strip-bom": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+ "dev": true
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lodash.truncate": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
+ "dev": true
+ },
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/markdown-it": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
+ "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "entities": "~2.0.0",
+ "linkify-it": "^2.0.0",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.js"
+ }
+ },
+ "node_modules/markdown-it-anchor": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz",
+ "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==",
+ "dev": true,
+ "peerDependencies": {
+ "markdown-it": "*"
+ }
+ },
+ "node_modules/marked": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.7.tgz",
+ "integrity": "sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ==",
+ "dev": true,
+ "bin": {
+ "marked": "bin/marked"
+ },
+ "engines": {
+ "node": ">= 8.16.2"
+ }
+ },
+ "node_modules/mdurl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+ "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
+ "dev": true
+ },
+ "node_modules/mime-db": {
+ "version": "1.51.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
+ "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.34",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
+ "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "1.51.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "node_modules/normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "dependencies": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "node_modules/normalize-package-data/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz",
+ "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==",
+ "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.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz",
+ "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz",
+ "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "dependencies": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "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-to-regexp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz",
+ "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==",
+ "dev": true
+ },
+ "node_modules/path-type": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
+ "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/read-pkg": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+ "dev": true,
+ "dependencies": {
+ "load-json-file": "^4.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/read-pkg-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz",
+ "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^2.0.0",
+ "read-pkg": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regexpp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/registry-auth-token": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
+ "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
+ "dev": true,
+ "dependencies": {
+ "rc": "^1.1.6",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/registry-url": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+ "dev": true,
+ "dependencies": {
+ "rc": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/requizzle": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz",
+ "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ },
+ "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",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "node_modules/semver": {
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/serve": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/serve/-/serve-13.0.2.tgz",
+ "integrity": "sha512-71R6fKvNgKrqARAag6lYJNnxDzpH7DCNrMuvPY5PLVaC2PDhJsGTj/34o4o4tPWhTuLgEXqvgnAWbATQ9zGZTQ==",
+ "dev": true,
+ "dependencies": {
+ "@zeit/schemas": "2.6.0",
+ "ajv": "6.12.6",
+ "arg": "2.0.0",
+ "boxen": "5.1.2",
+ "chalk": "2.4.1",
+ "clipboardy": "2.3.0",
+ "compression": "1.7.3",
+ "serve-handler": "6.1.3",
+ "update-check": "1.5.2"
+ },
+ "bin": {
+ "serve": "bin/serve.js"
+ }
+ },
+ "node_modules/serve-handler": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz",
+ "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==",
+ "dev": true,
+ "dependencies": {
+ "bytes": "3.0.0",
+ "content-disposition": "0.5.2",
+ "fast-url-parser": "1.1.3",
+ "mime-types": "2.1.18",
+ "minimatch": "3.0.4",
+ "path-is-inside": "1.0.2",
+ "path-to-regexp": "2.2.1",
+ "range-parser": "1.2.0"
+ }
+ },
+ "node_modules/serve-handler/node_modules/mime-db": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
+ "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve-handler/node_modules/mime-types": {
+ "version": "2.1.18",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
+ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "~1.33.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve/node_modules/chalk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/serve/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
+ "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==",
+ "dev": true
+ },
+ "node_modules/slice-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/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/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "dependencies": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-license-ids": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz",
+ "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==",
+ "dev": true
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "node_modules/string-width": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+ "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+ "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+ "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-eof": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/table": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz",
+ "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^8.0.1",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/table/node_modules/ajv": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz",
+ "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/table/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "node_modules/taffydb": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz",
+ "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=",
+ "dev": true
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
+ "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
+ "dev": true,
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.0",
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.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==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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==",
+ "dev": true
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+ "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has-bigints": "^1.0.1",
+ "has-symbols": "^1.0.2",
+ "which-boxed-primitive": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/underscore": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
+ "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==",
+ "dev": true
+ },
+ "node_modules/update-check": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz",
+ "integrity": "sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==",
+ "dev": true,
+ "dependencies": {
+ "registry-auth-token": "3.3.2",
+ "registry-url": "3.1.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/v8-compile-cache": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+ "dev": true
+ },
+ "node_modules/validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "dependencies": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "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==",
+ "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/widest-line": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
+ "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/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/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "node_modules/xmlcreate": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz",
+ "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==",
+ "dev": true
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ },
"dependencies": {
"@babel/code-frame": {
"version": "7.12.11",
@@ -78,6 +3079,22 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
+ "@zeit/schemas": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz",
+ "integrity": "sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==",
+ "dev": true
+ },
+ "accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ }
+ },
"acorn": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
@@ -88,7 +3105,8 @@
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"ajv": {
"version": "6.12.6",
@@ -102,6 +3120,15 @@
"uri-js": "^4.2.2"
}
},
+ "ansi-align": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.1.0"
+ }
+ },
"ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@@ -123,6 +3150,18 @@
"color-convert": "^1.9.0"
}
},
+ "arch": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
+ "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
+ "dev": true
+ },
+ "arg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz",
+ "integrity": "sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==",
+ "dev": true
+ },
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -174,6 +3213,22 @@
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true
},
+ "boxen": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
+ "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==",
+ "dev": true,
+ "requires": {
+ "ansi-align": "^3.0.0",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.1.0",
+ "cli-boxes": "^2.2.1",
+ "string-width": "^4.2.2",
+ "type-fest": "^0.20.2",
+ "widest-line": "^3.1.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -184,6 +3239,12 @@
"concat-map": "0.0.1"
}
},
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+ "dev": true
+ },
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -200,6 +3261,12 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
+ "camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true
+ },
"catharsis": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
@@ -260,6 +3327,23 @@
}
}
},
+ "cli-boxes": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
+ "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
+ "dev": true
+ },
+ "clipboardy": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
+ "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==",
+ "dev": true,
+ "requires": {
+ "arch": "^2.1.1",
+ "execa": "^1.0.0",
+ "is-wsl": "^2.1.1"
+ }
+ },
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -275,6 +3359,47 @@
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
+ "compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "requires": {
+ "mime-db": ">= 1.43.0 < 2"
+ }
+ },
+ "compression": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz",
+ "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.14",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.1",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -287,6 +3412,12 @@
"integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==",
"dev": true
},
+ "content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
+ "dev": true
+ },
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -307,6 +3438,12 @@
"ms": "2.1.2"
}
},
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true
+ },
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@@ -337,6 +3474,15 @@
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
"enquirer": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
@@ -661,6 +3807,72 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -679,6 +3891,23 @@
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
+ "fast-url-parser": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz",
+ "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=",
+ "dev": true,
+ "requires": {
+ "punycode": "^1.3.2"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ }
+ }
+ },
"file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -742,6 +3971,15 @@
"has-symbols": "^1.0.1"
}
},
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
"glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
@@ -851,6 +4089,12 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -893,6 +4137,12 @@
"integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==",
"dev": true
},
+ "is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true
+ },
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -936,6 +4186,12 @@
"has-symbols": "^1.0.2"
}
},
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
+ },
"is-string": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz",
@@ -951,6 +4207,15 @@
"has-symbols": "^1.0.2"
}
},
+ "is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
+ },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -1139,7 +4404,8 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz",
"integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"marked": {
"version": "2.0.7",
@@ -1153,6 +4419,21 @@
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
"dev": true
},
+ "mime-db": {
+ "version": "1.51.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
+ "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.34",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
+ "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.51.0"
+ }
+ },
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -1186,6 +4467,18 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
+ "negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
+ "dev": true
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@@ -1206,6 +4499,23 @@
}
}
},
+ "npm-run-path": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+ "dev": true,
+ "requires": {
+ "path-key": "^2.0.0"
+ },
+ "dependencies": {
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ }
+ }
+ },
"object-inspect": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz",
@@ -1252,6 +4562,12 @@
"es-abstract": "^1.18.2"
}
},
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true
+ },
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1275,6 +4591,12 @@
"word-wrap": "^1.2.3"
}
},
+ "p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+ "dev": true
+ },
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
@@ -1330,6 +4652,12 @@
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -1342,6 +4670,12 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
+ "path-to-regexp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz",
+ "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==",
+ "dev": true
+ },
"path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
@@ -1387,12 +4721,48 @@
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
+ "range-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
+ "dev": true
+ },
+ "rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "dev": true
+ }
+ }
+ },
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
@@ -1420,6 +4790,25 @@
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
"dev": true
},
+ "registry-auth-token": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
+ "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
+ "dev": true,
+ "requires": {
+ "rc": "^1.1.6",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "registry-url": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+ "dev": true,
+ "requires": {
+ "rc": "^1.0.1"
+ }
+ },
"require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -1460,6 +4849,12 @@
"glob": "^7.1.3"
}
},
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@@ -1469,6 +4864,75 @@
"lru-cache": "^6.0.0"
}
},
+ "serve": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/serve/-/serve-13.0.2.tgz",
+ "integrity": "sha512-71R6fKvNgKrqARAag6lYJNnxDzpH7DCNrMuvPY5PLVaC2PDhJsGTj/34o4o4tPWhTuLgEXqvgnAWbATQ9zGZTQ==",
+ "dev": true,
+ "requires": {
+ "@zeit/schemas": "2.6.0",
+ "ajv": "6.12.6",
+ "arg": "2.0.0",
+ "boxen": "5.1.2",
+ "chalk": "2.4.1",
+ "clipboardy": "2.3.0",
+ "compression": "1.7.3",
+ "serve-handler": "6.1.3",
+ "update-check": "1.5.2"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ }
+ }
+ },
+ "serve-handler": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz",
+ "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.0.0",
+ "content-disposition": "0.5.2",
+ "fast-url-parser": "1.1.3",
+ "mime-types": "2.1.18",
+ "minimatch": "3.0.4",
+ "path-is-inside": "1.0.2",
+ "path-to-regexp": "2.2.1",
+ "range-parser": "1.2.0"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
+ "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.18",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
+ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
+ "dev": true,
+ "requires": {
+ "mime-db": "~1.33.0"
+ }
+ }
+ }
+ },
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -1484,6 +4948,12 @@
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
+ "signal-exit": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
+ "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==",
+ "dev": true
+ },
"slice-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
@@ -1605,6 +5075,12 @@
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
},
+ "strip-eof": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+ "dev": true
+ },
"strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -1717,6 +5193,16 @@
"integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==",
"dev": true
},
+ "update-check": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz",
+ "integrity": "sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==",
+ "dev": true,
+ "requires": {
+ "registry-auth-token": "3.3.2",
+ "registry-url": "3.1.0"
+ }
+ },
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -1742,6 +5228,12 @@
"spdx-expression-parse": "^3.0.0"
}
},
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+ "dev": true
+ },
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -1764,12 +5256,58 @@
"is-symbol": "^1.0.3"
}
},
+ "widest-line": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
+ "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.0.0"
+ }
+ },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"dev": true
},
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "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,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
diff --git a/platform/javascript/package.json b/platform/javascript/package.json
index 9dafae30c5..2ff1544837 100644
--- a/platform/javascript/package.json
+++ b/platform/javascript/package.json
@@ -2,9 +2,8 @@
"name": "godot",
"private": true,
"version": "1.0.0",
- "description": "Linting setup for Godot's HTML5 platform code",
+ "description": "Development and linting setup for Godot's HTML5 platform code",
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
"docs": "jsdoc --template js/jsdoc2rst/ js/engine/engine.js js/engine/config.js --destination ''",
"lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules && npm run lint:tools",
"lint:engine": "eslint \"js/engine/*.js\" --no-eslintrc -c .eslintrc.engine.js",
@@ -15,7 +14,8 @@
"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:tools": "npm run lint:tools -- --fix",
+ "serve": "serve"
},
"author": "Godot Engine contributors",
"license": "MIT",
@@ -23,6 +23,7 @@
"eslint": "^7.28.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.23.4",
- "jsdoc": "^3.6.7"
+ "jsdoc": "^3.6.7",
+ "serve": "^13.0.2"
}
}
diff --git a/platform/javascript/serve.json b/platform/javascript/serve.json
new file mode 100644
index 0000000000..f2ef24751f
--- /dev/null
+++ b/platform/javascript/serve.json
@@ -0,0 +1,21 @@
+{
+ "public": "../../bin",
+ "headers": [{
+ "source": "**/*",
+ "headers": [
+ {
+ "key": "Cross-Origin-Embedder-Policy",
+ "value": "require-corp"
+ }, {
+ "key": "Cross-Origin-Opener-Policy",
+ "value": "same-origin"
+ }, {
+ "key": "Access-Control-Allow-Origin",
+ "value": "*"
+ }, {
+ "key": "Cache-Control",
+ "value": "no-store, max-age=0"
+ }
+ ]
+ }]
+}
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 747b5beeda..198eacd1f3 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -64,7 +64,6 @@
// EWMH
#define _NET_WM_STATE_REMOVE 0L // remove/unset property
#define _NET_WM_STATE_ADD 1L // add/set property
-#define _NET_WM_STATE_TOGGLE 2L // toggle property
#include <dlfcn.h>
#include <fcntl.h>
@@ -324,20 +323,21 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
//flush pending motion events
_flush_mouse_motion();
- WindowData &main_window = windows[MAIN_WINDOW_ID];
+ WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ WindowData &window = windows[window_id];
if (XGrabPointer(
- x11_display, main_window.x11_window, True,
+ x11_display, window.x11_window, True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
- GrabModeAsync, GrabModeAsync, windows[MAIN_WINDOW_ID].x11_window, None, CurrentTime) != GrabSuccess) {
+ GrabModeAsync, GrabModeAsync, window.x11_window, None, CurrentTime) != GrabSuccess) {
ERR_PRINT("NO GRAB");
}
if (mouse_mode == MOUSE_MODE_CAPTURED) {
- center.x = main_window.size.width / 2;
- center.y = main_window.size.height / 2;
+ center.x = window.size.width / 2;
+ center.y = window.size.height / 2;
- XWarpPointer(x11_display, None, main_window.x11_window,
+ XWarpPointer(x11_display, None, window.x11_window,
0, 0, 0, 0, (int)center.x, (int)center.y);
Input::get_singleton()->set_mouse_position(center);
@@ -359,27 +359,13 @@ void DisplayServerX11::mouse_warp_to_position(const Point2i &p_to) {
if (mouse_mode == MOUSE_MODE_CAPTURED) {
last_mouse_pos = p_to;
} else {
- XWarpPointer(x11_display, None, windows[MAIN_WINDOW_ID].x11_window,
+ WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ XWarpPointer(x11_display, None, windows[window_id].x11_window,
0, 0, 0, 0, (int)p_to.x, (int)p_to.y);
}
}
Point2i DisplayServerX11::mouse_get_position() const {
- int root_x, root_y;
- int win_x, win_y;
- unsigned int mask_return;
- Window window_returned;
-
- Bool result = XQueryPointer(x11_display, RootWindow(x11_display, DefaultScreen(x11_display)), &window_returned,
- &window_returned, &root_x, &root_y, &win_x, &win_y,
- &mask_return);
- if (result == True) {
- return Point2i(root_x, root_y);
- }
- return Point2i();
-}
-
-Point2i DisplayServerX11::mouse_get_absolute_position() const {
int number_of_screens = XScreenCount(x11_display);
for (int i = 0; i < number_of_screens; i++) {
Window root, child;
@@ -1170,6 +1156,24 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
windows.erase(p_id);
}
+int64_t DisplayServerX11::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
+ ERR_FAIL_COND_V(!windows.has(p_window), 0);
+ switch (p_handle_type) {
+ case DISPLAY_HANDLE: {
+ return (int64_t)x11_display;
+ }
+ case WINDOW_HANDLE: {
+ return (int64_t)windows[p_window].x11_window;
+ }
+ case WINDOW_VIEW: {
+ return 0; // Not supported.
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
void DisplayServerX11::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
@@ -1330,8 +1334,9 @@ int DisplayServerX11::window_get_current_screen(WindowID p_window) const {
void DisplayServerX11::gl_window_make_current(DisplayServer::WindowID p_window_id) {
#if defined(GLES3_ENABLED)
- if (gl_manager)
+ if (gl_manager) {
gl_manager->window_make_current(p_window_id);
+ }
#endif
}
@@ -3350,7 +3355,7 @@ void DisplayServerX11::process_events() {
DEBUG_LOG_X11("[%u] FocusIn window=%lu (%u), mode='%u' \n", frame, event.xfocus.window, window_id, event.xfocus.mode);
WindowData &wd = windows[window_id];
-
+ last_focused_window = window_id;
wd.focused = true;
if (wd.xic) {
@@ -3416,7 +3421,7 @@ void DisplayServerX11::process_events() {
if (mouse_mode_grab) {
for (const KeyValue<WindowID, WindowData> &E : windows) {
- //dear X11, I try, I really try, but you never work, you do whathever you want.
+ //dear X11, I try, I really try, but you never work, you do whatever you want.
if (mouse_mode == MOUSE_MODE_CAPTURED) {
// Show the cursor if we're in captured mode so it doesn't look weird.
XUndefineCursor(x11_display, E.value.x11_window);
@@ -3550,9 +3555,9 @@ void DisplayServerX11::process_events() {
// The X11 API requires filtering one-by-one through the motion
// notify events, in order to figure out which event is the one
// generated by warping the mouse pointer.
-
+ WindowID focused_window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
while (true) {
- if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == windows[MAIN_WINDOW_ID].size.width / 2 && event.xmotion.y == windows[MAIN_WINDOW_ID].size.height / 2) {
+ if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == windows[focused_window_id].size.width / 2 && event.xmotion.y == windows[focused_window_id].size.height / 2) {
//this is likely the warp event since it was warped here
center = Vector2(event.xmotion.x, event.xmotion.y);
break;
@@ -3627,9 +3632,8 @@ void DisplayServerX11::process_events() {
// Reset to prevent lingering motion
xi.relative_motion.x = 0;
xi.relative_motion.y = 0;
-
if (mouse_mode == MOUSE_MODE_CAPTURED) {
- pos = Point2i(windows[MAIN_WINDOW_ID].size.width / 2, windows[MAIN_WINDOW_ID].size.height / 2);
+ pos = Point2i(windows[focused_window_id].size.width / 2, windows[focused_window_id].size.height / 2);
}
Ref<InputEventMouseMotion> mm;
@@ -3647,7 +3651,6 @@ void DisplayServerX11::process_events() {
mm->set_button_mask((MouseButton)mouse_get_button_state());
mm->set_position(pos);
mm->set_global_position(pos);
- Input::get_singleton()->set_mouse_position(pos);
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
mm->set_relative(rel);
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index 1dcedabb1a..c17e120db5 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -143,7 +143,7 @@ class DisplayServerX11 : public DisplayServer {
bool borderless = false;
bool resize_disabled = false;
Vector2i last_position_before_fs;
- bool focused = false;
+ bool focused = true;
bool minimized = false;
unsigned int focus_order = 0;
@@ -151,6 +151,8 @@ class DisplayServerX11 : public DisplayServer {
Map<WindowID, WindowData> windows;
+ WindowID last_focused_window = INVALID_WINDOW_ID;
+
WindowID window_id_counter = MAIN_WINDOW_ID;
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect);
@@ -289,7 +291,6 @@ public:
virtual void mouse_warp_to_position(const Point2i &p_to) override;
virtual Point2i mouse_get_position() const override;
- virtual Point2i mouse_get_absolute_position() const override;
virtual MouseButton mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
@@ -317,6 +318,8 @@ public:
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
+ virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
+
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
diff --git a/platform/linuxbsd/gl_manager_x11.cpp b/platform/linuxbsd/gl_manager_x11.cpp
index 1721d0e0b3..d3fb1d6705 100644
--- a/platform/linuxbsd/gl_manager_x11.cpp
+++ b/platform/linuxbsd/gl_manager_x11.cpp
@@ -68,8 +68,9 @@ static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
int GLManager_X11::_find_or_create_display(Display *p_x11_display) {
for (unsigned int n = 0; n < _displays.size(); n++) {
const GLDisplay &d = _displays[n];
- if (d.x11_display == p_x11_display)
+ if (d.x11_display == p_x11_display) {
return n;
+ }
}
// create
@@ -82,8 +83,7 @@ int GLManager_X11::_find_or_create_display(Display *p_x11_display) {
GLDisplay &d = _displays[new_display_id];
d.context = memnew(GLManager_X11_Private);
- ;
- d.context->glx_context = 0;
+ d.context->glx_context = nullptr;
//Error err = _create_context(d);
_create_context(d);
@@ -124,7 +124,7 @@ Error GLManager_X11::_create_context(GLDisplay &gl_display) {
};
int fbcount;
- GLXFBConfig fbconfig = 0;
+ GLXFBConfig fbconfig = nullptr;
XVisualInfo *vi = nullptr;
gl_display.x_swa.event_mask = StructureNotifyMask;
@@ -137,8 +137,9 @@ Error GLManager_X11::_create_context(GLDisplay &gl_display) {
for (int i = 0; i < fbcount; i++) {
vi = (XVisualInfo *)glXGetVisualFromFBConfig(x11_display, fbc[i]);
- if (!vi)
+ if (!vi) {
continue;
+ }
XRenderPictFormat *pict_format = XRenderFindVisualFormat(x11_display, vi->visual);
if (!pict_format) {
@@ -262,22 +263,26 @@ void GLManager_X11::window_destroy(DisplayServer::WindowID p_window_id) {
}
void GLManager_X11::release_current() {
- if (!_current_window)
+ if (!_current_window) {
return;
+ }
glXMakeCurrent(_x_windisp.x11_display, None, nullptr);
}
void GLManager_X11::window_make_current(DisplayServer::WindowID p_window_id) {
- if (p_window_id == -1)
+ if (p_window_id == -1) {
return;
+ }
GLWindow &win = _windows[p_window_id];
- if (!win.in_use)
+ if (!win.in_use) {
return;
+ }
// noop
- if (&win == _current_window)
+ if (&win == _current_window) {
return;
+ }
const GLDisplay &disp = get_display(win.gldisplay_id);
@@ -287,8 +292,9 @@ void GLManager_X11::window_make_current(DisplayServer::WindowID p_window_id) {
}
void GLManager_X11::make_current() {
- if (!_current_window)
+ if (!_current_window) {
return;
+ }
if (!_current_window->in_use) {
WARN_PRINT("current window not in use!");
return;
@@ -301,8 +307,9 @@ void GLManager_X11::swap_buffers() {
// NO NEED TO CALL SWAP BUFFERS for each window...
// see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glXSwapBuffers.xml
- if (!_current_window)
+ if (!_current_window) {
return;
+ }
if (!_current_window->in_use) {
WARN_PRINT("current window not in use!");
return;
@@ -335,19 +342,23 @@ void GLManager_X11::set_use_vsync(bool p_use) {
}
// we need an active window to get a display to set the vsync
- if (!_current_window)
+ if (!_current_window) {
return;
+ }
const GLDisplay &disp = get_current_display();
if (!setup) {
setup = true;
String extensions = glXQueryExtensionsString(disp.x11_display, DefaultScreen(disp.x11_display));
- if (extensions.find("GLX_EXT_swap_control") != -1)
+ if (extensions.find("GLX_EXT_swap_control") != -1) {
glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalEXT");
- if (extensions.find("GLX_MESA_swap_control") != -1)
+ }
+ if (extensions.find("GLX_MESA_swap_control") != -1) {
glXSwapIntervalMESA = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalMESA");
- if (extensions.find("GLX_SGI_swap_control") != -1)
+ }
+ if (extensions.find("GLX_SGI_swap_control") != -1) {
glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalSGI");
+ }
}
int val = p_use ? 1 : 0;
if (glXSwapIntervalMESA) {
@@ -357,8 +368,9 @@ void GLManager_X11::set_use_vsync(bool p_use) {
} else if (glXSwapIntervalEXT) {
GLXDrawable drawable = glXGetCurrentDrawable();
glXSwapIntervalEXT(disp.x11_display, drawable, val);
- } else
+ } else {
return;
+ }
use_vsync = p_use;
}
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 37606de3bc..5eda42fea6 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -61,13 +61,9 @@ JoypadLinux::Joypad::~Joypad() {
void JoypadLinux::Joypad::reset() {
dpad = HatMask::CENTER;
fd = -1;
-
- Input::JoyAxisValue jx;
- jx.min = -1;
- jx.value = 0.0f;
for (int i = 0; i < MAX_ABS; i++) {
abs_map[i] = -1;
- curr_axis[i] = jx;
+ curr_axis[i] = 0;
}
}
@@ -429,23 +425,11 @@ void JoypadLinux::joypad_vibration_stop(int p_id, uint64_t p_timestamp) {
joy.ff_effect_timestamp = p_timestamp;
}
-Input::JoyAxisValue JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
+float JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
int min = p_abs->minimum;
int max = p_abs->maximum;
- Input::JoyAxisValue jx;
-
- if (min < 0) {
- jx.min = -1;
- if (p_value < 0) {
- jx.value = (float)-p_value / min;
- } else {
- jx.value = (float)p_value / max;
- }
- } else if (min == 0) {
- jx.min = 0;
- jx.value = 0.0f + (float)p_value / max;
- }
- return jx;
+ // Convert to a value between -1.0f and 1.0f.
+ return 2.0f * (p_value - min) / (max - min) - 1.0f;
}
void JoypadLinux::process_joypads() {
@@ -514,7 +498,7 @@ void JoypadLinux::process_joypads() {
return;
}
if (joy->abs_map[ev.code] != -1 && joy->abs_info[ev.code]) {
- Input::JoyAxisValue value = axis_correct(joy->abs_info[ev.code], ev.value);
+ float value = axis_correct(joy->abs_info[ev.code], ev.value);
joy->curr_axis[joy->abs_map[ev.code]] = value;
}
break;
diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h
index edbcfcbfa6..9177465547 100644
--- a/platform/linuxbsd/joypad_linux.h
+++ b/platform/linuxbsd/joypad_linux.h
@@ -52,7 +52,7 @@ private:
};
struct Joypad {
- Input::JoyAxisValue curr_axis[MAX_ABS];
+ float curr_axis[MAX_ABS];
int key_map[MAX_KEY];
int abs_map[MAX_ABS];
HatMask dpad = HatMask::CENTER;
@@ -96,7 +96,7 @@ private:
void joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop(int p_id, uint64_t p_timestamp);
- Input::JoyAxisValue axis_correct(const input_absinfo *p_abs, int p_value) const;
+ float axis_correct(const input_absinfo *p_abs, int p_value) const;
};
#endif // JOYDEV_ENABLED
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index b5f127bb16..e95a865636 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -448,7 +448,7 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
// Create needed directories for decided trash can location.
{
- DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
Error err = dir_access->make_dir_recursive(trash_path);
// Issue an error if trash can is not created properly.
@@ -457,7 +457,6 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"/files");
err = dir_access->make_dir_recursive(trash_path + "/info");
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"/info");
- memdelete(dir_access);
}
// The trash can is successfully created, now we check that we don't exceed our file name length limit.
@@ -497,16 +496,15 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
String trash_info = "[Trash Info]\nPath=" + p_path.uri_encode() + "\nDeletionDate=" + timestamp + "\n";
{
Error err;
- FileAccess *file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err);
+ FileAccessRef file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't create trashinfo file:" + trash_path + "/info/" + file_name + ".trashinfo");
file->store_string(trash_info);
file->close();
// Rename our resource before moving it to the trash can.
- DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
err = dir_access->rename(p_path, p_path.get_base_dir() + "/" + file_name);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't rename file \"" + p_path + "\"");
- memdelete(dir_access);
}
// Move the given resource to the trash can.
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index 4b558d973d..719a45c2a8 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -98,6 +98,8 @@ public:
NSTimeInterval last_warp = 0;
bool ignore_warp = false;
+ float display_max_scale = 1.f;
+
Vector<KeyEvent> key_event_buffer;
int key_event_pos;
@@ -143,6 +145,8 @@ public:
Map<WindowID, WindowData> windows;
+ WindowID last_focused_window = INVALID_WINDOW_ID;
+
WindowID window_id_counter = MAIN_WINDOW_ID;
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect);
@@ -214,7 +218,6 @@ public:
virtual void mouse_warp_to_position(const Point2i &p_to) override;
virtual Point2i mouse_get_position() const override;
- virtual Point2i mouse_get_absolute_position() const override;
virtual MouseButton mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
@@ -281,6 +284,8 @@ public:
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
+ virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
+
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void gl_window_make_current(DisplayServer::WindowID p_window_id) override;
@@ -314,9 +319,6 @@ public:
virtual void set_native_icon(const String &p_filename) override;
virtual void set_icon(const Ref<Image> &p_icon) override;
- virtual void console_set_visible(bool p_enabled) override;
- virtual bool is_console_visible() const override;
-
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 27d302a984..f7add5b688 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -158,12 +158,7 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
}
if (wd.transient_parent != DisplayServerOSX::INVALID_WINDOW_ID) {
- DisplayServerOSX::WindowData &pwd = DS_OSX->windows[wd.transient_parent];
- [pwd.window_object makeKeyAndOrderFront:nil]; // Move focus back to parent.
DS_OSX->window_set_transient(window_id, DisplayServerOSX::INVALID_WINDOW_ID);
- } else if ((window_id != DisplayServerOSX::MAIN_WINDOW_ID) && (DS_OSX->windows.size() == 1)) {
- DisplayServerOSX::WindowData &pwd = DS_OSX->windows[DisplayServerOSX::MAIN_WINDOW_ID];
- [pwd.window_object makeKeyAndOrderFront:nil]; // Move focus back to main window if there is no parent or other windows left.
}
#if defined(GLES3_ENABLED)
@@ -321,11 +316,12 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
CGWarpMouseCursorPosition(lMouseWarpPos);
} else {
- _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
+ _ALLOW_DISCARD_ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
Input::get_singleton()->set_mouse_position(wd.mouse_pos);
}
DS_OSX->window_focused = true;
+ DS_OSX->last_focused_window = window_id;
DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
}
@@ -360,6 +356,7 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
DS_OSX->window_focused = true;
+ DS_OSX->last_focused_window = window_id;
DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
}
@@ -792,7 +789,6 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, M
mm->set_relative(relativeMotion);
_get_key_modifier_state([event modifierFlags], mm);
- Input::get_singleton()->set_mouse_position(wd.mouse_pos);
Input::get_singleton()->parse_input_event(mm);
}
@@ -1396,7 +1392,7 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy
double deltaX, deltaY;
- _get_mouse_pos(wd, [event locationInWindow]);
+ _ALLOW_DISCARD_ _get_mouse_pos(wd, [event locationInWindow]);
deltaX = [event scrollingDeltaX];
deltaY = [event scrollingDeltaY];
@@ -1480,7 +1476,6 @@ bool DisplayServerOSX::has_feature(Feature p_feature) const {
case FEATURE_CURSOR_SHAPE:
case FEATURE_CUSTOM_CURSOR_SHAPE:
case FEATURE_NATIVE_DIALOG:
- //case FEATURE_CONSOLE_WINDOW:
case FEATURE_IME:
case FEATURE_WINDOW_TRANSPARENCY:
case FEATURE_HIDPI:
@@ -1920,7 +1915,8 @@ void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) {
return;
}
- WindowData &wd = windows[MAIN_WINDOW_ID];
+ WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ WindowData &wd = windows[window_id];
if (p_mode == MOUSE_MODE_CAPTURED) {
// Apple Docs state that the display parameter is not used.
// "This parameter is not used. By default, you may pass kCGDirectMainDisplay."
@@ -1979,7 +1975,8 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
if (mouse_mode == MOUSE_MODE_CAPTURED) {
last_mouse_pos = p_to;
} else {
- WindowData &wd = windows[MAIN_WINDOW_ID];
+ WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ WindowData &wd = windows[window_id];
//local point in window coords
const NSRect contentRect = [wd.window_view frame];
@@ -2002,10 +1999,6 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
}
Point2i DisplayServerOSX::mouse_get_position() const {
- return last_mouse_pos;
-}
-
-Point2i DisplayServerOSX::mouse_get_absolute_position() const {
_THREAD_SAFE_METHOD_
const NSPoint mouse_pos = [NSEvent mouseLocation];
@@ -2072,10 +2065,8 @@ int DisplayServerOSX::get_screen_count() const {
// to convert between OS X native screen coordinates and the ones expected by Godot
static bool displays_arrangement_dirty = true;
-static bool displays_scale_dirty = true;
static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
displays_arrangement_dirty = true;
- displays_scale_dirty = true;
}
Point2i DisplayServerOSX::_get_screens_origin() const {
@@ -2186,15 +2177,8 @@ float DisplayServerOSX::screen_get_scale(int p_screen) const {
float DisplayServerOSX::screen_get_max_scale() const {
_THREAD_SAFE_METHOD_
- static float scale = 1.f;
- if (displays_scale_dirty) {
- int screen_count = get_screen_count();
- for (int i = 0; i < screen_count; i++) {
- scale = fmax(scale, screen_get_scale(i));
- }
- displays_scale_dirty = false;
- }
- return scale;
+ // Note: Do not update max display scale on screen configuration change, existing editor windows can't be rescaled on the fly.
+ return display_max_scale;
}
Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const {
@@ -2381,8 +2365,24 @@ int DisplayServerOSX::window_get_current_screen(WindowID p_window) const {
void DisplayServerOSX::window_set_current_screen(int p_screen, WindowID p_window) {
_THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ bool was_fullscreen = false;
+ if (wd.fullscreen) {
+ // Temporary exit fullscreen mode to move window.
+ [wd.window_object toggleFullScreen:nil];
+ was_fullscreen = true;
+ }
+
Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
window_set_position(wpos + screen_get_position(p_screen), p_window);
+
+ if (was_fullscreen) {
+ // Re-enter fullscreen mode.
+ [wd.window_object toggleFullScreen:nil];
+ }
}
void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent) {
@@ -2405,7 +2405,7 @@ void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent
wd_window.transient_parent = INVALID_WINDOW_ID;
wd_parent.transient_children.erase(p_window);
- [wd_parent.window_object removeChildWindow:wd_window.window_object];
+ [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
} else {
ERR_FAIL_COND(!windows.has(p_parent));
ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
@@ -2414,7 +2414,7 @@ void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent
wd_window.transient_parent = p_parent;
wd_parent.transient_children.insert(p_window);
- [wd_parent.window_object addChildWindow:wd_window.window_object ordered:NSWindowAbove];
+ [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
}
}
@@ -2424,7 +2424,9 @@ Point2i DisplayServerOSX::window_get_position(WindowID p_window) const {
ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
const WindowData &wd = windows[p_window];
- NSRect nsrect = [wd.window_object frame];
+ // Use content rect position (without titlebar / window border).
+ const NSRect contentRect = [wd.window_view frame];
+ const NSRect nsrect = [wd.window_object convertRectToScreen:contentRect];
Point2i pos;
// Return the position of the top-left corner, for OS X the y starts at the bottom
@@ -2452,10 +2454,19 @@ void DisplayServerOSX::window_set_position(const Point2i &p_position, WindowID p
position += _get_screens_origin();
position /= screen_get_max_scale();
- [wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x, position.y)];
+ // Remove titlebar / window border size.
+ const NSRect contentRect = [wd.window_view frame];
+ const NSRect windowRect = [wd.window_object frame];
+ const NSRect nsrect = [wd.window_object convertRectToScreen:contentRect];
+ Point2i offset;
+ offset.x = (nsrect.origin.x - windowRect.origin.x);
+ offset.y = (nsrect.origin.y + nsrect.size.height);
+ offset.y -= (windowRect.origin.y + windowRect.size.height);
+
+ [wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x - offset.x, position.y - offset.y)];
_update_window(wd);
- _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
+ _ALLOW_DISCARD_ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
}
void DisplayServerOSX::window_set_max_size(const Size2i p_size, WindowID p_window) {
@@ -3519,6 +3530,24 @@ DisplayServer::WindowID DisplayServerOSX::get_window_at_screen_position(const Po
return INVALID_WINDOW_ID;
}
+int64_t DisplayServerOSX::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
+ ERR_FAIL_COND_V(!windows.has(p_window), 0);
+ switch (p_handle_type) {
+ case DISPLAY_HANDLE: {
+ return 0; // Not supported.
+ }
+ case WINDOW_HANDLE: {
+ return (int64_t)windows[p_window].window_object;
+ }
+ case WINDOW_VIEW: {
+ return (int64_t)windows[p_window].window_view;
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
void DisplayServerOSX::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -3679,14 +3708,6 @@ void DisplayServerOSX::swap_buffers() {
#endif
}
-void DisplayServerOSX::console_set_visible(bool p_enabled) {
- //TODO - open terminal and redirect
-}
-
-bool DisplayServerOSX::is_console_visible() const {
- return isatty(STDIN_FILENO);
-}
-
DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
@@ -3708,7 +3729,11 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
keyboard_layout_dirty = true;
displays_arrangement_dirty = true;
- displays_scale_dirty = true;
+
+ int screen_count = get_screen_count();
+ for (int i = 0; i < screen_count; i++) {
+ display_max_scale = fmax(display_max_scale, screen_get_scale(i));
+ }
// Register to be notified on keyboard layout changes
CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
diff --git a/platform/osx/export/codesign.cpp b/platform/osx/export/codesign.cpp
new file mode 100644
index 0000000000..8ea6ff519d
--- /dev/null
+++ b/platform/osx/export/codesign.cpp
@@ -0,0 +1,1564 @@
+/*************************************************************************/
+/* codesign.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "codesign.h"
+
+#include "lipo.h"
+#include "macho.h"
+#include "plist.h"
+
+#include "core/os/os.h"
+#include "editor/editor_settings.h"
+#include "modules/modules_enabled.gen.h" // For regex.
+
+#include <ctime>
+
+#ifdef MODULE_REGEX_ENABLED
+
+/*************************************************************************/
+/* CodeSignCodeResources */
+/*************************************************************************/
+
+String CodeSignCodeResources::hash_sha1_base64(const String &p_path) {
+ FileAccessRef fa = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(!fa, String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
+
+ CryptoCore::SHA1Context ctx;
+ ctx.start();
+
+ unsigned char step[4096];
+ while (true) {
+ uint64_t br = fa->get_buffer(step, 4096);
+ if (br > 0) {
+ ctx.update(step, br);
+ }
+ if (br < 4096) {
+ break;
+ }
+ }
+
+ unsigned char hash[0x14];
+ ctx.finish(hash);
+ fa->close();
+
+ return CryptoCore::b64_encode_str(hash, 0x14);
+}
+
+String CodeSignCodeResources::hash_sha256_base64(const String &p_path) {
+ FileAccessRef fa = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(!fa, String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
+
+ CryptoCore::SHA256Context ctx;
+ ctx.start();
+
+ unsigned char step[4096];
+ while (true) {
+ uint64_t br = fa->get_buffer(step, 4096);
+ if (br > 0) {
+ ctx.update(step, br);
+ }
+ if (br < 4096) {
+ break;
+ }
+ }
+
+ unsigned char hash[0x20];
+ ctx.finish(hash);
+ fa->close();
+
+ return CryptoCore::b64_encode_str(hash, 0x20);
+}
+
+void CodeSignCodeResources::add_rule1(const String &p_rule, const String &p_key, int p_weight, bool p_store) {
+ rules1.push_back(CRRule(p_rule, p_key, p_weight, p_store));
+}
+
+void CodeSignCodeResources::add_rule2(const String &p_rule, const String &p_key, int p_weight, bool p_store) {
+ rules2.push_back(CRRule(p_rule, p_key, p_weight, p_store));
+}
+
+CodeSignCodeResources::CRMatch CodeSignCodeResources::match_rules1(const String &p_path) const {
+ CRMatch found = CRMatch::CR_MATCH_NO;
+ int weight = 0;
+ for (int i = 0; i < rules1.size(); i++) {
+ RegEx regex = RegEx(rules1[i].file_pattern);
+ if (regex.search(p_path).is_valid()) {
+ if (rules1[i].key == "omit") {
+ return CRMatch::CR_MATCH_NO;
+ } else if (rules1[i].key == "nested") {
+ if (weight <= rules1[i].weight) {
+ found = CRMatch::CR_MATCH_NESTED;
+ weight = rules1[i].weight;
+ }
+ } else if (rules1[i].key == "optional") {
+ if (weight <= rules1[i].weight) {
+ found = CRMatch::CR_MATCH_OPTIONAL;
+ weight = rules1[i].weight;
+ }
+ } else {
+ if (weight <= rules1[i].weight) {
+ found = CRMatch::CR_MATCH_YES;
+ weight = rules1[i].weight;
+ }
+ }
+ }
+ }
+ return found;
+}
+
+CodeSignCodeResources::CRMatch CodeSignCodeResources::match_rules2(const String &p_path) const {
+ CRMatch found = CRMatch::CR_MATCH_NO;
+ int weight = 0;
+ for (int i = 0; i < rules2.size(); i++) {
+ RegEx regex = RegEx(rules2[i].file_pattern);
+ if (regex.search(p_path).is_valid()) {
+ if (rules2[i].key == "omit") {
+ return CRMatch::CR_MATCH_NO;
+ } else if (rules2[i].key == "nested") {
+ if (weight <= rules2[i].weight) {
+ found = CRMatch::CR_MATCH_NESTED;
+ weight = rules2[i].weight;
+ }
+ } else if (rules2[i].key == "optional") {
+ if (weight <= rules2[i].weight) {
+ found = CRMatch::CR_MATCH_OPTIONAL;
+ weight = rules2[i].weight;
+ }
+ } else {
+ if (weight <= rules2[i].weight) {
+ found = CRMatch::CR_MATCH_YES;
+ weight = rules2[i].weight;
+ }
+ }
+ }
+ }
+ return found;
+}
+
+bool CodeSignCodeResources::add_file1(const String &p_root, const String &p_path) {
+ CRMatch found = match_rules1(p_path);
+ if (found != CRMatch::CR_MATCH_YES && found != CRMatch::CR_MATCH_OPTIONAL) {
+ return true; // No match.
+ }
+
+ CRFile f;
+ f.name = p_path;
+ f.optional = (found == CRMatch::CR_MATCH_OPTIONAL);
+ f.nested = false;
+ f.hash = hash_sha1_base64(p_root.plus_file(p_path));
+ print_verbose(vformat("CodeSign/CodeResources: File(V1) %s hash1:%s", f.name, f.hash));
+
+ files1.push_back(f);
+ return true;
+}
+
+bool CodeSignCodeResources::add_file2(const String &p_root, const String &p_path) {
+ CRMatch found = match_rules2(p_path);
+ if (found == CRMatch::CR_MATCH_NESTED) {
+ return add_nested_file(p_root, p_path, p_root.plus_file(p_path));
+ }
+ if (found != CRMatch::CR_MATCH_YES && found != CRMatch::CR_MATCH_OPTIONAL) {
+ return true; // No match.
+ }
+
+ CRFile f;
+ f.name = p_path;
+ f.optional = (found == CRMatch::CR_MATCH_OPTIONAL);
+ f.nested = false;
+ f.hash = hash_sha1_base64(p_root.plus_file(p_path));
+ f.hash2 = hash_sha256_base64(p_root.plus_file(p_path));
+
+ print_verbose(vformat("CodeSign/CodeResources: File(V2) %s hash1:%s hash2:%s", f.name, f.hash, f.hash2));
+
+ files2.push_back(f);
+ return true;
+}
+
+bool CodeSignCodeResources::add_nested_file(const String &p_root, const String &p_path, const String &p_exepath) {
+#define CLEANUP() \
+ if (files_to_add.size() > 1) { \
+ for (int j = 0; j < files_to_add.size(); j++) { \
+ da->remove(files_to_add[j]); \
+ } \
+ }
+
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V(!da, false);
+
+ Vector<String> files_to_add;
+ if (LipO::is_lipo(p_exepath)) {
+ String tmp_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file("_lipo");
+ Error err = da->make_dir_recursive(tmp_path_name);
+ if (err != OK) {
+ ERR_FAIL_V_MSG(false, vformat("CodeSign/CodeResources: Failed to create \"%s\" subfolder.", tmp_path_name));
+ }
+ LipO lip;
+ if (lip.open_file(p_exepath)) {
+ for (int i = 0; i < lip.get_arch_count(); i++) {
+ if (!lip.extract_arch(i, tmp_path_name.plus_file("_rqexe_" + itos(i)))) {
+ CLEANUP();
+ ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Failed to extract thin binary.");
+ }
+ files_to_add.push_back(tmp_path_name.plus_file("_rqexe_" + itos(i)));
+ }
+ }
+ } else if (MachO::is_macho(p_exepath)) {
+ files_to_add.push_back(p_exepath);
+ }
+
+ CRFile f;
+ f.name = p_path;
+ f.optional = false;
+ f.nested = true;
+ for (int i = 0; i < files_to_add.size(); i++) {
+ MachO mh;
+ if (!mh.open_file(files_to_add[i])) {
+ CLEANUP();
+ ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid executable file.");
+ }
+ PackedByteArray hash = mh.get_cdhash_sha256(); // Use SHA-256 variant, if available.
+ if (hash.size() != 0x20) {
+ hash = mh.get_cdhash_sha1(); // Use SHA-1 instead.
+ if (hash.size() != 0x14) {
+ CLEANUP();
+ ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Unsigned nested executable file.");
+ }
+ }
+ hash.resize(0x14); // Always clamp to 0x14 size.
+ f.hash = CryptoCore::b64_encode_str(hash.ptr(), hash.size());
+
+ PackedByteArray rq_blob = mh.get_requirements();
+ String req_string;
+ if (rq_blob.size() > 8) {
+ CodeSignRequirements rq = CodeSignRequirements(rq_blob);
+ Vector<String> rqs = rq.parse_requirements();
+ for (int j = 0; j < rqs.size(); j++) {
+ if (rqs[j].begins_with("designated => ")) {
+ req_string = rqs[j].replace("designated => ", "");
+ }
+ }
+ }
+ if (req_string.is_empty()) {
+ req_string = "cdhash H\"" + String::hex_encode_buffer(hash.ptr(), hash.size()) + "\"";
+ }
+ print_verbose(vformat("CodeSign/CodeResources: Nested object %s (cputype: %d) cdhash:%s designated rq:%s", f.name, mh.get_cputype(), f.hash, req_string));
+ if (f.requirements != req_string) {
+ if (i != 0) {
+ f.requirements += " or ";
+ }
+ f.requirements += req_string;
+ }
+ }
+ files2.push_back(f);
+
+ CLEANUP();
+ return true;
+
+#undef CLEANUP
+}
+
+bool CodeSignCodeResources::add_folder_recursive(const String &p_root, const String &p_path, const String &p_main_exe_path) {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V(!da, false);
+ Error err = da->change_dir(p_root.plus_file(p_path));
+ ERR_FAIL_COND_V(err != OK, false);
+
+ bool ret = true;
+ da->list_dir_begin();
+ String n = da->get_next();
+ while (n != String()) {
+ if (n != "." && n != "..") {
+ String path = p_root.plus_file(p_path).plus_file(n);
+ if (path == p_main_exe_path) {
+ n = da->get_next();
+ continue; // Skip main executable.
+ }
+ if (da->current_is_dir()) {
+ CRMatch found = match_rules2(p_path.plus_file(n));
+ String fmw_ver = "Current"; // Framework version (default).
+ String info_path;
+ String main_exe;
+ bool bundle = false;
+ if (da->file_exists(path.plus_file("Contents/Info.plist"))) {
+ info_path = path.plus_file("Contents/Info.plist");
+ main_exe = path.plus_file("Contents/MacOS");
+ bundle = true;
+ } else if (da->file_exists(path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver)))) {
+ info_path = path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver));
+ main_exe = path.plus_file(vformat("Versions/%s", fmw_ver));
+ bundle = true;
+ } else if (da->file_exists(path.plus_file("Info.plist"))) {
+ info_path = path.plus_file("Info.plist");
+ main_exe = path;
+ bundle = true;
+ }
+ if (bundle && found == CRMatch::CR_MATCH_NESTED && !info_path.is_empty()) {
+ // Read Info.plist.
+ PList info_plist;
+ if (info_plist.load_file(info_path)) {
+ if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleExecutable")) {
+ main_exe = main_exe.plus_file(String::utf8(info_plist.get_root()->data_dict["CFBundleExecutable"]->data_string.get_data()));
+ } else {
+ ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid Info.plist, no exe name.");
+ }
+ } else {
+ ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid Info.plist, can't load.");
+ }
+ ret = ret && add_nested_file(p_root, p_path.plus_file(n), main_exe);
+ } else {
+ ret = ret && add_folder_recursive(p_root, p_path.plus_file(n), p_main_exe_path);
+ }
+ } else {
+ ret = ret && add_file1(p_root, p_path.plus_file(n));
+ ret = ret && add_file2(p_root, p_path.plus_file(n));
+ }
+ }
+
+ n = da->get_next();
+ }
+
+ da->list_dir_end();
+ return ret;
+}
+
+bool CodeSignCodeResources::save_to_file(const String &p_path) {
+ PList pl;
+
+ print_verbose(vformat("CodeSign/CodeResources: Writing to file: %s", p_path));
+
+ // Write version 1 hashes.
+ Ref<PListNode> files1_dict = PListNode::new_dict();
+ pl.get_root()->push_subnode(files1_dict, "files");
+ for (int i = 0; i < files1.size(); i++) {
+ if (files1[i].optional) {
+ Ref<PListNode> file_dict = PListNode::new_dict();
+ files1_dict->push_subnode(file_dict, files1[i].name);
+
+ file_dict->push_subnode(PListNode::new_data(files1[i].hash), "hash");
+ file_dict->push_subnode(PListNode::new_bool(true), "optional");
+ } else {
+ files1_dict->push_subnode(PListNode::new_data(files1[i].hash), files1[i].name);
+ }
+ }
+
+ // Write version 2 hashes.
+ Ref<PListNode> files2_dict = PListNode::new_dict();
+ pl.get_root()->push_subnode(files2_dict, "files2");
+ for (int i = 0; i < files2.size(); i++) {
+ Ref<PListNode> file_dict = PListNode::new_dict();
+ files2_dict->push_subnode(file_dict, files2[i].name);
+
+ if (files2[i].nested) {
+ file_dict->push_subnode(PListNode::new_data(files2[i].hash), "cdhash");
+ file_dict->push_subnode(PListNode::new_string(files2[i].requirements), "requirement");
+ } else {
+ file_dict->push_subnode(PListNode::new_data(files2[i].hash), "hash");
+ file_dict->push_subnode(PListNode::new_data(files2[i].hash2), "hash2");
+ if (files2[i].optional) {
+ file_dict->push_subnode(PListNode::new_bool(true), "optional");
+ }
+ }
+ }
+
+ // Write version 1 rules.
+ Ref<PListNode> rules1_dict = PListNode::new_dict();
+ pl.get_root()->push_subnode(rules1_dict, "rules");
+ for (int i = 0; i < rules1.size(); i++) {
+ if (rules1[i].store) {
+ if (rules1[i].key.is_empty() && rules1[i].weight <= 0) {
+ rules1_dict->push_subnode(PListNode::new_bool(true), rules1[i].file_pattern);
+ } else {
+ Ref<PListNode> rule_dict = PListNode::new_dict();
+ rules1_dict->push_subnode(rule_dict, rules1[i].file_pattern);
+ if (!rules1[i].key.is_empty()) {
+ rule_dict->push_subnode(PListNode::new_bool(true), rules1[i].key);
+ }
+ if (rules1[i].weight != 1) {
+ rule_dict->push_subnode(PListNode::new_real(rules1[i].weight), "weight");
+ }
+ }
+ }
+ }
+
+ // Write version 2 rules.
+ Ref<PListNode> rules2_dict = PListNode::new_dict();
+ pl.get_root()->push_subnode(rules2_dict, "rules2");
+ for (int i = 0; i < rules2.size(); i++) {
+ if (rules2[i].store) {
+ if (rules2[i].key.is_empty() && rules2[i].weight <= 0) {
+ rules2_dict->push_subnode(PListNode::new_bool(true), rules2[i].file_pattern);
+ } else {
+ Ref<PListNode> rule_dict = PListNode::new_dict();
+ rules2_dict->push_subnode(rule_dict, rules2[i].file_pattern);
+ if (!rules2[i].key.is_empty()) {
+ rule_dict->push_subnode(PListNode::new_bool(true), rules2[i].key);
+ }
+ if (rules2[i].weight != 1) {
+ rule_dict->push_subnode(PListNode::new_real(rules2[i].weight), "weight");
+ }
+ }
+ }
+ }
+ String text = pl.save_text();
+ ERR_FAIL_COND_V_MSG(text.is_empty(), false, "CodeSign/CodeResources: Generating resources PList failed.");
+
+ FileAccessRef fa = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(!fa, false, vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path));
+
+ CharString cs = text.utf8();
+ fa->store_buffer((const uint8_t *)cs.ptr(), cs.length());
+ fa->close();
+ return true;
+}
+
+/*************************************************************************/
+/* CodeSignRequirements */
+/*************************************************************************/
+
+CodeSignRequirements::CodeSignRequirements() {
+ blob.append_array({ 0xFA, 0xDE, 0x0C, 0x01 }); // Requirement set magic.
+ blob.append_array({ 0x00, 0x00, 0x00, 0x0C }); // Length of requirements set (12 bytes).
+ blob.append_array({ 0x00, 0x00, 0x00, 0x00 }); // Empty.
+}
+
+CodeSignRequirements::CodeSignRequirements(const PackedByteArray &p_data) {
+ blob = p_data;
+}
+
+_FORCE_INLINE_ void CodeSignRequirements::_parse_certificate_slot(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
+#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
+ ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
+ r_out += "certificate ";
+ uint32_t tag_slot = _R(r_pos);
+ if (tag_slot == 0x00000000) {
+ r_out += "leaf";
+ } else if (tag_slot == 0xffffffff) {
+ r_out += "root";
+ } else {
+ r_out += itos((int32_t)tag_slot);
+ }
+ r_pos += 4;
+#undef _R
+}
+
+_FORCE_INLINE_ void CodeSignRequirements::_parse_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
+#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
+ ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
+ uint32_t key_size = _R(r_pos);
+ ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
+ CharString key;
+ key.resize(key_size);
+ memcpy(key.ptrw(), blob.ptr() + r_pos + 4, key_size);
+ r_pos += 4 + key_size + PAD(key_size, 4);
+ r_out += "[" + String::utf8(key, key_size) + "]";
+#undef _R
+}
+
+_FORCE_INLINE_ void CodeSignRequirements::_parse_oid_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
+#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
+ ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
+ uint32_t key_size = _R(r_pos);
+ ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
+ r_out += "[field.";
+ r_out += itos(blob[r_pos + 4] / 40) + ".";
+ r_out += itos(blob[r_pos + 4] % 40);
+ uint32_t spos = r_pos + 5;
+ while (spos < r_pos + 4 + key_size) {
+ r_out += ".";
+ if (blob[spos] <= 127) {
+ r_out += itos(blob[spos]);
+ spos += 1;
+ } else {
+ uint32_t x = (0x7F & blob[spos]) << 7;
+ spos += 1;
+ while (blob[spos] > 127) {
+ x = (x + (0x7F & blob[spos])) << 7;
+ spos += 1;
+ }
+ x = (x + (0x7F & blob[spos]));
+ r_out += itos(x);
+ spos += 1;
+ }
+ }
+ r_out += "]";
+ r_pos += 4 + key_size + PAD(key_size, 4);
+#undef _R
+}
+
+_FORCE_INLINE_ void CodeSignRequirements::_parse_hash_string(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
+#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
+ ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
+ uint32_t tag_size = _R(r_pos);
+ ERR_FAIL_COND_MSG(r_pos + tag_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
+ PackedByteArray data;
+ data.resize(tag_size);
+ memcpy(data.ptrw(), blob.ptr() + r_pos + 4, tag_size);
+ r_out += "H\"" + String::hex_encode_buffer(data.ptr(), data.size()) + "\"";
+ r_pos += 4 + tag_size + PAD(tag_size, 4);
+#undef _R
+}
+
+_FORCE_INLINE_ void CodeSignRequirements::_parse_value(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
+#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
+ ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
+ uint32_t key_size = _R(r_pos);
+ ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds.");
+ CharString key;
+ key.resize(key_size);
+ memcpy(key.ptrw(), blob.ptr() + r_pos + 4, key_size);
+ r_pos += 4 + key_size + PAD(key_size, 4);
+ r_out += "\"" + String::utf8(key, key_size) + "\"";
+#undef _R
+}
+
+_FORCE_INLINE_ void CodeSignRequirements::_parse_date(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
+#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
+ ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds.");
+ uint32_t date = _R(r_pos);
+ time_t t = 978307200 + date;
+ struct tm lt;
+#ifdef WINDOWS_ENABLED
+ gmtime_s(&lt, &t);
+#else
+ gmtime_r(&t, &lt);
+#endif
+ r_out += vformat("<%04d-%02d-%02d ", (int)(1900 + lt.tm_year), (int)(lt.tm_mon + 1), (int)(lt.tm_mday)) + vformat("%02d:%02d:%02d +0000>", (int)(lt.tm_hour), (int)(lt.tm_min), (int)(lt.tm_sec));
+#undef _R
+}
+
+_FORCE_INLINE_ bool CodeSignRequirements::_parse_match(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const {
+#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
+ ERR_FAIL_COND_V_MSG(r_pos >= p_rq_size, false, "CodeSign/Requirements: Out of bounds.");
+ uint32_t match = _R(r_pos);
+ r_pos += 4;
+ switch (match) {
+ case 0x00000000: {
+ r_out += "exists";
+ } break;
+ case 0x00000001: {
+ r_out += "= ";
+ _parse_value(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x00000002: {
+ r_out += "~ ";
+ _parse_value(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x00000003: {
+ r_out += "= *";
+ _parse_value(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x00000004: {
+ r_out += "= ";
+ _parse_value(r_pos, r_out, p_rq_size);
+ r_out += "*";
+ } break;
+ case 0x00000005: {
+ r_out += "< ";
+ _parse_value(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x00000006: {
+ r_out += "> ";
+ _parse_value(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x00000007: {
+ r_out += "<= ";
+ _parse_value(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x00000008: {
+ r_out += ">= ";
+ _parse_value(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x00000009: {
+ r_out += "= ";
+ _parse_date(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x0000000A: {
+ r_out += "< ";
+ _parse_date(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x0000000B: {
+ r_out += "> ";
+ _parse_date(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x0000000C: {
+ r_out += "<= ";
+ _parse_date(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x0000000D: {
+ r_out += ">= ";
+ _parse_date(r_pos, r_out, p_rq_size);
+ } break;
+ case 0x0000000E: {
+ r_out += "absent";
+ } break;
+ default: {
+ return false;
+ }
+ }
+ return true;
+#undef _R
+}
+
+Vector<String> CodeSignRequirements::parse_requirements() const {
+#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x))
+ Vector<String> list;
+
+ // Read requirements set header.
+ ERR_FAIL_COND_V_MSG(blob.size() < 12, list, "CodeSign/Requirements: Blob is too small.");
+ uint32_t magic = _R(0);
+ ERR_FAIL_COND_V_MSG(magic != 0xfade0c01, list, "CodeSign/Requirements: Invalid set magic.");
+ uint32_t size = _R(4);
+ ERR_FAIL_COND_V_MSG(size != (uint32_t)blob.size(), list, "CodeSign/Requirements: Invalid set size.");
+ uint32_t count = _R(8);
+
+ for (uint32_t i = 0; i < count; i++) {
+ String out;
+
+ // Read requirement header.
+ uint32_t rq_type = _R(12 + i * 8);
+ uint32_t rq_offset = _R(12 + i * 8 + 4);
+ ERR_FAIL_COND_V_MSG(rq_offset + 12 >= (uint32_t)blob.size(), list, "CodeSign/Requirements: Invalid requirement offset.");
+ switch (rq_type) {
+ case 0x00000001: {
+ out += "host => ";
+ } break;
+ case 0x00000002: {
+ out += "guest => ";
+ } break;
+ case 0x00000003: {
+ out += "designated => ";
+ } break;
+ case 0x00000004: {
+ out += "library => ";
+ } break;
+ case 0x00000005: {
+ out += "plugin => ";
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid requirement type.");
+ }
+ }
+ uint32_t rq_magic = _R(rq_offset);
+ uint32_t rq_size = _R(rq_offset + 4);
+ uint32_t rq_ver = _R(rq_offset + 8);
+ uint32_t pos = rq_offset + 12;
+ ERR_FAIL_COND_V_MSG(rq_magic != 0xfade0c00, list, "CodeSign/Requirements: Invalid requirement magic.");
+ ERR_FAIL_COND_V_MSG(rq_ver != 0x00000001, list, "CodeSign/Requirements: Invalid requirement version.");
+
+ // Read requirement tokens.
+ List<String> tokens;
+ while (pos < rq_offset + rq_size) {
+ uint32_t rq_tag = _R(pos);
+ pos += 4;
+ String token;
+ switch (rq_tag) {
+ case 0x00000000: {
+ token = "false";
+ } break;
+ case 0x00000001: {
+ token = "true";
+ } break;
+ case 0x00000002: {
+ token = "identifier ";
+ _parse_value(pos, token, rq_offset + rq_size);
+ } break;
+ case 0x00000003: {
+ token = "anchor apple";
+ } break;
+ case 0x00000004: {
+ _parse_certificate_slot(pos, token, rq_offset + rq_size);
+ token += " ";
+ _parse_hash_string(pos, token, rq_offset + rq_size);
+ } break;
+ case 0x00000005: {
+ token = "info";
+ _parse_key(pos, token, rq_offset + rq_size);
+ token += " = ";
+ _parse_value(pos, token, rq_offset + rq_size);
+ } break;
+ case 0x00000006: {
+ token = "and";
+ } break;
+ case 0x00000007: {
+ token = "or";
+ } break;
+ case 0x00000008: {
+ token = "cdhash ";
+ _parse_hash_string(pos, token, rq_offset + rq_size);
+ } break;
+ case 0x00000009: {
+ token = "!";
+ } break;
+ case 0x0000000A: {
+ token = "info";
+ _parse_key(pos, token, rq_offset + rq_size);
+ token += " ";
+ ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix.");
+ } break;
+ case 0x0000000B: {
+ _parse_certificate_slot(pos, token, rq_offset + rq_size);
+ _parse_key(pos, token, rq_offset + rq_size);
+ token += " ";
+ ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix.");
+ } break;
+ case 0x0000000C: {
+ _parse_certificate_slot(pos, token, rq_offset + rq_size);
+ token += " trusted";
+ } break;
+ case 0x0000000D: {
+ token = "anchor trusted";
+ } break;
+ case 0x0000000E: {
+ _parse_certificate_slot(pos, token, rq_offset + rq_size);
+ _parse_oid_key(pos, token, rq_offset + rq_size);
+ token += " ";
+ ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix.");
+ } break;
+ case 0x0000000F: {
+ token = "anchor apple generic";
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid requirement token.");
+ } break;
+ }
+ tokens.push_back(token);
+ }
+
+ // Polish to infix notation (w/o bracket optimization).
+ for (List<String>::Element *E = tokens.back(); E; E = E->prev()) {
+ if (E->get() == "and") {
+ ERR_FAIL_COND_V_MSG(!E->next() || !E->next()->next(), list, "CodeSign/Requirements: Invalid token sequence.");
+ String token = "(" + E->next()->get() + " and " + E->next()->next()->get() + ")";
+ tokens.erase(E->next()->next());
+ tokens.erase(E->next());
+ E->get() = token;
+ } else if (E->get() == "or") {
+ ERR_FAIL_COND_V_MSG(!E->next() || !E->next()->next(), list, "CodeSign/Requirements: Invalid token sequence.");
+ String token = "(" + E->next()->get() + " or " + E->next()->next()->get() + ")";
+ tokens.erase(E->next()->next());
+ tokens.erase(E->next());
+ E->get() = token;
+ }
+ }
+
+ if (tokens.size() == 1) {
+ list.push_back(out + tokens.front()->get());
+ } else {
+ ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid token sequence.");
+ }
+ }
+
+ return list;
+#undef _R
+}
+
+PackedByteArray CodeSignRequirements::get_hash_sha1() const {
+ PackedByteArray hash;
+ hash.resize(0x14);
+
+ CryptoCore::SHA1Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+}
+
+PackedByteArray CodeSignRequirements::get_hash_sha256() const {
+ PackedByteArray hash;
+ hash.resize(0x20);
+
+ CryptoCore::SHA256Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+}
+
+int CodeSignRequirements::get_size() const {
+ return blob.size();
+}
+
+void CodeSignRequirements::write_to_file(FileAccess *p_file) const {
+ ERR_FAIL_COND_MSG(!p_file, "CodeSign/Requirements: Invalid file handle.");
+ p_file->store_buffer(blob.ptr(), blob.size());
+}
+
+/*************************************************************************/
+/* CodeSignEntitlementsText */
+/*************************************************************************/
+
+CodeSignEntitlementsText::CodeSignEntitlementsText() {
+ blob.append_array({ 0xFA, 0xDE, 0x71, 0x71 }); // Text Entitlements set magic.
+ blob.append_array({ 0x00, 0x00, 0x00, 0x08 }); // Length (8 bytes).
+}
+
+CodeSignEntitlementsText::CodeSignEntitlementsText(const String &p_string) {
+ CharString utf8 = p_string.utf8();
+ blob.append_array({ 0xFA, 0xDE, 0x71, 0x71 }); // Text Entitlements set magic.
+ for (int i = 3; i >= 0; i--) {
+ uint8_t x = ((utf8.length() + 8) >> i * 8) & 0xFF; // Size.
+ blob.push_back(x);
+ }
+ for (int i = 0; i < utf8.length(); i++) { // Write data.
+ blob.push_back(utf8[i]);
+ }
+}
+
+PackedByteArray CodeSignEntitlementsText::get_hash_sha1() const {
+ PackedByteArray hash;
+ hash.resize(0x14);
+
+ CryptoCore::SHA1Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+}
+
+PackedByteArray CodeSignEntitlementsText::get_hash_sha256() const {
+ PackedByteArray hash;
+ hash.resize(0x20);
+
+ CryptoCore::SHA256Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+}
+
+int CodeSignEntitlementsText::get_size() const {
+ return blob.size();
+}
+
+void CodeSignEntitlementsText::write_to_file(FileAccess *p_file) const {
+ ERR_FAIL_COND_MSG(!p_file, "CodeSign/EntitlementsText: Invalid file handle.");
+ p_file->store_buffer(blob.ptr(), blob.size());
+}
+
+/*************************************************************************/
+/* CodeSignEntitlementsBinary */
+/*************************************************************************/
+
+CodeSignEntitlementsBinary::CodeSignEntitlementsBinary() {
+ blob.append_array({ 0xFA, 0xDE, 0x71, 0x72 }); // Binary Entitlements magic.
+ blob.append_array({ 0x00, 0x00, 0x00, 0x08 }); // Length (8 bytes).
+}
+
+CodeSignEntitlementsBinary::CodeSignEntitlementsBinary(const String &p_string) {
+ PList pl = PList(p_string);
+
+ PackedByteArray asn1 = pl.save_asn1();
+ blob.append_array({ 0xFA, 0xDE, 0x71, 0x72 }); // Binary Entitlements magic.
+ uint32_t size = asn1.size() + 8;
+ for (int i = 3; i >= 0; i--) {
+ uint8_t x = (size >> i * 8) & 0xFF; // Size.
+ blob.push_back(x);
+ }
+ blob.append_array(asn1); // Write data.
+}
+
+PackedByteArray CodeSignEntitlementsBinary::get_hash_sha1() const {
+ PackedByteArray hash;
+ hash.resize(0x14);
+
+ CryptoCore::SHA1Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+}
+
+PackedByteArray CodeSignEntitlementsBinary::get_hash_sha256() const {
+ PackedByteArray hash;
+ hash.resize(0x20);
+
+ CryptoCore::SHA256Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+}
+
+int CodeSignEntitlementsBinary::get_size() const {
+ return blob.size();
+}
+
+void CodeSignEntitlementsBinary::write_to_file(FileAccess *p_file) const {
+ ERR_FAIL_COND_MSG(!p_file, "CodeSign/EntitlementsBinary: Invalid file handle.");
+ p_file->store_buffer(blob.ptr(), blob.size());
+}
+
+/*************************************************************************/
+/* CodeSignCodeDirectory */
+/*************************************************************************/
+
+CodeSignCodeDirectory::CodeSignCodeDirectory() {
+ blob.append_array({ 0xFA, 0xDE, 0x0C, 0x02 }); // Code Directory magic.
+ blob.append_array({ 0x00, 0x00, 0x00, 0x00 }); // Size (8 bytes).
+}
+
+CodeSignCodeDirectory::CodeSignCodeDirectory(uint8_t p_hash_size, uint8_t p_hash_type, bool p_main, const CharString &p_id, const CharString &p_team_id, uint32_t p_page_size, uint64_t p_exe_limit, uint64_t p_code_limit) {
+ pages = p_code_limit / (uint64_t(1) << p_page_size);
+ remain = p_code_limit % (uint64_t(1) << p_page_size);
+ code_slots = pages + (remain > 0 ? 1 : 0);
+ special_slots = 7;
+
+ int cd_size = 8 + sizeof(CodeDirectoryHeader) + (code_slots + special_slots) * p_hash_size + p_id.size() + p_team_id.size();
+ int cd_off = 8 + sizeof(CodeDirectoryHeader);
+ blob.append_array({ 0xFA, 0xDE, 0x0C, 0x02 }); // Code Directory magic.
+ for (int i = 3; i >= 0; i--) {
+ uint8_t x = (cd_size >> i * 8) & 0xFF; // Size.
+ blob.push_back(x);
+ }
+ blob.resize(cd_size);
+ memset(blob.ptrw() + 8, 0x00, cd_size - 8);
+ CodeDirectoryHeader *cd = (CodeDirectoryHeader *)(blob.ptrw() + 8);
+
+ bool is_64_cl = (p_code_limit >= std::numeric_limits<uint32_t>::max());
+
+ // Version and options.
+ cd->version = BSWAP32(0x20500);
+ cd->flags = BSWAP32(SIGNATURE_ADHOC | SIGNATURE_RUNTIME);
+ cd->special_slots = BSWAP32(special_slots);
+ cd->code_slots = BSWAP32(code_slots);
+ if (is_64_cl) {
+ cd->code_limit_64 = BSWAP64(p_code_limit);
+ } else {
+ cd->code_limit = BSWAP32(p_code_limit);
+ }
+ cd->hash_size = p_hash_size;
+ cd->hash_type = p_hash_type;
+ cd->page_size = p_page_size;
+ cd->exec_seg_base = 0x00;
+ cd->exec_seg_limit = BSWAP64(p_exe_limit);
+ cd->exec_seg_flags = 0;
+ if (p_main) {
+ cd->exec_seg_flags |= EXECSEG_MAIN_BINARY;
+ }
+ cd->exec_seg_flags = BSWAP64(cd->exec_seg_flags);
+ uint32_t version = (11 << 16) + (3 << 8) + 0; // Version 11.3.0
+ cd->runtime = BSWAP32(version);
+
+ // Copy ID.
+ cd->ident_offset = BSWAP32(cd_off);
+ memcpy(blob.ptrw() + cd_off, p_id.get_data(), p_id.size());
+ cd_off += p_id.size();
+
+ // Copy Team ID.
+ if (p_team_id.length() > 0) {
+ cd->team_offset = BSWAP32(cd_off);
+ memcpy(blob.ptrw() + cd_off, p_team_id.get_data(), p_team_id.size());
+ cd_off += p_team_id.size();
+ } else {
+ cd->team_offset = 0;
+ }
+
+ // Scatter vector.
+ cd->scatter_vector_offset = 0; // Not used.
+
+ // Executable hashes offset.
+ cd->hash_offset = BSWAP32(cd_off + special_slots * cd->hash_size);
+}
+
+bool CodeSignCodeDirectory::set_hash_in_slot(const PackedByteArray &p_hash, int p_slot) {
+ ERR_FAIL_COND_V_MSG((p_slot < -special_slots) || (p_slot >= code_slots), false, vformat("CodeSign/CodeDirectory: Invalid hash slot index: %d.", p_slot));
+ CodeDirectoryHeader *cd = reinterpret_cast<CodeDirectoryHeader *>(blob.ptrw() + 8);
+ for (int i = 0; i < cd->hash_size; i++) {
+ blob.write[BSWAP32(cd->hash_offset) + p_slot * cd->hash_size + i] = p_hash[i];
+ }
+ return true;
+}
+
+int32_t CodeSignCodeDirectory::get_page_count() {
+ return pages;
+}
+
+int32_t CodeSignCodeDirectory::get_page_remainder() {
+ return remain;
+}
+
+PackedByteArray CodeSignCodeDirectory::get_hash_sha1() const {
+ PackedByteArray hash;
+ hash.resize(0x14);
+
+ CryptoCore::SHA1Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+}
+
+PackedByteArray CodeSignCodeDirectory::get_hash_sha256() const {
+ PackedByteArray hash;
+ hash.resize(0x20);
+
+ CryptoCore::SHA256Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+}
+
+int CodeSignCodeDirectory::get_size() const {
+ return blob.size();
+}
+
+void CodeSignCodeDirectory::write_to_file(FileAccess *p_file) const {
+ ERR_FAIL_COND_MSG(!p_file, "CodeSign/CodeDirectory: Invalid file handle.");
+ p_file->store_buffer(blob.ptr(), blob.size());
+}
+
+/*************************************************************************/
+/* CodeSignSignature */
+/*************************************************************************/
+
+CodeSignSignature::CodeSignSignature() {
+ blob.append_array({ 0xFA, 0xDE, 0x0B, 0x01 }); // Signature magic.
+ uint32_t sign_size = 8; // Ad-hoc signature is empty.
+ for (int i = 3; i >= 0; i--) {
+ uint8_t x = (sign_size >> i * 8) & 0xFF; // Size.
+ blob.push_back(x);
+ }
+}
+
+PackedByteArray CodeSignSignature::get_hash_sha1() const {
+ PackedByteArray hash;
+ hash.resize(0x14);
+
+ CryptoCore::SHA1Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+}
+
+PackedByteArray CodeSignSignature::get_hash_sha256() const {
+ PackedByteArray hash;
+ hash.resize(0x20);
+
+ CryptoCore::SHA256Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+}
+
+int CodeSignSignature::get_size() const {
+ return blob.size();
+}
+
+void CodeSignSignature::write_to_file(FileAccess *p_file) const {
+ ERR_FAIL_COND_MSG(!p_file, "CodeSign/Signature: Invalid file handle.");
+ p_file->store_buffer(blob.ptr(), blob.size());
+}
+
+/*************************************************************************/
+/* CodeSignSuperBlob */
+/*************************************************************************/
+
+bool CodeSignSuperBlob::add_blob(const Ref<CodeSignBlob> &p_blob) {
+ if (p_blob.is_valid()) {
+ blobs.push_back(p_blob);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int CodeSignSuperBlob::get_size() const {
+ int size = 12 + blobs.size() * 8;
+ for (int i = 0; i < blobs.size(); i++) {
+ if (blobs[i].is_null()) {
+ return 0;
+ }
+ size += blobs[i]->get_size();
+ }
+ return size;
+}
+
+void CodeSignSuperBlob::write_to_file(FileAccess *p_file) const {
+ ERR_FAIL_COND_MSG(!p_file, "CodeSign/SuperBlob: Invalid file handle.");
+ uint32_t size = get_size();
+ uint32_t data_offset = 12 + blobs.size() * 8;
+
+ // Write header.
+ p_file->store_32(BSWAP32(0xfade0cc0));
+ p_file->store_32(BSWAP32(size));
+ p_file->store_32(BSWAP32(blobs.size()));
+
+ // Write index.
+ for (int i = 0; i < blobs.size(); i++) {
+ if (blobs[i].is_null()) {
+ return;
+ }
+ p_file->store_32(BSWAP32(blobs[i]->get_index_type()));
+ p_file->store_32(BSWAP32(data_offset));
+ data_offset += blobs[i]->get_size();
+ }
+
+ // Write blobs.
+ for (int i = 0; i < blobs.size(); i++) {
+ blobs[i]->write_to_file(p_file);
+ }
+}
+
+/*************************************************************************/
+/* CodeSign */
+/*************************************************************************/
+
+PackedByteArray CodeSign::file_hash_sha1(const String &p_path) {
+ PackedByteArray file_hash;
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(!f, PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path));
+
+ CryptoCore::SHA1Context ctx;
+ ctx.start();
+
+ unsigned char step[4096];
+ while (true) {
+ uint64_t br = f->get_buffer(step, 4096);
+ if (br > 0) {
+ ctx.update(step, br);
+ }
+ if (br < 4096) {
+ break;
+ }
+ }
+
+ file_hash.resize(0x14);
+ ctx.finish(file_hash.ptrw());
+ return file_hash;
+}
+
+PackedByteArray CodeSign::file_hash_sha256(const String &p_path) {
+ PackedByteArray file_hash;
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(!f, PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path));
+
+ CryptoCore::SHA256Context ctx;
+ ctx.start();
+
+ unsigned char step[4096];
+ while (true) {
+ uint64_t br = f->get_buffer(step, 4096);
+ if (br > 0) {
+ ctx.update(step, br);
+ }
+ if (br < 4096) {
+ break;
+ }
+ }
+
+ file_hash.resize(0x20);
+ ctx.finish(file_hash.ptrw());
+ return file_hash;
+}
+
+Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const String &p_info, const String &p_exe_path, const String &p_bundle_path, const String &p_ent_path, bool p_ios_bundle, String &r_error_msg) {
+#define CLEANUP() \
+ if (files_to_sign.size() > 1) { \
+ for (int j = 0; j < files_to_sign.size(); j++) { \
+ da->remove(files_to_sign[j]); \
+ } \
+ }
+
+ print_verbose(vformat("CodeSign: Signing executable: %s, bundle: %s with entitlements %s", p_exe_path, p_bundle_path, p_ent_path));
+
+ PackedByteArray info_hash1, info_hash2;
+ PackedByteArray res_hash1, res_hash2;
+ String id;
+ String main_exe = p_exe_path;
+
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (!da) {
+ r_error_msg = TTR("Can't get filesystem access.");
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access.");
+ }
+
+ // Read Info.plist.
+ if (!p_info.is_empty()) {
+ print_verbose(vformat("CodeSign: Reading bundle info..."));
+ PList info_plist;
+ if (info_plist.load_file(p_info)) {
+ info_hash1 = file_hash_sha1(p_info);
+ info_hash2 = file_hash_sha256(p_info);
+ if (info_hash1.is_empty() || info_hash2.is_empty()) {
+ r_error_msg = TTR("Failed to get Info.plist hash.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to get Info.plist hash.");
+ }
+
+ if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleExecutable")) {
+ main_exe = p_exe_path.plus_file(String::utf8(info_plist.get_root()->data_dict["CFBundleExecutable"]->data_string.get_data()));
+ } else {
+ r_error_msg = TTR("Invalid Info.plist, no exe name.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, no exe name.");
+ }
+
+ if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleIdentifier")) {
+ id = info_plist.get_root()->data_dict["CFBundleIdentifier"]->data_string.get_data();
+ } else {
+ r_error_msg = TTR("Invalid Info.plist, no bundle id.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, no bundle id.");
+ }
+ } else {
+ r_error_msg = TTR("Invalid Info.plist, can't load.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, can't load.");
+ }
+ }
+
+ // Extract fat binary.
+ Vector<String> files_to_sign;
+ if (LipO::is_lipo(main_exe)) {
+ print_verbose(vformat("CodeSign: Executable is fat, extracting..."));
+ String tmp_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file("_lipo");
+ Error err = da->make_dir_recursive(tmp_path_name);
+ if (err != OK) {
+ r_error_msg = vformat(TTR("Failed to create \"%s\" subfolder."), tmp_path_name);
+ ERR_FAIL_V_MSG(FAILED, vformat("CodeSign: Failed to create \"%s\" subfolder.", tmp_path_name));
+ }
+ LipO lip;
+ if (lip.open_file(main_exe)) {
+ for (int i = 0; i < lip.get_arch_count(); i++) {
+ if (!lip.extract_arch(i, tmp_path_name.plus_file("_exe_" + itos(i)))) {
+ CLEANUP();
+ r_error_msg = TTR("Failed to extract thin binary.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to extract thin binary.");
+ }
+ files_to_sign.push_back(tmp_path_name.plus_file("_exe_" + itos(i)));
+ }
+ }
+ } else if (MachO::is_macho(main_exe)) {
+ print_verbose(vformat("CodeSign: Executable is thin..."));
+ files_to_sign.push_back(main_exe);
+ } else {
+ r_error_msg = TTR("Invalid binary format.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid binary format.");
+ }
+
+ // Check if it's already signed.
+ if (!p_force) {
+ for (int i = 0; i < files_to_sign.size(); i++) {
+ MachO mh;
+ mh.open_file(files_to_sign[i]);
+ if (mh.is_signed()) {
+ CLEANUP();
+ r_error_msg = TTR("Already signed!");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Already signed!");
+ }
+ }
+ }
+
+ // Generate core resources.
+ if (!p_bundle_path.is_empty()) {
+ print_verbose(vformat("CodeSign: Generating bundle CodeResources..."));
+ CodeSignCodeResources cr;
+
+ if (p_ios_bundle) {
+ cr.add_rule1("^.*");
+ cr.add_rule1("^.*\\.lproj/", "optional", 100);
+ cr.add_rule1("^.*\\.lproj/locversion.plist$", "omit", 1100);
+ cr.add_rule1("^Base\\.lproj/", "", 1010);
+ cr.add_rule1("^version.plist$");
+
+ cr.add_rule2(".*\\.dSYM($|/)", "", 11);
+ cr.add_rule2("^(.*/)?\\.DS_Store$", "omit", 2000);
+ cr.add_rule2("^.*");
+ cr.add_rule2("^.*\\.lproj/", "optional", 1000);
+ cr.add_rule2("^.*\\.lproj/locversion.plist$", "omit", 1100);
+ cr.add_rule2("^Base\\.lproj/", "", 1010);
+ cr.add_rule2("^Info\\.plist$", "omit", 20);
+ cr.add_rule2("^PkgInfo$", "omit", 20);
+ cr.add_rule2("^embedded\\.provisionprofile$", "", 10);
+ cr.add_rule2("^version\\.plist$", "", 20);
+
+ cr.add_rule2("^_MASReceipt", "omit", 2000, false);
+ cr.add_rule2("^_CodeSignature", "omit", 2000, false);
+ cr.add_rule2("^CodeResources", "omit", 2000, false);
+ } else {
+ cr.add_rule1("^Resources/");
+ cr.add_rule1("^Resources/.*\\.lproj/", "optional", 1000);
+ cr.add_rule1("^Resources/.*\\.lproj/locversion.plist$", "omit", 1100);
+ cr.add_rule1("^Resources/Base\\.lproj/", "", 1010);
+ cr.add_rule1("^version.plist$");
+
+ cr.add_rule2(".*\\.dSYM($|/)", "", 11);
+ cr.add_rule2("^(.*/)?\\.DS_Store$", "omit", 2000);
+ cr.add_rule2("^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/", "nested", 10);
+ cr.add_rule2("^.*");
+ cr.add_rule2("^Info\\.plist$", "omit", 20);
+ cr.add_rule2("^PkgInfo$", "omit", 20);
+ cr.add_rule2("^Resources/", "", 20);
+ cr.add_rule2("^Resources/.*\\.lproj/", "optional", 1000);
+ cr.add_rule2("^Resources/.*\\.lproj/locversion.plist$", "omit", 1100);
+ cr.add_rule2("^Resources/Base\\.lproj/", "", 1010);
+ cr.add_rule2("^[^/]+$", "nested", 10);
+ cr.add_rule2("^embedded\\.provisionprofile$", "", 10);
+ cr.add_rule2("^version\\.plist$", "", 20);
+ cr.add_rule2("^_MASReceipt", "omit", 2000, false);
+ cr.add_rule2("^_CodeSignature", "omit", 2000, false);
+ cr.add_rule2("^CodeResources", "omit", 2000, false);
+ }
+
+ if (!cr.add_folder_recursive(p_bundle_path, "", main_exe)) {
+ CLEANUP();
+ r_error_msg = TTR("Failed to process nested resources.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to process nested resources.");
+ }
+ Error err = da->make_dir_recursive(p_bundle_path.plus_file("_CodeSignature"));
+ if (err != OK) {
+ CLEANUP();
+ r_error_msg = TTR("Failed to create _CodeSignature subfolder.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to create _CodeSignature subfolder.");
+ }
+ cr.save_to_file(p_bundle_path.plus_file("_CodeSignature").plus_file("CodeResources"));
+ res_hash1 = file_hash_sha1(p_bundle_path.plus_file("_CodeSignature").plus_file("CodeResources"));
+ res_hash2 = file_hash_sha256(p_bundle_path.plus_file("_CodeSignature").plus_file("CodeResources"));
+ if (res_hash1.is_empty() || res_hash2.is_empty()) {
+ CLEANUP();
+ r_error_msg = TTR("Failed to get CodeResources hash.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to get CodeResources hash.");
+ }
+ }
+
+ // Generate common signature structures.
+ if (id.is_empty()) {
+ Ref<Crypto> crypto = Ref<Crypto>(Crypto::create());
+ PackedByteArray uuid = crypto->generate_random_bytes(16);
+ id = (String("a-55554944") /*a-UUID*/ + String::hex_encode_buffer(uuid.ptr(), 16));
+ }
+ CharString uuid_str = id.utf8();
+ print_verbose(vformat("CodeSign: Used bundle ID: %s", id));
+
+ print_verbose(vformat("CodeSign: Processing entitlements..."));
+
+ Ref<CodeSignEntitlementsText> cet;
+ Ref<CodeSignEntitlementsBinary> ceb;
+ if (!p_ent_path.is_empty()) {
+ String entitlements = FileAccess::get_file_as_string(p_ent_path);
+ if (entitlements.is_empty()) {
+ CLEANUP();
+ r_error_msg = TTR("Invalid entitlements file.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid entitlements file.");
+ }
+ cet = Ref<CodeSignEntitlementsText>(memnew(CodeSignEntitlementsText(entitlements)));
+ ceb = Ref<CodeSignEntitlementsBinary>(memnew(CodeSignEntitlementsBinary(entitlements)));
+ }
+
+ print_verbose(vformat("CodeSign: Generating requirements..."));
+ Ref<CodeSignRequirements> rq;
+ String team_id = "";
+ rq = Ref<CodeSignRequirements>(memnew(CodeSignRequirements()));
+
+ // Sign executables.
+ for (int i = 0; i < files_to_sign.size(); i++) {
+ MachO mh;
+ if (!mh.open_file(files_to_sign[i])) {
+ CLEANUP();
+ r_error_msg = TTR("Invalid executable file.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid executable file.");
+ }
+ print_verbose(vformat("CodeSign: Signing executable for cputype: %d ...", mh.get_cputype()));
+
+ print_verbose(vformat("CodeSign: Generating CodeDirectory..."));
+ Ref<CodeSignCodeDirectory> cd1 = memnew(CodeSignCodeDirectory(0x14, 0x01, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit()));
+ Ref<CodeSignCodeDirectory> cd2 = memnew(CodeSignCodeDirectory(0x20, 0x02, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit()));
+ print_verbose(vformat("CodeSign: Calculating special slot hashes..."));
+ if (info_hash2.size() == 0x20) {
+ cd2->set_hash_in_slot(info_hash2, CodeSignCodeDirectory::SLOT_INFO_PLIST);
+ }
+ if (info_hash1.size() == 0x14) {
+ cd1->set_hash_in_slot(info_hash1, CodeSignCodeDirectory::SLOT_INFO_PLIST);
+ }
+ cd1->set_hash_in_slot(rq->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_REQUIREMENTS);
+ cd2->set_hash_in_slot(rq->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_REQUIREMENTS);
+ if (res_hash2.size() == 0x20) {
+ cd2->set_hash_in_slot(res_hash2, CodeSignCodeDirectory::SLOT_RESOURCES);
+ }
+ if (res_hash1.size() == 0x14) {
+ cd1->set_hash_in_slot(res_hash1, CodeSignCodeDirectory::SLOT_RESOURCES);
+ }
+ if (cet.is_valid()) {
+ cd1->set_hash_in_slot(cet->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_ENTITLEMENTS); //Text variant.
+ cd2->set_hash_in_slot(cet->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_ENTITLEMENTS);
+ }
+ if (ceb.is_valid()) {
+ cd1->set_hash_in_slot(ceb->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_DER_ENTITLEMENTS); //ASN.1 variant.
+ cd2->set_hash_in_slot(ceb->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_DER_ENTITLEMENTS);
+ }
+
+ // Calculate signature size.
+ int sign_size = 12; // SuperBlob header.
+ sign_size += cd1->get_size() + 8;
+ sign_size += cd2->get_size() + 8;
+ sign_size += rq->get_size() + 8;
+ if (cet.is_valid()) {
+ sign_size += cet->get_size() + 8;
+ }
+ if (ceb.is_valid()) {
+ sign_size += ceb->get_size() + 8;
+ }
+ sign_size += 16; // Empty signature size.
+
+ // Alloc/resize signature load command.
+ print_verbose(vformat("CodeSign: Reallocating space for the signature superblob (%d)...", sign_size));
+ if (!mh.set_signature_size(sign_size)) {
+ CLEANUP();
+ r_error_msg = TTR("Can't resize signature load command.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Can't resize signature load command.");
+ }
+
+ print_verbose(vformat("CodeSign: Calculating executable code hashes..."));
+ // Calculate executable code hashes.
+ PackedByteArray buffer;
+ PackedByteArray hash1, hash2;
+ hash1.resize(0x14);
+ hash2.resize(0x20);
+ buffer.resize(1 << 12);
+ mh.get_file()->seek(0);
+ for (int32_t j = 0; j < cd2->get_page_count(); j++) {
+ mh.get_file()->get_buffer(buffer.ptrw(), (1 << 12));
+ CryptoCore::SHA256Context ctx2;
+ ctx2.start();
+ ctx2.update(buffer.ptr(), (1 << 12));
+ ctx2.finish(hash2.ptrw());
+ cd2->set_hash_in_slot(hash2, j);
+
+ CryptoCore::SHA1Context ctx1;
+ ctx1.start();
+ ctx1.update(buffer.ptr(), (1 << 12));
+ ctx1.finish(hash1.ptrw());
+ cd1->set_hash_in_slot(hash1, j);
+ }
+ if (cd2->get_page_remainder() > 0) {
+ mh.get_file()->get_buffer(buffer.ptrw(), cd2->get_page_remainder());
+ CryptoCore::SHA256Context ctx2;
+ ctx2.start();
+ ctx2.update(buffer.ptr(), cd2->get_page_remainder());
+ ctx2.finish(hash2.ptrw());
+ cd2->set_hash_in_slot(hash2, cd2->get_page_count());
+
+ CryptoCore::SHA1Context ctx1;
+ ctx1.start();
+ ctx1.update(buffer.ptr(), cd1->get_page_remainder());
+ ctx1.finish(hash1.ptrw());
+ cd1->set_hash_in_slot(hash1, cd1->get_page_count());
+ }
+
+ print_verbose(vformat("CodeSign: Generating signature..."));
+ Ref<CodeSignSignature> cs;
+ cs = Ref<CodeSignSignature>(memnew(CodeSignSignature()));
+
+ print_verbose(vformat("CodeSign: Writing signature superblob..."));
+ // Write signature data to the executable.
+ CodeSignSuperBlob sb = CodeSignSuperBlob();
+ sb.add_blob(cd2);
+ sb.add_blob(cd1);
+ sb.add_blob(rq);
+ if (cet.is_valid()) {
+ sb.add_blob(cet);
+ }
+ if (ceb.is_valid()) {
+ sb.add_blob(ceb);
+ }
+ sb.add_blob(cs);
+ mh.get_file()->seek(mh.get_signature_offset());
+ sb.write_to_file(mh.get_file());
+ }
+ if (files_to_sign.size() > 1) {
+ print_verbose(vformat("CodeSign: Rebuilding fat executable..."));
+ LipO lip;
+ if (!lip.create_file(main_exe, files_to_sign)) {
+ CLEANUP();
+ r_error_msg = TTR("Failed to create fat binary.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to create fat binary.");
+ }
+ CLEANUP();
+ }
+ FileAccess::set_unix_permissions(main_exe, 0755); // Restore unix permissions.
+ return OK;
+#undef CLEANUP
+}
+
+Error CodeSign::codesign(bool p_use_hardened_runtime, bool p_force, const String &p_path, const String &p_ent_path, String &r_error_msg) {
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (!da) {
+ r_error_msg = TTR("Can't get filesystem access.");
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access.");
+ }
+
+ if (da->dir_exists(p_path)) {
+ String fmw_ver = "Current"; // Framework version (default).
+ String info_path;
+ String main_exe;
+ String bundle_path;
+ bool bundle = false;
+ bool ios_bundle = false;
+ if (da->file_exists(p_path.plus_file("Contents/Info.plist"))) {
+ info_path = p_path.plus_file("Contents/Info.plist");
+ main_exe = p_path.plus_file("Contents/MacOS");
+ bundle_path = p_path.plus_file("Contents");
+ bundle = true;
+ } else if (da->file_exists(p_path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver)))) {
+ info_path = p_path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver));
+ main_exe = p_path.plus_file(vformat("Versions/%s", fmw_ver));
+ bundle_path = p_path.plus_file(vformat("Versions/%s", fmw_ver));
+ bundle = true;
+ } else if (da->file_exists(p_path.plus_file("Info.plist"))) {
+ info_path = p_path.plus_file("Info.plist");
+ main_exe = p_path;
+ bundle_path = p_path;
+ bundle = true;
+ ios_bundle = true;
+ }
+ if (bundle) {
+ return _codesign_file(p_use_hardened_runtime, p_force, info_path, main_exe, bundle_path, p_ent_path, ios_bundle, r_error_msg);
+ } else {
+ r_error_msg = TTR("Unknown bundle type.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Unknown bundle type.");
+ }
+ } else if (da->file_exists(p_path)) {
+ return _codesign_file(p_use_hardened_runtime, p_force, "", p_path, "", p_ent_path, false, r_error_msg);
+ } else {
+ r_error_msg = TTR("Unknown object type.");
+ ERR_FAIL_V_MSG(FAILED, "CodeSign: Unknown object type.");
+ }
+}
+
+#endif // MODULE_REGEX_ENABLED
diff --git a/platform/osx/export/codesign.h b/platform/osx/export/codesign.h
new file mode 100644
index 0000000000..927df79281
--- /dev/null
+++ b/platform/osx/export/codesign.h
@@ -0,0 +1,369 @@
+/*************************************************************************/
+/* codesign.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+// macOS code signature creation utility.
+//
+// Current implementation has the following limitation:
+// - Only version 11.3.0 signatures are supported.
+// - Only "framework" and "app" bundle types are supported.
+// - Page hash array scattering is not supported.
+// - Reading and writing binary property lists i snot supported (third-party frameworks with binary Info.plist will not work unless .plist is converted to text format).
+// - Requirements code generator is not implemented (only hard-coded requirements for the ad-hoc signing is supported).
+// - RFC5652/CMS blob generation is not implemented, supports ad-hoc signing only.
+
+#ifndef CODESIGN_H
+#define CODESIGN_H
+
+#include "core/crypto/crypto.h"
+#include "core/crypto/crypto_core.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
+
+#include "modules/modules_enabled.gen.h" // For regex.
+#ifdef MODULE_REGEX_ENABLED
+#include "modules/regex/regex.h"
+#endif
+
+#include "plist.h"
+
+#ifdef MODULE_REGEX_ENABLED
+
+/*************************************************************************/
+/* CodeSignCodeResources */
+/*************************************************************************/
+
+class CodeSignCodeResources {
+public:
+ enum class CRMatch {
+ CR_MATCH_NO,
+ CR_MATCH_YES,
+ CR_MATCH_NESTED,
+ CR_MATCH_OPTIONAL,
+ };
+
+private:
+ struct CRFile {
+ String name;
+ String hash;
+ String hash2;
+ bool optional;
+ bool nested;
+ String requirements;
+ };
+
+ struct CRRule {
+ String file_pattern;
+ String key;
+ int weight;
+ bool store;
+ CRRule() {
+ weight = 1;
+ store = true;
+ }
+ CRRule(const String &p_file_pattern, const String &p_key, int p_weight, bool p_store) {
+ file_pattern = p_file_pattern;
+ key = p_key;
+ weight = p_weight;
+ store = p_store;
+ }
+ };
+
+ Vector<CRRule> rules1;
+ Vector<CRRule> rules2;
+
+ Vector<CRFile> files1;
+ Vector<CRFile> files2;
+
+ String hash_sha1_base64(const String &p_path);
+ String hash_sha256_base64(const String &p_path);
+
+public:
+ void add_rule1(const String &p_rule, const String &p_key = "", int p_weight = 0, bool p_store = true);
+ void add_rule2(const String &p_rule, const String &p_key = "", int p_weight = 0, bool p_store = true);
+
+ CRMatch match_rules1(const String &p_path) const;
+ CRMatch match_rules2(const String &p_path) const;
+
+ bool add_file1(const String &p_root, const String &p_path);
+ bool add_file2(const String &p_root, const String &p_path);
+ bool add_nested_file(const String &p_root, const String &p_path, const String &p_exepath);
+
+ bool add_folder_recursive(const String &p_root, const String &p_path = "", const String &p_main_exe_path = "");
+
+ bool save_to_file(const String &p_path);
+};
+
+/*************************************************************************/
+/* CodeSignBlob */
+/*************************************************************************/
+
+class CodeSignBlob : public RefCounted {
+public:
+ virtual PackedByteArray get_hash_sha1() const = 0;
+ virtual PackedByteArray get_hash_sha256() const = 0;
+
+ virtual int get_size() const = 0;
+ virtual uint32_t get_index_type() const = 0;
+
+ virtual void write_to_file(FileAccess *p_file) const = 0;
+};
+
+/*************************************************************************/
+/* CodeSignRequirements */
+/*************************************************************************/
+
+// Note: Proper code generator is not implemented (any we probably won't ever need it), just a hardcoded bytecode for the limited set of cases.
+
+class CodeSignRequirements : public CodeSignBlob {
+ PackedByteArray blob;
+
+ static inline size_t PAD(size_t s, size_t a) {
+ return (s % a == 0) ? 0 : (a - s % a);
+ }
+
+ _FORCE_INLINE_ void _parse_certificate_slot(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
+ _FORCE_INLINE_ void _parse_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
+ _FORCE_INLINE_ void _parse_oid_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
+ _FORCE_INLINE_ void _parse_hash_string(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
+ _FORCE_INLINE_ void _parse_value(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
+ _FORCE_INLINE_ void _parse_date(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
+ _FORCE_INLINE_ bool _parse_match(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const;
+
+public:
+ CodeSignRequirements();
+ CodeSignRequirements(const PackedByteArray &p_data);
+
+ Vector<String> parse_requirements() const;
+
+ virtual PackedByteArray get_hash_sha1() const override;
+ virtual PackedByteArray get_hash_sha256() const override;
+
+ virtual int get_size() const override;
+
+ virtual uint32_t get_index_type() const override { return 0x00000002; };
+ virtual void write_to_file(FileAccess *p_file) const override;
+};
+
+/*************************************************************************/
+/* CodeSignEntitlementsText */
+/*************************************************************************/
+
+// PList formatted entitlements.
+
+class CodeSignEntitlementsText : public CodeSignBlob {
+ PackedByteArray blob;
+
+public:
+ CodeSignEntitlementsText();
+ CodeSignEntitlementsText(const String &p_string);
+
+ virtual PackedByteArray get_hash_sha1() const override;
+ virtual PackedByteArray get_hash_sha256() const override;
+
+ virtual int get_size() const override;
+
+ virtual uint32_t get_index_type() const override { return 0x00000005; };
+ virtual void write_to_file(FileAccess *p_file) const override;
+};
+
+/*************************************************************************/
+/* CodeSignEntitlementsBinary */
+/*************************************************************************/
+
+// ASN.1 serialized entitlements.
+
+class CodeSignEntitlementsBinary : public CodeSignBlob {
+ PackedByteArray blob;
+
+public:
+ CodeSignEntitlementsBinary();
+ CodeSignEntitlementsBinary(const String &p_string);
+
+ virtual PackedByteArray get_hash_sha1() const override;
+ virtual PackedByteArray get_hash_sha256() const override;
+
+ virtual int get_size() const override;
+
+ virtual uint32_t get_index_type() const override { return 0x00000007; };
+ virtual void write_to_file(FileAccess *p_file) const override;
+};
+
+/*************************************************************************/
+/* CodeSignCodeDirectory */
+/*************************************************************************/
+
+// Code Directory, runtime options, code segment and special structure hashes.
+
+class CodeSignCodeDirectory : public CodeSignBlob {
+public:
+ enum Slot {
+ SLOT_INFO_PLIST = -1,
+ SLOT_REQUIREMENTS = -2,
+ SLOT_RESOURCES = -3,
+ SLOT_APP_SPECIFIC = -4, // Unused.
+ SLOT_ENTITLEMENTS = -5,
+ SLOT_RESERVER1 = -6, // Unused.
+ SLOT_DER_ENTITLEMENTS = -7,
+ };
+
+ enum CodeSignExecSegFlags {
+ EXECSEG_MAIN_BINARY = 0x1,
+ EXECSEG_ALLOW_UNSIGNED = 0x10,
+ EXECSEG_DEBUGGER = 0x20,
+ EXECSEG_JIT = 0x40,
+ EXECSEG_SKIP_LV = 0x80,
+ EXECSEG_CAN_LOAD_CDHASH = 0x100,
+ EXECSEG_CAN_EXEC_CDHASH = 0x200,
+ };
+
+ enum CodeSignatureFlags {
+ SIGNATURE_HOST = 0x0001,
+ SIGNATURE_ADHOC = 0x0002,
+ SIGNATURE_TASK_ALLOW = 0x0004,
+ SIGNATURE_INSTALLER = 0x0008,
+ SIGNATURE_FORCED_LV = 0x0010,
+ SIGNATURE_INVALID_ALLOWED = 0x0020,
+ SIGNATURE_FORCE_HARD = 0x0100,
+ SIGNATURE_FORCE_KILL = 0x0200,
+ SIGNATURE_FORCE_EXPIRATION = 0x0400,
+ SIGNATURE_RESTRICT = 0x0800,
+ SIGNATURE_ENFORCEMENT = 0x1000,
+ SIGNATURE_LIBRARY_VALIDATION = 0x2000,
+ SIGNATURE_ENTITLEMENTS_VALIDATED = 0x4000,
+ SIGNATURE_NVRAM_UNRESTRICTED = 0x8000,
+ SIGNATURE_RUNTIME = 0x10000,
+ SIGNATURE_LINKER_SIGNED = 0x20000,
+ };
+
+private:
+ PackedByteArray blob;
+
+ struct CodeDirectoryHeader {
+ uint32_t version; // Using version 0x0020500.
+ uint32_t flags; // // Option flags.
+ uint32_t hash_offset; // Slot zero offset.
+ uint32_t ident_offset; // Identifier string offset.
+ uint32_t special_slots; // Nr. of slots with negative index.
+ uint32_t code_slots; // Nr. of slots with index >= 0, (code_limit / page_size).
+ uint32_t code_limit; // Everything before code signature load command offset.
+ uint8_t hash_size; // 20 (SHA-1) or 32 (SHA-256).
+ uint8_t hash_type; // 1 (SHA-1) or 2 (SHA-256).
+ uint8_t platform; // Not used.
+ uint8_t page_size; // Page size, power of two, 2^12 (4096).
+ uint32_t spare2; // Not used.
+ // Version 0x20100
+ uint32_t scatter_vector_offset; // Set to 0 and ignore.
+ // Version 0x20200
+ uint32_t team_offset; // Team id string offset.
+ // Version 0x20300
+ uint32_t spare3; // Not used.
+ uint64_t code_limit_64; // Set to 0 and ignore.
+ // Version 0x20400
+ uint64_t exec_seg_base; // Start of the signed code segmet.
+ uint64_t exec_seg_limit; // Code segment (__TEXT) vmsize.
+ uint64_t exec_seg_flags; // Executable segment flags.
+ // Version 0x20500
+ uint32_t runtime; // Runtime version.
+ uint32_t pre_encrypt_offset; // Set to 0 and ignore.
+ };
+
+ int32_t pages = 0;
+ int32_t remain = 0;
+ int32_t code_slots = 0;
+ int32_t special_slots = 0;
+
+public:
+ CodeSignCodeDirectory();
+ CodeSignCodeDirectory(uint8_t p_hash_size, uint8_t p_hash_type, bool p_main, const CharString &p_id, const CharString &p_team_id, uint32_t p_page_size, uint64_t p_exe_limit, uint64_t p_code_limit);
+
+ int32_t get_page_count();
+ int32_t get_page_remainder();
+
+ bool set_hash_in_slot(const PackedByteArray &p_hash, int p_slot);
+
+ virtual PackedByteArray get_hash_sha1() const override;
+ virtual PackedByteArray get_hash_sha256() const override;
+
+ virtual int get_size() const override;
+ virtual uint32_t get_index_type() const override { return 0x00000000; };
+
+ virtual void write_to_file(FileAccess *p_file) const override;
+};
+
+/*************************************************************************/
+/* CodeSignSignature */
+/*************************************************************************/
+
+class CodeSignSignature : public CodeSignBlob {
+ PackedByteArray blob;
+
+public:
+ CodeSignSignature();
+
+ virtual PackedByteArray get_hash_sha1() const override;
+ virtual PackedByteArray get_hash_sha256() const override;
+
+ virtual int get_size() const override;
+ virtual uint32_t get_index_type() const override { return 0x00010000; };
+
+ virtual void write_to_file(FileAccess *p_file) const override;
+};
+
+/*************************************************************************/
+/* CodeSignSuperBlob */
+/*************************************************************************/
+
+class CodeSignSuperBlob {
+ Vector<Ref<CodeSignBlob>> blobs;
+
+public:
+ bool add_blob(const Ref<CodeSignBlob> &p_blob);
+
+ int get_size() const;
+ void write_to_file(FileAccess *p_file) const;
+};
+
+/*************************************************************************/
+/* CodeSign */
+/*************************************************************************/
+
+class CodeSign {
+ static PackedByteArray file_hash_sha1(const String &p_path);
+ static PackedByteArray file_hash_sha256(const String &p_path);
+ static Error _codesign_file(bool p_use_hardened_runtime, bool p_force, const String &p_info, const String &p_exe_path, const String &p_bundle_path, const String &p_ent_path, bool p_ios_bundle, String &r_error_msg);
+
+public:
+ static Error codesign(bool p_use_hardened_runtime, bool p_force, const String &p_path, const String &p_ent_path, String &r_error_msg);
+};
+
+#endif // MODULE_REGEX_ENABLED
+
+#endif // CODESIGN_H
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index afc0d63338..bd35b39e9e 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -33,6 +33,9 @@
#include "export_plugin.h"
void register_osx_exporter() {
+ EDITOR_DEF("export/macos/force_builtin_codesign", false);
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::BOOL, "export/macos/force_builtin_codesign", PROPERTY_HINT_NONE));
+
Ref<EditorExportPlatformOSX> platform;
platform.instantiate();
diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp
index 3a731f2172..f0b58efb63 100644
--- a/platform/osx/export/export_plugin.cpp
+++ b/platform/osx/export/export_plugin.cpp
@@ -28,6 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#include "modules/modules_enabled.gen.h" // For regex.
+
+#include "codesign.h"
#include "export_plugin.h"
void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
@@ -44,6 +47,23 @@ void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset>
r_features->push_back("64");
}
+bool EditorExportPlatformOSX::get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ // These options are not supported by built-in codesign, used on non macOS host.
+ if (!OS::get_singleton()->has_feature("macos")) {
+ if (p_option == "codesign/identity" || p_option == "codesign/timestamp" || p_option == "codesign/hardened_runtime" || p_option == "codesign/custom_options" || p_option.begins_with("notarization/")) {
+ return false;
+ }
+ }
+
+ // These entitlements are required to run managed code, and are always enabled in Mono builds.
+ if (Engine::get_singleton()->has_singleton("GodotSharp")) {
+ if (p_option == "codesign/entitlements/allow_jit_code_execution" || p_option == "codesign/entitlements/allow_unsigned_executable_memory" || p_option == "codesign/entitlements/allow_dyld_environment_variables") {
+ return false;
+ }
+ }
+ return true;
+}
+
void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
@@ -58,23 +78,28 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/location_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the location information"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/address_book_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the address book"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/calendar_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the calendar"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photos_library_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the photo library"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/desktop_folder_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use Desktop folder"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/documents_folder_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use Documents folder"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/downloads_folder_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use Downloads folder"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/network_volumes_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use network volumes"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/removable_volumes_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use removable volumes"), ""));
-#ifdef OSX_ENABLED
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
- if (!Engine::get_singleton()->has_singleton("GodotSharp")) {
- // These entitlements are required to run managed code, and are always enabled in Mono builds.
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false));
- }
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false));
@@ -103,7 +128,6 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), ""));
-#endif
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
@@ -305,13 +329,56 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
} else if (lines[i].find("$copyright") != -1) {
strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
} else if (lines[i].find("$highres") != -1) {
- strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "<true/>" : "<false/>") + "\n";
- } else if (lines[i].find("$camera_usage_description") != -1) {
- String description = p_preset->get("privacy/camera_usage_description");
- strnew += lines[i].replace("$camera_usage_description", description) + "\n";
- } else if (lines[i].find("$microphone_usage_description") != -1) {
- String description = p_preset->get("privacy/microphone_usage_description");
- strnew += lines[i].replace("$microphone_usage_description", description) + "\n";
+ strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "\t<true/>" : "\t<false/>") + "\n";
+ } else if (lines[i].find("$usage_descriptions") != -1) {
+ String descriptions;
+ if (!((String)p_preset->get("privacy/microphone_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSMicrophoneUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/microphone_usage_description") + "</string>\n";
+ }
+ if (!((String)p_preset->get("privacy/camera_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSCameraUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/camera_usage_description") + "</string>\n";
+ }
+ if (!((String)p_preset->get("privacy/location_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSLocationUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/location_usage_description") + "</string>\n";
+ }
+ if (!((String)p_preset->get("privacy/address_book_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSContactsUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/address_book_usage_description") + "</string>\n";
+ }
+ if (!((String)p_preset->get("privacy/calendar_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSCalendarsUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/calendar_usage_description") + "</string>\n";
+ }
+ if (!((String)p_preset->get("privacy/photos_library_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSPhotoLibraryUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/photos_library_usage_description") + "</string>\n";
+ }
+ if (!((String)p_preset->get("privacy/desktop_folder_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSDesktopFolderUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/desktop_folder_usage_description") + "</string>\n";
+ }
+ if (!((String)p_preset->get("privacy/documents_folder_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSDocumentsFolderUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/documents_folder_usage_description") + "</string>\n";
+ }
+ if (!((String)p_preset->get("privacy/downloads_folder_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSDownloadsFolderUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/downloads_folder_usage_description") + "</string>\n";
+ }
+ if (!((String)p_preset->get("privacy/network_volumes_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSNetworkVolumesUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/network_volumes_usage_description") + "</string>\n";
+ }
+ if (!((String)p_preset->get("privacy/removable_volumes_usage_description")).is_empty()) {
+ descriptions += "\t<key>NSRemovableVolumesUsageDescription</key>\n";
+ descriptions += "\t<string>" + (String)p_preset->get("privacy/removable_volumes_usage_description") + "</string>\n";
+ }
+ if (!descriptions.is_empty()) {
+ strnew += lines[i].replace("$usage_descriptions", descriptions);
+ }
} else {
strnew += lines[i] + "\n";
}
@@ -362,14 +429,16 @@ Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset
Error err = OS::get_singleton()->execute("xcrun", args, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
- print_line("altool (" + p_path + "):\n" + str);
+ print_verbose("altool (" + p_path + "):\n" + str);
if (str.find("RequestUUID") == -1) {
EditorNode::add_io_error("altool: " + str);
return FAILED;
} else {
- print_line("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email.");
- print_line(" You can check progress manually by opening a Terminal and running the following command:");
- print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\"");
+ print_line(TTR("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email."));
+ print_line(" " + TTR("You can check progress manually by opening a Terminal and running the following command:"));
+ print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\"");
+ print_line(" " + TTR("Run the following command to staple notarization ticket to the exported application (optional):"));
+ print_line(" \"xcrun stapler staple <app path>\"");
}
#endif
@@ -378,71 +447,94 @@ Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset
}
Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path) {
-#ifdef OSX_ENABLED
- List<String> args;
-
+ bool force_builtin_codesign = EditorSettings::get_singleton()->get("export/macos/force_builtin_codesign");
bool ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-");
- if (p_preset->get("codesign/timestamp")) {
- if (ad_hoc) {
+ if ((!FileAccess::exists("/usr/bin/codesign") && !FileAccess::exists("/bin/codesign")) || force_builtin_codesign) {
+ print_verbose("using built-in codesign...");
+#ifdef MODULE_REGEX_ENABLED
+
+#ifdef OSX_ENABLED
+ if (p_preset->get("codesign/timestamp")) {
WARN_PRINT("Timestamping is not compatible with ad-hoc signature, and was disabled!");
- } else {
- args.push_back("--timestamp");
}
- }
- if (p_preset->get("codesign/hardened_runtime")) {
- if (ad_hoc) {
+ if (p_preset->get("codesign/hardened_runtime")) {
WARN_PRINT("Hardened Runtime is not compatible with ad-hoc signature, and was disabled!");
- } else {
- args.push_back("--options");
- args.push_back("runtime");
}
- }
+#endif
- if (p_path.get_extension() != "dmg") {
- args.push_back("--entitlements");
- args.push_back(p_ent_path);
- }
+ String error_msg;
+ Error err = CodeSign::codesign(false, p_preset->get("codesign/replace_existing_signature"), p_path, p_ent_path, error_msg);
+ if (err != OK) {
+ EditorNode::add_io_error("Built-in CodeSign: " + error_msg);
+ return FAILED;
+ }
+#else
+ ERR_FAIL_V_MSG(FAILED, "Built-in CodeSign require regex module");
+#endif
+ return OK;
+ } else {
+ print_verbose("using external codesign...");
+ List<String> args;
+ if (p_preset->get("codesign/timestamp")) {
+ if (ad_hoc) {
+ WARN_PRINT("Timestamping is not compatible with ad-hoc signature, and was disabled!");
+ } else {
+ args.push_back("--timestamp");
+ }
+ }
+ if (p_preset->get("codesign/hardened_runtime")) {
+ if (ad_hoc) {
+ WARN_PRINT("Hardened Runtime is not compatible with ad-hoc signature, and was disabled!");
+ } else {
+ args.push_back("--options");
+ args.push_back("runtime");
+ }
+ }
- PackedStringArray user_args = p_preset->get("codesign/custom_options");
- for (int i = 0; i < user_args.size(); i++) {
- String user_arg = user_args[i].strip_edges();
- if (!user_arg.is_empty()) {
- args.push_back(user_arg);
+ if (p_path.get_extension() != "dmg") {
+ args.push_back("--entitlements");
+ args.push_back(p_ent_path);
}
- }
- args.push_back("-s");
- if (ad_hoc) {
- args.push_back("-");
- } else {
- args.push_back(p_preset->get("codesign/identity"));
- }
+ PackedStringArray user_args = p_preset->get("codesign/custom_options");
+ for (int i = 0; i < user_args.size(); i++) {
+ String user_arg = user_args[i].strip_edges();
+ if (!user_arg.is_empty()) {
+ args.push_back(user_arg);
+ }
+ }
- args.push_back("-v"); /* provide some more feedback */
+ args.push_back("-s");
+ if (ad_hoc) {
+ args.push_back("-");
+ } else {
+ args.push_back(p_preset->get("codesign/identity"));
+ }
- if (p_preset->get("codesign/replace_existing_signature")) {
- args.push_back("-f");
- }
+ args.push_back("-v"); /* provide some more feedback */
- args.push_back(p_path);
+ if (p_preset->get("codesign/replace_existing_signature")) {
+ args.push_back("-f");
+ }
- String str;
- Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true);
- ERR_FAIL_COND_V(err != OK, err);
+ args.push_back(p_path);
- print_line("codesign (" + p_path + "):\n" + str);
- if (str.find("no identity found") != -1) {
- EditorNode::add_io_error("codesign: no identity found");
- return FAILED;
- }
- if ((str.find("unrecognized blob type") != -1) || (str.find("cannot read entitlement data") != -1)) {
- EditorNode::add_io_error("codesign: invalid entitlements file");
- return FAILED;
- }
-#endif
+ String str;
+ Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true);
+ ERR_FAIL_COND_V(err != OK, err);
- return OK;
+ print_verbose("codesign (" + p_path + "):\n" + str);
+ if (str.find("no identity found") != -1) {
+ EditorNode::add_io_error("CodeSign: " + TTR("No identity found."));
+ return FAILED;
+ }
+ if ((str.find("unrecognized blob type") != -1) || (str.find("cannot read entitlement data") != -1)) {
+ EditorNode::add_io_error("CodeSign: " + TTR("Invalid entitlements file."));
+ return FAILED;
+ }
+ return OK;
+ }
}
Error EditorExportPlatformOSX::_code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path,
@@ -560,12 +652,12 @@ Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const Strin
Error err = OS::get_singleton()->execute("hdiutil", args, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
- print_line("hdiutil returned: " + str);
+ print_verbose("hdiutil returned: " + str);
if (str.find("create failed") != -1) {
if (str.find("File exists") != -1) {
- EditorNode::add_io_error("hdiutil: create failed - file exists");
+ EditorNode::add_io_error("hdiutil: " + TTR("DMG creation failed, file already exists."));
} else {
- EditorNode::add_io_error("hdiutil: create failed");
+ EditorNode::add_io_error("hdiutil: " + TTR("DMG create failed."));
}
return FAILED;
}
@@ -602,13 +694,13 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
FileAccess *src_f = nullptr;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
- if (ep.step("Creating app", 0)) {
+ if (ep.step(TTR("Creating app bundle"), 0)) {
return ERR_SKIP;
}
unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
if (!src_pkg_zip) {
- EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name);
+ EditorNode::add_io_error(TTR("Could not find template app to export:") + "\n" + src_pkg_name);
return ERR_FILE_NOT_FOUND;
}
@@ -627,12 +719,27 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
- String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip";
+ String export_format;
+ if (use_dmg() && p_path.ends_with("dmg")) {
+ export_format = "dmg";
+ } else if (p_path.ends_with("zip")) {
+ export_format = "zip";
+ } else if (p_path.ends_with("app")) {
+ export_format = "app";
+ } else {
+ EditorNode::add_io_error("Invalid export format");
+ return ERR_CANT_CREATE;
+ }
// Create our application bundle.
String tmp_app_dir_name = pkg_name + ".app";
- String tmp_app_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name);
- print_line("Exporting to " + tmp_app_path_name);
+ String tmp_app_path_name;
+ if (export_format == "app") {
+ tmp_app_path_name = p_path;
+ } else {
+ tmp_app_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name);
+ }
+ print_verbose("Exporting to " + tmp_app_path_name);
Error err = OK;
@@ -641,16 +748,22 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
err = ERR_CANT_CREATE;
}
+ if (DirAccess::exists(tmp_app_dir_name)) {
+ if (tmp_app_dir->change_dir(tmp_app_path_name) == OK) {
+ tmp_app_dir->erase_contents_recursive();
+ }
+ }
+
Array helpers = p_preset->get("codesign/entitlements/app_sandbox/helper_executables");
// Create our folder structure.
if (err == OK) {
- print_line("Creating " + tmp_app_path_name + "/Contents/MacOS");
+ print_verbose("Creating " + tmp_app_path_name + "/Contents/MacOS");
err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS");
}
if (err == OK) {
- print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks");
+ print_verbose("Creating " + tmp_app_path_name + "/Contents/Frameworks");
err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks");
}
@@ -660,10 +773,28 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
if (err == OK) {
- print_line("Creating " + tmp_app_path_name + "/Contents/Resources");
+ print_verbose("Creating " + tmp_app_path_name + "/Contents/Resources");
err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
}
+ Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations");
+ if (translations.size() > 0) {
+ {
+ String fname = tmp_app_path_name + "/Contents/Resources/en.lproj";
+ tmp_app_dir->make_dir_recursive(fname);
+ FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
+ }
+
+ for (const String &E : translations) {
+ Ref<Translation> tr = ResourceLoader::load(E);
+ if (tr.is_valid()) {
+ String fname = tmp_app_path_name + "/Contents/Resources/" + tr->get_locale() + ".lproj";
+ tmp_app_dir->make_dir_recursive(fname);
+ FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
+ }
+ }
+ }
+
// Now process our template.
bool found_binary = false;
Vector<String> dylibs_found;
@@ -689,6 +820,25 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
// Write.
file = file.replace_first("osx_template.app/", "");
+ if (((info.external_fa >> 16L) & 0120000) == 0120000) {
+#ifndef UNIX_ENABLED
+ WARN_PRINT(vformat("Relative symlinks are not supported on this OS, exported project might be broken!"));
+#endif
+ // Handle symlinks in the archive.
+ file = tmp_app_path_name.plus_file(file);
+ if (err == OK) {
+ err = tmp_app_dir->make_dir_recursive(file.get_base_dir());
+ }
+ if (err == OK) {
+ String lnk_data = String::utf8((const char *)data.ptr(), data.size());
+ err = tmp_app_dir->create_link(lnk_data, file);
+ print_verbose(vformat("ADDING SYMLINK %s => %s\n", file, lnk_data));
+ }
+
+ ret = unzGoToNextFile(src_pkg_zip);
+ continue; // next
+ }
+
if (file == "Contents/Info.plist") {
_fix_plist(p_preset, data, pkg_name);
}
@@ -752,7 +902,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
dylibs_found.push_back(file);
}
- print_line("ADDING: " + file + " size: " + itos(data.size()));
+ print_verbose("ADDING: " + file + " size: " + itos(data.size()));
// Write it into our application bundle.
file = tmp_app_path_name.plus_file(file);
@@ -782,12 +932,12 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
unzClose(src_pkg_zip);
if (!found_binary) {
- ERR_PRINT("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive.");
+ ERR_PRINT(vformat("Requested template binary '%s' not found. It might be missing from your template archive.", binary_to_use));
err = ERR_FILE_NOT_FOUND;
}
if (err == OK) {
- if (ep.step("Making PKG", 1)) {
+ if (ep.step(TTR("Making PKG"), 1)) {
return ERR_SKIP;
}
@@ -965,6 +1115,22 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
FileAccess::set_unix_permissions(tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), 0755);
}
}
+
+ bool ad_hoc = true;
+ if (err == OK) {
+#ifdef OSX_ENABLED
+ String sign_identity = p_preset->get("codesign/identity");
+#else
+ String sign_identity = "-";
+#endif
+ ad_hoc = (sign_identity == "" || sign_identity == "-");
+ bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation");
+ if ((!dylibs_found.is_empty() || !shared_objects.is_empty()) && sign_enabled && ad_hoc && !lib_validation) {
+ ERR_PRINT("Application with an ad-hoc signature require 'Disable Library Validation' entitlement to load dynamic libraries.");
+ err = ERR_CANT_CREATE;
+ }
+ }
+
if (err == OK) {
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
@@ -994,31 +1160,31 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
if (err == OK && sign_enabled) {
- if (ep.step("Code signing bundle", 2)) {
+ if (ep.step(TTR("Code signing bundle"), 2)) {
return ERR_SKIP;
}
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name, ent_path);
+ err = _code_sign(p_preset, tmp_app_path_name, ent_path);
}
if (export_format == "dmg") {
// Create a DMG.
if (err == OK) {
- if (ep.step("Making DMG", 3)) {
+ if (ep.step(TTR("Making DMG"), 3)) {
return ERR_SKIP;
}
err = _create_dmg(p_path, pkg_name, tmp_app_path_name);
}
// Sign DMG.
- if (err == OK && sign_enabled) {
- if (ep.step("Code signing DMG", 3)) {
+ if (err == OK && sign_enabled && !ad_hoc) {
+ if (ep.step(TTR("Code signing DMG"), 3)) {
return ERR_SKIP;
}
err = _code_sign(p_preset, p_path, ent_path);
}
- } else {
+ } else if (export_format == "zip") {
// Create ZIP.
if (err == OK) {
- if (ep.step("Making ZIP", 3)) {
+ if (ep.step(TTR("Making ZIP"), 3)) {
return ERR_SKIP;
}
if (FileAccess::exists(p_path)) {
@@ -1035,22 +1201,34 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
}
+#ifdef OSX_ENABLED
bool noto_enabled = p_preset->get("notarization/enable");
if (err == OK && noto_enabled) {
- if (ep.step("Sending archive for notarization", 4)) {
- return ERR_SKIP;
+ if (export_format == "app") {
+ WARN_PRINT("Notarization require app to be archived first, select DMG or ZIP export format instead.");
+ } else {
+ if (ep.step(TTR("Sending archive for notarization"), 4)) {
+ return ERR_SKIP;
+ }
+ err = _notarize(p_preset, p_path);
}
- err = _notarize(p_preset, p_path);
}
+#endif
// Clean up temporary entitlements files.
DirAccess::remove_file_or_error(hlp_ent_path);
- // Clean up temporary .app dir.
- tmp_app_dir->change_dir(tmp_app_path_name);
- tmp_app_dir->erase_contents_recursive();
- tmp_app_dir->change_dir("..");
- tmp_app_dir->remove(tmp_app_dir_name);
+ // Clean up temporary .app dir and generated entitlements.
+ if ((String)(p_preset->get("codesign/entitlements/custom_file")) == "") {
+ tmp_app_dir->remove(ent_path);
+ }
+ if (export_format != "app") {
+ if (tmp_app_dir->change_dir(tmp_app_path_name) == OK) {
+ tmp_app_dir->erase_contents_recursive();
+ tmp_app_dir->change_dir("..");
+ tmp_app_dir->remove(tmp_app_dir_name);
+ }
+ }
}
return err;
@@ -1152,7 +1330,7 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String
FileAccessRef fa = FileAccess::open(dir.plus_file(f), FileAccess::READ);
if (!fa) {
- ERR_FAIL_MSG("Can't open file to read from path '" + String(dir.plus_file(f)) + "'.");
+ ERR_FAIL_MSG(vformat("Can't open file to read from path \"%s\".", dir.plus_file(f)));
}
const int bufsize = 16384;
uint8_t buf[bufsize];
@@ -1209,11 +1387,19 @@ bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset
valid = false;
}
-#ifdef OSX_ENABLED
bool sign_enabled = p_preset->get("codesign/enable");
+
+#ifdef OSX_ENABLED
bool noto_enabled = p_preset->get("notarization/enable");
bool ad_hoc = ((p_preset->get("codesign/identity") == "") || (p_preset->get("codesign/identity") == "-"));
+ if (!ad_hoc && (bool)EditorSettings::get_singleton()->get("export/macos/force_builtin_codesign")) {
+ err += TTR("Warning: Built-in \"codesign\" is selected in the Editor Settings. Code signing is limited to ad-hoc signature only.") + "\n";
+ }
+ if (!ad_hoc && !FileAccess::exists("/usr/bin/codesign") && !FileAccess::exists("/bin/codesign")) {
+ err += TTR("Warning: Xcode command line tools are not installed, using built-in \"codesign\". Code signing is limited to ad-hoc signature only.") + "\n";
+ }
+
if (noto_enabled) {
if (ad_hoc) {
err += TTR("Notarization: Notarization with the ad-hoc signature is not supported.") + "\n";
@@ -1240,7 +1426,7 @@ bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset
valid = false;
}
} else {
- err += TTR("Notarization is disabled. Exported project will be blocked by Gatekeeper, if it's downloaded from an unknown source.") + "\n";
+ err += TTR("Warning: Notarization is disabled. Exported project will be blocked by Gatekeeper, if it's downloaded from an unknown source.") + "\n";
if (!sign_enabled) {
err += TTR("Code signing is disabled. Exported project will not run on Macs with enabled Gatekeeper and Apple Silicon powered Macs.") + "\n";
} else {
@@ -1253,9 +1439,39 @@ bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset
}
}
#else
- err += TTR("macOS code signing and Notarization is not supported on the host OS. Exported project will not run on Macs with enabled Gatekeeper and Apple Silicon powered Macs.") + "\n";
+ err += TTR("Warning: Notarization is not supported on this OS. Exported project will be blocked by Gatekeeper, if it's downloaded from an unknown source.") + "\n";
+ if (!sign_enabled) {
+ err += TTR("Code signing is disabled. Exported project will not run on Macs with enabled Gatekeeper and Apple Silicon powered Macs.") + "\n";
+ }
#endif
+ if (sign_enabled) {
+ if ((bool)p_preset->get("codesign/entitlements/audio_input") && ((String)p_preset->get("privacy/microphone_usage_description")).is_empty()) {
+ err += TTR("Privacy: Microphone access is enabled, but usage description is not specified.") + "\n";
+ valid = false;
+ }
+ if ((bool)p_preset->get("codesign/entitlements/camera") && ((String)p_preset->get("privacy/camera_usage_description")).is_empty()) {
+ err += TTR("Privacy: Camera access is enabled, but usage description is not specified.") + "\n";
+ valid = false;
+ }
+ if ((bool)p_preset->get("codesign/entitlements/location") && ((String)p_preset->get("privacy/location_usage_description")).is_empty()) {
+ err += TTR("Privacy: Location information access is enabled, but usage description is not specified.") + "\n";
+ valid = false;
+ }
+ if ((bool)p_preset->get("codesign/entitlements/address_book") && ((String)p_preset->get("privacy/address_book_usage_description")).is_empty()) {
+ err += TTR("Privacy: Address book access is enabled, but usage description is not specified.") + "\n";
+ valid = false;
+ }
+ if ((bool)p_preset->get("codesign/entitlements/calendars") && ((String)p_preset->get("privacy/calendar_usage_description")).is_empty()) {
+ err += TTR("Privacy: Calendar access is enabled, but usage description is not specified.") + "\n";
+ valid = false;
+ }
+ if ((bool)p_preset->get("codesign/entitlements/photos_library") && ((String)p_preset->get("privacy/photos_library_usage_description")).is_empty()) {
+ err += TTR("Privacy: Photo library access is enabled, but usage description is not specified.") + "\n";
+ valid = false;
+ }
+ }
+
if (!err.is_empty()) {
r_error = err;
}
diff --git a/platform/osx/export/export_plugin.h b/platform/osx/export/export_plugin.h
index aa22ad6384..0c2ac90206 100644
--- a/platform/osx/export/export_plugin.h
+++ b/platform/osx/export/export_plugin.h
@@ -68,13 +68,13 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
-#ifdef OSX_ENABLED
bool use_codesign() const { return true; }
+#ifdef OSX_ENABLED
bool use_dmg() const { return true; }
#else
- bool use_codesign() const { return false; }
bool use_dmg() const { return false; }
#endif
+
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
String pname = p_package;
@@ -101,6 +101,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
protected:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override;
virtual void get_export_options(List<ExportOption> *r_options) override;
+ virtual bool get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
public:
virtual String get_name() const override { return "macOS"; }
@@ -113,6 +114,7 @@ public:
list.push_back("dmg");
}
list.push_back("zip");
+ list.push_back("app");
return list;
}
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
diff --git a/platform/osx/export/lipo.cpp b/platform/osx/export/lipo.cpp
new file mode 100644
index 0000000000..66d2ecdbcf
--- /dev/null
+++ b/platform/osx/export/lipo.cpp
@@ -0,0 +1,243 @@
+/*************************************************************************/
+/* lipo.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "modules/modules_enabled.gen.h" // For regex.
+
+#include "lipo.h"
+
+#ifdef MODULE_REGEX_ENABLED
+
+bool LipO::is_lipo(const String &p_path) {
+ FileAccessRef fb = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(!fb, false, vformat("LipO: Can't open file: \"%s\".", p_path));
+ uint32_t magic = fb->get_32();
+ return (magic == 0xbebafeca || magic == 0xcafebabe || magic == 0xbfbafeca || magic == 0xcafebabf);
+}
+
+bool LipO::create_file(const String &p_output_path, const PackedStringArray &p_files) {
+ close();
+
+ fa = FileAccess::open(p_output_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(!fa, false, vformat("LipO: Can't open file: \"%s\".", p_output_path));
+
+ uint64_t max_size = 0;
+ for (int i = 0; i < p_files.size(); i++) {
+ MachO mh;
+ if (!mh.open_file(p_files[i])) {
+ ERR_FAIL_V_MSG(false, vformat("LipO: Invalid MachO file: \"%s.\"", p_files[i]));
+ }
+
+ FatArch arch;
+ arch.cputype = mh.get_cputype();
+ arch.cpusubtype = mh.get_cpusubtype();
+ arch.offset = 0;
+ arch.size = mh.get_size();
+ arch.align = mh.get_align();
+ max_size += arch.size;
+
+ archs.push_back(arch);
+
+ FileAccessRef fb = FileAccess::open(p_files[i], FileAccess::READ);
+ if (!fb) {
+ close();
+ ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s.\"", p_files[i]));
+ }
+ }
+
+ // Write header.
+ bool is_64 = (max_size >= std::numeric_limits<uint32_t>::max());
+ if (is_64) {
+ fa->store_32(0xbfbafeca);
+ } else {
+ fa->store_32(0xbebafeca);
+ }
+ fa->store_32(BSWAP32(archs.size()));
+ uint64_t offset = archs.size() * (is_64 ? 32 : 20) + 8;
+ for (int i = 0; i < archs.size(); i++) {
+ archs.write[i].offset = offset + PAD(offset, uint64_t(1) << archs[i].align);
+ if (is_64) {
+ fa->store_32(BSWAP32(archs[i].cputype));
+ fa->store_32(BSWAP32(archs[i].cpusubtype));
+ fa->store_64(BSWAP64(archs[i].offset));
+ fa->store_64(BSWAP64(archs[i].size));
+ fa->store_32(BSWAP32(archs[i].align));
+ fa->store_32(0);
+ } else {
+ fa->store_32(BSWAP32(archs[i].cputype));
+ fa->store_32(BSWAP32(archs[i].cpusubtype));
+ fa->store_32(BSWAP32(archs[i].offset));
+ fa->store_32(BSWAP32(archs[i].size));
+ fa->store_32(BSWAP32(archs[i].align));
+ }
+ offset = archs[i].offset + archs[i].size;
+ }
+
+ // Write files and padding.
+ for (int i = 0; i < archs.size(); i++) {
+ FileAccessRef fb = FileAccess::open(p_files[i], FileAccess::READ);
+ if (!fb) {
+ close();
+ ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s.\"", p_files[i]));
+ }
+ uint64_t cur = fa->get_position();
+ for (uint64_t j = cur; j < archs[i].offset; j++) {
+ fa->store_8(0);
+ }
+ int pages = archs[i].size / 4096;
+ int remain = archs[i].size % 4096;
+ unsigned char step[4096];
+ for (int j = 0; j < pages; j++) {
+ uint64_t br = fb->get_buffer(step, 4096);
+ if (br > 0) {
+ fa->store_buffer(step, br);
+ }
+ }
+ uint64_t br = fb->get_buffer(step, remain);
+ if (br > 0) {
+ fa->store_buffer(step, br);
+ }
+ fb->close();
+ }
+ return true;
+}
+
+bool LipO::open_file(const String &p_path) {
+ close();
+
+ fa = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(!fa, false, vformat("LipO: Can't open file: \"%s\".", p_path));
+
+ uint32_t magic = fa->get_32();
+ if (magic == 0xbebafeca) {
+ // 32-bit fat binary, bswap.
+ uint32_t nfat_arch = BSWAP32(fa->get_32());
+ for (uint32_t i = 0; i < nfat_arch; i++) {
+ FatArch arch;
+ arch.cputype = BSWAP32(fa->get_32());
+ arch.cpusubtype = BSWAP32(fa->get_32());
+ arch.offset = BSWAP32(fa->get_32());
+ arch.size = BSWAP32(fa->get_32());
+ arch.align = BSWAP32(fa->get_32());
+
+ archs.push_back(arch);
+ }
+ } else if (magic == 0xcafebabe) {
+ // 32-bit fat binary.
+ uint32_t nfat_arch = fa->get_32();
+ for (uint32_t i = 0; i < nfat_arch; i++) {
+ FatArch arch;
+ arch.cputype = fa->get_32();
+ arch.cpusubtype = fa->get_32();
+ arch.offset = fa->get_32();
+ arch.size = fa->get_32();
+ arch.align = fa->get_32();
+
+ archs.push_back(arch);
+ }
+ } else if (magic == 0xbfbafeca) {
+ // 64-bit fat binary, bswap.
+ uint32_t nfat_arch = BSWAP32(fa->get_32());
+ for (uint32_t i = 0; i < nfat_arch; i++) {
+ FatArch arch;
+ arch.cputype = BSWAP32(fa->get_32());
+ arch.cpusubtype = BSWAP32(fa->get_32());
+ arch.offset = BSWAP64(fa->get_64());
+ arch.size = BSWAP64(fa->get_64());
+ arch.align = BSWAP32(fa->get_32());
+ fa->get_32(); // Skip, reserved.
+
+ archs.push_back(arch);
+ }
+ } else if (magic == 0xcafebabf) {
+ // 64-bit fat binary.
+ uint32_t nfat_arch = fa->get_32();
+ for (uint32_t i = 0; i < nfat_arch; i++) {
+ FatArch arch;
+ arch.cputype = fa->get_32();
+ arch.cpusubtype = fa->get_32();
+ arch.offset = fa->get_64();
+ arch.size = fa->get_64();
+ arch.align = fa->get_32();
+ fa->get_32(); // Skip, reserved.
+
+ archs.push_back(arch);
+ }
+ } else {
+ close();
+ ERR_FAIL_V_MSG(false, vformat("LipO: Invalid fat binary: \"%s\".", p_path));
+ }
+ return true;
+}
+
+int LipO::get_arch_count() const {
+ ERR_FAIL_COND_V_MSG(!fa, 0, "LipO: File not opened.");
+ return archs.size();
+}
+
+bool LipO::extract_arch(int p_index, const String &p_path) {
+ ERR_FAIL_COND_V_MSG(!fa, false, "LipO: File not opened.");
+ ERR_FAIL_INDEX_V(p_index, archs.size(), false);
+
+ FileAccessRef fb = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(!fb, false, vformat("LipO: Can't open file: \"%s\".", p_path));
+
+ fa->seek(archs[p_index].offset);
+
+ int pages = archs[p_index].size / 4096;
+ int remain = archs[p_index].size % 4096;
+ unsigned char step[4096];
+ for (int i = 0; i < pages; i++) {
+ uint64_t br = fa->get_buffer(step, 4096);
+ if (br > 0) {
+ fb->store_buffer(step, br);
+ }
+ }
+ uint64_t br = fa->get_buffer(step, remain);
+ if (br > 0) {
+ fb->store_buffer(step, br);
+ }
+ fb->close();
+ return true;
+}
+
+void LipO::close() {
+ if (fa) {
+ fa->close();
+ memdelete(fa);
+ fa = nullptr;
+ }
+ archs.clear();
+}
+
+LipO::~LipO() {
+ close();
+}
+
+#endif // MODULE_REGEX_ENABLED
diff --git a/modules/pvr/register_types.cpp b/platform/osx/export/lipo.h
index dcc7f505a1..68bbe42dd6 100644
--- a/modules/pvr/register_types.cpp
+++ b/platform/osx/export/lipo.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* register_types.cpp */
+/* lipo.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,21 +28,49 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "register_types.h"
+// Universal / Universal 2 fat binary file creator and extractor.
-#include "image_compress_pvrtc.h"
-#include "texture_loader_pvr.h"
+#ifndef LIPO_H
+#define LIPO_H
-static Ref<ResourceFormatPVR> resource_loader_pvr;
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
+#include "modules/modules_enabled.gen.h" // For regex.
-void register_pvr_types() {
- resource_loader_pvr.instantiate();
- ResourceLoader::add_resource_format_loader(resource_loader_pvr);
+#include "macho.h"
- _register_pvrtc_compress_func();
-}
+#ifdef MODULE_REGEX_ENABLED
-void unregister_pvr_types() {
- ResourceLoader::remove_resource_format_loader(resource_loader_pvr);
- resource_loader_pvr.unref();
-}
+class LipO : public RefCounted {
+ struct FatArch {
+ uint32_t cputype;
+ uint32_t cpusubtype;
+ uint64_t offset;
+ uint64_t size;
+ uint32_t align;
+ };
+
+ FileAccess *fa = nullptr;
+ Vector<FatArch> archs;
+
+ static inline size_t PAD(size_t s, size_t a) {
+ return (a - s % a);
+ }
+
+public:
+ static bool is_lipo(const String &p_path);
+
+ bool create_file(const String &p_output_path, const PackedStringArray &p_files);
+
+ bool open_file(const String &p_path);
+ int get_arch_count() const;
+ bool extract_arch(int p_index, const String &p_path);
+
+ void close();
+
+ ~LipO();
+};
+
+#endif // MODULE_REGEX_ENABLED
+
+#endif // LIPO_H
diff --git a/platform/osx/export/macho.cpp b/platform/osx/export/macho.cpp
new file mode 100644
index 0000000000..08f2a855b0
--- /dev/null
+++ b/platform/osx/export/macho.cpp
@@ -0,0 +1,556 @@
+/*************************************************************************/
+/* macho.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "modules/modules_enabled.gen.h" // For regex.
+
+#include "macho.h"
+
+#ifdef MODULE_REGEX_ENABLED
+
+uint32_t MachO::seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max) {
+ uint32_t align = p_max;
+ if (p_vmaddr != 0) {
+ uint64_t seg_align = 1;
+ align = 0;
+ while ((seg_align & p_vmaddr) == 0) {
+ seg_align = seg_align << 1;
+ align++;
+ }
+ align = CLAMP(align, p_min, p_max);
+ }
+ return align;
+}
+
+bool MachO::alloc_signature(uint64_t p_size) {
+ ERR_FAIL_COND_V_MSG(!fa, false, "MachO: File not opened.");
+ if (signature_offset != 0) {
+ // Nothing to do, already have signature load command.
+ return true;
+ }
+ if (lc_limit == 0 || lc_limit + 16 > exe_base) {
+ ERR_FAIL_V_MSG(false, "MachO: Can't allocate signature load command, please use \"codesign_allocate\" utility first.");
+ } else {
+ // Add signature load command.
+ signature_offset = lc_limit;
+
+ fa->seek(lc_limit);
+ LoadCommandHeader lc;
+ lc.cmd = LC_CODE_SIGNATURE;
+ lc.cmdsize = 16;
+ if (swap) {
+ lc.cmdsize = BSWAP32(lc.cmdsize);
+ }
+ fa->store_buffer((const uint8_t *)&lc, sizeof(LoadCommandHeader));
+
+ uint32_t lc_offset = fa->get_length() + PAD(fa->get_length(), 16);
+ uint32_t lc_size = 0;
+ if (swap) {
+ lc_offset = BSWAP32(lc_offset);
+ lc_size = BSWAP32(lc_size);
+ }
+ fa->store_32(lc_offset);
+ fa->store_32(lc_size);
+
+ // Write new command number.
+ fa->seek(0x10);
+ uint32_t ncmds = fa->get_32();
+ uint32_t cmdssize = fa->get_32();
+ if (swap) {
+ ncmds = BSWAP32(ncmds);
+ cmdssize = BSWAP32(cmdssize);
+ }
+ ncmds += 1;
+ cmdssize += 16;
+ if (swap) {
+ ncmds = BSWAP32(ncmds);
+ cmdssize = BSWAP32(cmdssize);
+ }
+ fa->seek(0x10);
+ fa->store_32(ncmds);
+ fa->store_32(cmdssize);
+
+ lc_limit = lc_limit + sizeof(LoadCommandHeader) + 8;
+
+ return true;
+ }
+}
+
+bool MachO::is_macho(const String &p_path) {
+ FileAccessRef fb = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(!fb, false, vformat("MachO: Can't open file: \"%s\".", p_path));
+ uint32_t magic = fb->get_32();
+ return (magic == 0xcefaedfe || magic == 0xfeedface || magic == 0xcffaedfe || magic == 0xfeedfacf);
+}
+
+bool MachO::open_file(const String &p_path) {
+ fa = FileAccess::open(p_path, FileAccess::READ_WRITE);
+ ERR_FAIL_COND_V_MSG(!fa, false, vformat("MachO: Can't open file: \"%s\".", p_path));
+ uint32_t magic = fa->get_32();
+ MachHeader mach_header;
+
+ // Read MachO header.
+ swap = (magic == 0xcffaedfe || magic == 0xcefaedfe);
+ if (magic == 0xcefaedfe || magic == 0xfeedface) {
+ // Thin 32-bit binary.
+ fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
+ } else if (magic == 0xcffaedfe || magic == 0xfeedfacf) {
+ // Thin 64-bit binary.
+ fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader));
+ fa->get_32(); // Skip extra reserved field.
+ } else {
+ ERR_FAIL_V_MSG(false, vformat("MachO: File is not a valid MachO binary: \"%s\".", p_path));
+ }
+
+ if (swap) {
+ mach_header.ncmds = BSWAP32(mach_header.ncmds);
+ mach_header.cpusubtype = BSWAP32(mach_header.cpusubtype);
+ mach_header.cputype = BSWAP32(mach_header.cputype);
+ }
+ cpusubtype = mach_header.cpusubtype;
+ cputype = mach_header.cputype;
+ align = 0;
+ exe_base = std::numeric_limits<uint64_t>::max();
+ exe_limit = 0;
+ lc_limit = 0;
+ link_edit_offset = 0;
+ signature_offset = 0;
+
+ // Read load commands.
+ for (uint32_t i = 0; i < mach_header.ncmds; i++) {
+ LoadCommandHeader lc;
+ fa->get_buffer((uint8_t *)&lc, sizeof(LoadCommandHeader));
+ if (swap) {
+ lc.cmd = BSWAP32(lc.cmd);
+ lc.cmdsize = BSWAP32(lc.cmdsize);
+ }
+ uint64_t ps = fa->get_position();
+ switch (lc.cmd) {
+ case LC_SEGMENT: {
+ LoadCommandSegment lc_seg;
+ fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment));
+ if (swap) {
+ lc_seg.nsects = BSWAP32(lc_seg.nsects);
+ lc_seg.vmaddr = BSWAP32(lc_seg.vmaddr);
+ lc_seg.vmsize = BSWAP32(lc_seg.vmsize);
+ }
+ align = MAX(align, seg_align(lc_seg.vmaddr, 2, 15));
+ if (String(lc_seg.segname) == "__TEXT") {
+ exe_limit = MAX(exe_limit, lc_seg.vmsize);
+ for (uint32_t j = 0; j < lc_seg.nsects; j++) {
+ Section lc_sect;
+ fa->get_buffer((uint8_t *)&lc_sect, sizeof(Section));
+ if (String(lc_sect.sectname) == "__text") {
+ if (swap) {
+ exe_base = MIN(exe_base, BSWAP32(lc_sect.offset));
+ } else {
+ exe_base = MIN(exe_base, lc_sect.offset);
+ }
+ }
+ if (swap) {
+ align = MAX(align, BSWAP32(lc_sect.align));
+ } else {
+ align = MAX(align, lc_sect.align);
+ }
+ }
+ } else if (String(lc_seg.segname) == "__LINKEDIT") {
+ link_edit_offset = ps - 8;
+ }
+ } break;
+ case LC_SEGMENT_64: {
+ LoadCommandSegment64 lc_seg;
+ fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment64));
+ if (swap) {
+ lc_seg.nsects = BSWAP32(lc_seg.nsects);
+ lc_seg.vmaddr = BSWAP64(lc_seg.vmaddr);
+ lc_seg.vmsize = BSWAP64(lc_seg.vmsize);
+ }
+ align = MAX(align, seg_align(lc_seg.vmaddr, 3, 15));
+ if (String(lc_seg.segname) == "__TEXT") {
+ exe_limit = MAX(exe_limit, lc_seg.vmsize);
+ for (uint32_t j = 0; j < lc_seg.nsects; j++) {
+ Section64 lc_sect;
+ fa->get_buffer((uint8_t *)&lc_sect, sizeof(Section64));
+ if (String(lc_sect.sectname) == "__text") {
+ if (swap) {
+ exe_base = MIN(exe_base, BSWAP32(lc_sect.offset));
+ } else {
+ exe_base = MIN(exe_base, lc_sect.offset);
+ }
+ if (swap) {
+ align = MAX(align, BSWAP32(lc_sect.align));
+ } else {
+ align = MAX(align, lc_sect.align);
+ }
+ }
+ }
+ } else if (String(lc_seg.segname) == "__LINKEDIT") {
+ link_edit_offset = ps - 8;
+ }
+ } break;
+ case LC_CODE_SIGNATURE: {
+ signature_offset = ps - 8;
+ } break;
+ default: {
+ } break;
+ }
+ fa->seek(ps + lc.cmdsize - 8);
+ lc_limit = ps + lc.cmdsize - 8;
+ }
+
+ if (exe_limit == 0 || lc_limit == 0) {
+ ERR_FAIL_V_MSG(false, vformat("MachO: No load commands or executable code found: \"%s\".", p_path));
+ }
+
+ return true;
+}
+
+uint64_t MachO::get_exe_base() {
+ ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ return exe_base;
+}
+
+uint64_t MachO::get_exe_limit() {
+ ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ return exe_limit;
+}
+
+int32_t MachO::get_align() {
+ ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ return align;
+}
+
+uint32_t MachO::get_cputype() {
+ ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ return cputype;
+}
+
+uint32_t MachO::get_cpusubtype() {
+ ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ return cpusubtype;
+}
+
+uint64_t MachO::get_size() {
+ ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ return fa->get_length();
+}
+
+uint64_t MachO::get_signature_offset() {
+ ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(signature_offset == 0, 0, "MachO: No signature load command.");
+
+ fa->seek(signature_offset + 8);
+ if (swap) {
+ return BSWAP32(fa->get_32());
+ } else {
+ return fa->get_32();
+ }
+}
+
+uint64_t MachO::get_code_limit() {
+ ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+
+ if (signature_offset == 0) {
+ return fa->get_length() + PAD(fa->get_length(), 16);
+ } else {
+ return get_signature_offset();
+ }
+}
+
+uint64_t MachO::get_signature_size() {
+ ERR_FAIL_COND_V_MSG(!fa, 0, "MachO: File not opened.");
+ ERR_FAIL_COND_V_MSG(signature_offset == 0, 0, "MachO: No signature load command.");
+
+ fa->seek(signature_offset + 12);
+ if (swap) {
+ return BSWAP32(fa->get_32());
+ } else {
+ return fa->get_32();
+ }
+}
+
+bool MachO::is_signed() {
+ ERR_FAIL_COND_V_MSG(!fa, false, "MachO: File not opened.");
+ if (signature_offset == 0) {
+ return false;
+ }
+
+ fa->seek(get_signature_offset());
+ uint32_t magic = BSWAP32(fa->get_32());
+ if (magic != 0xfade0cc0) {
+ return false; // No SuperBlob found.
+ }
+ fa->get_32(); // Skip size field, unused.
+ uint32_t count = BSWAP32(fa->get_32());
+ for (uint32_t i = 0; i < count; i++) {
+ uint32_t index_type = BSWAP32(fa->get_32());
+ uint32_t offset = BSWAP32(fa->get_32());
+ if (index_type == 0x00000000) { // CodeDirectory index type.
+ fa->seek(get_signature_offset() + offset + 12);
+ uint32_t flags = BSWAP32(fa->get_32());
+ if (flags & 0x20000) {
+ return false; // Found CD, linker-signed.
+ } else {
+ return true; // Found CD, not linker-signed.
+ }
+ }
+ }
+ return false; // No CD found.
+}
+
+PackedByteArray MachO::get_cdhash_sha1() {
+ ERR_FAIL_COND_V_MSG(!fa, PackedByteArray(), "MachO: File not opened.");
+ if (signature_offset == 0) {
+ return PackedByteArray();
+ }
+
+ fa->seek(get_signature_offset());
+ uint32_t magic = BSWAP32(fa->get_32());
+ if (magic != 0xfade0cc0) {
+ return PackedByteArray(); // No SuperBlob found.
+ }
+ fa->get_32(); // Skip size field, unused.
+ uint32_t count = BSWAP32(fa->get_32());
+ for (uint32_t i = 0; i < count; i++) {
+ fa->get_32(); // Index type, skip.
+ uint32_t offset = BSWAP32(fa->get_32());
+ uint64_t pos = fa->get_position();
+
+ fa->seek(get_signature_offset() + offset);
+ uint32_t cdmagic = BSWAP32(fa->get_32());
+ uint32_t cdsize = BSWAP32(fa->get_32());
+ if (cdmagic == 0xfade0c02) { // CodeDirectory.
+ fa->seek(get_signature_offset() + offset + 36);
+ uint8_t hash_size = fa->get_8();
+ uint8_t hash_type = fa->get_8();
+ if (hash_size == 0x14 && hash_type == 0x01) { /* SHA-1 */
+ PackedByteArray hash;
+ hash.resize(0x14);
+
+ fa->seek(get_signature_offset() + offset);
+ PackedByteArray blob;
+ blob.resize(cdsize);
+ fa->get_buffer(blob.ptrw(), cdsize);
+
+ CryptoCore::SHA1Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+ }
+ }
+ fa->seek(pos);
+ }
+ return PackedByteArray();
+}
+
+PackedByteArray MachO::get_cdhash_sha256() {
+ ERR_FAIL_COND_V_MSG(!fa, PackedByteArray(), "MachO: File not opened.");
+ if (signature_offset == 0) {
+ return PackedByteArray();
+ }
+
+ fa->seek(get_signature_offset());
+ uint32_t magic = BSWAP32(fa->get_32());
+ if (magic != 0xfade0cc0) {
+ return PackedByteArray(); // No SuperBlob found.
+ }
+ fa->get_32(); // Skip size field, unused.
+ uint32_t count = BSWAP32(fa->get_32());
+ for (uint32_t i = 0; i < count; i++) {
+ fa->get_32(); // Index type, skip.
+ uint32_t offset = BSWAP32(fa->get_32());
+ uint64_t pos = fa->get_position();
+
+ fa->seek(get_signature_offset() + offset);
+ uint32_t cdmagic = BSWAP32(fa->get_32());
+ uint32_t cdsize = BSWAP32(fa->get_32());
+ if (cdmagic == 0xfade0c02) { // CodeDirectory.
+ fa->seek(get_signature_offset() + offset + 36);
+ uint8_t hash_size = fa->get_8();
+ uint8_t hash_type = fa->get_8();
+ if (hash_size == 0x20 && hash_type == 0x02) { /* SHA-256 */
+ PackedByteArray hash;
+ hash.resize(0x20);
+
+ fa->seek(get_signature_offset() + offset);
+ PackedByteArray blob;
+ blob.resize(cdsize);
+ fa->get_buffer(blob.ptrw(), cdsize);
+
+ CryptoCore::SHA256Context ctx;
+ ctx.start();
+ ctx.update(blob.ptr(), blob.size());
+ ctx.finish(hash.ptrw());
+
+ return hash;
+ }
+ }
+ fa->seek(pos);
+ }
+ return PackedByteArray();
+}
+
+PackedByteArray MachO::get_requirements() {
+ ERR_FAIL_COND_V_MSG(!fa, PackedByteArray(), "MachO: File not opened.");
+ if (signature_offset == 0) {
+ return PackedByteArray();
+ }
+
+ fa->seek(get_signature_offset());
+ uint32_t magic = BSWAP32(fa->get_32());
+ if (magic != 0xfade0cc0) {
+ return PackedByteArray(); // No SuperBlob found.
+ }
+ fa->get_32(); // Skip size field, unused.
+ uint32_t count = BSWAP32(fa->get_32());
+ for (uint32_t i = 0; i < count; i++) {
+ fa->get_32(); // Index type, skip.
+ uint32_t offset = BSWAP32(fa->get_32());
+ uint64_t pos = fa->get_position();
+
+ fa->seek(get_signature_offset() + offset);
+ uint32_t rqmagic = BSWAP32(fa->get_32());
+ uint32_t rqsize = BSWAP32(fa->get_32());
+ if (rqmagic == 0xfade0c01) { // Requirements.
+ PackedByteArray blob;
+ fa->seek(get_signature_offset() + offset);
+ blob.resize(rqsize);
+ fa->get_buffer(blob.ptrw(), rqsize);
+ return blob;
+ }
+ fa->seek(pos);
+ }
+ return PackedByteArray();
+}
+
+const FileAccess *MachO::get_file() const {
+ return fa;
+}
+
+FileAccess *MachO::get_file() {
+ return fa;
+}
+
+bool MachO::set_signature_size(uint64_t p_size) {
+ ERR_FAIL_COND_V_MSG(!fa, false, "MachO: File not opened.");
+
+ // Ensure signature load command exists.
+ ERR_FAIL_COND_V_MSG(link_edit_offset == 0, false, "MachO: No __LINKEDIT segment found.");
+ ERR_FAIL_COND_V_MSG(!alloc_signature(p_size), false, "MachO: Can't allocate signature load command.");
+
+ // Update signature load command.
+ uint64_t old_size = get_signature_size();
+ uint64_t new_size = p_size + PAD(p_size, 16384);
+
+ if (new_size <= old_size) {
+ fa->seek(get_signature_offset());
+ for (uint64_t i = 0; i < old_size; i++) {
+ fa->store_8(0x00);
+ }
+ return true;
+ }
+
+ fa->seek(signature_offset + 12);
+ if (swap) {
+ fa->store_32(BSWAP32(new_size));
+ } else {
+ fa->store_32(new_size);
+ }
+
+ uint64_t end = get_signature_offset() + new_size;
+
+ // Update "__LINKEDIT" segment.
+ LoadCommandHeader lc;
+ fa->seek(link_edit_offset);
+ fa->get_buffer((uint8_t *)&lc, sizeof(LoadCommandHeader));
+ if (swap) {
+ lc.cmd = BSWAP32(lc.cmd);
+ lc.cmdsize = BSWAP32(lc.cmdsize);
+ }
+ switch (lc.cmd) {
+ case LC_SEGMENT: {
+ LoadCommandSegment lc_seg;
+ fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment));
+ if (swap) {
+ lc_seg.vmsize = BSWAP32(lc_seg.vmsize);
+ lc_seg.filesize = BSWAP32(lc_seg.filesize);
+ lc_seg.fileoff = BSWAP32(lc_seg.fileoff);
+ }
+
+ lc_seg.vmsize = end - lc_seg.fileoff;
+ lc_seg.vmsize += PAD(lc_seg.vmsize, 4096);
+ lc_seg.filesize = end - lc_seg.fileoff;
+
+ if (swap) {
+ lc_seg.vmsize = BSWAP32(lc_seg.vmsize);
+ lc_seg.filesize = BSWAP32(lc_seg.filesize);
+ }
+ fa->seek(link_edit_offset + 8);
+ fa->store_buffer((const uint8_t *)&lc_seg, sizeof(LoadCommandSegment));
+ } break;
+ case LC_SEGMENT_64: {
+ LoadCommandSegment64 lc_seg;
+ fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment64));
+ if (swap) {
+ lc_seg.vmsize = BSWAP64(lc_seg.vmsize);
+ lc_seg.filesize = BSWAP64(lc_seg.filesize);
+ lc_seg.fileoff = BSWAP64(lc_seg.fileoff);
+ }
+ lc_seg.vmsize = end - lc_seg.fileoff;
+ lc_seg.vmsize += PAD(lc_seg.vmsize, 4096);
+ lc_seg.filesize = end - lc_seg.fileoff;
+ if (swap) {
+ lc_seg.vmsize = BSWAP64(lc_seg.vmsize);
+ lc_seg.filesize = BSWAP64(lc_seg.filesize);
+ }
+ fa->seek(link_edit_offset + 8);
+ fa->store_buffer((const uint8_t *)&lc_seg, sizeof(LoadCommandSegment64));
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(false, "MachO: Invalid __LINKEDIT segment type.");
+ } break;
+ }
+ fa->seek(get_signature_offset());
+ for (uint64_t i = 0; i < new_size; i++) {
+ fa->store_8(0x00);
+ }
+ return true;
+}
+
+MachO::~MachO() {
+ if (fa) {
+ fa->close();
+ memdelete(fa);
+ fa = nullptr;
+ }
+}
+
+#endif // MODULE_REGEX_ENABLED
diff --git a/platform/osx/export/macho.h b/platform/osx/export/macho.h
new file mode 100644
index 0000000000..e09906898b
--- /dev/null
+++ b/platform/osx/export/macho.h
@@ -0,0 +1,217 @@
+/*************************************************************************/
+/* macho.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+// Mach-O binary object file format parser and editor.
+
+#ifndef MACHO_H
+#define MACHO_H
+
+#include "core/crypto/crypto.h"
+#include "core/crypto/crypto_core.h"
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
+#include "modules/modules_enabled.gen.h" // For regex.
+
+#ifdef MODULE_REGEX_ENABLED
+
+class MachO : public RefCounted {
+ struct MachHeader {
+ uint32_t cputype;
+ uint32_t cpusubtype;
+ uint32_t filetype;
+ uint32_t ncmds;
+ uint32_t sizeofcmds;
+ uint32_t flags;
+ };
+
+ enum LoadCommandID {
+ LC_SEGMENT = 0x00000001,
+ LC_SYMTAB = 0x00000002,
+ LC_SYMSEG = 0x00000003,
+ LC_THREAD = 0x00000004,
+ LC_UNIXTHREAD = 0x00000005,
+ LC_LOADFVMLIB = 0x00000006,
+ LC_IDFVMLIB = 0x00000007,
+ LC_IDENT = 0x00000008,
+ LC_FVMFILE = 0x00000009,
+ LC_PREPAGE = 0x0000000a,
+ LC_DYSYMTAB = 0x0000000b,
+ LC_LOAD_DYLIB = 0x0000000c,
+ LC_ID_DYLIB = 0x0000000d,
+ LC_LOAD_DYLINKER = 0x0000000e,
+ LC_ID_DYLINKER = 0x0000000f,
+ LC_PREBOUND_DYLIB = 0x00000010,
+ LC_ROUTINES = 0x00000011,
+ LC_SUB_FRAMEWORK = 0x00000012,
+ LC_SUB_UMBRELLA = 0x00000013,
+ LC_SUB_CLIENT = 0x00000014,
+ LC_SUB_LIBRARY = 0x00000015,
+ LC_TWOLEVEL_HINTS = 0x00000016,
+ LC_PREBIND_CKSUM = 0x00000017,
+ LC_LOAD_WEAK_DYLIB = 0x80000018,
+ LC_SEGMENT_64 = 0x00000019,
+ LC_ROUTINES_64 = 0x0000001a,
+ LC_UUID = 0x0000001b,
+ LC_RPATH = 0x8000001c,
+ LC_CODE_SIGNATURE = 0x0000001d,
+ LC_SEGMENT_SPLIT_INFO = 0x0000001e,
+ LC_REEXPORT_DYLIB = 0x8000001f,
+ LC_LAZY_LOAD_DYLIB = 0x00000020,
+ LC_ENCRYPTION_INFO = 0x00000021,
+ LC_DYLD_INFO = 0x00000022,
+ LC_DYLD_INFO_ONLY = 0x80000022,
+ LC_LOAD_UPWARD_DYLIB = 0x80000023,
+ LC_VERSION_MIN_MACOSX = 0x00000024,
+ LC_VERSION_MIN_IPHONEOS = 0x00000025,
+ LC_FUNCTION_STARTS = 0x00000026,
+ LC_DYLD_ENVIRONMENT = 0x00000027,
+ LC_MAIN = 0x80000028,
+ LC_DATA_IN_CODE = 0x00000029,
+ LC_SOURCE_VERSION = 0x0000002a,
+ LC_DYLIB_CODE_SIGN_DRS = 0x0000002b,
+ LC_ENCRYPTION_INFO_64 = 0x0000002c,
+ LC_LINKER_OPTION = 0x0000002d,
+ LC_LINKER_OPTIMIZATION_HINT = 0x0000002e,
+ LC_VERSION_MIN_TVOS = 0x0000002f,
+ LC_VERSION_MIN_WATCHOS = 0x00000030,
+ };
+
+ struct LoadCommandHeader {
+ uint32_t cmd;
+ uint32_t cmdsize;
+ };
+
+ struct LoadCommandSegment {
+ char segname[16];
+ uint32_t vmaddr;
+ uint32_t vmsize;
+ uint32_t fileoff;
+ uint32_t filesize;
+ uint32_t maxprot;
+ uint32_t initprot;
+ uint32_t nsects;
+ uint32_t flags;
+ };
+
+ struct LoadCommandSegment64 {
+ char segname[16];
+ uint64_t vmaddr;
+ uint64_t vmsize;
+ uint64_t fileoff;
+ uint64_t filesize;
+ uint32_t maxprot;
+ uint32_t initprot;
+ uint32_t nsects;
+ uint32_t flags;
+ };
+
+ struct Section {
+ char sectname[16];
+ char segname[16];
+ uint32_t addr;
+ uint32_t size;
+ uint32_t offset;
+ uint32_t align;
+ uint32_t reloff;
+ uint32_t nreloc;
+ uint32_t flags;
+ uint32_t reserved1;
+ uint32_t reserved2;
+ };
+
+ struct Section64 {
+ char sectname[16];
+ char segname[16];
+ uint64_t addr;
+ uint64_t size;
+ uint32_t offset;
+ uint32_t align;
+ uint32_t reloff;
+ uint32_t nreloc;
+ uint32_t flags;
+ uint32_t reserved1;
+ uint32_t reserved2;
+ uint32_t reserved3;
+ };
+
+ FileAccess *fa = nullptr;
+ bool swap = false;
+
+ uint64_t lc_limit = 0;
+
+ uint64_t exe_limit = 0;
+ uint64_t exe_base = std::numeric_limits<uint64_t>::max(); // Start of first __text section.
+ uint32_t align = 0;
+ uint32_t cputype = 0;
+ uint32_t cpusubtype = 0;
+
+ uint64_t link_edit_offset = 0; // __LINKEDIT segment offset.
+ uint64_t signature_offset = 0; // Load command offset.
+
+ uint32_t seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max);
+ bool alloc_signature(uint64_t p_size);
+
+ static inline size_t PAD(size_t s, size_t a) {
+ return (a - s % a);
+ }
+
+public:
+ static bool is_macho(const String &p_path);
+
+ bool open_file(const String &p_path);
+
+ uint64_t get_exe_base();
+ uint64_t get_exe_limit();
+ int32_t get_align();
+ uint32_t get_cputype();
+ uint32_t get_cpusubtype();
+ uint64_t get_size();
+ uint64_t get_code_limit();
+
+ uint64_t get_signature_offset();
+ bool is_signed();
+
+ PackedByteArray get_cdhash_sha1();
+ PackedByteArray get_cdhash_sha256();
+
+ PackedByteArray get_requirements();
+
+ const FileAccess *get_file() const;
+ FileAccess *get_file();
+
+ uint64_t get_signature_size();
+ bool set_signature_size(uint64_t p_size);
+
+ ~MachO();
+};
+
+#endif // MODULE_REGEX_ENABLED
+
+#endif // MACHO_H
diff --git a/platform/osx/export/plist.cpp b/platform/osx/export/plist.cpp
new file mode 100644
index 0000000000..553b864180
--- /dev/null
+++ b/platform/osx/export/plist.cpp
@@ -0,0 +1,570 @@
+/*************************************************************************/
+/* plist.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "modules/modules_enabled.gen.h" // For regex.
+
+#include "plist.h"
+
+#ifdef MODULE_REGEX_ENABLED
+
+Ref<PListNode> PListNode::new_array() {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_ARRAY;
+ return node;
+}
+
+Ref<PListNode> PListNode::new_dict() {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT;
+ return node;
+}
+
+Ref<PListNode> PListNode::new_string(const String &p_string) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_STRING;
+ node->data_string = p_string.utf8();
+ return node;
+}
+
+Ref<PListNode> PListNode::new_data(const String &p_string) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATA;
+ node->data_string = p_string.utf8();
+ return node;
+}
+
+Ref<PListNode> PListNode::new_date(const String &p_string) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATE;
+ node->data_string = p_string.utf8();
+ return node;
+}
+
+Ref<PListNode> PListNode::new_bool(bool p_bool) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_BOOLEAN;
+ node->data_bool = p_bool;
+ return node;
+}
+
+Ref<PListNode> PListNode::new_int(int32_t p_int) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_INTEGER;
+ node->data_int = p_int;
+ return node;
+}
+
+Ref<PListNode> PListNode::new_real(float p_real) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_REAL;
+ node->data_real = p_real;
+ return node;
+}
+
+bool PListNode::push_subnode(const Ref<PListNode> &p_node, const String &p_key) {
+ ERR_FAIL_COND_V(p_node.is_null(), false);
+ if (data_type == PList::PLNodeType::PL_NODE_TYPE_DICT) {
+ ERR_FAIL_COND_V(p_key.is_empty(), false);
+ ERR_FAIL_COND_V(data_dict.has(p_key), false);
+ data_dict[p_key] = p_node;
+ return true;
+ } else if (data_type == PList::PLNodeType::PL_NODE_TYPE_ARRAY) {
+ data_array.push_back(p_node);
+ return true;
+ } else {
+ ERR_FAIL_V_MSG(false, "PList: Invalid parent node type, should be DICT or ARRAY.");
+ }
+}
+
+size_t PListNode::get_asn1_size(uint8_t p_len_octets) const {
+ // Get size of all data, excluding type and size information.
+ switch (data_type) {
+ case PList::PLNodeType::PL_NODE_TYPE_NIL: {
+ return 0;
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DATA:
+ case PList::PLNodeType::PL_NODE_TYPE_DATE: {
+ ERR_FAIL_V_MSG(0, "PList: DATE and DATA nodes are not supported by ASN.1 serialization.");
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_STRING: {
+ return data_string.length();
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
+ return 1;
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_INTEGER:
+ case PList::PLNodeType::PL_NODE_TYPE_REAL: {
+ return 4;
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
+ size_t size = 0;
+ for (int i = 0; i < data_array.size(); i++) {
+ size += 1 + _asn1_size_len(p_len_octets) + data_array[i]->get_asn1_size(p_len_octets);
+ }
+ return size;
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DICT: {
+ size_t size = 0;
+ for (const Map<String, Ref<PListNode>>::Element *it = data_dict.front(); it; it = it->next()) {
+ size += 1 + _asn1_size_len(p_len_octets); // Sequence.
+ size += 1 + _asn1_size_len(p_len_octets) + it->key().utf8().length(); //Key.
+ size += 1 + _asn1_size_len(p_len_octets) + it->value()->get_asn1_size(p_len_octets); // Value.
+ }
+ return size;
+ } break;
+ default: {
+ return 0;
+ } break;
+ }
+}
+
+int PListNode::_asn1_size_len(uint8_t p_len_octets) {
+ if (p_len_octets > 1) {
+ return p_len_octets + 1;
+ } else {
+ return 1;
+ }
+}
+
+void PListNode::store_asn1_size(PackedByteArray &p_stream, uint8_t p_len_octets) const {
+ uint32_t size = get_asn1_size(p_len_octets);
+ if (p_len_octets > 1) {
+ p_stream.push_back(0x80 + p_len_octets);
+ }
+ for (int i = p_len_octets - 1; i >= 0; i--) {
+ uint8_t x = (size >> i * 8) & 0xFF;
+ p_stream.push_back(x);
+ }
+}
+
+bool PListNode::store_asn1(PackedByteArray &p_stream, uint8_t p_len_octets) const {
+ // Convert to binary ASN1 stream.
+ bool valid = true;
+ switch (data_type) {
+ case PList::PLNodeType::PL_NODE_TYPE_NIL: {
+ // Nothing to store.
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DATE:
+ case PList::PLNodeType::PL_NODE_TYPE_DATA: {
+ ERR_FAIL_V_MSG(false, "PList: DATE and DATA nodes are not supported by ASN.1 serialization.");
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_STRING: {
+ p_stream.push_back(0x0C);
+ store_asn1_size(p_stream, p_len_octets);
+ for (int i = 0; i < data_string.size(); i++) {
+ p_stream.push_back(data_string[i]);
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
+ p_stream.push_back(0x01);
+ store_asn1_size(p_stream, p_len_octets);
+ if (data_bool) {
+ p_stream.push_back(0x01);
+ } else {
+ p_stream.push_back(0x00);
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_INTEGER: {
+ p_stream.push_back(0x02);
+ store_asn1_size(p_stream, p_len_octets);
+ for (int i = 4; i >= 0; i--) {
+ uint8_t x = (data_int >> i * 8) & 0xFF;
+ p_stream.push_back(x);
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_REAL: {
+ p_stream.push_back(0x03);
+ store_asn1_size(p_stream, p_len_octets);
+ for (int i = 4; i >= 0; i--) {
+ uint8_t x = (data_int >> i * 8) & 0xFF;
+ p_stream.push_back(x);
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
+ p_stream.push_back(0x30); // Sequence.
+ store_asn1_size(p_stream, p_len_octets);
+ for (int i = 0; i < data_array.size(); i++) {
+ valid = valid && data_array[i]->store_asn1(p_stream, p_len_octets);
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DICT: {
+ p_stream.push_back(0x31); // Set.
+ store_asn1_size(p_stream, p_len_octets);
+ for (const Map<String, Ref<PListNode>>::Element *it = data_dict.front(); it; it = it->next()) {
+ CharString cs = it->key().utf8();
+ uint32_t size = cs.length();
+
+ // Sequence.
+ p_stream.push_back(0x30);
+ uint32_t seq_size = 2 * (1 + _asn1_size_len(p_len_octets)) + size + it->value()->get_asn1_size(p_len_octets);
+ if (p_len_octets > 1) {
+ p_stream.push_back(0x80 + p_len_octets);
+ }
+ for (int i = p_len_octets - 1; i >= 0; i--) {
+ uint8_t x = (seq_size >> i * 8) & 0xFF;
+ p_stream.push_back(x);
+ }
+ // Key.
+ p_stream.push_back(0x0C);
+ if (p_len_octets > 1) {
+ p_stream.push_back(0x80 + p_len_octets);
+ }
+ for (int i = p_len_octets - 1; i >= 0; i--) {
+ uint8_t x = (size >> i * 8) & 0xFF;
+ p_stream.push_back(x);
+ }
+ for (uint32_t i = 0; i < size; i++) {
+ p_stream.push_back(cs[i]);
+ }
+ // Value.
+ valid = valid && it->value()->store_asn1(p_stream, p_len_octets);
+ }
+ } break;
+ }
+ return valid;
+}
+
+void PListNode::store_text(String &p_stream, uint8_t p_indent) const {
+ // Convert to text XML stream.
+ switch (data_type) {
+ case PList::PLNodeType::PL_NODE_TYPE_NIL: {
+ // Nothing to store.
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DATA: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<data>\n";
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += data_string + "\n";
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "</data>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DATE: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<date>";
+ p_stream += data_string;
+ p_stream += "</date>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_STRING: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<string>";
+ p_stream += String::utf8(data_string);
+ p_stream += "</string>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
+ p_stream += String("\t").repeat(p_indent);
+ if (data_bool) {
+ p_stream += "<true/>\n";
+ } else {
+ p_stream += "<false/>\n";
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_INTEGER: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<integer>";
+ p_stream += itos(data_int);
+ p_stream += "</integer>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_REAL: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<real>";
+ p_stream += rtos(data_real);
+ p_stream += "</real>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<array>\n";
+ for (int i = 0; i < data_array.size(); i++) {
+ data_array[i]->store_text(p_stream, p_indent + 1);
+ }
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "</array>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DICT: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<dict>\n";
+ for (const Map<String, Ref<PListNode>>::Element *it = data_dict.front(); it; it = it->next()) {
+ p_stream += String("\t").repeat(p_indent + 1);
+ p_stream += "<key>";
+ p_stream += it->key();
+ p_stream += "</key>\n";
+ it->value()->store_text(p_stream, p_indent + 1);
+ }
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "</dict>\n";
+ } break;
+ }
+}
+
+/*************************************************************************/
+
+PList::PList() {
+ root = PListNode::new_dict();
+}
+
+PList::PList(const String &p_string) {
+ load_string(p_string);
+}
+
+bool PList::load_file(const String &p_filename) {
+ root = Ref<PListNode>();
+
+ FileAccessRef fb = FileAccess::open(p_filename, FileAccess::READ);
+ if (!fb) {
+ return false;
+ }
+
+ unsigned char magic[8];
+ fb->get_buffer(magic, 8);
+
+ if (String((const char *)magic, 8) == "bplist00") {
+ ERR_FAIL_V_MSG(false, "PList: Binary property lists are not supported.");
+ } else {
+ // Load text plist.
+ Error err;
+ Vector<uint8_t> array = FileAccess::get_file_as_array(p_filename, &err);
+ ERR_FAIL_COND_V(err != OK, false);
+
+ String ret;
+ ret.parse_utf8((const char *)array.ptr(), array.size());
+ return load_string(ret);
+ }
+}
+
+bool PList::load_string(const String &p_string) {
+ root = Ref<PListNode>();
+
+ int pos = 0;
+ bool in_plist = false;
+ bool done_plist = false;
+ List<Ref<PListNode>> stack;
+ String key;
+ while (pos >= 0) {
+ int open_token_s = p_string.find("<", pos);
+ if (open_token_s == -1) {
+ ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. No tags found.");
+ }
+ int open_token_e = p_string.find(">", open_token_s);
+ pos = open_token_e;
+
+ String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1);
+ if (token.is_empty()) {
+ ERR_FAIL_V_MSG(false, "PList: Invalid token name.");
+ }
+ String value;
+ if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... >
+ int end_token_e = p_string.find(">", open_token_s);
+ pos = end_token_e;
+ continue;
+ }
+
+ if (token.find("plist", 0) == 0) {
+ in_plist = true;
+ continue;
+ }
+
+ if (token == "/plist") {
+ in_plist = false;
+ done_plist = true;
+ break;
+ }
+
+ if (!in_plist) {
+ ERR_FAIL_V_MSG(false, "PList: Node outside of <plist> tag.");
+ }
+
+ if (token == "dict") {
+ if (!stack.is_empty()) {
+ // Add subnode end enter it.
+ Ref<PListNode> dict = PListNode::new_dict();
+ dict->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT;
+ if (!stack.back()->get()->push_subnode(dict, key)) {
+ ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type.");
+ }
+ stack.push_back(dict);
+ } else {
+ // Add root node.
+ if (!root.is_null()) {
+ ERR_FAIL_V_MSG(false, "PList: Root node already set.");
+ }
+ Ref<PListNode> dict = PListNode::new_dict();
+ stack.push_back(dict);
+ root = dict;
+ }
+ continue;
+ }
+
+ if (token == "/dict") {
+ // Exit current dict.
+ if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_DICT) {
+ ERR_FAIL_V_MSG(false, "PList: Mismatched </dict> tag.");
+ }
+ stack.pop_back();
+ continue;
+ }
+
+ if (token == "array") {
+ if (!stack.is_empty()) {
+ // Add subnode end enter it.
+ Ref<PListNode> arr = PListNode::new_array();
+ if (!stack.back()->get()->push_subnode(arr, key)) {
+ ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type.");
+ }
+ stack.push_back(arr);
+ } else {
+ // Add root node.
+ if (!root.is_null()) {
+ ERR_FAIL_V_MSG(false, "PList: Root node already set.");
+ }
+ Ref<PListNode> arr = PListNode::new_array();
+ stack.push_back(arr);
+ root = arr;
+ }
+ continue;
+ }
+
+ if (token == "/array") {
+ // Exit current array.
+ if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_ARRAY) {
+ ERR_FAIL_V_MSG(false, "PList: Mismatched </array> tag.");
+ }
+ stack.pop_back();
+ continue;
+ }
+
+ if (token[token.length() - 1] == '/') {
+ token = token.substr(0, token.length() - 1);
+ } else {
+ int end_token_s = p_string.find("</", pos);
+ if (end_token_s == -1) {
+ ERR_FAIL_V_MSG(false, vformat("PList: Mismatched <%s> tag.", token));
+ }
+ int end_token_e = p_string.find(">", end_token_s);
+ pos = end_token_e;
+ String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2);
+ if (end_token != token) {
+ ERR_FAIL_V_MSG(false, vformat("PList: Mismatched <%s> and <%s> token pair.", token, end_token));
+ }
+ value = p_string.substr(open_token_e + 1, end_token_s - open_token_e - 1);
+ }
+ if (token == "key") {
+ key = value;
+ } else {
+ Ref<PListNode> var = nullptr;
+ if (token == "true") {
+ var = PListNode::new_bool(true);
+ } else if (token == "false") {
+ var = PListNode::new_bool(false);
+ } else if (token == "integer") {
+ var = PListNode::new_int(value.to_int());
+ } else if (token == "real") {
+ var = PListNode::new_real(value.to_float());
+ } else if (token == "string") {
+ var = PListNode::new_string(value);
+ } else if (token == "data") {
+ var = PListNode::new_data(value);
+ } else if (token == "date") {
+ var = PListNode::new_date(value);
+ } else {
+ ERR_FAIL_V_MSG(false, "PList: Invalid value type.");
+ }
+ if (stack.is_empty() || !stack.back()->get()->push_subnode(var, key)) {
+ ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type.");
+ }
+ }
+ }
+ if (!stack.is_empty() || !done_plist) {
+ ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. Root node is not closed.");
+ }
+ return true;
+}
+
+PackedByteArray PList::save_asn1() const {
+ if (root == nullptr) {
+ ERR_FAIL_V_MSG(PackedByteArray(), "PList: Invalid PList, no root node.");
+ }
+ size_t size = root->get_asn1_size(1);
+ uint8_t len_octets = 0;
+ if (size < 0x80) {
+ len_octets = 1;
+ } else {
+ size = root->get_asn1_size(2);
+ if (size < 0xFFFF) {
+ len_octets = 2;
+ } else {
+ size = root->get_asn1_size(3);
+ if (size < 0xFFFFFF) {
+ len_octets = 3;
+ } else {
+ size = root->get_asn1_size(4);
+ if (size < 0xFFFFFFFF) {
+ len_octets = 4;
+ } else {
+ ERR_FAIL_V_MSG(PackedByteArray(), "PList: Data is too big for ASN.1 serializer, should be < 4 GiB.");
+ }
+ }
+ }
+ }
+
+ PackedByteArray ret;
+ if (!root->store_asn1(ret, len_octets)) {
+ ERR_FAIL_V_MSG(PackedByteArray(), "PList: ASN.1 serializer error.");
+ }
+ return ret;
+}
+
+String PList::save_text() const {
+ if (root == nullptr) {
+ ERR_FAIL_V_MSG(String(), "PList: Invalid PList, no root node.");
+ }
+
+ String ret;
+ ret += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ ret += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
+ ret += "<plist version=\"1.0\">\n";
+
+ root->store_text(ret, 0);
+
+ ret += "</plist>\n\n";
+ return ret;
+}
+
+Ref<PListNode> PList::get_root() {
+ return root;
+}
+
+#endif // MODULE_REGEX_ENABLED
diff --git a/modules/pvr/image_compress_pvrtc.cpp b/platform/osx/export/plist.h
index b996d910d5..fb4aaaa935 100644
--- a/modules/pvr/image_compress_pvrtc.cpp
+++ b/platform/osx/export/plist.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* image_compress_pvrtc.cpp */
+/* plist.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,61 +28,89 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "image_compress_pvrtc.h"
-
-#include "core/io/image.h"
-#include "core/object/ref_counted.h"
-
-#include <PvrTcEncoder.h>
-#include <RgbaBitmap.h>
-
-static void _compress_pvrtc1_4bpp(Image *p_img) {
- Ref<Image> img = p_img->duplicate();
-
- bool make_mipmaps = false;
- if (!img->is_size_po2() || img->get_width() != img->get_height()) {
- make_mipmaps = img->has_mipmaps();
- img->resize_to_po2(true);
- // Resizing can fail for some formats
- if (!img->is_size_po2() || img->get_width() != img->get_height()) {
- ERR_FAIL_MSG("Failed to resize the image for compression.");
- }
- }
- img->convert(Image::FORMAT_RGBA8);
- if (!img->has_mipmaps() && make_mipmaps) {
- img->generate_mipmaps();
- }
-
- bool use_alpha = img->detect_alpha();
-
- Ref<Image> new_img;
- new_img.instantiate();
- new_img->create(img->get_width(), img->get_height(), img->has_mipmaps(), use_alpha ? Image::FORMAT_PVRTC1_4A : Image::FORMAT_PVRTC1_4);
-
- Vector<uint8_t> data = new_img->get_data();
- {
- uint8_t *wr = data.ptrw();
- const uint8_t *r = img->get_data().ptr();
-
- for (int i = 0; i <= new_img->get_mipmap_count(); i++) {
- int ofs, size, w, h;
- img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
- Javelin::RgbaBitmap bm(w, h);
- void *dst = (void *)bm.GetData();
- memcpy(dst, &r[ofs], size);
- Javelin::ColorRgba<unsigned char> *dp = bm.GetData();
- for (int j = 0; j < size / 4; j++) {
- // Red and blue colors are swapped.
- SWAP(dp[j].r, dp[j].b);
- }
- new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
- Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm);
- }
- }
-
- p_img->create(new_img->get_width(), new_img->get_height(), new_img->has_mipmaps(), new_img->get_format(), data);
-}
-
-void _register_pvrtc_compress_func() {
- Image::_image_compress_pvrtc1_4bpp_func = _compress_pvrtc1_4bpp;
-}
+// Property list file format (application/x-plist) parser, property list ASN-1 serialization.
+
+#ifndef PLIST_H
+#define PLIST_H
+
+#include "core/crypto/crypto_core.h"
+#include "core/io/file_access.h"
+#include "modules/modules_enabled.gen.h" // For regex.
+
+#ifdef MODULE_REGEX_ENABLED
+
+class PListNode;
+
+class PList : public RefCounted {
+ friend class PListNode;
+
+public:
+ enum PLNodeType {
+ PL_NODE_TYPE_NIL,
+ PL_NODE_TYPE_STRING,
+ PL_NODE_TYPE_ARRAY,
+ PL_NODE_TYPE_DICT,
+ PL_NODE_TYPE_BOOLEAN,
+ PL_NODE_TYPE_INTEGER,
+ PL_NODE_TYPE_REAL,
+ PL_NODE_TYPE_DATA,
+ PL_NODE_TYPE_DATE,
+ };
+
+private:
+ Ref<PListNode> root;
+
+public:
+ PList();
+ PList(const String &p_string);
+
+ bool load_file(const String &p_filename);
+ bool load_string(const String &p_string);
+
+ PackedByteArray save_asn1() const;
+ String save_text() const;
+
+ Ref<PListNode> get_root();
+};
+
+/*************************************************************************/
+
+class PListNode : public RefCounted {
+ static int _asn1_size_len(uint8_t p_len_octets);
+
+public:
+ PList::PLNodeType data_type = PList::PLNodeType::PL_NODE_TYPE_NIL;
+
+ CharString data_string;
+ Vector<Ref<PListNode>> data_array;
+ Map<String, Ref<PListNode>> data_dict;
+ union {
+ int32_t data_int;
+ bool data_bool;
+ float data_real;
+ };
+
+ static Ref<PListNode> new_array();
+ static Ref<PListNode> new_dict();
+ static Ref<PListNode> new_string(const String &p_string);
+ static Ref<PListNode> new_data(const String &p_string);
+ static Ref<PListNode> new_date(const String &p_string);
+ static Ref<PListNode> new_bool(bool p_bool);
+ static Ref<PListNode> new_int(int32_t p_int);
+ static Ref<PListNode> new_real(float p_real);
+
+ bool push_subnode(const Ref<PListNode> &p_node, const String &p_key = "");
+
+ size_t get_asn1_size(uint8_t p_len_octets) const;
+
+ void store_asn1_size(PackedByteArray &p_stream, uint8_t p_len_octets) const;
+ bool store_asn1(PackedByteArray &p_stream, uint8_t p_len_octets) const;
+ void store_text(String &p_stream, uint8_t p_indent) const;
+
+ PListNode() {}
+ ~PListNode() {}
+};
+
+#endif // MODULE_REGEX_ENABLED
+
+#endif // PLIST_H
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index 2152b34aff..c2356f12cd 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -449,20 +449,9 @@ void JoypadOSX::poll_joypads() const {
}
}
-static const Input::JoyAxisValue axis_correct(int p_value, int p_min, int p_max) {
- Input::JoyAxisValue jx;
- if (p_min < 0) {
- jx.min = -1;
- if (p_value < 0) {
- jx.value = (float)-p_value / p_min;
- } else
- jx.value = (float)p_value / p_max;
- }
- if (p_min == 0) {
- jx.min = 0;
- jx.value = 0.0f + (float)p_value / p_max;
- }
- return jx;
+static float axis_correct(int p_value, int p_min, int p_max) {
+ // Convert to a value between -1.0f and 1.0f.
+ return 2.0f * (p_value - p_min) / (p_max - p_min) - 1.0f;
}
void JoypadOSX::process_joypads() {
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 555ce4c75f..60dec1fe3f 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -94,7 +94,7 @@ public:
String get_locale() const override;
virtual String get_executable_path() const override;
- virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
virtual String get_unique_id() const override; //++
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 821036de9c..32d0e6dd94 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -497,13 +497,13 @@ Error OS_OSX::create_instance(const List<String> &p_arguments, ProcessID *r_chil
if (nsappname != nil) {
String path;
path.parse_utf8([[[NSBundle mainBundle] bundlePath] UTF8String]);
- return create_process(path, p_arguments, r_child_id);
+ return create_process(path, p_arguments, r_child_id, false);
} else {
- return create_process(get_executable_path(), p_arguments, r_child_id);
+ return create_process(get_executable_path(), p_arguments, r_child_id, false);
}
}
-Error OS_OSX::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
+Error OS_OSX::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
if (@available(macOS 10.15, *)) {
// Use NSWorkspace if path is an .app bundle.
NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
@@ -542,10 +542,10 @@ Error OS_OSX::create_process(const String &p_path, const List<String> &p_argumen
return err;
} else {
- return OS_Unix::create_process(p_path, p_arguments, r_child_id);
+ return OS_Unix::create_process(p_path, p_arguments, r_child_id, p_open_console);
}
} else {
- return OS_Unix::create_process(p_path, p_arguments, r_child_id);
+ return OS_Unix::create_process(p_path, p_arguments, r_child_id, p_open_console);
}
}
diff --git a/platform/uwp/joypad_uwp.cpp b/platform/uwp/joypad_uwp.cpp
index ef44f0b14d..e48016919b 100644
--- a/platform/uwp/joypad_uwp.cpp
+++ b/platform/uwp/joypad_uwp.cpp
@@ -134,13 +134,12 @@ void JoypadUWP::OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Inp
input->joy_connection_changed(idx, false, "Xbox Controller");
}
-InputDefault::JoyAxisValue JoypadUWP::axis_correct(double p_val, bool p_negate, bool p_trigger) const {
- InputDefault::JoyAxisValue jx;
-
- jx.min = p_trigger ? 0 : -1;
- jx.value = (float)(p_negate ? -p_val : p_val);
-
- return jx;
+float JoypadUWP::axis_correct(double p_val, bool p_negate, bool p_trigger) const {
+ if (p_trigger) {
+ // Convert to a value between -1.0f and 1.0f.
+ return 2.0f * p_val - 1.0f;
+ }
+ return (float)(p_negate ? -p_val : p_val);
}
void JoypadUWP::joypad_vibration_start(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h
index 1d68996358..29f5109056 100644
--- a/platform/uwp/joypad_uwp.h
+++ b/platform/uwp/joypad_uwp.h
@@ -73,7 +73,7 @@ private:
void OnGamepadAdded(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value);
void OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value);
- InputDefault::JoyAxisValue axis_correct(double p_val, bool p_negate = false, bool p_trigger = false) const;
+ float axis_correct(double p_val, bool p_negate = false, bool p_trigger = false) const;
void joypad_vibration_start(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop(int p_device, uint64_t p_timestamp);
};
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index ca486633bf..b6dde9c63f 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -626,11 +626,11 @@ void OS_UWP::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
// TODO
}
-Error OS_UWP::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+Error OS_UWP::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
return FAILED;
};
-Error OS_UWP::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
+Error OS_UWP::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
return FAILED;
};
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 5784c11d53..573d86af7c 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -195,8 +195,8 @@ public:
virtual void delay_usec(uint32_t p_usec) const;
virtual uint64_t get_ticks_usec() const;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr);
- virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr);
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false);
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false);
virtual Error kill(const ProcessID &p_pid);
virtual bool has_environment(const String &p_var) const;
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index e9ecc99ef5..249a0d2e79 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -65,7 +65,7 @@ def get_opts():
# Vista support dropped after EOL due to GH-10243
("target_win_version", "Targeted Windows version, >= 0x0601 (Windows 7)", "0x0601"),
BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
- EnumVariable("windows_subsystem", "Windows subsystem", "default", ("default", "console", "gui")),
+ EnumVariable("windows_subsystem", "Windows subsystem", "gui", ("gui", "console")),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
("msvc_version", "MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.", None),
BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
@@ -178,15 +178,6 @@ def configure_msvc(env, manual_msvc_config):
# Build type
- if env["tests"]:
- env["windows_subsystem"] = "console"
- elif env["windows_subsystem"] == "default":
- # Default means we use console for debug, gui for release.
- if "debug" in env["target"]:
- env["windows_subsystem"] = "console"
- else:
- env["windows_subsystem"] = "gui"
-
if env["target"] == "release":
if env["optimize"] == "speed": # optimize for speed (default)
env.Append(CCFLAGS=["/O2"])
@@ -326,15 +317,6 @@ def configure_mingw(env):
## Build type
- if env["tests"]:
- env["windows_subsystem"] = "console"
- elif env["windows_subsystem"] == "default":
- # Default means we use console for debug, gui for release.
- if "debug" in env["target"]:
- env["windows_subsystem"] = "console"
- else:
- env["windows_subsystem"] = "gui"
-
if env["target"] == "release":
env.Append(CCFLAGS=["-msse2"])
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 091bed36ea..d288c27016 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -77,7 +77,6 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_CLIPBOARD:
case FEATURE_CURSOR_SHAPE:
case FEATURE_CUSTOM_CURSOR_SHAPE:
- case FEATURE_CONSOLE_WINDOW:
case FEATURE_IME:
case FEATURE_WINDOW_TRANSPARENCY:
case FEATURE_HIDPI:
@@ -98,7 +97,10 @@ String DisplayServerWindows::get_name() const {
void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) {
// Mouse is grabbed (captured or confined).
- WindowData &wd = windows[MAIN_WINDOW_ID];
+
+ WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+
+ WindowData &wd = windows[window_id];
RECT clipRect;
GetClientRect(wd.hWnd, &clipRect);
@@ -571,6 +573,24 @@ void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_wind
#endif
}
+int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
+ ERR_FAIL_COND_V(!windows.has(p_window), 0);
+ switch (p_handle_type) {
+ case DISPLAY_HANDLE: {
+ return 0; // Not supported.
+ }
+ case WINDOW_HANDLE: {
+ return (int64_t)windows[p_window].hWnd;
+ }
+ case WINDOW_VIEW: {
+ return 0; // Not supported.
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -675,8 +695,29 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi
ERR_FAIL_COND(!windows.has(p_window));
ERR_FAIL_INDEX(p_screen, get_screen_count());
- Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
- window_set_position(ofs + screen_get_position(p_screen), p_window);
+ const WindowData &wd = windows[p_window];
+ if (wd.fullscreen) {
+ int cs = window_get_current_screen(p_window);
+ if (cs == p_screen) {
+ return;
+ }
+ Point2 pos = screen_get_position(p_screen);
+ Size2 size = screen_get_size(p_screen);
+
+ MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
+ } else {
+ Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
+ window_set_position(ofs + screen_get_position(p_screen), p_window);
+ }
+
+ // Don't let the mouse leave the window when resizing to a smaller resolution.
+ if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
+ RECT crect;
+ GetClientRect(wd.hWnd, &crect);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.right);
+ ClipCursor(&crect);
+ }
}
Point2i DisplayServerWindows::window_get_position(WindowID p_window) const {
@@ -1039,6 +1080,15 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, 0, 0);
}
}
+
+ // Don't let the mouse leave the window when resizing to a smaller resolution.
+ if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
+ RECT crect;
+ GetClientRect(wd.hWnd, &crect);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.right);
+ ClipCursor(&crect);
+ }
}
DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const {
@@ -1207,23 +1257,6 @@ void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowI
ImmReleaseContext(wd.hWnd, himc);
}
-void DisplayServerWindows::console_set_visible(bool p_enabled) {
- _THREAD_SAFE_METHOD_
-
- if (console_visible == p_enabled) {
- return;
- }
- if (!((OS_Windows *)OS::get_singleton())->_is_win11_terminal()) {
- // GetConsoleWindow is not supported by the Windows Terminal.
- ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE);
- console_visible = p_enabled;
- }
-}
-
-bool DisplayServerWindows::is_console_visible() const {
- return console_visible;
-}
-
void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {
_THREAD_SAFE_METHOD_
@@ -2078,7 +2111,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_position(c);
mm->set_global_position(c);
- Input::get_singleton()->set_mouse_position(c);
mm->set_velocity(Vector2(0, 0));
if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
@@ -2183,7 +2215,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
SetCursorPos(pos.x, pos.y);
}
- Input::get_singleton()->set_mouse_position(mm->get_position());
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
if (old_invalid) {
@@ -2325,7 +2356,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
SetCursorPos(pos.x, pos.y);
}
- Input::get_singleton()->set_mouse_position(mm->get_position());
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
if (old_invalid) {
@@ -2426,7 +2456,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
SetCursorPos(pos.x, pos.y);
}
- Input::get_singleton()->set_mouse_position(mm->get_position());
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
if (old_invalid) {
@@ -2819,6 +2848,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_DEVICECHANGE: {
joypad->probe_joypads();
} break;
+ case WM_DESTROY: {
+ Input::get_singleton()->flush_buffered_events();
+ } break;
case WM_SETCURSOR: {
if (LOWORD(lParam) == HTCLIENT) {
if (windows[window_id].window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) {
@@ -2886,6 +2918,9 @@ void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM
alt_mem = false;
control_mem = false;
shift_mem = false;
+
+ // Restore mouse mode.
+ _set_mouse_mode_impl(mouse_mode);
} else { // WM_INACTIVE.
Input::get_singleton()->release_pressed_events();
_send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT);
@@ -3240,7 +3275,6 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
shift_mem = false;
control_mem = false;
meta_mem = false;
- console_visible = IsWindowVisible(GetConsoleWindow());
hInstance = ((OS_Windows *)OS::get_singleton())->get_hinstance();
pressrc = 0;
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 409335b41c..803c2d4836 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -414,7 +414,6 @@ class DisplayServerWindows : public DisplayServer {
bool use_raw_input = false;
bool drop_events = false;
bool in_dispatch_input_event = false;
- bool console_visible = false;
WNDCLASSEXW wc;
@@ -473,11 +472,13 @@ public:
virtual void show_window(WindowID p_window) override;
virtual void delete_sub_window(WindowID p_window) override;
+ virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
+
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void gl_window_make_current(DisplayServer::WindowID p_window_id);
+ virtual void gl_window_make_current(DisplayServer::WindowID p_window_id) override;
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
@@ -529,9 +530,6 @@ public:
virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
- virtual void console_set_visible(bool p_enabled) override;
- virtual bool is_console_visible() const override;
-
virtual void cursor_set_shape(CursorShape p_shape) override;
virtual CursorShape cursor_get_shape() const override;
virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 02b2d026b5..d30d0afc5c 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -54,13 +54,19 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
return err;
}
+bool EditorExportPlatformWindows::get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ // This option is not supported by "osslsigncode", used on non-Windows host.
+ if (!OS::get_singleton()->has_feature("windows") && p_option == "codesign/identity_type") {
+ return false;
+ }
+ return true;
+}
+
void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {
EditorExportPlatformPC::get_export_options(r_options);
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));
-#ifdef WINDOWS_ENABLED
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0));
-#endif
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
@@ -70,8 +76,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), ""));
@@ -83,6 +89,7 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset>
String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
if (rcedit_path.is_empty()) {
+ WARN_PRINT("The rcedit tool is not configured in the Editor Settings (Export > Windows > Rcedit). No custom icon or app information data will be embedded in the exported executable.");
return;
}
@@ -321,3 +328,46 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
return OK;
}
+
+bool EditorExportPlatformWindows::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+ String err = "";
+ bool valid = EditorExportPlatformPC::can_export(p_preset, err, r_missing_templates);
+
+ String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
+ if (rcedit_path.is_empty()) {
+ err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > Rcedit) to change the icon or app information data.") + "\n";
+ }
+
+ String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon"));
+ if (!icon_path.is_empty() && !FileAccess::exists(icon_path)) {
+ err += TTR("Invalid icon path:") + " " + icon_path + "\n";
+ }
+
+ // Only non-negative integers can exist in the version string.
+
+ String file_version = p_preset->get("application/file_version");
+ if (!file_version.is_empty()) {
+ PackedStringArray version_array = file_version.split(".", false);
+ if (version_array.size() != 4 || !version_array[0].is_valid_int() ||
+ !version_array[1].is_valid_int() || !version_array[2].is_valid_int() ||
+ !version_array[3].is_valid_int() || file_version.find("-") > -1) {
+ err += TTR("Invalid file version:") + " " + file_version + "\n";
+ }
+ }
+
+ String product_version = p_preset->get("application/product_version");
+ if (!product_version.is_empty()) {
+ PackedStringArray version_array = product_version.split(".", false);
+ if (version_array.size() != 4 || !version_array[0].is_valid_int() ||
+ !version_array[1].is_valid_int() || !version_array[2].is_valid_int() ||
+ !version_array[3].is_valid_int() || product_version.find("-") > -1) {
+ err += TTR("Invalid product version:") + " " + product_version + "\n";
+ }
+ }
+
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+
+ return valid;
+}
diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h
index 4ec9342cdf..89e5b1b635 100644
--- a/platform/windows/export/export_plugin.h
+++ b/platform/windows/export/export_plugin.h
@@ -43,9 +43,11 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC {
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
public:
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
- virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
- virtual void get_export_options(List<ExportOption> *r_options);
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+ virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) override;
+ virtual void get_export_options(List<ExportOption> *r_options) override;
+ virtual bool get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
};
#endif
diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp
index 7819ab9a32..618d5670d2 100644
--- a/platform/windows/godot_windows.cpp
+++ b/platform/windows/godot_windows.cpp
@@ -39,7 +39,7 @@
#ifndef TOOLS_ENABLED
#if defined _MSC_VER
#pragma section("pck", read)
-__declspec(allocate("pck")) static char dummy[8] = { 0 };
+__declspec(allocate("pck")) static const char dummy[8] = { 0 };
#elif defined __GNUC__
static const char dummy[8] __attribute__((section("pck"), used)) = { 0 };
#endif
@@ -140,6 +140,11 @@ int widechar_main(int argc, wchar_t **argv) {
setlocale(LC_CTYPE, "");
+#ifndef TOOLS_ENABLED
+ // Workaround to prevent LTCG (MSVC LTO) from removing "pck" section
+ const char *dummy_guard = dummy;
+#endif
+
char **argv_utf8 = new char *[argc];
for (int i = 0; i < argc; ++i) {
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 8b6081d606..b0dd86a4b7 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -448,33 +448,27 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
input->joy_hat(p_device, dpad_val);
};
-Input::JoyAxisValue JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
- Input::JoyAxisValue jx;
+float JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
if (Math::abs(p_val) < MIN_JOY_AXIS) {
- jx.min = p_trigger ? 0 : -1;
- jx.value = 0.0f;
- return jx;
+ return p_trigger ? -1.0f : 0.0f;
}
- if (p_xinput) {
- if (p_trigger) {
- jx.min = 0;
- jx.value = (float)p_val / MAX_TRIGGER;
- return jx;
- }
- jx.min = -1;
- if (p_val < 0) {
- jx.value = (float)p_val / MAX_JOY_AXIS;
- } else {
- jx.value = (float)p_val / (MAX_JOY_AXIS - 1);
- }
- if (p_negate) {
- jx.value = -jx.value;
- }
- return jx;
+ if (!p_xinput) {
+ return (float)p_val / MAX_JOY_AXIS;
+ }
+ if (p_trigger) {
+ // Convert to a value between -1.0f and 1.0f.
+ return 2.0f * p_val / MAX_TRIGGER - 1.0f;
+ }
+ float value;
+ if (p_val < 0) {
+ value = (float)p_val / MAX_JOY_AXIS;
+ } else {
+ value = (float)p_val / (MAX_JOY_AXIS - 1);
+ }
+ if (p_negate) {
+ value = -value;
}
- jx.min = -1;
- jx.value = (float)p_val / MAX_JOY_AXIS;
- return jx;
+ return value;
}
void JoypadWindows::joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h
index 4faefe932f..0e3d03fa52 100644
--- a/platform/windows/joypad_windows.h
+++ b/platform/windows/joypad_windows.h
@@ -132,7 +132,7 @@ private:
void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp);
- Input::JoyAxisValue axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
+ float axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
XInputGetState_t xinput_get_state;
XInputSetState_t xinput_set_state;
};
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 203e078cb4..d844531071 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -52,8 +52,6 @@
#include <regstr.h>
#include <shlobj.h>
-static const WORD MAX_CONSOLE_LINES = 1500;
-
extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
@@ -85,66 +83,26 @@ static String format_error_message(DWORD id) {
return msg;
}
-void RedirectIOToConsole() {
- int hConHandle;
-
- intptr_t lStdHandle;
-
- CONSOLE_SCREEN_BUFFER_INFO coninfo;
-
- FILE *fp;
-
- // allocate a console for this app
-
- AllocConsole();
-
- // set the screen buffer to be big enough to let us scroll text
-
- GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
-
- coninfo.dwSize.Y = MAX_CONSOLE_LINES;
-
- SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
-
- // redirect unbuffered STDOUT to the console
-
- lStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE);
-
- hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
-
- fp = _fdopen(hConHandle, "w");
-
- *stdout = *fp;
-
- setvbuf(stdout, nullptr, _IONBF, 0);
-
- // redirect unbuffered STDIN to the console
-
- lStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE);
-
- hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
-
- fp = _fdopen(hConHandle, "r");
-
- *stdin = *fp;
-
- setvbuf(stdin, nullptr, _IONBF, 0);
-
- // redirect unbuffered STDERR to the console
-
- lStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE);
-
- hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
-
- fp = _fdopen(hConHandle, "w");
-
- *stderr = *fp;
-
- setvbuf(stderr, nullptr, _IONBF, 0);
+void RedirectStream(const char *p_file_name, const char *p_mode, FILE *p_cpp_stream, const DWORD p_std_handle) {
+ const HANDLE h_existing = GetStdHandle(p_std_handle);
+ if (h_existing != INVALID_HANDLE_VALUE) { // Redirect only if attached console have a valid handle.
+ const HANDLE h_cpp = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(p_cpp_stream)));
+ if (h_cpp == INVALID_HANDLE_VALUE) { // Redirect only if it's not already redirected to the pipe or file.
+ FILE *fp = p_cpp_stream;
+ freopen_s(&fp, p_file_name, p_mode, p_cpp_stream); // Redirect stream.
+ setvbuf(p_cpp_stream, nullptr, _IONBF, 0); // Disable stream buffering.
+ }
+ }
+}
- // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
+void RedirectIOToConsole() {
+ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
+ RedirectStream("CONIN$", "r", stdin, STD_INPUT_HANDLE);
+ RedirectStream("CONOUT$", "w", stdout, STD_OUTPUT_HANDLE);
+ RedirectStream("CONOUT$", "w", stderr, STD_ERROR_HANDLE);
- // point to console as well
+ printf("\n"); // Make sure our output is starting from the new line.
+ }
}
BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
@@ -172,7 +130,9 @@ void OS_Windows::initialize_debugging() {
void OS_Windows::initialize() {
crash_handler.initialize();
- //RedirectIOToConsole();
+#ifndef WINDOWS_SUBSYSTEM_CONSOLE
+ RedirectIOToConsole();
+#endif
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
@@ -401,78 +361,87 @@ String OS_Windows::_quote_command_line_argument(const String &p_text) const {
return p_text;
}
-Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
String path = p_path.replace("/", "\\");
String command = _quote_command_line_argument(path);
for (const String &E : p_arguments) {
command += " " + _quote_command_line_argument(E);
}
+ ProcessInfo pi;
+ ZeroMemory(&pi.si, sizeof(pi.si));
+ pi.si.cb = sizeof(pi.si);
+ ZeroMemory(&pi.pi, sizeof(pi.pi));
+ LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
+
+ bool inherit_handles = false;
+ HANDLE pipe[2] = { nullptr, nullptr };
if (r_pipe) {
+ // Create pipe for StdOut and StdErr.
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = true;
+ sa.lpSecurityDescriptor = nullptr;
+
+ ERR_FAIL_COND_V(!CreatePipe(&pipe[0], &pipe[1], &sa, 0), ERR_CANT_FORK);
+ ERR_FAIL_COND_V(!SetHandleInformation(pipe[0], HANDLE_FLAG_INHERIT, 0), ERR_CANT_FORK); // Read handle is for host process only and should not be inherited.
+
+ pi.si.dwFlags |= STARTF_USESTDHANDLES;
+ pi.si.hStdOutput = pipe[1];
if (read_stderr) {
- command += " 2>&1"; // Include stderr
+ pi.si.hStdError = pipe[1];
}
- // Add extra quotes around the full command, to prevent it from stripping quotes in the command,
- // because _wpopen calls command as "cmd.exe /c command", instead of executing it directly
- command = _quote_command_line_argument(command);
-
- FILE *f = _wpopen((LPCWSTR)(command.utf16().get_data()), L"r");
- ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot create pipe from command: " + command);
- char buf[65535];
- while (fgets(buf, 65535, f)) {
+ inherit_handles = true;
+ }
+ DWORD creation_flags = NORMAL_PRIORITY_CLASS;
+ if (p_open_console) {
+ creation_flags |= CREATE_NEW_CONSOLE;
+ } else {
+ creation_flags |= CREATE_NO_WINDOW;
+ }
+
+ int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, inherit_handles, creation_flags, nullptr, nullptr, si_w, &pi.pi);
+ if (!ret && r_pipe) {
+ CloseHandle(pipe[0]); // Cleanup pipe handles.
+ CloseHandle(pipe[1]);
+ }
+ ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command);
+
+ if (r_pipe) {
+ CloseHandle(pipe[1]); // Close pipe write handle (only child process is writing).
+ char buf[4096];
+ DWORD read = 0;
+ for (;;) { // Read StdOut and StdErr from pipe.
+ bool success = ReadFile(pipe[0], buf, 4096, &read, NULL);
+ if (!success || read == 0) {
+ break;
+ }
if (p_pipe_mutex) {
p_pipe_mutex->lock();
}
- (*r_pipe) += String::utf8(buf);
+ (*r_pipe) += String::utf8(buf, read);
if (p_pipe_mutex) {
p_pipe_mutex->unlock();
}
- }
- int rv = _pclose(f);
-
- if (r_exitcode) {
- *r_exitcode = rv;
- }
- return OK;
+ };
+ CloseHandle(pipe[0]); // Close pipe read handle.
+ } else {
+ WaitForSingleObject(pi.pi.hProcess, INFINITE);
}
- ProcessInfo pi;
- ZeroMemory(&pi.si, sizeof(pi.si));
- pi.si.cb = sizeof(pi.si);
- ZeroMemory(&pi.pi, sizeof(pi.pi));
- LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
-
- DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS;
-#ifndef DEBUG_ENABLED
- dwCreationFlags |= CREATE_NO_WINDOW;
-#endif
-
- int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, dwCreationFlags, nullptr, nullptr, si_w, &pi.pi);
- ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command);
-
- WaitForSingleObject(pi.pi.hProcess, INFINITE);
if (r_exitcode) {
DWORD ret2;
GetExitCodeProcess(pi.pi.hProcess, &ret2);
*r_exitcode = ret2;
}
+
CloseHandle(pi.pi.hProcess);
CloseHandle(pi.pi.hThread);
return OK;
};
-bool OS_Windows::_is_win11_terminal() const {
- HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
- DWORD dwMode = 0;
- if (GetConsoleMode(hStdOut, &dwMode)) {
- return ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == ENABLE_VIRTUAL_TERMINAL_PROCESSING);
- } else {
- return false;
- }
-}
-
-Error OS_Windows::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
+Error OS_Windows::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
String path = p_path.replace("/", "\\");
String command = _quote_command_line_argument(path);
for (const String &E : p_arguments) {
@@ -485,16 +454,14 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg
ZeroMemory(&pi.pi, sizeof(pi.pi));
LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
- DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS;
-#ifndef DEBUG_ENABLED
- dwCreationFlags |= CREATE_NO_WINDOW;
-#endif
- if (p_path == get_executable_path() && GetConsoleWindow() != nullptr && _is_win11_terminal()) {
- // Open a new terminal as a workaround for Windows Terminal bug.
- dwCreationFlags |= CREATE_NEW_CONSOLE;
+ DWORD creation_flags = NORMAL_PRIORITY_CLASS;
+ if (p_open_console) {
+ creation_flags |= CREATE_NEW_CONSOLE;
+ } else {
+ creation_flags |= CREATE_NO_WINDOW;
}
- int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, dwCreationFlags, nullptr, nullptr, si_w, &pi.pi);
+ int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, creation_flags, nullptr, nullptr, si_w, &pi.pi);
ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command);
ProcessID pid = pi.pi.dwProcessId;
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 4e61f3be7e..28baa855b4 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -128,8 +128,8 @@ public:
virtual void delay_usec(uint32_t p_usec) const override;
virtual uint64_t get_ticks_usec() const override;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) override;
- virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
@@ -157,7 +157,6 @@ public:
void run();
- bool _is_win11_terminal() const;
virtual bool _check_internal_feature_support(const String &p_feature) override;
virtual void disable_crash_handler() override;
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index 4916eb573c..decb3d0dd8 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -417,7 +417,7 @@ void AnimatedSprite2D::_reset_timeout() {
void AnimatedSprite2D::set_animation(const StringName &p_animation) {
ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", p_animation));
- ERR_FAIL_COND_MSG(frames->get_animation_names().find(p_animation) == -1, vformat("There is no animation with name '%s'.", p_animation));
+ ERR_FAIL_COND_MSG(!frames->get_animation_names().has(p_animation), vformat("There is no animation with name '%s'.", p_animation));
if (animation == p_animation) {
return;
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index f4c0665f36..e8dfaf9c2e 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -530,7 +530,7 @@ Point2 Camera2D::get_camera_screen_center() const {
Size2 Camera2D::_get_camera_screen_size() const {
// special case if the camera2D is in the root viewport
if (Engine::get_singleton()->is_editor_hint() && get_viewport()->get_parent_viewport() == get_tree()->get_root()) {
- return Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ return Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
}
return get_viewport_rect().size;
}
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index 0f4e3c8bed..70c7e48fd4 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -268,7 +268,7 @@ uint32_t CollisionObject2D::create_shape_owner(Object *p_owner) {
id = shapes.back()->key() + 1;
}
- sd.owner = p_owner;
+ sd.owner_id = p_owner ? p_owner->get_instance_id() : ObjectID();
shapes[id] = sd;
@@ -382,7 +382,7 @@ Transform2D CollisionObject2D::shape_owner_get_transform(uint32_t p_owner) const
Object *CollisionObject2D::shape_owner_get_owner(uint32_t p_owner) const {
ERR_FAIL_COND_V(!shapes.has(p_owner), nullptr);
- return shapes[p_owner].owner;
+ return ObjectDB::get_instance(shapes[p_owner].owner_id);
}
void CollisionObject2D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape2D> &p_shape) {
diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h
index 9463b2c429..f2b7eecc7b 100644
--- a/scene/2d/collision_object_2d.h
+++ b/scene/2d/collision_object_2d.h
@@ -59,7 +59,7 @@ private:
PhysicsServer2D::BodyMode body_mode = PhysicsServer2D::BODY_MODE_STATIC;
struct ShapeData {
- Object *owner = nullptr;
+ ObjectID owner_id;
Transform2D xform;
struct Shape {
Ref<Shape2D> shape;
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index b6d1e5c934..11c036ac9c 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -335,6 +335,42 @@ Ref<Texture2D> GPUParticles2D::get_texture() const {
void GPUParticles2D::_validate_property(PropertyInfo &property) const {
}
+void GPUParticles2D::emit_particle(const Transform2D &p_transform2d, const Vector2 &p_velocity2d, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
+ Transform3D transform;
+ transform.basis.set_axis(0, Vector3(p_transform2d.get_axis(0).x, p_transform2d.get_axis(0).y, 0));
+ transform.basis.set_axis(1, Vector3(p_transform2d.get_axis(1).x, p_transform2d.get_axis(1).y, 0));
+ transform.set_origin(Vector3(p_transform2d.get_origin().x, p_transform2d.get_origin().y, 0));
+ Vector3 velocity = Vector3(p_velocity2d.x, p_velocity2d.y, 0);
+
+ RS::get_singleton()->particles_emit(particles, transform, velocity, p_color, p_custom, p_emit_flags);
+}
+
+void GPUParticles2D::_attach_sub_emitter() {
+ Node *n = get_node_or_null(sub_emitter);
+ if (n) {
+ GPUParticles2D *sen = Object::cast_to<GPUParticles2D>(n);
+ if (sen && sen != this) {
+ RS::get_singleton()->particles_set_subemitter(particles, sen->particles);
+ }
+ }
+}
+
+void GPUParticles2D::set_sub_emitter(const NodePath &p_path) {
+ if (is_inside_tree()) {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ }
+
+ sub_emitter = p_path;
+
+ if (is_inside_tree() && sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+}
+
+NodePath GPUParticles2D::get_sub_emitter() const {
+ return sub_emitter;
+}
+
void GPUParticles2D::restart() {
RS::get_singleton()->particles_restart(particles);
RS::get_singleton()->particles_set_emitting(particles, true);
@@ -462,6 +498,16 @@ void GPUParticles2D::_notification(int p_what) {
#endif
}
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ if (sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+ }
+
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ }
+
if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) {
if (can_process()) {
RS::get_singleton()->particles_set_speed_scale(particles, speed_scale);
@@ -523,6 +569,11 @@ void GPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("restart"), &GPUParticles2D::restart);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter", "path"), &GPUParticles2D::set_sub_emitter);
+ ClassDB::bind_method(D_METHOD("get_sub_emitter"), &GPUParticles2D::get_sub_emitter);
+
+ ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles2D::emit_particle);
+
ClassDB::bind_method(D_METHOD("set_trail_enabled", "enabled"), &GPUParticles2D::set_trail_enabled);
ClassDB::bind_method(D_METHOD("set_trail_length", "secs"), &GPUParticles2D::set_trail_length);
@@ -538,6 +589,7 @@ void GPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false.
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles2D"), "set_sub_emitter", "get_sub_emitter");
ADD_GROUP("Time", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
@@ -566,6 +618,12 @@ void GPUParticles2D::_bind_methods() {
BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
BIND_ENUM_CONSTANT(DRAW_ORDER_REVERSE_LIFETIME);
+
+ BIND_ENUM_CONSTANT(EMIT_FLAG_POSITION);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_ROTATION_SCALE);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_VELOCITY);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_COLOR);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM);
}
GPUParticles2D::GPUParticles2D() {
diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h
index aa9a8da129..fc95ae27b2 100644
--- a/scene/2d/gpu_particles_2d.h
+++ b/scene/2d/gpu_particles_2d.h
@@ -79,6 +79,8 @@ private:
RID mesh;
+ void _attach_sub_emitter();
+
protected:
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const override;
@@ -139,6 +141,19 @@ public:
TypedArray<String> get_configuration_warnings() const override;
+ void set_sub_emitter(const NodePath &p_path);
+ NodePath get_sub_emitter() const;
+
+ enum EmitFlags {
+ EMIT_FLAG_POSITION = RS::PARTICLES_EMIT_FLAG_POSITION,
+ EMIT_FLAG_ROTATION_SCALE = RS::PARTICLES_EMIT_FLAG_ROTATION_SCALE,
+ EMIT_FLAG_VELOCITY = RS::PARTICLES_EMIT_FLAG_VELOCITY,
+ EMIT_FLAG_COLOR = RS::PARTICLES_EMIT_FLAG_COLOR,
+ EMIT_FLAG_CUSTOM = RS::PARTICLES_EMIT_FLAG_CUSTOM
+ };
+
+ void emit_particle(const Transform2D &p_transform, const Vector2 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags);
+
void restart();
Rect2 capture_rect() const;
GPUParticles2D();
@@ -146,5 +161,6 @@ public:
};
VARIANT_ENUM_CAST(GPUParticles2D::DrawOrder)
+VARIANT_ENUM_CAST(GPUParticles2D::EmitFlags)
#endif // PARTICLES_2D_H
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 1a6aaecaa8..7f2290bdc7 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -133,7 +133,7 @@ int Line2D::get_point_count() const {
void Line2D::clear_points() {
int count = _points.size();
if (count > 0) {
- _points.resize(0);
+ _points.clear();
update();
}
}
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index e5df089771..fad54070a5 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -53,9 +53,9 @@ void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const {
void NavigationObstacle2D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
- initialize_agent();
+ case NOTIFICATION_ENTER_TREE: {
parent_node2d = Object::cast_to<Node2D>(get_parent());
+ reevaluate_agent_radius();
if (parent_node2d != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
NavigationServer2D::get_singleton()->agent_set_map(get_rid(), parent_node2d->get_world_2d()->get_navigation_map());
@@ -83,6 +83,7 @@ void NavigationObstacle2D::_notification(int p_what) {
NavigationObstacle2D::NavigationObstacle2D() {
agent = NavigationServer2D::get_singleton()->agent_create();
+ initialize_agent();
}
NavigationObstacle2D::~NavigationObstacle2D() {
@@ -110,7 +111,7 @@ void NavigationObstacle2D::initialize_agent() {
void NavigationObstacle2D::reevaluate_agent_radius() {
if (!estimate_radius) {
NavigationServer2D::get_singleton()->agent_set_radius(agent, radius);
- } else if (parent_node2d) {
+ } else if (parent_node2d && parent_node2d->is_inside_tree()) {
NavigationServer2D::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
}
}
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 34f5830d8d..4bead978f1 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -299,7 +299,7 @@ void NavigationPolygon::make_polygons_from_outlines() {
}
polygons.clear();
- vertices.resize(0);
+ vertices.clear();
Map<Vector2, int> points;
for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) {
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 9331340e1b..9d26543243 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -413,7 +413,7 @@ void Node2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_relative_transform_to_parent", "parent"), &Node2D::get_relative_transform_to_parent);
ADD_GROUP("Transform", "");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,0,or_lesser,or_greater,noslider,suffix:px"), "set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_lesser,or_greater,noslider,suffix:px"), "set_position", "get_position");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "skew", PROPERTY_HINT_RANGE, "-89.9,89.9,0.1,radians"), "set_skew", "get_skew");
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index b2cc8164b6..fb611addf8 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -34,8 +34,8 @@
#include "scene/scene_string_names.h"
void PhysicsBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08));
- ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08));
+ ClassDB::bind_method(D_METHOD("move_and_collide", "distance", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08));
+ ClassDB::bind_method(D_METHOD("test_move", "from", "distance", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08));
ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with);
@@ -54,11 +54,8 @@ PhysicsBody2D::~PhysicsBody2D() {
}
}
-Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_linear_velocity, bool p_test_only, real_t p_margin) {
- // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
- double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
-
- PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin);
+Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_distance, bool p_test_only, real_t p_margin) {
+ PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_distance, p_margin);
PhysicsServer2D::MotionResult result;
if (move_and_collide(parameters, result, p_test_only)) {
@@ -129,7 +126,7 @@ bool PhysicsBody2D::move_and_collide(const PhysicsServer2D::MotionParameters &p_
return colliding;
}
-bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) {
+bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_distance, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) {
ERR_FAIL_COND_V(!is_inside_tree(), false);
PhysicsServer2D::MotionResult *r = nullptr;
@@ -141,10 +138,7 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_linear
r = &temp_result;
}
- // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
- double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
-
- PhysicsServer2D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin);
+ PhysicsServer2D::MotionParameters parameters(p_from, p_distance, p_margin);
bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r);
@@ -1162,7 +1156,7 @@ bool CharacterBody2D::move_and_slide() {
if (motion_mode == MOTION_MODE_GROUNDED) {
_move_and_slide_grounded(delta, was_on_floor);
} else {
- _move_and_slide_free(delta);
+ _move_and_slide_floating(delta);
}
// Compute real velocity.
@@ -1350,7 +1344,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
}
}
-void CharacterBody2D::_move_and_slide_free(double p_delta) {
+void CharacterBody2D::_move_and_slide_floating(double p_delta) {
Vector2 motion = motion_velocity * p_delta;
platform_rid = RID();
@@ -1376,7 +1370,7 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
break;
}
- if (free_mode_min_slide_angle != 0 && result.get_angle(-motion_velocity.normalized()) < free_mode_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
+ if (wall_min_slide_angle != 0 && result.get_angle(-motion_velocity.normalized()) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
motion = Vector2();
} else if (first_slide) {
Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized();
@@ -1668,12 +1662,12 @@ void CharacterBody2D::set_floor_snap_length(real_t p_floor_snap_length) {
floor_snap_length = p_floor_snap_length;
}
-real_t CharacterBody2D::get_free_mode_min_slide_angle() const {
- return free_mode_min_slide_angle;
+real_t CharacterBody2D::get_wall_min_slide_angle() const {
+ return wall_min_slide_angle;
}
-void CharacterBody2D::set_free_mode_min_slide_angle(real_t p_radians) {
- free_mode_min_slide_angle = p_radians;
+void CharacterBody2D::set_wall_min_slide_angle(real_t p_radians) {
+ wall_min_slide_angle = p_radians;
}
const Vector2 &CharacterBody2D::get_up_direction() const {
@@ -1681,7 +1675,7 @@ const Vector2 &CharacterBody2D::get_up_direction() const {
}
void CharacterBody2D::set_up_direction(const Vector2 &p_up_direction) {
- ERR_FAIL_COND_MSG(p_up_direction == Vector2(), "up_direction can't be equal to Vector2.ZERO, consider using Free motion mode instead.");
+ ERR_FAIL_COND_MSG(p_up_direction == Vector2(), "up_direction can't be equal to Vector2.ZERO, consider using Floating motion mode instead.");
up_direction = p_up_direction.normalized();
}
@@ -1728,8 +1722,8 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_floor_max_angle", "radians"), &CharacterBody2D::set_floor_max_angle);
ClassDB::bind_method(D_METHOD("get_floor_snap_length"), &CharacterBody2D::get_floor_snap_length);
ClassDB::bind_method(D_METHOD("set_floor_snap_length", "floor_snap_length"), &CharacterBody2D::set_floor_snap_length);
- ClassDB::bind_method(D_METHOD("get_free_mode_min_slide_angle"), &CharacterBody2D::get_free_mode_min_slide_angle);
- ClassDB::bind_method(D_METHOD("set_free_mode_min_slide_angle", "radians"), &CharacterBody2D::set_free_mode_min_slide_angle);
+ ClassDB::bind_method(D_METHOD("get_wall_min_slide_angle"), &CharacterBody2D::get_wall_min_slide_angle);
+ ClassDB::bind_method(D_METHOD("set_wall_min_slide_angle", "radians"), &CharacterBody2D::set_wall_min_slide_angle);
ClassDB::bind_method(D_METHOD("get_up_direction"), &CharacterBody2D::get_up_direction);
ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody2D::set_up_direction);
ClassDB::bind_method(D_METHOD("set_motion_mode", "mode"), &CharacterBody2D::set_motion_mode);
@@ -1754,14 +1748,12 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody2D::_get_slide_collision);
ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody2D::_get_last_slide_collision);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Floating", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides");
-
- ADD_GROUP("Free Mode", "free_mode_");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "free_mode_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_free_mode_min_slide_angle", "get_free_mode_min_slide_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle");
ADD_GROUP("Floor", "floor_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled");
@@ -1775,7 +1767,7 @@ void CharacterBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED);
- BIND_ENUM_CONSTANT(MOTION_MODE_FREE);
+ BIND_ENUM_CONSTANT(MOTION_MODE_FLOATING);
BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS);
BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY);
@@ -1783,12 +1775,12 @@ void CharacterBody2D::_bind_methods() {
}
void CharacterBody2D::_validate_property(PropertyInfo &property) const {
- if (motion_mode == MOTION_MODE_FREE) {
+ if (motion_mode == MOTION_MODE_FLOATING) {
if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") {
property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
} else {
- if (property.name == "free_mode_min_slide_angle") {
+ if (property.name == "wall_min_slide_angle") {
property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 649d67d759..cfaa2570fb 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -47,11 +47,11 @@ protected:
Ref<KinematicCollision2D> motion_cache;
- Ref<KinematicCollision2D> _move(const Vector2 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.08);
+ Ref<KinematicCollision2D> _move(const Vector2 &p_distance, bool p_test_only = false, real_t p_margin = 0.08);
public:
bool move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true);
- bool test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
+ bool test_move(const Transform2D &p_from, const Vector2 &p_distance, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
TypedArray<PhysicsBody2D> get_collision_exceptions();
void add_collision_exception_with(Node *p_node); //must be physicsbody
@@ -328,7 +328,7 @@ class CharacterBody2D : public PhysicsBody2D {
public:
enum MotionMode {
MOTION_MODE_GROUNDED,
- MOTION_MODE_FREE,
+ MOTION_MODE_FLOATING,
};
enum MovingPlatformApplyVelocityOnLeave {
PLATFORM_VEL_ON_LEAVE_ALWAYS,
@@ -374,7 +374,7 @@ private:
int platform_layer = 0;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
real_t floor_snap_length = 1;
- real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0);
+ real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0);
Vector2 up_direction = Vector2(0.0, -1.0);
uint32_t moving_platform_floor_layers = UINT32_MAX;
uint32_t moving_platform_wall_layers = 0;
@@ -420,8 +420,8 @@ private:
real_t get_floor_snap_length();
void set_floor_snap_length(real_t p_floor_snap_length);
- real_t get_free_mode_min_slide_angle() const;
- void set_free_mode_min_slide_angle(real_t p_radians);
+ real_t get_wall_min_slide_angle() const;
+ void set_wall_min_slide_angle(real_t p_radians);
uint32_t get_moving_platform_floor_layers() const;
void set_moving_platform_floor_layers(const uint32_t p_exclude_layer);
@@ -435,7 +435,7 @@ private:
void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity);
MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const;
- void _move_and_slide_free(double p_delta);
+ void _move_and_slide_floating(double p_delta);
void _move_and_slide_grounded(double p_delta, bool p_was_on_floor);
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 1fdd8b05a6..9521667854 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -263,26 +263,18 @@ void RayCast2D::add_exception_rid(const RID &p_rid) {
exclude.insert(p_rid);
}
-void RayCast2D::add_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- add_exception_rid(co->get_rid());
+void RayCast2D::add_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ add_exception_rid(p_node->get_rid());
}
void RayCast2D::remove_exception_rid(const RID &p_rid) {
exclude.erase(p_rid);
}
-void RayCast2D::remove_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- remove_exception_rid(co->get_rid());
+void RayCast2D::remove_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ remove_exception_rid(p_node->get_rid());
}
void RayCast2D::clear_exceptions() {
diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h
index a1015c6ce0..2c6f2d5c00 100644
--- a/scene/2d/ray_cast_2d.h
+++ b/scene/2d/ray_cast_2d.h
@@ -33,6 +33,8 @@
#include "scene/2d/node_2d.h"
+class CollisionObject2D;
+
class RayCast2D : public Node2D {
GDCLASS(RayCast2D, Node2D);
@@ -94,9 +96,9 @@ public:
Vector2 get_collision_normal() const;
void add_exception_rid(const RID &p_rid);
- void add_exception(const Object *p_object);
+ void add_exception(const CollisionObject2D *p_node);
void remove_exception_rid(const RID &p_rid);
- void remove_exception(const Object *p_object);
+ void remove_exception(const CollisionObject2D *p_node);
void clear_exceptions();
RayCast2D();
diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp
index 10194861b4..24199c96b5 100644
--- a/scene/2d/shape_cast_2d.cpp
+++ b/scene/2d/shape_cast_2d.cpp
@@ -322,26 +322,18 @@ void ShapeCast2D::add_exception_rid(const RID &p_rid) {
exclude.insert(p_rid);
}
-void ShapeCast2D::add_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- add_exception_rid(co->get_rid());
+void ShapeCast2D::add_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ add_exception_rid(p_node->get_rid());
}
void ShapeCast2D::remove_exception_rid(const RID &p_rid) {
exclude.erase(p_rid);
}
-void ShapeCast2D::remove_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject2D *co = Object::cast_to<CollisionObject2D>(p_object);
- if (!co) {
- return;
- }
- remove_exception_rid(co->get_rid());
+void ShapeCast2D::remove_exception(const CollisionObject2D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject2D.");
+ remove_exception_rid(p_node->get_rid());
}
void ShapeCast2D::clear_exceptions() {
diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h
index 7e1ebeb315..ea36b25068 100644
--- a/scene/2d/shape_cast_2d.h
+++ b/scene/2d/shape_cast_2d.h
@@ -34,6 +34,8 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/shape_2d.h"
+class CollisionObject2D;
+
class ShapeCast2D : public Node2D {
GDCLASS(ShapeCast2D, Node2D);
@@ -109,9 +111,9 @@ public:
real_t get_closest_collision_unsafe_fraction() const;
void add_exception_rid(const RID &p_rid);
- void add_exception(const Object *p_object);
+ void add_exception(const CollisionObject2D *p_node);
void remove_exception_rid(const RID &p_rid);
- void remove_exception(const Object *p_object);
+ void remove_exception(const CollisionObject2D *p_node);
void clear_exceptions();
TypedArray<String> get_configuration_warnings() const override;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 62dc4d1c15..02ca1ba2aa 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -1119,7 +1119,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
if (q.runtime_tile_data_cache.has(E_cell.value)) {
tile_data = q.runtime_tile_data_cache[E_cell.value];
} else {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
Ref<ShaderMaterial> mat = tile_data->get_material();
@@ -1311,7 +1311,7 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
}
// Get tile data.
- const TileData *tile_data = p_tile_data_override ? p_tile_data_override : Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
+ 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;
@@ -1474,7 +1474,7 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
if (q.runtime_tile_data_cache.has(E_cell->get())) {
tile_data = q.runtime_tile_data_cache[E_cell->get()];
} else {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
for (int tile_set_physics_layer = 0; tile_set_physics_layer < tile_set->get_physics_layers_count(); tile_set_physics_layer++) {
Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer);
@@ -1671,7 +1671,7 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
if (q.runtime_tile_data_cache.has(E_cell->get())) {
tile_data = q.runtime_tile_data_cache[E_cell->get()];
} else {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count());
@@ -1760,7 +1760,7 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
if (p_quadrant->runtime_tile_data_cache.has(E_cell->get())) {
tile_data = p_quadrant->runtime_tile_data_cache[E_cell->get()];
} else {
- tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
Transform2D xform;
@@ -1809,7 +1809,7 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
// Clear the scenes.
for (const KeyValue<Vector2i, String> &E : q.scenes) {
- Node *node = get_node(E.value);
+ Node *node = get_node_or_null(E.value);
if (node) {
node->queue_delete();
}
@@ -1857,7 +1857,7 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
// Clear the scenes.
for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) {
- Node *node = get_node(E.value);
+ Node *node = get_node_or_null(E.value);
if (node) {
node->queue_delete();
}
@@ -2204,7 +2204,7 @@ Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_ce
Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id);
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile);
if (tile_data && tile_data->get_terrain_set() == p_terrain_set) {
neighbor_tile_data = tile_data;
}
@@ -2580,7 +2580,7 @@ void TileMap::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r
if (atlas_source) {
bool ret = false;
if (GDVIRTUAL_CALL(_use_tile_data_runtime_update, q.layer, E_cell.value, ret) && ret) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
// Create the runtime TileData.
TileData *tile_data_runtime_use = tile_data->duplicate();
@@ -2725,7 +2725,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const {
}
Vector2 TileMap::map_to_world(const Vector2i &p_pos) const {
- // SHOULD RETURN THE CENTER OF THE TILE
+ // SHOULD RETURN THE CENTER OF THE CELL
ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2());
Vector2 ret = p_pos;
@@ -3648,9 +3648,6 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_dirty_quadrants"), &TileMap::_update_dirty_quadrants);
- ClassDB::bind_method(D_METHOD("_set_tile_data", "layer", "data"), &TileMap::_set_tile_data);
- ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data);
-
ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileMap::_tile_set_changed_deferred_update);
GDVIRTUAL_BIND(_use_tile_data_runtime_update, "layer", "coords");
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index df7c044f9e..3ab09550fa 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -484,7 +484,7 @@ uint32_t CollisionObject3D::create_shape_owner(Object *p_owner) {
id = shapes.back()->key() + 1;
}
- sd.owner = p_owner;
+ sd.owner_id = p_owner ? p_owner->get_instance_id() : ObjectID();
shapes[id] = sd;
@@ -563,7 +563,7 @@ Transform3D CollisionObject3D::shape_owner_get_transform(uint32_t p_owner) const
Object *CollisionObject3D::shape_owner_get_owner(uint32_t p_owner) const {
ERR_FAIL_COND_V(!shapes.has(p_owner), nullptr);
- return shapes[p_owner].owner;
+ return ObjectDB::get_instance(shapes[p_owner].owner_id);
}
void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3D> &p_shape) {
diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h
index f560753543..e92843d784 100644
--- a/scene/3d/collision_object_3d.h
+++ b/scene/3d/collision_object_3d.h
@@ -57,7 +57,7 @@ private:
PhysicsServer3D::BodyMode body_mode = PhysicsServer3D::BODY_MODE_STATIC;
struct ShapeData {
- Object *owner = nullptr;
+ ObjectID owner_id;
Transform3D xform;
struct ShapeBase {
RID debug_shape;
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index 0a0507207a..d88bb815bc 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -247,7 +247,7 @@ void Light3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Dynamic,Static"), "set_bake_mode", "get_bake_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI/SDFGI only)"), "set_bake_mode", "get_bake_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow", "has_shadow");
@@ -284,8 +284,8 @@ void Light3D::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_MAX);
BIND_ENUM_CONSTANT(BAKE_DISABLED);
- BIND_ENUM_CONSTANT(BAKE_DYNAMIC);
BIND_ENUM_CONSTANT(BAKE_STATIC);
+ BIND_ENUM_CONSTANT(BAKE_DYNAMIC);
}
Light3D::Light3D(RenderingServer::LightType p_type) {
diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h
index 93dc8155bb..d5d2aee43d 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -63,8 +63,8 @@ public:
enum BakeMode {
BAKE_DISABLED,
+ BAKE_STATIC,
BAKE_DYNAMIC,
- BAKE_STATIC
};
private:
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 715c421632..825742da35 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -217,7 +217,7 @@ LightmapGIData::~LightmapGIData() {
void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &meshes, Vector<LightsFound> &lights, Vector<Vector3> &probes) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);
- if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_BAKED && mi->is_visible_in_tree()) {
+ if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) {
Ref<Mesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
bool all_have_uv2_and_normal = true;
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index b1f6f0cf91..ba6c50d98c 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -54,9 +54,9 @@ void NavigationObstacle3D::_validate_property(PropertyInfo &p_property) const {
void NavigationObstacle3D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
- initialize_agent();
+ case NOTIFICATION_ENTER_TREE: {
parent_node3d = Object::cast_to<Node3D>(get_parent());
+ reevaluate_agent_radius();
if (parent_node3d != nullptr) {
// place agent on navigation map first or else the RVO agent callback creation fails silently later
NavigationServer3D::get_singleton()->agent_set_map(get_rid(), parent_node3d->get_world_3d()->get_navigation_map());
@@ -91,6 +91,7 @@ void NavigationObstacle3D::_notification(int p_what) {
NavigationObstacle3D::NavigationObstacle3D() {
agent = NavigationServer3D::get_singleton()->agent_create();
+ initialize_agent();
}
NavigationObstacle3D::~NavigationObstacle3D() {
@@ -118,7 +119,7 @@ void NavigationObstacle3D::initialize_agent() {
void NavigationObstacle3D::reevaluate_agent_radius() {
if (!estimate_radius) {
NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
- } else if (parent_node3d) {
+ } else if (parent_node3d && parent_node3d->is_inside_tree()) {
NavigationServer3D::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
}
}
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index a992d2aaf2..cad1201d63 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -300,8 +300,9 @@ Node3D *Node3D::get_parent_node_3d() const {
}
Transform3D Node3D::get_relative_transform(const Node *p_parent) const {
- if (p_parent == this)
+ if (p_parent == this) {
return Transform3D();
+ }
ERR_FAIL_COND_V(!data.parent, Transform3D());
@@ -990,7 +991,7 @@ void Node3D::_bind_methods() {
ADD_GROUP("Transform", "");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_transform", "get_transform");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0,or_greater,or_lesser,noslider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_greater,or_lesser,noslider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "quaternion", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_quaternion", "get_quaternion");
ADD_PROPERTY(PropertyInfo(Variant::BASIS, "basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_basis", "get_basis");
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index b3192a5bb5..13a38f3b9f 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -34,8 +34,8 @@
#include "scene/scene_string_names.h"
void PhysicsBody3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1));
- ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("move_and_collide", "distance", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("test_move", "from", "distance", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1));
ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicsBody3D::set_axis_lock);
ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicsBody3D::get_axis_lock);
@@ -91,11 +91,8 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) {
PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid());
}
-Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_linear_velocity, bool p_test_only, real_t p_margin, int p_max_collisions) {
- // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
- double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
-
- PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin);
+Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_distance, bool p_test_only, real_t p_margin, int p_max_collisions) {
+ PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_distance, p_margin);
parameters.max_collisions = p_max_collisions;
PhysicsServer3D::MotionResult result;
@@ -170,7 +167,7 @@ bool PhysicsBody3D::move_and_collide(const PhysicsServer3D::MotionParameters &p_
return colliding;
}
-bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_linear_velocity, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) {
+bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_distance, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) {
ERR_FAIL_COND_V(!is_inside_tree(), false);
PhysicsServer3D::MotionResult *r = nullptr;
@@ -182,10 +179,7 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_linear
r = &temp_result;
}
- // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
- double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
-
- PhysicsServer3D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin);
+ PhysicsServer3D::MotionParameters parameters(p_from, p_distance, p_margin);
bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r);
@@ -1233,7 +1227,7 @@ bool CharacterBody3D::move_and_slide() {
if (motion_mode == MOTION_MODE_GROUNDED) {
_move_and_slide_grounded(delta, was_on_floor);
} else {
- _move_and_slide_free(delta);
+ _move_and_slide_floating(delta);
}
// Compute real velocity.
@@ -1512,7 +1506,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
}
}
-void CharacterBody3D::_move_and_slide_free(double p_delta) {
+void CharacterBody3D::_move_and_slide_floating(double p_delta) {
Vector3 motion = motion_velocity * p_delta;
platform_rid = RID();
@@ -1929,7 +1923,7 @@ const Vector3 &CharacterBody3D::get_up_direction() const {
}
void CharacterBody3D::set_up_direction(const Vector3 &p_up_direction) {
- ERR_FAIL_COND_MSG(p_up_direction == Vector3(), "up_direction can't be equal to Vector3.ZERO, consider using Free motion mode instead.");
+ ERR_FAIL_COND_MSG(p_up_direction == Vector3(), "up_direction can't be equal to Vector3.ZERO, consider using Floating motion mode instead.");
up_direction = p_up_direction.normalized();
}
@@ -2000,12 +1994,11 @@ void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody3D::_get_slide_collision);
ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody3D::_get_last_slide_collision);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Floating", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_motion_velocity", "get_motion_velocity");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_slides", "get_max_slides");
- ADD_GROUP("Free Mode", "free_mode_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle");
ADD_GROUP("Floor", "floor_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled");
@@ -2020,7 +2013,7 @@ void CharacterBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED);
- BIND_ENUM_CONSTANT(MOTION_MODE_FREE);
+ BIND_ENUM_CONSTANT(MOTION_MODE_FLOATING);
BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS);
BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY);
@@ -2028,7 +2021,7 @@ void CharacterBody3D::_bind_methods() {
}
void CharacterBody3D::_validate_property(PropertyInfo &property) const {
- if (motion_mode == MOTION_MODE_FREE) {
+ if (motion_mode == MOTION_MODE_FLOATING) {
if (property.name.begins_with("floor_") || property.name == "up_direction" || property.name == "slide_on_ceiling") {
property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index e37b841117..67dc7382c3 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -50,11 +50,11 @@ protected:
uint16_t locked_axis = 0;
- Ref<KinematicCollision3D> _move(const Vector3 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1);
+ Ref<KinematicCollision3D> _move(const Vector3 &p_distance, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1);
public:
bool move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true);
- bool test_move(const Transform3D &p_from, const Vector3 &p_linear_velocity, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1);
+ bool test_move(const Transform3D &p_from, const Vector3 &p_distance, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1);
void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock);
bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const;
@@ -345,7 +345,7 @@ class CharacterBody3D : public PhysicsBody3D {
public:
enum MotionMode {
MOTION_MODE_GROUNDED,
- MOTION_MODE_FREE,
+ MOTION_MODE_FLOATING,
};
enum MovingPlatformApplyVelocityOnLeave {
PLATFORM_VEL_ON_LEAVE_ALWAYS,
@@ -468,7 +468,7 @@ private:
void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity);
MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const;
- void _move_and_slide_free(double p_delta);
+ void _move_and_slide_floating(double p_delta);
void _move_and_slide_grounded(double p_delta, bool p_was_on_floor);
Ref<KinematicCollision3D> _get_slide_collision(int p_bounce);
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index 3bb65d07a0..b71c54dcf9 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -243,26 +243,18 @@ void RayCast3D::add_exception_rid(const RID &p_rid) {
exclude.insert(p_rid);
}
-void RayCast3D::add_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object);
- if (!co) {
- return;
- }
- add_exception_rid(co->get_rid());
+void RayCast3D::add_exception(const CollisionObject3D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D.");
+ add_exception_rid(p_node->get_rid());
}
void RayCast3D::remove_exception_rid(const RID &p_rid) {
exclude.erase(p_rid);
}
-void RayCast3D::remove_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object);
- if (!co) {
- return;
- }
- remove_exception_rid(co->get_rid());
+void RayCast3D::remove_exception(const CollisionObject3D *p_node) {
+ ERR_FAIL_NULL_MSG(p_node, "The passed Node must be an instance of CollisionObject3D.");
+ remove_exception_rid(p_node->get_rid());
}
void RayCast3D::clear_exceptions() {
diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h
index a53e2c83fc..ad85001591 100644
--- a/scene/3d/ray_cast_3d.h
+++ b/scene/3d/ray_cast_3d.h
@@ -33,6 +33,8 @@
#include "scene/3d/node_3d.h"
+class CollisionObject3D;
+
class RayCast3D : public Node3D {
GDCLASS(RayCast3D, Node3D);
@@ -116,9 +118,9 @@ public:
Vector3 get_collision_normal() const;
void add_exception_rid(const RID &p_rid);
- void add_exception(const Object *p_object);
+ void add_exception(const CollisionObject3D *p_node);
void remove_exception_rid(const RID &p_rid);
- void remove_exception(const Object *p_object);
+ void remove_exception(const CollisionObject3D *p_node);
void clear_exceptions();
RayCast3D();
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index a37fce9469..331d58927b 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -1088,8 +1088,9 @@ void AnimatedSprite3D::set_frame(int p_frame) {
if (frames->has_animation(animation)) {
int limit = frames->get_frame_count(animation);
- if (p_frame >= limit)
+ if (p_frame >= limit) {
p_frame = limit - 1;
+ }
}
if (p_frame < 0) {
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index 0db2fe9fc6..005bb5a737 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -247,7 +247,7 @@ bool GeometryInstance3D::_set(const StringName &p_name, const Variant &p_value)
}
#ifndef DISABLE_DEPRECATED
if (p_name == SceneStringNames::get_singleton()->use_in_baked_light && bool(p_value)) {
- set_gi_mode(GI_MODE_BAKED);
+ set_gi_mode(GI_MODE_STATIC);
return true;
}
@@ -358,7 +358,7 @@ void GeometryInstance3D::set_gi_mode(GIMode p_mode) {
RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false);
RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_DYNAMIC_GI, false);
} break;
- case GI_MODE_BAKED: {
+ case GI_MODE_STATIC: {
RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_BAKED_LIGHT, true);
RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_USE_DYNAMIC_GI, false);
@@ -462,7 +462,7 @@ void GeometryInstance3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling");
ADD_GROUP("Global Illumination", "gi_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)"), "set_gi_mode", "get_gi_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale");
ADD_GROUP("Visibility Range", "visibility_range_");
@@ -479,7 +479,7 @@ void GeometryInstance3D::_bind_methods() {
BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_SHADOWS_ONLY);
BIND_ENUM_CONSTANT(GI_MODE_DISABLED);
- BIND_ENUM_CONSTANT(GI_MODE_BAKED);
+ BIND_ENUM_CONSTANT(GI_MODE_STATIC);
BIND_ENUM_CONSTANT(GI_MODE_DYNAMIC);
BIND_ENUM_CONSTANT(LIGHTMAP_SCALE_1X);
diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h
index dd0eb25001..be964e5080 100644
--- a/scene/3d/visual_instance_3d.h
+++ b/scene/3d/visual_instance_3d.h
@@ -89,7 +89,7 @@ public:
enum GIMode {
GI_MODE_DISABLED,
- GI_MODE_BAKED,
+ GI_MODE_STATIC,
GI_MODE_DYNAMIC
};
diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp
index 35ac1792e9..bfe3c80a4f 100644
--- a/scene/3d/voxel_gi.cpp
+++ b/scene/3d/voxel_gi.cpp
@@ -282,7 +282,7 @@ Vector3 VoxelGI::get_extents() const {
void VoxelGI::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);
- if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_BAKED && mi->is_visible_in_tree()) {
+ if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) {
Ref<Mesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
AABB aabb = mesh->get_aabb();
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index 33939d4cd6..849316c568 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -292,10 +292,10 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) {
// actually blend the animations now
- float max_time_remaining = 0.0;
+ double max_time_remaining = 0.0;
for (int i = 0; i < blend_points_used; i++) {
- float remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false);
+ double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false);
max_time_remaining = MAX(max_time_remaining, remaining);
}
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index f169e79751..a3aa3f6cc8 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -438,7 +438,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
Vector2 blend_pos = get_parameter(blend_position);
int closest = get_parameter(this->closest);
double length_internal = get_parameter(this->length_internal);
- float mind = 0.0; //time of min distance point
+ double mind = 0.0; //time of min distance point
if (blend_mode == BLEND_MODE_INTERPOLATED) {
if (triangles.size() == 0) {
@@ -502,7 +502,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
for (int j = 0; j < 3; j++) {
if (i == triangle_points[j]) {
//blend with the given weight
- float t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
+ double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
if (first || t < mind) {
mind = t;
first = false;
@@ -530,7 +530,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
}
if (new_closest != closest && new_closest != -1) {
- float from = 0.0;
+ double from = 0.0;
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && closest != -1) {
//for ping-pong loop
Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[closest].node);
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 2740103a4a..3b55dd4a42 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -371,6 +371,8 @@ void AnimationNodeOneShot::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeOneShot::set_use_sync);
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeOneShot::is_using_sync);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_mode", PROPERTY_HINT_ENUM, "Blend,Add"), "set_mix_mode", "get_mix_mode");
+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadein_time", "get_fadein_time");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadeout_time", "get_fadeout_time");
@@ -748,7 +750,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) {
return 0;
}
- float rem = 0.0;
+ double rem = 0.0;
if (prev < 0) { // process current animation, check for transition
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index a551417778..d153b882f9 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -88,7 +88,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
}
}
-void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
+void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->player->has_animation(p_animation));
@@ -119,13 +119,13 @@ void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time
state->animation_states.push_back(anim_state);
}
-real_t AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, real_t p_time, bool p_seek, const Vector<StringName> &p_connections) {
+double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections) {
base_path = p_base_path;
parent = p_parent;
connections = p_connections;
state = p_state;
- real_t t = process(p_time, p_seek);
+ double t = process(p_time, p_seek);
state = nullptr;
parent = nullptr;
@@ -144,7 +144,7 @@ void AnimationNode::make_invalid(const String &p_reason) {
state->invalid_reasons += String::utf8("• ") + p_reason;
}
-real_t AnimationNode::blend_input(int p_input, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
+double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
ERR_FAIL_INDEX_V(p_input, inputs.size(), 0);
ERR_FAIL_COND_V(!state, 0);
@@ -163,7 +163,7 @@ real_t AnimationNode::blend_input(int p_input, real_t p_time, bool p_seek, real_
//inputs.write[p_input].last_pass = state->last_pass;
real_t activity = 0.0;
- real_t ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity);
+ double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity);
Vector<AnimationTree::Activity> *activity_ptr = state->tree->input_activity_map.getptr(base_path);
@@ -174,11 +174,11 @@ real_t AnimationNode::blend_input(int p_input, real_t p_time, bool p_seek, real_
return ret;
}
-real_t AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
+double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_blend, p_filter, p_optimize);
}
-real_t AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
+double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
ERR_FAIL_COND_V(!p_node.is_valid(), 0);
ERR_FAIL_COND_V(!state, 0);
@@ -796,7 +796,7 @@ void AnimationTree::_clear_caches() {
cache_valid = false;
}
-void AnimationTree::_process_graph(real_t p_delta) {
+void AnimationTree::_process_graph(double p_delta) {
_update_properties(); //if properties need updating, update them
//check all tracks, see if they need modification
@@ -1335,10 +1335,10 @@ void AnimationTree::_process_graph(real_t p_delta) {
t->playing = false;
playing_caches.erase(t);
} else {
- real_t start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ double start_ofs = a->audio_track_get_key_start_offset(i, idx);
start_ofs += time - a->track_get_key_time(i, idx);
- real_t end_ofs = a->audio_track_get_key_end_offset(i, idx);
- real_t len = stream->get_length();
+ double end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ double len = stream->get_length();
if (start_ofs > len - end_ofs) {
t->object->call("stop");
@@ -1374,9 +1374,9 @@ void AnimationTree::_process_graph(real_t p_delta) {
t->playing = false;
playing_caches.erase(t);
} else {
- real_t start_ofs = a->audio_track_get_key_start_offset(i, idx);
- real_t end_ofs = a->audio_track_get_key_end_offset(i, idx);
- real_t len = stream->get_length();
+ double start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ double end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ double len = stream->get_length();
t->object->call("set_stream", stream);
t->object->call("play", start_ofs);
@@ -1407,7 +1407,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
}
} else if (t->len > 0) {
- real_t len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
+ double len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
if (len > t->len) {
stop = true;
@@ -1455,7 +1455,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
Ref<Animation> anim = player2->get_animation(anim_name);
- real_t at_anim_pos = 0.0;
+ double at_anim_pos = 0.0;
switch (anim->get_loop_mode()) {
case Animation::LoopMode::LOOP_NONE: {
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 11c9bcd4ef..705ee91c76 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -83,7 +83,7 @@ public:
Vector<real_t> blends;
State *state = nullptr;
- real_t _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, real_t p_time, bool p_seek, const Vector<StringName> &p_connections);
+ double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections);
//all this is temporary
StringName base_path;
@@ -96,12 +96,12 @@ public:
Array _get_filters() const;
void _set_filters(const Array &p_filters);
friend class AnimationNodeBlendTree;
- real_t _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
+ double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
protected:
- void blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
- real_t blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
- real_t blend_input(int p_input, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+ void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
+ double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+ double blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
void make_invalid(const String &p_reason);
@@ -234,8 +234,8 @@ private:
struct TrackCacheAudio : public TrackCache {
bool playing = false;
- real_t start = 0.0;
- real_t len = 0.0;
+ double start = 0.0;
+ double len = 0.0;
TrackCacheAudio() {
type = Animation::TYPE_AUDIO;
@@ -265,7 +265,7 @@ private:
void _clear_caches();
bool _update_caches(AnimationPlayer *player);
- void _process_graph(real_t p_delta);
+ void _process_graph(double p_delta);
uint64_t setup_pass = 1;
uint64_t process_pass = 1;
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index a37c6f5355..a2fed718be 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -249,8 +249,6 @@ bool Tween::custom_step(float p_delta) {
}
bool Tween::step(float p_delta) {
- ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners.");
-
if (dead) {
return false;
}
@@ -271,6 +269,7 @@ bool Tween::step(float p_delta) {
}
if (!started) {
+ ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners.");
current_step = 0;
loops_done = 0;
start_tweeners();
@@ -284,6 +283,10 @@ bool Tween::step(float p_delta) {
float step_delta = rem_delta;
step_active = false;
+#ifdef DEBUG_ENABLED
+ float prev_delta = rem_delta;
+#endif
+
for (Ref<Tweener> &tweener : tweeners.write[current_step]) {
// Modified inside Tweener.step().
float temp_delta = rem_delta;
@@ -313,6 +316,12 @@ bool Tween::step(float p_delta) {
start_tweeners();
}
}
+
+#ifdef DEBUG_ENABLED
+ if (Math::is_equal_approx(rem_delta, prev_delta) && running && loops <= 0) {
+ ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info.");
+ }
+#endif
}
return true;
diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h
index 432317a423..c8298391bb 100644
--- a/scene/debugger/scene_debugger.h
+++ b/scene/debugger/scene_debugger.h
@@ -37,6 +37,7 @@
#include "core/variant/array.h"
class Script;
+class Node;
class SceneDebugger {
public:
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index eee7663b09..da2ef6c5ec 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -382,8 +382,11 @@ Ref<ButtonGroup> BaseButton::get_button_group() const {
}
void BaseButton::set_shortcut_context(Node *p_node) {
- ERR_FAIL_NULL_MSG(p_node, "Shortcut context node can't be null.");
- shortcut_context = p_node->get_instance_id();
+ if (p_node != nullptr) {
+ shortcut_context = p_node->get_instance_id();
+ } else {
+ shortcut_context = ObjectID();
+ }
}
Node *BaseButton::get_shortcut_context() const {
@@ -400,7 +403,7 @@ bool BaseButton::_is_focus_owner_in_shorcut_context() const {
}
Node *ctx_node = get_shortcut_context();
- Control *vp_focus = get_focus_owner();
+ Control *vp_focus = get_viewport() ? get_viewport()->gui_get_focus_owner() : nullptr;
// If the context is valid and the viewport focus is valid, check if the context is the focus or is a parent of it.
return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_ancestor_of(vp_focus));
@@ -441,10 +444,11 @@ void BaseButton::_bind_methods() {
ADD_SIGNAL(MethodInfo("button_up"));
ADD_SIGNAL(MethodInfo("button_down"));
ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "button_pressed")));
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "button_pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside");
@@ -500,7 +504,8 @@ BaseButton *ButtonGroup::get_pressed_button() {
void ButtonGroup::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pressed_button"), &ButtonGroup::get_pressed_button);
ClassDB::bind_method(D_METHOD("get_buttons"), &ButtonGroup::_get_buttons);
- ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button")));
+
+ ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button", PROPERTY_HINT_RESOURCE_TYPE, "BaseButton")));
}
ButtonGroup::ButtonGroup() {
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 046b9c18c7..3ed1b873af 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -197,6 +197,8 @@ void Button::_notification(int p_what) {
color = get_theme_color(SNAME("font_disabled_color"));
if (has_theme_color(SNAME("icon_disabled_color"))) {
color_icon = get_theme_color(SNAME("icon_disabled_color"));
+ } else {
+ color_icon.a = 0.4;
}
} break;
@@ -232,9 +234,6 @@ void Button::_notification(int p_what) {
}
if (!_icon.is_null()) {
int valign = size.height - style->get_minimum_size().y;
- if (is_disabled()) {
- color_icon.a = 0.4;
- }
float icon_ofs_region = 0.0;
Point2 style_offset;
@@ -569,7 +568,7 @@ void Button::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 398b909195..22def607ed 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -92,7 +92,7 @@ void CodeEdit::_notification(int p_what) {
if (line_length_guideline_columns.size() > 0) {
const int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width();
const int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0);
- const int char_size = (int)font->get_char_size('0', 0, font_size).width;
+ const int char_size = Math::round(font->get_char_size('0', 0, font_size).width);
for (int i = 0; i < line_length_guideline_columns.size(); i++) {
const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll();
@@ -571,6 +571,8 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
// Overridable actions
void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
bool had_selection = has_selection();
+ String selection_text = (had_selection ? get_selected_text() : "");
+
if (had_selection) {
begin_complex_operation();
delete_selection();
@@ -591,27 +593,38 @@ void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
if (auto_brace_completion_enabled) {
int cl = get_caret_line();
int cc = get_caret_column();
- int caret_move_offset = 1;
-
- int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
- if (has_string_delimiter(chr) && cc > 0 && _is_char(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
- insert_text_at_caret(chr);
- } else if (cc < get_line(cl).length() && _is_char(get_line(cl)[cc])) {
- insert_text_at_caret(chr);
- } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
- caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
- } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
+ if (had_selection) {
insert_text_at_caret(chr);
+
+ String close_key = get_auto_brace_completion_close_key(chr);
+ if (!close_key.is_empty()) {
+ insert_text_at_caret(selection_text + close_key);
+ set_caret_column(get_caret_column() - 1);
+ }
} else {
- insert_text_at_caret(chr);
+ int caret_move_offset = 1;
+
+ int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
+
+ if (has_string_delimiter(chr) && cc > 0 && _is_char(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
+ insert_text_at_caret(chr);
+ } else if (cc < get_line(cl).length() && _is_char(get_line(cl)[cc])) {
+ insert_text_at_caret(chr);
+ } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
+ caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
+ } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
+ insert_text_at_caret(chr);
+ } else {
+ insert_text_at_caret(chr);
- int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
- if (pre_brace_pair != -1) {
- insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
+ int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
+ if (pre_brace_pair != -1) {
+ insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
+ }
}
+ set_caret_column(cc + caret_move_offset);
}
- set_caret_column(cc + caret_move_offset);
} else {
insert_text_at_caret(chr);
}
@@ -937,8 +950,10 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
return;
}
- const int cc = get_caret_column();
+ /* When not splitting the line, we need to factor in indentation from the end of the current line. */
+ const int cc = p_split_current_line ? get_caret_column() : get_line(get_caret_line()).length();
const int cl = get_caret_line();
+
const String line = get_line(cl);
String ins = "\n";
@@ -1012,6 +1027,8 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
bool first_line = false;
if (!p_split_current_line) {
+ deselect();
+
if (p_above) {
if (cl > 0) {
set_caret_line(cl - 1, false);
@@ -1793,7 +1810,7 @@ void CodeEdit::request_code_completion(bool p_force) {
}
if (p_force) {
- emit_signal(SNAME("request_code_completion"));
+ emit_signal(SNAME("code_completion_requested"));
return;
}
@@ -1801,9 +1818,9 @@ void CodeEdit::request_code_completion(bool p_force) {
int ofs = CLAMP(get_caret_column(), 0, line.length());
if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || _is_char(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) {
- emit_signal(SNAME("request_code_completion"));
+ emit_signal(SNAME("code_completion_requested"));
} else if (ofs > 1 && line[ofs - 1] == ' ' && code_completion_prefixes.has(line[ofs - 2])) {
- emit_signal(SNAME("request_code_completion"));
+ emit_signal(SNAME("code_completion_requested"));
}
}
@@ -2085,8 +2102,6 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auto_brace_completion_close_key", "open_key"), &CodeEdit::get_auto_brace_completion_close_key);
/* Main Gutter */
- ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback);
-
ClassDB::bind_method(D_METHOD("set_draw_breakpoints_gutter", "enable"), &CodeEdit::set_draw_breakpoints_gutter);
ClassDB::bind_method(D_METHOD("is_drawing_breakpoints_gutter"), &CodeEdit::is_drawing_breakpoints_gutter);
@@ -2115,16 +2130,12 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_executing_lines"), &CodeEdit::get_executing_lines);
/* Line numbers */
- ClassDB::bind_method(D_METHOD("_line_number_draw_callback"), &CodeEdit::_line_number_draw_callback);
-
ClassDB::bind_method(D_METHOD("set_draw_line_numbers", "enable"), &CodeEdit::set_draw_line_numbers);
ClassDB::bind_method(D_METHOD("is_draw_line_numbers_enabled"), &CodeEdit::is_draw_line_numbers_enabled);
ClassDB::bind_method(D_METHOD("set_line_numbers_zero_padded", "enable"), &CodeEdit::set_line_numbers_zero_padded);
ClassDB::bind_method(D_METHOD("is_line_numbers_zero_padded"), &CodeEdit::is_line_numbers_zero_padded);
/* Fold Gutter */
- ClassDB::bind_method(D_METHOD("_fold_gutter_draw_callback"), &CodeEdit::_fold_gutter_draw_callback);
-
ClassDB::bind_method(D_METHOD("set_draw_fold_gutter", "enable"), &CodeEdit::set_draw_fold_gutter);
ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &CodeEdit::is_drawing_fold_gutter);
@@ -2267,7 +2278,7 @@ void CodeEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "line")));
/* Code Completion */
- ADD_SIGNAL(MethodInfo("request_code_completion"));
+ ADD_SIGNAL(MethodInfo("code_completion_requested"));
/* Symbol lookup */
ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::INT, "column")));
@@ -2859,7 +2870,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
completion_options_casei.push_back(option);
} else if (s.is_subsequence_of(option.display)) {
completion_options_subseq.push_back(option);
- } else if (s.is_subsequence_ofi(option.display)) {
+ } else if (s.is_subsequence_ofn(option.display)) {
completion_options_subseq_casei.push_back(option);
}
@@ -3084,7 +3095,7 @@ CodeEdit::CodeEdit() {
set_gutter_draw(gutter_idx, false);
set_gutter_overwritable(gutter_idx, true);
set_gutter_type(gutter_idx, GUTTER_TYPE_CUSTOM);
- set_gutter_custom_draw(gutter_idx, this, "_main_gutter_draw_callback");
+ set_gutter_custom_draw(gutter_idx, callable_mp(this, &CodeEdit::_main_gutter_draw_callback));
gutter_idx++;
/* Line numbers */
@@ -3092,7 +3103,7 @@ CodeEdit::CodeEdit() {
set_gutter_name(gutter_idx, "line_numbers");
set_gutter_draw(gutter_idx, false);
set_gutter_type(gutter_idx, GUTTER_TYPE_CUSTOM);
- set_gutter_custom_draw(gutter_idx, this, "_line_number_draw_callback");
+ set_gutter_custom_draw(gutter_idx, callable_mp(this, &CodeEdit::_line_number_draw_callback));
gutter_idx++;
/* Fold Gutter */
@@ -3100,7 +3111,7 @@ CodeEdit::CodeEdit() {
set_gutter_name(gutter_idx, "fold_gutter");
set_gutter_draw(gutter_idx, false);
set_gutter_type(gutter_idx, GUTTER_TYPE_CUSTOM);
- set_gutter_custom_draw(gutter_idx, this, "_fold_gutter_draw_callback");
+ set_gutter_custom_draw(gutter_idx, callable_mp(this, &CodeEdit::_fold_gutter_draw_callback));
gutter_idx++;
connect("lines_edited_from", callable_mp(this, &CodeEdit::_lines_edited_from));
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 69e6d74292..fdae8e2f1f 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -240,7 +240,7 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
return false;
}
- if (p_value.get_type() == Variant::NIL) {
+ if (p_value.get_type() == Variant::NIL || (p_value.get_type() == Variant::OBJECT && (Object *)p_value == nullptr)) {
if (name.begins_with("theme_override_icons/") || name.begins_with("custom_icons/")) {
String dname = name.get_slicec('/', 1);
if (data.icon_override.has(dname)) {
@@ -576,6 +576,11 @@ void Control::_update_canvas_item_transform() {
Transform2D xform = _get_internal_transform();
xform[2] += get_position();
+ // We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
+ if (is_inside_tree() && Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) {
+ xform[2] = xform[2].round();
+ }
+
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), xform);
}
@@ -733,8 +738,10 @@ void Control::_notification(int p_notification) {
} break;
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
- data.is_rtl_dirty = true;
- _size_changed();
+ if (is_inside_tree()) {
+ data.is_rtl_dirty = true;
+ _size_changed();
+ }
} break;
}
}
@@ -1306,7 +1313,7 @@ Rect2 Control::get_parent_anchorable_rect() const {
#ifdef TOOLS_ENABLED
Node *edited_root = get_tree()->get_edited_scene_root();
if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) {
- parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
} else {
parent_rect = get_viewport()->get_visible_rect();
}
@@ -2189,8 +2196,7 @@ void Control::release_focus() {
return;
}
- get_viewport()->_gui_remove_focus();
- update();
+ get_viewport()->gui_release_focus();
}
bool Control::is_top_level_control() const {
@@ -2593,11 +2599,6 @@ Control::MouseFilter Control::get_mouse_filter() const {
return data.mouse_filter;
}
-Control *Control::get_focus_owner() const {
- ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
- return get_viewport()->_gui_get_focus_owner();
-}
-
void Control::warp_mouse(const Point2 &p_to_pos) {
ERR_FAIL_COND(!is_inside_tree());
get_viewport()->warp_mouse(get_global_transform().xform(p_to_pos));
@@ -2887,7 +2888,6 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("release_focus"), &Control::release_focus);
ClassDB::bind_method(D_METHOD("find_prev_valid_focus"), &Control::find_prev_valid_focus);
ClassDB::bind_method(D_METHOD("find_next_valid_focus"), &Control::find_next_valid_focus);
- ClassDB::bind_method(D_METHOD("get_focus_owner"), &Control::get_focus_owner);
ClassDB::bind_method(D_METHOD("set_h_size_flags", "flags"), &Control::set_h_size_flags);
ClassDB::bind_method(D_METHOD("get_h_size_flags"), &Control::get_h_size_flags);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index bf79f790e7..962135280f 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -462,8 +462,6 @@ public:
void set_focus_previous(const NodePath &p_prev);
NodePath get_focus_previous() const;
- Control *get_focus_owner() const;
-
void set_mouse_filter(MouseFilter p_filter);
MouseFilter get_mouse_filter() const;
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index f9b6b1274d..dad84461f4 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -42,6 +42,17 @@ FileDialog::RegisterFunc FileDialog::unregister_func = nullptr;
void FileDialog::popup_file_dialog() {
popup_centered_clamped(Size2i(700, 500), 0.8f);
+ _focus_file_text();
+}
+
+void FileDialog::_focus_file_text() {
+ int lp = file->get_text().rfind(".");
+ if (lp != -1) {
+ file->select(0, lp);
+ if (file->is_inside_tree() && !get_tree()->is_node_being_edited(file)) {
+ file->grab_focus();
+ }
+ }
}
VBoxContainer *FileDialog::get_vbox() {
@@ -99,6 +110,9 @@ void FileDialog::_notification(int p_what) {
show_hidden->set_icon(vbox->get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog")));
_theme_changed();
}
+ if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
+ update_filters();
+ }
}
void FileDialog::unhandled_input(const Ref<InputEvent> &p_event) {
@@ -155,7 +169,14 @@ void FileDialog::update_dir() {
dir->set_text(dir_access->get_current_dir(false));
if (drives->is_visible()) {
- drives->select(dir_access->get_current_drive());
+ if (dir_access->get_current_dir().is_network_share_path()) {
+ _update_drives(false);
+ drives->add_item(RTR("Network"));
+ drives->set_item_disabled(drives->get_item_count() - 1, true);
+ drives->select(drives->get_item_count() - 1);
+ } else {
+ drives->select(dir_access->get_current_drive());
+ }
}
// Deselect any item, to make "Select Current Folder" button text by default.
@@ -627,7 +648,7 @@ void FileDialog::update_filters() {
all_filters += ", ...";
}
- filter->add_item(String(TTRC("All Recognized")) + " (" + all_filters + ")");
+ filter->add_item(RTR("All Recognized") + " (" + all_filters + ")");
}
for (int i = 0; i < filters.size(); i++) {
String flt = filters[i].get_slice(";", 0).strip_edges();
@@ -639,7 +660,7 @@ void FileDialog::update_filters() {
}
}
- filter->add_item(TTRC("All Files (*)"));
+ filter->add_item(RTR("All Files") + " (*)");
}
void FileDialog::clear_filters() {
@@ -688,13 +709,7 @@ void FileDialog::set_current_file(const String &p_file) {
file->set_text(p_file);
update_dir();
invalidate();
- int lp = p_file.rfind(".");
- if (lp != -1) {
- file->select(0, lp);
- if (file->is_inside_tree() && !get_tree()->is_node_being_edited(file)) {
- file->grab_focus();
- }
- }
+ _focus_file_text();
}
void FileDialog::set_current_path(const String &p_path) {
@@ -838,7 +853,7 @@ void FileDialog::_select_drive(int p_idx) {
_push_history();
}
-void FileDialog::_update_drives() {
+void FileDialog::_update_drives(bool p_select) {
int dc = dir_access->get_drive_count();
if (dc == 0 || access != ACCESS_FILESYSTEM) {
drives->hide();
@@ -856,7 +871,9 @@ void FileDialog::_update_drives() {
drives->add_item(dir_access->get_drive(i));
}
- drives->select(dir_access->get_current_drive());
+ if (p_select) {
+ drives->select(dir_access->get_current_drive());
+ }
}
}
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 782d11afe1..36a6b262b0 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -113,6 +113,8 @@ private:
void update_file_list();
void update_filters();
+ void _focus_file_text();
+
void _tree_multi_selected(Object *p_object, int p_cell, bool p_selected);
void _tree_selected();
@@ -130,7 +132,7 @@ private:
void _go_back();
void _go_forward();
- void _update_drives();
+ void _update_drives(bool p_select = true);
virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp
new file mode 100644
index 0000000000..d1ac60b325
--- /dev/null
+++ b/scene/gui/flow_container.cpp
@@ -0,0 +1,252 @@
+/*************************************************************************/
+/* flow_container.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/gui/container.h"
+
+#include "flow_container.h"
+
+struct _LineData {
+ int child_count = 0;
+ int min_line_height = 0;
+ int min_line_length = 0;
+ int stretch_avail = 0;
+ float stretch_ratio_total = 0;
+};
+
+void FlowContainer::_resort() {
+ int separation_horizontal = get_theme_constant(SNAME("hseparation"));
+ int separation_vertical = get_theme_constant(SNAME("vseparation"));
+
+ bool rtl = is_layout_rtl();
+
+ Map<Control *, Size2i> children_minsize_cache;
+
+ Vector<_LineData> lines_data;
+
+ Vector2i ofs;
+ int line_height = 0;
+ int line_length = 0;
+ float line_stretch_ratio_total = 0;
+ int current_container_size = vertical ? get_rect().size.y : get_rect().size.x;
+ int children_in_current_line = 0;
+
+ // First pass for line wrapping and minimum size calculation.
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *child = Object::cast_to<Control>(get_child(i));
+ if (!child || !child->is_visible_in_tree()) {
+ continue;
+ }
+ if (child->is_set_as_top_level()) {
+ continue;
+ }
+
+ Size2i child_msc = child->get_combined_minimum_size();
+
+ if (vertical) { /* VERTICAL */
+ if (children_in_current_line > 0) {
+ ofs.y += separation_vertical;
+ }
+ if (ofs.y + child_msc.y > current_container_size) {
+ line_length = ofs.y - separation_vertical;
+ lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+ // Move in new column (vertical line).
+ ofs.x += line_height + separation_horizontal;
+ ofs.y = 0;
+ line_height = 0;
+ line_stretch_ratio_total = 0;
+ children_in_current_line = 0;
+ }
+
+ line_height = MAX(line_height, child_msc.x);
+ if (child->get_v_size_flags() & SIZE_EXPAND) {
+ line_stretch_ratio_total += child->get_stretch_ratio();
+ }
+ ofs.y += child_msc.y;
+
+ } else { /* HORIZONTAL */
+ if (children_in_current_line > 0) {
+ ofs.x += separation_horizontal;
+ }
+ if (ofs.x + child_msc.x > current_container_size) {
+ line_length = ofs.x - separation_horizontal;
+ lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+ // Move in new line.
+ ofs.y += line_height + separation_vertical;
+ ofs.x = 0;
+ line_height = 0;
+ line_stretch_ratio_total = 0;
+ children_in_current_line = 0;
+ }
+
+ line_height = MAX(line_height, child_msc.y);
+ if (child->get_h_size_flags() & SIZE_EXPAND) {
+ line_stretch_ratio_total += child->get_stretch_ratio();
+ }
+ ofs.x += child_msc.x;
+ }
+
+ children_minsize_cache[child] = child_msc;
+ children_in_current_line++;
+ }
+ line_length = vertical ? (ofs.y) : (ofs.x);
+ lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+ // Second pass for in-line expansion and alignment.
+
+ int current_line_idx = 0;
+ int child_idx_in_line = 0;
+
+ ofs.x = 0;
+ ofs.y = 0;
+
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *child = Object::cast_to<Control>(get_child(i));
+ if (!child || !child->is_visible_in_tree()) {
+ continue;
+ }
+ if (child->is_set_as_top_level()) {
+ continue;
+ }
+ Size2i child_size = children_minsize_cache[child];
+
+ _LineData line_data = lines_data[current_line_idx];
+ if (child_idx_in_line >= lines_data[current_line_idx].child_count) {
+ current_line_idx++;
+ child_idx_in_line = 0;
+ if (vertical) {
+ ofs.x += line_data.min_line_height + separation_horizontal;
+ ofs.y = 0;
+ } else {
+ ofs.x = 0;
+ ofs.y += line_data.min_line_height + separation_vertical;
+ }
+ line_data = lines_data[current_line_idx];
+ }
+
+ if (vertical) { /* VERTICAL */
+ if (child->get_h_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
+ child_size.width = line_data.min_line_height;
+ }
+
+ if (child->get_v_size_flags() & SIZE_EXPAND) {
+ int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
+ child_size.height += stretch;
+ }
+
+ } else { /* HORIZONTAL */
+ if (child->get_v_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
+ child_size.height = line_data.min_line_height;
+ }
+
+ if (child->get_h_size_flags() & SIZE_EXPAND) {
+ int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
+ child_size.width += stretch;
+ }
+ }
+
+ Rect2 child_rect = Rect2(ofs, child_size);
+ if (rtl) {
+ child_rect.position.x = get_rect().size.x - child_rect.position.x - child_rect.size.width;
+ }
+
+ fit_child_in_rect(child, child_rect);
+
+ if (vertical) { /* VERTICAL */
+ ofs.y += child_size.height + separation_vertical;
+ } else { /* HORIZONTAL */
+ ofs.x += child_size.width + separation_horizontal;
+ }
+
+ child_idx_in_line++;
+ }
+ cached_size = (vertical ? ofs.x : ofs.y) + line_height;
+ cached_line_count = lines_data.size();
+}
+
+Size2 FlowContainer::get_minimum_size() const {
+ Size2i minimum;
+
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c) {
+ continue;
+ }
+ if (c->is_set_as_top_level()) {
+ continue;
+ }
+
+ if (!c->is_visible()) {
+ continue;
+ }
+
+ Size2i size = c->get_combined_minimum_size();
+
+ if (vertical) { /* VERTICAL */
+ minimum.height = MAX(minimum.height, size.height);
+ minimum.width = cached_size;
+
+ } else { /* HORIZONTAL */
+ minimum.width = MAX(minimum.width, size.width);
+ minimum.height = cached_size;
+ }
+ }
+
+ return minimum;
+}
+
+void FlowContainer::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_SORT_CHILDREN: {
+ _resort();
+ update_minimum_size();
+ } break;
+ case NOTIFICATION_THEME_CHANGED: {
+ update_minimum_size();
+ } break;
+ case NOTIFICATION_TRANSLATION_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ queue_sort();
+ } break;
+ }
+}
+
+int FlowContainer::get_line_count() const {
+ return cached_line_count;
+}
+
+FlowContainer::FlowContainer(bool p_vertical) {
+ vertical = p_vertical;
+}
+
+void FlowContainer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count);
+}
diff --git a/modules/pvr/texture_loader_pvr.h b/scene/gui/flow_container.h
index 06e8e91ba2..e3ed423ae1 100644
--- a/modules/pvr/texture_loader_pvr.h
+++ b/scene/gui/flow_container.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* texture_loader_pvr.h */
+/* flow_container.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,21 +28,49 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef TEXTURE_LOADER_PVR_H
-#define TEXTURE_LOADER_PVR_H
+#ifndef FLOW_CONTAINER_H
+#define FLOW_CONTAINER_H
-#include "core/io/resource_loader.h"
-#include "scene/resources/texture.h"
+class Container;
+
+class FlowContainer : public Container {
+ GDCLASS(FlowContainer, Container);
+
+private:
+ int cached_size = 0;
+ int cached_line_count = 0;
+
+ bool vertical = false;
+
+ void _resort();
+
+protected:
+ void _notification(int p_what);
+
+ static void _bind_methods();
-class ResourceFormatPVR : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path, Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
+ int get_line_count() const;
- ResourceFormatPVR();
- virtual ~ResourceFormatPVR() {}
+ virtual Size2 get_minimum_size() const override;
+
+ FlowContainer(bool p_vertical = false);
+};
+
+class HFlowContainer : public FlowContainer {
+ GDCLASS(HFlowContainer, FlowContainer);
+
+public:
+ HFlowContainer() :
+ FlowContainer(false) {}
+};
+
+class VFlowContainer : public FlowContainer {
+ GDCLASS(VFlowContainer, FlowContainer);
+
+public:
+ VFlowContainer() :
+ FlowContainer(true) {}
};
-#endif // TEXTURE_LOADER_PVR_H
+#endif // FLOW_CONTAINER_H
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index 407f61f7c1..67531d4f4a 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -33,7 +33,6 @@
#include "scene/gui/color_picker.h"
#include "scene/gui/popup.h"
-#include "scene/resources/default_theme/theme_data.h"
#include "scene/resources/gradient.h"
class GradientEdit : public Control {
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 79b73f7cc3..95575a8226 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -1070,7 +1070,9 @@ void GraphEdit::set_selected(Node *p_child) {
void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
ERR_FAIL_COND(p_ev.is_null());
- panner->gui_input(p_ev);
+ if (panner->gui_input(p_ev, warped_panning ? get_global_rect() : Rect2())) {
+ return;
+ }
Ref<InputEventMouseMotion> mm = p_ev;
@@ -1272,7 +1274,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (_filter_input(b->get_position())) {
return;
}
- if (Input::get_singleton()->is_key_pressed(Key::SPACE)) {
+ if (panner->is_panning()) {
return;
}
@@ -1354,7 +1356,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
}
-void GraphEdit::_scroll_callback(Vector2 p_scroll_vec) {
+void GraphEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) {
if (p_scroll_vec.x != 0) {
h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * Math::abs(p_scroll_vec.x) / 8) * SIGN(p_scroll_vec.x));
} else {
@@ -1367,7 +1369,7 @@ void GraphEdit::_pan_callback(Vector2 p_scroll_vec) {
v_scroll->set_value(v_scroll->get_value() - p_scroll_vec.y);
}
-void GraphEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin) {
+void GraphEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) {
set_zoom_custom(p_scroll_vec.y < 0 ? zoom * zoom_step : zoom / zoom_step, p_origin);
}
@@ -1678,12 +1680,21 @@ HBoxContainer *GraphEdit::get_zoom_hbox() {
return zoom_hb;
}
+Ref<ViewPanner> GraphEdit::get_panner() {
+ return panner;
+}
+
+void GraphEdit::set_warped_panning(bool p_warped) {
+ warped_panning = p_warped;
+}
+
int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u, const Set<StringName> &r_v) {
switch (p_operation) {
case GraphEdit::IS_EQUAL: {
for (Set<StringName>::Element *E = r_u.front(); E; E = E->next()) {
- if (!r_v.has(E->get()))
+ if (!r_v.has(E->get())) {
return 0;
+ }
}
return r_u.size() == r_v.size();
} break;
@@ -1692,8 +1703,9 @@ int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u,
return 1;
}
for (Set<StringName>::Element *E = r_u.front(); E; E = E->next()) {
- if (!r_v.has(E->get()))
+ if (!r_v.has(E->get())) {
return 0;
+ }
}
return 1;
} break;
@@ -2305,7 +2317,6 @@ GraphEdit::GraphEdit() {
panner.instantiate();
panner->set_callbacks(callable_mp(this, &GraphEdit::_scroll_callback), callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback));
- panner->set_disable_rmb(true);
top_layer = memnew(GraphEditFilter(this));
add_child(top_layer, false, INTERNAL_MODE_BACK);
@@ -2313,6 +2324,7 @@ GraphEdit::GraphEdit() {
top_layer->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
top_layer->connect("draw", callable_mp(this, &GraphEdit::_top_layer_draw));
top_layer->connect("gui_input", callable_mp(this, &GraphEdit::_top_layer_input));
+ top_layer->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
connections_layer = memnew(Control);
add_child(connections_layer, false, INTERNAL_MODE_FRONT);
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 4e998d30a7..da973b46f0 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -130,9 +130,10 @@ private:
float port_grab_distance_vertical;
Ref<ViewPanner> panner;
- void _scroll_callback(Vector2 p_scroll_vec);
+ bool warped_panning = true;
+ void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
void _pan_callback(Vector2 p_scroll_vec);
- void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt);
bool connecting = false;
String connecting_from;
@@ -348,6 +349,8 @@ public:
bool is_connection_lines_antialiased() const;
HBoxContainer *get_zoom_hbox();
+ Ref<ViewPanner> get_panner();
+ void set_warped_panning(bool p_warped);
void arrange_nodes();
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index d6569e3de4..30f6cf4a14 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -1022,7 +1022,7 @@ void GraphNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position_offset"), "set_position_offset", "get_position_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_close"), "set_show_close_button", "is_close_button_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resizable"), "set_resizable", "is_resizable");
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 861b70f17e..852aaaab24 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -82,8 +82,11 @@ void Label::_shape() {
Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label"));
int width = (get_size().width - style->get_minimum_size().width);
- if (dirty) {
- TS->shaped_text_clear(text_rid);
+ if (dirty || font_dirty) {
+ String lang = (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale();
+ if (dirty) {
+ TS->shaped_text_clear(text_rid);
+ }
if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
TS->shaped_text_set_direction(text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
} else {
@@ -92,13 +95,21 @@ void Label::_shape() {
const Ref<Font> &font = get_theme_font(SNAME("font"));
int font_size = get_theme_font_size(SNAME("font_size"));
ERR_FAIL_COND(font.is_null());
- String text = (uppercase) ? xl_text.to_upper() : xl_text;
+ String text = (uppercase) ? TS->string_to_upper(xl_text, lang) : xl_text;
if (visible_chars >= 0 && visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
text = text.substr(0, visible_chars);
}
- TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
+ if (dirty) {
+ TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang);
+ } else {
+ int spans = TS->shaped_get_span_count(text_rid);
+ for (int i = 0; i < spans; i++) {
+ TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features);
+ }
+ }
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text));
dirty = false;
+ font_dirty = false;
lines_dirty = true;
}
@@ -275,7 +286,7 @@ void Label::_notification(int p_what) {
RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
}
- if (dirty || lines_dirty) {
+ if (dirty || font_dirty || lines_dirty) {
_shape();
}
@@ -520,7 +531,7 @@ void Label::_notification(int p_what) {
}
if (p_what == NOTIFICATION_THEME_CHANGED) {
- dirty = true;
+ font_dirty = true;
update();
}
if (p_what == NOTIFICATION_RESIZED) {
@@ -530,7 +541,7 @@ void Label::_notification(int p_what) {
Size2 Label::get_minimum_size() const {
// don't want to mutable everything
- if (dirty || lines_dirty) {
+ if (dirty || font_dirty || lines_dirty) {
const_cast<Label *>(this)->_shape();
}
@@ -554,7 +565,7 @@ int Label::get_line_count() const {
if (!is_inside_tree()) {
return 1;
}
- if (dirty || lines_dirty) {
+ if (dirty || font_dirty || lines_dirty) {
const_cast<Label *>(this)->_shape();
}
@@ -629,7 +640,7 @@ void Label::set_text_direction(Control::TextDirection p_text_direction) {
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (text_direction != p_text_direction) {
text_direction = p_text_direction;
- dirty = true;
+ font_dirty = true;
update();
}
}
@@ -637,7 +648,7 @@ void Label::set_text_direction(Control::TextDirection p_text_direction) {
void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
if (st_parser != p_parser) {
st_parser = p_parser;
- dirty = true;
+ font_dirty = true;
update();
}
}
@@ -648,7 +659,7 @@ Control::StructuredTextParser Label::get_structured_text_bidi_override() const {
void Label::set_structured_text_bidi_override_options(Array p_args) {
st_args = p_args;
- dirty = true;
+ font_dirty = true;
update();
}
@@ -662,7 +673,7 @@ Control::TextDirection Label::get_text_direction() const {
void Label::clear_opentype_features() {
opentype_features.clear();
- dirty = true;
+ font_dirty = true;
update();
}
@@ -670,7 +681,7 @@ void Label::set_opentype_feature(const String &p_name, int p_value) {
int32_t tag = TS->name_to_tag(p_name);
if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
opentype_features[tag] = p_value;
- dirty = true;
+ font_dirty = true;
update();
}
}
@@ -797,7 +808,7 @@ int Label::get_max_lines_visible() const {
}
int Label::get_total_character_count() const {
- if (dirty || lines_dirty) {
+ if (dirty || font_dirty || lines_dirty) {
const_cast<Label *>(this)->_shape();
}
@@ -813,13 +824,13 @@ bool Label::_set(const StringName &p_name, const Variant &p_value) {
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
- dirty = true;
+ font_dirty = true;
update();
}
} else {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
- dirty = true;
+ font_dirty = true;
update();
}
}
@@ -915,7 +926,7 @@ void Label::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
ADD_GROUP("Locale", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 354e9c664d..0b931b3084 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -73,6 +73,7 @@ private:
bool lines_dirty = true;
bool dirty = true;
+ bool font_dirty = true;
RID text_rid;
Vector<RID> lines_rid;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index f7d6850a88..3aae3377bc 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -752,7 +752,7 @@ void LineEdit::_notification(int p_what) {
// Draw placeholder color.
if (using_placeholder) {
- font_color.a *= placeholder_alpha;
+ font_color = get_theme_color(SNAME("font_placeholder_color"));
}
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
@@ -1476,15 +1476,6 @@ String LineEdit::get_placeholder() const {
return placeholder;
}
-void LineEdit::set_placeholder_alpha(float p_alpha) {
- placeholder_alpha = p_alpha;
- update();
-}
-
-float LineEdit::get_placeholder_alpha() const {
- return placeholder_alpha;
-}
-
void LineEdit::set_caret_column(int p_column) {
if (p_column > (int)text.length()) {
p_column = text.length();
@@ -2245,8 +2236,6 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &LineEdit::get_structured_text_bidi_override_options);
ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &LineEdit::set_placeholder);
ClassDB::bind_method(D_METHOD("get_placeholder"), &LineEdit::get_placeholder);
- ClassDB::bind_method(D_METHOD("set_placeholder_alpha", "alpha"), &LineEdit::set_placeholder_alpha);
- ClassDB::bind_method(D_METHOD("get_placeholder_alpha"), &LineEdit::get_placeholder_alpha);
ClassDB::bind_method(D_METHOD("set_caret_column", "position"), &LineEdit::set_caret_column);
ClassDB::bind_method(D_METHOD("get_caret_column"), &LineEdit::get_caret_column);
ClassDB::bind_method(D_METHOD("get_scroll_offset"), &LineEdit::get_scroll_offset);
@@ -2328,6 +2317,7 @@ void LineEdit::_bind_methods() {
BIND_ENUM_CONSTANT(MENU_MAX);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_max_length", "get_max_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
@@ -2344,14 +2334,11 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
- ADD_GROUP("Placeholder", "placeholder_");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
ADD_GROUP("Caret", "caret_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed");
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 0c313f71c2..1519c09d73 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -82,7 +82,6 @@ private:
String placeholder;
String placeholder_translated;
String secret_character = "*";
- float placeholder_alpha = 0.6;
String ime_text;
Point2 ime_selection;
@@ -262,9 +261,6 @@ public:
void set_placeholder(String p_text);
String get_placeholder() const;
- void set_placeholder_alpha(float p_alpha);
- float get_placeholder_alpha() const;
-
void set_caret_column(int p_column);
int get_caret_column() const;
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index 67ffa2c7ed..0ff05faf85 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -308,7 +308,7 @@ void LinkButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode");
ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index f7805136f9..a985a9d031 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -98,7 +98,13 @@ void MenuButton::pressed() {
popup->set_position(gp);
popup->set_parent_rect(Rect2(Point2(gp - popup->get_position()), size));
- popup->take_mouse_focus();
+ // If not triggered by the mouse, start the popup with its first item selected.
+ if (popup->get_item_count() > 0 &&
+ ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) ||
+ (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept")))) {
+ popup->set_current_index(0);
+ }
+
popup->popup();
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index e955fde43a..9984ab240a 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -32,6 +32,8 @@
#include "core/string/print_string.h"
+static const int NONE_SELECTED = -1;
+
Size2 OptionButton::get_minimum_size() const {
Size2 minsize = Button::get_minimum_size();
@@ -119,7 +121,7 @@ bool OptionButton::_set(const StringName &p_name, const Variant &p_value) {
int idx = components[1].get_slice("_", 1).to_int();
if (idx == current) {
// Force refreshing currently displayed item.
- current = -1;
+ current = NONE_SELECTED;
_select(idx, false);
}
@@ -154,7 +156,7 @@ void OptionButton::_get_property_list(List<PropertyInfo> *p_list) const {
pi.usage &= ~(!popup->is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
- pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater");
+ pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater");
p_list->push_back(pi);
pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i));
@@ -179,6 +181,14 @@ void OptionButton::pressed() {
Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y));
popup->set_size(Size2(size.width, 0));
+
+ // If not triggered by the mouse, start the popup with the checked item selected.
+ if (popup->get_item_count() > 0 &&
+ ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) ||
+ (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept")))) {
+ popup->set_current_index(current > -1 ? current : 0);
+ }
+
popup->popup();
}
@@ -233,6 +243,10 @@ Ref<Texture2D> OptionButton::get_item_icon(int p_idx) const {
}
int OptionButton::get_item_id(int p_idx) const {
+ if (p_idx == NONE_SELECTED) {
+ return NONE_SELECTED;
+ }
+
return popup->get_item_id(p_idx);
}
@@ -265,26 +279,33 @@ void OptionButton::add_separator() {
void OptionButton::clear() {
popup->clear();
set_text("");
- current = -1;
+ current = NONE_SELECTED;
}
void OptionButton::_select(int p_which, bool p_emit) {
- if (p_which < 0) {
- return;
- }
if (p_which == current) {
return;
}
- ERR_FAIL_INDEX(p_which, popup->get_item_count());
+ if (p_which == NONE_SELECTED) {
+ for (int i = 0; i < popup->get_item_count(); i++) {
+ popup->set_item_checked(i, false);
+ }
- for (int i = 0; i < popup->get_item_count(); i++) {
- popup->set_item_checked(i, i == p_which);
- }
+ current = NONE_SELECTED;
+ set_text("");
+ set_icon(NULL);
+ } else {
+ ERR_FAIL_INDEX(p_which, popup->get_item_count());
- current = p_which;
- set_text(popup->get_item_text(current));
- set_icon(popup->get_item_icon(current));
+ for (int i = 0; i < popup->get_item_count(); i++) {
+ popup->set_item_checked(i, i == p_which);
+ }
+
+ current = p_which;
+ set_text(popup->get_item_text(current));
+ set_icon(popup->get_item_icon(current));
+ }
if (is_inside_tree() && p_emit) {
emit_signal(SNAME("item_selected"), current);
@@ -292,7 +313,7 @@ void OptionButton::_select(int p_which, bool p_emit) {
}
void OptionButton::_select_int(int p_which) {
- if (p_which < 0 || p_which >= popup->get_item_count()) {
+ if (p_which < NONE_SELECTED || p_which >= popup->get_item_count()) {
return;
}
_select(p_which, false);
@@ -307,10 +328,6 @@ int OptionButton::get_selected() const {
}
int OptionButton::get_selected_id() const {
- int idx = get_selected();
- if (idx < 0) {
- return 0;
- }
return get_item_id(current);
}
@@ -324,6 +341,9 @@ Variant OptionButton::get_selected_metadata() const {
void OptionButton::remove_item(int p_idx) {
popup->remove_item(p_idx);
+ if (current == p_idx) {
+ _select(NONE_SELECTED);
+ }
}
PopupMenu *OptionButton::get_popup() const {
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index f4d45fe1fa..812339dc19 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -192,7 +192,7 @@ void PopupMenu::_activate_submenu(int p_over) {
Popup *submenu_popup = Object::cast_to<Popup>(n);
ERR_FAIL_COND_MSG(!submenu_popup, "Item subnode is not a Popup: " + items[p_over].submenu + ".");
if (submenu_popup->is_visible()) {
- return; //already visible!
+ return; // Already visible.
}
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
@@ -223,24 +223,33 @@ void PopupMenu::_activate_submenu(int p_over) {
submenu_popup->set_close_on_parent_focus(false);
submenu_popup->set_position(submenu_pos);
submenu_popup->set_as_minsize(); // Shrink the popup size to its contents.
- submenu_popup->popup();
- // Set autohide areas
PopupMenu *submenu_pum = Object::cast_to<PopupMenu>(submenu_popup);
- if (submenu_pum) {
- submenu_pum->take_mouse_focus();
- // Make the position of the parent popup relative to submenu popup
- this_rect.position = this_rect.position - submenu_pum->get_position();
-
- // Autohide area above the submenu item
- submenu_pum->clear_autohide_areas();
- submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2));
-
- // If there is an area below the submenu item, add an autohide area there.
- if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) {
- int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height;
- submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
- }
+ if (!submenu_pum) {
+ submenu_popup->popup();
+ return;
+ }
+
+ // If not triggered by the mouse, start the popup with its first item selected.
+ if (submenu_pum->get_item_count() > 0 && Input::get_singleton()->is_action_just_pressed("ui_accept")) {
+ submenu_pum->set_current_index(0);
+ }
+
+ submenu_pum->popup();
+
+ // Set autohide areas.
+
+ // Make the position of the parent popup relative to submenu popup.
+ this_rect.position = this_rect.position - submenu_pum->get_position();
+
+ // Autohide area above the submenu item.
+ submenu_pum->clear_autohide_areas();
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2));
+
+ // If there is an area below the submenu item, add an autohide area there.
+ if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) {
+ int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height;
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
}
}
@@ -810,7 +819,7 @@ void PopupMenu::_notification(int p_what) {
#define ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel) \
item.text = p_label; \
item.xl_text = atr(p_label); \
- item.id = p_id == -1 ? items.size() : p_id; \
+ item.id = p_id == -1 ? items.size() - 1 : p_id; \
item.accel = p_accel;
void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) {
@@ -892,7 +901,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
_ref_shortcut(p_shortcut); \
item.text = p_shortcut->get_name(); \
item.xl_text = atr(item.text); \
- item.id = p_id == -1 ? items.size() : p_id; \
+ item.id = p_id == -1 ? items.size() - 1 : p_id; \
item.shortcut = p_shortcut; \
item.shortcut_is_global = p_global;
@@ -961,7 +970,7 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu,
Item item;
item.text = p_label;
item.xl_text = atr(p_label);
- item.id = p_id == -1 ? items.size() : p_id;
+ item.id = p_id == -1 ? items.size() - 1 : p_id;
item.submenu = p_submenu;
items.push_back(item);
_shape_item(items.size() - 1);
@@ -1269,6 +1278,13 @@ bool PopupMenu::is_item_shortcut_disabled(int p_idx) const {
return items[p_idx].shortcut_is_disabled;
}
+void PopupMenu::set_current_index(int p_idx) {
+ ERR_FAIL_INDEX(p_idx, items.size());
+ mouse_over = p_idx;
+ _scroll_to_item(mouse_over);
+ control->update();
+}
+
int PopupMenu::get_current_index() const {
return mouse_over;
}
@@ -1396,12 +1412,12 @@ void PopupMenu::activate_item(int p_item) {
need_hide = false;
}
- emit_signal(SNAME("id_pressed"), id);
- emit_signal(SNAME("index_pressed"), p_item);
-
if (need_hide) {
hide();
}
+
+ emit_signal(SNAME("id_pressed"), id);
+ emit_signal(SNAME("index_pressed"), p_item);
}
void PopupMenu::remove_item(int p_idx) {
@@ -1740,6 +1756,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_tooltip", "index"), &PopupMenu::get_item_tooltip);
ClassDB::bind_method(D_METHOD("get_item_shortcut", "index"), &PopupMenu::get_item_shortcut);
+ ClassDB::bind_method(D_METHOD("set_current_index", "index"), &PopupMenu::set_current_index);
ClassDB::bind_method(D_METHOD("get_current_index"), &PopupMenu::get_current_index);
ClassDB::bind_method(D_METHOD("set_item_count", "count"), &PopupMenu::set_item_count);
ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 5d6b75cbf5..7c2212d82d 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -212,6 +212,7 @@ public:
Ref<Shortcut> get_item_shortcut(int p_idx) const;
int get_item_state(int p_idx) const;
+ void set_current_index(int p_idx);
int get_current_index() const;
void set_item_count(int p_count);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 863555120d..4865b9770e 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -205,6 +205,49 @@ String RichTextLabel::_letters(int p_num, bool p_capitalize) const {
return s;
}
+void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size) {
+ ERR_FAIL_COND(p_frame == nullptr);
+ ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
+
+ Line &l = p_frame->lines.write[p_line];
+
+ RID t = l.text_buf->get_rid();
+ int spans = TS->shaped_get_span_count(t);
+ for (int i = 0; i < spans; i++) {
+ ItemText *it = (ItemText *)(uint64_t)TS->shaped_get_span_meta(t, i);
+ if (it) {
+ Ref<Font> font = _find_font(it);
+ if (font.is_null()) {
+ font = p_base_font;
+ }
+ int font_size = _find_font_size(it);
+ if (font_size == -1) {
+ font_size = p_base_font_size;
+ }
+ Dictionary font_ftr = _find_font_features(it);
+ TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font_ftr);
+ }
+ }
+
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ switch (it->type) {
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ for (Item *E : table->subitems) {
+ ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E);
+ for (int i = 0; i < frame->lines.size(); i++) {
+ _update_line_font(frame, i, p_base_font, p_base_font_size);
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+}
+
void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width) {
ERR_FAIL_COND(p_frame == nullptr);
ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
@@ -238,7 +281,8 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
ItemFrame *frame = static_cast<ItemFrame *>(E);
for (int i = 0; i < frame->lines.size(); i++) {
- _resize_line(frame, i, p_base_font, p_base_font_size, 1);
+ int w = _find_margin(frame->lines[i].from, p_base_font, p_base_font_size) + 1;
+ _resize_line(frame, i, p_base_font, p_base_font_size, w);
}
idx++;
}
@@ -264,7 +308,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
// Assign actual widths.
for (int i = 0; i < col_count; i++) {
table->columns.write[i].width = table->columns[i].min_width;
- if (table->columns[i].expand && total_ratio > 0) {
+ if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) {
table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
table->total_width += table->columns[i].width + hseparation;
@@ -325,13 +369,16 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
if (i > 0) {
- frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y;
+ frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + frame->lines[i - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
} else {
frame->lines.write[i].offset.y = 0;
}
frame->lines.write[i].offset += offset;
- float h = frame->lines[i].text_buf->get_size().y;
+ float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation"));
+ if (i > 0) {
+ h += get_theme_constant(SNAME("line_separation"));
+ }
if (frame->min_size_over.y > 0) {
h = MAX(h, frame->min_size_over.y);
}
@@ -362,7 +409,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
if (p_line > 0) {
- l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
+ l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + p_frame->lines[p_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
} else {
l.offset.y = 0;
}
@@ -374,9 +421,24 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
Line &l = p_frame->lines.write[p_line];
+ uint16_t autowrap_flags = TextServer::BREAK_MANDATORY;
+ switch (autowrap_mode) {
+ case AUTOWRAP_WORD_SMART:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_WORD:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_ARBITRARY:
+ autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_OFF:
+ break;
+ }
+
// Clear cache.
l.text_buf->clear();
- l.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
+ l.text_buf->set_flags(autowrap_flags | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
l.char_offset = *r_char_offset;
l.char_count = 0;
@@ -445,7 +507,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
remaining_characters -= tx.length();
- l.text_buf->add_string(tx, font, font_size, font_ftr, lang);
+ l.text_buf->add_string(tx, font, font_size, font_ftr, lang, (uint64_t)it);
text += tx;
l.char_count += tx.length();
} break;
@@ -479,7 +541,8 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
int column = idx % col_count;
for (int i = 0; i < frame->lines.size(); i++) {
int char_offset = l.char_offset + l.char_count;
- _shape_line(frame, i, p_base_font, p_base_font_size, 1, &char_offset);
+ int w = _find_margin(frame->lines[i].from, p_base_font, p_base_font_size) + 1;
+ _shape_line(frame, i, p_base_font, p_base_font_size, w, &char_offset);
int cell_ch = (char_offset - (l.char_offset + l.char_count));
l.char_count += cell_ch;
t_char_count += cell_ch;
@@ -509,7 +572,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
// Assign actual widths.
for (int i = 0; i < col_count; i++) {
table->columns.write[i].width = table->columns[i].min_width;
- if (table->columns[i].expand && total_ratio > 0) {
+ if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) {
table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
table->total_width += table->columns[i].width + hseparation;
@@ -570,13 +633,16 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
if (i > 0) {
- frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y;
+ frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + frame->lines[i - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
} else {
frame->lines.write[i].offset.y = 0;
}
frame->lines.write[i].offset += offset;
- float h = frame->lines[i].text_buf->get_size().y;
+ float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation"));
+ if (i > 0) {
+ h += get_theme_constant(SNAME("line_separation"));
+ }
if (frame->min_size_over.y > 0) {
h = MAX(h, frame->min_size_over.y);
}
@@ -615,18 +681,19 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
*r_char_offset = l.char_offset + l.char_count;
if (p_line > 0) {
- l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
+ l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + p_frame->lines[p_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
} else {
l.offset.y = 0;
}
}
int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs) {
- Vector2 off;
-
ERR_FAIL_COND_V(p_frame == nullptr, 0);
ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), 0);
+ Vector2 off;
+ int line_spacing = get_theme_constant(SNAME("line_separation"));
+
Line &l = p_frame->lines.write[p_line];
Item *it_from = l.from;
@@ -712,6 +779,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Size2 ctrl_size = get_size();
// Draw text.
for (int line = 0; line < l.text_buf->get_line_count(); line++) {
+ if (line > 0) {
+ off.y += line_spacing;
+ }
+
RID rid = l.text_buf->get_line_rid(line);
if (p_ofs.y + off.y >= ctrl_size.height) {
break;
@@ -1177,7 +1248,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
//TODO, change to binary search ?
while (from_line < main->lines.size()) {
- if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) {
+ if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")) >= vofs) {
break;
}
from_line++;
@@ -1190,7 +1261,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
while (ofs.y < size.height && from_line < main->lines.size()) {
_find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char);
- ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
+ ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) {
if (r_outside != nullptr) {
*r_outside = false;
@@ -1309,7 +1380,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
if (rect.has_point(p_click) && !table_hit) {
char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
}
- off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom();
+ off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom() + get_theme_constant(SNAME("line_separation"));
}
if (char_pos >= 0) {
@@ -1435,7 +1506,10 @@ void RichTextLabel::_notification(int p_what) {
update();
} break;
- case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_THEME_CHANGED: {
+ main->first_invalid_font_line = 0; //invalidate ALL
+ update();
+ } break;
case NOTIFICATION_ENTER_TREE: {
if (!text.is_empty()) {
set_text(text);
@@ -1473,7 +1547,7 @@ void RichTextLabel::_notification(int p_what) {
//TODO, change to binary search ?
while (from_line < main->lines.size()) {
- if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y >= vofs) {
+ if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")) >= vofs) {
break;
}
from_line++;
@@ -1499,7 +1573,7 @@ void RichTextLabel::_notification(int p_what) {
while (ofs.y < size.height && from_line < main->lines.size()) {
visible_paragraph_count++;
visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs, processed_glyphs);
- ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
+ ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
from_line++;
}
} break;
@@ -1532,6 +1606,10 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
return get_default_cursor_shape(); //invalid
}
+ if (main->first_invalid_font_line < main->lines.size()) {
+ return get_default_cursor_shape(); //invalid
+ }
+
if (main->first_resized_line < main->lines.size()) {
return get_default_cursor_shape(); //invalid
}
@@ -1556,6 +1634,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
if (main->first_invalid_line < main->lines.size()) {
return;
}
+ if (main->first_invalid_font_line < main->lines.size()) {
+ return;
+ }
if (main->first_resized_line < main->lines.size()) {
return;
}
@@ -1719,6 +1800,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
if (main->first_invalid_line < main->lines.size()) {
return;
}
+ if (main->first_invalid_font_line < main->lines.size()) {
+ return;
+ }
if (main->first_resized_line < main->lines.size()) {
return;
}
@@ -2171,27 +2255,32 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
if (p_frame->first_invalid_line == p_frame->lines.size()) {
+ Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
+ int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
+
+ // Update fonts.
+ if (p_frame->first_invalid_font_line != p_frame->lines.size()) {
+ for (int i = p_frame->first_invalid_font_line; i < p_frame->lines.size(); i++) {
+ _update_line_font(p_frame, i, base_font, base_font_size);
+ }
+ p_frame->first_resized_line = p_frame->first_invalid_font_line;
+ p_frame->first_invalid_font_line = p_frame->lines.size();
+ }
+
if (p_frame->first_resized_line == p_frame->lines.size()) {
return;
}
// Resize lines without reshaping.
- Size2 size = get_size();
- if (fixed_width != -1) {
- size.width = fixed_width;
- }
Rect2 text_rect = _get_text_rect();
- Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
- int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
-
for (int i = p_frame->first_resized_line; i < p_frame->lines.size(); i++) {
_resize_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w);
}
int total_height = 0;
if (p_frame->lines.size()) {
- total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y;
+ total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
}
p_frame->first_resized_line = p_frame->lines.size();
@@ -2200,7 +2289,7 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
vscroll->set_max(total_height);
vscroll->set_page(text_rect.size.height);
if (scroll_follow && scroll_following) {
- vscroll->set_value(total_height - size.height);
+ vscroll->set_value(total_height);
}
updating_scroll = false;
@@ -2211,10 +2300,6 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
}
// Shape invalid lines.
- Size2 size = get_size();
- if (fixed_width != -1) {
- size.width = fixed_width;
- }
Rect2 text_rect = _get_text_rect();
Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
@@ -2227,17 +2312,18 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
int total_height = 0;
if (p_frame->lines.size()) {
- total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y;
+ total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
}
p_frame->first_invalid_line = p_frame->lines.size();
p_frame->first_resized_line = p_frame->lines.size();
+ p_frame->first_invalid_font_line = p_frame->lines.size();
updating_scroll = true;
vscroll->set_max(total_height);
vscroll->set_page(text_rect.size.height);
if (scroll_follow && scroll_following) {
- vscroll->set_value(total_height - size.height);
+ vscroll->set_value(total_height);
}
updating_scroll = false;
@@ -3141,7 +3227,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
indent_level++;
push_list(indent_level, LIST_NUMBERS, false);
pos = brk_end + 1;
- tag_stack.push_front(tag);
+ tag_stack.push_front("ol");
} else if (tag == "ol type=a") {
indent_level++;
push_list(indent_level, LIST_LETTERS, false);
@@ -4006,6 +4092,19 @@ String RichTextLabel::get_language() const {
return language;
}
+void RichTextLabel::set_autowrap_mode(RichTextLabel::AutowrapMode p_mode) {
+ if (autowrap_mode != p_mode) {
+ autowrap_mode = p_mode;
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ update();
+ }
+}
+
+RichTextLabel::AutowrapMode RichTextLabel::get_autowrap_mode() const {
+ return autowrap_mode;
+}
+
void RichTextLabel::set_percent_visible(float p_percent) {
if (percent_visible != p_percent) {
if (p_percent < 0 || p_percent >= 1) {
@@ -4053,7 +4152,7 @@ void RichTextLabel::install_effect(const Variant effect) {
int RichTextLabel::get_content_height() const {
int total_height = 0;
if (main->lines.size()) {
- total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y;
+ total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y + main->lines[main->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation"));
}
return total_height;
}
@@ -4117,6 +4216,9 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_language", "language"), &RichTextLabel::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &RichTextLabel::get_language);
+ ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &RichTextLabel::set_autowrap_mode);
+ ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &RichTextLabel::get_autowrap_mode);
+
ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline);
ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined);
@@ -4207,7 +4309,9 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
@@ -4217,6 +4321,11 @@ void RichTextLabel::_bind_methods() {
ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
+ BIND_ENUM_CONSTANT(AUTOWRAP_OFF);
+ BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY);
+ BIND_ENUM_CONSTANT(AUTOWRAP_WORD);
+ BIND_ENUM_CONSTANT(AUTOWRAP_WORD_SMART);
+
BIND_ENUM_CONSTANT(LIST_NUMBERS);
BIND_ENUM_CONSTANT(LIST_LETTERS);
BIND_ENUM_CONSTANT(LIST_ROMAN);
@@ -4340,7 +4449,7 @@ Size2 RichTextLabel::get_minimum_size() const {
size.x += fixed_width;
}
- if (fixed_width != -1 || fit_content_height) {
+ if (fit_content_height) {
const_cast<RichTextLabel *>(this)->_validate_line_caches(main);
size.y += get_content_height();
}
@@ -4489,6 +4598,7 @@ RichTextLabel::RichTextLabel() {
main->lines.write[0].from = main;
main->first_invalid_line = 0;
main->first_resized_line = 0;
+ main->first_invalid_font_line = 0;
current_frame = main;
vscroll = memnew(VScrollBar);
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 70467e7e7c..e79244f2e4 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -39,6 +39,13 @@ class RichTextLabel : public Control {
GDCLASS(RichTextLabel, Control);
public:
+ enum AutowrapMode {
+ AUTOWRAP_OFF,
+ AUTOWRAP_ARBITRARY,
+ AUTOWRAP_WORD,
+ AUTOWRAP_WORD_SMART
+ };
+
enum ListType {
LIST_NUMBERS,
LIST_LETTERS,
@@ -129,6 +136,7 @@ private:
Vector<Line> lines;
int first_invalid_line = 0;
+ int first_invalid_font_line = 0;
int first_resized_line = 0;
ItemFrame *parent_frame = nullptr;
@@ -345,6 +353,8 @@ private:
VScrollBar *vscroll = nullptr;
+ AutowrapMode autowrap_mode = AUTOWRAP_WORD_SMART;
+
bool scroll_visible = false;
bool scroll_follow = false;
bool scroll_following = false;
@@ -414,6 +424,7 @@ private:
void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset);
void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width);
+ void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size);
int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs);
float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr);
@@ -572,6 +583,9 @@ public:
void set_language(const String &p_language);
String get_language() const;
+ void set_autowrap_mode(AutowrapMode p_mode);
+ AutowrapMode get_autowrap_mode() const;
+
void set_structured_text_bidi_override(Control::StructuredTextParser p_parser);
Control::StructuredTextParser get_structured_text_bidi_override() const;
@@ -601,6 +615,7 @@ public:
~RichTextLabel();
};
+VARIANT_ENUM_CAST(RichTextLabel::AutowrapMode);
VARIANT_ENUM_CAST(RichTextLabel::ListType);
VARIANT_ENUM_CAST(RichTextLabel::ItemType);
VARIANT_ENUM_CAST(RichTextLabel::VisibleCharactersBehavior);
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index bd823d12ed..5a551ec5a5 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -38,48 +38,71 @@
#include "scene/gui/texture_rect.h"
Size2 TabBar::get_minimum_size() const {
+ Size2 ms;
+
+ if (tabs.is_empty()) {
+ return ms;
+ }
+
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
+ Ref<StyleBox> button_highlight = get_theme_stylebox(SNAME("button_highlight"));
+ Ref<Texture2D> close = get_theme_icon(SNAME("close"));
+ int hseparation = get_theme_constant(SNAME("hseparation"));
int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height);
- Size2 ms(0, 0);
-
for (int i = 0; i < tabs.size(); i++) {
- Ref<Texture2D> tex = tabs[i].icon;
- if (tex.is_valid()) {
- ms.height = MAX(ms.height, tex->get_size().height);
- if (!tabs[i].text.is_empty()) {
- ms.width += get_theme_constant(SNAME("hseparation"));
- }
+ if (tabs[i].hidden) {
+ continue;
}
- ms.width += Math::ceil(tabs[i].text_buf->get_size().x);
- ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin);
+ int ofs = ms.width;
+ Ref<StyleBox> style;
if (tabs[i].disabled) {
- ms.width += tab_disabled->get_minimum_size().width;
+ style = tab_disabled;
} else if (current == i) {
- ms.width += tab_selected->get_minimum_size().width;
+ style = tab_selected;
} else {
- ms.width += tab_unselected->get_minimum_size().width;
+ style = tab_unselected;
}
+ ms.width += style->get_minimum_size().width;
+
+ Ref<Texture2D> tex = tabs[i].icon;
+ if (tex.is_valid()) {
+ ms.height = MAX(ms.height, tex->get_size().height);
+ ms.width += tex->get_size().width + hseparation;
+ }
+
+ if (!tabs[i].text.is_empty()) {
+ ms.width += tabs[i].size_text + hseparation;
+ }
+ ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin);
+
+ bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current);
if (tabs[i].right_button.is_valid()) {
Ref<Texture2D> rb = tabs[i].right_button;
- Size2 bms = rb->get_size();
- bms.width += get_theme_constant(SNAME("hseparation"));
- ms.width += bms.width;
- ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height);
+
+ if (close_visible) {
+ ms.width += button_highlight->get_minimum_size().width + rb->get_width();
+ } else {
+ ms.width += button_highlight->get_margin(SIDE_LEFT) + rb->get_width() + hseparation;
+ }
+
+ ms.height = MAX(rb->get_height() + style->get_minimum_size().height, ms.height);
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- Size2 bms = cb->get_size();
- bms.width += get_theme_constant(SNAME("hseparation"));
- ms.width += bms.width;
- ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height);
+ if (close_visible) {
+ ms.width += button_highlight->get_margin(SIDE_LEFT) + close->get_width() + hseparation;
+
+ ms.height = MAX(close->get_height() + style->get_minimum_size().height, ms.height);
+ }
+
+ if (ms.width - ofs > style->get_minimum_size().width) {
+ ms.width -= hseparation;
}
}
@@ -165,8 +188,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (rb_hover != -1) {
- // Right mouse button clicked.
- emit_signal(SNAME("tab_rmb_clicked"), rb_hover);
+ emit_signal(SNAME("tab_button_pressed"), rb_hover);
}
rb_pressing = false;
@@ -175,7 +197,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (cb_hover != -1) {
- // Close button pressed.
emit_signal(SNAME("tab_close_pressed"), cb_hover);
}
@@ -184,7 +205,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
}
if (mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || (select_with_rmb && mb->get_button_index() == MouseButton::RIGHT))) {
- // Clicks.
Point2 pos = mb->get_position();
if (buttons_visible) {
@@ -234,6 +254,10 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
int found = -1;
for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
if (tabs[i].rb_rect.has_point(pos)) {
rb_pressing = true;
update();
@@ -256,6 +280,12 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (found != -1) {
set_current_tab(found);
+
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ // Right mouse button clicked.
+ emit_signal(SNAME("tab_rmb_clicked"), found);
+ }
+
emit_signal(SNAME("tab_clicked"), found);
}
}
@@ -275,13 +305,12 @@ void TabBar::_shape(int p_tab) {
tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction);
}
- tabs.write[p_tab].text_buf->add_string(tabs.write[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, !tabs[p_tab].language.is_empty() ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale());
+ tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, !tabs[p_tab].language.is_empty() ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale());
}
void TabBar::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
- _update_cache();
update();
} break;
case NOTIFICATION_THEME_CHANGED:
@@ -289,166 +318,78 @@ void TabBar::_notification(int p_what) {
for (int i = 0; i < tabs.size(); ++i) {
_shape(i);
}
- _update_cache();
- update_minimum_size();
- update();
- } break;
+
+ [[fallthrough]];
+ }
case NOTIFICATION_RESIZED: {
+ int ofs_old = offset;
+ int max_old = max_drawn_tab;
+
_update_cache();
_ensure_no_over_offset();
- ensure_tab_visible(current);
+
+ if (scroll_to_selected && (offset != ofs_old || max_drawn_tab != max_old)) {
+ ensure_tab_visible(current);
+ }
} break;
case NOTIFICATION_DRAW: {
if (tabs.is_empty()) {
return;
}
- RID ci = get_canvas_item();
-
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
Color font_unselected_color = get_theme_color(SNAME("font_unselected_color"));
Color font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
- Ref<Texture2D> close = get_theme_icon(SNAME("close"));
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
Ref<Texture2D> incr_hl = get_theme_icon(SNAME("increment_highlight"));
Ref<Texture2D> decr_hl = get_theme_icon(SNAME("decrement_highlight"));
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
- Vector2 size = get_size();
bool rtl = is_layout_rtl();
- int limit = get_size().width;
- int limit_minus_buttons = limit - incr->get_width() - decr->get_width();
+ Vector2 size = get_size();
+ int limit_minus_buttons = size.width - incr->get_width() - decr->get_width();
- int h = get_size().height;
- int w = tabs[offset].ofs_cache;
+ int ofs = tabs[offset].ofs_cache;
+ // Draw unselected tabs in the back.
for (int i = offset; i <= max_drawn_tab; i++) {
- Ref<StyleBox> sb;
- Color col;
-
- if (tabs[i].disabled) {
- sb = tab_disabled;
- col = font_disabled_color;
- } else if (i == current) {
- sb = tab_selected;
- col = font_selected_color;
- } else {
- sb = tab_unselected;
- col = font_unselected_color;
- }
-
- Rect2 sb_rect;
- if (rtl) {
- sb_rect = Rect2(size.width - w - tabs[i].size_cache, 0, tabs[i].size_cache, h);
- } else {
- sb_rect = Rect2(w, 0, tabs[i].size_cache, h);
- }
- sb->draw(ci, sb_rect);
-
- w += sb->get_margin(SIDE_LEFT);
-
- Size2i sb_ms = sb->get_minimum_size();
- Ref<Texture2D> icon = tabs[i].icon;
- if (icon.is_valid()) {
- if (rtl) {
- icon->draw(ci, Point2i(size.width - w - icon->get_width(), sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
- } else {
- icon->draw(ci, Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
- }
- if (!tabs[i].text.is_empty()) {
- w += icon->get_width() + get_theme_constant(SNAME("hseparation"));
- }
+ if (tabs[i].hidden) {
+ continue;
}
- if (rtl) {
- Vector2 text_pos = Point2i(size.width - w - tabs[i].text_buf->get_size().x, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
- }
- tabs[i].text_buf->draw(ci, text_pos, col);
- } else {
- Vector2 text_pos = Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
- }
- tabs[i].text_buf->draw(ci, text_pos, col);
- }
-
- w += tabs[i].size_text;
-
- if (tabs[i].right_button.is_valid()) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
- Ref<Texture2D> rb = tabs[i].right_button;
-
- w += get_theme_constant(SNAME("hseparation"));
+ if (i != current) {
+ Ref<StyleBox> sb;
+ Color col;
- Rect2 rb_rect;
- rb_rect.size = style->get_minimum_size() + rb->get_size();
- if (rtl) {
- rb_rect.position.x = size.width - w - rb_rect.size.x;
+ if (tabs[i].disabled) {
+ sb = tab_disabled;
+ col = font_disabled_color;
+ } else if (i == current) {
+ sb = tab_selected;
+ col = font_selected_color;
} else {
- rb_rect.position.x = w;
+ sb = tab_unselected;
+ col = font_unselected_color;
}
- rb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2;
- if (rb_hover == i) {
- if (rb_pressing) {
- get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect);
- } else {
- style->draw(ci, rb_rect);
- }
- }
-
- if (rtl) {
- rb->draw(ci, Point2i(size.width - w - rb_rect.size.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
- } else {
- rb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
- }
- w += rb->get_width();
- tabs.write[i].rb_rect = rb_rect;
+ _draw_tab(sb, col, i, rtl ? size.width - ofs - tabs[i].size_cache : ofs);
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
- Ref<Texture2D> cb = close;
-
- w += get_theme_constant(SNAME("hseparation"));
-
- Rect2 cb_rect;
- cb_rect.size = style->get_minimum_size() + cb->get_size();
- if (rtl) {
- cb_rect.position.x = size.width - w - cb_rect.size.x;
- } else {
- cb_rect.position.x = w;
- }
- cb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2;
+ ofs += tabs[i].size_cache;
+ }
- if (!tabs[i].disabled && cb_hover == i) {
- if (cb_pressing) {
- get_theme_stylebox(SNAME("close_bg_pressed"))->draw(ci, cb_rect);
- } else {
- style->draw(ci, cb_rect);
- }
- }
+ // Draw selected tab in the front, but only if it's visible.
+ if (current >= offset && current <= max_drawn_tab && !tabs[current].hidden) {
+ Ref<StyleBox> sb = tabs[current].disabled ? tab_disabled : tab_selected;
+ float x = rtl ? size.width - tabs[current].ofs_cache - tabs[current].size_cache : tabs[current].ofs_cache;
- if (rtl) {
- cb->draw(ci, Point2i(size.width - w - cb_rect.size.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
- } else {
- cb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
- }
- w += cb->get_width();
- tabs.write[i].cb_rect = cb_rect;
- }
-
- w += sb->get_margin(SIDE_RIGHT);
+ _draw_tab(sb, font_selected_color, current, x);
}
- if (offset > 0 || missing_right) {
+ if (buttons_visible) {
int vofs = (get_size().height - incr->get_size().height) / 2;
if (rtl) {
@@ -481,11 +422,117 @@ void TabBar::_notification(int p_what) {
}
}
+void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) {
+ RID ci = get_canvas_item();
+ bool rtl = is_layout_rtl();
+
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
+ int hseparation = get_theme_constant(SNAME("hseparation"));
+
+ Rect2 sb_rect = Rect2(p_x, 0, tabs[p_index].size_cache, get_size().height);
+ p_tab_style->draw(ci, sb_rect);
+
+ p_x += rtl ? tabs[p_index].size_cache - p_tab_style->get_margin(SIDE_LEFT) : p_tab_style->get_margin(SIDE_LEFT);
+
+ Size2i sb_ms = p_tab_style->get_minimum_size();
+
+ // Draw the icon.
+ Ref<Texture2D> icon = tabs[p_index].icon;
+ if (icon.is_valid()) {
+ icon->draw(ci, Point2i(rtl ? p_x - icon->get_width() : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
+
+ p_x = rtl ? p_x - icon->get_width() - hseparation : p_x + icon->get_width() + hseparation;
+ }
+
+ // Draw the text.
+ if (!tabs[p_index].text.is_empty()) {
+ Point2i text_pos = Point2i(rtl ? p_x - tabs[p_index].size_text : p_x,
+ p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[p_index].text_buf->get_size().y) / 2);
+
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ tabs[p_index].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ tabs[p_index].text_buf->draw(ci, text_pos, p_font_color);
+
+ p_x = rtl ? p_x - tabs[p_index].size_text - hseparation : p_x + tabs[p_index].size_text + hseparation;
+ }
+
+ // Draw and calculate rect of the right button.
+ if (tabs[p_index].right_button.is_valid()) {
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight"));
+ Ref<Texture2D> rb = tabs[p_index].right_button;
+
+ Rect2 rb_rect;
+ rb_rect.size = style->get_minimum_size() + rb->get_size();
+ rb_rect.position.x = rtl ? p_x - rb_rect.size.width : p_x;
+ rb_rect.position.y = p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2;
+
+ tabs.write[p_index].rb_rect = rb_rect;
+
+ if (rb_hover == p_index) {
+ if (rb_pressing) {
+ get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect);
+ } else {
+ style->draw(ci, rb_rect);
+ }
+ }
+
+ rb->draw(ci, Point2i(rb_rect.position.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
+
+ p_x = rtl ? rb_rect.position.x : rb_rect.position.x + rb_rect.size.width;
+ }
+
+ // Draw and calculate rect of the close button.
+ if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_index == current)) {
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight"));
+ Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
+
+ Rect2 cb_rect;
+ cb_rect.size = style->get_minimum_size() + cb->get_size();
+ cb_rect.position.x = rtl ? p_x - cb_rect.size.width : p_x;
+ cb_rect.position.y = p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2;
+
+ tabs.write[p_index].cb_rect = cb_rect;
+
+ if (!tabs[p_index].disabled && cb_hover == p_index) {
+ if (cb_pressing) {
+ get_theme_stylebox(SNAME("button_pressed"))->draw(ci, cb_rect);
+ } else {
+ style->draw(ci, cb_rect);
+ }
+ }
+
+ cb->draw(ci, Point2i(cb_rect.position.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
+ }
+}
+
void TabBar::set_tab_count(int p_count) {
+ if (p_count == tabs.size()) {
+ return;
+ }
+
ERR_FAIL_COND(p_count < 0);
tabs.resize(p_count);
+
+ if (p_count == 0) {
+ offset = 0;
+ max_drawn_tab = 0;
+ current = 0;
+ previous = 0;
+ } else {
+ offset = MIN(offset, p_count - 1);
+ max_drawn_tab = MIN(max_drawn_tab, p_count - 1);
+ current = MIN(current, p_count - 1);
+ }
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
notify_property_list_changed();
}
@@ -494,15 +541,26 @@ int TabBar::get_tab_count() const {
}
void TabBar::set_current_tab(int p_current) {
- if (current == p_current) {
- return;
- }
ERR_FAIL_INDEX(p_current, get_tab_count());
previous = current;
current = p_current;
+ if (current == previous) {
+ emit_signal(SNAME("tab_selected"), current);
+ return;
+ }
+ // Triggered by dragging a tab from another TabBar to the selected index, to ensure that tab_changed is emitted.
+ if (previous == -1) {
+ previous = current;
+ }
+
+ emit_signal(SNAME("tab_selected"), current);
+
_update_cache();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
emit_signal(SNAME("tab_changed"), p_current);
@@ -531,8 +589,13 @@ bool TabBar::get_offset_buttons_visible() const {
void TabBar::set_tab_title(int p_tab, const String &p_title) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].text = p_title;
+
_shape(p_tab);
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
@@ -545,6 +608,7 @@ String TabBar::get_tab_title(int p_tab) const {
void TabBar::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) {
ERR_FAIL_INDEX(p_tab, tabs.size());
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
+
if (tabs[p_tab].text_direction != p_text_direction) {
tabs.write[p_tab].text_direction = p_text_direction;
_shape(p_tab);
@@ -560,24 +624,38 @@ Control::TextDirection TabBar::get_tab_text_direction(int p_tab) const {
void TabBar::clear_tab_opentype_features(int p_tab) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].opentype_features.clear();
+
_shape(p_tab);
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
void TabBar::set_tab_opentype_feature(int p_tab, const String &p_name, int p_value) {
ERR_FAIL_INDEX(p_tab, tabs.size());
+
int32_t tag = TS->name_to_tag(p_name);
if (!tabs[p_tab].opentype_features.has(tag) || (int)tabs[p_tab].opentype_features[tag] != p_value) {
tabs.write[p_tab].opentype_features[tag] = p_value;
+
_shape(p_tab);
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
}
int TabBar::get_tab_opentype_feature(int p_tab, const String &p_name) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), -1);
+
int32_t tag = TS->name_to_tag(p_name);
if (!tabs[p_tab].opentype_features.has(tag)) {
return -1;
@@ -587,10 +665,17 @@ int TabBar::get_tab_opentype_feature(int p_tab, const String &p_name) const {
void TabBar::set_tab_language(int p_tab, const String &p_language) {
ERR_FAIL_INDEX(p_tab, tabs.size());
+
if (tabs[p_tab].language != p_language) {
tabs.write[p_tab].language = p_language;
_shape(p_tab);
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
}
@@ -602,7 +687,12 @@ String TabBar::get_tab_language(int p_tab) const {
void TabBar::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].icon = p_icon;
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
@@ -615,7 +705,14 @@ Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const {
void TabBar::set_tab_disabled(int p_tab, bool p_disabled) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].disabled = p_disabled;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
bool TabBar::is_tab_disabled(int p_tab) const {
@@ -623,15 +720,38 @@ bool TabBar::is_tab_disabled(int p_tab) const {
return tabs[p_tab].disabled;
}
-void TabBar::set_tab_right_button(int p_tab, const Ref<Texture2D> &p_right_button) {
+void TabBar::set_tab_hidden(int p_tab, bool p_hidden) {
+ ERR_FAIL_INDEX(p_tab, tabs.size());
+ tabs.write[p_tab].hidden = p_hidden;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
+ update();
+ update_minimum_size();
+}
+
+bool TabBar::is_tab_hidden(int p_tab) const {
+ ERR_FAIL_INDEX_V(p_tab, tabs.size(), false);
+ return tabs[p_tab].hidden;
+}
+
+void TabBar::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) {
ERR_FAIL_INDEX(p_tab, tabs.size());
- tabs.write[p_tab].right_button = p_right_button;
+ tabs.write[p_tab].right_button = p_icon;
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
-Ref<Texture2D> TabBar::get_tab_right_button(int p_tab) const {
+Ref<Texture2D> TabBar::get_tab_button_icon(int p_tab) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture2D>());
return tabs[p_tab].right_button;
}
@@ -642,34 +762,53 @@ void TabBar::_update_hover() {
}
const Point2 &pos = get_local_mouse_position();
- // test hovering to display right or close button.
+ // Test hovering to display right or close button.
int hover_now = -1;
int hover_buttons = -1;
- for (int i = offset; i < tabs.size(); i++) {
+ for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
Rect2 rect = get_tab_rect(i);
if (rect.has_point(pos)) {
hover_now = i;
}
+
if (tabs[i].rb_rect.has_point(pos)) {
rb_hover = i;
cb_hover = -1;
hover_buttons = i;
- break;
} else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) {
cb_hover = i;
rb_hover = -1;
hover_buttons = i;
+ }
+
+ if (hover_buttons != -1) {
+ update();
break;
}
}
+
if (hover != hover_now) {
hover = hover_now;
- emit_signal(SNAME("tab_hovered"), hover);
+
+ if (hover != -1) {
+ emit_signal(SNAME("tab_hovered"), hover);
+ }
}
if (hover_buttons == -1) { // No hover.
+ int rb_hover_old = rb_hover;
+ int cb_hover_old = cb_hover;
+
rb_hover = hover_buttons;
cb_hover = hover_buttons;
+
+ if (rb_hover != rb_hover_old || cb_hover != cb_hover_old) {
+ update();
+ }
}
}
@@ -693,16 +832,20 @@ void TabBar::_update_cache() {
int count_resize = 0;
for (int i = 0; i < tabs.size(); i++) {
- tabs.write[i].ofs_cache = 0;
- tabs.write[i].size_cache = get_tab_width(i);
tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x);
tabs.write[i].text_buf->set_width(-1);
- mw += tabs[i].size_cache;
- if (tabs[i].size_cache <= min_width || i == current) {
- size_fixed += tabs[i].size_cache;
- } else {
- count_resize++;
+ tabs.write[i].ofs_cache = 0;
+ tabs.write[i].size_cache = get_tab_width(i);
+
+ if (!tabs[i].hidden) {
+ mw += tabs[i].size_cache;
+
+ if (tabs[i].size_cache <= min_width || i == current) {
+ size_fixed += tabs[i].size_cache;
+ } else {
+ count_resize++;
+ }
}
}
@@ -712,34 +855,20 @@ void TabBar::_update_cache() {
}
for (int i = offset; i < tabs.size(); i++) {
- Ref<StyleBox> sb;
- if (tabs[i].disabled) {
- sb = tab_disabled;
- } else if (i == current) {
- sb = tab_selected;
- } else {
- sb = tab_unselected;
+ if (tabs[i].hidden) {
+ tabs.write[i].ofs_cache = w;
+ max_drawn_tab = i;
+
+ continue;
}
int lsize = tabs[i].size_cache;
int slen = tabs[i].size_text;
- if (min_width > 0 && mw > limit_minus_buttons && i != current) {
- if (lsize > m_width) {
- slen = m_width - (sb->get_margin(SIDE_LEFT) + sb->get_margin(SIDE_RIGHT));
- if (tabs[i].icon.is_valid()) {
- slen -= tabs[i].icon->get_width();
- slen -= get_theme_constant(SNAME("hseparation"));
- }
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- slen -= cb->get_width();
- slen -= get_theme_constant(SNAME("hseparation"));
- }
-
- slen = MAX(slen, 1);
- lsize = m_width;
- }
+ // FIXME: This is completely broken.
+ if (min_width > 0 && (mw > limit || (offset > 0 && mw > limit_minus_buttons)) && i != current && lsize > m_width) {
+ slen = MAX(m_width - tabs[i].size_cache + tabs[i].size_text, 1);
+ lsize = m_width;
}
tabs.write[i].ofs_cache = w;
@@ -751,13 +880,22 @@ void TabBar::_update_cache() {
max_drawn_tab = i;
// Check if all tabs would fit inside the area.
- if (i > offset && (w > limit || (offset > 0 && w > limit_minus_buttons))) {
- w -= get_tab_width(i);
- max_drawn_tab -= 1;
+ if (clip_tabs && i > offset && (w > limit || (offset > 0 && w > limit_minus_buttons))) {
+ tabs.write[i].ofs_cache = 0;
+ tabs.write[i].text_buf->set_width(-1);
+
+ w -= tabs[i].size_cache;
+ max_drawn_tab--;
while (w > limit_minus_buttons && max_drawn_tab > offset) {
- w -= get_tab_width(max_drawn_tab);
- max_drawn_tab -= 1;
+ tabs.write[max_drawn_tab].ofs_cache = 0;
+
+ if (!tabs[max_drawn_tab].hidden) {
+ tabs.write[max_drawn_tab].text_buf->set_width(-1);
+ w -= tabs[max_drawn_tab].size_cache;
+ }
+
+ max_drawn_tab--;
}
break;
@@ -768,21 +906,25 @@ void TabBar::_update_cache() {
buttons_visible = offset > 0 || missing_right;
if (tab_alignment == ALIGNMENT_LEFT) {
+ _update_hover();
return;
- } else if (tab_alignment == ALIGNMENT_CENTER) {
+ }
+
+ if (tab_alignment == ALIGNMENT_CENTER) {
w = ((buttons_visible ? limit_minus_buttons : limit) - w) / 2;
} else if (tab_alignment == ALIGNMENT_RIGHT) {
w = (buttons_visible ? limit_minus_buttons : limit) - w;
}
- if (w < 0) {
- w = 0;
- }
-
for (int i = offset; i <= max_drawn_tab; i++) {
tabs.write[i].ofs_cache = w;
- w += tabs.write[i].size_cache;
+
+ if (!tabs[i].hidden) {
+ w += tabs[i].size_cache;
+ }
}
+
+ _update_hover();
}
void TabBar::_on_mouse_exited() {
@@ -800,20 +942,26 @@ void TabBar::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) {
t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
t.text_buf->add_string(t.xl_text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
t.icon = p_icon;
-
tabs.push_back(t);
+
_update_cache();
- call_deferred(SNAME("_update_hover"));
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
void TabBar::clear_tabs() {
tabs.clear();
+ offset = 0;
+ max_drawn_tab = 0;
current = 0;
previous = 0;
- call_deferred(SNAME("_update_hover"));
+
+ _update_cache();
update();
+ update_minimum_size();
notify_property_list_changed();
}
@@ -823,10 +971,6 @@ void TabBar::remove_tab(int p_idx) {
if (current >= p_idx) {
current--;
}
- _update_cache();
- call_deferred(SNAME("_update_hover"));
- update();
- update_minimum_size();
if (current < 0) {
current = 0;
@@ -836,7 +980,13 @@ void TabBar::remove_tab(int p_idx) {
current = tabs.size() - 1;
}
+ _update_cache();
_ensure_no_over_offset();
+ if (scroll_to_selected && !tabs.is_empty()) {
+ ensure_tab_visible(current);
+ }
+ update();
+ update_minimum_size();
notify_property_list_changed();
}
@@ -856,15 +1006,13 @@ Variant TabBar::get_drag_data(const Point2 &p_point) {
if (!tabs[tab_over].icon.is_null()) {
TextureRect *tf = memnew(TextureRect);
tf->set_texture(tabs[tab_over].icon);
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
drag_preview->add_child(tf);
}
+
Label *label = memnew(Label(tabs[tab_over].xl_text));
drag_preview->add_child(label);
- if (!tabs[tab_over].right_button.is_null()) {
- TextureRect *tf = memnew(TextureRect);
- tf->set_texture(tabs[tab_over].right_button);
- drag_preview->add_child(tf);
- }
+
set_drag_preview(drag_preview);
Dictionary drag_data;
@@ -917,31 +1065,40 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
int tab_from_id = d["tab_element"];
NodePath from_path = d["from_path"];
NodePath to_path = get_path();
+
if (from_path == to_path) {
if (hover_now < 0) {
hover_now = get_tab_count() - 1;
}
+
move_tab(tab_from_id, hover_now);
emit_signal(SNAME("active_tab_rearranged"), hover_now);
set_current_tab(hover_now);
} else if (get_tabs_rearrange_group() != -1) {
// Drag and drop between Tabs.
+
Node *from_node = get_node(from_path);
TabBar *from_tabs = Object::cast_to<TabBar>(from_node);
+
if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
if (tab_from_id >= from_tabs->get_tab_count()) {
return;
}
+
Tab moving_tab = from_tabs->tabs[tab_from_id];
if (hover_now < 0) {
hover_now = get_tab_count();
}
+
+ // Workaround to ensure that tab_changed is emitted.
+ if (current == hover_now) {
+ current = -1;
+ }
+
tabs.insert(hover_now, moving_tab);
from_tabs->remove_tab(tab_from_id);
set_current_tab(hover_now);
- emit_signal(SNAME("tab_changed"), hover_now);
- _update_cache();
- update();
+ update_minimum_size();
}
}
}
@@ -962,6 +1119,7 @@ int TabBar::get_tab_idx_at_point(const Point2 &p_point) const {
void TabBar::set_tab_alignment(AlignmentMode p_alignment) {
ERR_FAIL_INDEX(p_alignment, ALIGNMENT_MAX);
tab_alignment = p_alignment;
+
_update_cache();
update();
}
@@ -975,7 +1133,16 @@ void TabBar::set_clip_tabs(bool p_clip_tabs) {
return;
}
clip_tabs = p_clip_tabs;
+
+ if (!clip_tabs) {
+ offset = 0;
+ max_drawn_tab = 0;
+ }
+
_update_cache();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
@@ -997,6 +1164,10 @@ void TabBar::move_tab(int from, int to) {
tabs.insert(to, tab_from);
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
notify_property_list_changed();
}
@@ -1007,37 +1178,49 @@ int TabBar::get_tab_width(int p_idx) const {
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
+ int hseparation = get_theme_constant(SNAME("hseparation"));
- int x = 0;
+ Ref<StyleBox> style;
+
+ if (tabs[p_idx].disabled) {
+ style = tab_disabled;
+ } else if (current == p_idx) {
+ style = tab_selected;
+ } else {
+ style = tab_unselected;
+ }
+ int x = style->get_minimum_size().width;
Ref<Texture2D> tex = tabs[p_idx].icon;
if (tex.is_valid()) {
- x += tex->get_width();
- if (!tabs[p_idx].text.is_empty()) {
- x += get_theme_constant(SNAME("hseparation"));
- }
+ x += tex->get_width() + hseparation;
}
- x += Math::ceil(tabs[p_idx].text_buf->get_size().x);
-
- if (tabs[p_idx].disabled) {
- x += tab_disabled->get_minimum_size().width;
- } else if (current == p_idx) {
- x += tab_selected->get_minimum_size().width;
- } else {
- x += tab_unselected->get_minimum_size().width;
+ if (!tabs[p_idx].text.is_empty()) {
+ x += tabs[p_idx].size_text + hseparation;
}
+ bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current);
+
if (tabs[p_idx].right_button.is_valid()) {
+ Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight"));
Ref<Texture2D> rb = tabs[p_idx].right_button;
- x += rb->get_width();
- x += get_theme_constant(SNAME("hseparation"));
+
+ if (close_visible) {
+ x += btn_style->get_minimum_size().width + rb->get_width();
+ } else {
+ x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + hseparation;
+ }
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current)) {
+ if (close_visible) {
+ Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight"));
Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- x += cb->get_width();
- x += get_theme_constant(SNAME("hseparation"));
+ x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + hseparation;
+ }
+
+ if (x > style->get_minimum_size().width) {
+ x -= hseparation;
}
return x;
@@ -1055,8 +1238,12 @@ void TabBar::_ensure_no_over_offset() {
int prev_offset = offset;
int total_w = tabs[max_drawn_tab].ofs_cache + tabs[max_drawn_tab].size_cache - tabs[offset].ofs_cache;
- while (offset > 0) {
- total_w += tabs[offset - 1].size_cache;
+ for (int i = offset; i > 0; i--) {
+ if (tabs[i - 1].hidden) {
+ continue;
+ }
+
+ total_w += tabs[i - 1].size_cache;
if (total_w < limit_minus_buttons) {
offset--;
@@ -1077,7 +1264,7 @@ void TabBar::ensure_tab_visible(int p_idx) {
}
ERR_FAIL_INDEX(p_idx, tabs.size());
- if (p_idx >= offset && p_idx <= max_drawn_tab) {
+ if (tabs[p_idx].hidden || (p_idx >= offset && p_idx <= max_drawn_tab)) {
return;
}
@@ -1095,12 +1282,20 @@ void TabBar::ensure_tab_visible(int p_idx) {
int total_w = tabs[max_drawn_tab].ofs_cache - tabs[offset].ofs_cache;
for (int i = max_drawn_tab; i <= p_idx; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
total_w += tabs[i].size_cache;
}
int prev_offset = offset;
for (int i = offset; i < p_idx; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
if (total_w > limit_minus_buttons) {
total_w -= tabs[i].size_cache;
offset++;
@@ -1127,8 +1322,14 @@ Rect2 TabBar::get_tab_rect(int p_tab) const {
void TabBar::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) {
ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX);
cb_displaypolicy = p_policy;
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
TabBar::CloseButtonDisplayPolicy TabBar::get_tab_close_display_policy() const {
@@ -1163,6 +1364,17 @@ int TabBar::get_tabs_rearrange_group() const {
return tabs_rearrange_group;
}
+void TabBar::set_scroll_to_selected(bool p_enabled) {
+ scroll_to_selected = p_enabled;
+ if (p_enabled) {
+ ensure_tab_visible(current);
+ }
+}
+
+bool TabBar::get_scroll_to_selected() const {
+ return scroll_to_selected;
+}
+
void TabBar::set_select_with_rmb(bool p_enabled) {
select_with_rmb = p_enabled;
}
@@ -1241,8 +1453,12 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &TabBar::get_tab_language);
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabBar::set_tab_icon);
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabBar::get_tab_icon);
+ ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabBar::set_tab_button_icon);
+ ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabBar::get_tab_button_icon);
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabBar::set_tab_disabled);
ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabBar::is_tab_disabled);
+ ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabBar::set_tab_hidden);
+ ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabBar::is_tab_hidden);
ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &TabBar::remove_tab);
ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &TabBar::add_tab, DEFVAL(""), DEFVAL(Ref<Texture2D>()));
ClassDB::bind_method(D_METHOD("set_tab_alignment", "alignment"), &TabBar::set_tab_alignment);
@@ -1262,16 +1478,19 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabBar::get_drag_to_rearrange_enabled);
ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabBar::set_tabs_rearrange_group);
ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabBar::get_tabs_rearrange_group);
-
+ ClassDB::bind_method(D_METHOD("set_scroll_to_selected", "enabled"), &TabBar::set_scroll_to_selected);
+ ClassDB::bind_method(D_METHOD("get_scroll_to_selected"), &TabBar::get_scroll_to_selected);
ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &TabBar::set_select_with_rmb);
ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &TabBar::get_select_with_rmb);
+ ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_rmb_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_close_pressed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_button_pressed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to")));
- ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment");
@@ -1279,6 +1498,8 @@ void TabBar::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_to_selected"), "set_scroll_to_selected", "get_scroll_to_selected");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_with_rmb"), "set_select_with_rmb", "get_select_with_rmb");
ADD_ARRAY_COUNT("Tabs", "tab_count", "set_tab_count", "get_tab_count", "tab_");
@@ -1294,5 +1515,6 @@ void TabBar::_bind_methods() {
}
TabBar::TabBar() {
+ set_size(Size2(get_size().width, get_minimum_size().height));
connect("mouse_exited", callable_mp(this, &TabBar::_on_mouse_exited));
}
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index 1741481b40..b428538570 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -63,12 +63,11 @@ private:
Ref<TextLine> text_buf;
Ref<Texture2D> icon;
- int ofs_cache = 0;
bool disabled = false;
+ bool hidden = false;
+ int ofs_cache = 0;
int size_cache = 0;
int size_text = 0;
- int x_cache = 0;
- int x_size_cache = 0;
Ref<Texture2D> right_button;
Rect2 rb_rect;
@@ -102,6 +101,7 @@ private:
int min_width = 0;
bool scrolling_enabled = true;
bool drag_to_rearrange_enabled = false;
+ bool scroll_to_selected = true;
int tabs_rearrange_group = -1;
int get_tab_width(int p_idx) const;
@@ -113,6 +113,7 @@ private:
void _on_mouse_exited();
void _shape(int p_tab);
+ void _draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x);
protected:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
@@ -149,8 +150,11 @@ public:
void set_tab_disabled(int p_tab, bool p_disabled);
bool is_tab_disabled(int p_tab) const;
- void set_tab_right_button(int p_tab, const Ref<Texture2D> &p_right_button);
- Ref<Texture2D> get_tab_right_button(int p_tab) const;
+ void set_tab_hidden(int p_tab, bool p_hidden);
+ bool is_tab_hidden(int p_tab) const;
+
+ void set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon);
+ Ref<Texture2D> get_tab_button_icon(int p_tab) const;
void set_tab_alignment(AlignmentMode p_alignment);
AlignmentMode get_tab_alignment() const;
@@ -186,6 +190,9 @@ public:
void set_tabs_rearrange_group(int p_group_id);
int get_tabs_rearrange_group() const;
+ void set_scroll_to_selected(bool p_enabled);
+ bool get_scroll_to_selected() const;
+
void set_select_with_rmb(bool p_enabled);
bool get_select_with_rmb() const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 17f2ed1db5..0ee4a6af4e 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -128,6 +128,10 @@ void TextEdit::Text::set_width(float p_width) {
width = p_width;
}
+float TextEdit::Text::get_width() const {
+ return width;
+}
+
int TextEdit::Text::get_line_wrap_amount(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
@@ -183,29 +187,44 @@ void TextEdit::Text::_calculate_max_line_width() {
max_width = width;
}
-void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Array &p_bidi_override) {
+void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_changed, const String &p_ime_text, const Array &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
if (font.is_null() || font_size <= 0) {
return; // Not in tree?
}
- text.write[p_line].data_buf->clear();
+ if (p_text_changed) {
+ text.write[p_line].data_buf->clear();
+ }
+
text.write[p_line].data_buf->set_width(width);
text.write[p_line].data_buf->set_direction((TextServer::Direction)direction);
text.write[p_line].data_buf->set_preserve_control(draw_control_chars);
if (p_ime_text.length() > 0) {
- text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language);
+ if (p_text_changed) {
+ text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language);
+ }
if (!p_bidi_override.is_empty()) {
TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), p_bidi_override);
}
} else {
- text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language);
+ if (p_text_changed) {
+ text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language);
+ }
if (!text[p_line].bidi_override.is_empty()) {
TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), text[p_line].bidi_override);
}
}
+ if (!p_text_changed) {
+ RID r = text.write[p_line].data_buf->get_rid();
+ int spans = TS->shaped_get_span_count(r);
+ for (int i = 0; i < spans; i++) {
+ TS->shaped_set_span_update_font(r, i, font->get_rids(), font_size, opentype_features);
+ }
+ }
+
// Apply tab align.
if (tab_size > 0) {
Vector<float> tabs;
@@ -262,6 +281,24 @@ void TextEdit::Text::invalidate_all_lines() {
}
}
+void TextEdit::Text::invalidate_font() {
+ if (!is_dirty) {
+ return;
+ }
+
+ max_width = -1;
+ line_height = -1;
+
+ if (!font.is_null() && font_size > 0) {
+ font_height = font->get_height(font_size);
+ }
+
+ for (int i = 0; i < text.size(); i++) {
+ invalidate_cache(i, -1, false);
+ }
+ is_dirty = false;
+}
+
void TextEdit::Text::invalidate_all() {
if (!is_dirty) {
return;
@@ -275,7 +312,7 @@ void TextEdit::Text::invalidate_all() {
}
for (int i = 0; i < text.size(); i++) {
- invalidate_cache(i);
+ invalidate_cache(i, -1, true);
}
is_dirty = false;
}
@@ -290,7 +327,7 @@ void TextEdit::Text::clear() {
line.gutters.resize(gutter_count);
line.data = "";
text.insert(0, line);
- invalidate_cache(0);
+ invalidate_cache(0, -1, true);
}
int TextEdit::Text::get_max_width() const {
@@ -302,7 +339,7 @@ void TextEdit::Text::set(int p_line, const String &p_text, const Array &p_bidi_o
text.write[p_line].data = p_text;
text.write[p_line].bidi_override = p_bidi_override;
- invalidate_cache(p_line);
+ invalidate_cache(p_line, -1, true);
}
void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override) {
@@ -327,7 +364,7 @@ void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector
line.data = p_text[i];
line.bidi_override = p_bidi_override[i];
text.write[p_at + i] = line;
- invalidate_cache(p_at + i);
+ invalidate_cache(p_at + i, -1, true);
}
}
@@ -646,6 +683,8 @@ void TextEdit::_notification(int p_what) {
}
}
+ bool draw_placeholder = text.size() == 1 && text[0].length() == 0;
+
// Get the highlighted words.
String highlighted_text = get_selected_text();
@@ -656,7 +695,7 @@ void TextEdit::_notification(int p_what) {
int first_visible_line = get_first_visible_line() - 1;
int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
- draw_amount += get_line_wrap_count(first_visible_line + 1);
+ draw_amount += draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_line_wrap_count(first_visible_line + 1);
// Draw minimap.
if (draw_minimap) {
@@ -841,7 +880,7 @@ void TextEdit::_notification(int p_what) {
// Draw main text.
caret.visible = false;
line_drawing_cache.clear();
- int row_height = get_line_height();
+ int row_height = draw_placeholder ? placeholder_line_height + line_spacing : get_line_height();
int line = first_visible_line;
for (int i = 0; i < draw_amount; i++) {
line++;
@@ -867,11 +906,14 @@ void TextEdit::_notification(int p_what) {
// Ensure we at least use the font color.
Color current_color = !editable ? font_readonly_color : font_color;
+ if (draw_placeholder) {
+ current_color = font_placeholder_color;
+ }
- const Ref<TextParagraph> ldata = text.get_line_data(line);
+ const Ref<TextParagraph> ldata = draw_placeholder ? placeholder_data_buf : text.get_line_data(line);
- Vector<String> wrap_rows = get_line_wrapped_text(line);
- int line_wrap_amount = get_line_wrap_count(line);
+ Vector<String> wrap_rows = draw_placeholder ? placeholder_wraped_rows : get_line_wrapped_text(line);
+ int line_wrap_amount = draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_line_wrap_count(line);
for (int line_wrap_index = 0; line_wrap_index <= line_wrap_amount; line_wrap_index++) {
if (line_wrap_index != 0) {
@@ -1008,15 +1050,17 @@ void TextEdit::_notification(int p_what) {
icon->draw_rect(ci, gutter_rect, false, get_line_gutter_item_color(line, g));
} break;
case GUTTER_TYPE_CUSTOM: {
- if (gutter.custom_draw_obj.is_valid()) {
- Object *cdo = ObjectDB::get_instance(gutter.custom_draw_obj);
- if (cdo) {
- Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height));
- if (rtl) {
- gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x;
- }
- cdo->call(gutter.custom_draw_callback, line, g, Rect2(gutter_rect));
+ if (gutter.custom_draw_callback.is_valid()) {
+ Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height));
+ if (rtl) {
+ gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x;
}
+
+ Variant args[3] = { line, g, Rect2(gutter_rect) };
+ const Variant *argp[] = { &args[0], &args[1], &args[2] };
+ Callable::CallError ce;
+ Variant ret;
+ gutter.custom_draw_callback.call(argp, 3, ret, ce);
}
} break;
}
@@ -1383,7 +1427,9 @@ void TextEdit::_notification(int p_what) {
}
}
- line_drawing_cache[line] = cache_entry;
+ if (draw_placeholder) {
+ line_drawing_cache[line] = cache_entry;
+ }
}
if (has_focus()) {
@@ -1433,9 +1479,11 @@ void TextEdit::_notification(int p_what) {
DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id());
DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id());
}
- ime_text = "";
- ime_selection = Point2();
- text.invalidate_cache(caret.line, caret.column, ime_text);
+ if (!ime_text.is_empty()) {
+ ime_text = "";
+ ime_selection = Point2();
+ text.invalidate_cache(caret.line, caret.column, true, ime_text);
+ }
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
@@ -1457,7 +1505,7 @@ void TextEdit::_notification(int p_what) {
t = ime_text;
}
- text.invalidate_cache(caret.line, caret.column, t, structured_text_parser(st_parser, st_args, t));
+ text.invalidate_cache(caret.line, caret.column, true, t, structured_text_parser(st_parser, st_args, t));
update();
}
} break;
@@ -2045,6 +2093,7 @@ void TextEdit::_new_line(bool p_split_current_line, bool p_above) {
bool first_line = false;
if (!p_split_current_line) {
+ deselect();
if (p_above) {
if (caret.line > 0) {
set_caret_line(caret.line - 1, false);
@@ -2430,6 +2479,47 @@ void TextEdit::_move_caret_document_end(bool p_select) {
}
}
+void TextEdit::_update_placeholder() {
+ if (font.is_null() || font_size <= 0) {
+ return; // Not in tree?
+ }
+
+ // Placeholder is generally smaller then text docuemnts, and updates less so this should be fast enough for now.
+ placeholder_data_buf->clear();
+ placeholder_data_buf->set_width(text.get_width());
+ placeholder_data_buf->set_direction((TextServer::Direction)text_direction);
+ placeholder_data_buf->set_preserve_control(draw_control_chars);
+ placeholder_data_buf->add_string(placeholder_text, font, font_size, opentype_features, language);
+
+ placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_text);
+ if (placeholder_bidi_override.is_empty()) {
+ TS->shaped_text_set_bidi_override(placeholder_data_buf->get_rid(), placeholder_bidi_override);
+ }
+
+ if (get_tab_size() > 0) {
+ Vector<float> tabs;
+ tabs.push_back(font->get_char_size(' ', 0, font_size).width * get_tab_size());
+ placeholder_data_buf->tab_align(tabs);
+ }
+
+ // Update height.
+ const int wrap_amount = placeholder_data_buf->get_line_count() - 1;
+ placeholder_line_height = font->get_height(font_size);
+ for (int i = 0; i <= wrap_amount; i++) {
+ placeholder_line_height = MAX(placeholder_line_height, placeholder_data_buf->get_line_size(i).y);
+ }
+
+ // Update width.
+ placeholder_max_width = placeholder_data_buf->get_size().x;
+
+ // Update wrapped rows.
+ placeholder_wraped_rows.clear();
+ for (int i = 0; i <= wrap_amount; i++) {
+ Vector2i line_range = placeholder_data_buf->get_line_range(i);
+ placeholder_wraped_rows.push_back(placeholder_text.substr(line_range.x, line_range.y - line_range.x));
+ }
+}
+
void TextEdit::_update_caches() {
/* Internal API for CodeEdit. */
brace_mismatch_color = get_theme_color(SNAME("brace_mismatch_color"), SNAME("CodeEdit"));
@@ -2460,6 +2550,7 @@ void TextEdit::_update_caches() {
font_size = get_theme_font_size(SNAME("font_size"));
font_color = get_theme_color(SNAME("font_color"));
font_readonly_color = get_theme_color(SNAME("font_readonly_color"));
+ font_placeholder_color = get_theme_color(SNAME("font_placeholder_color"));
outline_size = get_theme_constant(SNAME("outline_size"));
outline_color = get_theme_color(SNAME("font_outline_color"));
@@ -2482,7 +2573,8 @@ void TextEdit::_update_caches() {
text.set_draw_control_chars(draw_control_chars);
text.set_font(font);
text.set_font_size(font_size);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
/* Syntax highlighting. */
if (syntax_highlighter.is_valid()) {
@@ -2598,8 +2690,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
}
String TextEdit::get_tooltip(const Point2 &p_pos) const {
- Object *tooltip_obj = ObjectDB::get_instance(tooltip_obj_id);
- if (!tooltip_obj) {
+ if (!tooltip_callback.is_valid()) {
return Control::get_tooltip(p_pos);
}
Point2i pos = get_line_column_at_pos(p_pos);
@@ -2612,19 +2703,20 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const {
}
int beg, end;
if (select_word(s, col, beg, end)) {
- String tt = tooltip_obj->call(tooltip_func, s.substr(beg, end - beg), tooltip_ud);
-
- return tt;
+ Variant args[1] = { s.substr(beg, end - beg) };
+ const Variant *argp[] = { &args[0] };
+ Callable::CallError ce;
+ Variant ret;
+ tooltip_callback.call(argp, 1, ret, ce);
+ ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, "", "Failed to call custom tooltip.");
+ return ret;
}
return Control::get_tooltip(p_pos);
}
-void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata) {
- ERR_FAIL_NULL(p_obj);
- tooltip_obj_id = p_obj->get_instance_id();
- tooltip_func = p_function;
- tooltip_ud = p_udata;
+void TextEdit::set_tooltip_request_func(const Callable &p_tooltip_callback) {
+ tooltip_callback = p_tooltip_callback;
}
/* Text */
@@ -2661,7 +2753,8 @@ void TextEdit::set_text_direction(Control::TextDirection p_text_direction) {
dir = (TextServer::Direction)text_direction;
}
text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
if (menu_dir) {
menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED);
@@ -2682,7 +2775,8 @@ void TextEdit::set_opentype_feature(const String &p_name, int p_value) {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
opentype_features[tag] = p_value;
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
update();
}
}
@@ -2698,7 +2792,8 @@ int TextEdit::get_opentype_feature(const String &p_name) const {
void TextEdit::clear_opentype_features() {
opentype_features.clear();
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
update();
}
@@ -2713,6 +2808,7 @@ void TextEdit::set_language(const String &p_language) {
}
text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
text.invalidate_all();
+ _update_placeholder();
update();
}
}
@@ -2754,6 +2850,7 @@ void TextEdit::set_tab_size(const int p_size) {
}
text.set_tab_size(p_size);
text.invalidate_all_lines();
+ _update_placeholder();
update();
}
@@ -2812,6 +2909,17 @@ void TextEdit::clear() {
}
void TextEdit::_clear() {
+ if (editable && undo_enabled) {
+ _move_caret_document_start(false);
+ begin_complex_operation();
+
+ _remove_text(0, 0, MAX(0, get_line_count() - 1), MAX(get_line(MAX(get_line_count() - 1, 0)).size() - 1, 0));
+ insert_text_at_caret("");
+ text.clear();
+
+ end_complex_operation();
+ return;
+ }
clear_undo_history();
text.clear();
caret.column = 0;
@@ -2865,6 +2973,16 @@ int TextEdit::get_line_count() const {
return text.size();
}
+void TextEdit::set_placeholder(const String &p_text) {
+ placeholder_text = p_text;
+ _update_placeholder();
+ update();
+}
+
+String TextEdit::get_placeholder() const {
+ return placeholder_text;
+}
+
void TextEdit::set_line(int p_line, const String &p_new_text) {
if (p_line < 0 || p_line >= text.size()) {
return;
@@ -4647,12 +4765,10 @@ void TextEdit::merge_gutters(int p_from_line, int p_to_line) {
update();
}
-void TextEdit::set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback) {
+void TextEdit::set_gutter_custom_draw(int p_gutter, const Callable &p_draw_callback) {
ERR_FAIL_INDEX(p_gutter, gutters.size());
- ERR_FAIL_NULL(p_object);
- gutters.write[p_gutter].custom_draw_obj = p_object->get_instance_id();
- gutters.write[p_gutter].custom_draw_callback = p_callback;
+ gutters.write[p_gutter].custom_draw_callback = p_draw_callback;
update();
}
@@ -4771,7 +4887,8 @@ void TextEdit::set_draw_control_chars(bool p_enabled) {
menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
}
text.set_draw_control_chars(draw_control_chars);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
update();
}
}
@@ -4851,6 +4968,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &TextEdit::get_text);
ClassDB::bind_method(D_METHOD("get_line_count"), &TextEdit::get_line_count);
+ ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &TextEdit::set_placeholder);
+ ClassDB::bind_method(D_METHOD("get_placeholder"), &TextEdit::get_placeholder);
+
ClassDB::bind_method(D_METHOD("set_line", "line", "new_text"), &TextEdit::set_line);
ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line);
@@ -4942,7 +5062,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("search", "text", "flags", "from_line", "from_colum"), &TextEdit::search);
/* Tooltip */
- ClassDB::bind_method(D_METHOD("set_tooltip_request_func", "object", "callback", "data"), &TextEdit::set_tooltip_request_func);
+ ClassDB::bind_method(D_METHOD("set_tooltip_request_func", "callback"), &TextEdit::set_tooltip_request_func);
/* Mouse */
ClassDB::bind_method(D_METHOD("get_local_mouse_pos"), &TextEdit::get_local_mouse_pos);
@@ -5114,7 +5234,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_gutter_overwritable", "gutter", "overwritable"), &TextEdit::set_gutter_overwritable);
ClassDB::bind_method(D_METHOD("is_gutter_overwritable", "gutter"), &TextEdit::is_gutter_overwritable);
ClassDB::bind_method(D_METHOD("merge_gutters", "from_line", "to_line"), &TextEdit::merge_gutters);
- ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "object", "callback"), &TextEdit::set_gutter_custom_draw);
+ ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "draw_callback"), &TextEdit::set_gutter_custom_draw);
ClassDB::bind_method(D_METHOD("get_total_gutter_width"), &TextEdit::get_total_gutter_width);
// Line gutters.
@@ -5159,8 +5279,9 @@ void TextEdit::_bind_methods() {
/* Inspector */
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text", PROPERTY_HINT_MULTILINE_TEXT), "set_placeholder", "get_placeholder");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
@@ -5233,14 +5354,16 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
update();
}
} else {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
+ _update_placeholder();
update();
}
}
@@ -5892,6 +6015,7 @@ void TextEdit::_update_wrap_at_column(bool p_force) {
text.set_width(-1);
}
text.invalidate_all_lines();
+ _update_placeholder();
}
_update_caret_wrap_offset();
@@ -5919,14 +6043,16 @@ void TextEdit::_update_scrollbars() {
h_scroll->set_begin(Point2(0, size.height - hmin.height));
h_scroll->set_end(Point2(size.width - vmin.width, size.height));
+ bool draw_placeholder = text.size() == 1 && text[0].length() == 0;
+
int visible_rows = get_visible_line_count();
- int total_rows = get_total_visible_line_count();
+ int total_rows = draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_total_visible_line_count();
if (scroll_past_end_of_file_enabled) {
total_rows += visible_rows - 1;
}
int visible_width = size.width - style_normal->get_minimum_size().width;
- int total_width = text.get_max_width() + vmin.x + gutters_width + gutter_padding;
+ int total_width = (draw_placeholder ? placeholder_max_width : text.get_max_width()) + vmin.x + gutters_width + gutter_padding;
if (draw_minimap) {
total_width += minimap_width;
@@ -5998,20 +6124,22 @@ void TextEdit::_scroll_moved(double p_to_val) {
}
if (v_scroll->is_visible_in_tree()) {
// Set line ofs and wrap ofs.
+ bool draw_placeholder = text.size() == 1 && text[0].length() == 0;
+
int v_scroll_i = floor(get_v_scroll());
int sc = 0;
int n_line;
for (n_line = 0; n_line < text.size(); n_line++) {
if (!_is_line_hidden(n_line)) {
sc++;
- sc += get_line_wrap_count(n_line);
+ sc += draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_line_wrap_count(n_line);
if (sc > v_scroll_i) {
break;
}
}
}
n_line = MIN(n_line, text.size() - 1);
- int line_wrap_amount = get_line_wrap_count(n_line);
+ int line_wrap_amount = draw_placeholder ? placeholder_wraped_rows.size() - 1 : get_line_wrap_count(n_line);
int wi = line_wrap_amount - (sc - v_scroll_i - 1);
wi = CLAMP(wi, 0, line_wrap_amount);
@@ -6439,6 +6567,8 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
}
TextEdit::TextEdit() {
+ placeholder_data_buf.instantiate();
+
clear();
set_focus_mode(FOCUS_ALL);
_update_caches();
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 57b48b5f52..83a63ae40a 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -120,8 +120,7 @@ private:
bool clickable = false;
bool overwritable = false;
- ObjectID custom_draw_obj = ObjectID();
- StringName custom_draw_callback;
+ Callable custom_draw_callback;
};
class Text {
@@ -190,6 +189,7 @@ private:
int get_max_width() const;
void set_width(float p_width);
+ float get_width() const;
int get_line_wrap_amount(int p_line) const;
Vector<Vector2i> get_line_wrap_ranges(int p_line) const;
@@ -210,7 +210,8 @@ private:
int size() const { return text.size(); }
void clear();
- void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Array &p_bidi_override = Array());
+ void invalidate_cache(int p_line, int p_column = -1, bool p_text_changed = false, const String &p_ime_text = String(), const Array &p_bidi_override = Array());
+ void invalidate_font();
void invalidate_all();
void invalidate_all_lines();
@@ -250,6 +251,17 @@ private:
String ime_text = "";
Point2 ime_selection;
+ // Placeholder
+ String placeholder_text = "";
+ Array placeholder_bidi_override;
+ Ref<TextParagraph> placeholder_data_buf;
+ int placeholder_line_height = -1;
+ int placeholder_max_width = -1;
+
+ Vector<String> placeholder_wraped_rows;
+
+ void _update_placeholder();
+
/* Initialise to opposite first, so we get past the early-out in set_editable. */
bool editable = false;
@@ -332,9 +344,7 @@ private:
int _get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) const;
/* Tooltip. */
- ObjectID tooltip_obj_id;
- StringName tooltip_func;
- Variant tooltip_ud;
+ Callable tooltip_callback;
/* Mouse */
struct LineDrawingCache {
@@ -514,6 +524,7 @@ private:
int font_size = 16;
Color font_color = Color(1, 1, 1);
Color font_readonly_color = Color(1, 1, 1);
+ Color font_placeholder_color = Color(1, 1, 1, 0.6);
int outline_size = 0;
Color outline_color = Color(1, 1, 1);
@@ -620,7 +631,7 @@ public:
virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
virtual void drop_data(const Point2 &p_point, const Variant &p_data) override;
virtual String get_tooltip(const Point2 &p_pos) const override;
- void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata);
+ void set_tooltip_request_func(const Callable &p_tooltip_callback);
/* Text */
// Text properties.
@@ -670,6 +681,9 @@ public:
String get_text() const;
int get_line_count() const;
+ void set_placeholder(const String &p_text);
+ String get_placeholder() const;
+
void set_line(int p_line, const String &p_new_text);
String get_line(int p_line) const;
@@ -884,7 +898,7 @@ public:
void merge_gutters(int p_from_line, int p_to_line);
- void set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback);
+ void set_gutter_custom_draw(int p_gutter, const Callable &p_draw_callback);
// Line gutters.
void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata);
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index da202c1c8f..89a17ae854 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -173,7 +173,8 @@ void TextureButton::_notification(int p_what) {
bool draw_focus = (has_focus() && focused.is_valid());
// If no other texture is valid, try using focused texture.
- if (!texdraw.is_valid() && draw_focus) {
+ bool draw_focus_only = draw_focus && !texdraw.is_valid();
+ if (draw_focus_only) {
texdraw = focused;
}
@@ -232,7 +233,7 @@ void TextureButton::_notification(int p_what) {
size.width *= hflip ? -1.0f : 1.0f;
size.height *= vflip ? -1.0f : 1.0f;
- if (texdraw == focused) {
+ if (draw_focus_only) {
// Do nothing, we only needed to calculate the rectangle.
} else if (_tile) {
draw_texture_rect(texdraw, Rect2(ofs, size), _tile);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index e46de43f1e..1b32884880 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -198,6 +198,65 @@ bool TreeItem::is_indeterminate(int p_column) const {
return cells[p_column].indeterminate;
}
+void TreeItem::propagate_check(int p_column, bool p_emit_signal) {
+ bool ch = cells[p_column].checked;
+
+ if (p_emit_signal) {
+ tree->emit_signal("check_propagated_to_item", this, p_column);
+ }
+ _propagate_check_through_children(p_column, ch, p_emit_signal);
+ _propagate_check_through_parents(p_column, p_emit_signal);
+}
+
+void TreeItem::_propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal) {
+ TreeItem *current = get_first_child();
+ while (current) {
+ current->set_checked(p_column, p_checked);
+ if (p_emit_signal) {
+ current->tree->emit_signal("check_propagated_to_item", current, p_column);
+ }
+ current->_propagate_check_through_children(p_column, p_checked, p_emit_signal);
+ current = current->get_next();
+ }
+}
+
+void TreeItem::_propagate_check_through_parents(int p_column, bool p_emit_signal) {
+ TreeItem *current = get_parent();
+ if (!current) {
+ return;
+ }
+
+ bool all_unchecked_and_not_indeterminate = true;
+ bool any_unchecked_or_indeterminate = false;
+
+ TreeItem *child_item = current->get_first_child();
+ while (child_item) {
+ if (!child_item->is_checked(p_column)) {
+ any_unchecked_or_indeterminate = true;
+ if (child_item->is_indeterminate(p_column)) {
+ all_unchecked_and_not_indeterminate = false;
+ break;
+ }
+ } else {
+ all_unchecked_and_not_indeterminate = false;
+ }
+ child_item = child_item->get_next();
+ }
+
+ if (all_unchecked_and_not_indeterminate) {
+ current->set_checked(p_column, false);
+ } else if (any_unchecked_or_indeterminate) {
+ current->set_indeterminate(p_column, true);
+ } else {
+ current->set_checked(p_column, true);
+ }
+
+ if (p_emit_signal) {
+ current->tree->emit_signal("check_propagated_to_item", current, p_column);
+ }
+ current->_propagate_check_through_parents(p_column, p_emit_signal);
+}
+
void TreeItem::set_text(int p_column, String p_text) {
ERR_FAIL_INDEX(p_column, cells.size());
cells.write[p_column].text = p_text;
@@ -496,8 +555,9 @@ void TreeItem::uncollapse_tree() {
void TreeItem::set_custom_minimum_height(int p_height) {
custom_min_height = p_height;
- for (Cell &c : cells)
+ for (Cell &c : cells) {
c.cached_minimum_size_dirty = true;
+ }
_changed_notify();
}
@@ -1033,8 +1093,9 @@ bool TreeItem::get_expand_right(int p_column) const {
void TreeItem::set_disable_folding(bool p_disable) {
disable_folding = p_disable;
- for (Cell &c : cells)
+ for (Cell &c : cells) {
c.cached_minimum_size_dirty = true;
+ }
_changed_notify(0);
}
@@ -1141,6 +1202,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_checked", "column"), &TreeItem::is_checked);
ClassDB::bind_method(D_METHOD("is_indeterminate", "column"), &TreeItem::is_indeterminate);
+ ClassDB::bind_method(D_METHOD("propagate_check", "column", "emit_signal"), &TreeItem::propagate_check, DEFVAL(true));
+
ClassDB::bind_method(D_METHOD("set_text", "column", "text"), &TreeItem::set_text);
ClassDB::bind_method(D_METHOD("get_text", "column"), &TreeItem::get_text);
@@ -1256,10 +1319,10 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
ClassDB::bind_method(D_METHOD("get_index"), &TreeItem::get_index);
- ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::_move_before);
- ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::_move_after);
+ ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::move_before);
+ ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::move_after);
- ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child);
+ ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::remove_child);
{
MethodInfo mi;
@@ -4752,7 +4815,7 @@ bool Tree::get_allow_reselect() const {
void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &Tree::clear);
- ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::_create_item, DEFVAL(Variant()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::create_item, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_root"), &Tree::get_root);
ClassDB::bind_method(D_METHOD("set_column_custom_minimum_width", "column", "min_width"), &Tree::set_column_custom_minimum_width);
@@ -4767,7 +4830,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_root", "enable"), &Tree::set_hide_root);
ClassDB::bind_method(D_METHOD("is_root_hidden"), &Tree::is_root_hidden);
- ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::_get_next_selected);
+ ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::get_next_selected);
ClassDB::bind_method(D_METHOD("get_selected"), &Tree::get_selected);
ClassDB::bind_method(D_METHOD("get_selected_column"), &Tree::get_selected_column);
ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button);
@@ -4781,7 +4844,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_edited_column"), &Tree::get_edited_column);
ClassDB::bind_method(D_METHOD("edit_selected"), &Tree::edit_selected);
ClassDB::bind_method(D_METHOD("get_custom_popup_rect"), &Tree::get_custom_popup_rect);
- ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column"), &Tree::_get_item_rect, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column"), &Tree::get_item_rect, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_item_at_position", "position"), &Tree::get_item_at_position);
ClassDB::bind_method(D_METHOD("get_column_at_position", "position"), &Tree::get_column_at_position);
ClassDB::bind_method(D_METHOD("get_drop_section_at_position", "position"), &Tree::get_drop_section_at_position);
@@ -4805,7 +4868,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language);
ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll);
- ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::_scroll_to_item);
+ ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::scroll_to_item);
ClassDB::bind_method(D_METHOD("set_h_scroll_enabled", "h_scroll"), &Tree::set_h_scroll_enabled);
ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &Tree::is_h_scroll_enabled);
@@ -4847,6 +4910,7 @@ void Tree::_bind_methods() {
ADD_SIGNAL(MethodInfo("item_custom_button_pressed"));
ADD_SIGNAL(MethodInfo("item_double_clicked"));
ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem")));
+ ADD_SIGNAL(MethodInfo("check_propagated_to_item", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column")));
//ADD_SIGNAL( MethodInfo("item_double_clicked" ) );
ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("custom_popup_edited", PropertyInfo(Variant::BOOL, "arrow_clicked")));
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index c60c87564e..c24763a0e4 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -188,16 +188,6 @@ protected:
return d;
}
- void _remove_child(Object *p_child) {
- remove_child(Object::cast_to<TreeItem>(p_child));
- }
-
- void _move_before(Object *p_item) {
- move_before(Object::cast_to<TreeItem>(p_item));
- }
- void _move_after(Object *p_item) {
- move_after(Object::cast_to<TreeItem>(p_item));
- }
Variant _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
@@ -212,6 +202,14 @@ public:
bool is_checked(int p_column) const;
bool is_indeterminate(int p_column) const;
+ void propagate_check(int p_column, bool p_emit_signal = true);
+
+private:
+ // Check helpers.
+ void _propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal);
+ void _propagate_check_through_parents(int p_column, bool p_emit_signal);
+
+public:
void set_text(int p_column, String p_text);
String get_text(int p_column) const;
@@ -609,23 +607,6 @@ private:
protected:
static void _bind_methods();
- //bind helpers
- TreeItem *_create_item(Object *p_parent, int p_idx = -1) {
- return create_item(Object::cast_to<TreeItem>(p_parent), p_idx);
- }
-
- TreeItem *_get_next_selected(Object *p_item) {
- return get_next_selected(Object::cast_to<TreeItem>(p_item));
- }
-
- Rect2 _get_item_rect(Object *p_item, int p_column) const {
- return get_item_rect(Object::cast_to<TreeItem>(p_item), p_column);
- }
-
- void _scroll_to_item(Object *p_item) {
- scroll_to_item(Object::cast_to<TreeItem>(p_item));
- }
-
public:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp
index 375b3732a4..71865b4864 100644
--- a/scene/gui/view_panner.cpp
+++ b/scene/gui/view_panner.cpp
@@ -31,6 +31,7 @@
#include "view_panner.h"
#include "core/input/input.h"
+#include "core/input/shortcut.h"
#include "core/os/keyboard.h"
bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) {
@@ -40,7 +41,9 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
if (scroll_vec != Vector2()) {
if (control_scheme == SCROLL_PANS) {
if (mb->is_ctrl_pressed()) {
- callback_helper(zoom_callback, scroll_vec, mb->get_position());
+ scroll_vec.y *= mb->get_factor();
+ callback_helper(zoom_callback, varray(scroll_vec, mb->get_position(), mb->is_alt_pressed()));
+ return true;
} else {
Vector2 panning;
if (mb->is_shift_pressed()) {
@@ -50,8 +53,7 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
panning.y += mb->get_factor() * scroll_vec.y;
panning.x += mb->get_factor() * scroll_vec.x;
}
- callback_helper(scroll_callback, panning);
-
+ callback_helper(scroll_callback, varray(panning, mb->is_alt_pressed()));
return true;
}
} else {
@@ -64,23 +66,33 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
panning.y += mb->get_factor() * scroll_vec.y;
panning.x += mb->get_factor() * scroll_vec.x;
}
- callback_helper(scroll_callback, panning);
-
+ callback_helper(scroll_callback, varray(panning, mb->is_alt_pressed()));
return true;
} else if (!mb->is_shift_pressed()) {
- callback_helper(zoom_callback, scroll_vec, mb->get_position());
+ scroll_vec.y *= mb->get_factor();
+ callback_helper(zoom_callback, varray(scroll_vec, mb->get_position(), mb->is_alt_pressed()));
return true;
}
}
}
- if (mb->get_button_index() == MouseButton::MIDDLE || (mb->get_button_index() == MouseButton::RIGHT && !disable_rmb) || (mb->get_button_index() == MouseButton::LEFT && (Input::get_singleton()->is_key_pressed(Key::SPACE) || !mb->is_pressed()))) {
+ // Alt is not used for button presses, so ignore it.
+ if (mb->is_alt_pressed()) {
+ return false;
+ }
+
+ bool is_drag_event = mb->get_button_index() == MouseButton::MIDDLE ||
+ (enable_rmb && mb->get_button_index() == MouseButton::RIGHT) ||
+ (!simple_panning_enabled && mb->get_button_index() == MouseButton::LEFT && is_panning()) ||
+ (force_drag && mb->get_button_index() == MouseButton::LEFT);
+
+ if (is_drag_event) {
if (mb->is_pressed()) {
is_dragging = true;
} else {
is_dragging = false;
}
- return true;
+ return mb->get_button_index() != MouseButton::LEFT || mb->is_pressed(); // Don't consume LMB release events (it fixes some selection problems).
}
}
@@ -88,9 +100,20 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
if (mm.is_valid()) {
if (is_dragging) {
if (p_canvas_rect != Rect2()) {
- callback_helper(pan_callback, Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect));
+ callback_helper(pan_callback, varray(Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect)));
} else {
- callback_helper(pan_callback, mm->get_relative());
+ callback_helper(pan_callback, varray(mm->get_relative()));
+ }
+ return true;
+ }
+ }
+
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid()) {
+ if (pan_view_shortcut.is_valid() && pan_view_shortcut->matches_event(k)) {
+ pan_key_pressed = k->is_pressed();
+ if (simple_panning_enabled || (Input::get_singleton()->get_mouse_button_mask() & MouseButton::LEFT) != MouseButton::NONE) {
+ is_dragging = pan_key_pressed;
}
return true;
}
@@ -99,26 +122,20 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
return false;
}
-void ViewPanner::callback_helper(Callable p_callback, Vector2 p_arg1, Vector2 p_arg2) {
- if (p_callback == zoom_callback) {
- const Variant **argptr = (const Variant **)alloca(sizeof(Variant *) * 2);
- Variant var1 = p_arg1;
- argptr[0] = &var1;
- Variant var2 = p_arg2;
- argptr[1] = &var2;
-
- Variant result;
- Callable::CallError ce;
- p_callback.call(argptr, 2, result, ce);
- } else {
- const Variant **argptr = (const Variant **)alloca(sizeof(Variant *));
- Variant var = p_arg1;
- argptr[0] = &var;
-
- Variant result;
- Callable::CallError ce;
- p_callback.call(argptr, 1, result, ce);
+void ViewPanner::release_pan_key() {
+ pan_key_pressed = false;
+ is_dragging = false;
+}
+
+void ViewPanner::callback_helper(Callable p_callback, Vector<Variant> p_args) {
+ const Variant **argptr = (const Variant **)alloca(sizeof(Variant *) * p_args.size());
+ for (int i = 0; i < p_args.size(); i++) {
+ argptr[i] = &p_args[i];
}
+
+ Variant result;
+ Callable::CallError ce;
+ p_callback.call(argptr, p_args.size(), result, ce);
}
void ViewPanner::set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback) {
@@ -131,6 +148,37 @@ void ViewPanner::set_control_scheme(ControlScheme p_scheme) {
control_scheme = p_scheme;
}
-void ViewPanner::set_disable_rmb(bool p_disable) {
- disable_rmb = p_disable;
+void ViewPanner::set_enable_rmb(bool p_enable) {
+ enable_rmb = p_enable;
+}
+
+void ViewPanner::set_pan_shortcut(Ref<Shortcut> p_shortcut) {
+ pan_view_shortcut = p_shortcut;
+ pan_key_pressed = false;
+}
+
+void ViewPanner::set_simple_panning_enabled(bool p_enabled) {
+ simple_panning_enabled = p_enabled;
+}
+
+void ViewPanner::setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning) {
+ set_control_scheme(p_scheme);
+ set_pan_shortcut(p_shortcut);
+ set_simple_panning_enabled(p_simple_panning);
+}
+
+bool ViewPanner::is_panning() const {
+ return is_dragging || pan_key_pressed;
+}
+
+void ViewPanner::set_force_drag(bool p_force) {
+ force_drag = p_force;
+}
+
+ViewPanner::ViewPanner() {
+ Array inputs;
+ inputs.append(InputEventKey::create_reference(Key::SPACE));
+
+ pan_view_shortcut.instantiate();
+ pan_view_shortcut->set_events(inputs);
}
diff --git a/scene/gui/view_panner.h b/scene/gui/view_panner.h
index e083d83de4..5b820c5f8f 100644
--- a/scene/gui/view_panner.h
+++ b/scene/gui/view_panner.h
@@ -34,30 +34,50 @@
#include "core/object/ref_counted.h"
class InputEvent;
+class Shortcut;
class ViewPanner : public RefCounted {
GDCLASS(ViewPanner, RefCounted);
+public:
+ enum ControlScheme {
+ SCROLL_ZOOMS,
+ SCROLL_PANS,
+ };
+
+private:
bool is_dragging = false;
- bool disable_rmb = false;
+ bool pan_key_pressed = false;
+ bool force_drag = false;
+
+ bool enable_rmb = false;
+ bool simple_panning_enabled = false;
+
+ Ref<Shortcut> pan_view_shortcut;
Callable scroll_callback;
Callable pan_callback;
Callable zoom_callback;
- void callback_helper(Callable p_callback, Vector2 p_arg1, Vector2 p_arg2 = Vector2());
-
-public:
- enum ControlScheme {
- SCROLL_ZOOMS,
- SCROLL_PANS,
- };
+ void callback_helper(Callable p_callback, Vector<Variant> p_args);
ControlScheme control_scheme = SCROLL_ZOOMS;
+public:
void set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback);
void set_control_scheme(ControlScheme p_scheme);
- void set_disable_rmb(bool p_disable);
+ void set_enable_rmb(bool p_enable);
+ void set_pan_shortcut(Ref<Shortcut> p_shortcut);
+ void set_simple_panning_enabled(bool p_enabled);
+
+ void setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning);
+
+ bool is_panning() const;
+ void set_force_drag(bool p_force);
+
bool gui_input(const Ref<InputEvent> &p_ev, Rect2 p_canvas_rect = Rect2());
+ void release_pan_key();
+
+ ViewPanner();
};
#endif // VIEW_PANNER_H
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 59d933fa1d..a0916c6291 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -1017,8 +1017,8 @@ void CanvasItem::set_notify_transform(bool p_enable) {
notify_transform = p_enable;
if (notify_transform && is_inside_tree()) {
- //this ensures that invalid globals get resolved, so notifications can be received
- get_global_transform();
+ // This ensures that invalid globals get resolved, so notifications can be received.
+ _ALLOW_DISCARD_ get_global_transform();
}
}
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 34cc4aceb8..4e91548d14 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -46,7 +46,7 @@ Error HTTPRequest::_parse_url(const String &p_url) {
request_sent = false;
got_response = false;
body_len = -1;
- body.resize(0);
+ body.clear();
downloaded.set(0);
redirections = 0;
@@ -86,9 +86,9 @@ String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const S
String lowwer_case_header_name = p_header_name.to_lower();
for (int i = 0; i < p_headers.size(); i++) {
- if (p_headers[i].find(":", 0) >= 0) {
+ if (p_headers[i].find(":") > 0) {
Vector<String> parts = p_headers[i].split(":", false, 1);
- if (parts[0].strip_edges().to_lower() == lowwer_case_header_name) {
+ if (parts.size() > 1 && parts[0].strip_edges().to_lower() == lowwer_case_header_name) {
value = parts[1].strip_edges();
break;
}
@@ -99,7 +99,7 @@ String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const S
}
Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const String &p_request_data) {
- // Copy the string into a raw buffer
+ // Copy the string into a raw buffer.
Vector<uint8_t> raw_data;
CharString charstr = p_request_data.utf8();
@@ -134,7 +134,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust
headers = p_custom_headers;
if (accept_gzip) {
- // If the user has specified a different Accept-Encoding, don't overwrite it
+ // If the user has specified an Accept-Encoding header, don't overwrite it.
if (!has_header(headers, "Accept-Encoding")) {
headers.push_back("Accept-Encoding: gzip, deflate");
}
@@ -202,7 +202,7 @@ void HTTPRequest::cancel_request() {
file = nullptr;
}
client->close();
- body.resize(0);
+ body.clear();
got_response = false;
response_code = -1;
request_sent = false;
@@ -220,14 +220,14 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
response_code = client->get_response_code();
List<String> rheaders;
client->get_response_headers(&rheaders);
- response_headers.resize(0);
+ response_headers.clear();
downloaded.set(0);
for (const String &E : rheaders) {
response_headers.push_back(E);
}
if (response_code == 301 || response_code == 302) {
- // Handle redirect
+ // Handle redirect.
if (max_redirects >= 0 && redirections >= max_redirects) {
call_deferred(SNAME("_request_done"), RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray());
@@ -244,12 +244,12 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
}
if (!new_request.is_empty()) {
- // Process redirect
+ // Process redirect.
client->close();
- int new_redirs = redirections + 1; // Because _request() will clear it
+ int new_redirs = redirections + 1; // Because _request() will clear it.
Error err;
if (new_request.begins_with("http")) {
- // New url, request all again
+ // New url, new request.
_parse_url(new_request);
} else {
request_string = new_request;
@@ -260,7 +260,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
request_sent = false;
got_response = false;
body_len = -1;
- body.resize(0);
+ body.clear();
downloaded.set(0);
redirections = new_redirs;
*ret_value = false;
@@ -276,11 +276,11 @@ bool HTTPRequest::_update_connection() {
switch (client->get_status()) {
case HTTPClient::STATUS_DISCONNECTED: {
call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
- return true; // End it, since it's doing something
+ return true; // End it, since it's disconnected.
} break;
case HTTPClient::STATUS_RESOLVING: {
client->poll();
- // Must wait
+ // Must wait.
return false;
} break;
case HTTPClient::STATUS_CANT_RESOLVE: {
@@ -290,9 +290,9 @@ bool HTTPRequest::_update_connection() {
} break;
case HTTPClient::STATUS_CONNECTING: {
client->poll();
- // Must wait
+ // Must wait.
return false;
- } break; // Connecting to IP
+ } break; // Connecting to IP.
case HTTPClient::STATUS_CANT_CONNECT: {
call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
return true;
@@ -301,7 +301,7 @@ bool HTTPRequest::_update_connection() {
case HTTPClient::STATUS_CONNECTED: {
if (request_sent) {
if (!got_response) {
- // No body
+ // No body.
bool ret_value;
@@ -313,16 +313,16 @@ bool HTTPRequest::_update_connection() {
return true;
}
if (body_len < 0) {
- // Chunked transfer is done
+ // Chunked transfer is done.
call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body);
return true;
}
call_deferred(SNAME("_request_done"), RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray());
return true;
- // Request might have been done
+ // Request might have been done.
} else {
- // Did not request yet, do request
+ // Did not request yet, do request.
int size = request_data.size();
Error err = client->request(method, request_string, headers, size > 0 ? request_data.ptr() : nullptr, size);
@@ -334,13 +334,13 @@ bool HTTPRequest::_update_connection() {
request_sent = true;
return false;
}
- } break; // Connected: break requests only accepted here
+ } break; // Connected: break requests only accepted here.
case HTTPClient::STATUS_REQUESTING: {
- // Must wait, still requesting
+ // Must wait, still requesting.
client->poll();
return false;
- } break; // Request in progress
+ } break; // Request in progress.
case HTTPClient::STATUS_BODY: {
if (!got_response) {
bool ret_value;
@@ -411,7 +411,7 @@ bool HTTPRequest::_update_connection() {
return false;
- } break; // Request resulted in body: break which must be read
+ } break; // Request resulted in body: break which must be read.
case HTTPClient::STATUS_CONNECTION_ERROR: {
call_deferred(SNAME("_request_done"), RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
return true;
@@ -428,7 +428,7 @@ bool HTTPRequest::_update_connection() {
void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
cancel_request();
- // Determine if the request body is compressed
+ // Determine if the request body is compressed.
bool is_compressed;
String content_encoding = get_header_value(p_headers, "Content-Encoding").to_lower();
Compression::Mode mode;
@@ -442,30 +442,25 @@ void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArra
is_compressed = false;
}
- const PackedByteArray *data = nullptr;
-
if (accept_gzip && is_compressed && p_data.size() > 0) {
// Decompress request body
- PackedByteArray *decompressed = memnew(PackedByteArray);
- int result = Compression::decompress_dynamic(decompressed, body_size_limit, p_data.ptr(), p_data.size(), mode);
+ PackedByteArray decompressed;
+ int result = Compression::decompress_dynamic(&decompressed, body_size_limit, p_data.ptr(), p_data.size(), mode);
if (result == OK) {
- data = decompressed;
+ emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, decompressed);
+ return;
} else if (result == -5) {
WARN_PRINT("Decompressed size of HTTP response body exceeded body_size_limit");
p_status = RESULT_BODY_SIZE_LIMIT_EXCEEDED;
- // Just return the raw data if we failed to decompress it
- data = &p_data;
+ // Just return the raw data if we failed to decompress it.
} else {
WARN_PRINT("Failed to decompress HTTP response body");
p_status = RESULT_BODY_DECOMPRESS_FAILED;
- // Just return the raw data if we failed to decompress it
- data = &p_data;
+ // Just return the raw data if we failed to decompress it.
}
- } else {
- data = &p_data;
}
- emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, *data);
+ emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, p_data);
}
void HTTPRequest::_notification(int p_what) {
@@ -476,7 +471,6 @@ void HTTPRequest::_notification(int p_what) {
bool done = _update_connection();
if (done) {
set_process_internal(false);
- // cancel_request(); called from _request done now
}
}
@@ -624,7 +618,6 @@ void HTTPRequest::_bind_methods() {
ADD_SIGNAL(MethodInfo("request_completed", PropertyInfo(Variant::INT, "result"), PropertyInfo(Variant::INT, "response_code"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "headers"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "body")));
BIND_ENUM_CONSTANT(RESULT_SUCCESS);
- //BIND_ENUM_CONSTANT( RESULT_NO_BODY );
BIND_ENUM_CONSTANT(RESULT_CHUNKED_BODY_SIZE_MISMATCH);
BIND_ENUM_CONSTANT(RESULT_CANT_CONNECT);
BIND_ENUM_CONSTANT(RESULT_CANT_RESOLVE);
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 8b5883a742..a2415442f8 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1316,12 +1316,14 @@ Node *Node::get_node_or_null(const NodePath &p_path) const {
Node *Node::get_node(const NodePath &p_path) const {
Node *node = get_node_or_null(p_path);
- if (p_path.is_absolute()) {
- ERR_FAIL_COND_V_MSG(!node, nullptr,
- vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, get_path()));
- } else {
- ERR_FAIL_COND_V_MSG(!node, nullptr,
- vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, get_path()));
+ if (unlikely(!node)) {
+ if (p_path.is_absolute()) {
+ ERR_FAIL_V_MSG(nullptr,
+ vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, get_path()));
+ } else {
+ ERR_FAIL_V_MSG(nullptr,
+ vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, get_path()));
+ }
}
return node;
@@ -1986,6 +1988,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
#endif
node = res->instantiate(ges);
ERR_FAIL_COND_V(!node, nullptr);
+ node->set_scene_instance_load_placeholder(get_scene_instance_load_placeholder());
instantiated = true;
@@ -2185,7 +2188,7 @@ void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_re
}
Variant v = p_node->get(E.name);
- if (v.is_ref()) {
+ if (v.is_ref_counted()) {
RES res = v;
if (res.is_valid()) {
if (p_resource_remap.has(res)) {
@@ -2211,7 +2214,7 @@ void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resourc
}
Variant v = p_resource->get(E.name);
- if (v.is_ref()) {
+ if (v.is_ref_counted()) {
RES res = v;
if (res.is_valid()) {
if (p_resource_remap.has(res)) {
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index cafa4a43fd..45f04b28b9 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -130,7 +130,7 @@ SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_nod
E = group_map.insert(p_group, Group());
}
- ERR_FAIL_COND_V_MSG(E->get().nodes.find(p_node) != -1, &E->get(), "Already in group: " + p_group + ".");
+ ERR_FAIL_COND_V_MSG(E->get().nodes.has(p_node), &E->get(), "Already in group: " + p_group + ".");
E->get().nodes.push_back(p_node);
//E->get().last_tree_version=0;
E->get().changed = true;
@@ -1328,6 +1328,8 @@ SceneTree::SceneTree() {
root = memnew(Window);
root->set_process_mode(Node::PROCESS_MODE_PAUSABLE);
root->set_name("root");
+ root->set_title(ProjectSettings::get_singleton()->get("application/config/name"));
+
#ifndef _3D_DISABLED
if (!root->get_world_3d().is_valid()) {
root->set_world_3d(Ref<World3D>(memnew(World3D)));
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 4ad250ff54..09880ad6cf 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -492,20 +492,26 @@ void Viewport::_notification(int p_what) {
}
#endif // _3D_DISABLED
} break;
+ case NOTIFICATION_WM_MOUSE_ENTER: {
+ gui.mouse_in_window = true;
+ } break;
case NOTIFICATION_WM_MOUSE_EXIT: {
+ gui.mouse_in_window = false;
_drop_physics_mouseover();
-
- // Unlike on loss of focus (NOTIFICATION_WM_WINDOW_FOCUS_OUT), do not
- // drop the gui mouseover here, as a scrollbar may be dragged while the
- // mouse is outside the window (without the window having lost focus).
- // See bug #39634
+ _drop_mouse_over();
+ // When the mouse exits the window, we want to end mouse_over, but
+ // not mouse_focus, because, for example, we want to continue
+ // dragging a scrollbar even if the mouse has left the window.
} break;
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
_drop_physics_mouseover();
-
if (gui.mouse_focus && !gui.forced_mouse_focus) {
_drop_mouse_focus();
}
+ // When the window focus changes, we want to end mouse_focus, but
+ // not the mouse_over. Note: The OS will trigger a separate mouse
+ // exit event if the change in focus results in the mouse exiting
+ // the window.
} break;
}
}
@@ -523,6 +529,13 @@ void Viewport::_process_picking() {
_drop_physics_mouseover(true);
+#ifndef _3D_DISABLED
+ Vector2 last_pos(1e20, 1e20);
+ CollisionObject3D *last_object = nullptr;
+ ObjectID last_id;
+ PhysicsDirectSpaceState3D::RayResult result;
+#endif // _3D_DISABLED
+
PhysicsDirectSpaceState2D *ss2d = PhysicsServer2D::get_singleton()->space_get_direct_state(find_world_2d()->get_space());
if (physics_has_last_mousepos) {
@@ -690,10 +703,6 @@ void Viewport::_process_picking() {
}
#ifndef _3D_DISABLED
- Vector2 last_pos(1e20, 1e20);
- CollisionObject3D *last_object = nullptr;
- ObjectID last_id;
- PhysicsDirectSpaceState3D::RayResult result;
bool captured = false;
if (physics_object_capture.is_valid()) {
@@ -790,7 +799,7 @@ void Viewport::update_canvas_items() {
}
void Viewport::_set_size(const Size2i &p_size, const Size2i &p_size_2d_override, const Rect2i &p_to_screen_rect, const Transform2D &p_stretch_transform, bool p_allocated) {
- if (size == p_size && size_allocated == p_allocated && stretch_transform == p_stretch_transform && p_size_2d_override == size_2d_override && to_screen_rect != p_to_screen_rect) {
+ if (size == p_size && size_allocated == p_allocated && stretch_transform == p_stretch_transform && p_size_2d_override == size_2d_override && to_screen_rect == p_to_screen_rect) {
return;
}
@@ -1090,7 +1099,7 @@ Transform2D Viewport::_get_input_pre_xform() const {
if (to_screen_rect.size.x != 0 && to_screen_rect.size.y != 0) {
pre_xf.elements[2] = -to_screen_rect.position;
- pre_xf.scale(size / to_screen_rect.size);
+ pre_xf.scale(Vector2(size) / to_screen_rect.size);
}
return pre_xf;
@@ -1444,8 +1453,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (mb.is_valid()) {
gui.key_event_accepted = false;
- Control *over = nullptr;
-
Point2 mpos = mb->get_position();
gui.last_mouse_pos = mpos;
if (mb->is_pressed()) {
@@ -1591,6 +1598,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
// it is different, rather than wait for it to be updated the next time the
// mouse is moved, notify the control so that it can e.g. drop the highlight.
// This code is duplicated from the mm.is_valid()-case further below.
+ Control *over = nullptr;
if (gui.mouse_focus) {
over = gui.mouse_focus;
} else {
@@ -1598,10 +1606,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (gui.mouse_focus_mask == MouseButton::NONE && over != gui.mouse_over) {
- if (gui.mouse_over) {
- _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
- }
-
+ _drop_mouse_over();
_gui_cancel_tooltip();
if (over) {
@@ -1622,8 +1627,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.last_mouse_pos = mpos;
- Control *over = nullptr;
-
// Drag & drop.
if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
gui.drag_accum += mm->get_relative();
@@ -1671,29 +1674,23 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
- // These sections of code are reused in the mb.is_valid() case further up
- // for the purpose of notifying controls about potential changes in focus
- // when the mousebutton is released.
+ Control *over = nullptr;
if (gui.mouse_focus) {
over = gui.mouse_focus;
- } else {
+ } else if (gui.mouse_in_window) {
over = gui_find_control(mpos);
}
if (over != gui.mouse_over) {
- if (gui.mouse_over) {
- _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
- }
-
+ _drop_mouse_over();
_gui_cancel_tooltip();
if (over) {
_gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER);
+ gui.mouse_over = over;
}
}
- gui.mouse_over = over;
-
DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)Input::get_singleton()->get_default_cursor_shape();
if (over) {
@@ -2095,7 +2092,7 @@ void Viewport::_gui_hide_control(Control *p_control) {
}
if (gui.key_focus == p_control) {
- _gui_remove_focus();
+ gui_release_focus();
}
if (gui.mouse_over == p_control) {
gui.mouse_over = nullptr;
@@ -2145,15 +2142,7 @@ Window *Viewport::get_base_window() const {
}
void Viewport::_gui_remove_focus_for_window(Node *p_window) {
if (get_base_window() == p_window) {
- _gui_remove_focus();
- }
-}
-
-void Viewport::_gui_remove_focus() {
- if (gui.key_focus) {
- Node *f = gui.key_focus;
- gui.key_focus = nullptr;
- f->notification(Control::NOTIFICATION_FOCUS_EXIT, true);
+ gui_release_focus();
}
}
@@ -2180,6 +2169,13 @@ void Viewport::_gui_accept_event() {
}
}
+void Viewport::_drop_mouse_over() {
+ if (gui.mouse_over) {
+ _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
+ gui.mouse_over = nullptr;
+ }
+}
+
void Viewport::_drop_mouse_focus() {
Control *c = gui.mouse_focus;
MouseButton mask = gui.mouse_focus_mask;
@@ -2275,10 +2271,6 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus
}
}
-Control *Viewport::_gui_get_focus_owner() {
- return gui.key_focus;
-}
-
void Viewport::_gui_grab_click_focus(Control *p_control) {
gui.mouse_click_grabber = p_control;
call_deferred(SNAME("_post_gui_grab_click_focus"));
@@ -2348,7 +2340,7 @@ void Viewport::push_text_input(const String &p_text) {
}
Viewport::SubWindowResize Viewport::_sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point) {
- if (p_subwindow->get_flag(Window::FLAG_BORDERLESS)) {
+ if (p_subwindow->get_flag(Window::FLAG_BORDERLESS) || p_subwindow->get_flag(Window::FLAG_RESIZE_DISABLED)) {
return SUB_WINDOW_RESIZE_DISABLED;
}
@@ -2794,6 +2786,19 @@ int Viewport::gui_get_canvas_sort_index() {
return gui.canvas_sort_index++;
}
+void Viewport::gui_release_focus() {
+ if (gui.key_focus) {
+ Control *f = gui.key_focus;
+ gui.key_focus = nullptr;
+ f->notification(Control::NOTIFICATION_FOCUS_EXIT, true);
+ f->update();
+ }
+}
+
+Control *Viewport::gui_get_focus_owner() {
+ return gui.key_focus;
+}
+
void Viewport::set_msaa(MSAA p_msaa) {
ERR_FAIL_INDEX(p_msaa, MSAA_MAX);
if (msaa == p_msaa) {
@@ -3587,6 +3592,9 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging);
ClassDB::bind_method(D_METHOD("gui_is_drag_successful"), &Viewport::gui_is_drag_successful);
+ ClassDB::bind_method(D_METHOD("gui_release_focus"), &Viewport::gui_release_focus);
+ ClassDB::bind_method(D_METHOD("gui_get_focus_owner"), &Viewport::gui_get_focus_owner);
+
ClassDB::bind_method(D_METHOD("set_disable_input", "disable"), &Viewport::set_disable_input);
ClassDB::bind_method(D_METHOD("is_input_disabled"), &Viewport::is_input_disabled);
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index a3127811f5..3a71745f44 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -335,6 +335,7 @@ private:
// info used when this is a window
bool forced_mouse_focus = false; //used for menu buttons
+ bool mouse_in_window = true;
bool key_event_accepted = false;
Control *mouse_focus = nullptr;
Control *last_mouse_focus = nullptr;
@@ -410,7 +411,6 @@ private:
Control *_gui_get_drag_preview();
void _gui_remove_focus_for_window(Node *p_window);
- void _gui_remove_focus();
void _gui_unfocus_control(Control *p_control);
bool _gui_control_has_focus(const Control *p_control);
void _gui_control_grab_focus(Control *p_control);
@@ -418,8 +418,6 @@ private:
void _post_gui_grab_click_focus();
void _gui_accept_event();
- Control *_gui_get_focus_owner();
-
bool _gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check);
friend class AudioListener2D;
@@ -433,6 +431,7 @@ private:
void _canvas_layer_add(CanvasLayer *p_canvas_layer);
void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
+ void _drop_mouse_over();
void _drop_mouse_focus();
void _drop_physics_mouseover(bool p_paused_only = false);
@@ -557,6 +556,9 @@ public:
void gui_reset_canvas_sort_index();
int gui_get_canvas_sort_index();
+ void gui_release_focus();
+ Control *gui_get_focus_owner();
+
TypedArray<String> get_configuration_warnings() const override;
void set_debug_draw(DebugDraw p_debug_draw);
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 43de4187d4..fbc0bc5301 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -41,7 +41,16 @@ void Window::set_title(const String &p_title) {
if (embedder) {
embedder->_sub_window_update(this);
} else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
- DisplayServer::get_singleton()->window_set_title(atr(p_title), window_id);
+ String tr_title = atr(p_title);
+#ifdef DEBUG_ENABLED
+ if (window_id == DisplayServer::MAIN_WINDOW_ID) {
+ // Append a suffix to the window title to denote that the project is running
+ // from a debug build (including the editor). Since this results in lower performance,
+ // this should be clearly presented to the user.
+ tr_title = vformat("%s (DEBUG)", tr_title);
+ }
+#endif
+ DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
}
}
@@ -234,7 +243,16 @@ void Window::_make_window() {
DisplayServer::get_singleton()->window_set_current_screen(current_screen, window_id);
DisplayServer::get_singleton()->window_set_max_size(max_size, window_id);
DisplayServer::get_singleton()->window_set_min_size(min_size, window_id);
- DisplayServer::get_singleton()->window_set_title(atr(title), window_id);
+ String tr_title = atr(title);
+#ifdef DEBUG_ENABLED
+ if (window_id == DisplayServer::MAIN_WINDOW_ID) {
+ // Append a suffix to the window title to denote that the project is running
+ // from a debug build (including the editor). Since this results in lower performance,
+ // this should be clearly presented to the user.
+ tr_title = vformat("%s (DEBUG)", tr_title);
+ }
+#endif
+ DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id);
_update_window_size();
@@ -281,6 +299,11 @@ void Window::_clear_window() {
DisplayServer::get_singleton()->delete_sub_window(window_id);
window_id = DisplayServer::INVALID_WINDOW_ID;
+ // If closing window was focused and has a parent, return focus.
+ if (focused && transient_parent) {
+ transient_parent->grab_focus();
+ }
+
_update_viewport_size();
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
}
@@ -768,7 +791,16 @@ void Window::_notification(int p_what) {
if (embedder) {
embedder->_sub_window_update(this);
} else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
- DisplayServer::get_singleton()->window_set_title(atr(title), window_id);
+ String tr_title = atr(title);
+#ifdef DEBUG_ENABLED
+ if (window_id == DisplayServer::MAIN_WINDOW_ID) {
+ // Append a suffix to the window title to denote that the project is running
+ // from a debug build (including the editor). Since this results in lower performance,
+ // this should be clearly presented to the user.
+ tr_title = vformat("%s (DEBUG)", tr_title);
+ }
+#endif
+ DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
}
child_controls_changed();
@@ -1574,6 +1606,7 @@ void Window::_bind_methods() {
ADD_SIGNAL(MethodInfo("go_back_requested"));
ADD_SIGNAL(MethodInfo("visibility_changed"));
ADD_SIGNAL(MethodInfo("about_to_popup"));
+ ADD_SIGNAL(MethodInfo("theme_changed"));
BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index e6b73b7780..838cef824b 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -91,6 +91,7 @@
#include "scene/gui/control.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/file_dialog.h"
+#include "scene/gui/flow_container.h"
#include "scene/gui/graph_edit.h"
#include "scene/gui/graph_node.h"
#include "scene/gui/grid_container.h"
@@ -353,6 +354,9 @@ void register_scene_types() {
GDREGISTER_CLASS(CenterContainer);
GDREGISTER_CLASS(ScrollContainer);
GDREGISTER_CLASS(PanelContainer);
+ GDREGISTER_VIRTUAL_CLASS(FlowContainer);
+ GDREGISTER_CLASS(HFlowContainer);
+ GDREGISTER_CLASS(VFlowContainer);
OS::get_singleton()->yield(); // may take time to init
@@ -390,6 +394,7 @@ void register_scene_types() {
GDREGISTER_VIRTUAL_CLASS(SplitContainer);
GDREGISTER_CLASS(HSplitContainer);
GDREGISTER_CLASS(VSplitContainer);
+
GDREGISTER_CLASS(GraphNode);
GDREGISTER_CLASS(GraphEdit);
@@ -550,11 +555,13 @@ void register_scene_types() {
GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeResizableBase);
GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeGroupBase);
GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeConstant);
+ GDREGISTER_VIRTUAL_CLASS(VisualShaderNodeVectorBase);
GDREGISTER_CLASS(VisualShaderNodeComment);
GDREGISTER_CLASS(VisualShaderNodeFloatConstant);
GDREGISTER_CLASS(VisualShaderNodeIntConstant);
GDREGISTER_CLASS(VisualShaderNodeBooleanConstant);
GDREGISTER_CLASS(VisualShaderNodeColorConstant);
+ GDREGISTER_CLASS(VisualShaderNodeVec2Constant);
GDREGISTER_CLASS(VisualShaderNodeVec3Constant);
GDREGISTER_CLASS(VisualShaderNodeTransformConstant);
GDREGISTER_CLASS(VisualShaderNodeFloatOp);
@@ -572,8 +579,7 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeDotProduct);
GDREGISTER_CLASS(VisualShaderNodeVectorLen);
GDREGISTER_CLASS(VisualShaderNodeDeterminant);
- GDREGISTER_CLASS(VisualShaderNodeScalarDerivativeFunc);
- GDREGISTER_CLASS(VisualShaderNodeVectorDerivativeFunc);
+ GDREGISTER_CLASS(VisualShaderNodeDerivativeFunc);
GDREGISTER_CLASS(VisualShaderNodeClamp);
GDREGISTER_CLASS(VisualShaderNodeFaceForward);
GDREGISTER_CLASS(VisualShaderNodeOuterProduct);
@@ -599,6 +605,7 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeIntUniform);
GDREGISTER_CLASS(VisualShaderNodeBooleanUniform);
GDREGISTER_CLASS(VisualShaderNodeColorUniform);
+ GDREGISTER_CLASS(VisualShaderNodeVec2Uniform);
GDREGISTER_CLASS(VisualShaderNodeVec3Uniform);
GDREGISTER_CLASS(VisualShaderNodeTransformUniform);
GDREGISTER_CLASS(VisualShaderNodeTextureUniform);
@@ -1019,6 +1026,8 @@ void register_scene_types() {
ClassDB::add_compatibility_class("VisualShaderNodeVectorScalarStep", "VisualShaderNodeStep");
ClassDB::add_compatibility_class("VisualShaderNodeScalarSwitch", "VisualShaderNodeSwitch");
ClassDB::add_compatibility_class("VisualShaderNodeScalarTransformMult", "VisualShaderNodeTransformOp");
+ ClassDB::add_compatibility_class("VisualShaderNodeScalarDerivativeFunc", "VisualShaderNodeDerivativeFunc");
+ ClassDB::add_compatibility_class("VisualShaderNodeVectorDerivativeFunc", "VisualShaderNodeDerivativeFunc");
ClassDB::add_compatibility_class("World", "World3D");
#endif /* DISABLE_DEPRECATED */
@@ -1046,8 +1055,9 @@ void register_scene_types() {
}
void initialize_theme() {
- bool default_theme_hidpi = GLOBAL_DEF("gui/theme/use_hidpi", false);
- ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/use_hidpi", PropertyInfo(Variant::BOOL, "gui/theme/use_hidpi", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+ // Allow creating the default theme at a different scale to suit higher/lower base resolutions.
+ float default_theme_scale = GLOBAL_DEF("gui/theme/default_theme_scale", 1.0);
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_theme_scale", PropertyInfo(Variant::FLOAT, "gui/theme/default_theme_scale", PROPERTY_HINT_RANGE, "0.5,8,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
String theme_path = GLOBAL_DEF_RST("gui/theme/custom", "");
ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
String font_path = GLOBAL_DEF_RST("gui/theme/custom_font", "");
@@ -1063,7 +1073,7 @@ void initialize_theme() {
// Always make the default theme to avoid invalid default font/icon/style in the given theme.
if (RenderingServer::get_singleton()) {
- make_default_theme(default_theme_hidpi, font);
+ make_default_theme(default_theme_scale, font);
}
if (!theme_path.is_empty()) {
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 44d3e4af19..b6151bccf4 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -2474,34 +2474,38 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
real_t delta = (length - p_keys[next].time) - (length - p_keys[idx].time);
real_t from = (length - p_time) - (length - p_keys[idx].time);
- if (Math::is_zero_approx(delta))
+ if (Math::is_zero_approx(delta)) {
c = 0;
- else
+ } else {
c = from / delta;
+ }
} else {
next = len - 1;
real_t delta = p_keys[idx].time + (length - p_keys[next].time);
real_t from = (length - p_time) - (length - p_keys[idx].time);
- if (Math::is_zero_approx(delta))
+ if (Math::is_zero_approx(delta)) {
c = 0;
- else
+ } else {
c = from / delta;
+ }
}
} else {
// on loop, in front of last key
idx = 0;
next = len - 1;
real_t endtime = p_keys[idx].time;
- if (endtime > length) // may be keys past the end
+ if (endtime > length) { // may be keys past the end
endtime = length;
+ }
real_t delta = p_keys[next].time - endtime;
real_t from = p_time - endtime;
- if (Math::is_zero_approx(delta))
+ if (Math::is_zero_approx(delta)) {
c = 0;
- else
+ } else {
c = from / delta;
+ }
}
}
} else { // no loop
@@ -2609,7 +2613,7 @@ Variant Animation::value_track_interpolate(int p_track, double p_time) const {
void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
- to_time = length * 1.001; //include a little more if at the end
+ to_time = length + CMP_EPSILON; //include a little more if at the end
}
int to = _find(vt->values, to_time);
@@ -2730,7 +2734,7 @@ Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const
template <class T>
void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
- to_time = length * 1.01; //include a little more if at the end
+ to_time = length + CMP_EPSILON; //include a little more if at the end
}
int to = _find(p_array, to_time);
@@ -3081,7 +3085,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, double from_time, double to_time, List<int> *p_indices) const {
if (from_time != length && to_time == length) {
- to_time = length * 1.01; //include a little more if at the end
+ to_time = length + CMP_EPSILON; //include a little more if at the end
}
int to = _find(mt->methods, to_time);
diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp
index 25f169b6a2..c2988c2e8c 100644
--- a/scene/resources/bit_map.cpp
+++ b/scene/resources/bit_map.cpp
@@ -667,11 +667,13 @@ void BitMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_true_bit_count"), &BitMap::get_true_bit_count);
ClassDB::bind_method(D_METHOD("get_size"), &BitMap::get_size);
+ ClassDB::bind_method(D_METHOD("resize", "new_size"), &BitMap::resize);
ClassDB::bind_method(D_METHOD("_set_data"), &BitMap::_set_data);
ClassDB::bind_method(D_METHOD("_get_data"), &BitMap::_get_data);
ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask);
+ ClassDB::bind_method(D_METHOD("convert_to_image"), &BitMap::convert_to_image);
ClassDB::bind_method(D_METHOD("opaque_to_polygons", "rect", "epsilon"), &BitMap::_opaque_to_polygons_bind, DEFVAL(2.0));
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
diff --git a/scene/resources/box_shape_3d.cpp b/scene/resources/box_shape_3d.cpp
index b97d378e02..a1ec9a2230 100644
--- a/scene/resources/box_shape_3d.cpp
+++ b/scene/resources/box_shape_3d.cpp
@@ -95,5 +95,5 @@ void BoxShape3D::_bind_methods() {
BoxShape3D::BoxShape3D() :
Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_BOX)) {
- set_size(Vector3(2, 2, 2));
+ set_size(Vector3(1, 1, 1));
}
diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp
index cb8a189116..4d2698d27d 100644
--- a/scene/resources/capsule_shape_2d.cpp
+++ b/scene/resources/capsule_shape_2d.cpp
@@ -84,13 +84,11 @@ real_t CapsuleShape2D::get_height() const {
void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector<Vector2> points = _get_points();
- Vector<Color> col;
- col.push_back(p_color);
+ Vector<Color> col = { p_color };
RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
if (is_collision_outline_enabled()) {
+ points.push_back(points[0]);
RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
- // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
- RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
}
}
diff --git a/scene/resources/capsule_shape_3d.h b/scene/resources/capsule_shape_3d.h
index 967a413da4..4c039ab326 100644
--- a/scene/resources/capsule_shape_3d.h
+++ b/scene/resources/capsule_shape_3d.h
@@ -35,8 +35,8 @@
class CapsuleShape3D : public Shape3D {
GDCLASS(CapsuleShape3D, Shape3D);
- float radius = 1.0;
- float height = 3.0;
+ float radius = 0.5;
+ float height = 2.0;
protected:
static void _bind_methods();
diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp
index 68ee1be9f9..9c16ac2eed 100644
--- a/scene/resources/circle_shape_2d.cpp
+++ b/scene/resources/circle_shape_2d.cpp
@@ -71,18 +71,19 @@ real_t CircleShape2D::get_enclosing_radius() const {
void CircleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
Vector<Vector2> points;
+ points.resize(24);
+
const real_t turn_step = Math_TAU / 24.0;
for (int i = 0; i < 24; i++) {
- points.push_back(Vector2(Math::cos(i * turn_step), Math::sin(i * turn_step)) * get_radius());
+ points.write[i] = Vector2(Math::cos(i * turn_step), Math::sin(i * turn_step)) * get_radius();
}
- Vector<Color> col;
- col.push_back(p_color);
+ Vector<Color> col = { p_color };
RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
+
if (is_collision_outline_enabled()) {
+ points.push_back(points[0]);
RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col);
- // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`.
- RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color);
}
}
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index d2cd76b796..6485c1ac77 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -661,8 +661,8 @@ void Curve2D::_bake() const {
baked_cache_dirty = false;
if (points.size() == 0) {
- baked_point_cache.resize(0);
- baked_dist_cache.resize(0);
+ baked_point_cache.clear();
+ baked_dist_cache.clear();
return;
}
@@ -1164,10 +1164,10 @@ void Curve3D::_bake() const {
baked_cache_dirty = false;
if (points.size() == 0) {
- baked_point_cache.resize(0);
- baked_tilt_cache.resize(0);
- baked_up_vector_cache.resize(0);
- baked_dist_cache.resize(0);
+ baked_point_cache.clear();
+ baked_tilt_cache.clear();
+ baked_up_vector_cache.clear();
+ baked_dist_cache.clear();
return;
}
@@ -1183,7 +1183,7 @@ void Curve3D::_bake() const {
baked_up_vector_cache.resize(1);
baked_up_vector_cache.set(0, Vector3(0, 1, 0));
} else {
- baked_up_vector_cache.resize(0);
+ baked_up_vector_cache.clear();
}
return;
diff --git a/scene/resources/cylinder_shape_3d.h b/scene/resources/cylinder_shape_3d.h
index 0211f2b08f..65427423c8 100644
--- a/scene/resources/cylinder_shape_3d.h
+++ b/scene/resources/cylinder_shape_3d.h
@@ -35,7 +35,7 @@
class CylinderShape3D : public Shape3D {
GDCLASS(CylinderShape3D, Shape3D);
- float radius = 1.0;
+ float radius = 0.5;
float height = 2.0;
protected:
diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub
index 3667ab7c14..f27bd9144e 100644
--- a/scene/resources/default_theme/SCsub
+++ b/scene/resources/default_theme/SCsub
@@ -4,6 +4,7 @@ Import("env")
from platform_methods import run_in_subprocess
import default_theme_builders
+import default_theme_icons_builders
env.add_source_files(env.scene_sources, "*.cpp")
@@ -13,3 +14,19 @@ env.CommandNoCache(
"#thirdparty/fonts/OpenSans_SemiBold.ttf",
run_in_subprocess(default_theme_builders.make_fonts_header),
)
+
+env["BUILDERS"]["MakeDefaultThemeIconsBuilder"] = Builder(
+ action=env.Run(
+ default_theme_icons_builders.make_default_theme_icons_action, "Generating default project theme icons header."
+ ),
+ suffix=".h",
+ src_suffix=".svg",
+)
+
+# Default theme icons
+icon_sources = Glob("*.svg")
+
+env.Alias(
+ "default_theme_icons",
+ [env.MakeDefaultThemeIconsBuilder("#scene/resources/default_theme/default_theme_icons.gen.h", icon_sources)],
+)
diff --git a/scene/resources/default_theme/add.svg b/scene/resources/default_theme/add.svg
new file mode 100644
index 0000000000..6884a07aa0
--- /dev/null
+++ b/scene/resources/default_theme/add.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6z" fill="#b2b2b2" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/arrow_down.png b/scene/resources/default_theme/arrow_down.png
deleted file mode 100644
index bfb87a4761..0000000000
--- a/scene/resources/default_theme/arrow_down.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/arrow_down.svg b/scene/resources/default_theme/arrow_down.svg
new file mode 100644
index 0000000000..16cc00fa8f
--- /dev/null
+++ b/scene/resources/default_theme/arrow_down.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m5 7 3 3 3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/arrow_left.png b/scene/resources/default_theme/arrow_left.png
deleted file mode 100644
index 4163059dd3..0000000000
--- a/scene/resources/default_theme/arrow_left.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/arrow_left.svg b/scene/resources/default_theme/arrow_left.svg
new file mode 100644
index 0000000000..7dbf01e186
--- /dev/null
+++ b/scene/resources/default_theme/arrow_left.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m9 11-3-3 3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/arrow_right.png b/scene/resources/default_theme/arrow_right.png
deleted file mode 100644
index 1e4c8e5529..0000000000
--- a/scene/resources/default_theme/arrow_right.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/arrow_right.svg b/scene/resources/default_theme/arrow_right.svg
new file mode 100644
index 0000000000..b031f12ec0
--- /dev/null
+++ b/scene/resources/default_theme/arrow_right.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m6 11 3-3-3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/bar_arrow.png b/scene/resources/default_theme/bar_arrow.png
deleted file mode 100644
index 7cf146b8da..0000000000
--- a/scene/resources/default_theme/bar_arrow.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/bookmark.png b/scene/resources/default_theme/bookmark.png
deleted file mode 100644
index 9718cf53b6..0000000000
--- a/scene/resources/default_theme/bookmark.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/bookmark.svg b/scene/resources/default_theme/bookmark.svg
new file mode 100644
index 0000000000..0b50c2b034
--- /dev/null
+++ b/scene/resources/default_theme/bookmark.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#fefffe" fill-opacity=".85"><path d="m14.505 1.476h-12.995v13.049l6.491-5.185 6.504 5.185z"/><path d="m15.018 1.476c0-.25-.23-.452-.514-.452h-12.994c-.284 0-.513.202-.513.452v13.049c0 .178.119.34.306.413.185.071.402.041.552-.079l6.146-4.909 6.158 4.909c.151.12.367.15.553.079.187-.073.306-.235.306-.413zm-.514 0h-12.994v13.049l6.491-5.185 6.503 5.185z"/></g></svg>
diff --git a/scene/resources/default_theme/breakpoint.svg b/scene/resources/default_theme/breakpoint.svg
new file mode 100644
index 0000000000..9b18b276d8
--- /dev/null
+++ b/scene/resources/default_theme/breakpoint.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8c0 3.84-3.16 7-7 7s-7-3.16-7-7 3.16-7 7-7 7 3.16 7 7" fill="#ff5d5d" fill-opacity=".5" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/button_disabled.png b/scene/resources/default_theme/button_disabled.png
deleted file mode 100644
index 708748dfe9..0000000000
--- a/scene/resources/default_theme/button_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/button_focus.png b/scene/resources/default_theme/button_focus.png
deleted file mode 100644
index 70e16b953b..0000000000
--- a/scene/resources/default_theme/button_focus.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/button_hover.png b/scene/resources/default_theme/button_hover.png
deleted file mode 100644
index ff2258281e..0000000000
--- a/scene/resources/default_theme/button_hover.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/button_normal.png b/scene/resources/default_theme/button_normal.png
deleted file mode 100644
index c189b61b89..0000000000
--- a/scene/resources/default_theme/button_normal.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/button_pressed.png b/scene/resources/default_theme/button_pressed.png
deleted file mode 100644
index 19a7e237aa..0000000000
--- a/scene/resources/default_theme/button_pressed.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/checked.png b/scene/resources/default_theme/checked.png
deleted file mode 100644
index bde031b6a2..0000000000
--- a/scene/resources/default_theme/checked.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/checked.svg b/scene/resources/default_theme/checked.svg
new file mode 100644
index 0000000000..6c3bc8f210
--- /dev/null
+++ b/scene/resources/default_theme/checked.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#fff" fill-opacity=".75" stroke-width="1.16667"/><path d="m11.500773 3.7343508-5.6117507 5.6117502-1.7045017-1.6814543-1.4992276 1.4992276 3.2037293 3.1806817 7.1109777-7.1109775z" fill="#1a1a1a" stroke-width="1.06023"/></svg>
diff --git a/scene/resources/default_theme/checked_disabled.png b/scene/resources/default_theme/checked_disabled.png
deleted file mode 100644
index 70549e2edc..0000000000
--- a/scene/resources/default_theme/checked_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/checked_disabled.svg b/scene/resources/default_theme/checked_disabled.svg
new file mode 100644
index 0000000000..109c4dc129
--- /dev/null
+++ b/scene/resources/default_theme/checked_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#fff" fill-opacity=".37" stroke-width="1.16667"/><path d="m11.500773 3.7343508-5.6117507 5.6117502-1.7045017-1.6814543-1.4992276 1.4992276 3.2037293 3.1806817 7.1109777-7.1109775z" fill="#1a1a1a" fill-opacity=".5" stroke-width="1.06023"/></svg>
diff --git a/scene/resources/default_theme/checker_bg.png b/scene/resources/default_theme/checker_bg.png
deleted file mode 100644
index 3eff2f0e08..0000000000
--- a/scene/resources/default_theme/checker_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/close.png b/scene/resources/default_theme/close.png
deleted file mode 100644
index 4d4ac4a551..0000000000
--- a/scene/resources/default_theme/close.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/close.svg b/scene/resources/default_theme/close.svg
new file mode 100644
index 0000000000..c26cca06c3
--- /dev/null
+++ b/scene/resources/default_theme/close.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 3 2-2 5 5 5-5 2 2-5 5 5 5-2 2-5.3020979-5-4.6979021 5-2-2 5-5z" fill="#fff" fill-opacity=".75"/></svg>
diff --git a/scene/resources/default_theme/close_hl.png b/scene/resources/default_theme/close_hl.png
deleted file mode 100644
index 4d4ac4a551..0000000000
--- a/scene/resources/default_theme/close_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/close_hl.svg b/scene/resources/default_theme/close_hl.svg
new file mode 100644
index 0000000000..ec44ba6d24
--- /dev/null
+++ b/scene/resources/default_theme/close_hl.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 3 2-2 5 5 5-5 2 2-5 5 5 5-2 2-5.3020979-5-4.6979021 5-2-2 5-5z" fill="#fff"/></svg>
diff --git a/scene/resources/default_theme/color_picker_bar_arrow.svg b/scene/resources/default_theme/color_picker_bar_arrow.svg
new file mode 100644
index 0000000000..5043da0761
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_bar_arrow.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 20" xmlns="http://www.w3.org/2000/svg"><path d="m3.564 15.218h8.872l-4.436-4.436z" fill="#b2b2b2" fill-rule="nonzero" stroke="#b2b2b2"/></svg>
diff --git a/scene/resources/default_theme/color_picker_cursor.svg b/scene/resources/default_theme/color_picker_cursor.svg
new file mode 100644
index 0000000000..88ee3f55ce
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_cursor.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2a6 6 0 0 0 -6 6 6 6 0 0 0 6 6 6 6 0 0 0 6-6 6 6 0 0 0 -6-6zm0 1a5 5 0 0 1 5 5 5 5 0 0 1 -5 5 5 5 0 0 1 -5-5 5 5 0 0 1 5-5z" fill="#fff"/><path d="m8 3a5 5 0 0 0 -5 5 5 5 0 0 0 5 5 5 5 0 0 0 5-5 5 5 0 0 0 -5-5zm-.0605469 1a4 4 0 0 1 .0605469 0 4 4 0 0 1 4 4 4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 3.9394531-4z"/></svg>
diff --git a/scene/resources/default_theme/color_picker_hue.png b/scene/resources/default_theme/color_picker_hue.png
deleted file mode 100644
index 7b46f03cb4..0000000000
--- a/scene/resources/default_theme/color_picker_hue.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/color_picker_hue.svg b/scene/resources/default_theme/color_picker_hue.svg
new file mode 100644
index 0000000000..ff75d5eb9e
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_hue.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 1 256" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(0 256 -256 0 0 0)" gradientUnits="userSpaceOnUse" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#f00"/><stop offset=".04" stop-color="#ff4000"/><stop offset=".08" stop-color="#ff8000"/><stop offset=".17" stop-color="#ff0"/><stop offset=".25" stop-color="#80ff00"/><stop offset=".33" stop-color="#0f0"/><stop offset=".42" stop-color="#00ff80"/><stop offset=".5" stop-color="#0ff"/><stop offset=".58" stop-color="#0080ff"/><stop offset=".63" stop-color="#0040ff"/><stop offset=".67" stop-color="#00f"/><stop offset=".75" stop-color="#8000ff"/><stop offset=".83" stop-color="#f0f"/><stop offset=".92" stop-color="#ff0080"/><stop offset="1" stop-color="#f00"/></linearGradient><path d="m0 0h1v256h-1z" fill="url(#a)"/></svg>
diff --git a/scene/resources/default_theme/color_picker_overbright.svg b/scene/resources/default_theme/color_picker_overbright.svg
new file mode 100644
index 0000000000..f618980d51
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_overbright.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m.5.5v10l10-10z" fill="#fff" stroke="#000"/><path d="m0 12 12-12h-1.714286l-10.285714 10.285714z" fill="#000003" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/color_picker_pipette.svg b/scene/resources/default_theme/color_picker_pipette.svg
new file mode 100644
index 0000000000..5213a6e1bc
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_pipette.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-1.108 0-2 .892-2 2v2h-1v2h1v5c.001.712.383 1.372 1 1.729v1.271h2v-1.27c.618-.356.999-1.017 1-1.73v-5h1v-2h-1v-2c0-1.108-.892-2-2-2zm-1 6h2v5c0 .549-.451 1-1 1s-1-.451-1-1z" fill="#b2b2b2" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/color_picker_sample.png b/scene/resources/default_theme/color_picker_sample.png
deleted file mode 100644
index e6ec28d307..0000000000
--- a/scene/resources/default_theme/color_picker_sample.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/color_picker_sample.svg b/scene/resources/default_theme/color_picker_sample.svg
new file mode 100644
index 0000000000..140ac20a99
--- /dev/null
+++ b/scene/resources/default_theme/color_picker_sample.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 256 20" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m0 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m5 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m80 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m85 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m159.978 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m164.978 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m39.991 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m44.991 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m119.99 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m124.99 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m199.968 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m204.968 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m9.98 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m14.98 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m89.98 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m94.98 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m169.957 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m174.957 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m49.97 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m54.97 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m129.97 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m134.97 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m209.948 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m214.948 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m19.995 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m24.995 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m99.995 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m104.995 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m179.973 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m184.973 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m59.986 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m64.986 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m139.986 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m144.986 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m219.964 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m224.964 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m30.011 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m35.011 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m110.011 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m115.011 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m189.989 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m194.989 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m70.001 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m75.001 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m150.001 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m155.001 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m229.979 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m234.979 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m240.017 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m245.017 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m249.996 0v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m254.996 0v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m249.996 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m254.996 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m240.017 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m245.017 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m0 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m5 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m80 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m85 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m159.978 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m164.978 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m39.991 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m44.991 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m119.99 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m124.99 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m199.968 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m204.968 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m9.98 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m14.98 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m89.98 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m94.98 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m169.957 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m174.957 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m49.97 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m54.97 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m129.97 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m134.97 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m209.948 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m214.948 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m19.995 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m24.995 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m99.995 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m104.995 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m179.973 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m184.973 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m59.986 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m64.986 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m139.986 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m144.986 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m219.964 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m224.964 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m30.011 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m35.011 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m110.011 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m115.011 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m189.989 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m194.989 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m70.001 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m75.001 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m150.001 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m155.001 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/><path d="m229.979 10v5h5v-5zm5 5v5h5v-5z" fill="#e0e0e0"/><path d="m234.979 10v5h5v-5zm0 5h-5v5h5z" fill="#fff"/></g></svg>
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 9c76d74fbd..670b141080 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -32,49 +32,22 @@
#include "core/os/os.h"
#include "default_font.gen.h"
+#include "default_theme_icons.gen.h"
#include "scene/resources/font.h"
#include "scene/resources/theme.h"
#include "servers/text_server.h"
-#include "theme_data.h"
-typedef Map<const void *, Ref<ImageTexture>> TexCacheMap;
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
-static TexCacheMap *tex_cache;
static float scale = 1.0;
-template <class T>
-static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, float p_right, float p_bottom, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1, bool p_draw_center = true) {
- Ref<ImageTexture> texture;
+static const int default_margin = 4;
+static const int default_corner_radius = 3;
- if (tex_cache->has(p_src)) {
- texture = (*tex_cache)[p_src];
- } else {
- texture = Ref<ImageTexture>(memnew(ImageTexture));
- Ref<Image> img = memnew(Image(p_src));
- const Size2 orig_size = img->get_size();
- img->convert(Image::FORMAT_RGBA8);
- img->resize(orig_size.x * scale, orig_size.y * scale);
-
- texture->create_from_image(img);
- (*tex_cache)[p_src] = texture;
- }
-
- Ref<StyleBoxTexture> style(memnew(StyleBoxTexture));
- style->set_texture(texture);
- style->set_margin_size(SIDE_LEFT, p_left * scale);
- style->set_margin_size(SIDE_RIGHT, p_right * scale);
- style->set_margin_size(SIDE_BOTTOM, p_bottom * scale);
- style->set_margin_size(SIDE_TOP, p_top * scale);
- style->set_default_margin(SIDE_LEFT, p_margin_left * scale);
- style->set_default_margin(SIDE_RIGHT, p_margin_right * scale);
- style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * scale);
- style->set_default_margin(SIDE_TOP, p_margin_top * scale);
- style->set_draw_center(p_draw_center);
-
- return style;
-}
-
-static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
+static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = default_margin, float p_margin_top = default_margin, float p_margin_right = default_margin, float p_margin_bottom = default_margin, int p_corner_radius = default_corner_radius, bool p_draw_center = true, int p_border_width = 0) {
Ref<StyleBoxFlat> style(memnew(StyleBoxFlat));
style->set_bg_color(p_color);
style->set_default_margin(SIDE_LEFT, p_margin_left * scale);
@@ -82,10 +55,18 @@ static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left =
style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * scale);
style->set_default_margin(SIDE_TOP, p_margin_top * scale);
+ style->set_corner_radius_all(p_corner_radius);
+ style->set_anti_aliased(true);
+ // Adjust level of detail based on the corners' effective sizes.
+ style->set_corner_detail(MIN(Math::ceil(1.5 * p_corner_radius), 6) * scale);
+
+ style->set_draw_center(p_draw_center);
+ style->set_border_width_all(p_border_width);
+
return style;
}
-static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox, float p_left, float p_top, float p_right, float p_bottom) {
+static Ref<StyleBoxFlat> sb_expand(Ref<StyleBoxFlat> p_sbox, float p_left, float p_top, float p_right, float p_bottom) {
p_sbox->set_expand_margin_size(SIDE_LEFT, p_left * scale);
p_sbox->set_expand_margin_size(SIDE_TOP, p_top * scale);
p_sbox->set_expand_margin_size(SIDE_RIGHT, p_right * scale);
@@ -93,36 +74,22 @@ static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox, float p_left,
return p_sbox;
}
-template <class T>
-static Ref<Texture2D> make_icon(T p_src) {
- Ref<ImageTexture> texture(memnew(ImageTexture));
- Ref<Image> img = memnew(Image(p_src));
- const Size2 orig_size = img->get_size();
- img->convert(Image::FORMAT_RGBA8);
- img->resize(orig_size.x * scale, orig_size.y * scale);
- texture->create_from_image(img);
-
- return texture;
-}
-
-static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, bool p_flip_x = false) {
- if (!p_flip_y && !p_flip_x) {
- return p_texture;
- }
-
- Ref<ImageTexture> texture(memnew(ImageTexture));
- Ref<Image> img = p_texture->get_image();
- img = img->duplicate();
-
- if (p_flip_y) {
- img->flip_y();
- }
- if (p_flip_x) {
- img->flip_x();
- }
-
- texture->create_from_image(img);
- return texture;
+// See also `editor_generate_icon()` in `editor/editor_themes.cpp`.
+static Ref<ImageTexture> generate_icon(int p_index) {
+ Ref<ImageTexture> icon = memnew(ImageTexture);
+ Ref<Image> img = memnew(Image);
+
+#ifdef MODULE_SVG_ENABLED
+ // Upsample icon generation only if the scale isn't an integer multiplier.
+ // Generating upsampled icons is slower, and the benefit is hardly visible
+ // with integer scales.
+ const bool upsample = !Math::is_equal_approx(Math::round(scale), scale);
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, false);
+#endif
+ icon->create_from_image(img);
+
+ return icon;
}
static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
@@ -136,48 +103,60 @@ static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margi
return style;
}
-void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &large_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale) {
+void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale) {
scale = p_scale;
- tex_cache = memnew(TexCacheMap);
-
- // Font Colors
-
- Color control_font_color = Color(0.88, 0.88, 0.88);
- Color control_font_lower_color = Color(0.63, 0.63, 0.63);
- Color control_font_low_color = Color(0.69, 0.69, 0.69);
- Color control_font_hover_color = Color(0.94, 0.94, 0.94);
- Color control_font_focus_color = Color(0.94, 0.94, 0.94);
- Color control_font_disabled_color = Color(0.9, 0.9, 0.9, 0.2);
- Color control_font_pressed_color = Color(1, 1, 1);
-
- Color control_selection_color = Color(0.49, 0.49, 0.49);
+ // Font colors
+ const Color control_font_color = Color(0.875, 0.875, 0.875);
+ const Color control_font_low_color = Color(0.7, 0.7, 0.7);
+ const Color control_font_lower_color = Color(0.65, 0.65, 0.65);
+ const Color control_font_hover_color = Color(0.95, 0.95, 0.95);
+ const Color control_font_focus_color = Color(0.95, 0.95, 0.95);
+ const Color control_font_disabled_color = control_font_color * Color(1, 1, 1, 0.5);
+ const Color control_font_placeholder_color = Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.6f);
+ const Color control_font_pressed_color = Color(1, 1, 1);
+ const Color control_selection_color = Color(0.5, 0.5, 0.5);
+
+ // StyleBox colors
+ const Color style_normal_color = Color(0.1, 0.1, 0.1, 0.6);
+ const Color style_hover_color = Color(0.225, 0.225, 0.225, 0.6);
+ const Color style_pressed_color = Color(0, 0, 0, 0.6);
+ const Color style_disabled_color = Color(0.1, 0.1, 0.1, 0.3);
+ const Color style_focus_color = Color(1, 1, 1, 0.75);
+ const Color style_popup_color = Color(0.25, 0.25, 0.25, 1);
+ const Color style_popup_border_color = Color(0.175, 0.175, 0.175, 1);
+ const Color style_popup_hover_color = Color(0.4, 0.4, 0.4, 1);
+ const Color style_selected_color = Color(1, 1, 1, 0.3);
+ // Don't use a color too bright to keep the percentage readable.
+ const Color style_progress_color = Color(1, 1, 1, 0.4);
+ const Color style_separator_color = Color(0.5, 0.5, 0.5);
+
+ // Convert the generated icon sources to a dictionary for easier access.
+ // Unlike the editor icons, there is no central repository of icons in the Theme resource itself to keep it tidy.
+ Dictionary icons;
+ for (int i = 0; i < default_theme_icons_count; i++) {
+ icons[default_theme_icons_names[i]] = generate_icon(i);
+ }
// Panel
-
- theme->set_stylebox("panel", "Panel", make_stylebox(panel_bg_png, 0, 0, 0, 0));
- theme->set_stylebox("panel_fg", "Panel", make_stylebox(panel_bg_png, 0, 0, 0, 0));
-
- // Focus
-
- Ref<StyleBoxTexture> focus = make_stylebox(focus_png, 5, 5, 5, 5);
- for (int i = 0; i < 4; i++) {
- focus->set_expand_margin_size(Side(i), 1 * scale);
- }
+ theme->set_stylebox("panel", "Panel", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
+ theme->set_stylebox("panel_fg", "Panel", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
// Button
- Ref<StyleBox> sb_button_normal = sb_expand(make_stylebox(button_normal_png, 4, 4, 4, 4, 6, 3, 6, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_button_pressed = sb_expand(make_stylebox(button_pressed_png, 4, 4, 4, 4, 6, 3, 6, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_button_hover = sb_expand(make_stylebox(button_hover_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
- Ref<StyleBox> sb_button_disabled = sb_expand(make_stylebox(button_disabled_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
- Ref<StyleBox> sb_button_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
+ const Ref<StyleBoxFlat> button_normal = make_flat_stylebox(style_normal_color);
+ const Ref<StyleBoxFlat> button_hover = make_flat_stylebox(style_hover_color);
+ const Ref<StyleBoxFlat> button_pressed = make_flat_stylebox(style_pressed_color);
+ const Ref<StyleBoxFlat> button_disabled = make_flat_stylebox(style_disabled_color);
+ Ref<StyleBoxFlat> focus = make_flat_stylebox(style_focus_color, default_margin, default_margin, default_margin, default_margin, default_corner_radius, false, 2);
+ // Make the focus outline appear to be flush with the buttons it's focusing.
+ focus->set_expand_margin_size_all(2 * scale);
- theme->set_stylebox("normal", "Button", sb_button_normal);
- theme->set_stylebox("pressed", "Button", sb_button_pressed);
- theme->set_stylebox("hover", "Button", sb_button_hover);
- theme->set_stylebox("disabled", "Button", sb_button_disabled);
- theme->set_stylebox("focus", "Button", sb_button_focus);
+ theme->set_stylebox("normal", "Button", button_normal);
+ theme->set_stylebox("hover", "Button", button_hover);
+ theme->set_stylebox("pressed", "Button", button_pressed);
+ theme->set_stylebox("disabled", "Button", button_disabled);
+ theme->set_stylebox("focus", "Button", focus);
theme->set_font("font", "Button", Ref<Font>());
theme->set_font_size("font_size", "Button", -1);
@@ -217,31 +196,29 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("underline_spacing", "LinkButton", 2 * scale);
// OptionButton
+ theme->set_stylebox("focus", "OptionButton", focus);
- Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
- theme->set_stylebox("focus", "OptionButton", sb_optbutton_focus);
-
- Ref<StyleBox> sb_optbutton_normal = sb_expand(make_stylebox(option_button_normal_png, 4, 4, 21, 4, 6, 3, 9, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_pressed = sb_expand(make_stylebox(option_button_pressed_png, 4, 4, 21, 4, 6, 3, 9, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_hover = sb_expand(make_stylebox(option_button_hover_png, 4, 4, 21, 4, 6, 2, 9, 2), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_disabled = sb_expand(make_stylebox(option_button_disabled_png, 4, 4, 21, 4, 6, 2, 9, 2), 2, 2, 2, 2);
+ Ref<StyleBox> sb_optbutton_normal = make_flat_stylebox(style_normal_color, 2 * default_margin, default_margin, 21, default_margin);
+ Ref<StyleBox> sb_optbutton_hover = make_flat_stylebox(style_hover_color, 2 * default_margin, default_margin, 21, default_margin);
+ Ref<StyleBox> sb_optbutton_pressed = make_flat_stylebox(style_pressed_color, 2 * default_margin, default_margin, 21, default_margin);
+ Ref<StyleBox> sb_optbutton_disabled = make_flat_stylebox(style_disabled_color, 2 * default_margin, default_margin, 21, default_margin);
theme->set_stylebox("normal", "OptionButton", sb_optbutton_normal);
- theme->set_stylebox("pressed", "OptionButton", sb_optbutton_pressed);
theme->set_stylebox("hover", "OptionButton", sb_optbutton_hover);
+ theme->set_stylebox("pressed", "OptionButton", sb_optbutton_pressed);
theme->set_stylebox("disabled", "OptionButton", sb_optbutton_disabled);
- Ref<StyleBox> sb_optbutton_normal_mirrored = sb_expand(make_stylebox(option_button_normal_mirrored_png, 21, 4, 4, 4, 9, 3, 6, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_pressed_mirrored = sb_expand(make_stylebox(option_button_pressed_mirrored_png, 21, 4, 4, 4, 9, 3, 6, 3), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_hover_mirrored = sb_expand(make_stylebox(option_button_hover_mirrored_png, 21, 4, 4, 4, 9, 2, 6, 2), 2, 2, 2, 2);
- Ref<StyleBox> sb_optbutton_disabled_mirrored = sb_expand(make_stylebox(option_button_disabled_mirrored_png, 21, 4, 4, 4, 9, 2, 6, 2), 2, 2, 2, 2);
+ Ref<StyleBox> sb_optbutton_normal_mirrored = make_flat_stylebox(style_normal_color, 21, default_margin, 2 * default_margin, default_margin);
+ Ref<StyleBox> sb_optbutton_hover_mirrored = make_flat_stylebox(style_hover_color, 21, default_margin, 2 * default_margin, default_margin);
+ Ref<StyleBox> sb_optbutton_pressed_mirrored = make_flat_stylebox(style_pressed_color, 21, default_margin, 2 * default_margin, default_margin);
+ Ref<StyleBox> sb_optbutton_disabled_mirrored = make_flat_stylebox(style_disabled_color, 21, default_margin, 2 * default_margin, default_margin);
theme->set_stylebox("normal_mirrored", "OptionButton", sb_optbutton_normal_mirrored);
- theme->set_stylebox("pressed_mirrored", "OptionButton", sb_optbutton_pressed_mirrored);
theme->set_stylebox("hover_mirrored", "OptionButton", sb_optbutton_hover_mirrored);
+ theme->set_stylebox("pressed_mirrored", "OptionButton", sb_optbutton_pressed_mirrored);
theme->set_stylebox("disabled_mirrored", "OptionButton", sb_optbutton_disabled_mirrored);
- theme->set_icon("arrow", "OptionButton", make_icon(option_arrow_png));
+ theme->set_icon("arrow", "OptionButton", icons["option_button_arrow"]);
theme->set_font("font", "OptionButton", Ref<Font>());
theme->set_font_size("font_size", "OptionButton", -1);
@@ -254,16 +231,16 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1));
theme->set_constant("hseparation", "OptionButton", 2 * scale);
- theme->set_constant("arrow_margin", "OptionButton", 2 * scale);
+ theme->set_constant("arrow_margin", "OptionButton", 4 * scale);
theme->set_constant("outline_size", "OptionButton", 0);
// MenuButton
- theme->set_stylebox("normal", "MenuButton", sb_button_normal);
- theme->set_stylebox("pressed", "MenuButton", sb_button_pressed);
- theme->set_stylebox("hover", "MenuButton", sb_button_hover);
- theme->set_stylebox("disabled", "MenuButton", sb_button_disabled);
- theme->set_stylebox("focus", "MenuButton", sb_button_focus);
+ theme->set_stylebox("normal", "MenuButton", button_normal);
+ theme->set_stylebox("pressed", "MenuButton", button_pressed);
+ theme->set_stylebox("hover", "MenuButton", button_hover);
+ theme->set_stylebox("disabled", "MenuButton", button_disabled);
+ theme->set_stylebox("focus", "MenuButton", focus);
theme->set_font("font", "MenuButton", Ref<Font>());
theme->set_font_size("font_size", "MenuButton", -1);
@@ -298,14 +275,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("hover_pressed", "CheckBox", cbx_empty);
theme->set_stylebox("focus", "CheckBox", cbx_focus);
- theme->set_icon("checked", "CheckBox", make_icon(checked_png));
- theme->set_icon("checked_disabled", "CheckBox", make_icon(checked_disabled_png));
- theme->set_icon("unchecked", "CheckBox", make_icon(unchecked_png));
- theme->set_icon("unchecked_disabled", "CheckBox", make_icon(unchecked_disabled_png));
- theme->set_icon("radio_checked", "CheckBox", make_icon(radio_checked_png));
- theme->set_icon("radio_checked_disabled", "CheckBox", make_icon(radio_checked_disabled_png));
- theme->set_icon("radio_unchecked", "CheckBox", make_icon(radio_unchecked_png));
- theme->set_icon("radio_unchecked_disabled", "CheckBox", make_icon(radio_unchecked_disabled_png));
+ theme->set_icon("checked", "CheckBox", icons["checked"]);
+ theme->set_icon("checked_disabled", "CheckBox", icons["checked"]);
+ theme->set_icon("unchecked", "CheckBox", icons["unchecked"]);
+ theme->set_icon("unchecked_disabled", "CheckBox", icons["unchecked"]);
+ theme->set_icon("radio_checked", "CheckBox", icons["radio_checked"]);
+ theme->set_icon("radio_checked_disabled", "CheckBox", icons["radio_checked"]);
+ theme->set_icon("radio_unchecked", "CheckBox", icons["radio_unchecked"]);
+ theme->set_icon("radio_unchecked_disabled", "CheckBox", icons["radio_unchecked"]);
theme->set_font("font", "CheckBox", Ref<Font>());
theme->set_font_size("font_size", "CheckBox", -1);
@@ -337,15 +314,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("hover_pressed", "CheckButton", cb_empty);
theme->set_stylebox("focus", "CheckButton", focus);
- theme->set_icon("on", "CheckButton", make_icon(toggle_on_png));
- theme->set_icon("on_disabled", "CheckButton", make_icon(toggle_on_disabled_png));
- theme->set_icon("off", "CheckButton", make_icon(toggle_off_png));
- theme->set_icon("off_disabled", "CheckButton", make_icon(toggle_off_disabled_png));
+ theme->set_icon("on", "CheckButton", icons["toggle_on"]);
+ theme->set_icon("on_disabled", "CheckButton", icons["toggle_on_disabled"]);
+ theme->set_icon("off", "CheckButton", icons["toggle_off"]);
+ theme->set_icon("off_disabled", "CheckButton", icons["toggle_off_disabled"]);
- theme->set_icon("on_mirrored", "CheckButton", make_icon(toggle_on_mirrored_png));
- theme->set_icon("on_disabled_mirrored", "CheckButton", make_icon(toggle_on_disabled_mirrored_png));
- theme->set_icon("off_mirrored", "CheckButton", make_icon(toggle_off_mirrored_png));
- theme->set_icon("off_disabled_mirrored", "CheckButton", make_icon(toggle_off_disabled_mirrored_png));
+ theme->set_icon("on_mirrored", "CheckButton", icons["toggle_on_mirrored"]);
+ theme->set_icon("on_disabled_mirrored", "CheckButton", icons["toggle_on_disabled_mirrored"]);
+ theme->set_icon("off_mirrored", "CheckButton", icons["toggle_off_mirrored"]);
+ theme->set_icon("off_disabled_mirrored", "CheckButton", icons["toggle_off_disabled_mirrored"]);
theme->set_font("font", "CheckButton", Ref<Font>());
theme->set_font_size("font_size", "CheckButton", -1);
@@ -389,16 +366,27 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// LineEdit
- theme->set_stylebox("normal", "LineEdit", make_stylebox(line_edit_png, 5, 5, 5, 5));
+ Ref<StyleBoxFlat> style_line_edit = make_flat_stylebox(style_normal_color);
+ // Add a line at the bottom to make LineEdits distinguishable from Buttons.
+ style_line_edit->set_border_width(SIDE_BOTTOM, 2);
+ style_line_edit->set_border_color(style_pressed_color);
+ theme->set_stylebox("normal", "LineEdit", style_line_edit);
+
theme->set_stylebox("focus", "LineEdit", focus);
- theme->set_stylebox("read_only", "LineEdit", make_stylebox(line_edit_disabled_png, 6, 6, 6, 6));
+
+ Ref<StyleBoxFlat> style_line_edit_read_only = make_flat_stylebox(style_disabled_color);
+ // Add a line at the bottom to make LineEdits distinguishable from Buttons.
+ style_line_edit_read_only->set_border_width(SIDE_BOTTOM, 2);
+ style_line_edit_read_only->set_border_color(style_pressed_color * Color(1, 1, 1, 0.5));
+ theme->set_stylebox("read_only", "LineEdit", style_line_edit_read_only);
theme->set_font("font", "LineEdit", Ref<Font>());
theme->set_font_size("font_size", "LineEdit", -1);
theme->set_color("font_color", "LineEdit", control_font_color);
- theme->set_color("font_selected_color", "LineEdit", Color(0, 0, 0));
- theme->set_color("font_uneditable_color", "LineEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("font_selected_color", "LineEdit", control_font_pressed_color);
+ theme->set_color("font_uneditable_color", "LineEdit", control_font_disabled_color);
+ theme->set_color("font_placeholder_color", "LineEdit", control_font_placeholder_color);
theme->set_color("font_outline_color", "LineEdit", Color(1, 1, 1));
theme->set_color("caret_color", "LineEdit", control_font_hover_color);
theme->set_color("selection_color", "LineEdit", control_selection_color);
@@ -409,12 +397,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("outline_size", "LineEdit", 0);
theme->set_constant("caret_width", "LineEdit", 1);
- theme->set_icon("clear", "LineEdit", make_icon(line_edit_clear_png));
+ theme->set_icon("clear", "LineEdit", icons["line_edit_clear"]);
// ProgressBar
- theme->set_stylebox("bg", "ProgressBar", make_stylebox(progress_bar_png, 4, 4, 4, 4, 0, 0, 0, 0));
- theme->set_stylebox("fg", "ProgressBar", make_stylebox(progress_fill_png, 6, 6, 6, 6, 2, 1, 2, 1));
+ theme->set_stylebox("bg", "ProgressBar", make_flat_stylebox(style_disabled_color, 2, 2, 2, 2, 6));
+ theme->set_stylebox("fg", "ProgressBar", make_flat_stylebox(style_progress_color, 2, 2, 2, 2, 6));
theme->set_font("font", "ProgressBar", Ref<Font>());
theme->set_font_size("font_size", "ProgressBar", -1);
@@ -427,26 +415,27 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// TextEdit
- theme->set_stylebox("normal", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
+ theme->set_stylebox("normal", "TextEdit", style_line_edit);
theme->set_stylebox("focus", "TextEdit", focus);
- theme->set_stylebox("read_only", "TextEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4, 0, 0, 0, 0));
+ theme->set_stylebox("read_only", "TextEdit", style_line_edit_read_only);
- theme->set_icon("tab", "TextEdit", make_icon(tab_png));
- theme->set_icon("space", "TextEdit", make_icon(space_png));
+ theme->set_icon("tab", "TextEdit", icons["text_edit_tab"]);
+ theme->set_icon("space", "TextEdit", icons["text_edit_space"]);
theme->set_font("font", "TextEdit", Ref<Font>());
theme->set_font_size("font_size", "TextEdit", -1);
theme->set_color("background_color", "TextEdit", Color(0, 0, 0, 0));
theme->set_color("font_color", "TextEdit", control_font_color);
- theme->set_color("font_selected_color", "TextEdit", Color(0, 0, 0));
- theme->set_color("font_readonly_color", "TextEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("font_selected_color", "TextEdit", control_font_pressed_color);
+ theme->set_color("font_readonly_color", "TextEdit", control_font_disabled_color);
+ theme->set_color("font_placeholder_color", "TextEdit", control_font_placeholder_color);
theme->set_color("font_outline_color", "TextEdit", Color(1, 1, 1));
theme->set_color("selection_color", "TextEdit", control_selection_color);
theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8));
theme->set_color("caret_color", "TextEdit", control_font_color);
theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0));
- theme->set_color("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15));
+ theme->set_color("word_highlighted_color", "TextEdit", Color(0.5, 0.5, 0.5, 0.25));
theme->set_color("search_result_color", "TextEdit", Color(0.3, 0.3, 0.3));
theme->set_color("search_result_border_color", "TextEdit", Color(0.3, 0.3, 0.3, 0.4));
@@ -456,19 +445,19 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// CodeEdit
- theme->set_stylebox("normal", "CodeEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
+ theme->set_stylebox("normal", "CodeEdit", style_line_edit);
theme->set_stylebox("focus", "CodeEdit", focus);
- theme->set_stylebox("read_only", "CodeEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4, 0, 0, 0, 0));
- theme->set_stylebox("completion", "CodeEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
-
- theme->set_icon("tab", "CodeEdit", make_icon(tab_png));
- theme->set_icon("space", "CodeEdit", make_icon(space_png));
- theme->set_icon("breakpoint", "CodeEdit", make_icon(graph_port_png));
- theme->set_icon("bookmark", "CodeEdit", make_icon(bookmark_png));
- theme->set_icon("executing_line", "CodeEdit", make_icon(arrow_right_png));
- theme->set_icon("can_fold", "CodeEdit", make_icon(arrow_down_png));
- theme->set_icon("folded", "CodeEdit", make_icon(arrow_right_png));
- theme->set_icon("folded_eol_icon", "CodeEdit", make_icon(ellipsis_png));
+ theme->set_stylebox("read_only", "CodeEdit", style_line_edit_read_only);
+ theme->set_stylebox("completion", "CodeEdit", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
+
+ theme->set_icon("tab", "CodeEdit", icons["text_edit_tab"]);
+ theme->set_icon("space", "CodeEdit", icons["text_edit_space"]);
+ theme->set_icon("breakpoint", "CodeEdit", icons["breakpoint"]);
+ theme->set_icon("bookmark", "CodeEdit", icons["bookmark"]);
+ theme->set_icon("executing_line", "CodeEdit", icons["arrow_right"]);
+ theme->set_icon("can_fold", "CodeEdit", icons["arrow_down"]);
+ theme->set_icon("folded", "CodeEdit", icons["arrow_right"]);
+ theme->set_icon("folded_eol_icon", "CodeEdit", icons["text_edit_ellipsis"]);
theme->set_font("font", "CodeEdit", Ref<Font>());
theme->set_font_size("font_size", "CodeEdit", -1);
@@ -482,6 +471,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "CodeEdit", control_font_color);
theme->set_color("font_selected_color", "CodeEdit", Color(0, 0, 0));
theme->set_color("font_readonly_color", "CodeEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+ theme->set_color("font_placeholder_color", "CodeEdit", control_font_placeholder_color);
theme->set_color("font_outline_color", "CodeEdit", Color(1, 1, 1));
theme->set_color("selection_color", "CodeEdit", control_selection_color);
theme->set_color("bookmark_color", "CodeEdit", Color(0.5, 0.64, 1, 0.8));
@@ -506,13 +496,18 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
Ref<Texture2D> empty_icon = memnew(ImageTexture);
+ const Ref<StyleBoxFlat> style_scrollbar = make_flat_stylebox(style_normal_color, 4, 4, 4, 4, 10);
+ Ref<StyleBoxFlat> style_scrollbar_grabber = make_flat_stylebox(style_progress_color, 4, 4, 4, 4, 10);
+ Ref<StyleBoxFlat> style_scrollbar_grabber_highlight = make_flat_stylebox(style_focus_color, 4, 4, 4, 4, 10);
+ Ref<StyleBoxFlat> style_scrollbar_grabber_pressed = make_flat_stylebox(style_focus_color * Color(0.75, 0.75, 0.75), 4, 4, 4, 4, 10);
+
// HScrollBar
- theme->set_stylebox("scroll", "HScrollBar", make_stylebox(scroll_bg_png, 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(scroll_bg_png, 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("grabber", "HScrollBar", make_stylebox(scroll_grabber_png, 5, 5, 5, 5, 2, 2, 2, 2));
- theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(scroll_grabber_hl_png, 5, 5, 5, 5, 2, 2, 2, 2));
- theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(scroll_grabber_pressed_png, 5, 5, 5, 5, 2, 2, 2, 2));
+ theme->set_stylebox("scroll", "HScrollBar", style_scrollbar);
+ theme->set_stylebox("scroll_focus", "HScrollBar", focus);
+ theme->set_stylebox("grabber", "HScrollBar", style_scrollbar_grabber);
+ theme->set_stylebox("grabber_highlight", "HScrollBar", style_scrollbar_grabber_highlight);
+ theme->set_stylebox("grabber_pressed", "HScrollBar", style_scrollbar_grabber_pressed);
theme->set_icon("increment", "HScrollBar", empty_icon);
theme->set_icon("increment_highlight", "HScrollBar", empty_icon);
@@ -523,11 +518,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// VScrollBar
- theme->set_stylebox("scroll", "VScrollBar", make_stylebox(scroll_bg_png, 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(scroll_bg_png, 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("grabber", "VScrollBar", make_stylebox(scroll_grabber_png, 5, 5, 5, 5, 2, 2, 2, 2));
- theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(scroll_grabber_hl_png, 5, 5, 5, 5, 2, 2, 2, 2));
- theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(scroll_grabber_pressed_png, 5, 5, 5, 5, 2, 2, 2, 2));
+ theme->set_stylebox("scroll", "VScrollBar", style_scrollbar);
+ theme->set_stylebox("scroll_focus", "VScrollBar", focus);
+ theme->set_stylebox("grabber", "VScrollBar", style_scrollbar_grabber);
+ theme->set_stylebox("grabber_highlight", "VScrollBar", style_scrollbar_grabber_highlight);
+ theme->set_stylebox("grabber_pressed", "VScrollBar", style_scrollbar_grabber_pressed);
theme->set_icon("increment", "VScrollBar", empty_icon);
theme->set_icon("increment_highlight", "VScrollBar", empty_icon);
@@ -536,31 +531,35 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("decrement_highlight", "VScrollBar", empty_icon);
theme->set_icon("decrement_pressed", "VScrollBar", empty_icon);
+ const Ref<StyleBoxFlat> style_slider = make_flat_stylebox(style_normal_color, 4, 4, 4, 4, 4);
+ const Ref<StyleBoxFlat> style_slider_grabber = make_flat_stylebox(style_progress_color, 4, 4, 4, 4, 4);
+ const Ref<StyleBoxFlat> style_slider_grabber_highlight = make_flat_stylebox(style_focus_color, 4, 4, 4, 4, 4);
+
// HSlider
- theme->set_stylebox("slider", "HSlider", make_stylebox(hslider_bg_png, 4, 4, 4, 4));
- theme->set_stylebox("grabber_area", "HSlider", make_stylebox(hslider_bg_png, 4, 4, 4, 4));
- theme->set_stylebox("grabber_area_highlight", "HSlider", make_stylebox(hslider_bg_png, 4, 4, 4, 4));
+ theme->set_stylebox("slider", "HSlider", style_slider);
+ theme->set_stylebox("grabber_area", "HSlider", style_slider_grabber);
+ theme->set_stylebox("grabber_area_highlight", "HSlider", style_slider_grabber_highlight);
- theme->set_icon("grabber", "HSlider", make_icon(hslider_grabber_png));
- theme->set_icon("grabber_highlight", "HSlider", make_icon(hslider_grabber_hl_png));
- theme->set_icon("grabber_disabled", "HSlider", make_icon(hslider_grabber_disabled_png));
- theme->set_icon("tick", "HSlider", make_icon(hslider_tick_png));
+ theme->set_icon("grabber", "HSlider", icons["slider_grabber"]);
+ theme->set_icon("grabber_highlight", "HSlider", icons["slider_grabber_hl"]);
+ theme->set_icon("grabber_disabled", "HSlider", icons["slider_grabber_disabled"]);
+ theme->set_icon("tick", "HSlider", icons["hslider_tick"]);
// VSlider
- theme->set_stylebox("slider", "VSlider", make_stylebox(vslider_bg_png, 4, 4, 4, 4));
- theme->set_stylebox("grabber_area", "VSlider", make_stylebox(vslider_bg_png, 4, 4, 4, 4));
- theme->set_stylebox("grabber_area_highlight", "VSlider", make_stylebox(vslider_bg_png, 4, 4, 4, 4));
+ theme->set_stylebox("slider", "VSlider", style_slider);
+ theme->set_stylebox("grabber_area", "VSlider", style_slider_grabber);
+ theme->set_stylebox("grabber_area_highlight", "VSlider", style_slider_grabber_highlight);
- theme->set_icon("grabber", "VSlider", make_icon(vslider_grabber_png));
- theme->set_icon("grabber_highlight", "VSlider", make_icon(vslider_grabber_hl_png));
- theme->set_icon("grabber_disabled", "VSlider", make_icon(vslider_grabber_disabled_png));
- theme->set_icon("tick", "VSlider", make_icon(vslider_tick_png));
+ theme->set_icon("grabber", "VSlider", icons["slider_grabber"]);
+ theme->set_icon("grabber_highlight", "VSlider", icons["slider_grabber_hl"]);
+ theme->set_icon("grabber_disabled", "VSlider", icons["slider_grabber_disabled"]);
+ theme->set_icon("tick", "VSlider", icons["vslider_tick"]);
// SpinBox
- theme->set_icon("updown", "SpinBox", make_icon(spinbox_updown_png));
+ theme->set_icon("updown", "SpinBox", icons["updown"]);
// ScrollContainer
@@ -570,67 +569,88 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Window
- theme->set_stylebox("embedded_border", "Window", sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6));
+ theme->set_stylebox("embedded_border", "Window", sb_expand(make_flat_stylebox(style_popup_color, 10, 28, 10, 8), 8, 32, 8, 6));
theme->set_constant("scaleborder_size", "Window", 4 * scale);
- theme->set_font("title_font", "Window", large_font);
+ theme->set_font("title_font", "Window", Ref<Font>());
theme->set_font_size("title_font_size", "Window", -1);
-
- theme->set_color("title_color", "Window", Color(0, 0, 0));
+ theme->set_color("title_color", "Window", control_font_color);
theme->set_color("title_outline_modulate", "Window", Color(1, 1, 1));
-
theme->set_constant("title_outline_size", "Window", 0);
- theme->set_constant("title_height", "Window", 20 * scale);
+ theme->set_constant("title_height", "Window", 36 * scale);
theme->set_constant("resize_margin", "Window", 4 * scale);
- theme->set_icon("close", "Window", make_icon(close_png));
- theme->set_icon("close_pressed", "Window", make_icon(close_hl_png));
+ theme->set_icon("close", "Window", icons["close"]);
+ theme->set_icon("close_pressed", "Window", icons["close_hl"]);
theme->set_constant("close_h_ofs", "Window", 18 * scale);
- theme->set_constant("close_v_ofs", "Window", 18 * scale);
+ theme->set_constant("close_v_ofs", "Window", 24 * scale);
+
+ // Dialogs
+
+ theme->set_constant("margin", "Dialogs", 8 * scale);
+ theme->set_constant("button_margin", "Dialogs", 32 * scale);
// AcceptDialog
- theme->set_stylebox("panel", "AcceptDialog", make_stylebox(dialog_bg_png, 0, 0, 0, 0));
+ theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, 0, 0, 0, 0));
// File Dialog
- theme->set_icon("parent_folder", "FileDialog", make_icon(icon_parent_folder_png));
- theme->set_icon("back_folder", "FileDialog", make_icon(arrow_left_png));
- theme->set_icon("forward_folder", "FileDialog", make_icon(arrow_right_png));
- theme->set_icon("reload", "FileDialog", make_icon(icon_reload_png));
- theme->set_icon("toggle_hidden", "FileDialog", make_icon(icon_visibility_png));
+ theme->set_icon("parent_folder", "FileDialog", icons["folder_up"]);
+ theme->set_icon("back_folder", "FileDialog", icons["arrow_left"]);
+ theme->set_icon("forward_folder", "FileDialog", icons["arrow_right"]);
+ theme->set_icon("reload", "FileDialog", icons["reload"]);
+ theme->set_icon("toggle_hidden", "FileDialog", icons["visibility_visible"]);
+ theme->set_icon("folder", "FileDialog", icons["folder"]);
+ theme->set_icon("file", "FileDialog", icons["file"]);
+ theme->set_color("folder_icon_modulate", "FileDialog", Color(1, 1, 1));
+ theme->set_color("file_icon_modulate", "FileDialog", Color(1, 1, 1));
+ theme->set_color("files_disabled", "FileDialog", Color(0, 0, 0, 0.7));
// Popup
- Ref<StyleBoxTexture> style_pp = sb_expand(make_stylebox(popup_bg_png, 5, 5, 5, 5, 4, 4, 4, 4), 2, 2, 2, 2);
-
- Ref<StyleBoxTexture> selected = make_stylebox(selection_png, 6, 6, 6, 6);
- for (int i = 0; i < 4; i++) {
- selected->set_expand_margin_size(Side(i), 2 * scale);
- }
-
- theme->set_stylebox("panel", "PopupPanel", style_pp);
+ theme->set_stylebox("panel", "PopupPanel", make_flat_stylebox(style_normal_color));
// PopupDialog
- Ref<StyleBoxTexture> style_pd = make_stylebox(popup_bg_png, 4, 4, 4, 4, 10, 10, 10, 10);
- theme->set_stylebox("panel", "PopupDialog", style_pd);
+ theme->set_stylebox("panel", "PopupDialog", make_flat_stylebox(style_normal_color));
// PopupMenu
- theme->set_stylebox("panel", "PopupMenu", style_pd);
- theme->set_stylebox("panel_disabled", "PopupMenu", make_stylebox(popup_bg_disabled_png, 4, 4, 4, 4));
- theme->set_stylebox("hover", "PopupMenu", selected);
- theme->set_stylebox("separator", "PopupMenu", make_stylebox(vseparator_png, 3, 3, 3, 3));
- theme->set_stylebox("labeled_separator_left", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0));
- theme->set_stylebox("labeled_separator_right", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0));
-
- theme->set_icon("checked", "PopupMenu", make_icon(checked_png));
- theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png));
- theme->set_icon("radio_checked", "PopupMenu", make_icon(radio_checked_png));
- theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png));
- theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png));
- theme->set_icon("submenu_mirrored", "PopupMenu", make_icon(submenu_mirrored_png));
+ Ref<StyleBoxLine> separator_horizontal = memnew(StyleBoxLine);
+ separator_horizontal->set_thickness(Math::round(scale));
+ separator_horizontal->set_color(style_separator_color);
+ separator_horizontal->set_default_margin(SIDE_LEFT, default_margin);
+ separator_horizontal->set_default_margin(SIDE_TOP, 0);
+ separator_horizontal->set_default_margin(SIDE_RIGHT, default_margin);
+ separator_horizontal->set_default_margin(SIDE_BOTTOM, 0);
+ Ref<StyleBoxLine> separator_vertical = separator_horizontal->duplicate();
+ separator_vertical->set_vertical(true);
+ separator_vertical->set_default_margin(SIDE_LEFT, 0);
+ separator_vertical->set_default_margin(SIDE_TOP, default_margin);
+ separator_vertical->set_default_margin(SIDE_RIGHT, 0);
+ separator_vertical->set_default_margin(SIDE_BOTTOM, default_margin);
+
+ // Always display a border for PopupMenus so they can be distinguished from their background.
+ Ref<StyleBoxFlat> style_popup_panel = make_flat_stylebox(style_popup_color);
+ style_popup_panel->set_border_width_all(2);
+ style_popup_panel->set_border_color(style_popup_border_color);
+ Ref<StyleBoxFlat> style_popup_panel_disabled = style_popup_panel->duplicate();
+ style_popup_panel_disabled->set_bg_color(style_disabled_color);
+
+ theme->set_stylebox("panel", "PopupMenu", style_popup_panel);
+ theme->set_stylebox("panel_disabled", "PopupMenu", style_popup_panel_disabled);
+ theme->set_stylebox("hover", "PopupMenu", make_flat_stylebox(style_popup_hover_color));
+ theme->set_stylebox("separator", "PopupMenu", separator_horizontal);
+ theme->set_stylebox("labeled_separator_left", "PopupMenu", separator_horizontal);
+ theme->set_stylebox("labeled_separator_right", "PopupMenu", separator_horizontal);
+
+ theme->set_icon("checked", "PopupMenu", icons["checked"]);
+ theme->set_icon("unchecked", "PopupMenu", icons["unchecked"]);
+ theme->set_icon("radio_checked", "PopupMenu", icons["radio_checked"]);
+ theme->set_icon("radio_unchecked", "PopupMenu", icons["radio_unchecked"]);
+ theme->set_icon("submenu", "PopupMenu", icons["arrow_right"]);
+ theme->set_icon("submenu_mirrored", "PopupMenu", icons["arrow_left"]);
theme->set_font("font", "PopupMenu", Ref<Font>());
theme->set_font_size("font_size", "PopupMenu", -1);
@@ -649,65 +669,63 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("item_end_padding", "PopupMenu", 2 * scale);
// GraphNode
-
- Ref<StyleBoxTexture> graphsb = make_stylebox(graph_node_png, 6, 24, 6, 5, 16, 24, 16, 6);
- Ref<StyleBoxTexture> graphsbcomment = make_stylebox(graph_node_comment_png, 6, 24, 6, 5, 16, 24, 16, 6);
- Ref<StyleBoxTexture> graphsbcommentselected = make_stylebox(graph_node_comment_focus_png, 6, 24, 6, 5, 16, 24, 16, 6);
- Ref<StyleBoxTexture> graphsbselected = make_stylebox(graph_node_selected_png, 6, 24, 6, 5, 16, 24, 16, 6);
- Ref<StyleBoxTexture> graphsbdefault = make_stylebox(graph_node_default_png, 4, 4, 4, 4, 6, 4, 4, 4);
- Ref<StyleBoxTexture> graphsbdeffocus = make_stylebox(graph_node_default_focus_png, 4, 4, 4, 4, 6, 4, 4, 4);
- Ref<StyleBoxTexture> graph_bpoint = make_stylebox(graph_node_breakpoint_png, 6, 24, 6, 5, 16, 24, 16, 6);
- Ref<StyleBoxTexture> graph_position = make_stylebox(graph_node_position_png, 6, 24, 6, 5, 16, 24, 16, 6);
-
- //graphsb->set_expand_margin_size(SIDE_LEFT,10);
- //graphsb->set_expand_margin_size(SIDE_RIGHT,10);
- theme->set_stylebox("frame", "GraphNode", graphsb);
- theme->set_stylebox("selectedframe", "GraphNode", graphsbselected);
- theme->set_stylebox("defaultframe", "GraphNode", graphsbdefault);
- theme->set_stylebox("defaultfocus", "GraphNode", graphsbdeffocus);
- theme->set_stylebox("comment", "GraphNode", graphsbcomment);
- theme->set_stylebox("commentfocus", "GraphNode", graphsbcommentselected);
- theme->set_stylebox("breakpoint", "GraphNode", graph_bpoint);
- theme->set_stylebox("position", "GraphNode", graph_position);
- theme->set_constant("separation", "GraphNode", 1 * scale);
- theme->set_icon("port", "GraphNode", make_icon(graph_port_png));
- theme->set_icon("close", "GraphNode", make_icon(graph_node_close_png));
- theme->set_icon("resizer", "GraphNode", make_icon(window_resizer_png));
+ Ref<StyleBoxFlat> graphnode_normal = make_flat_stylebox(style_normal_color, 18, 42, 18, 12);
+ graphnode_normal->set_border_width(SIDE_TOP, 30);
+ graphnode_normal->set_border_color(Color(0.325, 0.325, 0.325, 0.6));
+ Ref<StyleBoxFlat> graphnode_selected = graphnode_normal->duplicate();
+ graphnode_selected->set_border_color(Color(0.625, 0.625, 0.625, 0.6));
+ Ref<StyleBoxFlat> graphnode_comment_normal = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 3, true, 2);
+ graphnode_comment_normal->set_border_color(style_pressed_color);
+ Ref<StyleBoxFlat> graphnode_comment_selected = graphnode_comment_normal->duplicate();
+ graphnode_comment_selected->set_border_color(style_hover_color);
+ Ref<StyleBoxFlat> graphnode_breakpoint = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 6, true, 4);
+ graphnode_breakpoint->set_border_color(Color(0.9, 0.29, 0.3));
+ Ref<StyleBoxFlat> graphnode_position = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 6, true, 4);
+ graphnode_position->set_border_color(Color(0.98, 0.89, 0.27));
+
+ theme->set_stylebox("frame", "GraphNode", graphnode_normal);
+ theme->set_stylebox("selectedframe", "GraphNode", graphnode_selected);
+ theme->set_stylebox("comment", "GraphNode", graphnode_comment_normal);
+ theme->set_stylebox("commentfocus", "GraphNode", graphnode_comment_selected);
+ theme->set_stylebox("breakpoint", "GraphNode", graphnode_breakpoint);
+ theme->set_stylebox("position", "GraphNode", graphnode_position);
+
+ theme->set_icon("port", "GraphNode", icons["graph_port"]);
+ theme->set_icon("close", "GraphNode", icons["close"]);
+ theme->set_icon("resizer", "GraphNode", icons["resizer_se"]);
theme->set_font("title_font", "GraphNode", Ref<Font>());
- theme->set_color("title_color", "GraphNode", Color(0, 0, 0, 1));
- theme->set_color("close_color", "GraphNode", Color(0, 0, 0, 1));
- theme->set_color("resizer_color", "GraphNode", Color(0, 0, 0, 1));
- theme->set_constant("title_offset", "GraphNode", 20 * scale);
- theme->set_constant("close_offset", "GraphNode", 18 * scale);
- theme->set_constant("port_offset", "GraphNode", 3 * scale);
+ theme->set_color("title_color", "GraphNode", control_font_color);
+ theme->set_color("close_color", "GraphNode", control_font_color);
+ theme->set_color("resizer_color", "GraphNode", control_font_color);
+ theme->set_constant("separation", "GraphNode", 2 * scale);
+ theme->set_constant("title_offset", "GraphNode", 26 * scale);
+ theme->set_constant("close_offset", "GraphNode", 22 * scale);
+ theme->set_constant("port_offset", "GraphNode", 0);
// Tree
- Ref<StyleBoxTexture> tree_selected = make_stylebox(selection_png, 4, 4, 4, 4, 8, 0, 8, 0);
- Ref<StyleBoxTexture> tree_selected_oof = make_stylebox(selection_oof_png, 4, 4, 4, 4, 8, 0, 8, 0);
-
- theme->set_stylebox("bg", "Tree", make_stylebox(tree_bg_png, 4, 4, 4, 5));
+ theme->set_stylebox("bg", "Tree", make_flat_stylebox(style_normal_color, 4, 4, 4, 5));
theme->set_stylebox("bg_focus", "Tree", focus);
- theme->set_stylebox("selected", "Tree", tree_selected_oof);
- theme->set_stylebox("selected_focus", "Tree", tree_selected);
+ theme->set_stylebox("selected", "Tree", make_flat_stylebox(style_selected_color));
+ theme->set_stylebox("selected_focus", "Tree", make_flat_stylebox(style_selected_color));
theme->set_stylebox("cursor", "Tree", focus);
theme->set_stylebox("cursor_unfocused", "Tree", focus);
- theme->set_stylebox("button_pressed", "Tree", make_stylebox(button_pressed_png, 4, 4, 4, 4));
- theme->set_stylebox("title_button_normal", "Tree", make_stylebox(tree_title_png, 4, 4, 4, 4));
- theme->set_stylebox("title_button_pressed", "Tree", make_stylebox(tree_title_pressed_png, 4, 4, 4, 4));
- theme->set_stylebox("title_button_hover", "Tree", make_stylebox(tree_title_png, 4, 4, 4, 4));
- theme->set_stylebox("custom_button", "Tree", sb_button_normal);
- theme->set_stylebox("custom_button_pressed", "Tree", sb_button_pressed);
- theme->set_stylebox("custom_button_hover", "Tree", sb_button_hover);
-
- theme->set_icon("checked", "Tree", make_icon(checked_png));
- theme->set_icon("unchecked", "Tree", make_icon(unchecked_png));
- theme->set_icon("indeterminate", "Tree", make_icon(indeterminate_png));
- theme->set_icon("updown", "Tree", make_icon(updown_png));
- theme->set_icon("select_arrow", "Tree", make_icon(dropdown_png));
- theme->set_icon("arrow", "Tree", make_icon(arrow_down_png));
- theme->set_icon("arrow_collapsed", "Tree", make_icon(arrow_right_png));
- theme->set_icon("arrow_collapsed_mirrored", "Tree", make_icon(arrow_left_png));
+ theme->set_stylebox("button_pressed", "Tree", button_pressed);
+ theme->set_stylebox("title_button_normal", "Tree", make_flat_stylebox(style_pressed_color, 4, 4, 4, 4));
+ theme->set_stylebox("title_button_pressed", "Tree", make_flat_stylebox(style_hover_color, 4, 4, 4, 4));
+ theme->set_stylebox("title_button_hover", "Tree", make_flat_stylebox(style_normal_color, 4, 4, 4, 4));
+ theme->set_stylebox("custom_button", "Tree", button_normal);
+ theme->set_stylebox("custom_button_pressed", "Tree", button_pressed);
+ theme->set_stylebox("custom_button_hover", "Tree", button_hover);
+
+ theme->set_icon("checked", "Tree", icons["checked"]);
+ theme->set_icon("unchecked", "Tree", icons["unchecked"]);
+ theme->set_icon("indeterminate", "Tree", icons["indeterminate"]);
+ theme->set_icon("updown", "Tree", icons["updown"]);
+ theme->set_icon("select_arrow", "Tree", icons["option_button_arrow"]);
+ theme->set_icon("arrow", "Tree", icons["arrow_down"]);
+ theme->set_icon("arrow_collapsed", "Tree", icons["arrow_right"]);
+ theme->set_icon("arrow_collapsed_mirrored", "Tree", icons["arrow_left"]);
theme->set_font("title_button_font", "Tree", Ref<Font>());
theme->set_font("font", "Tree", Ref<Font>());
@@ -717,7 +735,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "Tree", control_font_low_color);
theme->set_color("font_selected_color", "Tree", control_font_pressed_color);
theme->set_color("font_outline_color", "Tree", Color(1, 1, 1));
- theme->set_color("guide_color", "Tree", Color(0, 0, 0, 0.1));
+ theme->set_color("guide_color", "Tree", Color(0.7, 0.7, 0.7, 0.25));
theme->set_color("drop_position_color", "Tree", Color(1, 0.3, 0.2));
theme->set_color("relationship_line_color", "Tree", Color(0.27, 0.27, 0.27));
theme->set_color("parent_hl_line_color", "Tree", Color(0.27, 0.27, 0.27));
@@ -726,7 +744,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("hseparation", "Tree", 4 * scale);
theme->set_constant("vseparation", "Tree", 4 * scale);
- theme->set_constant("item_margin", "Tree", 12 * scale);
+ theme->set_constant("item_margin", "Tree", 16 * scale);
theme->set_constant("button_margin", "Tree", 4 * scale);
theme->set_constant("draw_relationship_lines", "Tree", 0);
theme->set_constant("relationship_line_width", "Tree", 1);
@@ -740,10 +758,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// ItemList
- Ref<StyleBoxTexture> item_selected = make_stylebox(selection_png, 4, 4, 4, 4, 8, 2, 8, 2);
- Ref<StyleBoxTexture> item_selected_oof = make_stylebox(selection_oof_png, 4, 4, 4, 4, 8, 2, 8, 2);
-
- theme->set_stylebox("bg", "ItemList", make_stylebox(tree_bg_png, 4, 4, 4, 5));
+ theme->set_stylebox("bg", "ItemList", make_flat_stylebox(style_normal_color));
theme->set_stylebox("bg_focus", "ItemList", focus);
theme->set_constant("hseparation", "ItemList", 4);
theme->set_constant("vseparation", "ItemList", 2);
@@ -757,8 +772,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_selected_color", "ItemList", control_font_pressed_color);
theme->set_color("font_outline_color", "ItemList", Color(1, 1, 1));
theme->set_color("guide_color", "ItemList", Color(0, 0, 0, 0.1));
- theme->set_stylebox("selected", "ItemList", item_selected_oof);
- theme->set_stylebox("selected_focus", "ItemList", item_selected);
+ theme->set_stylebox("selected", "ItemList", make_flat_stylebox(style_selected_color));
+ theme->set_stylebox("selected_focus", "ItemList", make_flat_stylebox(style_selected_color));
theme->set_stylebox("cursor", "ItemList", focus);
theme->set_stylebox("cursor_unfocused", "ItemList", focus);
@@ -766,22 +781,28 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// TabContainer
- Ref<StyleBoxTexture> tc_sb = sb_expand(make_stylebox(tab_container_bg_png, 4, 4, 4, 4, 4, 4, 4, 4), 3, 3, 3, 3);
-
- tc_sb->set_expand_margin_size(SIDE_TOP, 2 * scale);
- tc_sb->set_default_margin(SIDE_TOP, 8 * scale);
-
- theme->set_stylebox("tab_selected", "TabContainer", sb_expand(make_stylebox(tab_current_png, 4, 4, 4, 1, 16, 4, 16, 4), 2, 2, 2, 2));
- theme->set_stylebox("tab_unselected", "TabContainer", sb_expand(make_stylebox(tab_behind_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
- theme->set_stylebox("tab_disabled", "TabContainer", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
- theme->set_stylebox("panel", "TabContainer", tc_sb);
-
- theme->set_icon("increment", "TabContainer", make_icon(scroll_button_right_png));
- theme->set_icon("increment_highlight", "TabContainer", make_icon(scroll_button_right_hl_png));
- theme->set_icon("decrement", "TabContainer", make_icon(scroll_button_left_png));
- theme->set_icon("decrement_highlight", "TabContainer", make_icon(scroll_button_left_hl_png));
- theme->set_icon("menu", "TabContainer", make_icon(tab_menu_png));
- theme->set_icon("menu_highlight", "TabContainer", make_icon(tab_menu_hl_png));
+ Ref<StyleBoxFlat> style_tab_selected = make_flat_stylebox(style_normal_color, 10, 4, 10, 4, 0);
+ style_tab_selected->set_border_width(SIDE_TOP, Math::round(2 * scale));
+ style_tab_selected->set_border_color(style_focus_color);
+ Ref<StyleBoxFlat> style_tab_unselected = make_flat_stylebox(style_pressed_color, 10, 4, 10, 4, 0);
+ // Add some spacing between unselected tabs to make them easier to distinguish from each other.
+ style_tab_unselected->set_border_width(SIDE_LEFT, Math::round(scale));
+ style_tab_unselected->set_border_width(SIDE_RIGHT, Math::round(scale));
+ style_tab_unselected->set_border_color(style_popup_border_color);
+ Ref<StyleBoxFlat> style_tab_disabled = style_tab_unselected->duplicate();
+ style_tab_disabled->set_bg_color(style_disabled_color);
+
+ theme->set_stylebox("tab_selected", "TabContainer", style_tab_selected);
+ theme->set_stylebox("tab_unselected", "TabContainer", style_tab_unselected);
+ theme->set_stylebox("tab_disabled", "TabContainer", style_tab_disabled);
+ theme->set_stylebox("panel", "TabContainer", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
+
+ theme->set_icon("increment", "TabContainer", icons["scroll_button_right"]);
+ theme->set_icon("increment_highlight", "TabContainer", icons["scroll_button_right_hl"]);
+ theme->set_icon("decrement", "TabContainer", icons["scroll_button_left"]);
+ theme->set_icon("decrement_highlight", "TabContainer", icons["scroll_button_left_hl"]);
+ theme->set_icon("menu", "TabContainer", icons["tabs_menu"]);
+ theme->set_icon("menu_highlight", "TabContainer", icons["tabs_menu_hl"]);
theme->set_font("font", "TabContainer", Ref<Font>());
theme->set_font_size("font_size", "TabContainer", -1);
@@ -797,17 +818,17 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// TabBar
- theme->set_stylebox("tab_selected", "TabBar", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2));
- theme->set_stylebox("tab_unselected", "TabBar", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3));
- theme->set_stylebox("tab_disabled", "TabBar", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
- theme->set_stylebox("close_bg_pressed", "TabBar", make_stylebox(button_pressed_png, 4, 4, 4, 4));
- theme->set_stylebox("close_bg_highlight", "TabBar", make_stylebox(button_normal_png, 4, 4, 4, 4));
+ theme->set_stylebox("tab_selected", "TabBar", style_tab_selected);
+ theme->set_stylebox("tab_unselected", "TabBar", style_tab_unselected);
+ theme->set_stylebox("tab_disabled", "TabBar", style_tab_disabled);
+ theme->set_stylebox("button_pressed", "TabBar", button_pressed);
+ theme->set_stylebox("button_highlight", "TabBar", button_normal);
- theme->set_icon("increment", "TabBar", make_icon(scroll_button_right_png));
- theme->set_icon("increment_highlight", "TabBar", make_icon(scroll_button_right_hl_png));
- theme->set_icon("decrement", "TabBar", make_icon(scroll_button_left_png));
- theme->set_icon("decrement_highlight", "TabBar", make_icon(scroll_button_left_hl_png));
- theme->set_icon("close", "TabBar", make_icon(tab_close_png));
+ theme->set_icon("increment", "TabBar", icons["scroll_button_right"]);
+ theme->set_icon("increment_highlight", "TabBar", icons["scroll_button_right_hl"]);
+ theme->set_icon("decrement", "TabBar", icons["scroll_button_left"]);
+ theme->set_icon("decrement_highlight", "TabBar", icons["scroll_button_left_hl"]);
+ theme->set_icon("close", "TabBar", icons["close"]);
theme->set_font("font", "TabBar", Ref<Font>());
theme->set_font_size("font_size", "TabBar", -1);
@@ -822,29 +843,16 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Separators
- theme->set_stylebox("separator", "HSeparator", make_stylebox(vseparator_png, 3, 3, 3, 3));
- theme->set_stylebox("separator", "VSeparator", make_stylebox(hseparator_png, 3, 3, 3, 3));
+ theme->set_stylebox("separator", "HSeparator", separator_horizontal);
+ theme->set_stylebox("separator", "VSeparator", separator_vertical);
- theme->set_icon("close", "Icons", make_icon(icon_close_png));
+ theme->set_icon("close", "Icons", icons["close"]);
theme->set_font("normal", "Fonts", Ref<Font>());
- theme->set_font("large", "Fonts", large_font);
+ theme->set_font("large", "Fonts", Ref<Font>());
theme->set_constant("separation", "HSeparator", 4 * scale);
theme->set_constant("separation", "VSeparator", 4 * scale);
- // Dialogs
-
- theme->set_constant("margin", "Dialogs", 8 * scale);
- theme->set_constant("button_margin", "Dialogs", 32 * scale);
-
- // FileDialog
-
- theme->set_icon("folder", "FileDialog", make_icon(icon_folder_png));
- theme->set_icon("file", "FileDialog", make_icon(icon_file_png));
- theme->set_color("folder_icon_modulate", "FileDialog", Color(1, 1, 1));
- theme->set_color("file_icon_modulate", "FileDialog", Color(1, 1, 1));
- theme->set_color("files_disabled", "FileDialog", Color(0, 0, 0, 0.7));
-
// ColorPicker
theme->set_constant("margin", "ColorPicker", 4 * scale);
@@ -853,23 +861,23 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("h_width", "ColorPicker", 30 * scale);
theme->set_constant("label_width", "ColorPicker", 10 * scale);
- theme->set_icon("screen_picker", "ColorPicker", make_icon(icon_color_pick_png));
- theme->set_icon("add_preset", "ColorPicker", make_icon(icon_add_png));
- theme->set_icon("color_hue", "ColorPicker", make_icon(color_picker_hue_png));
- theme->set_icon("color_sample", "ColorPicker", make_icon(color_picker_sample_png));
- theme->set_icon("sample_bg", "ColorPicker", make_icon(mini_checkerboard_png));
- theme->set_icon("overbright_indicator", "ColorPicker", make_icon(overbright_indicator_png));
- theme->set_icon("bar_arrow", "ColorPicker", make_icon(bar_arrow_png));
- theme->set_icon("picker_cursor", "ColorPicker", make_icon(picker_cursor_png));
+ theme->set_icon("screen_picker", "ColorPicker", icons["color_picker_pipette"]);
+ theme->set_icon("add_preset", "ColorPicker", icons["add"]);
+ theme->set_icon("color_hue", "ColorPicker", icons["color_picker_hue"]);
+ theme->set_icon("color_sample", "ColorPicker", icons["color_picker_sample"]);
+ theme->set_icon("sample_bg", "ColorPicker", icons["mini_checkerboard"]);
+ theme->set_icon("overbright_indicator", "ColorPicker", icons["color_picker_overbright"]);
+ theme->set_icon("bar_arrow", "ColorPicker", icons["color_picker_bar_arrow"]);
+ theme->set_icon("picker_cursor", "ColorPicker", icons["color_picker_cursor"]);
// ColorPickerButton
- theme->set_icon("bg", "ColorPickerButton", make_icon(mini_checkerboard_png));
- theme->set_stylebox("normal", "ColorPickerButton", sb_button_normal);
- theme->set_stylebox("pressed", "ColorPickerButton", sb_button_pressed);
- theme->set_stylebox("hover", "ColorPickerButton", sb_button_hover);
- theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled);
- theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus);
+ theme->set_icon("bg", "ColorPickerButton", icons["mini_checkerboard"]);
+ theme->set_stylebox("normal", "ColorPickerButton", button_normal);
+ theme->set_stylebox("pressed", "ColorPickerButton", button_pressed);
+ theme->set_stylebox("hover", "ColorPickerButton", button_hover);
+ theme->set_stylebox("disabled", "ColorPickerButton", button_disabled);
+ theme->set_stylebox("focus", "ColorPickerButton", focus);
theme->set_font("font", "ColorPickerButton", Ref<Font>());
theme->set_font_size("font_size", "ColorPickerButton", -1);
@@ -892,24 +900,20 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
preset_sb->set_anti_aliased(false);
theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb);
- theme->set_icon("preset_bg", "ColorPresetButton", make_icon(mini_checkerboard_png));
- theme->set_icon("overbright_indicator", "ColorPresetButton", make_icon(overbright_indicator_png));
-
- // TooltipPanel
+ theme->set_icon("preset_bg", "ColorPresetButton", icons["mini_checkerboard"]);
+ theme->set_icon("overbright_indicator", "ColorPresetButton", icons["color_picker_overbright"]);
- Ref<StyleBoxTexture> style_tt = make_stylebox(tooltip_bg_png, 4, 4, 4, 4);
- for (int i = 0; i < 4; i++) {
- style_tt->set_expand_margin_size((Side)i, 4 * scale);
- }
+ // TooltipPanel + TooltipLabel
- theme->set_stylebox("panel", "TooltipPanel", style_tt);
+ theme->set_stylebox("panel", "TooltipPanel",
+ make_flat_stylebox(Color(0, 0, 0, 0.5), 2 * default_margin, 0.5 * default_margin, 2 * default_margin, 0.5 * default_margin));
theme->set_font("font", "TooltipLabel", Ref<Font>());
theme->set_font_size("font_size", "TooltipLabel", -1);
- theme->set_color("font_color", "TooltipLabel", Color(0, 0, 0));
- theme->set_color("font_shadow_color", "TooltipLabel", Color(0, 0, 0, 0.1));
- theme->set_color("font_outline_color", "TooltipLabel", Color(1, 1, 1));
+ theme->set_color("font_color", "TooltipLabel", control_font_color);
+ theme->set_color("font_shadow_color", "TooltipLabel", Color(0, 0, 0, 0));
+ theme->set_color("font_outline_color", "TooltipLabel", Color(0, 0, 0, 0));
theme->set_constant("shadow_offset_x", "TooltipLabel", 1);
theme->set_constant("shadow_offset_y", "TooltipLabel", 1);
@@ -956,11 +960,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Containers
- theme->set_stylebox("bg", "VSplitContainer", make_stylebox(vsplit_bg_png, 1, 1, 1, 1));
- theme->set_stylebox("bg", "HSplitContainer", make_stylebox(hsplit_bg_png, 1, 1, 1, 1));
-
- theme->set_icon("grabber", "VSplitContainer", make_icon(vsplitter_png));
- theme->set_icon("grabber", "HSplitContainer", make_icon(hsplitter_png));
+ theme->set_icon("grabber", "VSplitContainer", icons["vsplitter"]);
+ theme->set_icon("grabber", "HSplitContainer", icons["hsplitter"]);
theme->set_constant("separation", "HBoxContainer", 4 * scale);
theme->set_constant("separation", "VBoxContainer", 4 * scale);
@@ -974,17 +975,20 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("separation", "VSplitContainer", 12 * scale);
theme->set_constant("autohide", "HSplitContainer", 1 * scale);
theme->set_constant("autohide", "VSplitContainer", 1 * scale);
-
- Ref<StyleBoxTexture> sb_pc = make_stylebox(tab_container_bg_png, 4, 4, 4, 4, 7, 7, 7, 7);
- theme->set_stylebox("panel", "PanelContainer", sb_pc);
-
- theme->set_icon("minus", "GraphEdit", make_icon(icon_zoom_less_png));
- theme->set_icon("reset", "GraphEdit", make_icon(icon_zoom_reset_png));
- theme->set_icon("more", "GraphEdit", make_icon(icon_zoom_more_png));
- theme->set_icon("snap", "GraphEdit", make_icon(icon_snap_grid_png));
- theme->set_icon("minimap", "GraphEdit", make_icon(icon_grid_minimap_png));
- theme->set_icon("layout", "GraphEdit", make_icon(icon_grid_layout_png));
- theme->set_stylebox("bg", "GraphEdit", make_stylebox(tree_bg_png, 4, 4, 4, 5));
+ theme->set_constant("hseparation", "HFlowContainer", 4 * scale);
+ theme->set_constant("vseparation", "HFlowContainer", 4 * scale);
+ theme->set_constant("hseparation", "VFlowContainer", 4 * scale);
+ theme->set_constant("vseparation", "VFlowContainer", 4 * scale);
+
+ theme->set_stylebox("panel", "PanelContainer", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
+
+ theme->set_icon("minus", "GraphEdit", icons["zoom_less"]);
+ theme->set_icon("reset", "GraphEdit", icons["zoom_reset"]);
+ theme->set_icon("more", "GraphEdit", icons["zoom_more"]);
+ theme->set_icon("snap", "GraphEdit", icons["grid_snap"]);
+ theme->set_icon("minimap", "GraphEdit", icons["grid_minimap"]);
+ theme->set_icon("layout", "GraphEdit", icons["grid_layout"]);
+ theme->set_stylebox("bg", "GraphEdit", make_flat_stylebox(style_normal_color, 4, 4, 4, 5));
theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05));
theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2));
theme->set_color("selection_fill", "GraphEdit", Color(1, 1, 1, 0.3));
@@ -999,33 +1003,30 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("port_grab_distance_vertical", "GraphEdit", 26 * scale);
theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0));
- Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0);
+ Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0, 0);
style_minimap_camera->set_border_color(Color(0.65, 0.65, 0.65, 0.45));
style_minimap_camera->set_border_width_all(1);
theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera);
- Ref<StyleBoxFlat> style_minimap_node = make_flat_stylebox(Color(1, 1, 1), 0, 0, 0, 0);
- style_minimap_node->set_corner_radius_all(2);
- theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node);
+ theme->set_stylebox("node", "GraphEditMinimap", make_flat_stylebox(Color(1, 1, 1), 0, 0, 0, 0, 2));
- Ref<Texture2D> resizer_icon = make_icon(window_resizer_png);
- theme->set_icon("resizer", "GraphEditMinimap", flip_icon(resizer_icon, true, true));
+ theme->set_icon("resizer", "GraphEditMinimap", icons["resizer_nw"]);
theme->set_color("resizer_color", "GraphEditMinimap", Color(1, 1, 1, 0.85));
// Theme
- default_icon = make_icon(error_icon_png);
- default_style = make_stylebox(error_icon_png, 2, 2, 2, 2);
-
- memdelete(tex_cache);
+ default_icon = icons["error_icon"];
+ // Same color as the error icon.
+ default_style = make_flat_stylebox(Color(1, 0.365, 0.365), 4, 4, 4, 4, 0, false, 2);
}
-void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
+void make_default_theme(float p_scale, Ref<Font> p_font) {
Ref<Theme> t;
t.instantiate();
Ref<StyleBox> default_style;
Ref<Texture2D> default_icon;
Ref<Font> default_font;
+ float default_scale = CLAMP(p_scale, 0.5, 8.0);
if (p_font.is_valid()) {
// Use the custom font defined in the Project Settings.
@@ -1045,21 +1046,14 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
default_font = dynamic_font;
}
- Ref<Font> large_font = default_font;
-
- float default_scale = 1.0;
- if (p_hidpi) {
- default_scale = 2.0;
- }
-
- fill_default_theme(t, default_font, large_font, default_icon, default_style, default_scale);
+ fill_default_theme(t, default_font, default_icon, default_style, default_scale);
Theme::set_default(t);
Theme::set_fallback_base_scale(default_scale);
Theme::set_fallback_icon(default_icon);
Theme::set_fallback_style(default_style);
Theme::set_fallback_font(default_font);
- Theme::set_fallback_font_size(default_font_size);
+ Theme::set_fallback_font_size(default_font_size * default_scale);
}
void clear_default_theme() {
diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h
index e6d7b31b3e..3016517824 100644
--- a/scene/resources/default_theme/default_theme.h
+++ b/scene/resources/default_theme/default_theme.h
@@ -35,8 +35,8 @@
const int default_font_size = 16;
-void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &large_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale);
-void make_default_theme(bool p_hidpi, Ref<Font> p_font);
+void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale);
+void make_default_theme(float p_scale, Ref<Font> p_font);
void clear_default_theme();
#endif
diff --git a/scene/resources/default_theme/default_theme_icons_builders.py b/scene/resources/default_theme/default_theme_icons_builders.py
new file mode 100644
index 0000000000..4dd5819a23
--- /dev/null
+++ b/scene/resources/default_theme/default_theme_icons_builders.py
@@ -0,0 +1,78 @@
+"""Functions used to generate source files during build time
+
+All such functions are invoked in a subprocess on Windows to prevent build flakiness.
+
+"""
+
+import os
+from io import StringIO
+from platform_methods import subprocess_main
+
+
+# See also `editor/icons/editor_icons_builders.py`.
+def make_default_theme_icons_action(target, source, env):
+
+ dst = target[0]
+ svg_icons = source
+
+ icons_string = StringIO()
+
+ for f in svg_icons:
+
+ fname = str(f)
+
+ icons_string.write('\t"')
+
+ with open(fname, "rb") as svgf:
+ b = svgf.read(1)
+ while len(b) == 1:
+ icons_string.write("\\" + str(hex(ord(b)))[1:])
+ b = svgf.read(1)
+
+ icons_string.write('"')
+ if fname != svg_icons[-1]:
+ icons_string.write(",")
+ icons_string.write("\n")
+
+ s = StringIO()
+ s.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n\n")
+ s.write('#include "modules/modules_enabled.gen.h"\n\n')
+ s.write("#ifndef _DEFAULT_THEME_ICONS_H\n")
+ s.write("#define _DEFAULT_THEME_ICONS_H\n")
+ s.write("static const int default_theme_icons_count = {};\n\n".format(len(svg_icons)))
+ s.write("#ifdef MODULE_SVG_ENABLED\n")
+ s.write("static const char *default_theme_icons_sources[] = {\n")
+ s.write(icons_string.getvalue())
+ s.write("};\n")
+ s.write("#endif // MODULE_SVG_ENABLED\n\n")
+ s.write("static const char *default_theme_icons_names[] = {\n")
+
+ index = 0
+ for f in svg_icons:
+
+ fname = str(f)
+
+ # Trim the `.svg` extension from the string.
+ icon_name = os.path.basename(fname)[:-4]
+
+ s.write('\t"{0}"'.format(icon_name))
+
+ if fname != svg_icons[-1]:
+ s.write(",")
+ s.write("\n")
+
+ index += 1
+
+ s.write("};\n")
+
+ s.write("#endif\n")
+
+ with open(dst, "w") as f:
+ f.write(s.getvalue())
+
+ s.close()
+ icons_string.close()
+
+
+if __name__ == "__main__":
+ subprocess_main(globals())
diff --git a/scene/resources/default_theme/dialog_bg.png b/scene/resources/default_theme/dialog_bg.png
deleted file mode 100644
index a23a10b48a..0000000000
--- a/scene/resources/default_theme/dialog_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/dropdown.png b/scene/resources/default_theme/dropdown.png
deleted file mode 100644
index b5d9ffbbb4..0000000000
--- a/scene/resources/default_theme/dropdown.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/ellipsis.png b/scene/resources/default_theme/ellipsis.png
deleted file mode 100644
index c949e2c95b..0000000000
--- a/scene/resources/default_theme/ellipsis.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/error_icon.png b/scene/resources/default_theme/error_icon.png
deleted file mode 100644
index 30336b91a4..0000000000
--- a/scene/resources/default_theme/error_icon.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/error_icon.svg b/scene/resources/default_theme/error_icon.svg
new file mode 100644
index 0000000000..2f5099aa29
--- /dev/null
+++ b/scene/resources/default_theme/error_icon.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1v8.5859l1.293-1.293a1.0001 1.0001 0 0 1 .69141-.29102 1.0001 1.0001 0 0 1 .72266.29102l2.293 2.293 2.293-2.293a1.0001 1.0001 0 0 1 1.4141 0l2.293 2.293 1-1v-3.5859h-5v-5h-7zm8 0v4h4zm-6 9.4141-2 2v2.5859h12v-2.5859l-.29297.29297a1.0001 1.0001 0 0 1 -1.4141 0l-2.293-2.293-2.293 2.293a1.0001 1.0001 0 0 1 -1.4141 0l-2.293-2.293z" fill="#ff5d5d" transform="translate(0 -.000017)"/></svg>
diff --git a/scene/resources/default_theme/error_icon.xpm b/scene/resources/default_theme/error_icon.xpm
deleted file mode 100644
index 666a60338d..0000000000
--- a/scene/resources/default_theme/error_icon.xpm
+++ /dev/null
@@ -1,38 +0,0 @@
-/* XPM */
-static const char * error_icon_xpm[] = {
-"32 32 3 1",
-" c None",
-". c #FF00FF",
-"+ c #000000",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"....++++.+++..+++...++..+++.....",
-"....+....+..+.+..+.+..+.+..+....",
-"....+....+..+.+..+.+..+.+..+....",
-"....+++..+++..+++..+..+.+++.....",
-"....+....+..+.+..+.+..+.+..+....",
-"....+....+..+.+..+.+..+.+..+....",
-"....++++.+..+.+..+..++..+..+....",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................",
-"................................"};
diff --git a/scene/resources/default_theme/file.svg b/scene/resources/default_theme/file.svg
new file mode 100644
index 0000000000..974644c5da
--- /dev/null
+++ b/scene/resources/default_theme/file.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1v14h12v-9h-5v-5zm8 0v4h4z" fill="#b2b2b2" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/focus.png b/scene/resources/default_theme/focus.png
deleted file mode 100644
index f51ea89e8f..0000000000
--- a/scene/resources/default_theme/focus.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/folder.svg b/scene/resources/default_theme/folder.svg
new file mode 100644
index 0000000000..ca4c8c9c70
--- /dev/null
+++ b/scene/resources/default_theme/folder.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2c-.549 0-1 .451-1 1v10c0 .549.451 1 1 1h12c.549 0 1-.451 1-1v-7c0-.549-.451-1-1-1h-4c-.549 0-1-.451-1-1v-1c0-.549-.451-1-1-1z" fill="#b2b2b2" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/folder_up.svg b/scene/resources/default_theme/folder_up.svg
new file mode 100644
index 0000000000..aaf5693d38
--- /dev/null
+++ b/scene/resources/default_theme/folder_up.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m3 8c.003.26.107.509.291.693.388.388 1.026.388 1.414 0l2.293-2.293v4.586c0 .553.448 1 1 1s1-.447 1-1v-4.586l2.293 2.293c.388.388 1.026.388 1.414 0 .388-.387.388-1.026 0-1.414l-4-4c-.388-.387-1.026-.387-1.414 0l-4 4c-.19.191-.295.451-.291.721z" fill="#b2b2b2" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/graph_node.png b/scene/resources/default_theme/graph_node.png
deleted file mode 100644
index d4b4dd3c1f..0000000000
--- a/scene/resources/default_theme/graph_node.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_breakpoint.png b/scene/resources/default_theme/graph_node_breakpoint.png
deleted file mode 100644
index e18c6f42e1..0000000000
--- a/scene/resources/default_theme/graph_node_breakpoint.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_close.png b/scene/resources/default_theme/graph_node_close.png
deleted file mode 100644
index 5c962ae1c6..0000000000
--- a/scene/resources/default_theme/graph_node_close.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_comment.png b/scene/resources/default_theme/graph_node_comment.png
deleted file mode 100644
index cdec1d1eac..0000000000
--- a/scene/resources/default_theme/graph_node_comment.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_comment_focus.png b/scene/resources/default_theme/graph_node_comment_focus.png
deleted file mode 100644
index 472a6b6f53..0000000000
--- a/scene/resources/default_theme/graph_node_comment_focus.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_default.png b/scene/resources/default_theme/graph_node_default.png
deleted file mode 100644
index 359bbdc205..0000000000
--- a/scene/resources/default_theme/graph_node_default.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_default_focus.png b/scene/resources/default_theme/graph_node_default_focus.png
deleted file mode 100644
index 204dd16ac0..0000000000
--- a/scene/resources/default_theme/graph_node_default_focus.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_position.png b/scene/resources/default_theme/graph_node_position.png
deleted file mode 100644
index 24c2759be6..0000000000
--- a/scene/resources/default_theme/graph_node_position.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_node_selected.png b/scene/resources/default_theme/graph_node_selected.png
deleted file mode 100644
index c52d88586b..0000000000
--- a/scene/resources/default_theme/graph_node_selected.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_port.png b/scene/resources/default_theme/graph_port.png
deleted file mode 100644
index 358934f4d8..0000000000
--- a/scene/resources/default_theme/graph_port.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/graph_port.svg b/scene/resources/default_theme/graph_port.svg
new file mode 100644
index 0000000000..423819ea68
--- /dev/null
+++ b/scene/resources/default_theme/graph_port.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m10 5c0 2.743-2.257 5-5 5s-5-2.257-5-5 2.257-5 5-5 5 2.257 5 5" fill="#fff"/><path d="m8.667 5c0 2.011-1.656 3.667-3.667 3.667s-3.667-1.656-3.667-3.667 1.656-3.667 3.667-3.667 3.667 1.656 3.667 3.667" fill="#b2b2b2" fill-opacity=".65"/></g></svg>
diff --git a/scene/resources/default_theme/grid_layout.svg b/scene/resources/default_theme/grid_layout.svg
new file mode 100644
index 0000000000..d5cafca687
--- /dev/null
+++ b/scene/resources/default_theme/grid_layout.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="1.5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke-width="1.3"><g stroke="#fefffe"><path d="m1.87 6.541h2.917v2.917h-2.917z"/><path d="m6.536 6.541h2.917v2.917h-2.917z"/><path d="m11.202 6.541h2.917v2.917h-2.917z"/></g><g stroke="#e0e0e0" stroke-opacity=".65"><path d="m5.432 1.112-1.95 1.95 1.95 1.95" stroke-linejoin="miter" stroke-miterlimit="10"/><path d="m3.482 3.062h9.386"/><path d="m10.731 11.112 1.95 1.95-1.95 1.95" stroke-linejoin="miter" stroke-miterlimit="10"/><path d="m3.294 13.062h9.387"/></g></g></svg>
diff --git a/scene/resources/default_theme/grid_minimap.svg b/scene/resources/default_theme/grid_minimap.svg
new file mode 100644
index 0000000000..a7e5150bc3
--- /dev/null
+++ b/scene/resources/default_theme/grid_minimap.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m14 2.199v2.615l-2.625 1.313v-2.615zm-12 4.065 2.625 1.312v2.551l-2.625-1.313zm12 0v2.55l-2.625 1.313v-2.551zm-8 1.455h4v2.64h-4zm-4 2.56 2.625 1.313v2.521l-2.625-1.312zm12 0v2.522l-2.625 1.312v-2.521zm-8 1.455h4v2.641h-4zm1.701-8.109h2.299v2.734h-4.156s-.749.647-.875.641c-.131-.007-1.51-1.456-1.51-1.456l-1.459-.73v-2.615l.068.034s.027-.457.063-.676c.034-.212.197-.592.197-.592l-1.049-.524c-.079-.04-.167-.062-.256-.066-.354-.013-.648.27-.648.625v12c0 .237.134.453.346.559l4 2c.086.043.182.066.279.066h6c.097 0 .193-.023.279-.066l4-2c.212-.106.346-.322.346-.559v-12c0-.464-.489-.766-.904-.559l-3.869 1.934h-2.971s.033.417.016.625c-.03.346-.196.625-.196.625z" fill="#b2b2b2" fill-opacity=".65"/><path d="m5 6s-2.219-2.162-2.219-3.243c0-1.08 0-2.607 2.219-2.607s2.219 1.527 2.219 2.607c0 1.081-2.219 3.243-2.219 3.243z" fill="#fefffe"/></g></svg>
diff --git a/scene/resources/default_theme/grid_snap.svg b/scene/resources/default_theme/grid_snap.svg
new file mode 100644
index 0000000000..633c85fe79
--- /dev/null
+++ b/scene/resources/default_theme/grid_snap.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m3 0v3h-3v2h3v4h-3v2h3v3h2v-9h9v-2h-3v-3h-2v3h-4v-3zm4 13v2h2v-2zm6 0v2h2v-2z" fill="#b2b2b2" fill-opacity=".65"/><path d="m11 7c-2.194 0-4 1.806-4 4v2h2v-2c0-1.097.903-2 2-2s2 .903 2 2v2h2v-2c0-2.194-1.806-4-4-4z" fill="#fefffe"/></g></svg>
diff --git a/scene/resources/default_theme/hseparator.png b/scene/resources/default_theme/hseparator.png
deleted file mode 100644
index d4fd71ace5..0000000000
--- a/scene/resources/default_theme/hseparator.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_bg.png b/scene/resources/default_theme/hslider_bg.png
deleted file mode 100644
index b402bd370d..0000000000
--- a/scene/resources/default_theme/hslider_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber.png b/scene/resources/default_theme/hslider_grabber.png
deleted file mode 100644
index d273b491ee..0000000000
--- a/scene/resources/default_theme/hslider_grabber.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber_disabled.png b/scene/resources/default_theme/hslider_grabber_disabled.png
deleted file mode 100644
index dddd1a468e..0000000000
--- a/scene/resources/default_theme/hslider_grabber_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_grabber_hl.png b/scene/resources/default_theme/hslider_grabber_hl.png
deleted file mode 100644
index e3defb3610..0000000000
--- a/scene/resources/default_theme/hslider_grabber_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_tick.png b/scene/resources/default_theme/hslider_tick.png
deleted file mode 100644
index 1ba19c37a1..0000000000
--- a/scene/resources/default_theme/hslider_tick.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hslider_tick.svg b/scene/resources/default_theme/hslider_tick.svg
new file mode 100644
index 0000000000..a97d05b9e8
--- /dev/null
+++ b/scene/resources/default_theme/hslider_tick.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 4 15.999999" width="4" xmlns="http://www.w3.org/2000/svg"><path d="m1 0h2v16h-2z" fill="#fff" fill-opacity=".25" stroke-width=".285079"/></svg>
diff --git a/scene/resources/default_theme/hsplit_bg.png b/scene/resources/default_theme/hsplit_bg.png
deleted file mode 100644
index a5749f6d5c..0000000000
--- a/scene/resources/default_theme/hsplit_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hsplitter.png b/scene/resources/default_theme/hsplitter.png
deleted file mode 100644
index 2287753c9d..0000000000
--- a/scene/resources/default_theme/hsplitter.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/hsplitter.svg b/scene/resources/default_theme/hsplitter.svg
new file mode 100644
index 0000000000..effc6996cc
--- /dev/null
+++ b/scene/resources/default_theme/hsplitter.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" viewBox="0 0 8 48" xmlns="http://www.w3.org/2000/svg"><path d="m4 4.012v39.976" fill="none" stroke="#808080" stroke-opacity=".65" stroke-width="1.7"/></svg>
diff --git a/scene/resources/default_theme/icon_add.png b/scene/resources/default_theme/icon_add.png
deleted file mode 100644
index eccb69b363..0000000000
--- a/scene/resources/default_theme/icon_add.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_close.png b/scene/resources/default_theme/icon_close.png
deleted file mode 100644
index 4d4ac4a551..0000000000
--- a/scene/resources/default_theme/icon_close.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_color_pick.png b/scene/resources/default_theme/icon_color_pick.png
deleted file mode 100644
index 46953febb8..0000000000
--- a/scene/resources/default_theme/icon_color_pick.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_file.png b/scene/resources/default_theme/icon_file.png
deleted file mode 100644
index bb4c361a8d..0000000000
--- a/scene/resources/default_theme/icon_file.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_folder.png b/scene/resources/default_theme/icon_folder.png
deleted file mode 100644
index d1b308e88d..0000000000
--- a/scene/resources/default_theme/icon_folder.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_grid_layout.png b/scene/resources/default_theme/icon_grid_layout.png
deleted file mode 100644
index a249252a79..0000000000
--- a/scene/resources/default_theme/icon_grid_layout.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_grid_minimap.png b/scene/resources/default_theme/icon_grid_minimap.png
deleted file mode 100644
index 00a6179d5e..0000000000
--- a/scene/resources/default_theme/icon_grid_minimap.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_parent_folder.png b/scene/resources/default_theme/icon_parent_folder.png
deleted file mode 100644
index 35d218722e..0000000000
--- a/scene/resources/default_theme/icon_parent_folder.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_reload.png b/scene/resources/default_theme/icon_reload.png
deleted file mode 100644
index bec5f3f4f9..0000000000
--- a/scene/resources/default_theme/icon_reload.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_snap_grid.png b/scene/resources/default_theme/icon_snap_grid.png
deleted file mode 100644
index 0680317d86..0000000000
--- a/scene/resources/default_theme/icon_snap_grid.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_visibility.png b/scene/resources/default_theme/icon_visibility.png
deleted file mode 100644
index b2df8cbdb8..0000000000
--- a/scene/resources/default_theme/icon_visibility.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_less.png b/scene/resources/default_theme/icon_zoom_less.png
deleted file mode 100644
index 03119c60ca..0000000000
--- a/scene/resources/default_theme/icon_zoom_less.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_more.png b/scene/resources/default_theme/icon_zoom_more.png
deleted file mode 100644
index 31467ec3de..0000000000
--- a/scene/resources/default_theme/icon_zoom_more.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/icon_zoom_reset.png b/scene/resources/default_theme/icon_zoom_reset.png
deleted file mode 100644
index cac68c09fa..0000000000
--- a/scene/resources/default_theme/icon_zoom_reset.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/indeterminate.png b/scene/resources/default_theme/indeterminate.png
deleted file mode 100644
index 28a457b251..0000000000
--- a/scene/resources/default_theme/indeterminate.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/indeterminate.svg b/scene/resources/default_theme/indeterminate.svg
new file mode 100644
index 0000000000..2a742e1475
--- /dev/null
+++ b/scene/resources/default_theme/indeterminate.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#fff" fill-opacity=".75" stroke-width="1.16667"/><path d="m3 7h10v2h-10z" fill="#1a1a1a" stroke-linecap="square" stroke-opacity=".75" stroke-width="2"/></svg>
diff --git a/scene/resources/default_theme/line_edit.png b/scene/resources/default_theme/line_edit.png
deleted file mode 100644
index 2b0c506f34..0000000000
--- a/scene/resources/default_theme/line_edit.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/line_edit_clear.png b/scene/resources/default_theme/line_edit_clear.png
deleted file mode 100644
index af2775a132..0000000000
--- a/scene/resources/default_theme/line_edit_clear.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/line_edit_clear.svg b/scene/resources/default_theme/line_edit_clear.svg
new file mode 100644
index 0000000000..3709c43410
--- /dev/null
+++ b/scene/resources/default_theme/line_edit_clear.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m1 3 2-2 5 5 5-5 2 2-5 5 5 5-2 2-5.302-5-4.698 5-2-2 5-5z" fill="#fff" fill-opacity=".75" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/line_edit_disabled.png b/scene/resources/default_theme/line_edit_disabled.png
deleted file mode 100644
index 69d78febd9..0000000000
--- a/scene/resources/default_theme/line_edit_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/make_header.py b/scene/resources/default_theme/make_header.py
deleted file mode 100755
index efad3b2815..0000000000
--- a/scene/resources/default_theme/make_header.py
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env python
-
-import glob
-import os
-
-enc = "utf-8"
-
-# Change to the directory where the script is located,
-# so that the script can be run from any location
-os.chdir(os.path.dirname(os.path.realpath(__file__)))
-
-# Generate include files
-
-f = open("theme_data.h", "wb")
-
-f.write(b"// THIS FILE HAS BEEN AUTOGENERATED, DON'T EDIT!!\n")
-
-# Generate png image block
-f.write(b"\n// png image block\n")
-
-pixmaps = glob.glob("*.png")
-pixmaps.sort()
-
-for x in pixmaps:
-
- var_str = x[:-4] + "_png"
-
- s = "\nstatic const unsigned char " + var_str + "[] = {\n\t"
- f.write(s.encode(enc))
-
- pngf = open(x, "rb")
-
- b = pngf.read(1)
- while len(b) == 1:
- f.write(hex(ord(b)).encode(enc))
- b = pngf.read(1)
- if len(b) == 1:
- f.write(b", ")
-
- f.write(b"\n};\n")
- pngf.close()
-
-# Generate shaders block
-f.write(b"\n// shaders block\n")
-
-shaders = glob.glob("*.gsl")
-shaders.sort()
-
-for x in shaders:
-
- var_str = x[:-4] + "_shader_code"
-
- s = "\nstatic const char *" + var_str + " = \n"
- f.write(s.encode(enc))
-
- sf = open(x, "rb")
-
- b = sf.readline()
- while b != "":
- if b.endswith("\r\n"):
- b = b[:-2]
- if b.endswith("\n"):
- b = b[:-1]
- s = ' "' + b
- f.write(s.encode(enc))
- b = sf.readline()
- if b != "":
- f.write(b'"\n')
-
- f.write(b'";\n')
- sf.close()
-
-f.close()
diff --git a/scene/resources/default_theme/mini_checkerboard.png b/scene/resources/default_theme/mini_checkerboard.png
deleted file mode 100644
index d8279bda80..0000000000
--- a/scene/resources/default_theme/mini_checkerboard.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/mini_checkerboard.svg b/scene/resources/default_theme/mini_checkerboard.svg
new file mode 100644
index 0000000000..0ae6a855bd
--- /dev/null
+++ b/scene/resources/default_theme/mini_checkerboard.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-linecap="round" stroke-linejoin="round" stroke-width="1.9994"><path d="m0 0v8h8v-8zm8 8v8h8v-8z" fill="#e0e0e0"/><path d="m8 0v8h8v-8zm0 8h-8v8h8z" fill="#fff"/></g></svg>
diff --git a/scene/resources/default_theme/option_arrow.png b/scene/resources/default_theme/option_arrow.png
deleted file mode 100644
index 40590fd60a..0000000000
--- a/scene/resources/default_theme/option_arrow.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_arrow.svg b/scene/resources/default_theme/option_button_arrow.svg
new file mode 100644
index 0000000000..1a33668708
--- /dev/null
+++ b/scene/resources/default_theme/option_button_arrow.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="m10 3c-.264.01-.514.117-.697.307l-3.293 3.293-3.293-3.293c-.189-.194-.447-.303-.717-.303-.897 0-1.34 1.091-.697 1.717l4 4c.39.39 1.023.39 1.414 0l4-4c.657-.632.195-1.742-.717-1.721z" fill="#b2b2b2" fill-opacity=".85" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/option_button_disabled.png b/scene/resources/default_theme/option_button_disabled.png
deleted file mode 100644
index 1961b673cd..0000000000
--- a/scene/resources/default_theme/option_button_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_disabled_mirrored.png b/scene/resources/default_theme/option_button_disabled_mirrored.png
deleted file mode 100644
index 9d149a35ca..0000000000
--- a/scene/resources/default_theme/option_button_disabled_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_hover.png b/scene/resources/default_theme/option_button_hover.png
deleted file mode 100644
index 826fe1c9ca..0000000000
--- a/scene/resources/default_theme/option_button_hover.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_hover_mirrored.png b/scene/resources/default_theme/option_button_hover_mirrored.png
deleted file mode 100644
index d49c165645..0000000000
--- a/scene/resources/default_theme/option_button_hover_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_normal.png b/scene/resources/default_theme/option_button_normal.png
deleted file mode 100644
index 43fc29e958..0000000000
--- a/scene/resources/default_theme/option_button_normal.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_normal_mirrored.png b/scene/resources/default_theme/option_button_normal_mirrored.png
deleted file mode 100644
index feec848f33..0000000000
--- a/scene/resources/default_theme/option_button_normal_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_pressed.png b/scene/resources/default_theme/option_button_pressed.png
deleted file mode 100644
index 68796f9d85..0000000000
--- a/scene/resources/default_theme/option_button_pressed.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/option_button_pressed_mirrored.png b/scene/resources/default_theme/option_button_pressed_mirrored.png
deleted file mode 100644
index 94cabb18d6..0000000000
--- a/scene/resources/default_theme/option_button_pressed_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/overbright_indicator.png b/scene/resources/default_theme/overbright_indicator.png
deleted file mode 100644
index e13f15dd02..0000000000
--- a/scene/resources/default_theme/overbright_indicator.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/panel_bg.png b/scene/resources/default_theme/panel_bg.png
deleted file mode 100644
index b496e2177e..0000000000
--- a/scene/resources/default_theme/panel_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/picker_cursor.png b/scene/resources/default_theme/picker_cursor.png
deleted file mode 100644
index 2f403492d2..0000000000
--- a/scene/resources/default_theme/picker_cursor.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/popup_bg.png b/scene/resources/default_theme/popup_bg.png
deleted file mode 100644
index 023029f936..0000000000
--- a/scene/resources/default_theme/popup_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/popup_bg_disabled.png b/scene/resources/default_theme/popup_bg_disabled.png
deleted file mode 100644
index 8eab5f27bc..0000000000
--- a/scene/resources/default_theme/popup_bg_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/popup_window.png b/scene/resources/default_theme/popup_window.png
deleted file mode 100644
index 442084049d..0000000000
--- a/scene/resources/default_theme/popup_window.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/progress_bar.png b/scene/resources/default_theme/progress_bar.png
deleted file mode 100644
index 057557e567..0000000000
--- a/scene/resources/default_theme/progress_bar.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/progress_fill.png b/scene/resources/default_theme/progress_fill.png
deleted file mode 100644
index e39bb2a021..0000000000
--- a/scene/resources/default_theme/progress_fill.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/radio_checked.png b/scene/resources/default_theme/radio_checked.png
deleted file mode 100644
index 0ce575c15f..0000000000
--- a/scene/resources/default_theme/radio_checked.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/radio_checked.svg b/scene/resources/default_theme/radio_checked.svg
new file mode 100644
index 0000000000..73d1c8e34a
--- /dev/null
+++ b/scene/resources/default_theme/radio_checked.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#fff" fill-opacity=".75" stroke-width="2.33333"/><path d="m12 8a4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 4-4 4 4 0 0 1 4 4" fill="#1a1a1a" stroke-width="1.33333"/></svg>
diff --git a/scene/resources/default_theme/radio_checked_disabled.png b/scene/resources/default_theme/radio_checked_disabled.png
deleted file mode 100644
index 72f08ecb96..0000000000
--- a/scene/resources/default_theme/radio_checked_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/radio_checked_disabled.svg b/scene/resources/default_theme/radio_checked_disabled.svg
new file mode 100644
index 0000000000..ef5eff8d97
--- /dev/null
+++ b/scene/resources/default_theme/radio_checked_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#fff" fill-opacity=".37" stroke-width="2.33333"/><path d="m12 8a4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 4-4 4 4 0 0 1 4 4" fill="#1a1a1a" fill-opacity=".5" stroke-width="1.33333"/></svg>
diff --git a/scene/resources/default_theme/radio_unchecked.png b/scene/resources/default_theme/radio_unchecked.png
deleted file mode 100644
index fe5bcf6ab1..0000000000
--- a/scene/resources/default_theme/radio_unchecked.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/radio_unchecked.svg b/scene/resources/default_theme/radio_unchecked.svg
new file mode 100644
index 0000000000..5429855db5
--- /dev/null
+++ b/scene/resources/default_theme/radio_unchecked.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#1a1a1a" fill-opacity=".5" stroke-width="2.33333"/></svg>
diff --git a/scene/resources/default_theme/radio_unchecked_disabled.png b/scene/resources/default_theme/radio_unchecked_disabled.png
deleted file mode 100644
index a8f4c1b555..0000000000
--- a/scene/resources/default_theme/radio_unchecked_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/radio_unchecked_disabled.svg b/scene/resources/default_theme/radio_unchecked_disabled.svg
new file mode 100644
index 0000000000..1e827d9374
--- /dev/null
+++ b/scene/resources/default_theme/radio_unchecked_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#1a1a1a" fill-opacity=".25" stroke-width="2.33333"/></svg>
diff --git a/scene/resources/default_theme/reload.svg b/scene/resources/default_theme/reload.svg
new file mode 100644
index 0000000000..66809f8374
--- /dev/null
+++ b/scene/resources/default_theme/reload.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#b2b2b2" fill-rule="nonzero"><path d="m8.987 2c-3.292 0-6 2.708-6 6h2c0-2.194 1.806-4 4-4s4 1.806 4 4-1.806 4-4 4v2c3.291 0 6-2.708 6-6s-2.709-6-6-6z"/><path d="m7.013 7.989-1.5 2-1.5 2-1.5-2-1.5-2z"/></g></svg>
diff --git a/scene/resources/default_theme/resizer_nw.svg b/scene/resources/default_theme/resizer_nw.svg
new file mode 100644
index 0000000000..92e25c3be2
--- /dev/null
+++ b/scene/resources/default_theme/resizer_nw.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".65"><path d="m4 12c.552 0 1-.448 1-1v-6h6c.552 0 1-.448 1-1s-.448-1-1-1h-7c-.552 0-1 .448-1 1v7c0 .552.448 1 1 1z" fill="#fefffe" fill-rule="nonzero"/><circle cx="7.5" cy="7.5" fill="#b2b2b2" r="1.479"/></g></svg>
diff --git a/scene/resources/default_theme/resizer_se.svg b/scene/resources/default_theme/resizer_se.svg
new file mode 100644
index 0000000000..2d492e976e
--- /dev/null
+++ b/scene/resources/default_theme/resizer_se.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".65"><path d="m11 3c-.552 0-1 .448-1 1v6h-6c-.552 0-1 .448-1 1s.448 1 1 1h7c.552 0 1-.448 1-1v-7c0-.552-.448-1-1-1z" fill="#fefffe" fill-rule="nonzero"/><circle cx="7.5" cy="7.5" fill="#b2b2b2" r="1.479"/></g></svg>
diff --git a/scene/resources/default_theme/scroll_bg.png b/scene/resources/default_theme/scroll_bg.png
deleted file mode 100644
index fb151a48b1..0000000000
--- a/scene/resources/default_theme/scroll_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_left.png b/scene/resources/default_theme/scroll_button_left.png
deleted file mode 100644
index e430cb4673..0000000000
--- a/scene/resources/default_theme/scroll_button_left.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_left.svg b/scene/resources/default_theme/scroll_button_left.svg
new file mode 100644
index 0000000000..947b417a00
--- /dev/null
+++ b/scene/resources/default_theme/scroll_button_left.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#fefffe" fill-opacity=".75" r="6"/><path d="m9.014 4c-.27-.004-.53.102-.721.293l-3 3c-.388.388-.388 1.026 0 1.414l3 3c.388.388 1.026.388 1.414 0s.388-1.026 0-1.414l-2.293-2.293 2.293-2.293c.388-.388.388-1.026 0-1.414-.184-.184-.433-.289-.693-.293z" fill="#1a1a1a" fill-opacity=".65"/></svg>
diff --git a/scene/resources/default_theme/scroll_button_left_hl.png b/scene/resources/default_theme/scroll_button_left_hl.png
deleted file mode 100644
index 2a6ef17a34..0000000000
--- a/scene/resources/default_theme/scroll_button_left_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_left_hl.svg b/scene/resources/default_theme/scroll_button_left_hl.svg
new file mode 100644
index 0000000000..fa092c861a
--- /dev/null
+++ b/scene/resources/default_theme/scroll_button_left_hl.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#fefffe" r="6"/><path d="m9.014 4c-.27-.004-.53.102-.721.293l-3 3c-.388.388-.388 1.026 0 1.414l3 3c.388.388 1.026.388 1.414 0s.388-1.026 0-1.414l-2.293-2.293 2.293-2.293c.388-.388.388-1.026 0-1.414-.184-.184-.433-.289-.693-.293z" fill="#1a1a1a"/></svg>
diff --git a/scene/resources/default_theme/scroll_button_right.png b/scene/resources/default_theme/scroll_button_right.png
deleted file mode 100644
index 4f61687aa4..0000000000
--- a/scene/resources/default_theme/scroll_button_right.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_right.svg b/scene/resources/default_theme/scroll_button_right.svg
new file mode 100644
index 0000000000..84619e7aa9
--- /dev/null
+++ b/scene/resources/default_theme/scroll_button_right.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#fefffe" fill-opacity=".75" r="6"/><path d="m6.986 4c.27-.004.53.102.721.293l3 3c.388.388.388 1.026 0 1.414l-3 3c-.388.388-1.026.388-1.414 0s-.388-1.026 0-1.414l2.293-2.293-2.293-2.293c-.388-.388-.388-1.026 0-1.414.184-.184.433-.289.693-.293z" fill="#1a1a1a" fill-opacity=".65"/></svg>
diff --git a/scene/resources/default_theme/scroll_button_right_hl.png b/scene/resources/default_theme/scroll_button_right_hl.png
deleted file mode 100644
index 10e2722509..0000000000
--- a/scene/resources/default_theme/scroll_button_right_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_button_right_hl.svg b/scene/resources/default_theme/scroll_button_right_hl.svg
new file mode 100644
index 0000000000..601f98c0d5
--- /dev/null
+++ b/scene/resources/default_theme/scroll_button_right_hl.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#fefffe" r="6"/><path d="m6.986 4c.27-.004.53.102.721.293l3 3c.388.388.388 1.026 0 1.414l-3 3c-.388.388-1.026.388-1.414 0s-.388-1.026 0-1.414l2.293-2.293-2.293-2.293c-.388-.388-.388-1.026 0-1.414.184-.184.433-.289.693-.293z" fill="#1a1a1a"/></svg>
diff --git a/scene/resources/default_theme/scroll_grabber.png b/scene/resources/default_theme/scroll_grabber.png
deleted file mode 100644
index 732725a28f..0000000000
--- a/scene/resources/default_theme/scroll_grabber.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_grabber_hl.png b/scene/resources/default_theme/scroll_grabber_hl.png
deleted file mode 100644
index 21ee486e0b..0000000000
--- a/scene/resources/default_theme/scroll_grabber_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/scroll_grabber_pressed.png b/scene/resources/default_theme/scroll_grabber_pressed.png
deleted file mode 100644
index f4886158fa..0000000000
--- a/scene/resources/default_theme/scroll_grabber_pressed.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/selection.png b/scene/resources/default_theme/selection.png
deleted file mode 100644
index 7d1c985b35..0000000000
--- a/scene/resources/default_theme/selection.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/selection_oof.png b/scene/resources/default_theme/selection_oof.png
deleted file mode 100644
index 2da0538389..0000000000
--- a/scene/resources/default_theme/selection_oof.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/slider_grabber.svg b/scene/resources/default_theme/slider_grabber.svg
new file mode 100644
index 0000000000..0813830134
--- /dev/null
+++ b/scene/resources/default_theme/slider_grabber.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#fefefe" fill-opacity=".75" stroke-width="2.33333"/></svg>
diff --git a/scene/resources/default_theme/slider_grabber_disabled.svg b/scene/resources/default_theme/slider_grabber_disabled.svg
new file mode 100644
index 0000000000..659c2e9e14
--- /dev/null
+++ b/scene/resources/default_theme/slider_grabber_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#fefefe" fill-opacity=".37" stroke-width="2.33333"/></svg>
diff --git a/scene/resources/default_theme/slider_grabber_hl.svg b/scene/resources/default_theme/slider_grabber_hl.svg
new file mode 100644
index 0000000000..d7d0594d67
--- /dev/null
+++ b/scene/resources/default_theme/slider_grabber_hl.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#fefefe" fill-opacity=".997951" stroke-width="2.33333"/></svg>
diff --git a/scene/resources/default_theme/space.png b/scene/resources/default_theme/space.png
deleted file mode 100644
index 3c66316074..0000000000
--- a/scene/resources/default_theme/space.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/spinbox_updown.png b/scene/resources/default_theme/spinbox_updown.png
deleted file mode 100644
index 74fab19f34..0000000000
--- a/scene/resources/default_theme/spinbox_updown.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/submenu.png b/scene/resources/default_theme/submenu.png
deleted file mode 100644
index 8f7de446d4..0000000000
--- a/scene/resources/default_theme/submenu.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/submenu_mirrored.png b/scene/resources/default_theme/submenu_mirrored.png
deleted file mode 100644
index 1142b9ba9f..0000000000
--- a/scene/resources/default_theme/submenu_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab.png b/scene/resources/default_theme/tab.png
deleted file mode 100644
index 895daa65e2..0000000000
--- a/scene/resources/default_theme/tab.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_behind.png b/scene/resources/default_theme/tab_behind.png
deleted file mode 100644
index 2803d9db65..0000000000
--- a/scene/resources/default_theme/tab_behind.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_close.png b/scene/resources/default_theme/tab_close.png
deleted file mode 100644
index af2775a132..0000000000
--- a/scene/resources/default_theme/tab_close.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_container_bg.png b/scene/resources/default_theme/tab_container_bg.png
deleted file mode 100644
index c189b61b89..0000000000
--- a/scene/resources/default_theme/tab_container_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_current.png b/scene/resources/default_theme/tab_current.png
deleted file mode 100644
index d5641e917a..0000000000
--- a/scene/resources/default_theme/tab_current.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_disabled.png b/scene/resources/default_theme/tab_disabled.png
deleted file mode 100644
index a7c04effa3..0000000000
--- a/scene/resources/default_theme/tab_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_menu.png b/scene/resources/default_theme/tab_menu.png
deleted file mode 100644
index fa4421a28a..0000000000
--- a/scene/resources/default_theme/tab_menu.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tab_menu_hl.png b/scene/resources/default_theme/tab_menu_hl.png
deleted file mode 100644
index fa4421a28a..0000000000
--- a/scene/resources/default_theme/tab_menu_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tabs_menu.svg b/scene/resources/default_theme/tabs_menu.svg
new file mode 100644
index 0000000000..4670a5deae
--- /dev/null
+++ b/scene/resources/default_theme/tabs_menu.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 0c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2zm0 6c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2zm0 6c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2z" fill="#b2b2b2" fill-opacity=".45" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/tabs_menu_hl.svg b/scene/resources/default_theme/tabs_menu_hl.svg
new file mode 100644
index 0000000000..4ea54a2fda
--- /dev/null
+++ b/scene/resources/default_theme/tabs_menu_hl.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 0c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2zm0 6c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2zm0 6c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2z" fill="#fefffe" fill-opacity=".65" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/text_edit_ellipsis.svg b/scene/resources/default_theme/text_edit_ellipsis.svg
new file mode 100644
index 0000000000..026a1fcba7
--- /dev/null
+++ b/scene/resources/default_theme/text_edit_ellipsis.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 14 8" xmlns="http://www.w3.org/2000/svg"><path d="m3.859 0c-2.138 0-3.859 1.721-3.859 3.859v.282c0 2.138 1.721 3.859 3.859 3.859h6.282c2.138 0 3.859-1.721 3.859-3.859v-.282c0-2.138-1.721-3.859-3.859-3.859zm-.859 3c.549 0 1 .451 1 1s-.451 1-1 1-1-.451-1-1 .451-1 1-1zm4 0c.549 0 1 .451 1 1s-.451 1-1 1-1-.451-1-1 .451-1 1-1zm4 0c.549 0 1 .451 1 1s-.451 1-1 1-1-.451-1-1 .451-1 1-1z" fill="#fefffe" fill-opacity=".25" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/text_edit_space.svg b/scene/resources/default_theme/text_edit_space.svg
new file mode 100644
index 0000000000..b2b1dd4676
--- /dev/null
+++ b/scene/resources/default_theme/text_edit_space.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg"><circle cx="4" cy="4" fill="#b2b2b2" fill-opacity=".25" r="1.5"/></svg>
diff --git a/scene/resources/default_theme/text_edit_tab.svg b/scene/resources/default_theme/text_edit_tab.svg
new file mode 100644
index 0000000000..1968afb1e7
--- /dev/null
+++ b/scene/resources/default_theme/text_edit_tab.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg"><path d="m6 0v8h2v-8zm-5.014.002c-.26.003-.509.108-.693.291-.388.388-.388 1.026 0 1.414l2.293 2.293-2.293 2.293c-.388.388-.388 1.026 0 1.414s1.026.388 1.414 0l3-3c.388-.388.388-1.026 0-1.414l-3-3c-.191-.19-.451-.295-.721-.291z" fill="#b2b2b2" fill-opacity=".25" fill-rule="nonzero"/></svg>
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
deleted file mode 100644
index 57ff9a5325..0000000000
--- a/scene/resources/default_theme/theme_data.h
+++ /dev/null
@@ -1,517 +0,0 @@
-// THIS FILE HAS BEEN AUTOGENERATED, DON'T EDIT!!
-
-// png image block
-
-static const unsigned char arrow_down_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x34, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x32, 0x78, 0xf0, 0x1f, 0x15, 0x52, 0x20, 0xf1, 0x30, 0xee, 0xc1, 0x17, 0xb8, 0xf0, 0xb7, 0x87, 0x69, 0x48, 0xb6, 0xdc, 0xd7, 0xb8, 0x7f, 0x9, 0x2c, 0x7c, 0xfd, 0xb1, 0x2e, 0x9a, 0x3, 0x5e, 0x70, 0x3f, 0x9c, 0xff, 0x70, 0xfe, 0xb, 0x6e, 0x6, 0xea, 0x3, 0x0, 0xfb, 0x81, 0x48, 0xb8, 0x4d, 0xe4, 0x75, 0xd9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char arrow_left_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0xa0, 0x0, 0xdc, 0x4f, 0x78, 0xf0, 0xff, 0xc1, 0xff, 0x7, 0xff, 0x21, 0x3c, 0x46, 0x98, 0xf0, 0x43, 0xed, 0xff, 0x27, 0x19, 0xb8, 0x19, 0x18, 0x18, 0x18, 0x14, 0x18, 0x19, 0x18, 0x18, 0x18, 0x98, 0x20, 0xc2, 0x2f, 0xb8, 0xff, 0xaf, 0x82, 0x8, 0xc3, 0x0, 0x54, 0xe2, 0xe7, 0x14, 0x6, 0x2d, 0x54, 0x83, 0x99, 0x70, 0xd9, 0x8, 0x95, 0x60, 0xcf, 0x61, 0xb8, 0x86, 0x55, 0x42, 0xe2, 0x2b, 0x63, 0x18, 0xc3, 0x57, 0xac, 0x46, 0xc9, 0x5f, 0xfd, 0x9f, 0x43, 0x89, 0x67, 0x19, 0x18, 0x0, 0xf4, 0x30, 0x14, 0x49, 0xef, 0xe6, 0x74, 0x60, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char arrow_right_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x17, 0x3c, 0xf8, 0xf, 0x82, 0xf7, 0x13, 0x70, 0x48, 0x3c, 0xf8, 0xf2, 0x50, 0x1b, 0x43, 0x2, 0xa, 0xaf, 0xbe, 0xe0, 0xc6, 0x2e, 0xf1, 0xff, 0xe1, 0x7c, 0x12, 0x24, 0x10, 0x46, 0x11, 0xb6, 0x1c, 0xe1, 0x5c, 0xa, 0x0, 0x0, 0xe0, 0x14, 0x48, 0xb1, 0x3d, 0x1b, 0x7a, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char bar_arrow_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x14, 0x8, 0x4, 0x0, 0x0, 0x0, 0x2e, 0x6b, 0x75, 0xfc, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc0, 0x0, 0x0, 0xe, 0xc0, 0x1, 0x6a, 0xd6, 0x89, 0x9, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0xfc, 0xcf, 0x80, 0x1f, 0x30, 0x8e, 0x20, 0x5, 0x8c, 0x38, 0x24, 0xff, 0x53, 0x5f, 0xc1, 0xb, 0xee, 0x9f, 0x53, 0x18, 0x18, 0xd8, 0x73, 0x24, 0xbe, 0x62, 0x55, 0x70, 0x5f, 0x83, 0x61, 0x15, 0xa3, 0x2e, 0x3, 0x3, 0xc3, 0xd, 0xe6, 0x30, 0xd9, 0xcb, 0x18, 0xa, 0x1e, 0xc6, 0xfd, 0x9f, 0xc6, 0xc0, 0xd, 0x35, 0xea, 0x3b, 0x63, 0x81, 0xfc, 0x2c, 0x14, 0x5, 0xf, 0x2a, 0x18, 0xda, 0xd1, 0x1c, 0x50, 0xa9, 0xd0, 0x1, 0x57, 0xf0, 0x10, 0x53, 0x9a, 0x81, 0x81, 0x81, 0xa1, 0x52, 0xbe, 0x83, 0x81, 0x81, 0xf1, 0x3f, 0x2e, 0x69, 0xa8, 0x12, 0x3a, 0x4, 0x14, 0x0, 0x7b, 0xda, 0x34, 0x1, 0xbb, 0xb5, 0x3e, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char bookmark_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xed, 0x93, 0x31, 0xa, 0xc0, 0x30, 0xc, 0x3, 0xa5, 0xd0, 0xff, 0x7f, 0x59, 0x1d, 0x8a, 0x42, 0x8, 0x9, 0x95, 0xc9, 0xd2, 0xa1, 0x9a, 0x8c, 0xf1, 0xdd, 0x62, 0x1b, 0x38, 0xc, 0x87, 0x5a, 0x5, 0xae, 0x79, 0xde, 0x2, 0x1, 0x80, 0x94, 0x39, 0x48, 0x76, 0x49, 0x17, 0xa4, 0xf0, 0x24, 0x61, 0x2b, 0x51, 0x8b, 0xfc, 0x82, 0xcf, 0xb, 0x48, 0x7a, 0xdf, 0x75, 0x81, 0xf, 0xe5, 0x29, 0xf7, 0x92, 0x6b, 0x3, 0x1a, 0x1e, 0xda, 0x7c, 0x3d, 0x77, 0x21, 0x7b, 0xa8, 0x74, 0x2e, 0xcb, 0xd, 0xc8, 0x75, 0x13, 0x28, 0x9, 0xed, 0xc2, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char button_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xc7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xd0, 0x81, 0x66, 0x43, 0x31, 0x14, 0x87, 0xf1, 0xf, 0x5, 0x17, 0xb8, 0x28, 0x2e, 0x8, 0x71, 0xf3, 0x6, 0x19, 0xb6, 0xb9, 0xcb, 0xac, 0x95, 0xa4, 0xb7, 0xad, 0x6a, 0xd5, 0x68, 0x5f, 0xe4, 0x3e, 0x76, 0x1e, 0xe1, 0xbf, 0x21, 0xa6, 0xab, 0xf8, 0x1, 0x7c, 0x9c, 0x73, 0xe, 0xac, 0xe8, 0xe8, 0x19, 0x30, 0x58, 0xc6, 0xca, 0x62, 0x18, 0xe8, 0xe9, 0x58, 0x41, 0xc7, 0x1a, 0x87, 0x27, 0x10, 0x49, 0xe4, 0x5f, 0x89, 0x48, 0xc0, 0xe3, 0x58, 0xd3, 0x41, 0x8f, 0xb, 0xcb, 0xbd, 0x7c, 0xeb, 0xbf, 0x7b, 0x9, 0xb, 0x8e, 0x1e, 0x6, 0xfc, 0xad, 0x64, 0x6d, 0xb5, 0x79, 0xb0, 0x55, 0xd6, 0xad, 0xe0, 0x19, 0xc0, 0x10, 0xae, 0xda, 0x34, 0x5c, 0x45, 0xc0, 0x80, 0x25, 0x5e, 0xf4, 0xd5, 0x70, 0x11, 0x11, 0xb, 0x23, 0xe9, 0xac, 0xcf, 0x86, 0xb3, 0x48, 0x8c, 0x30, 0x92, 0x4f, 0xa, 0xd, 0x27, 0x91, 0x6b, 0x70, 0xd4, 0x47, 0xc3, 0xf1, 0x2f, 0x48, 0x7, 0x4d, 0xd, 0x87, 0x3a, 0xc2, 0x12, 0x67, 0xbd, 0x37, 0xcc, 0x75, 0x49, 0x43, 0xd8, 0xe9, 0xad, 0x61, 0x57, 0xcf, 0x1c, 0xf0, 0xfb, 0x32, 0xe9, 0xf5, 0xc9, 0xa4, 0x7d, 0x7d, 0x54, 0x8f, 0x7b, 0x59, 0xe6, 0x92, 0x14, 0x1f, 0x24, 0xcd, 0x3f, 0x7b, 0x6b, 0xa, 0xe, 0x6a, 0x82, 0x91, 0x45, 0x30, 0xba, 0x1, 0x4a, 0x51, 0xc4, 0x35, 0x1f, 0xe5, 0xa1, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char button_focus_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x62, 0xed, 0x5e, 0xfc, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe, 0x39, 0x68, 0x7a, 0x7b, 0x3a, 0x74, 0x10, 0x8, 0x69, 0xf, 0x6, 0x75, 0x11, 0xb8, 0x16, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xc0, 0x2, 0x84, 0x4c, 0x42, 0x43, 0x9d, 0x15, 0x81, 0x8c, 0xf4, 0xce, 0x99, 0x33, 0x67, 0x94, 0x1, 0x19, 0xab, 0x41, 0xc2, 0xbb, 0x80, 0x38, 0x6, 0xc4, 0x38, 0xa, 0xc4, 0x91, 0x20, 0xc6, 0x54, 0x3c, 0xc, 0xb8, 0x62, 0x98, 0x76, 0x29, 0x20, 0xce, 0xdd, 0x39, 0x73, 0xe6, 0x3c, 0x90, 0x81, 0x62, 0x10, 0x2b, 0x30, 0x1, 0x0, 0xec, 0xe0, 0x11, 0x6d, 0xb5, 0xe0, 0x8c, 0x99, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char button_hover_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x8a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x5f, 0x5a, 0x6b, 0x56, 0x53, 0x64, 0x57, 0x53, 0x64, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x57, 0x53, 0x63, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5b, 0x57, 0x66, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x43, 0x40, 0x4c, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0x40, 0xdc, 0xe6, 0x80, 0x0, 0x0, 0x0, 0x16, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0x6b, 0x28, 0x52, 0x7a, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0xcf, 0xc5, 0x1, 0x84, 0x40, 0x14, 0x4, 0xd1, 0x2e, 0x5c, 0xf3, 0xf, 0x92, 0xbd, 0xaf, 0xd3, 0xb8, 0x53, 0x30, 0xf6, 0x6e, 0x1f, 0x31, 0x26, 0xc9, 0x63, 0x90, 0xcc, 0xe2, 0xb1, 0x4f, 0x34, 0x48, 0x8a, 0x86, 0xfc, 0xf6, 0x87, 0x1e, 0x82, 0x8c, 0x19, 0xf2, 0x17, 0x4, 0x50, 0xce, 0x6f, 0x8d, 0xd7, 0x88, 0x7e, 0x69, 0xc9, 0x23, 0x4, 0x1c, 0x40, 0x47, 0x50, 0x24, 0xed, 0x41, 0x3d, 0x78, 0xf, 0x56, 0xe4, 0x23, 0xb8, 0x7, 0x69, 0x11, 0xf7, 0x12, 0x12, 0x7e, 0xea, 0x60, 0xa, 0x9a, 0xef, 0x3f, 0xb0, 0x83, 0x26, 0x98, 0x7b, 0x70, 0x33, 0xdc, 0x65, 0xfc, 0xe, 0x81, 0x4e, 0x3b, 0x55, 0xea, 0xaa, 0xb0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char button_normal_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x87, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x56, 0x52, 0x60, 0x47, 0x44, 0x52, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x47, 0x44, 0x51, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x40, 0x3e, 0x48, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x3f, 0x38, 0xaa, 0x5e, 0x0, 0x0, 0x0, 0x15, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0xe8, 0xff, 0x76, 0xed, 0x0, 0x0, 0x0, 0x63, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0xcf, 0x45, 0x2, 0x84, 0x30, 0x0, 0x4, 0xc1, 0x69, 0x1c, 0xfe, 0xff, 0xca, 0xd5, 0xeb, 0x2a, 0x83, 0x6b, 0xd2, 0xb7, 0x54, 0x1c, 0x31, 0x26, 0xc9, 0x63, 0x50, 0xcc, 0x32, 0x8d, 0x3f, 0xd9, 0x20, 0x5, 0x1a, 0xf2, 0xc7, 0x1f, 0x7a, 0x48, 0x4a, 0x66, 0xa8, 0xde, 0xc, 0xd0, 0x38, 0xd1, 0x54, 0xdb, 0x4c, 0x2b, 0xd0, 0x5c, 0x62, 0x8e, 0xa0, 0x1, 0x74, 0x4, 0x65, 0xd2, 0x1e, 0xd4, 0x83, 0xf7, 0x60, 0x65, 0x3e, 0x82, 0x3, 0x48, 0x49, 0xdf, 0x55, 0xca, 0xd4, 0xef, 0xfa, 0xfb, 0x27, 0x36, 0xf, 0x92, 0x31, 0x9e, 0x44, 0x3e, 0x17, 0x7c, 0xbf, 0x3, 0xef, 0x34, 0x3f, 0x3e, 0xe0, 0x24, 0x67, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char button_pressed_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x7, 0x1c, 0xc, 0x14, 0x2b, 0xf9, 0x77, 0x52, 0x64, 0x0, 0x0, 0x1, 0x92, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, 0x8d, 0x93, 0x4d, 0x4e, 0x1b, 0x41, 0x10, 0x85, 0xbf, 0xea, 0xaa, 0x1e, 0x20, 0x83, 0xf9, 0x9, 0x36, 0xb2, 0x85, 0x72, 0x84, 0x8, 0x65, 0x11, 0xe5, 0xc, 0x9c, 0x80, 0x73, 0x10, 0xee, 0xc1, 0x41, 0x38, 0x1, 0xab, 0xec, 0xa3, 0x2c, 0x2, 0x8a, 0xe0, 0x2, 0x51, 0xc2, 0x48, 0x46, 0x42, 0x64, 0xc6, 0x91, 0x21, 0xf4, 0x74, 0x16, 0x2e, 0xb0, 0x71, 0x2, 0xf2, 0x93, 0x9e, 0xba, 0x16, 0xdd, 0xaf, 0x5e, 0xa9, 0x5f, 0x9, 0x60, 0x40, 0x1, 0x2c, 0x1, 0xcb, 0x5e, 0x2b, 0x10, 0x78, 0x8a, 0x16, 0x48, 0xc0, 0x1d, 0x30, 0x6, 0x6e, 0x81, 0x3b, 0xf3, 0x87, 0x25, 0xb0, 0x9, 0xac, 0x7b, 0xbd, 0xc, 0x88, 0x13, 0x20, 0x3b, 0xc7, 0xc0, 0x8, 0xb8, 0x1, 0xae, 0x81, 0x91, 0xf9, 0xe5, 0xad, 0x77, 0xbb, 0x1f, 0xf6, 0x7b, 0xdd, 0xfe, 0x41, 0x4a, 0x69, 0x2d, 0x93, 0xf9, 0x1f, 0x4, 0x41, 0x55, 0x7f, 0xd, 0xaf, 0xaa, 0xa3, 0xaf, 0x67, 0x9f, 0x8f, 0x81, 0x64, 0xde, 0xb1, 0xbb, 0xdd, 0x1b, 0x7c, 0xac, 0x9b, 0x9b, 0xce, 0xd9, 0xb7, 0x2f, 0xfc, 0xf3, 0x5e, 0xa6, 0xe5, 0xee, 0xdb, 0xf7, 0x6b, 0xdb, 0xbd, 0xc1, 0x21, 0xf0, 0x9, 0x18, 0x5, 0x60, 0x5, 0xd8, 0x48, 0x6d, 0xdb, 0x39, 0xbf, 0x38, 0xc5, 0x34, 0x62, 0x36, 0x47, 0x9d, 0xf2, 0xfc, 0xe2, 0x94, 0xd4, 0xb6, 0x1d, 0x60, 0x3, 0x58, 0x31, 0x20, 0x2, 0x65, 0x40, 0x88, 0x31, 0x92, 0xbd, 0xbb, 0x8, 0xe4, 0x3c, 0x39, 0x67, 0x91, 0x33, 0x84, 0x89, 0xa5, 0x12, 0x88, 0xf, 0x3f, 0x10, 0x45, 0x5, 0xb3, 0xc8, 0x73, 0x10, 0x11, 0xb2, 0xab, 0x8b, 0xa, 0xde, 0xb8, 0x30, 0x9f, 0xb0, 0x8, 0xa2, 0xc4, 0x58, 0xb0, 0x8, 0x82, 0x28, 0xde, 0x58, 0xcc, 0xff, 0x5c, 0x45, 0x64, 0x61, 0x1, 0x99, 0xcc, 0xa5, 0x80, 0x4e, 0x5, 0xc2, 0xcb, 0x2, 0xe2, 0x41, 0x10, 0x40, 0xc2, 0x53, 0x81, 0xc, 0xa8, 0x8a, 0x52, 0x2c, 0xe8, 0x40, 0x27, 0x23, 0x28, 0x90, 0xcd, 0xe3, 0x79, 0x1b, 0x34, 0x50, 0x14, 0x4b, 0xf0, 0x4c, 0x88, 0x66, 0xbd, 0x4, 0xd, 0x78, 0x94, 0x93, 0x79, 0x3c, 0x9b, 0x18, 0x63, 0x7a, 0xbd, 0xb9, 0xa5, 0xcd, 0xa8, 0x7e, 0xb4, 0x3a, 0x2f, 0x15, 0x10, 0xca, 0x72, 0x95, 0x18, 0x8b, 0x4, 0x34, 0xc0, 0xd8, 0x80, 0x1a, 0x18, 0x56, 0xd5, 0xcf, 0x93, 0x41, 0x7f, 0x67, 0xaf, 0xb3, 0xba, 0x1e, 0x5e, 0x8a, 0xb2, 0x99, 0xb5, 0x97, 0xd5, 0x8f, 0x13, 0x60, 0x8, 0xd4, 0x2, 0x74, 0x9d, 0x6f, 0x80, 0xbe, 0x2f, 0x55, 0xe9, 0x26, 0xc2, 0xcc, 0x26, 0x66, 0x5f, 0xa4, 0x6b, 0xa0, 0x2, 0xbe, 0x3, 0x57, 0xe6, 0x56, 0x1e, 0x66, 0x1a, 0x2, 0xaf, 0x3c, 0x24, 0x36, 0x67, 0xe0, 0x1e, 0xf8, 0x3, 0xfc, 0xf6, 0x6d, 0xac, 0x81, 0xe6, 0x2f, 0x7c, 0x22, 0x6d, 0x74, 0x25, 0xb, 0xb3, 0xa2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char checked_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x8d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x58, 0x56, 0x63, 0xb0, 0xaf, 0xb5, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0xb6, 0xb6, 0xb9, 0x57, 0x57, 0x5a, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x8b, 0x8b, 0x8d, 0xff, 0xff, 0xff, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x42, 0x42, 0x47, 0xf8, 0xf8, 0xf8, 0xfe, 0xfe, 0xfe, 0x25, 0x25, 0x2a, 0x4e, 0x4e, 0x52, 0x26, 0x26, 0x2b, 0xc5, 0xc5, 0xc7, 0xaa, 0xaa, 0xab, 0xb8, 0xb8, 0xba, 0x5f, 0x5f, 0x63, 0x74, 0x74, 0x77, 0xed, 0xed, 0xed, 0x33, 0x33, 0x38, 0x8d, 0x8d, 0x8f, 0xb8, 0xb8, 0xb9, 0x35, 0x35, 0x39, 0x3a, 0x3a, 0x3e, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xb2, 0xb2, 0xb4, 0x45, 0x45, 0x49, 0x61, 0x61, 0x65, 0x8f, 0x8f, 0x92, 0x63, 0x63, 0x66, 0x2a, 0x2a, 0x2f, 0x40, 0x82, 0xb, 0xf6, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xfa, 0xdd, 0xfb, 0xfb, 0xb4, 0xfa, 0xb8, 0xf0, 0x7f, 0x59, 0x0, 0x0, 0x0, 0x7e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5d, 0xca, 0x5, 0xb2, 0x2, 0x30, 0x18, 0x3, 0xe1, 0x4d, 0xed, 0xb9, 0x60, 0xf7, 0x3f, 0x20, 0xee, 0x4e, 0x99, 0xe0, 0xb0, 0x63, 0xfd, 0xbf, 0x86, 0xd7, 0x12, 0x72, 0x38, 0x69, 0x5b, 0x6b, 0x42, 0x45, 0xe5, 0xa, 0xab, 0x95, 0x41, 0x9f, 0x32, 0x20, 0x69, 0x2d, 0xbc, 0x50, 0x46, 0x3a, 0x10, 0x17, 0x5f, 0x49, 0x4, 0x7f, 0x90, 0x57, 0x89, 0xb7, 0xc5, 0x5f, 0x96, 0x17, 0x2e, 0x93, 0xcb, 0x8e, 0x3a, 0x83, 0xb, 0xc4, 0x8e, 0xd4, 0xff, 0x5c, 0x73, 0x83, 0x69, 0x9e, 0x95, 0xfc, 0x3b, 0xf4, 0x33, 0xe0, 0xf8, 0x61, 0xd3, 0xf1, 0x7d, 0x5d, 0x30, 0x7a, 0x6f, 0x89, 0xb, 0xd4, 0x5a, 0xe1, 0x40, 0xf, 0xfc, 0x34, 0x6c, 0xd2, 0x56, 0x80, 0xef, 0xfd, 0x9, 0xd2, 0x3a, 0x5e, 0x41, 0x15, 0x21, 0x77, 0x6, 0xc7, 0x6b, 0x47, 0x4e, 0x3a, 0x2f, 0x53, 0xb4, 0x10, 0xc7, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char checked_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x73, 0x72, 0x7b, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x82, 0x80, 0x8a, 0x90, 0x90, 0x93, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x93, 0x93, 0x95, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x7d, 0x7d, 0x7f, 0x58, 0x58, 0x5b, 0xa4, 0xa4, 0xa4, 0x9e, 0x9e, 0xa0, 0x9e, 0x9e, 0x9e, 0x9b, 0x9b, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x93, 0x93, 0x94, 0x8f, 0x8f, 0x8f, 0x86, 0x86, 0x88, 0x85, 0x85, 0x86, 0x82, 0x82, 0x83, 0x81, 0x81, 0x83, 0x7f, 0x7f, 0x81, 0x7c, 0x7c, 0x7e, 0x7a, 0x7a, 0x7d, 0x78, 0x78, 0x7b, 0x71, 0x71, 0x74, 0x68, 0x68, 0x6c, 0x66, 0x66, 0x6a, 0x65, 0x65, 0x68, 0x63, 0x63, 0x66, 0x5f, 0x5f, 0x63, 0x5c, 0x5c, 0x60, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x59, 0x59, 0x5c, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x10, 0x13, 0xbb, 0xf, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xb4, 0xdd, 0xfa, 0xfa, 0xfb, 0xfb, 0x5b, 0xd1, 0xf1, 0xe6, 0x0, 0x0, 0x0, 0x96, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x5d, 0x8f, 0xc9, 0x12, 0x82, 0x30, 0x14, 0x4, 0x87, 0x18, 0x50, 0x51, 0x44, 0x25, 0x42, 0x4, 0x77, 0xc4, 0x8d, 0x97, 0x0, 0xf9, 0xff, 0x8f, 0xb3, 0x88, 0xa4, 0x4a, 0xed, 0x63, 0x5f, 0xa6, 0x7, 0xf8, 0x7, 0x1e, 0xe3, 0x7e, 0x60, 0x19, 0x4f, 0x46, 0x1e, 0x0, 0x36, 0x8d, 0x4c, 0x67, 0x59, 0x65, 0x33, 0x6, 0x80, 0x47, 0xad, 0x56, 0x3d, 0xb7, 0x3c, 0x5d, 0x70, 0x0, 0xbe, 0xd1, 0x44, 0x65, 0x4d, 0x94, 0xc8, 0xc2, 0xf8, 0x0, 0x82, 0x4e, 0x91, 0x94, 0x15, 0x5d, 0xd2, 0xec, 0xde, 0x5, 0x83, 0x38, 0xc8, 0xe3, 0x63, 0x23, 0xce, 0xca, 0x9, 0x7a, 0x6e, 0xf3, 0x93, 0x48, 0x1a, 0x27, 0x14, 0x35, 0x3b, 0xb9, 0x5e, 0x56, 0xe4, 0x84, 0x22, 0xba, 0xa, 0x51, 0xd0, 0xb7, 0xa8, 0xcb, 0xfd, 0xcb, 0x9, 0x3b, 0xfb, 0x41, 0xdb, 0x59, 0x17, 0xa6, 0x94, 0x6e, 0xe3, 0x3e, 0x8c, 0x85, 0xf1, 0x90, 0x6e, 0xe6, 0x21, 0xfb, 0x39, 0xe7, 0x73, 0xe6, 0xfd, 0x5f, 0x7, 0xde, 0xc3, 0xb5, 0x16, 0x87, 0xb0, 0x9e, 0x42, 0x46, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char checker_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0xe1, 0x64, 0xe1, 0x57, 0x0, 0x0, 0x0, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0xc0, 0xd0, 0x0, 0xc4, 0xf8, 0x18, 0xf5, 0x84, 0x19, 0x0, 0x9f, 0x5f, 0xa, 0x1, 0xf8, 0xef, 0x65, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char close_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char close_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char color_picker_hue_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, 0x0, 0x8, 0x2, 0x0, 0x0, 0x0, 0xfd, 0x5c, 0x8b, 0xcf, 0x0, 0x0, 0x0, 0x54, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xcd, 0xc9, 0x1, 0x6, 0x43, 0x31, 0x10, 0x45, 0xd1, 0x63, 0xc8, 0xcf, 0xfe, 0x97, 0xdb, 0x9f, 0xf0, 0x1a, 0x15, 0xb4, 0x0, 0xa5, 0x1a, 0x1c, 0xd7, 0xcc, 0x13, 0x4a, 0x5b, 0xae, 0x2f, 0xf5, 0xdd, 0xdf, 0x92, 0xc9, 0x88, 0xba, 0x1d, 0x4d, 0xb, 0xa2, 0xb8, 0x96, 0xb6, 0xf4, 0x93, 0xcb, 0x48, 0xb9, 0x9d, 0x8c, 0x16, 0xa4, 0x2e, 0x5e, 0xda, 0x6e, 0xe7, 0xe3, 0xd7, 0xff, 0xbf, 0x9b, 0x22, 0x66, 0x71, 0x2f, 0x8f, 0xdd, 0xc5, 0x78, 0xbb, 0xc7, 0x9, 0xbb, 0xf0, 0x4, 0x75, 0x7b, 0x8a, 0xe5, 0x7c, 0x23, 0x8a, 0xd3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char color_picker_sample_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x47, 0x29, 0xbc, 0x83, 0x0, 0x0, 0x0, 0x3c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xd5, 0x21, 0x11, 0x0, 0x30, 0xc, 0x4, 0xc1, 0xfa, 0x57, 0x53, 0x87, 0xed, 0x4, 0x45, 0xc4, 0xed, 0xa3, 0xc3, 0x4b, 0xfe, 0xbc, 0xd9, 0x9d, 0x35, 0x2b, 0xe, 0x0, 0x0, 0x0, 0x80, 0xed, 0x66, 0xc5, 0x1, 0x0, 0x0, 0x0, 0xe0, 0x6, 0x1, 0x0, 0x0, 0x90, 0x6, 0x70, 0x83, 0x0, 0x0, 0x0, 0x28, 0x3, 0x7c, 0x54, 0x93, 0xd6, 0xf1, 0xd1, 0x16, 0x8a, 0x17, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char dialog_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x26, 0x8, 0x3, 0x0, 0x0, 0x0, 0xf7, 0x10, 0x9b, 0xa4, 0x0, 0x0, 0x2, 0xf5, 0x7a, 0x54, 0x58, 0x74, 0x52, 0x61, 0x77, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x78, 0x69, 0x66, 0x0, 0x0, 0x78, 0xda, 0xb5, 0x95, 0x6d, 0x72, 0xe4, 0x28, 0xc, 0x86, 0xff, 0x73, 0x8a, 0x3d, 0x2, 0xfa, 0x42, 0x70, 0x1c, 0xc, 0xa6, 0x6a, 0x6f, 0xb0, 0xc7, 0x9f, 0x17, 0xec, 0x38, 0xd9, 0x4c, 0x57, 0xda, 0x49, 0xcf, 0x40, 0xb5, 0xa5, 0x92, 0x5, 0x12, 0x3c, 0x92, 0x3b, 0xec, 0xff, 0xfd, 0x3b, 0xc2, 0x3f, 0x18, 0xa4, 0x39, 0x6, 0x35, 0xcf, 0xa9, 0xa4, 0x14, 0x31, 0xb4, 0x68, 0xe1, 0xa, 0x25, 0xc7, 0xf7, 0xb1, 0x9f, 0xf2, 0xb0, 0x51, 0xd4, 0xf5, 0x5c, 0xa3, 0xe8, 0xf9, 0x8e, 0x22, 0x85, 0x8f, 0x2f, 0x2e, 0x8d, 0x21, 0x5, 0x52, 0xe, 0xa3, 0xbf, 0x2d, 0x90, 0xd3, 0xfe, 0xe6, 0x9f, 0x2e, 0x89, 0x8d, 0x1e, 0xbc, 0x20, 0xfb, 0xb4, 0x40, 0xae, 0x30, 0xfc, 0x31, 0xb0, 0xd7, 0xd3, 0xce, 0x91, 0xff, 0x97, 0x91, 0x7a, 0xb4, 0xf8, 0x71, 0xe4, 0xf7, 0xdf, 0x18, 0x3d, 0x8f, 0xb1, 0x1f, 0xa7, 0xab, 0x9a, 0x70, 0xd, 0xe9, 0x38, 0xd4, 0x11, 0x22, 0xbc, 0x6d, 0x3, 0xc7, 0xd, 0x5b, 0xc9, 0x5a, 0x96, 0x30, 0x1d, 0x3f, 0x83, 0xee, 0x6b, 0x16, 0xcc, 0x1c, 0x6b, 0x6c, 0xa4, 0xb1, 0xc7, 0x16, 0x37, 0xcc, 0x46, 0x85, 0x98, 0x24, 0xe, 0x52, 0xea, 0x81, 0x2a, 0xd, 0xda, 0xa9, 0x43, 0x36, 0x6a, 0xc8, 0x51, 0x79, 0x67, 0x87, 0x64, 0x6e, 0x2c, 0xcb, 0x96, 0xc5, 0xb9, 0x70, 0x93, 0x28, 0x24, 0x3a, 0x27, 0xd, 0x76, 0x29, 0xd2, 0x25, 0xb, 0x4b, 0xe3, 0x5d, 0x44, 0x34, 0x8, 0x5f, 0xb9, 0xd0, 0x8a, 0x5b, 0x56, 0xbc, 0x46, 0x19, 0x91, 0x3b, 0xc1, 0x95, 0x9, 0x9b, 0x11, 0x96, 0x7c, 0x39, 0xc3, 0x33, 0x87, 0x3b, 0x73, 0x8c, 0x16, 0x71, 0x47, 0x44, 0x38, 0x3d, 0x9d, 0x75, 0x81, 0xbc, 0x98, 0x27, 0x7, 0x9a, 0xd7, 0x28, 0xf3, 0x9, 0x37, 0x0, 0xa1, 0x71, 0x72, 0xb3, 0x75, 0xc1, 0x6f, 0xf3, 0x1a, 0xe1, 0x3, 0x58, 0x1, 0x41, 0x5b, 0xd7, 0x9c, 0x71, 0xc0, 0x1a, 0xb7, 0x63, 0x8b, 0xcd, 0xe8, 0xbd, 0xb6, 0x64, 0x15, 0x80, 0xc0, 0xcf, 0x20, 0x8f, 0xfa, 0x22, 0xef, 0x93, 0x1a, 0xaf, 0x2a, 0x51, 0xc4, 0x36, 0x24, 0x43, 0x2, 0x4, 0x31, 0x91, 0x18, 0x25, 0x8a, 0xce, 0xec, 0x44, 0x2a, 0x9c, 0x1, 0xa8, 0x22, 0x73, 0x16, 0xe5, 0xd, 0x4, 0xc8, 0x8c, 0x3b, 0x92, 0x64, 0x15, 0x49, 0x60, 0x93, 0x51, 0x47, 0x88, 0x8d, 0x35, 0x4e, 0xcb, 0x97, 0x8d, 0xf, 0x3b, 0x5a, 0x5, 0x7c, 0x4c, 0x92, 0x38, 0xd8, 0x14, 0xa9, 0x80, 0xa5, 0x6a, 0xa8, 0x1f, 0xd7, 0x8c, 0x1a, 0xaa, 0x26, 0xa6, 0x66, 0x96, 0xcc, 0x2d, 0x5b, 0xb1, 0x1a, 0x92, 0x24, 0x4d, 0x96, 0x52, 0xf2, 0x34, 0x7b, 0xae, 0xba, 0xb8, 0xba, 0x79, 0x72, 0xf7, 0xec, 0xc5, 0x6b, 0x96, 0xac, 0xd9, 0x72, 0xca, 0x9e, 0x73, 0x2e, 0xb9, 0x16, 0x2e, 0x82, 0x96, 0xb4, 0x92, 0x8a, 0x97, 0x5c, 0x4a, 0xa9, 0x15, 0x31, 0xab, 0x86, 0x6a, 0x15, 0xab, 0x2b, 0x3c, 0x6a, 0xdd, 0x78, 0x93, 0x4d, 0x37, 0xdb, 0xd2, 0xe6, 0x5b, 0xde, 0xca, 0x56, 0x1b, 0xca, 0xa7, 0x69, 0xb3, 0x96, 0x9a, 0xb7, 0xdc, 0x4a, 0xab, 0x9d, 0xbb, 0x74, 0xed, 0xd6, 0x53, 0xf7, 0x9e, 0x7b, 0xe9, 0x75, 0xa7, 0x1d, 0xa5, 0x14, 0x76, 0xdd, 0x6d, 0x4f, 0xbb, 0xef, 0x79, 0x2f, 0x7b, 0x1d, 0xa8, 0xb5, 0x21, 0x43, 0x87, 0x8d, 0x34, 0x7c, 0xe4, 0x51, 0x46, 0xbd, 0xa8, 0xd1, 0xd9, 0xb6, 0x9f, 0xe7, 0x37, 0xa8, 0xd1, 0x49, 0x8d, 0x17, 0xa9, 0xe9, 0xe7, 0x17, 0x35, 0x58, 0xdd, 0xe7, 0x46, 0x6b, 0xb, 0x9a, 0xdf, 0x19, 0x9b, 0xcc, 0x40, 0x8c, 0x95, 0x40, 0xdc, 0x27, 0x1, 0x14, 0x34, 0x4f, 0x66, 0x31, 0x93, 0x2a, 0x4f, 0x72, 0x93, 0x59, 0x2c, 0x8c, 0xae, 0x30, 0x46, 0x92, 0x36, 0xd9, 0x74, 0x8a, 0x35, 0x50, 0x2, 0x42, 0xdd, 0x89, 0x6d, 0xd0, 0xc5, 0xee, 0x9d, 0xdc, 0x6d, 0x6e, 0x1, 0x77, 0xfd, 0x8c, 0x1b, 0xdf, 0x21, 0x17, 0x26, 0xba, 0x3f, 0x40, 0x8e, 0xc3, 0x2e, 0x9f, 0xb8, 0x3d, 0xa0, 0xd6, 0xe7, 0x97, 0xb0, 0x2d, 0x62, 0x47, 0x17, 0xce, 0x3b, 0x8d, 0x82, 0xee, 0x83, 0x6f, 0xe5, 0x5c, 0x79, 0x53, 0x50, 0x5b, 0x5a, 0x7e, 0x55, 0xfe, 0x85, 0x8d, 0x68, 0x9, 0x1c, 0xe2, 0x67, 0x32, 0x3c, 0x7c, 0xa1, 0x6b, 0xe7, 0xf4, 0x1d, 0x25, 0xdc, 0x70, 0x42, 0xce, 0x65, 0x85, 0xb0, 0x2f, 0x94, 0x70, 0xc7, 0xe9, 0x37, 0xe5, 0x41, 0xd2, 0xe1, 0x47, 0xe7, 0x78, 0x90, 0x74, 0x40, 0x91, 0x8a, 0xa6, 0x74, 0x1a, 0x5b, 0xa7, 0x72, 0x86, 0xfe, 0xa6, 0x39, 0xdc, 0x72, 0x47, 0x97, 0x1b, 0xa1, 0xbf, 0xf0, 0x3f, 0x8c, 0xe, 0x7d, 0xa8, 0x84, 0x2f, 0xde, 0x7d, 0xad, 0x90, 0x1a, 0xdb, 0xc, 0xb1, 0x2d, 0x25, 0x5c, 0xda, 0x8b, 0x4a, 0x38, 0xb3, 0x2e, 0x15, 0xa7, 0x39, 0x8c, 0x38, 0x53, 0xeb, 0x67, 0xe8, 0xfb, 0xe6, 0x70, 0xdb, 0xfd, 0x49, 0xa9, 0x86, 0x17, 0xa9, 0x5f, 0x5c, 0xc2, 0x8b, 0xd4, 0x2f, 0x73, 0x78, 0x4e, 0xdd, 0x6e, 0xc9, 0x70, 0xd7, 0xf1, 0x59, 0x3d, 0x84, 0x17, 0xca, 0x88, 0x3e, 0xca, 0xf0, 0xd9, 0xf0, 0x53, 0xf9, 0xe7, 0x36, 0xf2, 0x81, 0x8f, 0x7b, 0x81, 0xf2, 0xb, 0x12, 0xea, 0xac, 0x15, 0x79, 0x70, 0x44, 0x63, 0x0, 0x0, 0x1, 0x68, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xe8, 0xe5, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x22, 0x0, 0x0, 0x0, 0x1a, 0x19, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x21, 0x17, 0x16, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1b, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1e, 0x1c, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x0, 0x0, 0x0, 0x20, 0x20, 0x25, 0x20, 0x1d, 0x25, 0x20, 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1d, 0x1d, 0x20, 0x1d, 0x1a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x30, 0x38, 0xe8, 0xe5, 0xf1, 0xe5, 0xe2, 0xeb, 0xe3, 0xe1, 0xe8, 0xe1, 0xdf, 0xe7, 0xe0, 0xde, 0xe6, 0xdf, 0xdd, 0xe5, 0xde, 0xdc, 0xe4, 0xdd, 0xdb, 0xe3, 0xdc, 0xda, 0xe2, 0xda, 0xd8, 0xe0, 0xd9, 0xd7, 0xdf, 0xd7, 0xd6, 0xdf, 0xd6, 0xd4, 0xdd, 0xd5, 0xd3, 0xdc, 0xd4, 0xd1, 0xdb, 0xd3, 0xd0, 0xda, 0xd1, 0xce, 0xd8, 0xd0, 0xcd, 0xd7, 0xcf, 0xcd, 0xd7, 0xe2, 0xdf, 0xeb, 0x48, 0x46, 0x51, 0x42, 0x40, 0x4b, 0x40, 0x3e, 0x48, 0x40, 0x3d, 0x48, 0x48, 0x45, 0x50, 0x42, 0x3f, 0x4a, 0x3f, 0x3d, 0x48, 0x47, 0x44, 0x50, 0x41, 0x3f, 0x4a, 0x3f, 0x3d, 0x47, 0x41, 0x3e, 0x49, 0x3f, 0x3c, 0x47, 0x46, 0x43, 0x4f, 0x3e, 0x3c, 0x46, 0x40, 0x3e, 0x49, 0x3d, 0x3b, 0x46, 0x45, 0x43, 0x4e, 0x3d, 0x3b, 0x45, 0x44, 0x42, 0x4d, 0x3d, 0x3a, 0x45, 0x3e, 0x3c, 0x47, 0x3c, 0x3a, 0x44, 0x43, 0x42, 0x4c, 0x43, 0x40, 0x4c, 0x3e, 0x3b, 0x46, 0x3b, 0x39, 0x43, 0x43, 0x3f, 0x4c, 0x43, 0x3f, 0x4b, 0x3a, 0x38, 0x42, 0x42, 0x3e, 0x4b, 0x42, 0x3e, 0x49, 0x3a, 0x37, 0x41, 0x39, 0x37, 0x41, 0x3f, 0x3e, 0x48, 0x39, 0x37, 0x40, 0x38, 0x36, 0x40, 0x3e, 0x3d, 0x48, 0x38, 0x36, 0x3f, 0x3e, 0x3d, 0x47, 0x3a, 0x38, 0x41, 0x38, 0x35, 0x3f, 0x37, 0x35, 0x3e, 0x39, 0x36, 0x40, 0x37, 0x34, 0x3e, 0x3d, 0x3a, 0x46, 0x36, 0x34, 0x3d, 0x3d, 0x3a, 0x44, 0x37, 0x35, 0x3f, 0x35, 0x33, 0x3c, 0x6b, 0xff, 0x8f, 0xb1, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x88, 0x5, 0x1d, 0x48, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe5, 0x7, 0x1b, 0x17, 0x11, 0x18, 0xe6, 0xb9, 0x22, 0xac, 0x0, 0x0, 0x0, 0x3f, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0xf4, 0x64, 0xc0, 0x6, 0x98, 0x18, 0x68, 0x27, 0xcc, 0xc2, 0x88, 0x5d, 0x98, 0x99, 0x24, 0x61, 0x26, 0x52, 0x84, 0x71, 0xb9, 0x84, 0x89, 0x76, 0xc2, 0xb8, 0xac, 0x64, 0x21, 0x49, 0x35, 0x33, 0x15, 0x54, 0xe3, 0xa, 0x6f, 0x26, 0x6a, 0xa8, 0x26, 0xc9, 0x4a, 0xaa, 0x44, 0x3, 0x89, 0x89, 0x8d, 0x99, 0xee, 0xe9, 0x1b, 0x87, 0x30, 0x0, 0x20, 0x4, 0x0, 0xed, 0x48, 0xa7, 0x26, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char dropdown_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x60, 0xf8, 0xc0, 0xcc, 0x0, 0x2, 0x60, 0x16, 0x98, 0x78, 0x67, 0x8, 0x81, 0x6f, 0x4d, 0xde, 0x9a, 0x0, 0x5, 0xde, 0x3a, 0x3d, 0xfc, 0x8f, 0x80, 0xaf, 0xba, 0x18, 0xde, 0x29, 0x2, 0x19, 0xbf, 0x61, 0x2, 0x6f, 0x62, 0x18, 0x3e, 0xb0, 0xbd, 0x97, 0x4, 0x32, 0xff, 0x80, 0xb9, 0xb1, 0x20, 0x93, 0xc0, 0x42, 0x8, 0x2e, 0x54, 0xe8, 0x9d, 0xdc, 0x9b, 0x54, 0x10, 0xb, 0x21, 0xc4, 0x4, 0x63, 0x1, 0x0, 0x86, 0x1f, 0x3b, 0x1e, 0x92, 0x22, 0x3f, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char ellipsis_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x8, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc9, 0x11, 0xce, 0xcc, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x78, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x95, 0xd1, 0x31, 0xa, 0xc2, 0x50, 0x10, 0x4, 0xd0, 0xb7, 0x1f, 0xf, 0x60, 0x67, 0xa, 0xf, 0x12, 0x6f, 0x60, 0xe9, 0x51, 0x5, 0x3d, 0x44, 0xee, 0x61, 0xa1, 0xa5, 0xf6, 0x81, 0xb5, 0xc8, 0x47, 0x82, 0x84, 0x4f, 0xb2, 0xdd, 0xb0, 0x33, 0xb3, 0xb3, 0x4c, 0x40, 0x66, 0xee, 0x71, 0xc2, 0x1, 0x3b, 0xcb, 0x33, 0xe2, 0x85, 0x21, 0x22, 0xde, 0x51, 0x45, 0x97, 0x86, 0x60, 0xc9, 0xe0, 0x5a, 0xea, 0xa5, 0xb5, 0x22, 0x95, 0xdb, 0x97, 0x1a, 0xf, 0x6e, 0xb8, 0xcf, 0x8, 0x2d, 0xdc, 0x95, 0xd9, 0x22, 0xfe, 0x9c, 0x9b, 0x38, 0x32, 0xf3, 0x8c, 0xe3, 0x86, 0xa8, 0xf0, 0x28, 0x18, 0x4c, 0xf, 0xaf, 0x9d, 0x11, 0x43, 0xf0, 0xab, 0xa3, 0x47, 0xa7, 0x5d, 0xc7, 0xd3, 0x54, 0xc7, 0xe7, 0xb, 0xb9, 0xce, 0x1f, 0xc6, 0x2d, 0x99, 0x55, 0xc7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char error_icon_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0xe, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x5, 0xa3, 0x0, 0x1, 0x0, 0x2, 0x10, 0x0, 0x1, 0x14, 0xc2, 0xc0, 0x92, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char focus_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x4, 0x3, 0x0, 0x0, 0x0, 0xa4, 0x5b, 0x41, 0xd4, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0xff, 0xff, 0xff, 0xb9, 0xa2, 0x9b, 0xc9, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe, 0x39, 0x68, 0x7a, 0x7b, 0x3a, 0x74, 0x10, 0x8, 0x69, 0xf, 0x6, 0x75, 0x11, 0xb8, 0x16, 0x0, 0x1, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x10, 0x32, 0x9, 0xd, 0x75, 0x56, 0x64, 0x48, 0xef, 0x9c, 0x39, 0x73, 0x46, 0x19, 0xc3, 0x6a, 0x6, 0x20, 0xd8, 0xc5, 0x10, 0x3, 0xa2, 0x8e, 0x32, 0x44, 0x82, 0xa8, 0xa9, 0xd8, 0x29, 0xa8, 0x12, 0xb0, 0x6, 0x29, 0x86, 0xdc, 0x9d, 0x33, 0x67, 0xce, 0x2b, 0x63, 0x10, 0x3, 0x1b, 0x6, 0x0, 0xdf, 0xc6, 0x11, 0x6d, 0xb8, 0xf4, 0x9c, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x6, 0x0, 0x0, 0x0, 0x13, 0x7d, 0xf7, 0x96, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x8, 0x17, 0xd, 0x5, 0x12, 0xa1, 0x38, 0x83, 0x9b, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x2, 0x74, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x97, 0x3d, 0x6f, 0xd3, 0x60, 0x10, 0xc7, 0x7f, 0x17, 0x9b, 0x26, 0x25, 0x22, 0xad, 0xa, 0x8, 0xf1, 0x52, 0x75, 0x0, 0x16, 0x24, 0x90, 0x2a, 0x96, 0x7c, 0x1, 0x6, 0xc4, 0xce, 0xc4, 0x17, 0x0, 0x31, 0xb0, 0x30, 0x0, 0x23, 0x82, 0x85, 0x5, 0x9, 0x4, 0x5f, 0x80, 0x89, 0x1d, 0x31, 0xf0, 0x5, 0x58, 0x50, 0x25, 0x50, 0x59, 0x80, 0xa1, 0xe2, 0xad, 0x28, 0x34, 0x4a, 0x3, 0x25, 0x76, 0xea, 0xe7, 0x39, 0x6, 0x3f, 0x76, 0x6d, 0xc7, 0x49, 0x5f, 0xd8, 0x90, 0x6f, 0xb1, 0xf5, 0xe4, 0xb9, 0xdf, 0xdd, 0xfd, 0xef, 0x22, 0xf9, 0x84, 0x2d, 0x13, 0xa0, 0x6, 0x78, 0xee, 0x29, 0xe4, 0x4d, 0x1, 0xb, 0x18, 0xf7, 0x54, 0x32, 0x97, 0x6a, 0xc0, 0x7e, 0x60, 0xe, 0x38, 0xc, 0xb4, 0x80, 0x7d, 0x5, 0xc0, 0x26, 0xd0, 0x7, 0x3a, 0x40, 0x17, 0xf8, 0x3, 0xd8, 0x24, 0x6a, 0x13, 0x38, 0x35, 0x73, 0x60, 0xf6, 0x6a, 0xa3, 0xde, 0xb8, 0x38, 0x35, 0x55, 0x3f, 0x41, 0x89, 0xd, 0x87, 0xe1, 0x97, 0x20, 0xc, 0x5e, 0xae, 0xff, 0xea, 0x3d, 0x5, 0x3e, 0x2, 0x1b, 0xe2, 0x22, 0x2d, 0xcc, 0xcd, 0x1e, 0x7c, 0x78, 0x72, 0xe1, 0xf4, 0xa5, 0x7b, 0xb7, 0x1f, 0x7c, 0x9e, 0x6e, 0x35, 0xca, 0xfc, 0x19, 0xf4, 0x3, 0xee, 0xdc, 0xbf, 0x39, 0xff, 0x69, 0xe5, 0xc3, 0x8b, 0x6e, 0x6f, 0xed, 0x6, 0xb0, 0x22, 0x40, 0x3, 0x58, 0x3c, 0x7a, 0xe4, 0xf8, 0xab, 0x67, 0x4f, 0x9e, 0x77, 0xa3, 0xc8, 0x12, 0xd, 0xa3, 0x52, 0x80, 0x3f, 0xe5, 0xe3, 0xfb, 0x35, 0xae, 0x5c, 0xbb, 0x3c, 0xf7, 0xfd, 0xc7, 0xd7, 0xb, 0xc0, 0x92, 0xef, 0x74, 0x68, 0xfa, 0x9e, 0xdf, 0x1c, 0xfc, 0xe, 0xbb, 0x88, 0xc6, 0x47, 0xa, 0x2a, 0x59, 0x85, 0x95, 0x61, 0xb8, 0xc9, 0x30, 0x4, 0xdf, 0xf3, 0x9b, 0xae, 0x6c, 0x49, 0x0, 0x1e, 0x80, 0x51, 0x1b, 0xfb, 0xc7, 0x2, 0xc7, 0x5a, 0x3b, 0x88, 0x66, 0xcf, 0x63, 0xf3, 0x12, 0x80, 0x26, 0xbf, 0xa8, 0xb5, 0xa8, 0xa, 0x88, 0x22, 0x8, 0x8a, 0x22, 0x2a, 0x19, 0x37, 0x1d, 0xe9, 0xad, 0x9f, 0x6b, 0xb4, 0x55, 0x14, 0x5, 0x55, 0x44, 0xe2, 0x32, 0x6c, 0xe2, 0x24, 0x71, 0x18, 0x9d, 0x4, 0xb0, 0x6a, 0xd3, 0x1b, 0x2a, 0xe9, 0x4b, 0xfc, 0xae, 0xe4, 0x45, 0x19, 0x9f, 0x81, 0x2b, 0xdb, 0x15, 0xa6, 0xa2, 0x19, 0x2d, 0xec, 0x8, 0x24, 0x7, 0x30, 0xd6, 0x16, 0xf8, 0x82, 0xa8, 0xc6, 0x45, 0x68, 0x52, 0xbf, 0x4e, 0xce, 0x60, 0xeb, 0x42, 0x5c, 0xb4, 0x66, 0x9c, 0x6c, 0xc9, 0x1f, 0xa4, 0x0, 0xb0, 0x39, 0xc5, 0xc9, 0xa1, 0x28, 0x89, 0x5f, 0x14, 0xd1, 0x5a, 0x37, 0x32, 0x89, 0xf2, 0x82, 0xc6, 0x3, 0xe0, 0xda, 0xa, 0x22, 0x3a, 0xa9, 0xb, 0x9a, 0x26, 0xaa, 0xf1, 0x41, 0x9a, 0x42, 0x2, 0xb2, 0xb2, 0x6d, 0x6, 0x5b, 0xa3, 0xab, 0x23, 0xd5, 0x48, 0x32, 0x92, 0xe3, 0x33, 0x10, 0x37, 0xb7, 0x2a, 0xea, 0xee, 0x4a, 0x2a, 0x42, 0xe, 0x5a, 0x2a, 0xa2, 0x51, 0x37, 0x40, 0x85, 0x48, 0x96, 0xb4, 0x23, 0xdb, 0x88, 0x68, 0x5c, 0xc4, 0xcc, 0x3c, 0x14, 0xa7, 0x6f, 0xa2, 0x6, 0xaa, 0xb1, 0x7c, 0xd9, 0x30, 0xaa, 0x79, 0xc7, 0x49, 0x93, 0x78, 0xf7, 0xd1, 0xad, 0x79, 0x76, 0x69, 0x29, 0x20, 0xda, 0x34, 0x2c, 0x9e, 0x3d, 0xff, 0x7a, 0x27, 0x4e, 0x4b, 0xef, 0xde, 0xb4, 0x4b, 0x33, 0x58, 0xef, 0xf7, 0x76, 0x9b, 0x0, 0x35, 0xfe, 0xd1, 0x2a, 0x40, 0x5, 0xa8, 0x0, 0x15, 0xa0, 0x2, 0x54, 0x80, 0xa, 0xf0, 0x5f, 0x2, 0xa4, 0xe4, 0x13, 0x78, 0xd7, 0x19, 0xd8, 0x3d, 0xf8, 0xda, 0x4, 0x60, 0x81, 0xc0, 0x18, 0x13, 0xda, 0x68, 0x7, 0x5e, 0x11, 0x18, 0x63, 0x42, 0x20, 0x0, 0x6c, 0xcd, 0xad, 0xb2, 0x6b, 0x41, 0x38, 0x58, 0xee, 0x74, 0x57, 0xdb, 0x93, 0x20, 0x36, 0x82, 0x4e, 0x77, 0xb5, 0x1d, 0x84, 0x83, 0x65, 0x60, 0xd, 0x30, 0xc9, 0xe6, 0x3a, 0x3, 0x9c, 0x6b, 0x35, 0x67, 0x1f, 0x37, 0xea, 0xd3, 0x67, 0x3c, 0xcf, 0x2b, 0x15, 0xd7, 0x18, 0x63, 0x83, 0x70, 0xf0, 0xbe, 0xbf, 0xd1, 0xbb, 0xe, 0xbc, 0x5, 0xd6, 0x25, 0xb3, 0xc2, 0xb5, 0x80, 0x63, 0xc0, 0x21, 0xa0, 0x3e, 0x66, 0xf9, 0xe, 0x81, 0x9f, 0xc0, 0x37, 0xb7, 0x47, 0x1b, 0x29, 0x8, 0xea, 0x27, 0xfb, 0xe0, 0x98, 0x2a, 0xd4, 0x95, 0x1c, 0xed, 0x51, 0xf8, 0x51, 0xfb, 0xb, 0x1, 0xbe, 0x20, 0x9f, 0x90, 0x81, 0x17, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_breakpoint_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x2, 0x3, 0x0, 0x0, 0x0, 0x6e, 0x13, 0x1f, 0x5, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xf4, 0xe7, 0x2c, 0xf4, 0xe7, 0x2c, 0xec, 0x5a, 0x6b, 0x42, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe4, 0xd1, 0xf4, 0xeb, 0x59, 0x0, 0x0, 0x0, 0x30, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x0, 0x3, 0xb6, 0x55, 0xab, 0x26, 0x30, 0x88, 0x30, 0x0, 0x91, 0x42, 0xd6, 0x4a, 0xe, 0x6, 0x45, 0x7, 0x46, 0xf, 0x6, 0x25, 0x6, 0x86, 0xe, 0x20, 0x31, 0x4a, 0x80, 0x42, 0x3, 0x1c, 0x2e, 0xe0, 0x10, 0x82, 0x84, 0x15, 0x1c, 0x0, 0x0, 0x41, 0x2d, 0x2b, 0x21, 0xbb, 0xb7, 0x1a, 0xa9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_close_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x75, 0x90, 0xb5, 0x1, 0xc0, 0x30, 0x10, 0x3, 0x55, 0x1a, 0xbe, 0xc, 0x6e, 0xe6, 0xb5, 0xc3, 0x34, 0x4b, 0x98, 0xcc, 0xee, 0x7c, 0x4f, 0x92, 0x0, 0x70, 0x45, 0x19, 0x8c, 0x47, 0x19, 0x57, 0x37, 0x66, 0x1b, 0x6b, 0x74, 0x89, 0x32, 0xd6, 0xb0, 0xed, 0x2c, 0x51, 0xca, 0x6b, 0xb6, 0xb3, 0x41, 0x94, 0x0, 0xfe, 0x9f, 0x2c, 0x0, 0xa3, 0x64, 0x61, 0xa3, 0x6f, 0x66, 0xbd, 0xc6, 0x7f, 0xe9, 0x86, 0x3b, 0x5b, 0x34, 0x76, 0xa, 0xcf, 0xad, 0xe0, 0xaa, 0xbf, 0xa4, 0x4f, 0x5a, 0xa, 0x6d, 0x25, 0xba, 0x14, 0x37, 0x18, 0x8b, 0xe4, 0x0, 0x6f, 0xe9, 0x37, 0x83, 0x22, 0x73, 0x83, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_comment_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x24, 0xa3, 0x7, 0xa4, 0x0, 0x0, 0x0, 0x78, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xd, 0x10, 0x17, 0x14, 0x18, 0x1d, 0x1a, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x10, 0x13, 0x35, 0x2f, 0x38, 0x96, 0x42, 0x2b, 0x0, 0x0, 0x0, 0x19, 0x17, 0x1b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x15, 0x1c, 0x77, 0x2f, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0x0, 0x0, 0x0, 0xe, 0xb, 0x10, 0x24, 0x1e, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xb, 0x10, 0x16, 0x12, 0x19, 0x0, 0x0, 0x0, 0x85, 0xbb, 0x9b, 0xdf, 0x0, 0x0, 0x0, 0x28, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x6, 0x8, 0x9, 0x2, 0xc, 0x1e, 0x33, 0x41, 0x46, 0xd, 0x31, 0x9a, 0xe3, 0xff, 0x5, 0x24, 0xb4, 0xff, 0xe2, 0x39, 0xf4, 0x44, 0xa, 0x47, 0xff, 0x42, 0x45, 0x3d, 0xf8, 0x2a, 0xcd, 0xff, 0x11, 0x3f, 0xd3, 0xfd, 0x2b, 0x31, 0x64, 0xfe, 0xeb, 0x0, 0x0, 0x0, 0x8d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xc8, 0x35, 0x62, 0x3, 0x41, 0x14, 0x4, 0xd1, 0xee, 0x21, 0x33, 0x33, 0xdb, 0xf7, 0xbf, 0x93, 0x99, 0x49, 0xcc, 0xd2, 0x7c, 0x2d, 0x53, 0xa6, 0x58, 0x5b, 0x59, 0x3d, 0x2, 0xc, 0x3, 0x20, 0x61, 0x20, 0x5c, 0x9, 0xc6, 0x74, 0x11, 0x20, 0x2c, 0x86, 0xbd, 0xec, 0x63, 0xa1, 0xd9, 0xa7, 0x47, 0x9a, 0x92, 0x7f, 0xda, 0x83, 0xa1, 0xcd, 0x60, 0xb2, 0xfa, 0xc7, 0x33, 0xf6, 0xb, 0xb0, 0x2e, 0xb4, 0xce, 0x2e, 0x17, 0x20, 0x2f, 0x82, 0x5d, 0x66, 0x2f, 0xb2, 0x20, 0xd4, 0x50, 0xc3, 0x19, 0x59, 0x1, 0xe3, 0xb, 0xa0, 0xa6, 0x34, 0xe7, 0x9c, 0x65, 0xa0, 0xe5, 0x9d, 0x7b, 0x3b, 0xe4, 0x38, 0x79, 0x27, 0xd2, 0xa2, 0xbb, 0x22, 0xd9, 0x8b, 0x7e, 0x43, 0x44, 0x5e, 0x8, 0x75, 0x67, 0x66, 0x1f, 0x3b, 0x0, 0x5a, 0x67, 0x7a, 0xfa, 0xe0, 0x9, 0xb8, 0x99, 0x3a, 0x44, 0xd8, 0xaf, 0xd7, 0x63, 0x10, 0x95, 0xe6, 0x1e, 0x57, 0xc1, 0x90, 0xf7, 0xdc, 0x9d, 0x9f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_comment_focus_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x24, 0xa3, 0x7, 0xa4, 0x0, 0x0, 0x0, 0x6f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0xae, 0x6d, 0x5b, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x96, 0x42, 0x2b, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x77, 0x2f, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x5f, 0x8c, 0x8b, 0xc7, 0x0, 0x0, 0x0, 0x25, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x6, 0x8, 0x9, 0x2, 0xc, 0x1e, 0x33, 0x41, 0x46, 0xd, 0x31, 0x9a, 0xe3, 0xff, 0x5, 0x24, 0xb4, 0xe2, 0x39, 0xf4, 0x44, 0xa, 0x47, 0x42, 0x45, 0x3d, 0xf8, 0x2a, 0xcd, 0x11, 0x3f, 0xd3, 0xfd, 0x2b, 0xb1, 0x1b, 0xa4, 0x4f, 0x0, 0x0, 0x0, 0x90, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xca, 0xb5, 0x75, 0xc4, 0x50, 0x0, 0x5, 0xd1, 0xf7, 0x49, 0xcc, 0xcc, 0xdc, 0x7f, 0x8b, 0x2b, 0xe6, 0xc8, 0xb1, 0x75, 0xc3, 0x39, 0x3, 0x80, 0x50, 0xc6, 0xc5, 0x84, 0x33, 0x4a, 0x30, 0x91, 0x64, 0x45, 0xd5, 0xf4, 0x89, 0xa6, 0x2a, 0xb2, 0x4, 0x48, 0x86, 0x69, 0xd9, 0xce, 0xc2, 0xb6, 0x4c, 0x43, 0x82, 0xeb, 0xf9, 0x4e, 0xb0, 0x71, 0x7c, 0xcf, 0x5, 0xf, 0xa3, 0xe0, 0x10, 0x85, 0x1c, 0x22, 0x76, 0xce, 0xe0, 0xc4, 0x2, 0x49, 0x7a, 0xd, 0x69, 0x2, 0xa1, 0xff, 0xb3, 0x70, 0xda, 0x42, 0x76, 0xf8, 0x73, 0xf8, 0xc2, 0x17, 0x92, 0xf4, 0x19, 0xf2, 0x6b, 0xc8, 0x13, 0xf0, 0xa2, 0x3c, 0x43, 0x59, 0x70, 0xb8, 0x55, 0x7d, 0x2c, 0x4e, 0x5d, 0xb9, 0x90, 0x9a, 0xb6, 0xeb, 0x9d, 0x45, 0xdf, 0xb5, 0x8d, 0x4, 0xd0, 0x66, 0x68, 0xf5, 0x74, 0xa2, 0xb7, 0x43, 0x43, 0x31, 0x91, 0x98, 0x48, 0x16, 0x82, 0x49, 0x78, 0x1b, 0x1, 0xf, 0xa7, 0x50, 0x68, 0x35, 0xb8, 0x84, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_default_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0xe, 0xb, 0x10, 0xe, 0xb, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0x0, 0x0, 0x0, 0x19, 0x15, 0x1c, 0x24, 0x1e, 0x27, 0x16, 0x12, 0x19, 0x76, 0x9, 0xd2, 0x13, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x46, 0x47, 0x3f, 0x2b, 0x11, 0x3, 0xfd, 0xd3, 0xcd, 0x2a, 0x73, 0x45, 0xf8, 0x3d, 0x3f, 0x57, 0xda, 0x84, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x64, 0x2, 0x2, 0x46, 0x8, 0xc9, 0xcc, 0xc2, 0xca, 0xc6, 0xc0, 0x8f, 0x4, 0xd8, 0x39, 0x98, 0x59, 0x19, 0x50, 0x80, 0x0, 0x27, 0x17, 0x3, 0x2a, 0x10, 0xe4, 0x46, 0x13, 0xe0, 0x67, 0x1a, 0x18, 0x1, 0x1e, 0x34, 0x1, 0x5e, 0x3e, 0xc, 0xa7, 0x63, 0x78, 0xe, 0xc3, 0xfb, 0x0, 0x89, 0x4d, 0x2, 0xf2, 0xa2, 0x23, 0x3b, 0xc4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_default_focus_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x8a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x0, 0x81, 0xff, 0x8c, 0xff, 0x99, 0xff, 0xb3, 0x2, 0x21, 0x3b, 0x4, 0x82, 0xd8, 0x40, 0x11, 0x46, 0x88, 0xa4, 0xf8, 0x7f, 0x49, 0x20, 0x14, 0xff, 0x2f, 0xa, 0x84, 0x8, 0xb6, 0xe0, 0x7f, 0x6e, 0xa0, 0x22, 0x90, 0x92, 0x92, 0xff, 0xd8, 0x60, 0xe5, 0x9b, 0xb6, 0x15, 0xff, 0x79, 0xc1, 0x4a, 0xb0, 0x83, 0x17, 0xc2, 0x29, 0xff, 0x1b, 0xe, 0x1, 0x4d, 0x61, 0xc6, 0xa1, 0x80, 0x5b, 0xf9, 0xf, 0x63, 0xe9, 0x77, 0xa0, 0x45, 0xac, 0xc, 0x38, 0x0, 0xd8, 0x72, 0xa0, 0x5b, 0xd8, 0xf1, 0x2b, 0x10, 0x27, 0xa4, 0x40, 0x92, 0x7c, 0x5, 0x8, 0x2b, 0xc8, 0x77, 0xe4, 0xb, 0xe1, 0xd2, 0x6f, 0x78, 0xbc, 0xf9, 0x87, 0x17, 0x18, 0x50, 0x7, 0x40, 0x1, 0x85, 0x23, 0xa8, 0x4b, 0xbf, 0x3, 0xc3, 0x91, 0x1f, 0x14, 0xd4, 0x78, 0x23, 0xb, 0x2d, 0xa0, 0xff, 0xb3, 0x23, 0x20, 0x22, 0xba, 0x1, 0x39, 0x96, 0x8a, 0xa5, 0x9b, 0x88, 0xa3, 0x56, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_position_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x2, 0x3, 0x0, 0x0, 0x0, 0x6e, 0x13, 0x1f, 0x5, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xf4, 0x3f, 0x2c, 0xf4, 0x3f, 0x2c, 0x1c, 0x3e, 0x10, 0xcd, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe4, 0xd1, 0xf4, 0xeb, 0x59, 0x0, 0x0, 0x0, 0x30, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x0, 0x3, 0xb6, 0x55, 0xab, 0x26, 0x30, 0x88, 0x30, 0x0, 0x91, 0x42, 0xd6, 0x4a, 0xe, 0x6, 0x45, 0x7, 0x46, 0xf, 0x6, 0x25, 0x6, 0x86, 0xe, 0x20, 0x31, 0x4a, 0x80, 0x42, 0x3, 0x1c, 0x2e, 0xe0, 0x10, 0x82, 0x84, 0x15, 0x1c, 0x0, 0x0, 0x41, 0x2d, 0x2b, 0x21, 0xbb, 0xb7, 0x1a, 0xa9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_node_selected_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x24, 0xa3, 0x7, 0xa4, 0x0, 0x0, 0x1, 0x5f, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb2, 0xb2, 0xcc, 0xae, 0xa0, 0xbb, 0x9c, 0x92, 0xa6, 0x9a, 0x91, 0xa4, 0x95, 0x8c, 0x9e, 0xaf, 0x9f, 0xaf, 0xa0, 0x94, 0xa5, 0x8e, 0x84, 0x95, 0x7f, 0x75, 0x84, 0x73, 0x6a, 0x78, 0x6d, 0x64, 0x72, 0xa2, 0xa2, 0xb9, 0x9c, 0x90, 0xa2, 0x8b, 0x81, 0x90, 0xdc, 0xda, 0xce, 0xe2, 0xe1, 0xd2, 0x9d, 0x91, 0xa9, 0x85, 0x7c, 0x8c, 0xdb, 0xd9, 0xce, 0xdb, 0xd9, 0xcd, 0xda, 0xce, 0xe0, 0xde, 0xd5, 0xe3, 0xdf, 0xd6, 0xe4, 0x97, 0x8d, 0xa0, 0x7a, 0x70, 0x7f, 0xdb, 0xd0, 0xdf, 0xdb, 0xd0, 0xe1, 0xda, 0xd0, 0xe1, 0x70, 0x67, 0x75, 0xd8, 0xcb, 0xde, 0xda, 0xcf, 0xdf, 0xdb, 0xce, 0xe1, 0xdb, 0xcf, 0xe1, 0xdb, 0xd0, 0xe0, 0xda, 0xcf, 0xe0, 0xd8, 0xcc, 0xde, 0x90, 0x87, 0x99, 0x6d, 0x67, 0x72, 0xd7, 0xcc, 0xdf, 0xda, 0xce, 0xdf, 0xd8, 0xcb, 0xdf, 0xd7, 0xca, 0xde, 0xd9, 0xcc, 0xdf, 0xd9, 0xcd, 0xdf, 0xd6, 0xc9, 0xdd, 0xd9, 0xcd, 0xde, 0xd6, 0xc8, 0xdc, 0xd5, 0xc8, 0xdc, 0xd7, 0xcb, 0xdd, 0xd7, 0xca, 0xdd, 0xd5, 0xc7, 0xdc, 0xd3, 0xc6, 0xdb, 0xd5, 0xc9, 0xdc, 0xd5, 0xc9, 0xdd, 0xd6, 0xc9, 0xdc, 0xd4, 0xc6, 0xdb, 0xd3, 0xc5, 0xdb, 0xd5, 0xc8, 0xdb, 0xd4, 0xc8, 0xdc, 0xd3, 0xc4, 0xd9, 0xd4, 0xc6, 0xda, 0xd2, 0xc3, 0xd9, 0xd3, 0xc5, 0xda, 0xd2, 0xc5, 0xd9, 0xd3, 0xc5, 0xd9, 0xd2, 0xc5, 0xda, 0xd1, 0xc2, 0xd9, 0xd2, 0xc4, 0xd8, 0xd2, 0xc4, 0xd9, 0xd0, 0xc2, 0xd9, 0xd0, 0xc1, 0xd7, 0xd0, 0xc2, 0xd7, 0xd0, 0xc2, 0xd8, 0xd1, 0xc2, 0xd7, 0xcf, 0xc1, 0xd7, 0xd0, 0xc2, 0xd6, 0xcf, 0xc1, 0xd6, 0xcf, 0xc2, 0xd7, 0xcf, 0xc0, 0xd7, 0xce, 0xbf, 0xd6, 0xce, 0xc0, 0xd5, 0xce, 0xc0, 0xd6, 0xce, 0xbf, 0xd5, 0xcd, 0xbf, 0xd5, 0xcd, 0xbe, 0xd5, 0xcd, 0xbe, 0xd4, 0xcc, 0xbd, 0xd5, 0xcc, 0xbd, 0xd4, 0xcc, 0xbc, 0xd4, 0x47, 0x40, 0x4a, 0x1d, 0x1a, 0x1f, 0x69, 0x5f, 0x6f, 0x4a, 0x42, 0x4f, 0x5e, 0x54, 0x63, 0x3b, 0x34, 0x3f, 0x5e, 0x55, 0x63, 0x63, 0x59, 0x67, 0x77, 0x6d, 0x7b, 0x6d, 0x62, 0x73, 0x7f, 0x76, 0x85, 0xdb, 0xd9, 0xcd, 0xdb, 0xd8, 0xcd, 0x6d, 0x62, 0x74, 0x8f, 0x84, 0x94, 0x7f, 0x76, 0x83, 0xdb, 0xd8, 0xcd, 0xa4, 0x95, 0xa4, 0x7e, 0x74, 0x84, 0x74, 0x6b, 0x79, 0x6f, 0x66, 0x74, 0x96, 0x8a, 0xa2, 0x91, 0x88, 0x9b, 0x0, 0x0, 0x0, 0xaa, 0xaa, 0xaa, 0xbf, 0xbf, 0xbf, 0xf0, 0xc9, 0xec, 0x71, 0x0, 0x0, 0x0, 0x75, 0x74, 0x52, 0x4e, 0x53, 0x1, 0x3, 0xa, 0x13, 0x1a, 0x1c, 0x1d, 0x10, 0x2b, 0x4d, 0x64, 0x6e, 0x72, 0xb, 0x2c, 0x6a, 0xfc, 0xff, 0x15, 0x52, 0xfd, 0xff, 0xe2, 0xe2, 0xe2, 0x1b, 0x68, 0xe2, 0xe2, 0xe2, 0x71, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x1e, 0x72, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0x6b, 0xc7, 0x56, 0xfe, 0xff, 0xc7, 0x30, 0x74, 0xfe, 0x11, 0x57, 0x6d, 0x72, 0x16, 0x1c, 0x0, 0x3, 0x4, 0x35, 0xf5, 0x4, 0x26, 0x0, 0x0, 0x1, 0x16, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xcc, 0xc5, 0x61, 0x95, 0x61, 0x10, 0x85, 0xe1, 0xf7, 0x8c, 0x5c, 0xc3, 0xdd, 0x65, 0x4d, 0xea, 0xc8, 0x6, 0xdb, 0x63, 0xab, 0x74, 0x90, 0x42, 0xe8, 0x20, 0x5b, 0x3a, 0xa0, 0x12, 0xaa, 0xc0, 0x25, 0xd7, 0xe5, 0x1f, 0xec, 0xc3, 0xa5, 0x82, 0x9c, 0xdd, 0x79, 0x46, 0x84, 0xbe, 0x4, 0xa8, 0x2f, 0x91, 0x7a, 0xbf, 0xc0, 0x52, 0x47, 0x5a, 0xa5, 0x51, 0xe4, 0x6f, 0x27, 0xd1, 0xd7, 0x6c, 0x24, 0x15, 0xa0, 0xaa, 0xe9, 0xb0, 0xec, 0x75, 0x10, 0x51, 0x9, 0xbd, 0xde, 0x60, 0x44, 0xbc, 0xb6, 0xeb, 0x43, 0x86, 0xc, 0x56, 0x3d, 0x96, 0xfb, 0x99, 0xc, 0xaf, 0xfb, 0xfe, 0xd4, 0x8f, 0xf5, 0xf3, 0xc3, 0x72, 0xb3, 0x39, 0x67, 0xec, 0xbf, 0x3b, 0x12, 0x80, 0x97, 0x9d, 0x3, 0xc0, 0x81, 0x0, 0xec, 0x2d, 0x3d, 0x96, 0x3d, 0x96, 0x27, 0x1a, 0xe8, 0x14, 0xa8, 0x54, 0x58, 0x83, 0x10, 0x48, 0x55, 0xa6, 0x6, 0x29, 0xa6, 0x0, 0x87, 0xba, 0x6, 0x2e, 0x8e, 0x0, 0xa0, 0x6, 0x26, 0xa9, 0x13, 0xe5, 0x6b, 0xc0, 0x80, 0xcc, 0x8c, 0xea, 0xb2, 0xd6, 0x83, 0xef, 0x4f, 0x25, 0x87, 0x91, 0xba, 0xb6, 0x11, 0x74, 0x96, 0xde, 0xf3, 0xae, 0x6d, 0x90, 0x3d, 0x15, 0xea, 0x12, 0xd1, 0x0, 0x41, 0x39, 0xd0, 0xc0, 0x4, 0x88, 0xa2, 0x1a, 0xb8, 0x0, 0xe0, 0x3b, 0xd8, 0x7f, 0xc1, 0xf8, 0x25, 0x7, 0xf0, 0x4b, 0x3b, 0x80, 0xb8, 0xae, 0x17, 0x56, 0xad, 0x18, 0x97, 0xca, 0x98, 0xa1, 0xd6, 0x11, 0x33, 0x6c, 0x7f, 0x1b, 0xe3, 0xfb, 0xc6, 0xf6, 0xbe, 0xf7, 0xb6, 0xb6, 0x2c, 0x3b, 0xa, 0xb3, 0xbe, 0x6e, 0xe8, 0x59, 0xac, 0x42, 0x7a, 0xd2, 0x36, 0xee, 0x57, 0xad, 0x62, 0xec, 0x1b, 0x7f, 0xa4, 0x3d, 0x60, 0xa7, 0x6a, 0xed, 0x63, 0xa1, 0xc3, 0x3b, 0x7a, 0xa, 0xc0, 0xad, 0xda, 0x1b, 0x57, 0xec, 0xf2, 0xd8, 0x75, 0x17, 0x0, 0x6a, 0x7f, 0x97, 0x8f, 0x54, 0xa2, 0x67, 0xc8, 0xba, 0xb8, 0x46, 0x24, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char graph_port_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x6e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0x8e, 0xc1, 0xd, 0xc2, 0x30, 0x10, 0x4, 0xb7, 0x31, 0x22, 0x2a, 0x48, 0x41, 0x88, 0x4f, 0xa, 0x73, 0x42, 0x17, 0x1e, 0xd2, 0x40, 0xde, 0x3e, 0xf3, 0xb5, 0x8c, 0x8f, 0x13, 0x8, 0x29, 0x3b, 0xbf, 0xd1, 0x9e, 0xf6, 0xe4, 0xc9, 0x57, 0x36, 0x5e, 0x83, 0x95, 0x59, 0x11, 0xee, 0x34, 0xfa, 0x97, 0xbc, 0x44, 0x6b, 0xa8, 0xa3, 0xdb, 0xe0, 0x70, 0xdd, 0xf6, 0x49, 0x3c, 0x5c, 0xd5, 0x20, 0xf4, 0x2a, 0x2a, 0xdd, 0x7e, 0xb2, 0xb8, 0x34, 0x61, 0x27, 0x59, 0xc4, 0x76, 0x3a, 0x4f, 0x62, 0xa6, 0xbb, 0x2e, 0x83, 0x18, 0x7a, 0x5e, 0x7c, 0x7f, 0xf9, 0x7b, 0xa9, 0xe5, 0x9b, 0x22, 0xfb, 0x44, 0xa2, 0x62, 0xa4, 0x4f, 0x4b, 0x6f, 0x69, 0x3b, 0x9a, 0x7e, 0xcd, 0xde, 0x94, 0x27, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hseparator_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x2, 0x3, 0x0, 0x0, 0x0, 0xb9, 0x61, 0x56, 0x18, 0x0, 0x0, 0x0, 0xc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x73, 0x9b, 0xaa, 0xce, 0xdc, 0xe1, 0xff, 0xff, 0xff, 0x64, 0x6c, 0x1, 0xd2, 0x0, 0x0, 0x0, 0x3, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xb3, 0xb3, 0x67, 0xf6, 0xdb, 0x93, 0x0, 0x0, 0x0, 0x10, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x64, 0x60, 0x60, 0xc2, 0x40, 0x8c, 0xc, 0x0, 0x0, 0xc7, 0x0, 0xf, 0xf5, 0x92, 0x2f, 0xa7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hslider_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x40, 0x3e, 0x4a, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x20, 0x20, 0x24, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1e, 0x1e, 0x23, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x57, 0x2e, 0xcb, 0x70, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x4, 0x19, 0x40, 0x5d, 0x66, 0x68, 0x28, 0x93, 0xf0, 0xfc, 0x94, 0xfc, 0xfd, 0x1a, 0x96, 0x95, 0x6b, 0xe2, 0xd5, 0x49, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x85, 0x4f, 0x83, 0x11, 0x3, 0x1, 0x10, 0xcc, 0x39, 0xef, 0xfe, 0xb, 0x7d, 0xdb, 0xc6, 0x8e, 0x8e, 0xb, 0xf8, 0x9d, 0x70, 0x1d, 0x9c, 0x46, 0x2d, 0xcc, 0x18, 0xea, 0x11, 0x80, 0xda, 0xb7, 0x36, 0xe, 0xf2, 0xbe, 0x2f, 0x80, 0x1c, 0xb0, 0xe5, 0xa2, 0x1f, 0xa5, 0xc0, 0xea, 0x12, 0x2c, 0x4, 0x75, 0x52, 0x80, 0x38, 0x46, 0x2b, 0x65, 0x9d, 0xa7, 0x97, 0xc1, 0xf5, 0x25, 0x82, 0x7a, 0x47, 0x4a, 0x83, 0xac, 0x93, 0x33, 0x8f, 0x83, 0xaa, 0xb2, 0xb4, 0xb8, 0x18, 0xbb, 0x58, 0xff, 0x4e, 0xdb, 0x1, 0xc3, 0xf1, 0x34, 0x3b, 0x7e, 0xbc, 0xb3, 0xc3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hslider_grabber_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xf3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x1e, 0x30, 0x33, 0xf0, 0x32, 0xc8, 0x30, 0xa8, 0x3, 0xa1, 0xc, 0x90, 0xc5, 0x8c, 0x29, 0x2d, 0x96, 0x96, 0xd7, 0x79, 0x70, 0xc6, 0xfb, 0x19, 0xef, 0x3b, 0xf, 0xa6, 0xe5, 0x31, 0x88, 0xa1, 0x2b, 0xe1, 0x4d, 0xcb, 0x9b, 0xf4, 0xa2, 0xef, 0x7f, 0x3b, 0x10, 0xf6, 0xfd, 0x9f, 0xf4, 0x2, 0xa8, 0x84, 0x17, 0x55, 0x81, 0x4c, 0xe7, 0xc1, 0xbe, 0xff, 0x2d, 0xff, 0x9b, 0x81, 0xb0, 0x5, 0xa8, 0xa4, 0xf3, 0x20, 0xd0, 0x22, 0x14, 0xa0, 0x3e, 0xe3, 0x7d, 0x3b, 0x50, 0x12, 0xc, 0x81, 0xa6, 0xcc, 0x78, 0xf, 0x74, 0xb, 0xa, 0xd0, 0x98, 0xf6, 0x1, 0x59, 0xc1, 0xd4, 0xf, 0xc, 0x1a, 0xa8, 0xa, 0x94, 0xfa, 0x6f, 0x77, 0xc1, 0x15, 0x74, 0xfc, 0xef, 0xbb, 0xc7, 0xa0, 0x82, 0xaa, 0x40, 0xbc, 0x71, 0x7d, 0x3f, 0x5c, 0x41, 0xef, 0xff, 0xde, 0xa3, 0xc, 0x52, 0xa8, 0xa, 0xb8, 0x82, 0x52, 0xa7, 0xfd, 0x69, 0x5, 0x4b, 0xb7, 0xfe, 0x9f, 0xf6, 0xcf, 0x37, 0x85, 0x81, 0x7, 0x2d, 0x1c, 0x14, 0xd, 0xfb, 0x1f, 0x74, 0x82, 0x15, 0x74, 0xfe, 0x9f, 0xf8, 0x80, 0x45, 0x83, 0x81, 0x9, 0x55, 0x1, 0x23, 0x83, 0x48, 0xc5, 0x9c, 0xc9, 0xff, 0x5b, 0x81, 0x70, 0xf2, 0xff, 0x92, 0xa9, 0xc, 0xc2, 0x98, 0x41, 0xcd, 0xca, 0xa3, 0x33, 0xe1, 0x76, 0xcf, 0xff, 0x9e, 0xff, 0x13, 0xef, 0xf0, 0xe8, 0x30, 0xb0, 0x62, 0x8b, 0xd, 0x6e, 0xb, 0xff, 0x39, 0x5f, 0xe6, 0x7c, 0x77, 0x8, 0x45, 0xd8, 0x8f, 0x61, 0x4d, 0x51, 0x71, 0x55, 0x2d, 0x83, 0x18, 0x90, 0x85, 0x3, 0xb0, 0x30, 0x70, 0x3, 0x75, 0xb3, 0x20, 0xb, 0x1, 0x0, 0x86, 0xe, 0x79, 0x54, 0x16, 0xbe, 0x69, 0xf5, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hslider_grabber_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xe7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x1e, 0x30, 0x33, 0xf0, 0x32, 0xc8, 0x30, 0xa8, 0x3, 0xa1, 0xc, 0x90, 0xc5, 0x8c, 0x29, 0x2d, 0x66, 0x9c, 0xe7, 0x7a, 0xd0, 0xe7, 0xbd, 0xcf, 0x7b, 0xd7, 0x83, 0xc6, 0x79, 0xc, 0x62, 0xe8, 0x4a, 0x78, 0x8d, 0xf3, 0x3c, 0x5f, 0xb8, 0xff, 0x77, 0x1, 0x42, 0xf7, 0xff, 0x9e, 0x2f, 0x80, 0x4a, 0x78, 0x51, 0x15, 0xc8, 0xb8, 0x1e, 0x74, 0xff, 0xef, 0x4, 0x81, 0x40, 0x25, 0xae, 0x7, 0x81, 0x16, 0xa1, 0x0, 0x75, 0x9f, 0xf7, 0x2e, 0x30, 0x5, 0x40, 0x53, 0x7c, 0xde, 0x3, 0xdd, 0x82, 0x2, 0x34, 0xbc, 0x3f, 0x20, 0x2b, 0xf0, 0xfe, 0xc0, 0xa0, 0x81, 0xaa, 0x40, 0xc9, 0xe3, 0xb6, 0x2b, 0x92, 0x2, 0xf7, 0x7b, 0xc, 0x2a, 0xa8, 0xa, 0xc4, 0x1d, 0xd7, 0x7b, 0x20, 0xb9, 0xc1, 0xfd, 0x28, 0x83, 0x14, 0xaa, 0x2, 0x2e, 0xcd, 0x54, 0xef, 0x3f, 0xce, 0x60, 0x69, 0xe7, 0xff, 0xde, 0xff, 0xd4, 0x53, 0x18, 0x78, 0xd0, 0xc2, 0x41, 0xd0, 0xd0, 0xe3, 0x1, 0xc4, 0x12, 0xd7, 0xff, 0x9e, 0xf, 0x98, 0x34, 0x18, 0x98, 0x50, 0x15, 0x30, 0x32, 0x88, 0xd8, 0xcc, 0xf1, 0xfa, 0xef, 0xc, 0x84, 0x5e, 0xff, 0xad, 0xa6, 0x32, 0x8, 0x63, 0x6, 0x35, 0x2b, 0x9b, 0x8e, 0xc7, 0x6d, 0xb7, 0xff, 0x6e, 0xff, 0x3d, 0xef, 0xb0, 0xe9, 0x30, 0xb0, 0x62, 0x8b, 0xd, 0x6e, 0x19, 0x7f, 0xbf, 0x2f, 0x7e, 0xdf, 0x15, 0x42, 0x11, 0xf6, 0x63, 0x58, 0x63, 0x59, 0x6c, 0x5b, 0xcb, 0x20, 0x6, 0x64, 0xe1, 0x0, 0x2c, 0xc, 0xdc, 0x40, 0xdd, 0x2c, 0xc8, 0x42, 0x0, 0x4, 0x5c, 0x63, 0x9b, 0xfc, 0xae, 0x1b, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hslider_grabber_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xc3, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x48, 0x83, 0x83, 0x60, 0xaf, 0xb1, 0x65, 0xbb, 0xca, 0x61, 0xb3, 0xc2, 0x0, 0x0, 0x0, 0x63, 0xb7, 0xc8, 0x63, 0xb7, 0xc7, 0x0, 0x0, 0x0, 0x61, 0xb3, 0xbc, 0x60, 0xb1, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0xa6, 0xa5, 0x63, 0xb4, 0xb6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x69, 0x69, 0x5e, 0xb1, 0xcd, 0x5e, 0xb0, 0xcd, 0x36, 0x63, 0x63, 0x0, 0x0, 0x0, 0x17, 0x2a, 0x29, 0x60, 0xb2, 0xbd, 0x62, 0xb3, 0xbf, 0x3, 0x5, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x9b, 0x9a, 0x52, 0x96, 0x95, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xf, 0xf, 0x62, 0xb4, 0xbd, 0x63, 0xb7, 0xbf, 0x0, 0x0, 0x0, 0x27, 0x48, 0x47, 0x68, 0xc0, 0xcf, 0x68, 0xc1, 0xcf, 0x2d, 0x52, 0x52, 0x51, 0x93, 0x92, 0x56, 0x9d, 0x9c, 0x0, 0x0, 0x0, 0x54, 0xa2, 0xc8, 0x4c, 0x94, 0xc2, 0x48, 0x8e, 0xc0, 0x47, 0x8c, 0xbf, 0x4b, 0x93, 0xc2, 0x4b, 0x92, 0xc2, 0x4f, 0x98, 0xc4, 0x4d, 0x96, 0xc3, 0x55, 0xa3, 0xc8, 0x53, 0x9f, 0xc7, 0x49, 0x8f, 0xc0, 0x4e, 0x97, 0xc4, 0x5a, 0xab, 0xcb, 0x5a, 0xac, 0xcc, 0x52, 0x9e, 0xc6, 0x51, 0x9d, 0xc6, 0xd4, 0xd, 0x1d, 0x1c, 0x0, 0x0, 0x0, 0x31, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x3, 0xd, 0x1c, 0x27, 0x16, 0x6e, 0xc1, 0xef, 0xe8, 0x28, 0xf0, 0xf0, 0x22, 0xdb, 0xde, 0x24, 0x17, 0xaf, 0xc5, 0x1a, 0xa, 0x65, 0xfc, 0xfe, 0x64, 0xc, 0x31, 0xe0, 0xe0, 0x28, 0x2, 0x1, 0x14, 0x9c, 0x95, 0x13, 0x5, 0x2c, 0xdb, 0xdc, 0xb, 0x4f, 0xf4, 0xf7, 0x55, 0x73, 0x7d, 0x4, 0x28, 0xf1, 0xfd, 0xa1, 0x0, 0x0, 0x0, 0x7d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5d, 0xcc, 0x81, 0x6, 0xc3, 0x40, 0x10, 0x84, 0xe1, 0xdd, 0x9d, 0xd9, 0x3d, 0x40, 0x1f, 0xa1, 0x0, 0xd1, 0xbe, 0xff, 0xbb, 0x94, 0x10, 0x29, 0x40, 0x1a, 0x40, 0x9, 0xa, 0xbd, 0x34, 0x7b, 0x39, 0xfa, 0x63, 0xf0, 0x61, 0x4c, 0xfe, 0xfa, 0x81, 0x1a, 0x48, 0x98, 0x36, 0x50, 0x66, 0xda, 0x40, 0x71, 0xab, 0x41, 0x3b, 0xc, 0x56, 0x1b, 0x3a, 0xd4, 0x8e, 0x4d, 0x8, 0x40, 0x6a, 0x64, 0x24, 0x0, 0xb6, 0x83, 0xa1, 0x1, 0x79, 0x0, 0x79, 0xc2, 0x54, 0x44, 0xca, 0x14, 0x91, 0xb0, 0xba, 0xef, 0xa7, 0xee, 0x9e, 0x70, 0x8d, 0xd0, 0x52, 0x2c, 0xe2, 0x99, 0x30, 0x93, 0xb, 0x7d, 0x81, 0x4a, 0x82, 0x8c, 0xc0, 0xba, 0xfa, 0x7b, 0x3c, 0x41, 0xb6, 0xd, 0x78, 0x84, 0x74, 0x98, 0x2f, 0x9f, 0xd7, 0x7d, 0x96, 0xbd, 0x2f, 0xa5, 0x6b, 0x13, 0xc4, 0x35, 0x90, 0x18, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hslider_tick_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xc3, 0x98, 0xc3, 0xc0, 0x0, 0x0, 0x0, 0x1b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x38, 0x55, 0x5f, 0x8c, 0xac, 0xb8, 0x81, 0xa2, 0xad, 0x98, 0x98, 0x98, 0x4e, 0x4e, 0x4e, 0x8d, 0x8d, 0x8d, 0x82, 0x82, 0x82, 0x4e, 0x4e, 0x4e, 0xc9, 0xf6, 0x7, 0x31, 0x0, 0x0, 0x0, 0x9, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x79, 0x31, 0x79, 0x79, 0x1c, 0x7e, 0xed, 0x4b, 0xf4, 0x0, 0x0, 0x0, 0x20, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x9, 0x60, 0x60, 0xb, 0x60, 0x60, 0xf, 0x60, 0x60, 0x6d, 0x60, 0x60, 0x14, 0x60, 0xc0, 0x4, 0x4c, 0x2, 0xc, 0xcc, 0x2, 0x70, 0x65, 0x0, 0x46, 0x9d, 0x2, 0xbe, 0xb7, 0xdf, 0x14, 0x38, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hsplit_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x27, 0x27, 0x29, 0xff, 0xff, 0xff, 0x11, 0xab, 0xb9, 0xf3, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char hsplitter_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x40, 0x8, 0x0, 0x0, 0x0, 0x0, 0x2, 0x6f, 0x69, 0x56, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x1a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x80, 0x5, 0xa9, 0x10, 0xcc, 0x90, 0x6a, 0xd, 0xc6, 0x70, 0x80, 0x4b, 0xcd, 0xa8, 0x9a, 0x51, 0x35, 0x0, 0x78, 0xd5, 0x34, 0xa1, 0x54, 0x8c, 0xd5, 0x84, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_add_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x3c, 0xf8, 0xff, 0xe0, 0xff, 0xd0, 0x52, 0x80, 0x10, 0xc4, 0xd, 0x9, 0x2a, 0x18, 0x26, 0xe1, 0x40, 0x18, 0x0, 0x0, 0x5b, 0x26, 0x61, 0x4d, 0xc9, 0xc1, 0x48, 0x81, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_close_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_color_pick_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xaa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x9d, 0x8e, 0x35, 0x82, 0x2, 0x41, 0x10, 0x45, 0x3b, 0xda, 0x3d, 0xca, 0xba, 0x44, 0x2b, 0x70, 0x9, 0xdc, 0xe1, 0x20, 0xe8, 0x91, 0x90, 0x78, 0x6e, 0x40, 0x4c, 0x82, 0x74, 0xff, 0xc2, 0x9d, 0x18, 0xa7, 0x6, 0x77, 0x7b, 0x23, 0x2d, 0xaf, 0x4c, 0xdc, 0xc, 0xbd, 0x65, 0x1e, 0x84, 0x80, 0x19, 0x55, 0x34, 0x60, 0x3e, 0xd0, 0xea, 0x17, 0x3d, 0x4a, 0xc8, 0x80, 0x1a, 0x60, 0xc2, 0x4f, 0xfd, 0x30, 0xe0, 0x1b, 0x2d, 0x16, 0xab, 0xa7, 0x2c, 0xe, 0x41, 0x68, 0xa5, 0xb9, 0xca, 0x91, 0x16, 0x2e, 0x54, 0xe0, 0x59, 0x54, 0x91, 0xfe, 0xa3, 0x3a, 0xff, 0xce, 0xab, 0x5b, 0xf, 0xa0, 0x4, 0x8f, 0x7b, 0x4c, 0xd3, 0x1b, 0xca, 0x32, 0xcc, 0x55, 0x7a, 0xf4, 0x76, 0x42, 0x2b, 0x97, 0x3e, 0xae, 0xfa, 0xdd, 0xd2, 0xd2, 0x8e, 0x72, 0xe1, 0x83, 0xaf, 0x9f, 0xa9, 0x28, 0x7d, 0x5b, 0xe2, 0x2a, 0xd, 0xc3, 0xa2, 0x78, 0xfe, 0x7d, 0x51, 0xfc, 0x0, 0x8a, 0x41, 0xcb, 0x3d, 0xb2, 0xae, 0x1c, 0xd3, 0xc, 0xa5, 0x30, 0x81, 0xc6, 0xda, 0x29, 0x8e, 0x83, 0x34, 0x25, 0x29, 0x4a, 0x46, 0x71, 0x1f, 0x33, 0xbe, 0x51, 0x89, 0xaf, 0x78, 0xe3, 0x97, 0x7e, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_file_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x2, 0x3, 0x0, 0x0, 0x0, 0x62, 0x9d, 0x17, 0xf2, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0x42, 0xf, 0xc7, 0x49, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x88, 0x95, 0xf0, 0xc6, 0x2a, 0x0, 0x0, 0x0, 0x21, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x0, 0x1, 0xae, 0x55, 0x2d, 0x20, 0xa2, 0x13, 0x44, 0x74, 0x39, 0x80, 0x88, 0x9, 0x40, 0xa2, 0x1, 0xc4, 0x5d, 0xb5, 0x80, 0x68, 0x2, 0x4, 0x0, 0x95, 0x34, 0x18, 0xe4, 0x5e, 0x46, 0xf7, 0x27, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_folder_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x6, 0x78, 0x70, 0xf4, 0xc1, 0x7f, 0x24, 0x78, 0x18, 0x53, 0xc1, 0x7f, 0x54, 0x48, 0x50, 0xc1, 0x43, 0x1b, 0xbc, 0xa, 0x50, 0xad, 0x23, 0xa4, 0xe0, 0xff, 0x70, 0x52, 0x70, 0x18, 0x97, 0xf4, 0xfd, 0x43, 0xd4, 0x88, 0x4a, 0x0, 0x5a, 0xcb, 0x18, 0xab, 0x5e, 0xd9, 0x1a, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_grid_layout_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x5, 0x52, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0xa, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2e, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x65, 0x78, 0x69, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x78, 0x69, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x23, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x58, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x59, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x65, 0x78, 0x69, 0x66, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x53, 0x70, 0x61, 0x63, 0x65, 0x3d, 0x22, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x36, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x74, 0x3d, 0x22, 0x32, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x58, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x37, 0x32, 0x2e, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x59, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x37, 0x32, 0x2e, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x3d, 0x22, 0x33, 0x22, 0xa, 0x20, 0x20, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x49, 0x43, 0x43, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x3d, 0x22, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0xa, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x44, 0x61, 0x74, 0x65, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x20, 0x78, 0x6d, 0x6c, 0x3a, 0x6c, 0x61, 0x6e, 0x67, 0x3d, 0x22, 0x78, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x3e, 0x47, 0x72, 0x69, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x64, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x20, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x20, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x77, 0x68, 0x65, 0x6e, 0x3d, 0x22, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x38, 0x2d, 0x31, 0x35, 0x54, 0x30, 0x39, 0x3a, 0x34, 0x31, 0x3a, 0x34, 0x30, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0x22, 0x2f, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0xa, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0xa, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0x10, 0xfa, 0x51, 0xae, 0x0, 0x0, 0x1, 0x82, 0x69, 0x43, 0x43, 0x50, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x0, 0x0, 0x28, 0x91, 0x75, 0x91, 0xbf, 0x4b, 0x42, 0x51, 0x14, 0xc7, 0x3f, 0x5a, 0xa1, 0x94, 0x61, 0x50, 0x43, 0x43, 0x83, 0x84, 0x45, 0x83, 0x85, 0x15, 0x84, 0x2d, 0xd, 0x4a, 0xbf, 0xa0, 0x1a, 0xd4, 0x20, 0xab, 0x45, 0x9f, 0xbf, 0x2, 0xb5, 0xc7, 0x7b, 0x4a, 0x48, 0x6b, 0xd0, 0x2a, 0x14, 0x44, 0x2d, 0xfd, 0x1a, 0xea, 0x2f, 0xa8, 0x35, 0x68, 0xe, 0x82, 0xa2, 0x8, 0xa2, 0x2d, 0x68, 0x2e, 0x6a, 0xa9, 0x78, 0x9d, 0xa7, 0x82, 0x11, 0x79, 0x2e, 0xe7, 0x9e, 0xcf, 0xfd, 0xde, 0x7b, 0xe, 0xf7, 0x9e, 0xb, 0xd6, 0x70, 0x46, 0xc9, 0xea, 0x8d, 0x5e, 0xc8, 0xe6, 0xf2, 0x5a, 0x70, 0xd2, 0xef, 0x5a, 0x88, 0x2c, 0xba, 0x6c, 0xcf, 0xd8, 0x71, 0x62, 0xa3, 0xf, 0x5f, 0x54, 0xd1, 0xd5, 0xd9, 0xd0, 0x44, 0x98, 0xba, 0xf6, 0x71, 0x87, 0xc5, 0x8c, 0x37, 0xfd, 0x66, 0xad, 0xfa, 0xe7, 0xfe, 0xb5, 0x96, 0x78, 0x42, 0x57, 0xc0, 0x62, 0x17, 0x1e, 0x53, 0x54, 0x2d, 0x2f, 0x3c, 0x25, 0x3c, 0xb3, 0x96, 0x57, 0x4d, 0xde, 0x16, 0xee, 0x50, 0xd2, 0xd1, 0xb8, 0xf0, 0xa9, 0xb0, 0x47, 0x93, 0xb, 0xa, 0xdf, 0x9a, 0x7a, 0xac, 0xc2, 0x2f, 0x26, 0xa7, 0x2a, 0xfc, 0x65, 0xb2, 0x16, 0xe, 0x6, 0xc0, 0xda, 0x26, 0xec, 0x4a, 0xfd, 0xe2, 0xd8, 0x2f, 0x56, 0xd2, 0x5a, 0x56, 0x58, 0x5e, 0x8e, 0x3b, 0x9b, 0x29, 0x28, 0xd5, 0xfb, 0x98, 0x2f, 0x71, 0x24, 0x72, 0xf3, 0x21, 0x89, 0xdd, 0xe2, 0x5d, 0xe8, 0x4, 0x99, 0xc4, 0x8f, 0x8b, 0x69, 0xc6, 0x9, 0x30, 0xc2, 0x20, 0xa3, 0x32, 0x8f, 0xd0, 0xcf, 0x10, 0x3, 0xb2, 0xa2, 0x4e, 0xbe, 0xb7, 0x9c, 0x3f, 0xc7, 0xaa, 0xe4, 0x2a, 0x32, 0xab, 0x14, 0xd1, 0x58, 0x21, 0x45, 0x9a, 0x3c, 0x1e, 0x51, 0xb, 0x52, 0x3d, 0x21, 0x31, 0x29, 0x7a, 0x42, 0x46, 0x86, 0xa2, 0xd9, 0xff, 0xbf, 0x7d, 0xd5, 0x93, 0xc3, 0x43, 0x95, 0xea, 0xe, 0x3f, 0x34, 0x3d, 0x19, 0xc6, 0x5b, 0xf, 0xd8, 0xb6, 0xe0, 0xbb, 0x64, 0x18, 0x9f, 0x87, 0x86, 0xf1, 0x7d, 0x4, 0xd, 0x8f, 0x70, 0x91, 0xab, 0xe5, 0xaf, 0x1e, 0x80, 0xef, 0x5d, 0xf4, 0x52, 0x4d, 0x73, 0xef, 0x83, 0x73, 0x3, 0xce, 0x2e, 0x6b, 0x5a, 0x6c, 0x7, 0xce, 0x37, 0xa1, 0xf3, 0x41, 0x8d, 0x6a, 0xd1, 0xb2, 0xd4, 0x20, 0x6e, 0x4d, 0x26, 0xe1, 0xf5, 0x4, 0x5a, 0x23, 0xd0, 0x7e, 0xd, 0xcd, 0x4b, 0x95, 0x9e, 0x55, 0xf7, 0x39, 0xbe, 0x87, 0xf0, 0xba, 0x7c, 0xd5, 0x15, 0xec, 0xee, 0x41, 0xaf, 0x9c, 0x77, 0x2e, 0xff, 0x0, 0xa6, 0xc4, 0x68, 0x3, 0x1f, 0xd7, 0x32, 0xd8, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x1, 0x40, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x9d, 0xd2, 0xbd, 0x4a, 0x5d, 0x41, 0x14, 0x86, 0xe1, 0xe7, 0x84, 0xa3, 0x51, 0x22, 0x82, 0x41, 0x8c, 0x12, 0x2, 0xeb, 0x1a, 0x2c, 0x84, 0x4, 0x52, 0x88, 0xb1, 0xd2, 0x26, 0x76, 0x62, 0x67, 0xeb, 0xd, 0x78, 0x19, 0x56, 0x69, 0x82, 0x62, 0x67, 0x25, 0x82, 0x60, 0x97, 0xc2, 0x52, 0x8, 0x24, 0x95, 0x16, 0xa9, 0x6, 0x8c, 0x18, 0xb5, 0x30, 0x88, 0xa7, 0x90, 0x88, 0x5a, 0xec, 0x39, 0x66, 0xd8, 0x7a, 0x54, 0x5c, 0xb0, 0xd8, 0xcc, 0xbb, 0xe7, 0xfb, 0x58, 0x3f, 0xd3, 0x50, 0x8b, 0x94, 0xd2, 0x6b, 0xcc, 0x60, 0x25, 0x22, 0xae, 0xea, 0xff, 0xeb, 0xd1, 0xbc, 0x47, 0xfc, 0xd, 0xbd, 0x58, 0x7e, 0x4c, 0xc, 0x8d, 0x42, 0x3c, 0x82, 0x9f, 0xe8, 0xc1, 0x26, 0xce, 0x3b, 0x68, 0xae, 0xf1, 0x25, 0x22, 0x76, 0xeb, 0x15, 0xf4, 0xa3, 0xf, 0x97, 0x18, 0xce, 0xdf, 0x4e, 0x6, 0xfd, 0x77, 0x2a, 0xc8, 0x55, 0x4, 0xb6, 0xf1, 0x1b, 0x1f, 0x23, 0xe2, 0xfa, 0x49, 0x2d, 0xa4, 0x94, 0x7a, 0x30, 0x98, 0xd9, 0x5b, 0x4c, 0x63, 0x49, 0x35, 0xb, 0x68, 0xe1, 0x2f, 0x46, 0xf0, 0x22, 0xb3, 0x93, 0x88, 0xb8, 0x68, 0x1f, 0x36, 0xb0, 0x9f, 0x73, 0x7, 0x87, 0xf8, 0x53, 0xb0, 0x23, 0x2c, 0xe0, 0xa0, 0x60, 0xeb, 0xa, 0xb7, 0x1, 0xac, 0x60, 0x34, 0x5f, 0x1a, 0x52, 0xcd, 0x67, 0x16, 0x73, 0xe8, 0xca, 0x6c, 0x3f, 0xdf, 0x59, 0xcd, 0x9a, 0x5b, 0x83, 0x67, 0x47, 0x7b, 0xb, 0xa7, 0x98, 0xcf, 0x9, 0xc7, 0xaa, 0x2d, 0xac, 0xe5, 0xf3, 0xbf, 0xcc, 0xde, 0xe1, 0x47, 0x66, 0x5b, 0xa5, 0xc1, 0x67, 0xff, 0x87, 0x78, 0xa5, 0x9a, 0xc1, 0x1a, 0x5e, 0x65, 0xd6, 0xc2, 0x24, 0x3e, 0xe4, 0x36, 0xe0, 0x84, 0xda, 0x1a, 0x1f, 0x8a, 0x94, 0xd2, 0x6, 0xc6, 0x30, 0x1e, 0x11, 0xbf, 0xda, 0xbc, 0x7c, 0x89, 0xc3, 0x58, 0x44, 0x77, 0x7, 0x8f, 0x2e, 0x4c, 0xa9, 0x1e, 0xd1, 0xa7, 0x88, 0xd8, 0x29, 0x5b, 0xa0, 0x7a, 0xc2, 0xf1, 0x80, 0x41, 0x23, 0xe7, 0x4b, 0xbc, 0x79, 0x6a, 0xe5, 0x65, 0xb, 0x5f, 0x53, 0x4a, 0x67, 0x29, 0xa5, 0xf7, 0x25, 0x6f, 0x76, 0x12, 0xdc, 0x13, 0x7b, 0x98, 0x88, 0x88, 0xef, 0x25, 0xbc, 0x1, 0x6c, 0x4d, 0x56, 0x9e, 0x2a, 0x4e, 0x48, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_grid_minimap_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x2, 0xd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x75, 0x93, 0x31, 0x68, 0x14, 0x51, 0x10, 0x86, 0xbf, 0xd9, 0xd, 0xbb, 0xde, 0x76, 0x82, 0x21, 0xf8, 0xe0, 0xbc, 0x5d, 0x8b, 0x80, 0x69, 0x6c, 0xd2, 0x5a, 0x6a, 0x91, 0xc3, 0xd2, 0x46, 0x22, 0x8, 0x9, 0x89, 0x70, 0x85, 0x10, 0x41, 0xd, 0x24, 0x45, 0xb0, 0xb, 0x68, 0x11, 0x14, 0x24, 0x10, 0x22, 0x62, 0x21, 0x41, 0xe, 0x4b, 0x21, 0xa4, 0xb7, 0x49, 0x17, 0xb1, 0x8, 0xb9, 0xdd, 0xc7, 0x86, 0x33, 0x21, 0xe1, 0x3a, 0x8f, 0x64, 0x61, 0x6f, 0x2c, 0xbc, 0x3b, 0x36, 0xb9, 0xdc, 0xc0, 0x2b, 0xde, 0xcc, 0xfc, 0xf3, 0xff, 0xfc, 0xcc, 0x48, 0xa3, 0xd1, 0x78, 0x20, 0x22, 0x13, 0xbe, 0xef, 0xaf, 0xdf, 0xac, 0xd7, 0x1f, 0xe1, 0x38, 0xd3, 0xa8, 0x2a, 0xf0, 0x45, 0x6a, 0xb5, 0xcf, 0x5c, 0x11, 0xcd, 0x66, 0x33, 0x38, 0x3f, 0x3f, 0x9f, 0x13, 0x91, 0x7d, 0xb1, 0xd6, 0x6e, 0xaa, 0xea, 0xd3, 0xe0, 0xe8, 0xe8, 0xde, 0xe8, 0xee, 0xee, 0x37, 0xc0, 0xe9, 0xf6, 0x75, 0xf0, 0xfd, 0x9, 0x99, 0x9d, 0x6d, 0x15, 0x81, 0x59, 0x96, 0x3d, 0x3, 0x5e, 0x2, 0x63, 0x22, 0xf2, 0x69, 0xa4, 0x57, 0x1c, 0xdd, 0xdb, 0xfb, 0x5b, 0x0, 0x3, 0x38, 0x67, 0x41, 0x30, 0x11, 0xc7, 0xf1, 0x13, 0x0, 0x11, 0x71, 0xb2, 0x2c, 0x7b, 0xd8, 0xad, 0xad, 0x2, 0x6f, 0xb9, 0x0, 0x38, 0x3c, 0xfc, 0x5, 0x9c, 0xf6, 0xff, 0x22, 0x27, 0x27, 0xe3, 0xe3, 0x7f, 0xa, 0x3, 0x67, 0x45, 0xe4, 0xbb, 0xe7, 0x79, 0xb7, 0xc3, 0x30, 0x7c, 0xd7, 0x67, 0xe9, 0xe3, 0x67, 0x66, 0x5c, 0x60, 0x1, 0x50, 0x40, 0x51, 0x7d, 0x71, 0x6b, 0x72, 0xf2, 0x20, 0x8a, 0xa2, 0xf9, 0x28, 0x8a, 0xe6, 0x1, 0x3a, 0x9d, 0xce, 0x4f, 0x63, 0x4c, 0x3b, 0x4d, 0xd3, 0xd2, 0xc0, 0x80, 0x3c, 0xcf, 0xf, 0x92, 0xa9, 0xa9, 0x31, 0x60, 0x5, 0x58, 0x91, 0x5a, 0xed, 0xc7, 0x15, 0xfe, 0x95, 0xac, 0xb5, 0xcf, 0xf3, 0x3c, 0x3f, 0xe8, 0x25, 0x46, 0xa, 0xc5, 0xd, 0x11, 0x59, 0xb3, 0xd5, 0xea, 0x1b, 0xa0, 0x95, 0x54, 0xab, 0x5b, 0x97, 0xd1, 0x22, 0xb2, 0xa6, 0xaa, 0x6d, 0x60, 0xd, 0x58, 0xba, 0xa0, 0x20, 0xc, 0xc3, 0x65, 0xd7, 0x75, 0x23, 0xe0, 0x2e, 0xb0, 0x1, 0x5c, 0xbf, 0xf4, 0x0, 0xbe, 0xba, 0xae, 0x1b, 0x85, 0x61, 0xb8, 0x3c, 0xa0, 0x20, 0x4d, 0xd3, 0x52, 0xb9, 0x5c, 0x6e, 0xc5, 0x71, 0xbc, 0x23, 0x22, 0xd3, 0x61, 0x18, 0xde, 0x2f, 0xb2, 0x27, 0x49, 0xa2, 0xaa, 0xba, 0x53, 0x2e, 0x97, 0x5b, 0x69, 0x9a, 0x96, 0xf2, 0x3c, 0x1f, 0xf0, 0xc0, 0x5a, 0x6b, 0x5f, 0x1, 0x25, 0x86, 0x84, 0xe3, 0x38, 0x9e, 0xb5, 0x76, 0x2e, 0xcf, 0xf3, 0xfd, 0x1, 0x5, 0x22, 0xb2, 0xa1, 0xaa, 0x4b, 0x22, 0x72, 0xad, 0xcb, 0x38, 0xe0, 0x81, 0xaa, 0x7e, 0x0, 0xce, 0x44, 0xe4, 0xbd, 0xaa, 0xbe, 0xbe, 0xa0, 0xa0, 0x52, 0xa9, 0x2c, 0x7a, 0x9e, 0x17, 0x1, 0x3d, 0xe0, 0x55, 0x1e, 0x6c, 0x79, 0x9e, 0x17, 0x55, 0x2a, 0x95, 0xc5, 0x1, 0x5, 0xcd, 0x66, 0x33, 0x30, 0xc6, 0x9c, 0xc6, 0x71, 0xbc, 0x2d, 0x22, 0x8f, 0x87, 0x78, 0xb0, 0x6d, 0x8c, 0x39, 0xed, 0xae, 0x74, 0xdf, 0x83, 0x3a, 0x70, 0x9c, 0x65, 0x59, 0x23, 0x49, 0x92, 0x5, 0x11, 0x9, 0x86, 0x79, 0x20, 0x22, 0x41, 0x92, 0x24, 0xb, 0x59, 0x96, 0x35, 0x80, 0x63, 0xa0, 0x2e, 0x3d, 0xf6, 0xc2, 0x91, 0xdc, 0x0, 0x5c, 0x55, 0x5d, 0xbf, 0x4, 0x9e, 0x3, 0x72, 0xfe, 0xaf, 0xfb, 0xaa, 0xe7, 0x79, 0x1f, 0x8d, 0x31, 0x6d, 0x29, 0x36, 0xf5, 0xce, 0x14, 0xb8, 0x33, 0x44, 0xc4, 0x6f, 0xdf, 0xf7, 0xd7, 0x8d, 0x31, 0xed, 0x5e, 0xe2, 0x1f, 0xb, 0x5c, 0xe2, 0xcb, 0xd, 0x9b, 0x69, 0xcb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_parent_folder_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x68, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x33, 0xb8, 0x27, 0xfe, 0xe0, 0xfc, 0x83, 0x73, 0xf7, 0xc4, 0x71, 0x48, 0xdf, 0x11, 0x7b, 0x78, 0xe9, 0xc1, 0x3f, 0x20, 0xbc, 0xfe, 0x40, 0x12, 0x8f, 0x34, 0x4c, 0x9, 0xa6, 0xe1, 0x57, 0x80, 0x12, 0x17, 0x81, 0xf8, 0x2f, 0x58, 0xe1, 0x15, 0x34, 0x8b, 0x1e, 0x9c, 0x5, 0xa, 0x5e, 0xb8, 0x23, 0x6, 0x52, 0x70, 0x5b, 0x14, 0xac, 0xf0, 0xc, 0xaa, 0x82, 0x7d, 0xf, 0x8e, 0xde, 0x14, 0xf9, 0xcf, 0x8, 0x52, 0xc0, 0xc0, 0x70, 0x5b, 0xf4, 0xe1, 0xc9, 0x7, 0x47, 0xb1, 0xb8, 0x3, 0xaa, 0x0, 0xa, 0x48, 0x52, 0x80, 0xb0, 0xea, 0xc8, 0xc3, 0x83, 0xc, 0x83, 0xe, 0x0, 0x0, 0xb8, 0x27, 0x55, 0x4c, 0xbe, 0xc0, 0xd2, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_reload_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xb1, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xad, 0x50, 0x35, 0xba, 0xc2, 0x40, 0x10, 0x7e, 0xcd, 0x33, 0x5c, 0xca, 0x1c, 0x21, 0x87, 0xc1, 0xa9, 0xd3, 0x23, 0x47, 0xca, 0x69, 0xb0, 0x6, 0x77, 0xdb, 0xfd, 0x17, 0x2f, 0x91, 0x3e, 0xee, 0xd2, 0xc1, 0x4e, 0xb5, 0xdf, 0xcc, 0xaf, 0x5f, 0x9f, 0x7c, 0xdb, 0x5f, 0xda, 0x44, 0x17, 0x2f, 0x75, 0xba, 0xa4, 0xb1, 0xfd, 0xf5, 0xad, 0x8f, 0x1c, 0x86, 0x90, 0x5c, 0x33, 0x38, 0x72, 0x1e, 0xb4, 0xbe, 0x66, 0x28, 0xad, 0xe2, 0xab, 0x38, 0xcd, 0x63, 0xa9, 0x9d, 0xb8, 0x58, 0x68, 0x53, 0x5b, 0x1f, 0x33, 0xd6, 0x9f, 0xa5, 0xc1, 0x20, 0x91, 0xba, 0x7d, 0x80, 0x1e, 0x24, 0x94, 0xdc, 0x92, 0xa4, 0x2, 0x9, 0x1d, 0xe7, 0xe0, 0x9, 0x69, 0x15, 0xf7, 0x58, 0x4e, 0x40, 0xc2, 0xc3, 0x58, 0x8a, 0xb6, 0x31, 0xd1, 0x39, 0xd8, 0x27, 0xed, 0x83, 0x5b, 0x14, 0x33, 0x7d, 0x3d, 0xbb, 0x45, 0x5d, 0x12, 0x55, 0x97, 0x4, 0xe3, 0xb5, 0xf4, 0x8c, 0x77, 0xd6, 0xa7, 0x2c, 0x3b, 0x78, 0x4c, 0x52, 0x81, 0xa, 0x8e, 0x3a, 0xa9, 0x6a, 0x6b, 0xc, 0xe6, 0x3f, 0xa1, 0x8d, 0x86, 0x16, 0xe5, 0x39, 0x78, 0xa2, 0x4d, 0xea, 0xe, 0xfa, 0xdd, 0xa7, 0x0, 0x90, 0x4f, 0x8b, 0xd0, 0xe1, 0x9e, 0x1b, 0xc2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_snap_grid_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xf3, 0xf3, 0xf3, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x85, 0x85, 0xff, 0x83, 0x83, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x80, 0x80, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0xff, 0xff, 0xa, 0xa5, 0x43, 0x1, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xff, 0x1d, 0xac, 0xf2, 0xaf, 0x27, 0xed, 0xff, 0xee, 0xb4, 0x1b, 0x1c, 0xb6, 0xaa, 0xf1, 0x50, 0xa6, 0xdd, 0x5f, 0x0, 0x0, 0x0, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x0, 0x2, 0x40, 0x23, 0xd3, 0x60, 0x0, 0x40, 0xc, 0x3, 0xaf, 0x76, 0x93, 0xfd, 0x97, 0x7d, 0x9b, 0x55, 0x70, 0x12, 0x62, 0xaf, 0x68, 0x0, 0xc4, 0xe9, 0x3c, 0x1, 0x67, 0xf7, 0x17, 0x20, 0x95, 0xd6, 0xc6, 0xee, 0x80, 0x74, 0xde, 0x7b, 0x1f, 0x24, 0xb0, 0x64, 0x29, 0x1f, 0x53, 0x2e, 0xbe, 0x6e, 0x80, 0xf6, 0x19, 0x90, 0x9e, 0x36, 0x8b, 0xf7, 0xc0, 0x5c, 0xdf, 0x0, 0x66, 0x60, 0xae, 0xf3, 0xb9, 0x1, 0xfb, 0xe9, 0x1, 0xa6, 0x26, 0x1, 0xcd, 0x30, 0x66, 0x63, 0x6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_visibility_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x3, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0xdb, 0xe1, 0x4f, 0xe0, 0x0, 0x0, 0x0, 0x96, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xdf, 0xdf, 0xdf, 0xe3, 0xe3, 0xe3, 0xe6, 0xe6, 0xe6, 0xd5, 0xd5, 0xd5, 0xd8, 0xd8, 0xd8, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xe1, 0xe1, 0xe1, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xde, 0xde, 0xde, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xb7, 0x7e, 0xd, 0xb6, 0x0, 0x0, 0x0, 0x32, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x8, 0x9, 0xa, 0xc, 0xd, 0xe, 0xf, 0x11, 0x12, 0x13, 0x2e, 0x2f, 0x32, 0x33, 0x36, 0x37, 0x38, 0x48, 0x49, 0x4b, 0x50, 0x53, 0x55, 0x56, 0x6c, 0x6d, 0x6e, 0x70, 0x77, 0x79, 0x7b, 0x7c, 0xc5, 0xd7, 0xd8, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe1, 0xe2, 0xe3, 0xf0, 0xf2, 0xf3, 0xf4, 0xfe, 0x5e, 0x62, 0x1a, 0x26, 0x0, 0x0, 0x0, 0x83, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x8d, 0xc8, 0x45, 0x42, 0x3, 0x1, 0x0, 0xc0, 0xc0, 0xd4, 0x8b, 0x43, 0x1d, 0x5f, 0x77, 0xcb, 0xff, 0x1f, 0x87, 0xd3, 0xf3, 0xe6, 0x36, 0x61, 0x5c, 0x93, 0xc7, 0x70, 0xe8, 0xc3, 0xa7, 0xe9, 0xbf, 0xaf, 0x62, 0x55, 0x4d, 0x6e, 0x7e, 0x7d, 0xdd, 0x5a, 0x1d, 0x16, 0x8b, 0x7d, 0x64, 0x77, 0xfb, 0xed, 0x69, 0x66, 0x75, 0x1, 0xb0, 0xae, 0x2c, 0x67, 0xc0, 0xab, 0x1e, 0xd8, 0x35, 0xf5, 0x96, 0xa3, 0xbe, 0x1, 0x89, 0x2e, 0xa8, 0xb5, 0x62, 0xa9, 0x9, 0x90, 0x9e, 0xc7, 0x4a, 0x53, 0x20, 0xd0, 0x3d, 0xdb, 0xba, 0xda, 0x70, 0xd2, 0x77, 0x60, 0xde, 0x18, 0xad, 0x1, 0x2e, 0x6b, 0xab, 0x39, 0xc0, 0xc3, 0x60, 0x75, 0x5c, 0x2e, 0x4f, 0xb5, 0xc3, 0x1d, 0x3f, 0xdd, 0x17, 0xaa, 0x9a, 0xff, 0x19, 0x66, 0x2f, 0x61, 0xdf, 0x87, 0xcf, 0x33, 0x46, 0xf5, 0x9, 0xdb, 0xf3, 0x10, 0xc5, 0x55, 0x93, 0xc, 0xea, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_zoom_less_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x13, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0x31, 0xe0, 0xc1, 0x7f, 0x3c, 0x90, 0xb0, 0x82, 0x11, 0x2, 0x0, 0xbf, 0x57, 0x36, 0x25, 0x52, 0x24, 0x7b, 0x26, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_zoom_more_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x3c, 0xf8, 0xf, 0x84, 0x43, 0x4a, 0x1, 0x42, 0x10, 0xf, 0x24, 0xa8, 0x60, 0x78, 0x84, 0x3, 0x61, 0x0, 0x0, 0xca, 0x3a, 0x6d, 0x8d, 0x50, 0x1e, 0x9a, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char icon_zoom_reset_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x33, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0xa, 0x3c, 0xc, 0x7b, 0xf0, 0xff, 0xc1, 0x7f, 0x9c, 0x22, 0xcf, 0x44, 0x1e, 0xbc, 0x84, 0x72, 0xb1, 0x8b, 0x3c, 0x58, 0x5, 0xe4, 0x40, 0xb8, 0x38, 0x45, 0x18, 0x60, 0x5c, 0x84, 0x30, 0x59, 0xa, 0xa0, 0x80, 0x6e, 0xa, 0x86, 0x92, 0x2f, 0x8, 0x3, 0x0, 0x69, 0xc8, 0x86, 0x87, 0x72, 0xca, 0x85, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char indeterminate_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0xfe, 0xfe, 0xfe, 0x98, 0x4d, 0x2d, 0x9a, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1c, 0x77, 0x5e, 0x7f, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcf, 0xc1, 0x12, 0x80, 0x20, 0x8, 0x45, 0xd1, 0x4, 0x14, 0xd, 0xc5, 0xfa, 0xff, 0x9f, 0x8d, 0x9c, 0x6c, 0x9a, 0xb4, 0xe9, 0x2e, 0xcf, 0x42, 0x9e, 0xcb, 0x32, 0xe4, 0x0, 0xc9, 0xb7, 0x8, 0xc1, 0x19, 0x40, 0x60, 0xc9, 0x2d, 0xe1, 0x0, 0x6, 0xc8, 0x45, 0x6b, 0x4b, 0xb, 0xa3, 0x1, 0x89, 0x6e, 0x57, 0x2a, 0x64, 0xe0, 0x73, 0xed, 0x50, 0xb3, 0x9f, 0xc3, 0x7e, 0xf7, 0x5, 0x7f, 0x6f, 0xc, 0x67, 0x9f, 0xc3, 0xe2, 0x39, 0xc, 0x52, 0xec, 0xd3, 0xd7, 0x4, 0xb3, 0xcf, 0xbd, 0x3a, 0x0, 0xa0, 0xa2, 0x8, 0xbc, 0xf6, 0x84, 0x3a, 0x9d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char line_edit_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char line_edit_clear_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xad, 0x90, 0x1, 0x6, 0xc0, 0x30, 0xc, 0x45, 0x77, 0x89, 0xd5, 0x76, 0xb3, 0x9e, 0x7b, 0x65, 0x63, 0xd, 0xf9, 0xbb, 0x48, 0x3b, 0xb3, 0x92, 0x54, 0x42, 0xb1, 0x5, 0x88, 0xf7, 0xc8, 0xcf, 0x9f, 0xfe, 0x1a, 0x8e, 0x14, 0xf4, 0x4e, 0x81, 0x63, 0x87, 0x51, 0x90, 0x28, 0x8, 0x46, 0x42, 0x51, 0x4a, 0x9e, 0x79, 0x43, 0xc5, 0x81, 0x55, 0x6f, 0xbc, 0x34, 0xdc, 0x2b, 0x2e, 0x16, 0xe5, 0x3a, 0xb1, 0xb, 0xb6, 0xca, 0x3, 0x2b, 0xb2, 0xc2, 0xbe, 0xf0, 0x66, 0x71, 0x4f, 0x70, 0x3b, 0x61, 0x14, 0x89, 0x26, 0x71, 0x5d, 0x6c, 0x9f, 0x1e, 0x17, 0x35, 0xae, 0xfa, 0xeb, 0xdc, 0x62, 0xc3, 0x84, 0x2d, 0x77, 0x22, 0xda, 0x98, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char line_edit_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x94, 0xc8, 0x67, 0x6b, 0x60, 0xe6, 0x60, 0x64, 0x80, 0x80, 0xff, 0xc, 0x7f, 0x7f, 0xfc, 0x6a, 0x60, 0x94, 0xfb, 0xc0, 0xce, 0xcf, 0xc2, 0x80, 0x10, 0xfc, 0xc3, 0xf0, 0xf3, 0x23, 0xa3, 0xe2, 0x4f, 0xe, 0x36, 0x54, 0xc1, 0x1f, 0xbf, 0x18, 0x95, 0xbe, 0x73, 0x70, 0xb0, 0x30, 0xc0, 0x1, 0x48, 0xf0, 0x7, 0x85, 0x82, 0x58, 0x2d, 0xc2, 0xe6, 0xa4, 0x4f, 0x20, 0xc7, 0x37, 0x32, 0xb3, 0x23, 0x39, 0xfe, 0xfb, 0xaf, 0x46, 0x0, 0xee, 0x2a, 0x2f, 0xce, 0x4c, 0x47, 0x66, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char mini_checkerboard_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x98, 0xa0, 0xbd, 0x0, 0x0, 0x0, 0x17, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x78, 0x0, 0x5, 0xff, 0xa1, 0x60, 0xa0, 0x4, 0x60, 0xc, 0x98, 0xc4, 0x0, 0x9, 0x0, 0x0, 0x44, 0x81, 0xef, 0x81, 0xc1, 0x26, 0x8e, 0x8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_arrow_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x2, 0xdc, 0x4f, 0xb8, 0x9f, 0x80, 0x45, 0xf8, 0xa1, 0xf6, 0x83, 0x2f, 0xf, 0xbe, 0x3c, 0xd4, 0x46, 0x13, 0x7e, 0xc1, 0xfd, 0xe0, 0xea, 0x83, 0xff, 0x40, 0x78, 0xf5, 0x5, 0x37, 0xaa, 0xfa, 0xf9, 0x40, 0x41, 0x30, 0x7c, 0x38, 0x9f, 0x81, 0x12, 0x80, 0x69, 0x14, 0x61, 0xcb, 0x11, 0xce, 0xc5, 0xe5, 0x41, 0xc2, 0x0, 0x0, 0x95, 0x48, 0x37, 0x91, 0x1f, 0xec, 0x77, 0x5, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3f, 0x3f, 0x5a, 0x5a, 0x5a, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x59, 0x59, 0x59, 0x2a, 0x2a, 0x30, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x27, 0x35, 0x35, 0x35, 0x4a, 0x4a, 0x4a, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x56, 0x56, 0x56, 0x62, 0x62, 0x62, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x48, 0x48, 0x48, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x54, 0x54, 0x54, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x49, 0x49, 0x49, 0x3a, 0x3a, 0x3a, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x39, 0x39, 0x39, 0x47, 0x47, 0x47, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x46, 0x46, 0x46, 0xd3, 0xa7, 0xd4, 0x88, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xec, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0x8e, 0x5, 0x4e, 0x0, 0x31, 0x10, 0x45, 0xff, 0xef, 0x56, 0xc2, 0xe2, 0xee, 0x4e, 0x3c, 0xc8, 0xd, 0xb8, 0x38, 0xa7, 0xc0, 0xdd, 0xdd, 0x5d, 0xbb, 0x94, 0x4c, 0xd2, 0xc1, 0xdf, 0x4a, 0x47, 0x5e, 0x27, 0xc3, 0xc, 0x80, 0x64, 0x24, 0xb8, 0xc7, 0x4f, 0x2c, 0x6b, 0xd5, 0x90, 0xff, 0xdd, 0x23, 0x7e, 0xc1, 0xa2, 0xb6, 0x64, 0xad, 0x26, 0x82, 0xc6, 0xef, 0x45, 0xbc, 0xe3, 0x1, 0x2c, 0x69, 0x9b, 0xc8, 0x7f, 0x5, 0x36, 0x1e, 0x41, 0x84, 0xd6, 0x4, 0x25, 0x90, 0xb7, 0x39, 0x6c, 0x4c, 0x2f, 0xbe, 0x68, 0xdf, 0x13, 0xa1, 0x9e, 0xc8, 0xa4, 0xb7, 0xe7, 0x47, 0x93, 0x63, 0x1b, 0xf9, 0xdc, 0x8, 0x88, 0x60, 0xbf, 0x4, 0xff, 0xd2, 0x56, 0x93, 0xe3, 0xb7, 0xb2, 0xf6, 0x2c, 0x36, 0x1, 0x16, 0xf4, 0x5f, 0x42, 0xc4, 0x17, 0x8f, 0xb5, 0xc0, 0xa5, 0x8, 0x30, 0x5f, 0xc2, 0x5d, 0xcf, 0xc9, 0xd, 0x74, 0x87, 0x8b, 0x5e, 0x56, 0x22, 0x24, 0xf7, 0x6d, 0xc2, 0xd1, 0x80, 0x26, 0x27, 0x5d, 0x5b, 0x67, 0x7d, 0x2f, 0x80, 0x4d, 0xc9, 0x7f, 0x9, 0x63, 0xa7, 0x61, 0x23, 0xc7, 0x74, 0x3d, 0xf, 0xae, 0x41, 0x84, 0x6f, 0x13, 0xe6, 0x26, 0xfa, 0x36, 0x46, 0x73, 0xbc, 0xba, 0x3b, 0xd6, 0xca, 0x8, 0xd0, 0xd6, 0x36, 0x4e, 0x35, 0xeb, 0xad, 0xd7, 0xb0, 0x60, 0x72, 0xdc, 0xda, 0x9c, 0x2, 0x67, 0x76, 0x64, 0x82, 0x99, 0x9b, 0xb6, 0x80, 0xb0, 0x7e, 0x8d, 0xf6, 0x5a, 0xdd, 0xe1, 0xb5, 0xba, 0xba, 0xb1, 0x0, 0xcd, 0xc7, 0x50, 0x23, 0xeb, 0xfb, 0x7f, 0xb4, 0xc8, 0x22, 0x18, 0xdd, 0x0, 0xd5, 0xec, 0x4e, 0x53, 0xc6, 0x18, 0x44, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_disabled_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x5a, 0x5a, 0x5a, 0x3e, 0x3e, 0x3e, 0x2a, 0x2a, 0x30, 0x59, 0x59, 0x59, 0x22, 0x22, 0x27, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x4a, 0x4a, 0x4a, 0x36, 0x36, 0x36, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x62, 0x62, 0x62, 0x56, 0x56, 0x56, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x54, 0x54, 0x54, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x48, 0x48, 0x48, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x3a, 0x3a, 0x3a, 0x49, 0x49, 0x49, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x47, 0x47, 0x47, 0x39, 0x39, 0x39, 0x46, 0x46, 0x46, 0xda, 0x9d, 0x96, 0x5c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0xf, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0xef, 0x50, 0x45, 0x4b, 0x6d, 0xe, 0x95, 0xa6, 0xa5, 0x40, 0xab, 0x15, 0x5, 0xdc, 0xc0, 0x8a, 0xa, 0xb8, 0xa1, 0xef, 0xff, 0xc, 0x86, 0x90, 0x30, 0xc, 0x17, 0xfa, 0x5d, 0x26, 0xff, 0x4c, 0x4e, 0x12, 0x41, 0x98, 0x32, 0x33, 0x1b, 0x98, 0x9b, 0x17, 0x83, 0x84, 0x18, 0xa, 0x47, 0xa2, 0xb1, 0xe9, 0x7d, 0x21, 0x16, 0x58, 0x58, 0x5c, 0x5a, 0x96, 0x24, 0x29, 0x9e, 0x48, 0xa6, 0xd2, 0x2b, 0x51, 0xb2, 0xb4, 0xba, 0x96, 0xc9, 0xca, 0x87, 0x47, 0x8c, 0x9c, 0xc9, 0x1d, 0x9f, 0x30, 0xeb, 0x1b, 0xe9, 0x8, 0x9, 0x36, 0x15, 0x25, 0xaf, 0x9e, 0x6a, 0x8c, 0x8a, 0xa0, 0xa0, 0x8f, 0x9c, 0x15, 0xb7, 0x52, 0x61, 0x12, 0xa8, 0x6, 0x56, 0x4d, 0x2b, 0xcb, 0x98, 0xb8, 0xc4, 0x3, 0x5d, 0x2f, 0x24, 0x43, 0xc3, 0xc0, 0x6, 0xcd, 0x2a, 0xcb, 0x4c, 0xe, 0x4a, 0x95, 0x2a, 0x57, 0x49, 0x88, 0x24, 0x70, 0xc, 0x70, 0xcf, 0xcb, 0x17, 0x8c, 0x5, 0x8e, 0x77, 0xc9, 0x79, 0xf1, 0x20, 0x9, 0x80, 0x4, 0xd6, 0x64, 0x50, 0xbb, 0xe2, 0x6a, 0xd2, 0x30, 0xc0, 0xd7, 0xf5, 0x89, 0x19, 0xb4, 0x46, 0xbe, 0x79, 0xc3, 0x35, 0x69, 0x80, 0x6e, 0x15, 0xb8, 0x33, 0xef, 0x99, 0x7, 0x84, 0x5a, 0x6d, 0xae, 0x45, 0x8f, 0xb0, 0x1f, 0xed, 0x86, 0x3f, 0xbe, 0xa6, 0x6f, 0x3f, 0x75, 0x9e, 0xb9, 0xe, 0x1d, 0xd2, 0x78, 0x79, 0xed, 0x62, 0xf0, 0x19, 0xdc, 0xeb, 0x17, 0xdf, 0xb8, 0x77, 0x7a, 0xcd, 0xed, 0x8f, 0xcf, 0x5e, 0xb7, 0x8e, 0x19, 0xe5, 0xab, 0x3f, 0xf8, 0x66, 0xda, 0x3b, 0xf4, 0xa1, 0x76, 0xf7, 0x90, 0xe3, 0x8e, 0x67, 0x70, 0x1, 0xbc, 0x9f, 0x91, 0xc1, 0xfe, 0x1, 0x7d, 0xea, 0x7f, 0x3f, 0xeb, 0xef, 0xef, 0xfe, 0x5, 0xd6, 0xe3, 0x56, 0x89, 0xd8, 0x62, 0xb6, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_hover_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x5f, 0x5a, 0x6b, 0x2a, 0x2a, 0x30, 0x56, 0x53, 0x64, 0x22, 0x22, 0x27, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x67, 0x63, 0x76, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5a, 0x56, 0x65, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x5b, 0x57, 0x66, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x43, 0x40, 0x4c, 0x54, 0x50, 0x5f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x4f, 0x5f, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0x5f, 0x5a, 0x6c, 0xd3, 0x26, 0x54, 0x35, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe5, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x85, 0x91, 0x43, 0x62, 0xc5, 0x60, 0x18, 0x45, 0xef, 0x8d, 0x51, 0xdb, 0xee, 0x46, 0xca, 0x6d, 0x77, 0x58, 0xce, 0x6b, 0xdb, 0x7c, 0x8a, 0xad, 0xea, 0x44, 0x1f, 0x4e, 0x92, 0x1f, 0x4c, 0x0, 0xe0, 0x9, 0x61, 0xf0, 0x89, 0x32, 0x12, 0xcd, 0xd4, 0x8, 0xef, 0x1f, 0x35, 0x54, 0xa0, 0x68, 0x1a, 0x34, 0x89, 0x8, 0x86, 0xa4, 0xd, 0x57, 0xb4, 0xdf, 0x79, 0x5, 0x89, 0x94, 0xba, 0xf9, 0xb3, 0xc0, 0xce, 0xeb, 0xf0, 0x17, 0x1c, 0xab, 0x11, 0x9, 0x1a, 0xf9, 0x96, 0x84, 0x5d, 0x5e, 0x43, 0x15, 0x7, 0x2e, 0x42, 0x41, 0x51, 0xd3, 0xbe, 0x67, 0xd5, 0x6b, 0x42, 0x3a, 0x38, 0x9b, 0xf5, 0x2e, 0x20, 0xfa, 0x5, 0x33, 0x41, 0x69, 0xf4, 0xeb, 0x49, 0x6c, 0x19, 0xe6, 0xbd, 0xdd, 0xd, 0x48, 0xa0, 0x92, 0xb, 0x36, 0x72, 0x6a, 0x26, 0xf0, 0x14, 0xa, 0x10, 0x72, 0xe1, 0x7d, 0xf4, 0xf6, 0x35, 0x1b, 0xc3, 0xe3, 0x18, 0x9d, 0x50, 0xf0, 0xe4, 0xc2, 0x17, 0xae, 0x27, 0xd3, 0xe4, 0x6e, 0xe8, 0xf8, 0x7e, 0xbc, 0x1, 0x48, 0x9e, 0x57, 0xf8, 0xc5, 0xfc, 0xbd, 0x7a, 0x98, 0xc4, 0x94, 0x47, 0xbf, 0xe4, 0xce, 0x48, 0x8, 0x6e, 0x69, 0x91, 0x63, 0x47, 0x73, 0x49, 0xbc, 0x77, 0x3e, 0xd7, 0x4b, 0x3b, 0x12, 0x36, 0x16, 0xb3, 0x85, 0x6a, 0x68, 0xce, 0x39, 0x62, 0xc6, 0xba, 0x3d, 0x8d, 0xe7, 0x91, 0x20, 0xac, 0xad, 0xa6, 0xc2, 0xe, 0x6, 0xcc, 0x74, 0xc, 0x2d, 0xe7, 0xf9, 0x55, 0x2, 0x28, 0x94, 0x37, 0xab, 0xee, 0xa1, 0xcc, 0xbf, 0xdb, 0xed, 0x3, 0x70, 0xe6, 0x4f, 0x4a, 0xc3, 0xed, 0xed, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_hover_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x5f, 0x5a, 0x6c, 0x42, 0x40, 0x4b, 0x2a, 0x2a, 0x30, 0x5f, 0x5a, 0x6b, 0x22, 0x22, 0x27, 0x56, 0x53, 0x64, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x57, 0x53, 0x63, 0x3e, 0x3c, 0x47, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x67, 0x63, 0x76, 0x5a, 0x56, 0x67, 0x5b, 0x57, 0x68, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x5a, 0x56, 0x65, 0x48, 0x45, 0x51, 0x49, 0x46, 0x52, 0x4d, 0x4a, 0x57, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x5b, 0x57, 0x66, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x54, 0x50, 0x5f, 0x43, 0x40, 0x4c, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x53, 0x4f, 0x5f, 0x42, 0x3f, 0x4c, 0x43, 0x3f, 0x4d, 0x47, 0x43, 0x51, 0x5f, 0x5a, 0x6c, 0x53, 0x4f, 0x5e, 0x53, 0x50, 0x5f, 0x68, 0x6, 0xa3, 0x65, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x15, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0x8b, 0xb, 0x54, 0x51, 0x4b, 0x6d, 0x8f, 0x60, 0xd3, 0x62, 0xc1, 0x56, 0x11, 0x5, 0xdc, 0xd0, 0x5a, 0x14, 0x70, 0x5f, 0xdf, 0xff, 0x1, 0xc, 0x69, 0xc2, 0x30, 0x5c, 0xe8, 0x77, 0x99, 0xfc, 0x33, 0x39, 0x49, 0x38, 0x6e, 0xc8, 0xc8, 0xa8, 0x6f, 0x6c, 0x9c, 0xf7, 0x63, 0x7c, 0x20, 0x18, 0xa, 0x47, 0x86, 0xf7, 0xb9, 0x88, 0x6f, 0x62, 0x72, 0x6a, 0x5a, 0x10, 0x84, 0x68, 0x2c, 0x9e, 0x48, 0xce, 0x84, 0xf1, 0xd2, 0xec, 0x5c, 0x3a, 0x23, 0x6e, 0x6c, 0x52, 0x62, 0x3a, 0xbb, 0xb5, 0xed, 0xd9, 0x99, 0x5f, 0x48, 0x86, 0x70, 0xb0, 0x28, 0x49, 0x39, 0x79, 0x57, 0xa1, 0x64, 0x15, 0xf6, 0xf2, 0x9e, 0xc2, 0xfe, 0x52, 0x22, 0x88, 0x3, 0x59, 0x43, 0xb2, 0x6e, 0x64, 0x28, 0x1d, 0x15, 0x59, 0x90, 0x2f, 0x1c, 0xc4, 0x3, 0xbd, 0xc0, 0x4, 0xc5, 0x28, 0x89, 0x54, 0x16, 0x8a, 0xe5, 0x43, 0xa6, 0x1c, 0xe3, 0x71, 0x60, 0x69, 0x60, 0x1f, 0x95, 0x8e, 0x29, 0x3, 0xac, 0xca, 0x9, 0x53, 0x89, 0xfa, 0x71, 0x0, 0x38, 0x30, 0x6, 0x83, 0xea, 0x29, 0x53, 0x15, 0x7a, 0x1, 0x3a, 0xab, 0xd, 0xcc, 0xa0, 0xd4, 0x73, 0x8d, 0x73, 0xa6, 0x41, 0x2, 0xf5, 0x42, 0x82, 0x4b, 0xfd, 0x8a, 0xba, 0x56, 0xd5, 0xe6, 0xd, 0xd3, 0x24, 0x47, 0x98, 0xb7, 0x66, 0xdd, 0xe9, 0x5f, 0xd3, 0x31, 0xef, 0xdc, 0x16, 0xe3, 0x92, 0x21, 0xb5, 0xfb, 0x87, 0x36, 0x2, 0x87, 0x42, 0x9d, 0xee, 0xe3, 0x13, 0xd5, 0x72, 0xc9, 0x35, 0x97, 0x9f, 0x5f, 0x3a, 0xed, 0x1a, 0xa2, 0xa4, 0xd7, 0xee, 0xdb, 0xbb, 0xe7, 0xe3, 0x33, 0x45, 0x1e, 0x6a, 0x65, 0x55, 0xb5, 0xec, 0xfe, 0xc, 0x36, 0xc0, 0xd7, 0xb7, 0xe7, 0x67, 0x6d, 0x9d, 0x3c, 0xf5, 0xbf, 0x9f, 0xf5, 0xf7, 0x77, 0xff, 0x2, 0xa7, 0xc5, 0x58, 0xc8, 0x6e, 0x59, 0x8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_normal_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x56, 0x52, 0x60, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x52, 0x22, 0x22, 0x27, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x5d, 0x5a, 0x6a, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x46, 0x42, 0x4f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x4e, 0x4b, 0x58, 0x8, 0xd9, 0x10, 0xcb, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xd1, 0x55, 0x5a, 0xc4, 0x30, 0x14, 0x5, 0xe0, 0x73, 0xea, 0x82, 0xbb, 0x3b, 0x6c, 0x80, 0x45, 0xb0, 0x70, 0x5e, 0xb1, 0x37, 0xdc, 0x75, 0xdc, 0x3d, 0x43, 0x2a, 0x5f, 0x3a, 0x7e, 0x6a, 0xc9, 0xbd, 0x7f, 0x9d, 0x2a, 0x0, 0xa4, 0x16, 0xd, 0xaa, 0x18, 0x8e, 0x41, 0x3f, 0x11, 0xd1, 0xbe, 0x52, 0xc7, 0x48, 0xa8, 0xfb, 0x5e, 0x68, 0xd4, 0x24, 0x96, 0x6a, 0xfc, 0xaf, 0x8b, 0x32, 0xbf, 0x61, 0x90, 0xc6, 0x3c, 0x27, 0x3, 0xce, 0xfe, 0x20, 0x2, 0x4b, 0xd2, 0x45, 0x1f, 0x14, 0xd5, 0x78, 0x5e, 0x36, 0x1c, 0x7d, 0xe5, 0x33, 0x2, 0xb3, 0x84, 0x8a, 0xec, 0xd4, 0xeb, 0x9a, 0x1a, 0x1b, 0x82, 0xf5, 0x79, 0x20, 0x2, 0x46, 0x1f, 0x58, 0x8d, 0x95, 0xe4, 0x6a, 0x1d, 0xcf, 0x4f, 0x89, 0x85, 0x10, 0x80, 0x56, 0x1f, 0x8, 0xf4, 0x53, 0xf7, 0x81, 0x6c, 0x4, 0xa0, 0xf5, 0x41, 0x69, 0xeb, 0xb7, 0xd0, 0x7b, 0x86, 0xcc, 0x36, 0xbb, 0x11, 0x90, 0x66, 0x1f, 0x88, 0xef, 0xbd, 0x64, 0x92, 0x5a, 0x7b, 0x4e, 0xed, 0x34, 0x42, 0x20, 0xa5, 0xd5, 0x7, 0x27, 0x69, 0xfb, 0x51, 0x8d, 0x69, 0x6e, 0xd5, 0xcc, 0xb9, 0x18, 0xf4, 0xaf, 0x40, 0x6e, 0x3f, 0x1d, 0xab, 0xf1, 0xdd, 0xc7, 0xf1, 0x12, 0x45, 0xc, 0xce, 0x4f, 0x17, 0x12, 0xd0, 0xb6, 0xc4, 0x87, 0x1a, 0x6f, 0x2f, 0x48, 0x8b, 0xef, 0x31, 0xd0, 0x6e, 0xce, 0xa8, 0xc0, 0x25, 0x56, 0x7d, 0x5, 0x52, 0xed, 0x6e, 0xae, 0x68, 0x0, 0xd4, 0x86, 0x7f, 0x56, 0x43, 0x62, 0x38, 0xc, 0x46, 0x28, 0xba, 0x1, 0x7a, 0xad, 0x4f, 0x59, 0x90, 0xab, 0xbf, 0xa4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_normal_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x56, 0x53, 0x61, 0x3c, 0x3a, 0x45, 0x2a, 0x2a, 0x30, 0x56, 0x52, 0x60, 0x22, 0x22, 0x27, 0x47, 0x44, 0x52, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x47, 0x44, 0x50, 0x33, 0x31, 0x3a, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x5d, 0x5a, 0x6a, 0x51, 0x4f, 0x5d, 0x52, 0x50, 0x5d, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x51, 0x4e, 0x5b, 0x41, 0x3e, 0x49, 0x42, 0x3e, 0x4a, 0x46, 0x42, 0x4e, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x50, 0x4e, 0x5a, 0x40, 0x3e, 0x48, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x47, 0x43, 0x50, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x44, 0x41, 0x4e, 0x36, 0x34, 0x3e, 0x4e, 0x4b, 0x58, 0x44, 0x41, 0x4c, 0x44, 0x42, 0x4d, 0x7d, 0x2e, 0xcf, 0xc5, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x13, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0xe1, 0x2a, 0x5a, 0x56, 0x97, 0xb2, 0xb9, 0x8b, 0xb, 0x54, 0x51, 0x4b, 0x6d, 0xf, 0x68, 0xd3, 0xa2, 0x5, 0x5b, 0x40, 0x14, 0x70, 0x3, 0x2b, 0x2a, 0xe0, 0xbe, 0xbd, 0xff, 0x3, 0x18, 0xd2, 0x84, 0x61, 0xb8, 0xd0, 0xef, 0x32, 0xf9, 0x67, 0x72, 0x92, 0x70, 0xdc, 0x88, 0xb1, 0x71, 0xcf, 0xc4, 0x24, 0xef, 0xc5, 0x78, 0x9f, 0x3f, 0x10, 0xc, 0x8d, 0xee, 0x73, 0x21, 0xcf, 0xd4, 0xf4, 0xcc, 0xac, 0x20, 0x8, 0xe1, 0x48, 0x34, 0x16, 0x9f, 0xb, 0xe2, 0xa5, 0xf9, 0x85, 0x64, 0x4a, 0xdc, 0xda, 0xa6, 0xc4, 0x64, 0x7a, 0x67, 0xd7, 0xb5, 0xb7, 0xb8, 0x14, 0xf, 0xe0, 0x60, 0x59, 0x92, 0x32, 0xf2, 0xbe, 0x42, 0xc9, 0x2a, 0x64, 0x73, 0xae, 0x83, 0xc3, 0x95, 0x98, 0x1f, 0x7, 0xb2, 0x86, 0x64, 0xdd, 0x48, 0x51, 0x3a, 0xca, 0x1f, 0x1d, 0x53, 0xb9, 0x6c, 0xd4, 0xd7, 0xf, 0x4c, 0x50, 0x8c, 0x82, 0x48, 0xa5, 0x21, 0x5f, 0x3c, 0x61, 0x8a, 0x11, 0x1e, 0x7, 0x96, 0x6, 0x76, 0xa9, 0x50, 0xa6, 0xc, 0xb0, 0x2a, 0xa7, 0x4c, 0x25, 0xec, 0xc5, 0x1, 0xe0, 0xc0, 0x18, 0xe, 0xaa, 0x67, 0x4c, 0x55, 0xe8, 0x7, 0xe8, 0xbc, 0x36, 0x34, 0x83, 0x52, 0xcf, 0x34, 0x2e, 0x98, 0x6, 0x9, 0xd4, 0x4b, 0x9, 0xae, 0xf4, 0x6b, 0xea, 0x46, 0x55, 0x9b, 0x2d, 0xa6, 0x49, 0x8e, 0x30, 0x6f, 0xcd, 0xba, 0x33, 0xb8, 0xa6, 0x63, 0xde, 0xb5, 0xef, 0x99, 0x36, 0x19, 0x52, 0x7b, 0x78, 0xec, 0x20, 0x70, 0x28, 0xd4, 0xed, 0x3d, 0x3d, 0x33, 0x2f, 0xe4, 0x9a, 0xab, 0xaf, 0x6f, 0xdd, 0x4e, 0xd, 0x51, 0xd2, 0x7b, 0xef, 0xe3, 0x93, 0x6a, 0x25, 0xc8, 0x43, 0xad, 0xad, 0xab, 0x96, 0x3d, 0x98, 0xc1, 0x6, 0xf8, 0xfa, 0x76, 0xfd, 0x6c, 0x6c, 0x92, 0xa7, 0xfe, 0xf7, 0xb3, 0xfe, 0xfe, 0xee, 0x5f, 0xa1, 0x5f, 0x59, 0xbd, 0x75, 0x41, 0x2b, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_pressed_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x2f, 0x37, 0x46, 0x43, 0x4f, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x44, 0x50, 0x2a, 0x2a, 0x30, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x27, 0x3d, 0x3a, 0x45, 0x56, 0x52, 0x60, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x43, 0x40, 0x4c, 0x42, 0x40, 0x4b, 0x4c, 0x49, 0x56, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x3a, 0x38, 0x41, 0x36, 0x34, 0x3d, 0x44, 0x41, 0x4c, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x44, 0x42, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x50, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x3f, 0x3d, 0x47, 0x4f, 0x4c, 0x59, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x45, 0x42, 0x4d, 0x41, 0x3e, 0x49, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x52, 0x4e, 0x5c, 0x51, 0x4e, 0x5b, 0x5d, 0x59, 0x69, 0x10, 0x9d, 0xe0, 0x3c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xcf, 0x3, 0x62, 0x4, 0x51, 0x10, 0x4, 0xd0, 0xaa, 0x31, 0x62, 0xdb, 0xb8, 0x49, 0x2e, 0x9e, 0x3b, 0xc4, 0xb6, 0x9d, 0xc5, 0x58, 0x1f, 0xc1, 0xd6, 0xe8, 0x77, 0xf7, 0x1b, 0x59, 0x6c, 0x2, 0x20, 0x37, 0xaa, 0xc5, 0x17, 0x7e, 0xc7, 0x62, 0x28, 0x45, 0x75, 0xfe, 0x6c, 0xe1, 0x4f, 0x68, 0x86, 0x41, 0x69, 0x44, 0x51, 0x4b, 0xb1, 0xce, 0xcc, 0xe4, 0x83, 0xd7, 0xb0, 0x48, 0x6b, 0x98, 0xe8, 0x9, 0x38, 0x70, 0x8b, 0xa, 0xcc, 0x12, 0x1a, 0xf0, 0x4d, 0xac, 0x87, 0xf3, 0x96, 0x6f, 0x8e, 0x5f, 0x56, 0xc0, 0x53, 0x20, 0x8f, 0xbf, 0xdb, 0x86, 0x58, 0x5b, 0x9, 0xbf, 0x47, 0x80, 0xa, 0x58, 0x1a, 0x38, 0xad, 0x9, 0x5f, 0xac, 0xe3, 0x20, 0xbc, 0x4b, 0x46, 0x4b, 0x0, 0x3a, 0x1a, 0x24, 0xd0, 0x69, 0x85, 0xc0, 0x63, 0x5, 0x60, 0x68, 0xf0, 0x36, 0x7f, 0xf3, 0xaa, 0xbe, 0xe1, 0x61, 0x81, 0x69, 0x5, 0x72, 0x5b, 0x83, 0xe4, 0x6a, 0x59, 0x16, 0xf7, 0x53, 0x47, 0x77, 0x8b, 0xad, 0x12, 0xe4, 0xb9, 0xa3, 0xc1, 0xe6, 0x83, 0x7b, 0x20, 0xd6, 0xb4, 0xe7, 0xbf, 0xed, 0xe1, 0x1a, 0xd8, 0xfa, 0xdf, 0xb9, 0x70, 0xb8, 0x21, 0xd6, 0xbb, 0x17, 0x1b, 0xe3, 0x4c, 0x6a, 0xb0, 0xbd, 0x25, 0x5, 0x3b, 0x5e, 0x7c, 0x21, 0xc0, 0xc2, 0x68, 0xee, 0xf1, 0xbc, 0x6, 0x46, 0xb1, 0xbd, 0x5e, 0x30, 0x5, 0x27, 0x19, 0x24, 0xb8, 0x61, 0x6e, 0xf8, 0xf5, 0xf7, 0xcd, 0x47, 0x16, 0xa0, 0x18, 0x13, 0x6a, 0x64, 0x7d, 0xff, 0x8f, 0x1e, 0x59, 0x84, 0xa2, 0x1b, 0x0, 0xe5, 0xe0, 0x4e, 0x46, 0x1d, 0x98, 0x92, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char option_button_pressed_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x34, 0x2b, 0x2b, 0x31, 0x46, 0x43, 0x4f, 0x31, 0x2f, 0x38, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x50, 0x22, 0x22, 0x27, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x29, 0x24, 0x24, 0x28, 0x56, 0x52, 0x60, 0x3c, 0x3a, 0x45, 0x2a, 0x2a, 0x30, 0x2c, 0x2c, 0x32, 0x2d, 0x2d, 0x34, 0x2e, 0x2e, 0x35, 0x2f, 0x2f, 0x36, 0x2a, 0x2a, 0x31, 0x4c, 0x49, 0x56, 0x42, 0x40, 0x4b, 0x43, 0x40, 0x4c, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2b, 0x27, 0x27, 0x2d, 0x28, 0x28, 0x2e, 0x29, 0x29, 0x2f, 0x24, 0x24, 0x28, 0x26, 0x26, 0x2b, 0x44, 0x41, 0x4c, 0x36, 0x34, 0x3d, 0x3a, 0x38, 0x41, 0x22, 0x22, 0x26, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x26, 0x26, 0x2c, 0x44, 0x41, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4e, 0x22, 0x22, 0x27, 0x2d, 0x2d, 0x33, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x27, 0x27, 0x2b, 0x29, 0x29, 0x2e, 0x2c, 0x2c, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x20, 0x20, 0x25, 0x24, 0x24, 0x29, 0x24, 0x24, 0x2a, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x20, 0x20, 0x23, 0x29, 0x29, 0x2d, 0x2b, 0x2b, 0x30, 0x28, 0x28, 0x2d, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x1e, 0x1e, 0x22, 0x27, 0x27, 0x2c, 0x22, 0x22, 0x28, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x1d, 0x1d, 0x21, 0x20, 0x20, 0x24, 0x23, 0x23, 0x27, 0x21, 0x21, 0x25, 0x21, 0x21, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x3f, 0x3d, 0x48, 0x50, 0x4d, 0x5a, 0x1e, 0x1e, 0x21, 0x1f, 0x1f, 0x23, 0x1f, 0x1f, 0x24, 0x50, 0x4e, 0x5a, 0x40, 0x3e, 0x48, 0x41, 0x3e, 0x49, 0x45, 0x42, 0x4d, 0x5d, 0x59, 0x69, 0x51, 0x4e, 0x5b, 0x52, 0x4e, 0x5c, 0x49, 0x7e, 0x80, 0x9, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2, 0x7, 0xd, 0x16, 0x1d, 0x22, 0x24, 0x1f, 0x19, 0x11, 0xa, 0x4, 0x1b, 0x2d, 0x3a, 0x43, 0x48, 0x47, 0x46, 0x3f, 0x34, 0x25, 0x15, 0x49, 0xe6, 0xef, 0x77, 0xe6, 0xef, 0xe7, 0xef, 0x4a, 0xe4, 0xef, 0x77, 0x7e, 0xb9, 0x59, 0x66, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x1, 0x14, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x40, 0x3, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x40, 0xc0, 0xc6, 0xc1, 0xc9, 0xc5, 0xcd, 0x83, 0x2e, 0xcf, 0xc0, 0xc3, 0xcc, 0xcb, 0xc7, 0x2f, 0x20, 0x28, 0x28, 0x28, 0x24, 0x2c, 0x22, 0x2a, 0x26, 0xce, 0xd, 0x14, 0x92, 0x90, 0x54, 0x51, 0x55, 0x53, 0xd7, 0x80, 0x2, 0x35, 0x15, 0x4d, 0x2d, 0x6d, 0x8, 0xd0, 0x91, 0x92, 0x16, 0xe3, 0x2, 0x2a, 0x90, 0xd1, 0xd5, 0xd5, 0xd3, 0x37, 0x30, 0x84, 0x2, 0x7d, 0x23, 0x63, 0x13, 0x53, 0x28, 0x30, 0x93, 0x15, 0xe5, 0x4, 0x2a, 0xd0, 0x37, 0xb7, 0xd0, 0xb7, 0xb4, 0x52, 0x85, 0x2, 0x4b, 0xb, 0x6b, 0x1b, 0x5b, 0x18, 0xb0, 0x13, 0xe1, 0x0, 0x29, 0xb0, 0x37, 0x36, 0xb4, 0x72, 0x50, 0x83, 0x2, 0x4d, 0x63, 0x6b, 0x47, 0x27, 0x18, 0x70, 0x14, 0x66, 0x3, 0x2a, 0x70, 0x36, 0x37, 0x76, 0x71, 0x75, 0x70, 0x83, 0x2, 0x2b, 0x63, 0x67, 0x77, 0xf, 0x18, 0x70, 0x17, 0x62, 0x7, 0x2a, 0x30, 0x6, 0x2a, 0xb0, 0x42, 0x56, 0xe0, 0xe9, 0x5, 0x3, 0x9e, 0x82, 0x20, 0x5, 0x16, 0xde, 0x3e, 0x48, 0x6e, 0x30, 0xf4, 0xd5, 0xf3, 0xf3, 0x87, 0x1, 0x3f, 0xb0, 0x2, 0xa3, 0x0, 0x5d, 0xe3, 0x40, 0xcb, 0x20, 0x28, 0x8, 0x36, 0x32, 0xa, 0x9, 0x85, 0x81, 0x10, 0xb0, 0x15, 0xf6, 0x61, 0xf6, 0xbe, 0xe1, 0x70, 0x6f, 0x86, 0xdb, 0x47, 0x44, 0x46, 0xc1, 0x40, 0x24, 0xd8, 0x91, 0xe6, 0xd1, 0x31, 0xb1, 0x16, 0xc6, 0xe1, 0x50, 0x60, 0x11, 0x17, 0x9f, 0x90, 0x8, 0x5, 0x49, 0xc9, 0x60, 0x6f, 0xca, 0xa5, 0xa4, 0xc6, 0xc5, 0xfa, 0x58, 0x40, 0x81, 0x6e, 0x5a, 0x7c, 0x7a, 0x6, 0x4, 0x64, 0x66, 0xc9, 0x83, 0x3, 0x4a, 0x41, 0xd1, 0xc8, 0xd9, 0x5, 0xee, 0x6, 0x17, 0x63, 0xe3, 0xec, 0x1c, 0x8, 0xc8, 0x55, 0x52, 0x6, 0x7, 0x35, 0xc1, 0xc8, 0xc2, 0x1f, 0xdd, 0x0, 0xa5, 0xe, 0x59, 0xe5, 0x7f, 0xe9, 0xa4, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char overbright_indicator_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xcd, 0xcd, 0x4b, 0xe, 0x80, 0x20, 0xc, 0x45, 0xd1, 0x9b, 0xea, 0xbe, 0x40, 0x97, 0x87, 0x8b, 0xae, 0x13, 0x34, 0x88, 0x1f, 0xa, 0x9d, 0xf8, 0x92, 0xe, 0xcf, 0x2d, 0x80, 0xda, 0x4f, 0x14, 0x26, 0x5, 0x49, 0x14, 0x53, 0xcb, 0x42, 0x58, 0xe, 0xbc, 0x51, 0xcd, 0x85, 0x9b, 0x81, 0x16, 0xfe, 0xc, 0x58, 0xf0, 0x6b, 0xc0, 0x8a, 0x1f, 0x3, 0x3d, 0xf8, 0x16, 0xe8, 0xc5, 0x97, 0x40, 0x81, 0x53, 0x9b, 0x55, 0x81, 0x91, 0xcf, 0x67, 0x20, 0xc6, 0x75, 0xe8, 0x73, 0x9e, 0xc, 0x7f, 0xce, 0x73, 0x61, 0x80, 0xd9, 0x83, 0x7f, 0xb0, 0x1d, 0xec, 0x30, 0xf0, 0x78, 0x5a, 0x7, 0xa8, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char panel_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x25, 0x2a, 0x35, 0x32, 0x3b, 0x4a, 0x73, 0x58, 0x4a, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char picker_cursor_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0xb9, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x6d, 0x8f, 0x3d, 0x8a, 0xc2, 0x50, 0x18, 0x45, 0xcf, 0x6b, 0x92, 0x2a, 0x19, 0xd4, 0xa4, 0x72, 0x47, 0x3, 0x42, 0xc0, 0x9f, 0x55, 0x44, 0x17, 0x24, 0x88, 0xee, 0x24, 0x53, 0x4d, 0x7e, 0xa, 0xbf, 0x94, 0xd6, 0x71, 0x5, 0xf2, 0x5e, 0x7f, 0x2d, 0xa2, 0xa2, 0xe0, 0x29, 0xef, 0xb9, 0xcd, 0x1, 0x40, 0xb1, 0x76, 0x6a, 0x14, 0x14, 0xd4, 0x68, 0xab, 0x98, 0x11, 0xcd, 0xd5, 0xef, 0x9b, 0xac, 0x27, 0x10, 0x32, 0x3b, 0xb4, 0x32, 0xcd, 0xc7, 0x77, 0xff, 0xfb, 0xc7, 0xc0, 0x92, 0x84, 0x84, 0x82, 0xcb, 0xa2, 0x92, 0x29, 0x46, 0xbb, 0x7d, 0xc3, 0xc0, 0x94, 0x27, 0x13, 0x86, 0x63, 0xa7, 0x12, 0xb5, 0x59, 0xcf, 0x8a, 0x77, 0xd6, 0xb9, 0xa9, 0x46, 0xde, 0x5, 0x92, 0xf, 0x91, 0x3a, 0x2f, 0xff, 0x4d, 0xfc, 0x38, 0xaf, 0x1b, 0x6a, 0x33, 0xa3, 0xf8, 0x10, 0x9b, 0xfc, 0xac, 0x1a, 0x6d, 0xf, 0x2d, 0x17, 0x26, 0xaf, 0x79, 0xc6, 0xf5, 0xd4, 0xa9, 0x44, 0xb1, 0x6c, 0x51, 0x31, 0xb0, 0x26, 0x25, 0x65, 0xc3, 0xb5, 0xa8, 0x64, 0x8a, 0xc6, 0x40, 0x3b, 0x76, 0xb9, 0xb9, 0xe0, 0x42, 0x7e, 0x3e, 0x75, 0x8f, 0x40, 0x0, 0x45, 0x2a, 0x55, 0xcb, 0xcb, 0xeb, 0x5f, 0xa5, 0x22, 0x80, 0x3b, 0xa0, 0x2c, 0x6c, 0xa1, 0x40, 0x2f, 0xda, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char popup_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xa2, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3b, 0x3b, 0x43, 0x42, 0x42, 0x4b, 0x3e, 0x3e, 0x47, 0x3e, 0x3e, 0x46, 0x41, 0x41, 0x4a, 0x0, 0x0, 0x0, 0x3d, 0x3d, 0x45, 0x3b, 0x3b, 0x43, 0x3a, 0x3a, 0x42, 0x38, 0x38, 0x41, 0x37, 0x37, 0x3e, 0x36, 0x36, 0x3d, 0x35, 0x35, 0x3c, 0x0, 0x0, 0x0, 0x38, 0x38, 0x40, 0x38, 0x38, 0x40, 0x31, 0x31, 0x38, 0x34, 0x34, 0x3b, 0x34, 0x34, 0x3b, 0x39, 0x39, 0x3f, 0x31, 0x31, 0x38, 0x2f, 0x2f, 0x36, 0x2d, 0x2d, 0x33, 0x2c, 0x2c, 0x32, 0x2b, 0x2b, 0x31, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x29, 0x29, 0x30, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x28, 0x28, 0x2d, 0x27, 0x27, 0x2d, 0x27, 0x27, 0x2c, 0x29, 0x29, 0x2e, 0x26, 0x26, 0x2c, 0x36, 0xc6, 0xc8, 0x93, 0x0, 0x0, 0x0, 0x28, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x5, 0x8, 0xa, 0xb, 0x4, 0x13, 0x19, 0x1f, 0x22, 0x23, 0x16, 0x27, 0x35, 0x3f, 0x45, 0x46, 0x94, 0xf5, 0xfa, 0xfb, 0xf5, 0x40, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0x1a, 0xf5, 0xf6, 0x95, 0xfa, 0xfb, 0xf4, 0x94, 0x71, 0xda, 0xac, 0x92, 0x0, 0x0, 0x0, 0x7f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0x8f, 0x35, 0x82, 0xc3, 0x0, 0xc, 0x4, 0x77, 0x24, 0x85, 0xba, 0xe3, 0xff, 0xff, 0xee, 0xca, 0x74, 0x41, 0xdb, 0x32, 0xf3, 0x94, 0x82, 0x85, 0x10, 0x1d, 0x92, 0xb2, 0x3, 0x8e, 0x95, 0x77, 0x93, 0x6c, 0x28, 0xed, 0x15, 0x54, 0x67, 0xa6, 0x41, 0x3e, 0x8, 0x9c, 0xc3, 0xf4, 0xf2, 0xf6, 0x2a, 0x80, 0xf8, 0x44, 0x2d, 0x79, 0x2d, 0x20, 0xe0, 0x2, 0xa8, 0xc3, 0x2e, 0x6f, 0xc, 0x9e, 0x4c, 0x3c, 0x21, 0x4, 0xd8, 0xf0, 0x2, 0x28, 0x24, 0xcd, 0x3, 0xa9, 0x19, 0x64, 0xce, 0x83, 0x4c, 0x45, 0xe6, 0x69, 0x1a, 0xd8, 0xe9, 0x99, 0x96, 0x7f, 0x77, 0x37, 0x59, 0x83, 0xcc, 0xef, 0x7f, 0x89, 0x1f, 0x8e, 0xbf, 0x95, 0xd3, 0x1d, 0xf0, 0xff, 0x7a, 0x63, 0x7e, 0x86, 0xcb, 0x73, 0x8c, 0x5e, 0xee, 0xca, 0xb1, 0xad, 0x5f, 0x3, 0xaf, 0xdb, 0x49, 0x94, 0x4b, 0x90, 0x40, 0xdf, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char popup_bg_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x78, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x0, 0xff, 0x67, 0x7a, 0x85, 0x66, 0x7a, 0x86, 0x68, 0x7b, 0x86, 0x57, 0x51, 0x51, 0x4c, 0x42, 0x40, 0x4d, 0x43, 0x41, 0x56, 0x4c, 0x4b, 0x4d, 0x44, 0x41, 0x4e, 0x44, 0x42, 0x4f, 0x45, 0x43, 0x67, 0x7b, 0x87, 0x4f, 0x44, 0x43, 0x50, 0x45, 0x44, 0x52, 0x46, 0x44, 0x51, 0x46, 0x45, 0x4b, 0x40, 0x3f, 0x51, 0x47, 0x45, 0x52, 0x48, 0x46, 0x53, 0x48, 0x47, 0x4b, 0x41, 0x3f, 0x54, 0x49, 0x46, 0x55, 0x4a, 0x47, 0x55, 0x49, 0x47, 0x68, 0x7c, 0x88, 0x4a, 0x40, 0x3e, 0x55, 0x4b, 0x49, 0x56, 0x4d, 0x4b, 0x53, 0x49, 0x47, 0x50, 0x46, 0x44, 0x4a, 0x41, 0x3e, 0x48, 0x3e, 0x3c, 0x4b, 0x42, 0x3f, 0x49, 0x3f, 0x3d, 0x46, 0x3d, 0x3c, 0x47, 0x3d, 0x3b, 0x47, 0x3e, 0x3b, 0x49, 0x40, 0x3d, 0x45, 0x3c, 0x3b, 0x46, 0x3c, 0x3a, 0xdd, 0x63, 0x56, 0x8d, 0x0, 0x0, 0x0, 0xad, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x2d, 0x88, 0x35, 0x42, 0x4, 0x0, 0xc, 0x4, 0x77, 0x93, 0x9c, 0xbb, 0xf4, 0xc8, 0xff, 0xdf, 0x84, 0xb5, 0xb8, 0x5b, 0x84, 0x0, 0x37, 0xc5, 0xca, 0x10, 0xd, 0xc9, 0xce, 0xaa, 0xea, 0x24, 0x40, 0xca, 0x41, 0x64, 0x2b, 0x5, 0x87, 0x34, 0xa8, 0x88, 0x54, 0xef, 0x82, 0x80, 0x89, 0x34, 0x36, 0x96, 0xe0, 0x14, 0xa4, 0x12, 0xa6, 0x24, 0x18, 0xe1, 0xa8, 0x50, 0x59, 0x7c, 0x73, 0x30, 0x50, 0x55, 0x4a, 0x31, 0xd7, 0xb4, 0x89, 0xa1, 0x51, 0xb2, 0x9c, 0x1, 0x2c, 0x4, 0x83, 0x15, 0x12, 0x30, 0xab, 0xe9, 0x5a, 0x1, 0xb4, 0x40, 0xa1, 0x29, 0xbe, 0x75, 0xe, 0x5a, 0x70, 0xbe, 0x2a, 0xff, 0x12, 0xf1, 0xef, 0x1b, 0x5f, 0x8d, 0x5b, 0x68, 0xd, 0xdc, 0xe3, 0xf1, 0x71, 0x16, 0x3e, 0x5b, 0xc8, 0x33, 0xa9, 0xc7, 0xbc, 0x7f, 0xa4, 0x22, 0x6a, 0xb5, 0x90, 0xcb, 0xb2, 0x1a, 0x25, 0x67, 0x8b, 0x8f, 0x6f, 0xf8, 0x64, 0xa8, 0x35, 0x7a, 0x25, 0xa8, 0xa7, 0x1, 0x38, 0xc, 0xcc, 0xab, 0x4c, 0x5, 0xea, 0xe3, 0x76, 0x2f, 0x54, 0x93, 0xf3, 0xf, 0x4f, 0x10, 0x8d, 0x4c, 0x16, 0x9d, 0xcf, 0x1f, 0xd1, 0xf9, 0x3, 0x34, 0xc8, 0x4a, 0xb4, 0x1d, 0xb, 0xcd, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char popup_window_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x48, 0x8, 0x3, 0x0, 0x0, 0x0, 0xb7, 0x21, 0x97, 0x38, 0x0, 0x0, 0x1, 0x71, 0x69, 0x43, 0x43, 0x50, 0x69, 0x63, 0x63, 0x0, 0x0, 0x28, 0x91, 0x75, 0x91, 0x3d, 0x4b, 0xc3, 0x50, 0x14, 0x86, 0xdf, 0xb6, 0x96, 0x8a, 0xad, 0x74, 0xd0, 0x41, 0xa4, 0x43, 0x86, 0x2a, 0xe, 0x2d, 0x14, 0x5, 0x71, 0xd4, 0x3a, 0x74, 0x29, 0x52, 0x6a, 0x5, 0xab, 0x2e, 0xc9, 0x6d, 0xd2, 0xa, 0x49, 0x1a, 0x6e, 0x52, 0xa4, 0xb8, 0xa, 0x2e, 0xe, 0x5, 0x7, 0xd1, 0xc5, 0xaf, 0xc1, 0x7f, 0xa0, 0xab, 0xe0, 0xaa, 0x20, 0x8, 0x8a, 0x20, 0xe2, 0xe2, 0x1f, 0xf0, 0x6b, 0x91, 0x12, 0xcf, 0x6d, 0xa, 0x2d, 0xd2, 0x9e, 0x70, 0x73, 0x1e, 0xde, 0x7b, 0xde, 0xc3, 0xbd, 0xe7, 0x2, 0xfe, 0xac, 0xce, 0xc, 0x7b, 0x20, 0x5, 0x18, 0xa6, 0xc3, 0xf3, 0x99, 0xb4, 0xb4, 0x5a, 0x5c, 0x93, 0x42, 0xef, 0x8, 0x21, 0x6, 0x1f, 0x82, 0x8, 0xcb, 0xcc, 0xb6, 0x16, 0x72, 0xb9, 0x2c, 0xfa, 0xc6, 0xcf, 0x23, 0x55, 0x52, 0x3c, 0x24, 0x45, 0xaf, 0xfe, 0x75, 0x3d, 0x23, 0x5c, 0x52, 0x6d, 0x6, 0xf8, 0x6, 0x89, 0x67, 0x99, 0xc5, 0x1d, 0xe2, 0x79, 0xe2, 0xec, 0x96, 0x63, 0x9, 0xde, 0x23, 0x1e, 0x65, 0x15, 0xb9, 0x44, 0x7c, 0x42, 0x9c, 0xe0, 0x74, 0x40, 0xe2, 0x5b, 0xa1, 0x2b, 0x1e, 0xbf, 0x9, 0x2e, 0x7b, 0xfc, 0x25, 0x98, 0x17, 0xf2, 0x8b, 0x80, 0x5f, 0xf4, 0x94, 0xca, 0x5d, 0xac, 0x74, 0x31, 0xab, 0x70, 0x83, 0x78, 0x8a, 0x38, 0x6e, 0xe8, 0x35, 0xd6, 0x3e, 0x8f, 0xb8, 0x49, 0x44, 0x35, 0x57, 0x96, 0x29, 0x8f, 0xd3, 0x8a, 0xc1, 0x46, 0x1e, 0x19, 0xa4, 0x21, 0x41, 0x41, 0xd, 0x9b, 0xd0, 0xe1, 0x20, 0x49, 0xd9, 0xa4, 0x99, 0xf5, 0xf6, 0xa5, 0x5a, 0xbe, 0x25, 0x54, 0xc9, 0xc3, 0xe8, 0x6f, 0xa1, 0xe, 0x4e, 0x8e, 0x32, 0x2a, 0xe4, 0x4d, 0x90, 0x5a, 0xa3, 0xae, 0x2a, 0x65, 0x8d, 0x74, 0x95, 0x3e, 0x1d, 0x75, 0x31, 0xf7, 0xff, 0xf3, 0xb4, 0xb5, 0x99, 0x69, 0xaf, 0x7b, 0x24, 0xd, 0x4, 0x5f, 0x5d, 0xf7, 0x73, 0x2, 0x8, 0xed, 0x3, 0xcd, 0x86, 0xeb, 0xfe, 0x9e, 0xba, 0x6e, 0xf3, 0xc, 0x8, 0xbc, 0x0, 0xd7, 0x66, 0xc7, 0x5f, 0xa5, 0x39, 0xcd, 0x7d, 0x93, 0xde, 0xe8, 0x68, 0xf1, 0x63, 0x20, 0xba, 0x3, 0x5c, 0xde, 0x74, 0x34, 0xe5, 0x0, 0xb8, 0xda, 0x5, 0xc6, 0x9e, 0x2d, 0x99, 0xcb, 0x2d, 0x29, 0x40, 0xcb, 0xaf, 0x69, 0xc0, 0xc7, 0x5, 0x30, 0x5c, 0x4, 0x46, 0xee, 0x81, 0xa1, 0x75, 0x6f, 0x56, 0xed, 0x7d, 0x9c, 0x3f, 0x1, 0x85, 0x6d, 0x7a, 0xa2, 0x3b, 0xe0, 0xf0, 0x8, 0x98, 0xa4, 0xfa, 0xe8, 0xc6, 0x1f, 0x7b, 0xde, 0x67, 0xcd, 0xda, 0x7, 0x3a, 0xfb, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x12, 0x0, 0x0, 0xb, 0x12, 0x1, 0xd2, 0xdd, 0x7e, 0xfc, 0x0, 0x0, 0x0, 0xa8, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe8, 0xe5, 0xf1, 0x20, 0x1e, 0x23, 0x21, 0x1f, 0x24, 0x21, 0x1f, 0x24, 0x22, 0x20, 0x25, 0x1d, 0x1a, 0x20, 0x1d, 0x1d, 0x20, 0x1d, 0x1d, 0x22, 0x20, 0x1d, 0x22, 0x20, 0x1d, 0x25, 0x20, 0x20, 0x25, 0x22, 0x20, 0x25, 0xcf, 0xcd, 0xd7, 0xd0, 0xcd, 0xd7, 0xd1, 0xce, 0xd8, 0xd3, 0xd0, 0xda, 0xd4, 0xd1, 0xdb, 0xd5, 0xd3, 0xdc, 0xd6, 0xd4, 0xdd, 0xd7, 0xd6, 0xdf, 0xd9, 0xd7, 0xdf, 0xda, 0xd8, 0xe0, 0xdc, 0xda, 0xe2, 0xdd, 0xdb, 0xe3, 0xde, 0xdc, 0xe4, 0xdf, 0xdd, 0xe5, 0xe0, 0xde, 0xe6, 0xe1, 0xdf, 0xe7, 0xe2, 0xdf, 0xeb, 0xe3, 0xe1, 0xe8, 0xe5, 0xe2, 0xeb, 0xe8, 0xe5, 0xf1, 0xe8, 0x4e, 0x48, 0xd9, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x3, 0x9, 0xc, 0xf, 0x15, 0x1d, 0x20, 0x22, 0x25, 0x30, 0x3a, 0x44, 0x52, 0x57, 0x59, 0x5b, 0x5e, 0x60, 0x7e, 0x92, 0x9e, 0x9f, 0xa1, 0xa2, 0xb8, 0xba, 0xbd, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xf5, 0xb3, 0x97, 0xfb, 0x0, 0x0, 0x0, 0xea, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x96, 0x31, 0xe, 0x83, 0x30, 0xc, 0x45, 0x6d, 0x93, 0xc2, 0xda, 0xb5, 0x13, 0xf7, 0x3f, 0x4a, 0xef, 0xd0, 0xa5, 0xc7, 0x28, 0x10, 0xff, 0xe, 0x4, 0x35, 0x88, 0xc6, 0x25, 0x48, 0x34, 0x2a, 0x6a, 0x16, 0x86, 0x3c, 0x7d, 0xbe, 0xfd, 0x49, 0x30, 0x51, 0xe1, 0xc5, 0xe1, 0xc1, 0xc2, 0xcc, 0xbc, 0xd8, 0x6, 0x0, 0x5, 0x22, 0x94, 0xa5, 0x72, 0xc2, 0x22, 0xb, 0x54, 0x15, 0x3a, 0x78, 0x5, 0x11, 0x91, 0x1b, 0xd1, 0xea, 0xd4, 0xf0, 0x1b, 0x59, 0x0, 0xc0, 0x83, 0x10, 0xa1, 0xe2, 0x9a, 0xba, 0x4d, 0x58, 0xbc, 0x13, 0x54, 0x23, 0x55, 0xe1, 0x36, 0x38, 0x5a, 0x14, 0xd3, 0xde, 0x64, 0x7c, 0x59, 0x40, 0x99, 0xc9, 0x27, 0x54, 0xdd, 0xe4, 0x4b, 0x26, 0x55, 0xa3, 0x49, 0x73, 0x55, 0x61, 0xc2, 0x3a, 0x94, 0xc8, 0x42, 0x27, 0x27, 0xaf, 0xc6, 0x7c, 0x4a, 0xcb, 0x85, 0xaa, 0xea, 0x34, 0x5a, 0xf7, 0xcc, 0xd8, 0xa0, 0x7a, 0x5c, 0x34, 0x23, 0x82, 0x1d, 0xd2, 0xda, 0x11, 0x3d, 0xaf, 0x45, 0xdd, 0x35, 0x89, 0x5c, 0x6, 0x1f, 0x9d, 0x2, 0xfb, 0xaa, 0x98, 0x1d, 0x98, 0x2, 0x68, 0x13, 0x32, 0x90, 0xf5, 0xd7, 0xdb, 0x6, 0xd4, 0x8, 0x0, 0xf4, 0xd, 0x3, 0xc5, 0xcb, 0xfa, 0xa5, 0xe, 0x1c, 0x34, 0xad, 0xff, 0xe7, 0x72, 0x44, 0x14, 0x40, 0x97, 0x46, 0x3a, 0x8c, 0x63, 0xcd, 0x9e, 0x69, 0x41, 0x2d, 0x34, 0x6c, 0xe6, 0xa3, 0xb0, 0x50, 0x64, 0xab, 0x86, 0x3f, 0x82, 0xb3, 0xc6, 0xc7, 0x7e, 0x88, 0x66, 0x42, 0x78, 0x82, 0x31, 0x94, 0x22, 0x77, 0xd4, 0x2d, 0xbe, 0x9e, 0x25, 0x9d, 0x7b, 0xb0, 0x59, 0x6d, 0x9f, 0xd7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char progress_bar_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0x43, 0x65, 0x7d, 0x95, 0x0, 0x0, 0x0, 0xc, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xb7, 0x4a, 0xbe, 0x33, 0x0, 0x0, 0x0, 0x35, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x64, 0x54, 0x52, 0x64, 0x60, 0x60, 0x78, 0x77, 0x8f, 0x51, 0x34, 0x8, 0xcc, 0xb8, 0xcd, 0xa8, 0xd9, 0x4, 0x66, 0xdc, 0x60, 0x74, 0x9f, 0xe, 0x66, 0xb4, 0x33, 0x7a, 0xb4, 0x1b, 0x0, 0x19, 0x7f, 0x3b, 0x28, 0x64, 0xc0, 0xd, 0x84, 0x5b, 0x1, 0xb7, 0x14, 0xee, 0xc, 0x0, 0xcf, 0x9d, 0x26, 0xff, 0xba, 0xcb, 0x90, 0x39, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char progress_fill_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0x4c, 0xc0, 0xc1, 0xc2, 0xf8, 0xa5, 0xfa, 0x7f, 0x8, 0x34, 0x7e, 0xe9, 0x60, 0x81, 0xa1, 0xc0, 0xf8, 0xa5, 0xca, 0x17, 0x85, 0xff, 0x10, 0xa8, 0xf2, 0xc5, 0xf8, 0x25, 0x86, 0x2, 0x75, 0xa0, 0x4, 0x1c, 0x2, 0x4d, 0xa1, 0xbf, 0x2, 0x4c, 0x47, 0x12, 0xf6, 0xe6, 0x20, 0x2, 0x0, 0x78, 0x21, 0x45, 0x61, 0x7f, 0xe2, 0xad, 0xaf, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char radio_checked_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x42, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xd3, 0xd3, 0xd3, 0xa2, 0xa2, 0xa2, 0x79, 0x79, 0x79, 0x73, 0x73, 0x73, 0x1c, 0x1c, 0x1c, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0xd1, 0xa7, 0xf5, 0xaa, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x63, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0x4f, 0x55, 0x2, 0x43, 0x31, 0x8, 0x4b, 0xe8, 0x6a, 0xf7, 0xbf, 0xeb, 0xc, 0x9b, 0x6b, 0x1f, 0x9f, 0xc4, 0x89, 0xbf, 0xbb, 0x3f, 0x2a, 0x49, 0x64, 0xa6, 0x3e, 0x1e, 0x1c, 0x7c, 0x3e, 0xf2, 0x14, 0xb7, 0xc7, 0xac, 0xf1, 0x10, 0xa6, 0xe8, 0x1, 0x44, 0xad, 0x42, 0xb9, 0x33, 0x22, 0x43, 0x95, 0x68, 0x55, 0xa4, 0xdc, 0x1f, 0x1e, 0xa1, 0x67, 0xa2, 0x57, 0x96, 0x22, 0x0, 0xc2, 0x3d, 0xf5, 0x44, 0x8c, 0x8a, 0x5d, 0x21, 0x80, 0x74, 0x83, 0x1e, 0x97, 0xc7, 0x22, 0x59, 0x4c, 0xd7, 0xd8, 0xb5, 0x18, 0x4a, 0x7b, 0x57, 0x57, 0xdb, 0x1a, 0xf7, 0x77, 0x17, 0x3a, 0x56, 0x4e, 0x11, 0x6f, 0x82, 0x20, 0xde, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char radio_checked_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x45, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xd3, 0xd3, 0xd3, 0xa2, 0xa2, 0xa2, 0x79, 0x79, 0x79, 0x73, 0x73, 0x73, 0x1c, 0x1c, 0x1c, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x83, 0xac, 0xe9, 0xaf, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, 0x3f, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0x20, 0xe, 0xf0, 0x8, 0x2, 0x1, 0xf, 0x9c, 0x2b, 0x2c, 0x8, 0x5, 0xc2, 0x50, 0x1, 0xb0, 0x34, 0x58, 0x11, 0x5c, 0xbd, 0x10, 0x3f, 0x10, 0x8, 0xc1, 0x74, 0x1, 0x65, 0xf8, 0xc1, 0x0, 0xa6, 0x4, 0x28, 0x1, 0x11, 0xe0, 0xc1, 0x2d, 0x80, 0xa6, 0x5, 0xc3, 0x50, 0xc, 0x6b, 0x31, 0x1d, 0x86, 0xe1, 0x74, 0xfc, 0x0, 0x0, 0x1b, 0xc, 0x7, 0xb9, 0xa, 0x5e, 0x2e, 0x12, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char radio_unchecked_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0x2b, 0x6e, 0xf2, 0xbf, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x49, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x2, 0x61, 0x15, 0xed, 0xa9, 0x20, 0x5a, 0x72, 0xf5, 0x99, 0x33, 0xbb, 0x26, 0x1, 0x19, 0x73, 0xcf, 0x0, 0xc1, 0x4d, 0x6, 0x6, 0xd6, 0x35, 0x20, 0xc6, 0xa9, 0x0, 0x6, 0xb6, 0x3d, 0x20, 0xc6, 0xe9, 0x4, 0x6, 0xf6, 0x33, 0x60, 0x50, 0xc0, 0xc0, 0x1, 0x61, 0x34, 0xc0, 0x19, 0x70, 0x29, 0xb8, 0x62, 0xb8, 0x76, 0x84, 0x81, 0xc, 0x96, 0x20, 0x2b, 0xa6, 0xc0, 0x2d, 0x45, 0x0, 0x0, 0x37, 0xca, 0x3d, 0x81, 0xb4, 0x84, 0xb6, 0x80, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char radio_unchecked_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0xf, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x54, 0x54, 0x54, 0xad, 0xad, 0xad, 0x80, 0x80, 0x81, 0x64, 0x64, 0x64, 0xf4, 0x17, 0x36, 0x28, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1b, 0xfc, 0xf6, 0x4, 0xd4, 0x0, 0x0, 0x0, 0x40, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x7d, 0x8d, 0xb1, 0x11, 0x80, 0x40, 0xc, 0xc3, 0x94, 0xd, 0x12, 0x60, 0x1, 0xc4, 0x4, 0xb0, 0x1, 0xfb, 0x2f, 0x45, 0xe5, 0x3b, 0xaa, 0x57, 0xe5, 0xc2, 0xb6, 0x20, 0xc0, 0xcc, 0xc, 0x40, 0xed, 0x7a, 0x37, 0x70, 0xa8, 0xbe, 0x50, 0x9b, 0xea, 0xd9, 0xd4, 0xa3, 0x7a, 0x35, 0xa5, 0xaa, 0xeb, 0x90, 0x72, 0xe6, 0x39, 0xfc, 0x2b, 0x22, 0xd, 0x1f, 0xe7, 0x64, 0xa, 0x5d, 0x6c, 0xfe, 0xc1, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x45, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x40, 0x3e, 0x4a, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x20, 0x20, 0x24, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1e, 0x1e, 0x23, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x14, 0xee, 0x69, 0x20, 0x0, 0x0, 0x0, 0x11, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0x19, 0x40, 0x5d, 0x66, 0x28, 0x93, 0xf0, 0xfc, 0x94, 0xfc, 0xfd, 0x67, 0x1a, 0x96, 0x95, 0x1c, 0xf0, 0x43, 0x52, 0x0, 0x0, 0x0, 0x55, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x55, 0x8e, 0x45, 0x2, 0x80, 0x50, 0x10, 0x42, 0xc1, 0xee, 0xfb, 0x5f, 0xd4, 0xd6, 0xdf, 0xfd, 0x36, 0xd3, 0x3, 0x4, 0xd, 0x90, 0x6, 0xb2, 0x25, 0x39, 0xe0, 0xd2, 0xf9, 0xcb, 0x6a, 0x60, 0x6f, 0x27, 0xb7, 0xbc, 0x58, 0xb7, 0x53, 0x4d, 0x0, 0xf2, 0x3f, 0x5e, 0x36, 0x43, 0x5f, 0xc3, 0xf0, 0xdf, 0x17, 0xd7, 0xa6, 0xae, 0x60, 0x10, 0xff, 0x57, 0x16, 0xc5, 0x5a, 0xf1, 0x60, 0xe3, 0xe7, 0x5f, 0x37, 0x46, 0x74, 0xba, 0x9a, 0x16, 0xef, 0x37, 0x1c, 0x6f, 0x61, 0x47, 0x1, 0xa5, 0xc7, 0x32, 0x47, 0x38, 0x12, 0x92, 0xb1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_button_left_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0x59, 0x59, 0x59, 0x8e, 0x47, 0x76, 0xf1, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x19, 0x33, 0xa1, 0x8c, 0xae, 0x55, 0x50, 0xc6, 0x2e, 0x28, 0xa3, 0x7b, 0xf7, 0x6e, 0x8, 0xa3, 0xe7, 0xcc, 0x19, 0xa8, 0x14, 0x9c, 0xd1, 0x7b, 0x17, 0xa6, 0xfd, 0x1d, 0x86, 0x81, 0x60, 0x6, 0xdc, 0x52, 0x43, 0x88, 0x33, 0x44, 0x0, 0xcc, 0x4e, 0x3f, 0xd1, 0x4, 0x90, 0xbf, 0x60, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_button_left_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0x58, 0xf9, 0x63, 0x6a, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x42, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x19, 0x33, 0xa1, 0x8c, 0xae, 0x55, 0x50, 0xc6, 0x9e, 0x3d, 0x10, 0x46, 0xf7, 0xee, 0xdb, 0x10, 0x46, 0xef, 0xdd, 0xbb, 0x50, 0xa9, 0x77, 0xef, 0xa0, 0x8c, 0xfe, 0x7f, 0x30, 0xed, 0xff, 0x81, 0xc, 0x4c, 0x93, 0x11, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0xfe, 0x97, 0x40, 0xa0, 0xa6, 0x84, 0xb1, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_button_right_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0x59, 0x59, 0x59, 0x8e, 0x47, 0x76, 0xf1, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x40, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x18, 0x33, 0x61, 0x8c, 0x59, 0x2b, 0xa0, 0x8c, 0x5d, 0xab, 0xa0, 0x8c, 0xdd, 0xbb, 0x77, 0x40, 0x18, 0x67, 0xce, 0x9c, 0x80, 0x31, 0xa0, 0x52, 0x77, 0x6f, 0x40, 0x19, 0xef, 0x30, 0xc, 0x84, 0x30, 0xe0, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0xfd, 0x36, 0x40, 0x93, 0xf1, 0x83, 0x5f, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_button_right_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0x2e, 0x3d, 0xb1, 0x1e, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x49, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0xc8, 0x31, 0x16, 0x2, 0x20, 0x10, 0x43, 0xc1, 0xe4, 0x83, 0xdc, 0xff, 0xb8, 0x88, 0xf, 0x57, 0xb, 0x8b, 0x80, 0x53, 0xe, 0xf2, 0x23, 0x18, 0xc6, 0x68, 0x61, 0x74, 0xca, 0xa, 0x2e, 0x74, 0xf9, 0x85, 0xfd, 0x17, 0x5c, 0x81, 0xfb, 0x11, 0x2a, 0xaa, 0x65, 0x80, 0x20, 0xc3, 0x5f, 0xaf, 0x2b, 0x96, 0xce, 0x78, 0xea, 0x88, 0x39, 0x95, 0x91, 0x70, 0x29, 0x94, 0xd9, 0x6b, 0x87, 0xf5, 0xfe, 0x0, 0xc6, 0xa7, 0x1b, 0x66, 0x7b, 0x42, 0xf1, 0x14, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_grabber_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x5d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x5b, 0x59, 0x61, 0x5b, 0x59, 0x61, 0x5a, 0x58, 0x60, 0x59, 0x57, 0x5f, 0x5a, 0x58, 0x60, 0x5a, 0x58, 0x60, 0x57, 0x56, 0x5e, 0x58, 0x56, 0x5e, 0x56, 0x55, 0x5d, 0x57, 0x55, 0x5d, 0x57, 0x55, 0x5d, 0x55, 0x53, 0x5b, 0x55, 0x53, 0x5b, 0x54, 0x53, 0x5b, 0x55, 0x54, 0x5c, 0x54, 0x52, 0x5a, 0x55, 0x53, 0x5b, 0x5a, 0x58, 0x60, 0x56, 0x54, 0x5c, 0x54, 0x53, 0x5a, 0x55, 0x53, 0x5b, 0x53, 0x51, 0x59, 0x52, 0x51, 0x59, 0x52, 0x50, 0x58, 0x51, 0x50, 0x58, 0x51, 0x4f, 0x57, 0x50, 0x4e, 0x56, 0x4f, 0x4d, 0x55, 0x50, 0x4f, 0x57, 0x54, 0x52, 0x5a, 0xae, 0x55, 0xff, 0xf7, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2c, 0xb8, 0xf4, 0x2e, 0xf2, 0xb8, 0xf4, 0xf5, 0xf4, 0xf5, 0xb8, 0x2f, 0xf2, 0x2e, 0xb8, 0xf4, 0xb8, 0x66, 0xf6, 0xf7, 0x12, 0x0, 0x0, 0x0, 0x48, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x64, 0xc7, 0xb1, 0x11, 0x80, 0x40, 0x8, 0x45, 0x41, 0xff, 0x83, 0x2, 0xe, 0xfb, 0x2f, 0x13, 0xe2, 0xf3, 0x6, 0x12, 0x1d, 0x37, 0x5b, 0xae, 0x97, 0x5f, 0x84, 0xdd, 0x68, 0xe2, 0x1e, 0x41, 0xb8, 0x77, 0x58, 0x6, 0xb6, 0xe8, 0x88, 0xd1, 0xe1, 0x93, 0xad, 0x63, 0xab, 0xa3, 0x3a, 0xa3, 0x26, 0xa9, 0x4a, 0x52, 0xf9, 0xc, 0x62, 0xcf, 0xa7, 0x8f, 0x1f, 0x1e, 0xbd, 0xff, 0x84, 0xee, 0x2, 0x0, 0x54, 0x76, 0x10, 0x19, 0x1e, 0xd7, 0x1d, 0x9b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_grabber_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x69, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x97, 0xd0, 0xdf, 0x92, 0xcb, 0xdc, 0x84, 0xbb, 0xd4, 0x92, 0xca, 0xdc, 0x95, 0xd0, 0xdd, 0x83, 0xbb, 0xd3, 0x8b, 0xc8, 0xd7, 0x79, 0xb5, 0xcb, 0x78, 0xb4, 0xca, 0x73, 0xb0, 0xc7, 0x73, 0xb0, 0xc7, 0x7b, 0xc0, 0xcf, 0x79, 0xc5, 0xd1, 0x6b, 0xae, 0xc1, 0x75, 0xc6, 0xcf, 0x70, 0xbc, 0xca, 0x64, 0xa6, 0xbc, 0x71, 0xbc, 0xc9, 0x82, 0xba, 0xd4, 0x6a, 0xa2, 0xc6, 0x62, 0x9a, 0xc2, 0x61, 0x9a, 0xc1, 0x68, 0x9f, 0xc2, 0x5d, 0x92, 0xbb, 0x5c, 0x92, 0xb8, 0x58, 0x8d, 0xb6, 0x59, 0x8e, 0xb3, 0x56, 0x89, 0xb0, 0x5c, 0x91, 0xb2, 0x53, 0x84, 0xa9, 0x58, 0x8f, 0xae, 0x54, 0x83, 0xa4, 0x57, 0x8e, 0xad, 0x64, 0xa5, 0xba, 0x17, 0x3b, 0xfc, 0x67, 0x0, 0x0, 0x0, 0x13, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x25, 0xad, 0xf1, 0xad, 0x27, 0xef, 0xad, 0xf1, 0xf3, 0xf1, 0xf3, 0xad, 0x28, 0xef, 0x27, 0xad, 0xf2, 0xad, 0xcd, 0x8a, 0x27, 0xfe, 0x0, 0x0, 0x0, 0x46, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x7d, 0x8a, 0x25, 0x2, 0xc4, 0x40, 0x0, 0x3, 0x27, 0x39, 0x52, 0x57, 0xfa, 0xff, 0x1f, 0x17, 0x54, 0x19, 0x6d, 0x61, 0x4c, 0x90, 0x5b, 0x5e, 0x80, 0xec, 0x66, 0x58, 0x76, 0x3, 0x1f, 0x15, 0xd2, 0x9c, 0x97, 0x61, 0xf, 0xbf, 0x4a, 0xc0, 0x12, 0x3b, 0xde, 0xfb, 0xeb, 0x8, 0x66, 0xf, 0xbe, 0x8, 0x2, 0x83, 0x92, 0xec, 0x57, 0x12, 0x8, 0x28, 0xa5, 0x3a, 0x4e, 0x89, 0x7, 0x56, 0x51, 0x36, 0x11, 0x22, 0x4, 0xb, 0xcc, 0xf7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char scroll_grabber_pressed_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x3a, 0xd8, 0xcf, 0xb2, 0xc1, 0x7d, 0xc3, 0x14, 0x20, 0x74, 0xdf, 0xcf, 0x82, 0x22, 0xb1, 0xc1, 0x7d, 0xfd, 0x2e, 0x8, 0xdc, 0xe0, 0x8e, 0x2a, 0x31, 0x5, 0x2e, 0x31, 0x85, 0x90, 0x4, 0xa6, 0x51, 0xb8, 0x2d, 0xa7, 0x36, 0x0, 0x0, 0x7b, 0xcd, 0x2b, 0x75, 0x45, 0x5e, 0xf8, 0x88, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char selection_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfd, 0xfb, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf6, 0xff, 0xf6, 0xf4, 0xff, 0x15, 0x15, 0x17, 0xff, 0x70, 0xc0, 0x21, 0x0, 0x0, 0x0, 0xe, 0x74, 0x52, 0x4e, 0x53, 0x6, 0xf, 0x16, 0x18, 0x2a, 0x3b, 0x40, 0x3c, 0x6, 0x3d, 0x44, 0x3e, 0x31, 0x25, 0x8, 0x3d, 0x16, 0xb4, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x6, 0x2, 0x23, 0x1, 0x6, 0x91, 0xb0, 0x34, 0x20, 0x48, 0x75, 0x64, 0x50, 0xef, 0x5c, 0x5, 0x4, 0x33, 0x8a, 0x18, 0xcc, 0xf6, 0xdc, 0x5, 0x82, 0xd3, 0xc9, 0xc, 0x66, 0x6b, 0x41, 0x8c, 0x5b, 0x94, 0x33, 0x60, 0x6, 0xc2, 0xad, 0x80, 0x5b, 0xa, 0x73, 0x6, 0x0, 0x45, 0x34, 0x48, 0x41, 0xa3, 0xc5, 0x91, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char selection_oof_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x2, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xaf, 0xdf, 0x90, 0xa5, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0x3, 0x20, 0x25, 0x16, 0xc, 0x1f, 0x74, 0xbf, 0x74, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x64, 0x54, 0x52, 0x64, 0x60, 0x60, 0x78, 0x77, 0x8f, 0x51, 0x34, 0x8, 0xcc, 0xb8, 0xcd, 0xa8, 0xd9, 0x4, 0x66, 0xdc, 0x60, 0x74, 0x2f, 0x33, 0x4, 0x32, 0xde, 0xce, 0x64, 0xf4, 0x68, 0x53, 0x0, 0x32, 0xfe, 0xcd, 0xa0, 0x90, 0x1, 0x37, 0x10, 0x6e, 0x5, 0xdc, 0x52, 0xb8, 0x33, 0x0, 0xcc, 0x7, 0x26, 0xff, 0x1f, 0x38, 0x23, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char space_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x1c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x16, 0xfc, 0x17, 0xfc, 0x2f, 0x88, 0xcc, 0x15, 0xfa, 0x6f, 0x4, 0x84, 0x82, 0x18, 0x2, 0xe8, 0x5a, 0x88, 0x4, 0x0, 0x8c, 0xa4, 0xd, 0x47, 0x8, 0xea, 0xcc, 0xbb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char spinbox_updown_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0xdc, 0x4f, 0x7f, 0x98, 0x86, 0x47, 0xfa, 0x81, 0xe5, 0x83, 0x1f, 0xf, 0x7e, 0x3d, 0xb2, 0xc5, 0xa5, 0x5b, 0xe2, 0xc1, 0x93, 0x7, 0xff, 0x81, 0xf0, 0xf9, 0x63, 0x69, 0x2c, 0xd2, 0x67, 0x58, 0xef, 0x1f, 0x2, 0x4a, 0x42, 0xe0, 0xf1, 0xdb, 0xec, 0x98, 0xfa, 0x67, 0x2, 0x25, 0xe0, 0xf0, 0xe1, 0x2, 0x86, 0x41, 0x7, 0x30, 0x1d, 0x39, 0x3, 0xbf, 0x37, 0x8f, 0xdd, 0x66, 0x27, 0x29, 0xa0, 0x10, 0x4a, 0x2c, 0xa0, 0x41, 0x8d, 0x1b, 0x3c, 0x4c, 0x3, 0x46, 0x16, 0x69, 0x0, 0x0, 0x87, 0x2a, 0x58, 0xb5, 0x18, 0xe9, 0x80, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char submenu_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x78, 0xc0, 0xf0, 0xe0, 0x3f, 0x8, 0xde, 0x4f, 0x60, 0x0, 0x3, 0xb8, 0xc0, 0x83, 0x2f, 0xf, 0xb5, 0xe1, 0x2, 0x50, 0x78, 0xf5, 0x5, 0x37, 0xaa, 0xc0, 0xff, 0x87, 0xf3, 0x31, 0x4, 0x30, 0xb5, 0x60, 0x1a, 0x8a, 0x61, 0x2d, 0x0, 0xa6, 0x55, 0x4f, 0xb1, 0x91, 0xd6, 0xa7, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char submenu_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x55, 0xcb, 0xa1, 0x11, 0x83, 0x40, 0x14, 0x45, 0xd1, 0xb3, 0x6b, 0x10, 0x34, 0x10, 0x47, 0x34, 0xf4, 0x13, 0x9d, 0x2, 0x28, 0x87, 0x2a, 0xe8, 0x84, 0x2, 0x82, 0x65, 0x71, 0x69, 0x0, 0x11, 0xf5, 0x11, 0x4c, 0x66, 0xd8, 0xe7, 0xee, 0x99, 0x79, 0x80, 0xed, 0x5d, 0xa2, 0x44, 0x9, 0x12, 0xec, 0x43, 0x2c, 0x5a, 0x78, 0xa6, 0xcc, 0xb7, 0x8d, 0xf9, 0x4a, 0xc8, 0xfc, 0x26, 0x3d, 0x37, 0xa8, 0x97, 0x69, 0x46, 0x6b, 0x5, 0x8f, 0x23, 0xbd, 0x1c, 0xd5, 0xa5, 0xfb, 0xc4, 0xf8, 0x87, 0x13, 0xd2, 0x2f, 0x14, 0x49, 0x6f, 0xb1, 0x11, 0xe1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x19, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xc0, 0x2, 0xfe, 0x47, 0xfe, 0x17, 0x1, 0xc2, 0x48, 0xd2, 0x84, 0x10, 0x2, 0x84, 0xb9, 0x98, 0x0, 0x0, 0xbf, 0x67, 0x1d, 0x5, 0x89, 0x9b, 0x48, 0x90, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_behind_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x57, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0x2e, 0x36, 0x43, 0x40, 0x4d, 0x0, 0x0, 0x0, 0x43, 0x40, 0x4c, 0x3e, 0x3c, 0x47, 0x3e, 0x3b, 0x46, 0x31, 0x2f, 0x38, 0x2d, 0x2b, 0x33, 0x3f, 0x3c, 0x47, 0x91, 0xf8, 0xc4, 0xb2, 0x0, 0x0, 0x0, 0x18, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x5, 0x8, 0xa, 0xb, 0xc, 0x4, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x77, 0xf3, 0x7, 0xef, 0xd3, 0x51, 0x5e, 0xca, 0x0, 0x0, 0x0, 0x5a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xb5, 0x8c, 0x35, 0x2, 0x80, 0x30, 0x10, 0x4, 0x6f, 0x73, 0xc4, 0x53, 0xf3, 0xff, 0x3f, 0xe2, 0xee, 0x4e, 0x9d, 0xe9, 0x56, 0x41, 0xd8, 0xa1, 0x69, 0x7, 0xd0, 0x43, 0x72, 0x19, 0x3d, 0x37, 0x10, 0x6c, 0x1f, 0x8d, 0x6a, 0x0, 0x2b, 0x25, 0xc1, 0x20, 0xa2, 0x69, 0x98, 0xba, 0xb6, 0x45, 0x9a, 0x2b, 0xbd, 0xe9, 0xd5, 0x69, 0xda, 0x0, 0xa9, 0x94, 0x9f, 0x68, 0x7, 0xc5, 0xd2, 0x50, 0x4a, 0x69, 0x71, 0x18, 0x63, 0xb3, 0x18, 0x7a, 0x71, 0x2e, 0xa3, 0xfd, 0x1b, 0xff, 0xc9, 0xff, 0x34, 0x86, 0x31, 0x3, 0x12, 0xb2, 0x4c, 0x6a, 0xfb, 0x60, 0xc7, 0xdc, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_close_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xad, 0x90, 0x1, 0x6, 0xc0, 0x30, 0xc, 0x45, 0x77, 0x89, 0xd5, 0x76, 0xb3, 0x9e, 0x7b, 0x65, 0x63, 0xd, 0xf9, 0xbb, 0x48, 0x3b, 0xb3, 0x92, 0x54, 0x42, 0xb1, 0x5, 0x88, 0xf7, 0xc8, 0xcf, 0x9f, 0xfe, 0x1a, 0x8e, 0x14, 0xf4, 0x4e, 0x81, 0x63, 0x87, 0x51, 0x90, 0x28, 0x8, 0x46, 0x42, 0x51, 0x4a, 0x9e, 0x79, 0x43, 0xc5, 0x81, 0x55, 0x6f, 0xbc, 0x34, 0xdc, 0x2b, 0x2e, 0x16, 0xe5, 0x3a, 0xb1, 0xb, 0xb6, 0xca, 0x3, 0x2b, 0xb2, 0xc2, 0xbe, 0xf0, 0x66, 0x71, 0x4f, 0x70, 0x3b, 0x61, 0x14, 0x89, 0x26, 0x71, 0x5d, 0x6c, 0x9f, 0x1e, 0x17, 0x35, 0xae, 0xfa, 0xeb, 0xdc, 0x62, 0xc3, 0x84, 0x2d, 0x77, 0x22, 0xda, 0x98, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_container_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x87, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x56, 0x52, 0x60, 0x47, 0x44, 0x52, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x47, 0x44, 0x51, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x40, 0x3e, 0x48, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x3f, 0x38, 0xaa, 0x5e, 0x0, 0x0, 0x0, 0x15, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0xe8, 0xff, 0x76, 0xed, 0x0, 0x0, 0x0, 0x63, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0xcf, 0x45, 0x2, 0x84, 0x30, 0x0, 0x4, 0xc1, 0x69, 0x1c, 0xfe, 0xff, 0xca, 0xd5, 0xeb, 0x2a, 0x83, 0x6b, 0xd2, 0xb7, 0x54, 0x1c, 0x31, 0x26, 0xc9, 0x63, 0x50, 0xcc, 0x32, 0x8d, 0x3f, 0xd9, 0x20, 0x5, 0x1a, 0xf2, 0xc7, 0x1f, 0x7a, 0x48, 0x4a, 0x66, 0xa8, 0xde, 0xc, 0xd0, 0x38, 0xd1, 0x54, 0xdb, 0x4c, 0x2b, 0xd0, 0x5c, 0x62, 0x8e, 0xa0, 0x1, 0x74, 0x4, 0x65, 0xd2, 0x1e, 0xd4, 0x83, 0xf7, 0x60, 0x65, 0x3e, 0x82, 0x3, 0x48, 0x49, 0xdf, 0x55, 0xca, 0xd4, 0xef, 0xfa, 0xfb, 0x27, 0x36, 0xf, 0x92, 0x31, 0x9e, 0x44, 0x3e, 0x17, 0x7c, 0xbf, 0x3, 0xef, 0x34, 0x3f, 0x3e, 0xe0, 0x24, 0x67, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_current_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3d, 0x48, 0x5b, 0x58, 0x66, 0x5b, 0x57, 0x65, 0x57, 0x54, 0x62, 0x55, 0x53, 0x62, 0x4a, 0x46, 0x52, 0x46, 0x41, 0x4e, 0x45, 0x41, 0x4d, 0x55, 0x52, 0x60, 0x44, 0x41, 0x4c, 0x53, 0x50, 0x5e, 0x43, 0x40, 0x4b, 0x52, 0x4e, 0x5d, 0x41, 0x3e, 0x4a, 0x4f, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x4e, 0x4b, 0x59, 0x3e, 0x3c, 0x47, 0x4d, 0x4a, 0x58, 0x3d, 0x3b, 0x46, 0x4b, 0x49, 0x54, 0x3c, 0x3a, 0x44, 0x4b, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x3b, 0x39, 0x42, 0x3b, 0x38, 0x43, 0x3b, 0x38, 0x42, 0x3a, 0x37, 0x41, 0x39, 0x37, 0x41, 0x3a, 0x38, 0x41, 0x39, 0x36, 0x3f, 0x38, 0x36, 0x3f, 0x39, 0x36, 0x40, 0x38, 0x36, 0x40, 0x37, 0x35, 0x3e, 0x37, 0x34, 0x3e, 0x36, 0x35, 0x3d, 0xd7, 0x41, 0xa4, 0x19, 0x0, 0x0, 0x0, 0x11, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xa3, 0x31, 0x6b, 0xc2, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x55, 0xca, 0x85, 0xd, 0xc0, 0x40, 0x14, 0xc3, 0x50, 0x27, 0xf7, 0xd5, 0xfd, 0xd7, 0x2d, 0xa6, 0x4c, 0x16, 0x3f, 0x59, 0xe8, 0x8, 0xc8, 0x91, 0xd4, 0x5d, 0x92, 0xa3, 0xa1, 0x76, 0xb1, 0xd8, 0xcb, 0x92, 0x41, 0x1b, 0xb8, 0xe9, 0x2, 0xcf, 0xd2, 0x7e, 0xc4, 0x9c, 0x2d, 0xed, 0x3c, 0xc4, 0x95, 0xa3, 0x3f, 0xb0, 0x3, 0x7f, 0xa0, 0xe0, 0xb, 0x50, 0xe4, 0xb, 0xa1, 0xf2, 0x87, 0x38, 0x31, 0x4f, 0x4e, 0xa, 0x30, 0x22, 0x58, 0x33, 0x81, 0x1d, 0x4a, 0x44, 0x5a, 0xec, 0x8c, 0x27, 0x34, 0x10, 0x58, 0xf1, 0xf8, 0x39, 0xe0, 0x60, 0x56, 0x63, 0x63, 0x30, 0x69, 0xf8, 0x3c, 0xb4, 0xd1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0x2e, 0x23, 0x0, 0x0, 0x2e, 0x23, 0x1, 0x78, 0xa5, 0x3f, 0x76, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0xa6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x7c, 0xcd, 0xc5, 0x41, 0x4, 0x41, 0x10, 0x0, 0xc0, 0xea, 0xbd, 0xc1, 0x6d, 0xde, 0x47, 0x30, 0xe4, 0x40, 0x34, 0x1b, 0x2, 0xd1, 0x10, 0x1, 0x1a, 0xc, 0x4f, 0xdc, 0x6d, 0x1b, 0x77, 0xe6, 0xea, 0xd5, 0xde, 0xa1, 0x98, 0x36, 0x63, 0xd6, 0xb4, 0x91, 0xe, 0x30, 0x78, 0x74, 0xe7, 0xc6, 0xad, 0xbb, 0x97, 0xf6, 0x82, 0x6a, 0xc5, 0x82, 0x19, 0x21, 0x90, 0xd2, 0xad, 0x4b, 0xa7, 0x4e, 0x28, 0x66, 0xd4, 0xb5, 0xf5, 0xd5, 0xfe, 0xa1, 0xfa, 0xa1, 0x9c, 0x1c, 0x6c, 0xec, 0x6f, 0x7a, 0x28, 0x66, 0xad, 0x8c, 0xfb, 0xa3, 0xfa, 0x20, 0x7d, 0x9, 0xa5, 0x8e, 0x7b, 0xdb, 0x2e, 0x5e, 0x5f, 0xdc, 0xd7, 0x7b, 0x3f, 0xa5, 0x7b, 0xf7, 0xd5, 0x82, 0xe9, 0x62, 0x64, 0x66, 0x90, 0xfe, 0x1a, 0x98, 0x31, 0x2a, 0x3a, 0x91, 0x6, 0x7f, 0x25, 0xa1, 0x2b, 0x88, 0xe6, 0x85, 0xa0, 0x40, 0x73, 0x0, 0x5, 0x99, 0xf2, 0xff, 0x17, 0xf9, 0x79, 0x61, 0x98, 0x78, 0x21, 0x9a, 0x17, 0x82, 0xf2, 0x99, 0x34, 0x74, 0x13, 0xbb, 0x49, 0x87, 0xd0, 0x12, 0x4f, 0x3, 0x29, 0x20, 0x0, 0x0, 0xe, 0x38, 0x38, 0x83, 0xd2, 0xe8, 0x36, 0x36, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_menu_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x5, 0xa3, 0xe0, 0xc1, 0x7f, 0x54, 0x48, 0x3, 0x5, 0xf, 0xe3, 0x1e, 0x7c, 0x81, 0x4b, 0x7f, 0x7b, 0x98, 0x86, 0xc5, 0x15, 0xf7, 0x35, 0xee, 0x5f, 0x2, 0x4b, 0x5f, 0x7f, 0xac, 0x8b, 0xc3, 0xa1, 0x2f, 0xb8, 0x1f, 0xce, 0x7f, 0x38, 0xff, 0x5, 0x37, 0x75, 0xbd, 0xf, 0x0, 0x52, 0xd4, 0x48, 0xb8, 0x2d, 0x78, 0x5a, 0x91, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tab_menu_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x5, 0xa3, 0xe0, 0xc1, 0x7f, 0x54, 0x48, 0x3, 0x5, 0xf, 0xe3, 0x1e, 0x7c, 0x81, 0x4b, 0x7f, 0x7b, 0x98, 0x86, 0xc5, 0x15, 0xf7, 0x35, 0xee, 0x5f, 0x2, 0x4b, 0x5f, 0x7f, 0xac, 0x8b, 0xc3, 0xa1, 0x2f, 0xb8, 0x1f, 0xce, 0x7f, 0x38, 0xff, 0x5, 0x37, 0x75, 0xbd, 0xf, 0x0, 0x52, 0xd4, 0x48, 0xb8, 0x2d, 0x78, 0x5a, 0x91, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_off_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x7a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x19, 0x1c, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x15, 0x18, 0x20, 0x20, 0x25, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x27, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1e, 0x0, 0x0, 0x0, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x28, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x2d, 0x26, 0x2c, 0x4d, 0x2b, 0x37, 0x63, 0x2f, 0x3f, 0x6e, 0x31, 0x43, 0x71, 0x32, 0x44, 0x6c, 0x31, 0x42, 0x51, 0x2c, 0x39, 0x47, 0x2a, 0x35, 0x66, 0x30, 0x40, 0x4d, 0x2b, 0x38, 0x32, 0x26, 0x2e, 0x26, 0x25, 0x2a, 0x2e, 0x25, 0x2c, 0x3c, 0x28, 0x31, 0x52, 0x2c, 0x39, 0x68, 0x30, 0x40, 0x27, 0x25, 0x2a, 0x50, 0x2c, 0x38, 0x5f, 0x2e, 0x3d, 0x35, 0x27, 0x2f, 0x38, 0x27, 0x30, 0x5e, 0x2e, 0x3d, 0x43, 0x2a, 0x34, 0x5f, 0x2f, 0x3e, 0x2f, 0x25, 0x2c, 0x44, 0x2a, 0x34, 0x2b, 0x26, 0x2c, 0x64, 0x2f, 0x3f, 0x36, 0x27, 0x30, 0x37, 0x27, 0x30, 0x66, 0x2f, 0x40, 0x2c, 0x26, 0x2c, 0x46, 0x2a, 0x35, 0x53, 0x2c, 0x39, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x32, 0x32, 0x37, 0x5d, 0x2e, 0x3d, 0x3e, 0x29, 0x32, 0xc9, 0xc9, 0xca, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x69, 0x30, 0x41, 0x2f, 0x26, 0x2d, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x4e, 0x4e, 0x52, 0x48, 0x2b, 0x36, 0x2c, 0x26, 0x2b, 0x97, 0xb0, 0x86, 0xb4, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x98, 0xe5, 0xfa, 0xfe, 0x8, 0x17, 0x35, 0x86, 0xf3, 0x7, 0x3a, 0xb4, 0xb9, 0xb, 0x28, 0x8a, 0x8b, 0xf6, 0x45, 0x5, 0x9b, 0xe6, 0xe6, 0x37, 0xf, 0xfb, 0x4c, 0xfe, 0x4e, 0x4f, 0x50, 0xfb, 0x9c, 0xf6, 0x8c, 0x3b, 0xbb, 0x3c, 0x87, 0xf3, 0x53, 0x14, 0xe5, 0x7c, 0xf3, 0x66, 0x0, 0x0, 0x2, 0x29, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x3, 0x93, 0x24, 0x4b, 0x14, 0x85, 0x5f, 0xb9, 0xaa, 0x6d, 0x8e, 0x6d, 0xdb, 0x73, 0x2b, 0xb3, 0xe7, 0xad, 0x6d, 0xab, 0xdd, 0x63, 0xe3, 0xbf, 0x6f, 0x67, 0x65, 0xbb, 0xb4, 0xbb, 0x81, 0xc5, 0x97, 0x11, 0x27, 0x78, 0x4f, 0xea, 0xe2, 0xbf, 0x3f, 0x9, 0x86, 0xe5, 0x38, 0xde, 0x16, 0x8e, 0x63, 0x19, 0xc3, 0x70, 0x96, 0x17, 0x44, 0x49, 0x56, 0x1c, 0x36, 0x28, 0xb2, 0x24, 0xa, 0x3c, 0xab, 0xdf, 0x9d, 0x77, 0xca, 0x2e, 0xb7, 0xc7, 0xeb, 0xf3, 0x7, 0x2c, 0xf1, 0xfb, 0xbc, 0x1e, 0xb7, 0x4b, 0x76, 0xf2, 0x2d, 0xa7, 0x60, 0x83, 0xa1, 0x70, 0x24, 0x1a, 0x8b, 0x27, 0xb6, 0xc1, 0x86, 0xed, 0x44, 0x3c, 0x16, 0x8d, 0x84, 0x43, 0x41, 0xb6, 0x29, 0x3e, 0xd9, 0xd6, 0xde, 0xd1, 0xa9, 0x22, 0x84, 0x31, 0x6, 0xd, 0x4c, 0x40, 0x38, 0x85, 0xd, 0x4d, 0x3a, 0x3b, 0xda, 0xdb, 0x92, 0xd, 0xe, 0x4c, 0x97, 0xd2, 0xdd, 0xa3, 0xa2, 0x9d, 0xff, 0x6f, 0xdd, 0xbe, 0x43, 0xb9, 0x7b, 0x6f, 0x47, 0x73, 0x49, 0x21, 0x93, 0x73, 0xf4, 0x74, 0x2b, 0x5d, 0x4c, 0xfd, 0xfe, 0x92, 0xbb, 0x57, 0xc5, 0xf7, 0x1f, 0x3c, 0x7c, 0xf4, 0x18, 0x8, 0x8f, 0x9f, 0x3c, 0x7d, 0xf6, 0xfc, 0xfe, 0xb, 0xed, 0x24, 0x66, 0x37, 0xe9, 0x75, 0x4b, 0xb5, 0x77, 0x60, 0xfb, 0xfa, 0x7, 0x54, 0xfc, 0xf2, 0xd5, 0x6b, 0x68, 0xe0, 0xcd, 0xdb, 0x97, 0x2f, 0x2c, 0xdf, 0x62, 0xb0, 0xbf, 0xaf, 0x7a, 0x9, 0xbe, 0xcd, 0x33, 0x84, 0xde, 0xbd, 0x7f, 0x2, 0x4d, 0x7c, 0x78, 0xff, 0xf1, 0x31, 0x58, 0x30, 0x34, 0xdc, 0x36, 0x42, 0x8f, 0xc0, 0x8, 0xae, 0x51, 0xf4, 0xe9, 0xf3, 0x17, 0x68, 0xe1, 0xeb, 0xb7, 0x34, 0x58, 0x31, 0xea, 0x12, 0xa8, 0x1, 0x2b, 0xba, 0xc7, 0x50, 0xe6, 0x19, 0xe8, 0xc8, 0x66, 0x80, 0x92, 0xcb, 0x17, 0x8a, 0x25, 0xd8, 0x2d, 0x94, 0xd1, 0x64, 0xf, 0x8, 0xe3, 0x13, 0x93, 0xf4, 0xe, 0x9c, 0xe4, 0x89, 0xe3, 0xfd, 0x3, 0xd0, 0x71, 0xb0, 0xf, 0x94, 0xc3, 0xa3, 0xdc, 0x71, 0x21, 0xb7, 0x4b, 0x2, 0x35, 0xa1, 0x4c, 0xd, 0x4b, 0x1c, 0x35, 0x90, 0xa7, 0x67, 0xf0, 0xc9, 0x29, 0xe8, 0x38, 0x3d, 0xa9, 0x1c, 0x80, 0x4, 0x1d, 0x9d, 0xed, 0x9e, 0x13, 0x3, 0x22, 0x94, 0xed, 0x59, 0x99, 0xa7, 0x6, 0x6d, 0x73, 0x2a, 0xbe, 0x6d, 0xf0, 0x60, 0x8f, 0x6f, 0x13, 0x25, 0x41, 0x65, 0xb9, 0xc8, 0xef, 0x1e, 0x1e, 0x1e, 0xee, 0x69, 0x2, 0x94, 0xf9, 0xb6, 0x8a, 0x81, 0xec, 0x55, 0x2d, 0x4f, 0xb0, 0x47, 0x4e, 0x90, 0xbf, 0xdc, 0x25, 0x91, 0x44, 0x74, 0x27, 0x90, 0x3c, 0xb, 0xc8, 0xf2, 0xd, 0xce, 0xcf, 0x60, 0xaf, 0x58, 0xaa, 0x18, 0xe8, 0xdf, 0x80, 0x15, 0x27, 0x6c, 0x7e, 0x61, 0xb7, 0x78, 0x5e, 0xcc, 0x43, 0xab, 0x1, 0xfd, 0x5, 0x9a, 0x7, 0x8b, 0x36, 0x79, 0xb0, 0x77, 0x76, 0x5, 0x90, 0x2b, 0xed, 0x55, 0x84, 0xb2, 0x18, 0x16, 0x98, 0x5a, 0x26, 0x2e, 0xe1, 0x5f, 0xce, 0x44, 0x5a, 0xb, 0x83, 0x3f, 0x5f, 0xb, 0xcb, 0xb4, 0x16, 0xcc, 0xab, 0xf1, 0x9a, 0xc6, 0xdb, 0x57, 0x23, 0xed, 0x7, 0x2b, 0xab, 0x2a, 0xba, 0x69, 0xed, 0x7, 0xe6, 0x6c, 0xaf, 0xae, 0xd5, 0xfa, 0x1, 0xed, 0x48, 0x8a, 0x7b, 0x7d, 0x3, 0xa5, 0x10, 0x6d, 0x48, 0xb8, 0x2, 0x2a, 0x2f, 0x30, 0xa2, 0x73, 0xdd, 0xad, 0x24, 0x9b, 0x7b, 0x5a, 0x97, 0x14, 0xf6, 0x44, 0x63, 0x53, 0xdb, 0x60, 0xcb, 0xf6, 0x54, 0x2c, 0xea, 0x9, 0x4b, 0x5d, 0x6c, 0x6b, 0x57, 0xee, 0x93, 0x5d, 0x13, 0xc3, 0xb3, 0x9b, 0x1, 0x1b, 0x36, 0x67, 0x87, 0x27, 0x5c, 0x72, 0x1f, 0xcf, 0xe8, 0xa7, 0xca, 0x88, 0x30, 0xb9, 0xd5, 0x66, 0x3f, 0x17, 0xda, 0xb6, 0x26, 0x85, 0x11, 0x96, 0x31, 0x99, 0x4c, 0xfc, 0xf, 0x40, 0x27, 0xd3, 0xbf, 0xc4, 0x77, 0x82, 0xde, 0x40, 0xde, 0x4b, 0x3f, 0xe2, 0x98, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_off_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0xfc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x24, 0x24, 0x28, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x19, 0x1c, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0x4e, 0x4e, 0x52, 0x1a, 0x1a, 0x1d, 0x32, 0x32, 0x37, 0x2c, 0x26, 0x2c, 0x26, 0x25, 0x2a, 0x27, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x2f, 0x26, 0x2d, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x15, 0x18, 0x20, 0x20, 0x25, 0x20, 0x20, 0x24, 0x5b, 0x5b, 0x5f, 0x84, 0x84, 0x87, 0x77, 0x77, 0x7a, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x69, 0x69, 0x6c, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x27, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1e, 0x0, 0x0, 0x0, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71, 0xb, 0x1b, 0xbb, 0x0, 0x0, 0x0, 0x54, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x98, 0xe5, 0xfa, 0xfe, 0xff, 0xff, 0x8, 0x17, 0x35, 0x86, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x7, 0x3a, 0xb4, 0xff, 0xff, 0xff, 0xb9, 0xff, 0xff, 0xff, 0xff, 0xb, 0x28, 0x8a, 0xff, 0x8b, 0xf6, 0x45, 0x5, 0x9b, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x37, 0xf, 0xff, 0xfb, 0x4c, 0xfe, 0x4e, 0x4f, 0x50, 0xfb, 0x9c, 0xf6, 0x8c, 0x3b, 0xbb, 0x3c, 0x87, 0xf3, 0x53, 0x14, 0xd4, 0x6d, 0x6c, 0xf9, 0x0, 0x0, 0x2, 0x3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x55, 0x85, 0x9a, 0xe2, 0x30, 0x18, 0xbc, 0x7a, 0x8b, 0xbb, 0x7b, 0x2, 0xbb, 0x4d, 0x36, 0xb8, 0x3b, 0xeb, 0xae, 0xef, 0xff, 0x2e, 0x47, 0x48, 0x3f, 0xa0, 0x7a, 0xae, 0x83, 0x56, 0xfe, 0xe9, 0xfc, 0xfe, 0xe9, 0x6f, 0x2, 0xc7, 0xb, 0x82, 0xf8, 0x45, 0x8, 0x2, 0xcf, 0x39, 0x9a, 0xf3, 0xa2, 0x24, 0x2b, 0xaa, 0xe6, 0xfb, 0x2, 0x34, 0x55, 0x91, 0x25, 0x91, 0xb7, 0x3f, 0x5d, 0xf4, 0xab, 0x81, 0x60, 0x28, 0x1c, 0x89, 0xc6, 0x3c, 0x11, 0x8d, 0x84, 0x43, 0xc1, 0x80, 0xea, 0x17, 0x2d, 0x2a, 0xf8, 0x78, 0x22, 0x99, 0x4a, 0x67, 0xb2, 0xb9, 0x7c, 0xe1, 0xb, 0xc8, 0xe7, 0xb2, 0x99, 0x74, 0x2a, 0x99, 0x88, 0xf3, 0x26, 0xfb, 0x62, 0xa9, 0x5c, 0xa9, 0xd6, 0x0, 0x80, 0x10, 0xb2, 0xfb, 0x20, 0x5, 0x80, 0x75, 0xe8, 0x48, 0x52, 0xad, 0x94, 0x4b, 0xc5, 0x23, 0x6, 0xae, 0xa1, 0x9d, 0x9c, 0xd6, 0x80, 0x8e, 0xf0, 0x1e, 0x48, 0xdf, 0xb1, 0xd4, 0x81, 0x8b, 0x8e, 0xd3, 0x13, 0xad, 0xc1, 0x1d, 0xfc, 0x57, 0x82, 0x67, 0x35, 0x48, 0x30, 0x6a, 0xb6, 0x76, 0x97, 0x5b, 0x3a, 0x41, 0x98, 0xb4, 0x77, 0x4a, 0xdc, 0x3c, 0x39, 0xb, 0x2a, 0xfb, 0x38, 0xf0, 0x9d, 0x6e, 0xaf, 0x6, 0x11, 0xea, 0x1f, 0xdf, 0x41, 0x10, 0x6a, 0x7b, 0xc6, 0x62, 0xd0, 0xed, 0xf0, 0x6, 0x81, 0x58, 0xa, 0xd, 0x1, 0xa1, 0xa2, 0x8f, 0xa1, 0x23, 0xd2, 0xf2, 0x62, 0x18, 0x8e, 0x4a, 0x63, 0x26, 0x81, 0x93, 0x2, 0x13, 0xa0, 0xe3, 0xbe, 0xf5, 0xe, 0x82, 0x3d, 0x25, 0x14, 0x26, 0x1, 0x89, 0x11, 0xf0, 0x72, 0x70, 0xba, 0x15, 0x60, 0xbf, 0x3, 0x11, 0xe3, 0xcf, 0x6c, 0xbe, 0x58, 0xa2, 0x42, 0x7f, 0xb1, 0xc5, 0xee, 0x8b, 0x9d, 0x5f, 0xad, 0x37, 0xcc, 0x7, 0x41, 0x9, 0x65, 0x21, 0xbd, 0xd9, 0x8a, 0x3d, 0xe9, 0xf9, 0x7c, 0x46, 0x16, 0xb3, 0x3e, 0x35, 0xa4, 0x5f, 0x6, 0x2e, 0x46, 0x8a, 0xc0, 0x8, 0xd4, 0xcb, 0x2b, 0x88, 0x75, 0x3b, 0x81, 0x8e, 0xd, 0x1, 0xd4, 0x68, 0x8e, 0xfa, 0xe7, 0x94, 0xe0, 0x7c, 0x4f, 0x90, 0xbf, 0x56, 0x45, 0x46, 0x50, 0xba, 0xa9, 0x41, 0xec, 0x10, 0xb0, 0x96, 0x41, 0xd0, 0x3f, 0xdf, 0x7e, 0xe1, 0x79, 0xff, 0xfc, 0xfc, 0x9c, 0x6c, 0xbf, 0xf6, 0x14, 0xb7, 0x25, 0x83, 0x40, 0xd, 0xd7, 0x3c, 0x15, 0x90, 0x9d, 0x2, 0xec, 0xae, 0x40, 0x9, 0xdd, 0x1, 0xef, 0x18, 0xa0, 0x2, 0x59, 0xda, 0x5c, 0xd8, 0xc7, 0x80, 0x97, 0xd7, 0x5f, 0xc8, 0x42, 0x7f, 0x79, 0xbe, 0x9c, 0x17, 0x2c, 0x4, 0xfb, 0x2c, 0xd0, 0x3a, 0xb8, 0xff, 0x42, 0x1d, 0x10, 0x5a, 0x95, 0x33, 0x44, 0xd8, 0x97, 0x81, 0xfb, 0xa4, 0xc4, 0xed, 0x2b, 0xf1, 0x1, 0xfe, 0x40, 0x25, 0xd2, 0x5e, 0x18, 0x7c, 0x7b, 0x2f, 0x3c, 0xd2, 0x5e, 0xf8, 0x72, 0x37, 0x16, 0xbe, 0xdc, 0x8d, 0x6c, 0x1e, 0x3c, 0x3d, 0xd7, 0x40, 0xdb, 0x32, 0xf, 0xbc, 0xec, 0x9f, 0x5f, 0xf6, 0xf3, 0x80, 0x4d, 0x24, 0x2d, 0xf8, 0xfa, 0x6, 0xea, 0x80, 0xd, 0x24, 0x68, 0x0, 0x6c, 0x5f, 0x8e, 0xf6, 0xd5, 0xd7, 0xa0, 0x56, 0x34, 0xcf, 0xb4, 0x86, 0x92, 0xc, 0xa5, 0x33, 0x17, 0xf9, 0xc2, 0x17, 0x91, 0xbf, 0xc8, 0xa4, 0x43, 0x49, 0xa5, 0xc1, 0x5b, 0xa7, 0x72, 0x47, 0xd, 0xac, 0x47, 0xd7, 0xef, 0xb1, 0x2f, 0xe0, 0xfd, 0x7a, 0xb4, 0xe, 0xa8, 0x1d, 0x91, 0xb3, 0x6f, 0x95, 0xb1, 0xb4, 0xf9, 0x28, 0x7d, 0x79, 0x2f, 0x94, 0x3e, 0x36, 0xd2, 0x98, 0xe7, 0x5c, 0x36, 0x93, 0xf8, 0x15, 0xa0, 0x9b, 0xe9, 0xff, 0xc2, 0x67, 0x14, 0xf4, 0xa5, 0xb3, 0x35, 0x5e, 0x63, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_off_disabled_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x9c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x1d, 0x1d, 0x21, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x2e, 0x2e, 0x2e, 0x46, 0x46, 0x46, 0x57, 0x57, 0x57, 0x60, 0x60, 0x60, 0x62, 0x62, 0x62, 0x5e, 0x5e, 0x5e, 0x4a, 0x4a, 0x4a, 0x12, 0x12, 0x15, 0x25, 0x27, 0x2d, 0x42, 0x42, 0x42, 0x59, 0x59, 0x59, 0x32, 0x32, 0x32, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x39, 0x39, 0x39, 0x49, 0x49, 0x49, 0x5a, 0x5a, 0x5a, 0x48, 0x48, 0x48, 0x54, 0x54, 0x54, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x1e, 0x1e, 0x22, 0x25, 0x26, 0x2b, 0x3f, 0x3f, 0x3f, 0xe, 0xe, 0x10, 0x2c, 0x2c, 0x2c, 0x58, 0x58, 0x58, 0x5d, 0x5d, 0x5f, 0x80, 0x80, 0x80, 0x79, 0x79, 0x79, 0x40, 0x40, 0x44, 0x32, 0x32, 0x37, 0x41, 0x41, 0x41, 0x1a, 0x1a, 0x1d, 0x6a, 0x6a, 0x6d, 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x5b, 0x5b, 0x5b, 0x2f, 0x2f, 0x2f, 0x50, 0x50, 0x51, 0x13, 0x13, 0x15, 0x2b, 0xcd, 0x4, 0x96, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x1, 0x29, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xdb, 0x76, 0x83, 0x20, 0x10, 0x45, 0x8d, 0xe8, 0x24, 0x98, 0xda, 0x1a, 0x93, 0xda, 0x5b, 0xac, 0xb9, 0x50, 0x14, 0x52, 0x72, 0xfd, 0xff, 0x7f, 0xab, 0x68, 0x49, 0x28, 0x5a, 0x35, 0xf5, 0xa5, 0xf, 0x39, 0xf, 0xb3, 0x96, 0xe3, 0xda, 0x7, 0x98, 0x81, 0xb1, 0xac, 0x9b, 0xfe, 0x97, 0x6, 0x36, 0x72, 0x5c, 0x68, 0x91, 0xeb, 0x20, 0x7b, 0x50, 0xcf, 0xf, 0x5b, 0xe1, 0xb3, 0xc9, 0xb0, 0x6, 0x1f, 0xe1, 0x46, 0xc6, 0x1b, 0xdf, 0xf9, 0xf7, 0xa5, 0x1e, 0x2, 0xf, 0xf0, 0xc8, 0xe4, 0x27, 0x8d, 0xcb, 0x87, 0xd3, 0xd9, 0xf8, 0x31, 0x7a, 0x92, 0x7a, 0xf6, 0x5e, 0x5e, 0xdf, 0xa6, 0xa1, 0x3b, 0x31, 0xc, 0x1a, 0xd7, 0xf, 0xe7, 0xf1, 0xbb, 0xfe, 0x9d, 0xc4, 0xf3, 0x10, 0xff, 0xe4, 0x17, 0x4d, 0xfc, 0x72, 0x15, 0x7b, 0xc6, 0x81, 0xe2, 0xd5, 0x72, 0xa1, 0xf3, 0xeb, 0xc6, 0xf3, 0x93, 0x8f, 0xc4, 0x4c, 0x25, 0x33, 0x2, 0x6b, 0xcd, 0xc0, 0x56, 0x3f, 0x10, 0x4d, 0x33, 0x6, 0x24, 0xcd, 0x55, 0x4, 0x2e, 0x93, 0x9b, 0xa0, 0x6a, 0x1a, 0x6c, 0xe0, 0x53, 0x33, 0x40, 0x2a, 0x2f, 0x28, 0xe2, 0x29, 0x22, 0x12, 0x24, 0x25, 0x9d, 0x6b, 0xbb, 0xab, 0x1a, 0xec, 0xb6, 0x80, 0x34, 0x3, 0x47, 0x6d, 0x40, 0x42, 0x94, 0x11, 0x21, 0xd, 0x84, 0x32, 0xd8, 0x1f, 0xaa, 0x6, 0x87, 0x3d, 0xe8, 0x65, 0x54, 0x3d, 0x24, 0x22, 0xf, 0x47, 0x4a, 0x84, 0x10, 0xbc, 0x8, 0x45, 0xd6, 0x8f, 0xaa, 0x6, 0x91, 0xf, 0x50, 0xd3, 0x44, 0x5e, 0xec, 0xe0, 0x78, 0xfd, 0xe, 0x2e, 0x35, 0x60, 0xc0, 0x33, 0xf3, 0x8, 0x1d, 0x6a, 0x70, 0xee, 0x2, 0xc9, 0x44, 0x46, 0xc1, 0x30, 0xe8, 0xd0, 0x85, 0xcb, 0x3d, 0xe0, 0x4c, 0xd6, 0x92, 0xf1, 0xef, 0xd0, 0xf5, 0x1e, 0xf4, 0xbe, 0x89, 0xfd, 0xdf, 0x42, 0xff, 0xd7, 0x68, 0x9d, 0xae, 0x9b, 0x7, 0xa7, 0xba, 0x89, 0x4, 0x9d, 0x55, 0x37, 0x91, 0xca, 0x99, 0x88, 0xdb, 0x61, 0xfc, 0xeb, 0x4c, 0xbc, 0xe9, 0xaf, 0xfa, 0x2, 0xdc, 0x1a, 0x30, 0x60, 0x4e, 0xef, 0xb8, 0xbb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_off_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0xaa, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x24, 0x24, 0x28, 0x20, 0x20, 0x25, 0x14, 0x14, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x27, 0x10, 0x10, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x19, 0x19, 0x1c, 0x12, 0x12, 0x15, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x11, 0x11, 0x14, 0x1e, 0x1e, 0x22, 0x23, 0x23, 0x27, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x25, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x24, 0x24, 0x27, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x1a, 0x1a, 0x1e, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x26, 0x11, 0x11, 0x13, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x24, 0x24, 0x28, 0x2d, 0x26, 0x2c, 0x51, 0x2c, 0x39, 0x6c, 0x31, 0x42, 0x71, 0x32, 0x44, 0x6e, 0x31, 0x43, 0x63, 0x2f, 0x3f, 0x4d, 0x2b, 0x37, 0x27, 0x25, 0x2a, 0x47, 0x2a, 0x35, 0x68, 0x30, 0x40, 0x52, 0x2c, 0x39, 0x3c, 0x28, 0x31, 0x2e, 0x25, 0x2c, 0x26, 0x25, 0x2a, 0x32, 0x26, 0x2e, 0x4d, 0x2b, 0x38, 0x66, 0x30, 0x40, 0x50, 0x2c, 0x38, 0x5e, 0x2e, 0x3d, 0x38, 0x27, 0x30, 0x35, 0x27, 0x2f, 0x5f, 0x2e, 0x3d, 0x44, 0x2a, 0x34, 0x5f, 0x2f, 0x3e, 0x2f, 0x25, 0x2c, 0x43, 0x2a, 0x34, 0x2c, 0x26, 0x2c, 0x66, 0x2f, 0x40, 0x37, 0x27, 0x30, 0x36, 0x27, 0x30, 0x64, 0x2f, 0x3f, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x32, 0x32, 0x37, 0x46, 0x2a, 0x35, 0x53, 0x2c, 0x39, 0xc9, 0xc9, 0xca, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x5d, 0x2e, 0x3d, 0x3e, 0x29, 0x32, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x69, 0x30, 0x41, 0x2f, 0x26, 0x2d, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x4e, 0x4e, 0x52, 0x48, 0x2b, 0x36, 0x2c, 0x26, 0x2b, 0x25, 0x25, 0x27, 0xea, 0xac, 0x78, 0x5d, 0x0, 0x0, 0x0, 0x51, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xfa, 0xe5, 0x98, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xf3, 0x86, 0x7, 0x3a, 0x96, 0xf9, 0xb4, 0x9a, 0xb9, 0xb, 0x28, 0x76, 0xfb, 0x8a, 0xde, 0xf6, 0x82, 0x9b, 0xc6, 0xe6, 0x4c, 0xfe, 0x4f, 0xe9, 0xfb, 0x37, 0x83, 0x9c, 0xf6, 0x77, 0x8b, 0x3b, 0x9c, 0xbb, 0x74, 0xda, 0xf3, 0x87, 0xfb, 0x45, 0x4e, 0x53, 0x5, 0xf, 0x14, 0xc7, 0x22, 0x44, 0x61, 0x0, 0x0, 0x2, 0x45, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xd5, 0x55, 0xe7, 0x5f, 0xd3, 0x40, 0x18, 0x26, 0x3b, 0x91, 0x24, 0x65, 0x95, 0xd, 0xd, 0x8e, 0x22, 0x6a, 0x71, 0x2b, 0x82, 0x75, 0xe1, 0x66, 0xa6, 0x97, 0xa0, 0x56, 0x45, 0xa4, 0x2a, 0x2e, 0xdc, 0x76, 0x25, 0x18, 0x84, 0x6, 0x9c, 0xff, 0xb3, 0xb9, 0x26, 0x17, 0xda, 0xa4, 0x49, 0xd4, 0x4f, 0xfa, 0x7c, 0xb8, 0xdf, 0xe5, 0xcd, 0xef, 0x79, 0xee, 0xbd, 0xf7, 0xde, 0xd1, 0xd4, 0xf4, 0xf, 0x1, 0xc3, 0x9, 0x82, 0x8c, 0x4, 0x41, 0xe0, 0x58, 0x43, 0x3a, 0x4e, 0x52, 0x34, 0xc3, 0x72, 0xbb, 0x22, 0xc0, 0xb1, 0xc, 0x4d, 0x91, 0xb8, 0xff, 0x74, 0xb2, 0x99, 0xe5, 0x5, 0x31, 0xd6, 0xd2, 0xda, 0x16, 0x8a, 0xd6, 0x96, 0x98, 0x28, 0xf0, 0x6c, 0x33, 0xe9, 0xf1, 0x2, 0x6f, 0xef, 0x88, 0x77, 0x76, 0x75, 0xf7, 0xf4, 0xf6, 0xc9, 0x11, 0xc8, 0xf4, 0xf5, 0xf, 0xc, 0x76, 0xc6, 0x3b, 0xda, 0xeb, 0x9c, 0xc0, 0x13, 0xd2, 0xd0, 0xee, 0x3d, 0x99, 0x0, 0xe, 0x50, 0x20, 0xec, 0xbd, 0xb5, 0x1, 0x40, 0xdd, 0xbb, 0x6f, 0x48, 0x4a, 0xd4, 0x28, 0x60, 0x49, 0x6e, 0x78, 0xff, 0x48, 0xe0, 0xa1, 0x90, 0xbb, 0x70, 0xeb, 0xf6, 0x1d, 0x1b, 0xd9, 0xbb, 0xf7, 0x16, 0x80, 0x7a, 0x60, 0x98, 0x4b, 0x62, 0x3b, 0xf7, 0x67, 0x84, 0x83, 0xa1, 0xbe, 0xdf, 0x5f, 0x7c, 0xb0, 0xf4, 0x70, 0x39, 0x57, 0xdd, 0xe7, 0x1e, 0x3d, 0x7e, 0xb2, 0xa8, 0xa8, 0x87, 0x4, 0xc6, 0x8d, 0x3, 0x9e, 0x1a, 0x3d, 0x7c, 0x24, 0x94, 0xbf, 0xf2, 0xf4, 0x59, 0xed, 0xf7, 0xf3, 0x17, 0x2b, 0x8a, 0x7a, 0x74, 0x34, 0x85, 0x2e, 0x41, 0x4a, 0xe2, 0xb1, 0x30, 0x7e, 0xee, 0xe5, 0xea, 0xab, 0x7a, 0xcb, 0xf2, 0xea, 0x6b, 0x70, 0x5c, 0x94, 0x48, 0xe7, 0x6, 0x14, 0x7f, 0x22, 0x34, 0xf0, 0x6f, 0xde, 0xbe, 0xf3, 0x9a, 0xde, 0x7f, 0xf8, 0x8, 0x4e, 0xf2, 0x94, 0x73, 0x3, 0x5a, 0x38, 0x85, 0x7e, 0xe4, 0xb, 0xc5, 0x52, 0x59, 0xd6, 0x8a, 0x16, 0xaa, 0x8b, 0xe, 0x8d, 0x6b, 0x9f, 0xfc, 0xa2, 0x4b, 0x6b, 0xe0, 0xb4, 0x40, 0xdb, 0x2, 0x4, 0x23, 0xf6, 0x20, 0xbb, 0xb1, 0x9e, 0xff, 0x5c, 0xcc, 0x6b, 0x90, 0xa8, 0xd9, 0x6c, 0xb, 0x1b, 0x9b, 0x7e, 0x81, 0xcd, 0xd, 0xa5, 0x5f, 0x64, 0x1c, 0x1, 0x76, 0xac, 0x17, 0x39, 0x0, 0x49, 0xeb, 0x15, 0xcd, 0x84, 0x2, 0x26, 0x12, 0xd8, 0xda, 0xf6, 0xb, 0x6c, 0x6f, 0x29, 0x67, 0xc6, 0x58, 0x47, 0x40, 0x1a, 0x47, 0x6f, 0xa8, 0x99, 0xd6, 0xf2, 0xa5, 0xa0, 0x19, 0x86, 0xa1, 0x57, 0x97, 0xaa, 0x35, 0x9b, 0x6b, 0x10, 0xd8, 0xac, 0xa2, 0x8e, 0x4b, 0xc8, 0x83, 0x18, 0x4a, 0x22, 0x1d, 0x7a, 0x50, 0xf8, 0xaa, 0x41, 0xa6, 0x66, 0x44, 0x78, 0xa0, 0xc6, 0x58, 0x37, 0x6, 0x13, 0xc8, 0x6e, 0x56, 0x64, 0xbd, 0x54, 0xf6, 0x8, 0x34, 0x8e, 0x1, 0x38, 0x8b, 0x62, 0x80, 0xd3, 0x69, 0xf7, 0x15, 0xb4, 0x92, 0x59, 0x2a, 0xc8, 0x1e, 0x81, 0xa0, 0x57, 0x48, 0xd3, 0x6e, 0x1e, 0x9c, 0x73, 0x7f, 0xe8, 0x95, 0x6f, 0x56, 0x2c, 0xcb, 0xba, 0xb3, 0x84, 0xe5, 0xc1, 0x79, 0x94, 0x7, 0x7f, 0x97, 0x89, 0xca, 0x5, 0x37, 0x13, 0x61, 0x2d, 0x5c, 0xfc, 0xf3, 0x5a, 0xb8, 0xb4, 0x53, 0xb, 0xbf, 0x51, 0x8d, 0xdf, 0x43, 0xab, 0x11, 0xf6, 0x83, 0xc9, 0xcb, 0xa1, 0x3e, 0xd4, 0xf7, 0x83, 0x1f, 0x40, 0xbd, 0x32, 0x59, 0xd3, 0xf, 0x60, 0x47, 0xe2, 0x84, 0xab, 0xd7, 0x82, 0xc8, 0x76, 0x47, 0x72, 0x9a, 0x92, 0xd5, 0x91, 0x7e, 0x82, 0xeb, 0x37, 0x4, 0x2e, 0x51, 0xdf, 0xd3, 0x92, 0x4c, 0x5c, 0xec, 0xea, 0x9e, 0x18, 0x91, 0x23, 0x91, 0xb9, 0x39, 0x30, 0x28, 0xc6, 0x99, 0xa4, 0xa7, 0x31, 0x63, 0x64, 0x8a, 0xe5, 0xd3, 0x53, 0xd3, 0x33, 0x6d, 0x11, 0x98, 0x99, 0x9e, 0x4a, 0xf3, 0x6c, 0x8a, 0xf4, 0xcd, 0x6, 0xc, 0x9f, 0xa5, 0xe6, 0xe6, 0xa5, 0xe8, 0xb9, 0x20, 0xcd, 0xcf, 0x51, 0xb3, 0x8d, 0x67, 0x8b, 0x35, 0x99, 0xa2, 0x7, 0x93, 0x35, 0x9a, 0x2, 0x26, 0xd3, 0xff, 0x8b, 0x5f, 0x3d, 0xdc, 0x7c, 0xb4, 0x8c, 0xb2, 0xd8, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_on_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x74, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x12, 0x12, 0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x1a, 0x1a, 0x1e, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x2c, 0x36, 0x27, 0x49, 0x65, 0x29, 0x5d, 0x85, 0x2a, 0x66, 0x95, 0x2a, 0x68, 0x99, 0x29, 0x64, 0x92, 0x28, 0x4c, 0x6b, 0x25, 0x27, 0x2d, 0x27, 0x43, 0x5c, 0x29, 0x5f, 0x89, 0x27, 0x49, 0x66, 0x25, 0x30, 0x3e, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x25, 0x2d, 0x38, 0x25, 0x3a, 0x4c, 0x27, 0x4d, 0x6b, 0x29, 0x60, 0x8c, 0x27, 0x44, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x59, 0x7f, 0x25, 0x34, 0x43, 0x25, 0x35, 0x45, 0x28, 0x58, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x40, 0x57, 0x27, 0x41, 0x57, 0x25, 0x2a, 0x33, 0x29, 0x5d, 0x87, 0x25, 0x34, 0x44, 0x25, 0x2b, 0x34, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x27, 0x43, 0x5b, 0x27, 0x4d, 0x6c, 0x27, 0x4e, 0x6d, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x28, 0x56, 0x7b, 0x26, 0x3b, 0x4e, 0x26, 0x3a, 0x4e, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0xe4, 0xe4, 0xe5, 0x27, 0x44, 0x5d, 0xdd, 0xc9, 0xf2, 0x7e, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0x8, 0x17, 0x35, 0x73, 0xd9, 0x7, 0x3a, 0x96, 0xf9, 0x9a, 0xb, 0x28, 0x76, 0xfb, 0x77, 0xde, 0x45, 0x5, 0x82, 0xc6, 0xc6, 0x37, 0xf, 0xe9, 0x4c, 0x4e, 0x4f, 0x50, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x37, 0x21, 0x5a, 0x6c, 0x0, 0x0, 0x2, 0x4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x63, 0x83, 0xdc, 0x60, 0x10, 0xc7, 0x1b, 0x3c, 0xc1, 0xda, 0x3e, 0xdb, 0x36, 0xe7, 0x6c, 0xdb, 0xa, 0xcf, 0xc6, 0x7e, 0xf8, 0x2a, 0x6d, 0xb8, 0xac, 0x7b, 0xbf, 0xf7, 0xf3, 0x1f, 0xcf, 0x7c, 0xf8, 0x97, 0xc0, 0x70, 0x82, 0x20, 0xb3, 0x42, 0x10, 0x38, 0x96, 0xd2, 0x1c, 0x27, 0x11, 0x45, 0x33, 0xac, 0x2d, 0xb, 0x2c, 0x43, 0x53, 0x88, 0xc4, 0xad, 0xde, 0x49, 0x3b, 0xe3, 0x70, 0xba, 0xdc, 0x1e, 0xaf, 0x2f, 0x23, 0x5e, 0x8f, 0xdb, 0xe5, 0x74, 0x30, 0x76, 0xd2, 0x14, 0x5, 0xee, 0xf, 0x4, 0x43, 0xe1, 0x48, 0x34, 0x16, 0x87, 0x2c, 0xc4, 0x63, 0xd1, 0x48, 0x38, 0x14, 0xc, 0xf8, 0x71, 0x83, 0x7d, 0xa2, 0xa0, 0xb0, 0xa8, 0x78, 0x4, 0x72, 0x64, 0xa4, 0xb8, 0xa8, 0xb0, 0x20, 0xa1, 0x53, 0xc0, 0x4a, 0xd8, 0xd2, 0xb2, 0x72, 0xc8, 0xc4, 0xe8, 0xd8, 0xf8, 0xc4, 0xa4, 0xc2, 0xd4, 0xf4, 0x28, 0x94, 0x97, 0x95, 0xb2, 0x25, 0x98, 0x96, 0x3f, 0xed, 0xac, 0xc8, 0x18, 0xfb, 0xcc, 0xec, 0xdc, 0xfc, 0xc2, 0xe2, 0xd2, 0x17, 0x96, 0x57, 0x56, 0xd7, 0xd6, 0x37, 0x66, 0xe2, 0x15, 0x4e, 0x5a, 0xad, 0x3, 0x5e, 0x59, 0x55, 0x5d, 0x93, 0xd1, 0x7e, 0x73, 0x6b, 0x1b, 0x74, 0xec, 0xec, 0x6e, 0xce, 0xd4, 0xd4, 0x56, 0x55, 0x7e, 0x4f, 0x82, 0x2c, 0x70, 0xd5, 0x41, 0x6, 0xf6, 0xf6, 0xb7, 0x56, 0xc0, 0xc0, 0xca, 0xd6, 0xc1, 0x5e, 0x5d, 0x7d, 0x41, 0x83, 0x12, 0x2, 0x86, 0x1c, 0x8d, 0x90, 0x89, 0xc3, 0xa3, 0x63, 0x30, 0xb1, 0x33, 0x77, 0x2, 0x8d, 0xe, 0xa4, 0x8, 0xe0, 0x94, 0xb3, 0x9, 0x54, 0x4e, 0xcf, 0x38, 0x5e, 0x0, 0x91, 0x93, 0x40, 0x94, 0x41, 0xe1, 0xfc, 0x2, 0x2c, 0x5c, 0x9e, 0x43, 0x73, 0x4b, 0xab, 0x92, 0x3, 0x41, 0xbb, 0xa2, 0xa0, 0x22, 0x5f, 0x9d, 0x5e, 0x73, 0xa7, 0x22, 0x27, 0x6b, 0x2, 0x37, 0xb7, 0x60, 0xe1, 0xee, 0x6, 0xda, 0xea, 0x69, 0x42, 0x11, 0x60, 0xda, 0x63, 0x5a, 0x0, 0xdc, 0x3d, 0xc0, 0xd5, 0x83, 0xf8, 0xc8, 0x8b, 0xaa, 0xc0, 0xd3, 0x33, 0x58, 0x78, 0x7e, 0x82, 0xf2, 0xe, 0x86, 0x54, 0x4, 0xa, 0x3a, 0xb5, 0x1e, 0x8a, 0x8f, 0x0, 0xf0, 0x72, 0x26, 0xca, 0x2f, 0x8f, 0xaa, 0xc0, 0xc4, 0x22, 0x58, 0x58, 0x9c, 0x0, 0xe8, 0x2a, 0xf8, 0x26, 0xc0, 0xb8, 0xb5, 0x21, 0xba, 0xff, 0x12, 0xc1, 0xd9, 0xeb, 0x67, 0xe3, 0xb7, 0xb3, 0x9c, 0x23, 0xa0, 0x5d, 0x6d, 0xa0, 0xf2, 0xf8, 0x0, 0xf7, 0xbc, 0xf0, 0x59, 0x40, 0xe0, 0x72, 0xad, 0x1, 0x4e, 0xb5, 0xe8, 0xba, 0x20, 0xf2, 0x8f, 0xfc, 0xd9, 0xd7, 0x2, 0x3e, 0xe6, 0xda, 0x5, 0xc, 0x39, 0xba, 0x41, 0xe3, 0xfe, 0x41, 0x2, 0x38, 0x15, 0x0, 0x24, 0x21, 0xf3, 0x1c, 0x74, 0x7, 0x11, 0xf6, 0xd3, 0x93, 0xa8, 0xee, 0x42, 0x6d, 0xfe, 0xbb, 0xd0, 0xa3, 0xed, 0x42, 0xe, 0xdb, 0xb8, 0x61, 0xdc, 0xc6, 0xa4, 0xba, 0x8d, 0xea, 0x3d, 0xe8, 0xed, 0xab, 0xc9, 0xe7, 0x1e, 0xd4, 0xf4, 0xf5, 0xab, 0xf7, 0x40, 0xb9, 0x48, 0xac, 0x73, 0x60, 0x10, 0x72, 0x66, 0x70, 0xc0, 0xc9, 0x26, 0x8c, 0x37, 0xad, 0x84, 0xe, 0xba, 0xc2, 0x91, 0xb6, 0x72, 0xc8, 0x4a, 0x79, 0x5b, 0x24, 0xec, 0xa, 0xd2, 0x25, 0xb8, 0xf9, 0x2a, 0x57, 0x32, 0x8e, 0x96, 0xfa, 0x8e, 0x21, 0x5f, 0x16, 0x86, 0x3a, 0xea, 0x5b, 0x1c, 0x4c, 0x25, 0x89, 0x59, 0xbf, 0x4a, 0x3, 0x6a, 0x1d, 0x2e, 0xc8, 0xfe, 0x17, 0xa, 0x86, 0x5b, 0x51, 0x3, 0x8e, 0xa5, 0xf9, 0x4c, 0x64, 0xe, 0x68, 0x9f, 0xe9, 0xbd, 0xf0, 0x9, 0xb7, 0x71, 0x36, 0xc6, 0x9b, 0x3d, 0x7f, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_on_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x53, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x2e, 0x2e, 0x2e, 0x46, 0x46, 0x46, 0x57, 0x57, 0x57, 0x60, 0x60, 0x60, 0x62, 0x62, 0x62, 0x5e, 0x5e, 0x5e, 0x4a, 0x4a, 0x4a, 0x12, 0x12, 0x15, 0x25, 0x27, 0x2d, 0x42, 0x42, 0x42, 0x59, 0x59, 0x59, 0x32, 0x32, 0x32, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x39, 0x39, 0x39, 0x49, 0x49, 0x49, 0x5a, 0x5a, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x48, 0x48, 0x48, 0x54, 0x54, 0x54, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x25, 0x26, 0x2b, 0x3f, 0x3f, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x2c, 0x2c, 0x2c, 0x58, 0x58, 0x58, 0x1a, 0x1a, 0x1e, 0x40, 0x40, 0x44, 0x56, 0x56, 0x58, 0x80, 0x80, 0x80, 0x79, 0x79, 0x79, 0x3c, 0x3c, 0x3d, 0x2e, 0x2e, 0x30, 0x27, 0x27, 0x29, 0x64, 0x64, 0x66, 0x41, 0x41, 0x41, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 0x5d, 0x5f, 0x34, 0x34, 0x36, 0x52, 0x52, 0x52, 0x3a, 0x3a, 0x3a, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x32, 0x32, 0x37, 0x42, 0x42, 0x44, 0x6a, 0x6a, 0x6d, 0x5b, 0x5b, 0x5b, 0x2f, 0x2f, 0x2f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x49, 0x4a, 0x0, 0x0, 0x0, 0x50, 0x50, 0x51, 0x70, 0x70, 0x74, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbd, 0xb, 0x85, 0x35, 0x0, 0x0, 0x0, 0x71, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xff, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xff, 0x7, 0x3a, 0x96, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb, 0x28, 0x76, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x77, 0xde, 0xff, 0xff, 0x45, 0x5, 0x82, 0xff, 0xff, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x37, 0xf, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x4c, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4e, 0x4f, 0xff, 0x50, 0xff, 0xff, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x49, 0x96, 0x6e, 0xf, 0x0, 0x0, 0x1, 0xfa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x18, 0x5e, 0x0, 0xd0, 0x5c, 0x39, 0x28, 0x49, 0x12, 0xc0, 0x60, 0xb8, 0xda, 0xdd, 0xcb, 0x31, 0x33, 0xb6, 0x6d, 0xdb, 0x5e, 0xdb, 0xd6, 0xfb, 0x17, 0x4e, 0x7d, 0x37, 0xe6, 0xf9, 0x2b, 0x23, 0x7f, 0x9c, 0x20, 0x28, 0x86, 0xe1, 0xb, 0xc1, 0x30, 0x14, 0x99, 0x6a, 0x8e, 0xe2, 0x4, 0x49, 0xd1, 0xcc, 0xda, 0x2, 0x18, 0x9a, 0x22, 0x9, 0x1c, 0x9d, 0xf4, 0x8e, 0xaf, 0xd3, 0x1b, 0x9b, 0x5b, 0xdb, 0x1c, 0x2e, 0x6f, 0x2e, 0x5c, 0xce, 0xf6, 0xd6, 0xe6, 0x6, 0xbd, 0x8e, 0x8f, 0x45, 0x81, 0xf2, 0x5, 0x42, 0x91, 0x58, 0x22, 0x95, 0xc9, 0x61, 0x1, 0x72, 0x99, 0x54, 0x22, 0x16, 0x9, 0x5, 0x7c, 0x74, 0xc4, 0x5e, 0xa1, 0x54, 0xa9, 0x35, 0x5a, 0x58, 0x12, 0xad, 0x46, 0xad, 0x52, 0x2a, 0x86, 0x14, 0x10, 0x1d, 0xa3, 0x37, 0x18, 0x61, 0x1e, 0x26, 0xb3, 0xc5, 0x6a, 0x63, 0xb1, 0x3b, 0x4c, 0x60, 0x34, 0xe8, 0x19, 0x1d, 0x32, 0xc8, 0x9f, 0xda, 0x74, 0xce, 0x8d, 0xdd, 0xe5, 0xf6, 0x98, 0xbd, 0x3e, 0xff, 0x57, 0x2, 0xa6, 0x60, 0x28, 0xec, 0x76, 0xc9, 0x9d, 0x9b, 0x54, 0xbf, 0xe, 0x68, 0x24, 0x1a, 0x8b, 0xcf, 0xb5, 0x4f, 0x24, 0x53, 0x30, 0x44, 0x3a, 0x99, 0x70, 0xc5, 0x33, 0xd1, 0xc8, 0x8f, 0x24, 0x70, 0xe5, 0x56, 0x16, 0xe6, 0x90, 0xcb, 0x27, 0x4d, 0x63, 0x9, 0x25, 0xf3, 0xb9, 0x6c, 0x41, 0x59, 0x64, 0x43, 0x40, 0x88, 0x8d, 0x12, 0xcc, 0xa3, 0x5c, 0x49, 0xc3, 0x18, 0x69, 0x4f, 0x19, 0x4a, 0x1b, 0x4, 0x2b, 0x80, 0x92, 0x9b, 0x55, 0xe8, 0x53, 0xab, 0x37, 0x9a, 0x2d, 0x68, 0x37, 0x3a, 0xd0, 0xee, 0x2, 0x4b, 0xcf, 0x1, 0x13, 0x38, 0x7a, 0xb0, 0xb3, 0xbb, 0xc7, 0xe6, 0x80, 0x51, 0x5b, 0x52, 0xe8, 0xd3, 0xdd, 0xaf, 0x1d, 0x34, 0x6a, 0xed, 0x46, 0x77, 0x20, 0x70, 0x78, 0x4, 0x13, 0x1c, 0x1d, 0xc2, 0x71, 0x81, 0xc2, 0x58, 0x1, 0xfa, 0x44, 0x36, 0x8, 0xa0, 0x71, 0xa, 0xb0, 0x7f, 0xd6, 0x3e, 0x6f, 0xb6, 0xfb, 0x2, 0x17, 0x97, 0x30, 0xc1, 0xe5, 0x5, 0x18, 0xaf, 0x68, 0x9c, 0x15, 0x50, 0x5e, 0xf, 0x7a, 0xd8, 0x3e, 0x7, 0x80, 0x9b, 0x7a, 0xbb, 0x7b, 0x73, 0xde, 0x17, 0xb0, 0xfa, 0x60, 0x2, 0x9f, 0x15, 0xe0, 0x56, 0xf9, 0x5d, 0x80, 0xde, 0x1e, 0xc, 0xd1, 0xe9, 0xd7, 0x8, 0xea, 0x77, 0xed, 0x2e, 0xdc, 0xd7, 0x97, 0x8e, 0x80, 0xda, 0x3a, 0x86, 0x3e, 0xe7, 0x67, 0x70, 0xda, 0x6c, 0xb5, 0xbb, 0xd0, 0x6a, 0x2c, 0x5b, 0x3, 0x94, 0xdc, 0xad, 0x42, 0x9f, 0x76, 0xf3, 0xbc, 0x59, 0x87, 0xaf, 0xe1, 0x9f, 0x2f, 0xd5, 0x5, 0x76, 0xe, 0x1e, 0x60, 0xc0, 0xe9, 0x59, 0x7, 0xa0, 0xd6, 0x2, 0xe8, 0xb4, 0xe6, 0xcf, 0xc1, 0x83, 0x90, 0x40, 0x7e, 0x79, 0x12, 0xfb, 0xbb, 0x90, 0x59, 0x7d, 0x17, 0x1e, 0xd9, 0x5d, 0xf8, 0xd5, 0x6d, 0xec, 0xdf, 0x83, 0xa7, 0xe7, 0xf8, 0x2a, 0xf7, 0x20, 0xfe, 0xfc, 0xc2, 0xde, 0x83, 0xfe, 0x45, 0x62, 0x36, 0x5f, 0xdf, 0x60, 0x69, 0xde, 0x5e, 0x37, 0x19, 0xc5, 0xe8, 0x4d, 0xd3, 0x51, 0xc2, 0x2d, 0xb1, 0xe4, 0xd8, 0x8, 0xb, 0x31, 0x1e, 0x4b, 0xc4, 0x5b, 0x42, 0x4a, 0x87, 0x8e, 0x5f, 0xe5, 0x8, 0xbd, 0xb1, 0x5b, 0xb8, 0x7a, 0xe7, 0x2d, 0xe0, 0xfd, 0xaa, 0xb0, 0xbb, 0x41, 0x47, 0x70, 0x64, 0xf2, 0xab, 0x14, 0x89, 0xbd, 0xf, 0xe5, 0xe2, 0xbf, 0xa0, 0xfc, 0xd8, 0x23, 0x8a, 0x28, 0x32, 0xe3, 0x33, 0xe1, 0x4b, 0xc0, 0x7e, 0xa6, 0xff, 0x87, 0xcf, 0xb, 0x94, 0xb9, 0x37, 0x3c, 0xc6, 0xd8, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_on_disabled_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x69, 0x50, 0x4c, 0x54, 0x45, 0x93, 0x7f, 0x2b, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x19, 0x19, 0x1c, 0x2b, 0x26, 0x2c, 0x40, 0x40, 0x44, 0x4e, 0x4e, 0x52, 0x1a, 0x1a, 0x1d, 0x32, 0x32, 0x37, 0x2c, 0x26, 0x2c, 0x26, 0x25, 0x2a, 0x27, 0x25, 0x2a, 0x11, 0x11, 0x14, 0x2f, 0x26, 0x2d, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x15, 0x15, 0x18, 0x5b, 0x5b, 0x5f, 0x84, 0x84, 0x87, 0x77, 0x77, 0x7a, 0x69, 0x69, 0x6c, 0x20, 0x20, 0x24, 0x24, 0x24, 0x27, 0x23, 0x23, 0x28, 0x1a, 0x1a, 0x1e, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0xd7, 0x77, 0xc6, 0x92, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x1, 0x35, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xd1, 0x72, 0x83, 0x20, 0x10, 0x45, 0x4d, 0x90, 0xb8, 0x18, 0x96, 0x6c, 0xb5, 0x24, 0x41, 0x68, 0x9b, 0xf4, 0xff, 0x3f, 0xb2, 0x84, 0xb4, 0x8a, 0xa, 0x3a, 0x9d, 0x74, 0xa6, 0x2f, 0xb9, 0xbe, 0x38, 0xca, 0x3d, 0x5c, 0x16, 0xd8, 0xa2, 0x78, 0xea, 0x2f, 0xb5, 0xd9, 0xb2, 0xb2, 0xe4, 0x2b, 0x2a, 0x4b, 0xb6, 0xdd, 0x24, 0xed, 0xbb, 0x8a, 0x1, 0x8, 0x21, 0xee, 0xe3, 0xc4, 0x4d, 0x20, 0x6a, 0x91, 0x84, 0x54, 0xbb, 0xb9, 0x7f, 0xcf, 0x40, 0xa2, 0xea, 0x85, 0x32, 0x50, 0x6a, 0xc8, 0xe4, 0xd8, 0x4f, 0xfd, 0x7, 0x26, 0x48, 0xe1, 0x4b, 0x13, 0x7e, 0x37, 0x92, 0x50, 0x51, 0x1b, 0x92, 0xe4, 0x56, 0x72, 0x18, 0xfb, 0x5f, 0x99, 0x40, 0xd4, 0xf1, 0x8, 0x42, 0x6c, 0x17, 0x6b, 0x71, 0x1c, 0x1, 0x4e, 0x40, 0x21, 0x74, 0x24, 0x89, 0xd4, 0x2c, 0x11, 0x4e, 0xb1, 0xff, 0xc, 0x52, 0xe9, 0xe9, 0x8, 0x52, 0x8b, 0x11, 0xf8, 0x39, 0x2, 0x6c, 0x7d, 0x80, 0xf9, 0x8, 0xa4, 0xe1, 0xd5, 0x74, 0x16, 0xb9, 0xee, 0x5a, 0xae, 0xdd, 0xcf, 0xb7, 0xb7, 0x8, 0xe0, 0x2b, 0x40, 0x73, 0x40, 0x4, 0x75, 0x6, 0xa9, 0x43, 0xdd, 0xb9, 0x8, 0xc0, 0x46, 0x0, 0x25, 0xe7, 0x0, 0xa9, 0xfa, 0x0, 0x9d, 0xe7, 0x1b, 0xd4, 0xce, 0xea, 0x1, 0x50, 0x8e, 0x1, 0x89, 0x82, 0x35, 0x3d, 0x20, 0xb8, 0x94, 0xd1, 0x4e, 0xb9, 0x1, 0xc0, 0x7f, 0x91, 0x80, 0x42, 0x2, 0xe5, 0xcd, 0xd6, 0x24, 0x13, 0xbc, 0xc3, 0x5a, 0xd, 0x90, 0x93, 0xf5, 0x4b, 0xf0, 0x8b, 0x49, 0xd6, 0x60, 0x75, 0x17, 0xb4, 0x75, 0xd6, 0x84, 0x95, 0xb8, 0xe4, 0x2e, 0xac, 0x9f, 0x3, 0xba, 0x9d, 0x4b, 0xf4, 0xb3, 0xb4, 0xfd, 0x4c, 0xf1, 0x39, 0x28, 0x3e, 0xc4, 0x63, 0x27, 0xb1, 0x38, 0x3e, 0x7a, 0x17, 0xb2, 0xb7, 0x31, 0xeb, 0x9f, 0xdc, 0xc6, 0xa2, 0xb8, 0x30, 0x68, 0xa7, 0xfd, 0x60, 0xc1, 0x7f, 0x99, 0x77, 0x94, 0xeb, 0x27, 0xd4, 0x70, 0x6f, 0x48, 0xe2, 0x5b, 0xe0, 0x9f, 0xa4, 0xbf, 0xba, 0x66, 0x7b, 0x22, 0x5f, 0x55, 0xb6, 0x27, 0x3e, 0xf5, 0x8f, 0xfa, 0x2, 0xa0, 0x14, 0x20, 0xeb, 0xde, 0xb1, 0x8c, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char toggle_on_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x9b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x24, 0x24, 0x28, 0x20, 0x20, 0x25, 0x14, 0x14, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x22, 0x22, 0x27, 0x10, 0x10, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x19, 0x19, 0x1c, 0x12, 0x12, 0x15, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0x1e, 0x22, 0x23, 0x23, 0x27, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1d, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x15, 0x15, 0x18, 0xb, 0xb, 0xd, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x1a, 0x1a, 0x1e, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x11, 0x11, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x25, 0x2c, 0x36, 0x28, 0x4c, 0x6b, 0x29, 0x64, 0x92, 0x2a, 0x68, 0x99, 0x2a, 0x66, 0x95, 0x29, 0x5d, 0x85, 0x27, 0x49, 0x65, 0x25, 0x25, 0x28, 0x25, 0x27, 0x2d, 0x27, 0x44, 0x5c, 0x29, 0x60, 0x8c, 0x27, 0x4d, 0x6b, 0x25, 0x3a, 0x4c, 0x25, 0x2d, 0x38, 0x25, 0x26, 0x2c, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2d, 0x25, 0x30, 0x3e, 0x27, 0x49, 0x66, 0x29, 0x5f, 0x89, 0x27, 0x43, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x58, 0x7f, 0x25, 0x35, 0x45, 0x25, 0x34, 0x43, 0x28, 0x59, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x41, 0x57, 0x27, 0x40, 0x57, 0x25, 0x2b, 0x34, 0x25, 0x34, 0x44, 0x29, 0x5d, 0x87, 0x25, 0x2a, 0x33, 0x27, 0x43, 0x5b, 0x27, 0x4e, 0x6d, 0x27, 0x4d, 0x6c, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x28, 0x56, 0x7b, 0x26, 0x3a, 0x4e, 0x26, 0x3b, 0x4e, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x27, 0x44, 0x5d, 0xa6, 0xa2, 0x25, 0x5b, 0x0, 0x0, 0x0, 0x4c, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0xfa, 0xe5, 0x98, 0x8, 0x17, 0x35, 0x73, 0xd9, 0xf3, 0x86, 0x7, 0x3a, 0x96, 0xf9, 0xb4, 0x9a, 0xb9, 0xb, 0x28, 0x77, 0xfb, 0x8b, 0x5, 0x45, 0xde, 0xf6, 0x82, 0x9b, 0xf, 0x37, 0xc6, 0xe6, 0xe9, 0xfb, 0x4e, 0x50, 0x83, 0x9c, 0x78, 0x8c, 0x3c, 0x9c, 0xbb, 0x74, 0xda, 0x87, 0x53, 0x14, 0xd0, 0x92, 0x4e, 0x2c, 0x0, 0x0, 0x2, 0x35, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0x63, 0x60, 0x18, 0x44, 0x80, 0x91, 0x89, 0x99, 0x99, 0x85, 0x20, 0x60, 0x66, 0x66, 0x62, 0xc4, 0xaa, 0x9d, 0x89, 0x85, 0x95, 0x8d, 0x9d, 0x83, 0x93, 0x8b, 0x0, 0xe0, 0xe4, 0x60, 0x67, 0x63, 0x65, 0x61, 0xc2, 0xb4, 0x9d, 0x85, 0x9b, 0x83, 0x87, 0x97, 0x8f, 0x5f, 0x40, 0x50, 0x8, 0x2f, 0x10, 0x14, 0xe0, 0xe7, 0xe3, 0xe5, 0xe1, 0xe0, 0x66, 0x41, 0x73, 0x5, 0x93, 0xb0, 0x88, 0xa8, 0x98, 0xb8, 0x84, 0xa4, 0x94, 0xb4, 0xf, 0x1, 0xe0, 0x2b, 0x2d, 0x23, 0x2b, 0x27, 0x26, 0x2a, 0x22, 0x8c, 0xe2, 0x8, 0x26, 0x79, 0x5, 0x45, 0x25, 0x65, 0x5f, 0xc, 0xd5, 0x7e, 0xfe, 0x7e, 0x58, 0xd, 0x51, 0x51, 0x55, 0x54, 0x90, 0x47, 0x32, 0x81, 0x51, 0x8d, 0x53, 0x5d, 0x43, 0xd3, 0x27, 0x20, 0x30, 0x28, 0x18, 0x2, 0x42, 0x42, 0xc3, 0x2, 0x20, 0x6, 0x84, 0xe3, 0x70, 0x87, 0x96, 0x3a, 0xa7, 0x1a, 0x23, 0xc2, 0xff, 0xec, 0xbc, 0xda, 0xd2, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x20, 0x90, 0x90, 0x98, 0x94, 0x9c, 0x12, 0x1, 0x36, 0x2, 0x97, 0x4f, 0x74, 0x78, 0xd9, 0xe1, 0xe1, 0xc0, 0xa4, 0xab, 0xa7, 0x6f, 0x10, 0x91, 0x9a, 0x96, 0x8e, 0xac, 0x22, 0x23, 0x33, 0x35, 0x2, 0x6f, 0x58, 0x18, 0xea, 0xe9, 0x42, 0x3d, 0xc1, 0x68, 0xa4, 0x60, 0x6c, 0x92, 0x95, 0x9d, 0x19, 0x8b, 0xaa, 0x22, 0x36, 0x33, 0x27, 0xb, 0x9f, 0x9, 0xa6, 0xc6, 0xa, 0x46, 0x10, 0x27, 0x30, 0xb1, 0xf2, 0x98, 0xf9, 0xe4, 0x26, 0xa7, 0xa3, 0xab, 0xc8, 0xcb, 0x2f, 0xc0, 0x1b, 0x1f, 0xe6, 0x3c, 0xac, 0x10, 0x3, 0x58, 0x2c, 0x2c, 0xad, 0x7c, 0xa, 0x8b, 0x30, 0x55, 0x14, 0x17, 0xc2, 0x99, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x3e, 0x95, 0x65, 0x55, 0x3e, 0x95, 0xd5, 0x30, 0x31, 0x6b, 0x4b, 0xb, 0x88, 0x1f, 0x84, 0xd9, 0x8d, 0x6d, 0x7c, 0x6a, 0x6a, 0x31, 0xd, 0xa8, 0xab, 0x81, 0x33, 0xab, 0xeb, 0x4b, 0x1a, 0xca, 0x4a, 0x2a, 0xcb, 0xaa, 0x91, 0xc, 0xb0, 0x35, 0x66, 0x67, 0x6, 0x1b, 0xa0, 0xc6, 0x61, 0xa7, 0xe9, 0xd3, 0xd8, 0x84, 0x69, 0x40, 0x53, 0x23, 0xdc, 0x1, 0x65, 0xcd, 0x3e, 0x3e, 0xf5, 0x2d, 0x95, 0xad, 0xe5, 0x95, 0x8, 0x3, 0x7c, 0xed, 0x38, 0x58, 0x20, 0x6, 0x28, 0xd8, 0xfb, 0xf8, 0x84, 0x24, 0x60, 0x1a, 0x90, 0x10, 0x2, 0x63, 0x55, 0xb6, 0x2, 0x89, 0xb6, 0xd2, 0xca, 0xea, 0xb6, 0x56, 0x84, 0x1, 0x3e, 0xf6, 0xa, 0x2c, 0xc4, 0xba, 0xa0, 0x19, 0xe4, 0x82, 0xd2, 0x76, 0xa0, 0xe6, 0xf2, 0x52, 0x4c, 0x17, 0x10, 0x13, 0x6, 0xad, 0x2d, 0x3e, 0xcd, 0xe5, 0x15, 0x40, 0x3, 0x2a, 0xca, 0x30, 0xc3, 0x80, 0x98, 0x58, 0xa8, 0x2c, 0x6f, 0x2d, 0x2f, 0x5, 0x7, 0x60, 0x2b, 0x66, 0x2c, 0x30, 0xb1, 0x8a, 0x3a, 0x10, 0x4c, 0x7, 0xcd, 0x2d, 0x55, 0xc0, 0xb0, 0xac, 0xf0, 0xf1, 0xa9, 0xaa, 0x80, 0x9, 0x39, 0x8a, 0x42, 0xd3, 0x1, 0xc5, 0x29, 0x11, 0x94, 0x17, 0x9c, 0x48, 0xcf, 0xb, 0xce, 0xf0, 0xbc, 0x0, 0xcb, 0x8d, 0x1d, 0xa8, 0xb9, 0x31, 0x12, 0xbf, 0x7e, 0xe4, 0xdc, 0x8, 0x2a, 0xf, 0x5c, 0x5c, 0xd, 0xb0, 0x94, 0x7, 0xb8, 0xf5, 0xbb, 0xb9, 0x20, 0x95, 0x7, 0xa0, 0x12, 0x89, 0x93, 0xd7, 0xdd, 0x3, 0x53, 0x9d, 0x5f, 0x38, 0xf6, 0xf2, 0x40, 0xc5, 0x93, 0x97, 0x53, 0x1e, 0xb5, 0x4c, 0x53, 0x63, 0x17, 0xe5, 0x13, 0x97, 0xb0, 0xd1, 0xf4, 0x21, 0x8, 0x7c, 0x6d, 0x65, 0xe5, 0xf8, 0x44, 0xd9, 0xd5, 0xd0, 0xa, 0x66, 0x46, 0x16, 0x5d, 0xe, 0x1e, 0x4b, 0x63, 0x3b, 0x2f, 0x21, 0x2, 0xc0, 0xcb, 0xce, 0xd8, 0x92, 0x87, 0x43, 0x97, 0x5, 0xa3, 0x6e, 0x60, 0x64, 0x32, 0x62, 0xb5, 0xf0, 0x56, 0x20, 0x5c, 0x2f, 0x28, 0x78, 0x5b, 0xb0, 0x1a, 0x61, 0xaf, 0x5b, 0x80, 0x35, 0x13, 0xe1, 0x8a, 0x9, 0x58, 0x35, 0xe1, 0xa8, 0x99, 0x86, 0x2e, 0x0, 0x0, 0x69, 0x2c, 0x6b, 0xc2, 0xf1, 0x2f, 0x53, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tooltip_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0xdd, 0xdd, 0xdd, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0xbc, 0x3, 0x4f, 0xe9, 0x0, 0x0, 0x0, 0xd, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xcc, 0xff, 0xb7, 0x4a, 0xbe, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x54, 0x76, 0x1, 0x2, 0x23, 0x1, 0x6, 0xd1, 0xf4, 0xe, 0x20, 0x28, 0xb, 0x64, 0xd0, 0x5c, 0x7d, 0x17, 0x8, 0x76, 0x4d, 0x62, 0x70, 0x7f, 0x7f, 0x6, 0x8, 0xfe, 0x95, 0x30, 0x78, 0xdc, 0x1, 0x31, 0xce, 0xb6, 0x50, 0xc8, 0x80, 0x1b, 0x8, 0xb7, 0x2, 0x6e, 0x29, 0xdc, 0x19, 0x0, 0xcf, 0x24, 0x4d, 0xb3, 0xd0, 0x4d, 0xb9, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tree_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tree_bg_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x94, 0xc8, 0x67, 0x6b, 0x60, 0xe6, 0x60, 0x64, 0x80, 0x80, 0xff, 0xc, 0x7f, 0x7f, 0xfc, 0x6a, 0x60, 0x94, 0xfb, 0xc0, 0xce, 0xcf, 0xc2, 0x80, 0x10, 0xfc, 0xc3, 0xf0, 0xf3, 0x23, 0xa3, 0xe2, 0x4f, 0xe, 0x36, 0x54, 0xc1, 0x1f, 0xbf, 0x18, 0x95, 0xbe, 0x73, 0x70, 0xb0, 0x30, 0xc0, 0x1, 0x48, 0xf0, 0x7, 0x85, 0x82, 0x58, 0x2d, 0xc2, 0xe6, 0xa4, 0x4f, 0x20, 0xc7, 0x37, 0x32, 0xb3, 0x23, 0x39, 0xfe, 0xfb, 0xaf, 0x46, 0x0, 0xee, 0x2a, 0x2f, 0xce, 0x4c, 0x47, 0x66, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tree_title_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x1, 0x3, 0x0, 0x0, 0x0, 0x25, 0x3d, 0x6d, 0x22, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x23, 0x25, 0x4c, 0x4a, 0x4e, 0x1, 0xf9, 0x98, 0x2e, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x0, 0x0, 0x0, 0x30, 0x0, 0x1, 0x6e, 0xa6, 0xf, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char tree_title_pressed_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x1, 0x3, 0x0, 0x0, 0x0, 0x25, 0x3d, 0x6d, 0x22, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x36, 0x34, 0x36, 0x4c, 0x4a, 0x4e, 0x14, 0xd7, 0x5b, 0xf8, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x0, 0x0, 0x0, 0x30, 0x0, 0x1, 0x6e, 0xa6, 0xf, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char unchecked_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x23, 0xc3, 0x49, 0x39, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xa4, 0x7f, 0xe1, 0x5a, 0x0, 0x0, 0x0, 0x4f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xb5, 0xcf, 0x45, 0x2, 0x80, 0x40, 0x8, 0x0, 0x40, 0x97, 0x66, 0xfb, 0xff, 0x9f, 0xb5, 0xdb, 0xb3, 0x73, 0xa4, 0x19, 0xbe, 0x2, 0x20, 0xf1, 0x8a, 0x10, 0xc2, 0x1c, 0x0, 0xd1, 0x94, 0x57, 0x49, 0x5, 0xe6, 0x0, 0x6a, 0xa9, 0x6d, 0x55, 0x8b, 0xe2, 0x1c, 0xa0, 0x54, 0xfb, 0xae, 0x26, 0x9a, 0x3, 0x9c, 0xdb, 0x11, 0x68, 0x99, 0xff, 0xa, 0x7c, 0xd6, 0xde, 0xf, 0x33, 0x9c, 0x3, 0xe0, 0x76, 0x9c, 0x1e, 0x1d, 0xbe, 0xcf, 0x7d, 0x4c, 0x93, 0xe2, 0x8, 0xa4, 0x66, 0x3c, 0xec, 0xed, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char unchecked_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x58, 0x58, 0x5b, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x27, 0xa1, 0xa6, 0x53, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xb4, 0xfa, 0xfa, 0xfb, 0xc7, 0x8b, 0xf6, 0x7e, 0x0, 0x0, 0x0, 0x52, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0xb5, 0xcf, 0x31, 0x12, 0xc0, 0x20, 0x8, 0x44, 0xd1, 0x15, 0x50, 0x83, 0x11, 0xe5, 0xfe, 0xa7, 0xcd, 0xe8, 0xc4, 0x22, 0xf4, 0x79, 0xe5, 0x36, 0x7c, 0x80, 0x8, 0x89, 0x58, 0xf2, 0x26, 0x4c, 0x9, 0x0, 0x15, 0xf5, 0xb9, 0xb9, 0x16, 0x2, 0xc0, 0x3a, 0xac, 0x6f, 0x36, 0x94, 0x1, 0x88, 0xdb, 0xfd, 0x32, 0x17, 0x0, 0x79, 0xf6, 0x33, 0xf4, 0x99, 0xff, 0x1a, 0xe2, 0xd9, 0x4f, 0x58, 0x5b, 0x61, 0x54, 0xdb, 0x49, 0xbf, 0xea, 0x4a, 0x8f, 0xcf, 0x45, 0xf, 0x4, 0x40, 0x7, 0x90, 0xb0, 0x7b, 0x47, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char updown_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0x81, 0x83, 0xf6, 0xf6, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x8d, 0x4e, 0xb5, 0x1, 0xc0, 0x30, 0xc, 0xf3, 0x15, 0xfe, 0xff, 0x96, 0x64, 0xa, 0x6c, 0xca, 0x56, 0xd2, 0x25, 0x65, 0xe6, 0xc8, 0x8b, 0x49, 0x20, 0x79, 0x28, 0x95, 0x81, 0xa1, 0xd4, 0x7d, 0x4, 0xbb, 0xa1, 0x50, 0xea, 0x3c, 0xa6, 0x71, 0x98, 0x96, 0x69, 0x58, 0x31, 0xcc, 0xb7, 0xe5, 0x2f, 0x48, 0x63, 0x26, 0xf6, 0xa2, 0xd4, 0x18, 0xf9, 0x7, 0x2d, 0xe3, 0x46, 0x89, 0xb4, 0xd2, 0xf8, 0xa3, 0x68, 0xe3, 0xd7, 0x14, 0x20, 0xe6, 0xc3, 0x3d, 0xd8, 0xca, 0x5e, 0x94, 0x32, 0xd0, 0x3, 0x91, 0xba, 0x5f, 0x1b, 0x4a, 0x9b, 0x12, 0x62, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vseparator_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x2, 0x3, 0x0, 0x0, 0x0, 0xb9, 0x61, 0x56, 0x18, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x73, 0x9b, 0xaa, 0xce, 0xdc, 0xe1, 0xeb, 0x64, 0x9a, 0x78, 0x0, 0x0, 0x0, 0x3, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xb3, 0xb3, 0x67, 0xf6, 0xdb, 0x93, 0x0, 0x0, 0x0, 0xf, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x80, 0x83, 0xac, 0x95, 0xc, 0x48, 0x0, 0x0, 0xe, 0x79, 0x1, 0x14, 0x17, 0x9a, 0x55, 0x26, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vslider_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x51, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x34, 0x33, 0x3a, 0x2d, 0x2c, 0x32, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x40, 0x3e, 0x4a, 0x20, 0x20, 0x24, 0x34, 0x33, 0x3a, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x1e, 0x1e, 0x23, 0x23, 0x23, 0x27, 0x2d, 0x2c, 0x32, 0x1f, 0x1f, 0x23, 0x30, 0x7, 0x9c, 0xfe, 0x0, 0x0, 0x0, 0x13, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x4, 0x1a, 0x40, 0x5d, 0x19, 0x28, 0x96, 0xf0, 0xfd, 0x94, 0x95, 0xfc, 0x93, 0xfc, 0xc0, 0x0, 0xb4, 0xa, 0x5f, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x2c, 0xc8, 0x83, 0x11, 0x0, 0x30, 0x0, 0xc0, 0xc0, 0xda, 0xf6, 0xfe, 0x8b, 0xd6, 0x39, 0xe6, 0xc1, 0xe, 0x12, 0xca, 0x38, 0x67, 0x82, 0xc0, 0x73, 0xe7, 0xa5, 0xd2, 0xc6, 0x68, 0x2b, 0xbf, 0x40, 0xe1, 0x7c, 0x2e, 0x25, 0xfb, 0x20, 0x3e, 0x30, 0x9d, 0x6b, 0x6b, 0x35, 0x6b, 0xf6, 0x81, 0x9b, 0xd2, 0x76, 0x25, 0xf2, 0x7, 0x28, 0xf5, 0x71, 0x60, 0xf4, 0x84, 0x2e, 0xe0, 0x35, 0x49, 0x29, 0x51, 0x90, 0x80, 0xa8, 0x94, 0x24, 0x33, 0x19, 0x2, 0x18, 0x86, 0xa2, 0x5b, 0x8b, 0xe9, 0x30, 0x4c, 0xa7, 0x63, 0x7a, 0xe, 0xc3, 0xfb, 0x0, 0x89, 0x5c, 0xa, 0x6b, 0x4f, 0x78, 0xac, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vslider_grabber_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xbc, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x17, 0x60, 0x66, 0xe0, 0x65, 0x90, 0x61, 0x50, 0x67, 0xd0, 0x60, 0x50, 0x62, 0x10, 0x67, 0xe0, 0x2, 0xf2, 0x19, 0x51, 0xa5, 0xc5, 0xd2, 0xf2, 0x3a, 0xf, 0xce, 0x78, 0x3f, 0xed, 0x43, 0xff, 0xed, 0xc6, 0xf5, 0x41, 0xa9, 0x8a, 0x86, 0xc, 0x22, 0xc, 0xac, 0x8, 0x5, 0xbc, 0x69, 0x79, 0x93, 0x5e, 0xf4, 0xfd, 0x6f, 0x7, 0xc2, 0xae, 0xff, 0xfd, 0xff, 0xa7, 0xfd, 0xe9, 0x7f, 0x50, 0x31, 0x87, 0x47, 0x87, 0x81, 0x1b, 0x66, 0x8e, 0x4c, 0xe7, 0xc1, 0xbe, 0xff, 0x2d, 0xff, 0x9b, 0xa1, 0xb0, 0xf5, 0x7f, 0xe7, 0xff, 0xc9, 0xff, 0x27, 0xdc, 0xb6, 0xf0, 0x7, 0x9a, 0xc3, 0x2, 0x52, 0xa0, 0x3e, 0xe3, 0x7d, 0x3b, 0x54, 0x12, 0xa1, 0xa8, 0xe7, 0xff, 0x9c, 0x2f, 0x45, 0xc5, 0xc, 0xdc, 0xf8, 0x14, 0x7c, 0xaf, 0xaa, 0x65, 0xe0, 0xc1, 0x69, 0xc5, 0xc4, 0x3b, 0xe, 0xa1, 0xc, 0x62, 0xc, 0x2c, 0x68, 0x8e, 0xec, 0xf8, 0xdf, 0xfb, 0x7f, 0xda, 0xbf, 0x89, 0xf, 0x4a, 0xa6, 0xf2, 0xe8, 0x0, 0x75, 0x33, 0xa2, 0x79, 0x73, 0xea, 0x87, 0xbe, 0x7b, 0xbd, 0x47, 0x7d, 0x53, 0x58, 0x34, 0x18, 0x84, 0x19, 0x58, 0xb1, 0x7, 0x94, 0xa, 0x83, 0x14, 0x50, 0x27, 0x13, 0x3, 0x3d, 0x1, 0x0, 0x79, 0xc3, 0x79, 0x54, 0x19, 0x56, 0x3b, 0x28, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vslider_grabber_disabled_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xb4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x17, 0x60, 0x66, 0xe0, 0x65, 0x90, 0x61, 0x50, 0x67, 0xd0, 0x60, 0x50, 0x62, 0x10, 0x67, 0xe0, 0x2, 0xf2, 0x19, 0x51, 0xa5, 0xc5, 0x8c, 0xf3, 0x5c, 0xf, 0xfa, 0xbc, 0xf7, 0xfe, 0xe0, 0x71, 0xdb, 0x71, 0xbd, 0x66, 0xaa, 0xa0, 0x21, 0x83, 0x8, 0x3, 0x2b, 0x42, 0x1, 0xaf, 0x71, 0x9e, 0xe7, 0xb, 0xf7, 0xff, 0x2e, 0x40, 0xe8, 0xfa, 0xdf, 0xe3, 0xbf, 0xf7, 0x1f, 0x8f, 0x7, 0x36, 0x73, 0xd8, 0x74, 0x18, 0xb8, 0x61, 0xe6, 0xc8, 0xb8, 0x1e, 0x74, 0xff, 0xef, 0x4, 0x87, 0xce, 0x40, 0x65, 0x5e, 0xff, 0x3d, 0x6e, 0xcb, 0xf8, 0x3, 0xcd, 0x61, 0x1, 0x29, 0x50, 0xf7, 0x79, 0xef, 0x2, 0x95, 0x44, 0x28, 0x72, 0xfb, 0xef, 0xf7, 0xc5, 0xb2, 0x98, 0x81, 0x1b, 0x9f, 0x82, 0xef, 0xb6, 0xb5, 0xc, 0x3c, 0x38, 0xad, 0xf0, 0xbc, 0xa3, 0x10, 0xca, 0x20, 0xc6, 0xc0, 0x82, 0xec, 0x48, 0x30, 0x74, 0xff, 0xef, 0xfd, 0xcf, 0xf3, 0x81, 0xd5, 0x54, 0x36, 0x1d, 0xa0, 0x6e, 0x46, 0xc, 0x6f, 0xba, 0xdf, 0x73, 0x3f, 0xaa, 0x9e, 0xc2, 0xa4, 0xc1, 0x20, 0xcc, 0xc0, 0x8a, 0x3d, 0xa0, 0x54, 0x18, 0xa4, 0x80, 0x3a, 0x99, 0x18, 0xe8, 0x9, 0x0, 0xf1, 0x9, 0x63, 0x9b, 0x53, 0x7f, 0x6d, 0x9b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vslider_grabber_hl_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x6c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x5b, 0xa6, 0xa5, 0x61, 0xb3, 0xbc, 0x63, 0xb7, 0xc8, 0x65, 0xbb, 0xca, 0x60, 0xaf, 0xb1, 0x55, 0x9b, 0x9a, 0x60, 0xb2, 0xbd, 0x5e, 0xb1, 0xcd, 0x61, 0xb3, 0xc2, 0x62, 0xb4, 0xbd, 0x68, 0xc0, 0xcf, 0x68, 0xc1, 0xcf, 0x63, 0xb7, 0xbf, 0x52, 0x96, 0x95, 0x62, 0xb3, 0xbf, 0x5e, 0xb0, 0xcd, 0x63, 0xb4, 0xb6, 0x60, 0xb1, 0xbc, 0x63, 0xb7, 0xc7, 0x55, 0xa3, 0xc8, 0x4f, 0x98, 0xc4, 0x4b, 0x93, 0xc2, 0x4c, 0x94, 0xc2, 0x54, 0xa2, 0xc8, 0x5a, 0xab, 0xcb, 0x4e, 0x97, 0xc4, 0x49, 0x8f, 0xc0, 0x47, 0x8c, 0xbf, 0x48, 0x8e, 0xc0, 0x52, 0x9e, 0xc6, 0x51, 0x9d, 0xc6, 0x5a, 0xac, 0xcc, 0x53, 0x9f, 0xc7, 0x4d, 0x96, 0xc3, 0x4b, 0x92, 0xc2, 0x7f, 0xcb, 0x5d, 0x16, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, 0x47, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0x60, 0x65, 0x61, 0x66, 0x62, 0x44, 0xe2, 0x73, 0x4a, 0x88, 0x8b, 0x89, 0x8a, 0x70, 0xb0, 0xb3, 0xc1, 0xe4, 0x25, 0x64, 0x65, 0x80, 0x40, 0x5a, 0x4a, 0x92, 0xb, 0x22, 0xc0, 0x22, 0x2e, 0x3, 0x1, 0xd2, 0x72, 0xdc, 0x68, 0x2, 0xf2, 0x3c, 0xa8, 0x5a, 0x14, 0x78, 0x11, 0x86, 0x2a, 0x2b, 0x29, 0xa, 0xf0, 0xf3, 0x21, 0x59, 0x2b, 0x2c, 0x24, 0xc8, 0x40, 0x3d, 0x0, 0x0, 0x19, 0x8b, 0x5, 0xfc, 0x96, 0x5c, 0x15, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vslider_tick_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x4, 0x4, 0x3, 0x0, 0x0, 0x0, 0x75, 0x9a, 0xa2, 0xdf, 0x0, 0x0, 0x0, 0x1b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x81, 0xa2, 0xad, 0x8c, 0xac, 0xb8, 0x38, 0x55, 0x5f, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x98, 0x98, 0x98, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x7c, 0xda, 0x48, 0x6d, 0x0, 0x0, 0x0, 0x9, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x79, 0x79, 0x79, 0x31, 0x1c, 0x18, 0xed, 0xfe, 0x2b, 0x0, 0x0, 0x0, 0x1c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x80, 0x3, 0xd7, 0x44, 0x5, 0x6, 0x6, 0xe6, 0x92, 0x30, 0x86, 0xf2, 0x62, 0x3, 0x20, 0xa3, 0xbd, 0x1c, 0x2e, 0x3, 0x0, 0x3f, 0xce, 0x3, 0xaf, 0xed, 0xed, 0x7c, 0x2f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vsplit_bg_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x27, 0x27, 0x29, 0xff, 0xff, 0xff, 0x11, 0xab, 0xb9, 0xf3, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char vsplitter_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0x6c, 0x9, 0xa6, 0x3, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x18, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x1c, 0x2c, 0x48, 0xa5, 0xc, 0x33, 0xa4, 0x5a, 0x53, 0x86, 0x29, 0x7, 0xa3, 0x61, 0x0, 0x0, 0x18, 0x61, 0x34, 0xa1, 0xba, 0xa4, 0x4d, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char window_resizer_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0xbc, 0xe0, 0x45, 0x3f, 0x1, 0xe9, 0xec, 0xfe, 0x81, 0x94, 0x86, 0xb1, 0x70, 0x48, 0x23, 0x58, 0x84, 0xa4, 0x7, 0x15, 0x0, 0x0, 0xed, 0x9f, 0x18, 0xe8, 0xcd, 0x91, 0xd8, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-static const unsigned char window_resizer_mirrored_png[] = {
- 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1, 0x6f, 0x72, 0x4e, 0x54, 0x1, 0xcf, 0xa2, 0x77, 0x9a, 0x0, 0x0, 0x0, 0x27, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x44, 0xe0, 0x45, 0x3f, 0x76, 0x71, 0x26, 0x18, 0xa3, 0x19, 0xa7, 0x12, 0x38, 0xc8, 0xee, 0xa7, 0xb1, 0x12, 0x98, 0x4, 0x4e, 0x25, 0x8, 0x9, 0x4a, 0x94, 0xc, 0x10, 0x0, 0x0, 0x9d, 0x84, 0x18, 0x73, 0x33, 0x1c, 0x96, 0xd6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
-};
-
-// shaders block
diff --git a/scene/resources/default_theme/toggle_off.png b/scene/resources/default_theme/toggle_off.png
deleted file mode 100644
index 64b51c8c9d..0000000000
--- a/scene/resources/default_theme/toggle_off.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off.svg b/scene/resources/default_theme/toggle_off.svg
new file mode 100644
index 0000000000..95ec679cbe
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".5" stroke-width=".999997"><rect fill="#1a1a1a" height="14" rx="7.000001" width="30" x="1" y="1"/><circle cx="8" cy="8" fill="#fff" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_off_disabled.png b/scene/resources/default_theme/toggle_off_disabled.png
deleted file mode 100644
index 250cd29b66..0000000000
--- a/scene/resources/default_theme/toggle_off_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off_disabled.svg b/scene/resources/default_theme/toggle_off_disabled.svg
new file mode 100644
index 0000000000..c86a678768
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".25" stroke-width=".999997"><rect fill="#1a1a1a" height="14" rx="7.000001" width="30" x="1" y="1"/><circle cx="8" cy="8" fill="#fff" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_off_disabled_mirrored.png b/scene/resources/default_theme/toggle_off_disabled_mirrored.png
deleted file mode 100644
index 799b63c098..0000000000
--- a/scene/resources/default_theme/toggle_off_disabled_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off_disabled_mirrored.svg b/scene/resources/default_theme/toggle_off_disabled_mirrored.svg
new file mode 100644
index 0000000000..0182dd738e
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_disabled_mirrored.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".25" stroke-width=".999997" transform="scale(-1 1)"><rect fill="#1a1a1a" height="14" rx="7.000001" width="30" x="-31" y="1"/><circle cx="-24" cy="8" fill="#fff" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_off_mirrored.png b/scene/resources/default_theme/toggle_off_mirrored.png
deleted file mode 100644
index 3487605d58..0000000000
--- a/scene/resources/default_theme/toggle_off_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_off_mirrored.svg b/scene/resources/default_theme/toggle_off_mirrored.svg
new file mode 100644
index 0000000000..f61517a4ae
--- /dev/null
+++ b/scene/resources/default_theme/toggle_off_mirrored.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".5" stroke-width=".999997" transform="scale(-1 1)"><rect fill="#1a1a1a" height="14" rx="7.000001" width="30" x="-31" y="1"/><circle cx="-24" cy="8" fill="#fff" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_on.png b/scene/resources/default_theme/toggle_on.png
deleted file mode 100644
index f0c699c181..0000000000
--- a/scene/resources/default_theme/toggle_on.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on.svg b/scene/resources/default_theme/toggle_on.svg
new file mode 100644
index 0000000000..7e02602792
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g stroke-width=".999997" transform="scale(-1 1)"><rect fill="#fff" fill-opacity=".75" height="14" rx="7.000001" width="30" x="-31" y="1"/><circle cx="-24" cy="8" fill="#1a1a1a" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_on_disabled.png b/scene/resources/default_theme/toggle_on_disabled.png
deleted file mode 100644
index b1dacbaf32..0000000000
--- a/scene/resources/default_theme/toggle_on_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_disabled.svg b/scene/resources/default_theme/toggle_on_disabled.svg
new file mode 100644
index 0000000000..72ce5bb9b2
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g stroke-width=".999997" transform="scale(-1 1)"><rect fill="#fff" fill-opacity=".37" height="14" rx="7.000001" width="30" x="-31" y="1"/><circle cx="-24" cy="8" fill="#1a1a1a" fill-opacity=".5" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_on_disabled_mirrored.png b/scene/resources/default_theme/toggle_on_disabled_mirrored.png
deleted file mode 100644
index 0758babd4f..0000000000
--- a/scene/resources/default_theme/toggle_on_disabled_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_disabled_mirrored.svg b/scene/resources/default_theme/toggle_on_disabled_mirrored.svg
new file mode 100644
index 0000000000..340a386def
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_disabled_mirrored.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g stroke-width=".999997"><rect fill="#fff" fill-opacity=".37" height="14" rx="7.000001" width="30" x="1" y="1"/><circle cx="8" cy="8" fill="#1a1a1a" fill-opacity=".5" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/toggle_on_mirrored.png b/scene/resources/default_theme/toggle_on_mirrored.png
deleted file mode 100644
index 3fd953c8e2..0000000000
--- a/scene/resources/default_theme/toggle_on_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/toggle_on_mirrored.svg b/scene/resources/default_theme/toggle_on_mirrored.svg
new file mode 100644
index 0000000000..fbc6be2af9
--- /dev/null
+++ b/scene/resources/default_theme/toggle_on_mirrored.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 32 15.999999" width="32" xmlns="http://www.w3.org/2000/svg"><g stroke-width=".999997"><rect fill="#fff" fill-opacity=".75" height="14" rx="7.000001" width="30" x="1" y="1"/><circle cx="8" cy="8" fill="#1a1a1a" r="5"/></g></svg>
diff --git a/scene/resources/default_theme/tooltip_bg.png b/scene/resources/default_theme/tooltip_bg.png
deleted file mode 100644
index 07b7d942ca..0000000000
--- a/scene/resources/default_theme/tooltip_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tree_bg.png b/scene/resources/default_theme/tree_bg.png
deleted file mode 100644
index 2b0c506f34..0000000000
--- a/scene/resources/default_theme/tree_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tree_bg_disabled.png b/scene/resources/default_theme/tree_bg_disabled.png
deleted file mode 100644
index 69d78febd9..0000000000
--- a/scene/resources/default_theme/tree_bg_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tree_title.png b/scene/resources/default_theme/tree_title.png
deleted file mode 100644
index e5f3f49695..0000000000
--- a/scene/resources/default_theme/tree_title.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/tree_title_pressed.png b/scene/resources/default_theme/tree_title_pressed.png
deleted file mode 100644
index 35e2bb3008..0000000000
--- a/scene/resources/default_theme/tree_title_pressed.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/unchecked.png b/scene/resources/default_theme/unchecked.png
deleted file mode 100644
index 8c818af755..0000000000
--- a/scene/resources/default_theme/unchecked.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/unchecked.svg b/scene/resources/default_theme/unchecked.svg
new file mode 100644
index 0000000000..f475108f44
--- /dev/null
+++ b/scene/resources/default_theme/unchecked.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#1a1a1a" fill-opacity=".5" stroke-width="1.16667"/></svg>
diff --git a/scene/resources/default_theme/unchecked_disabled.png b/scene/resources/default_theme/unchecked_disabled.png
deleted file mode 100644
index bef9316f58..0000000000
--- a/scene/resources/default_theme/unchecked_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/unchecked_disabled.svg b/scene/resources/default_theme/unchecked_disabled.svg
new file mode 100644
index 0000000000..195b85e37f
--- /dev/null
+++ b/scene/resources/default_theme/unchecked_disabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#1a1a1a" fill-opacity=".25" stroke-width="1.16667"/></svg>
diff --git a/scene/resources/default_theme/updown.png b/scene/resources/default_theme/updown.png
deleted file mode 100644
index 56f81921e8..0000000000
--- a/scene/resources/default_theme/updown.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/updown.svg b/scene/resources/default_theme/updown.svg
new file mode 100644
index 0000000000..d6a630740f
--- /dev/null
+++ b/scene/resources/default_theme/updown.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff" stroke-opacity=".75" stroke-width="2"><path d="m4 6 4-3.5 4 3.5"/><path d="m12 10-4 3.5-4-3.5"/></g></svg>
diff --git a/scene/resources/default_theme/visibility_visible.svg b/scene/resources/default_theme/visibility_visible.svg
new file mode 100644
index 0000000000..ac1af8db0a
--- /dev/null
+++ b/scene/resources/default_theme/visibility_visible.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2c-2.557 0-5.791 1.948-6.955 5.705-.057.186-.059.384-.006.57 1.124 3.936 4.461 5.725 6.961 5.725s5.836-1.789 6.961-5.725c.052-.18.052-.372 0-.552-1.1-3.788-4.407-5.723-6.961-5.723zm0 2c2.194 0 4 1.806 4 4s-1.806 4-4 4-4-1.806-4-4 1.806-4 4-4zm0 2c-1.097 0-2 .903-2 2s.903 2 2 2 2-.903 2-2-.903-2-2-2z" fill="#b2b2b2"/></svg>
diff --git a/scene/resources/default_theme/vseparator.png b/scene/resources/default_theme/vseparator.png
deleted file mode 100644
index 51e79f3ac5..0000000000
--- a/scene/resources/default_theme/vseparator.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_bg.png b/scene/resources/default_theme/vslider_bg.png
deleted file mode 100644
index ba3244e3e5..0000000000
--- a/scene/resources/default_theme/vslider_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber.png b/scene/resources/default_theme/vslider_grabber.png
deleted file mode 100644
index 6c6bf93e68..0000000000
--- a/scene/resources/default_theme/vslider_grabber.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber_disabled.png b/scene/resources/default_theme/vslider_grabber_disabled.png
deleted file mode 100644
index 49cced5055..0000000000
--- a/scene/resources/default_theme/vslider_grabber_disabled.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_grabber_hl.png b/scene/resources/default_theme/vslider_grabber_hl.png
deleted file mode 100644
index 28774fdbf8..0000000000
--- a/scene/resources/default_theme/vslider_grabber_hl.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_tick.png b/scene/resources/default_theme/vslider_tick.png
deleted file mode 100644
index bde788b563..0000000000
--- a/scene/resources/default_theme/vslider_tick.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vslider_tick.svg b/scene/resources/default_theme/vslider_tick.svg
new file mode 100644
index 0000000000..1365d36aca
--- /dev/null
+++ b/scene/resources/default_theme/vslider_tick.svg
@@ -0,0 +1 @@
+<svg height="4" viewBox="0 0 16 3.9999998" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m-3 0h2v16h-2z" fill="#fff" fill-opacity=".25" stroke-width=".285079" transform="rotate(-90)"/></svg>
diff --git a/scene/resources/default_theme/vsplit_bg.png b/scene/resources/default_theme/vsplit_bg.png
deleted file mode 100644
index a5749f6d5c..0000000000
--- a/scene/resources/default_theme/vsplit_bg.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vsplitter.png b/scene/resources/default_theme/vsplitter.png
deleted file mode 100644
index dde1f390df..0000000000
--- a/scene/resources/default_theme/vsplitter.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/vsplitter.svg b/scene/resources/default_theme/vsplitter.svg
new file mode 100644
index 0000000000..c46d785925
--- /dev/null
+++ b/scene/resources/default_theme/vsplitter.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" viewBox="0 0 48 8" xmlns="http://www.w3.org/2000/svg"><path d="m4 4h40" fill="none" stroke="#808080" stroke-opacity=".65" stroke-width="1.7"/></svg>
diff --git a/scene/resources/default_theme/window_resizer.png b/scene/resources/default_theme/window_resizer.png
deleted file mode 100644
index b06e6f5366..0000000000
--- a/scene/resources/default_theme/window_resizer.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/window_resizer_mirrored.png b/scene/resources/default_theme/window_resizer_mirrored.png
deleted file mode 100644
index bbce5f1406..0000000000
--- a/scene/resources/default_theme/window_resizer_mirrored.png
+++ /dev/null
Binary files differ
diff --git a/scene/resources/default_theme/zoom_less.svg b/scene/resources/default_theme/zoom_less.svg
new file mode 100644
index 0000000000..a438f7f683
--- /dev/null
+++ b/scene/resources/default_theme/zoom_less.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#b2b2b2" fill-opacity=".65" r="8"/><circle cx="8" cy="8" fill="#fefffe" r="7"/><path d="m4 7h8v2h-8z" fill="#010001"/></svg>
diff --git a/scene/resources/default_theme/zoom_more.svg b/scene/resources/default_theme/zoom_more.svg
new file mode 100644
index 0000000000..192e0ed907
--- /dev/null
+++ b/scene/resources/default_theme/zoom_more.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#b2b2b2" fill-opacity=".65" r="8"/><circle cx="8" cy="8" fill="#fefffe" r="7"/><path d="m7 4h2v3h3v2h-3v3h-2v-3h-3v-2h3z" fill="#010001"/></svg>
diff --git a/scene/resources/default_theme/zoom_reset.svg b/scene/resources/default_theme/zoom_reset.svg
new file mode 100644
index 0000000000..427214cda2
--- /dev/null
+++ b/scene/resources/default_theme/zoom_reset.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" fill="#b2b2b2" fill-opacity=".65" r="8"/><circle cx="8" cy="8" fill="#fefffe" r="7"/><path d="m8.016 4.002h.029c.548 0 .999.45 1 .998v7h-2v-5.131l-1.445.963-1.11-1.664 3-2c.156-.103.338-.161.526-.166z" fill="#010001"/></svg>
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 7e71ad8986..b13ae9d016 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -496,13 +496,13 @@ bool Environment::is_sdfgi_enabled() const {
return sdfgi_enabled;
}
-void Environment::set_sdfgi_cascades(SDFGICascades p_cascades) {
- ERR_FAIL_INDEX(p_cascades, SDFGI_CASCADES_8 + 1);
+void Environment::set_sdfgi_cascades(int p_cascades) {
+ ERR_FAIL_COND_MSG(p_cascades < 1 || p_cascades > 8, "Invalid number of SDFGI cascades (must be between 1 and 8).");
sdfgi_cascades = p_cascades;
_update_sdfgi();
}
-Environment::SDFGICascades Environment::get_sdfgi_cascades() const {
+int Environment::get_sdfgi_cascades() const {
return sdfgi_cascades;
}
@@ -517,9 +517,7 @@ float Environment::get_sdfgi_min_cell_size() const {
void Environment::set_sdfgi_max_distance(float p_distance) {
p_distance /= 64.0;
- int cc[3] = { 4, 6, 8 };
- int cascades = cc[sdfgi_cascades];
- for (int i = 0; i < cascades; i++) {
+ for (int i = 0; i < sdfgi_cascades; i++) {
p_distance *= 0.5; //halve for each cascade
}
sdfgi_min_cell_size = p_distance;
@@ -529,9 +527,7 @@ void Environment::set_sdfgi_max_distance(float p_distance) {
float Environment::get_sdfgi_max_distance() const {
float md = sdfgi_min_cell_size;
md *= 64.0;
- int cc[3] = { 4, 6, 8 };
- int cascades = cc[sdfgi_cascades];
- for (int i = 0; i < cascades; i++) {
+ for (int i = 0; i < sdfgi_cascades; i++) {
md *= 2.0;
}
return md;
@@ -612,7 +608,7 @@ void Environment::_update_sdfgi() {
RS::get_singleton()->environment_set_sdfgi(
environment,
sdfgi_enabled,
- RS::EnvironmentSDFGICascades(sdfgi_cascades),
+ sdfgi_cascades,
sdfgi_min_cell_size,
RS::EnvironmentSDFGIYScale(sdfgi_y_scale),
sdfgi_use_occlusion,
@@ -732,6 +728,24 @@ float Environment::get_glow_hdr_luminance_cap() const {
return glow_hdr_luminance_cap;
}
+void Environment::set_glow_map_strength(float p_strength) {
+ glow_map_strength = p_strength;
+ _update_glow();
+}
+
+float Environment::get_glow_map_strength() const {
+ return glow_map_strength;
+}
+
+void Environment::set_glow_map(Ref<Texture> p_glow_map) {
+ glow_map = p_glow_map;
+ _update_glow();
+}
+
+Ref<Texture> Environment::get_glow_map() const {
+ return glow_map;
+}
+
void Environment::_update_glow() {
Vector<float> normalized_levels;
if (glow_normalize_levels) {
@@ -747,6 +761,15 @@ void Environment::_update_glow() {
normalized_levels = glow_levels;
}
+ float _glow_map_strength = 0.0f;
+ RID glow_map_rid;
+ if (glow_map.is_valid()) {
+ glow_map_rid = glow_map->get_rid();
+ _glow_map_strength = glow_map_strength;
+ } else {
+ glow_map_rid = RID();
+ }
+
RS::get_singleton()->environment_set_glow(
environment,
glow_enabled,
@@ -758,7 +781,9 @@ void Environment::_update_glow() {
RS::EnvironmentGlowBlendMode(glow_blend_mode),
glow_hdr_bleed_threshold,
glow_hdr_bleed_scale,
- glow_hdr_luminance_cap);
+ glow_hdr_luminance_cap,
+ _glow_map_strength,
+ glow_map_rid);
}
// Fog
@@ -1303,7 +1328,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_use_occlusion"), "set_sdfgi_use_occlusion", "is_sdfgi_using_occlusion");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_read_sky_light"), "set_sdfgi_read_sky_light", "is_sdfgi_reading_sky_light");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_bounce_feedback", PROPERTY_HINT_RANGE, "0,1.99,0.01"), "set_sdfgi_bounce_feedback", "get_sdfgi_bounce_feedback");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_cascades", PROPERTY_HINT_ENUM, "4 Cascades,6 Cascades,8 Cascades"), "set_sdfgi_cascades", "get_sdfgi_cascades");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_cascades", PROPERTY_HINT_RANGE, "1,8,1"), "set_sdfgi_cascades", "get_sdfgi_cascades");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_min_cell_size", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_sdfgi_min_cell_size", "get_sdfgi_min_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_cascade0_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_cascade0_distance", "get_sdfgi_cascade0_distance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_max_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_max_distance", "get_sdfgi_max_distance");
@@ -1336,6 +1361,10 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_glow_hdr_bleed_scale"), &Environment::get_glow_hdr_bleed_scale);
ClassDB::bind_method(D_METHOD("set_glow_hdr_luminance_cap", "amount"), &Environment::set_glow_hdr_luminance_cap);
ClassDB::bind_method(D_METHOD("get_glow_hdr_luminance_cap"), &Environment::get_glow_hdr_luminance_cap);
+ ClassDB::bind_method(D_METHOD("set_glow_map_strength", "strength"), &Environment::set_glow_map_strength);
+ ClassDB::bind_method(D_METHOD("get_glow_map_strength"), &Environment::get_glow_map_strength);
+ ClassDB::bind_method(D_METHOD("set_glow_map", "mode"), &Environment::set_glow_map);
+ ClassDB::bind_method(D_METHOD("get_glow_map"), &Environment::get_glow_map);
ADD_GROUP("Glow", "glow_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "glow_enabled"), "set_glow_enabled", "is_glow_enabled");
@@ -1355,6 +1384,8 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_hdr_threshold", PROPERTY_HINT_RANGE, "0.0,4.0,0.01"), "set_glow_hdr_bleed_threshold", "get_glow_hdr_bleed_threshold");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_hdr_scale", PROPERTY_HINT_RANGE, "0.0,4.0,0.01"), "set_glow_hdr_bleed_scale", "get_glow_hdr_bleed_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_hdr_luminance_cap", PROPERTY_HINT_RANGE, "0.0,256.0,0.01"), "set_glow_hdr_luminance_cap", "get_glow_hdr_luminance_cap");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "glow_map_strength", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_glow_map_strength", "get_glow_map_strength");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "glow_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_glow_map", "get_glow_map");
// Fog
@@ -1480,10 +1511,6 @@ void Environment::_bind_methods() {
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_REPLACE);
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_MIX);
- BIND_ENUM_CONSTANT(SDFGI_CASCADES_4);
- BIND_ENUM_CONSTANT(SDFGI_CASCADES_6);
- BIND_ENUM_CONSTANT(SDFGI_CASCADES_8);
-
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_DISABLED);
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT);
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_50_PERCENT);
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index 98e755c336..b04723a221 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -70,12 +70,6 @@ public:
TONE_MAPPER_ACES,
};
- enum SDFGICascades {
- SDFGI_CASCADES_4,
- SDFGI_CASCADES_6,
- SDFGI_CASCADES_8,
- };
-
enum SDFGIYScale {
SDFGI_Y_SCALE_DISABLED,
SDFGI_Y_SCALE_75_PERCENT,
@@ -153,7 +147,7 @@ private:
// SDFGI
bool sdfgi_enabled = false;
- SDFGICascades sdfgi_cascades = SDFGI_CASCADES_6;
+ int sdfgi_cascades = 6;
float sdfgi_min_cell_size = 0.2;
SDFGIYScale sdfgi_y_scale = SDFGI_Y_SCALE_DISABLED;
bool sdfgi_use_occlusion = false;
@@ -176,6 +170,8 @@ private:
float glow_hdr_bleed_threshold = 1.0;
float glow_hdr_bleed_scale = 2.0;
float glow_hdr_luminance_cap = 12.0;
+ float glow_map_strength = 0.8f;
+ Ref<Texture> glow_map;
void _update_glow();
// Fog
@@ -320,8 +316,8 @@ public:
// SDFGI
void set_sdfgi_enabled(bool p_enabled);
bool is_sdfgi_enabled() const;
- void set_sdfgi_cascades(SDFGICascades p_cascades);
- SDFGICascades get_sdfgi_cascades() const;
+ void set_sdfgi_cascades(int p_cascades);
+ int get_sdfgi_cascades() const;
void set_sdfgi_min_cell_size(float p_size);
float get_sdfgi_min_cell_size() const;
void set_sdfgi_max_distance(float p_distance);
@@ -366,6 +362,10 @@ public:
float get_glow_hdr_bleed_scale() const;
void set_glow_hdr_luminance_cap(float p_amount);
float get_glow_hdr_luminance_cap() const;
+ void set_glow_map_strength(float p_strength);
+ float get_glow_map_strength() const;
+ void set_glow_map(Ref<Texture> p_glow_map);
+ Ref<Texture> get_glow_map() const;
// Fog
@@ -433,7 +433,6 @@ VARIANT_ENUM_CAST(Environment::BGMode)
VARIANT_ENUM_CAST(Environment::AmbientSource)
VARIANT_ENUM_CAST(Environment::ReflectionSource)
VARIANT_ENUM_CAST(Environment::ToneMapper)
-VARIANT_ENUM_CAST(Environment::SDFGICascades)
VARIANT_ENUM_CAST(Environment::SDFGIYScale)
VARIANT_ENUM_CAST(Environment::GlowBlendMode)
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index d9e0c301de..b512acdd8a 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -30,6 +30,7 @@
#include "font.h"
+#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
#include "core/string/translation.h"
#include "core/templates/hashfuncs.h"
@@ -64,6 +65,9 @@ _FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const {
}
void FontData::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load_bitmap_font", "path"), &FontData::load_bitmap_font);
+ ClassDB::bind_method(D_METHOD("load_dynamic_font", "path"), &FontData::load_dynamic_font);
+
ClassDB::bind_method(D_METHOD("set_data", "data"), &FontData::set_data);
ClassDB::bind_method(D_METHOD("get_data"), &FontData::get_data);
@@ -428,11 +432,766 @@ void FontData::reset_state() {
hinting = TextServer::HINTING_LIGHT;
msdf_pixel_range = 14;
msdf_size = 128;
+ fixed_size = 0;
oversampling = 0.f;
}
+void FontData::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_r;
+ imgdata_r.resize(w * h * 2);
+ uint8_t *wr = imgdata_r.ptrw();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_b;
+ imgdata_b.resize(w * h * 2);
+ uint8_t *wb = imgdata_b.ptrw();
+
+ PackedByteArray imgdata_a;
+ imgdata_a.resize(w * h * 2);
+ uint8_t *wa = imgdata_a.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * 4;
+ int ofs_dst = (i * w + j) * 2;
+ wr[ofs_dst + 0] = 255;
+ wr[ofs_dst + 1] = r[ofs_src + 0];
+ wg[ofs_dst + 0] = 255;
+ wg[ofs_dst + 1] = r[ofs_src + 1];
+ wb[ofs_dst + 0] = 255;
+ wb[ofs_dst + 1] = r[ofs_src + 2];
+ wa[ofs_dst + 0] = 255;
+ wa[ofs_dst + 1] = r[ofs_src + 3];
+ }
+ }
+ Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
+ Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
+ Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
+}
+
+void FontData::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_r;
+ imgdata_r.resize(w * h * 2);
+ uint8_t *wr = imgdata_r.ptrw();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_b;
+ imgdata_b.resize(w * h * 2);
+ uint8_t *wb = imgdata_b.ptrw();
+
+ PackedByteArray imgdata_a;
+ imgdata_a.resize(w * h * 2);
+ uint8_t *wa = imgdata_a.ptrw();
+
+ PackedByteArray imgdata_ro;
+ imgdata_ro.resize(w * h * 2);
+ uint8_t *wro = imgdata_ro.ptrw();
+
+ PackedByteArray imgdata_go;
+ imgdata_go.resize(w * h * 2);
+ uint8_t *wgo = imgdata_go.ptrw();
+
+ PackedByteArray imgdata_bo;
+ imgdata_bo.resize(w * h * 2);
+ uint8_t *wbo = imgdata_bo.ptrw();
+
+ PackedByteArray imgdata_ao;
+ imgdata_ao.resize(w * h * 2);
+ uint8_t *wao = imgdata_ao.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * 4;
+ int ofs_dst = (i * w + j) * 2;
+ wr[ofs_dst + 0] = 255;
+ wro[ofs_dst + 0] = 255;
+ if (r[ofs_src + 0] > 0x0F) {
+ wr[ofs_dst + 1] = (r[ofs_src + 0] - 0x0F) * 2;
+ wro[ofs_dst + 1] = 0;
+ } else {
+ wr[ofs_dst + 1] = 0;
+ wro[ofs_dst + 1] = r[ofs_src + 0] * 2;
+ }
+ wg[ofs_dst + 0] = 255;
+ wgo[ofs_dst + 0] = 255;
+ if (r[ofs_src + 1] > 0x0F) {
+ wg[ofs_dst + 1] = (r[ofs_src + 1] - 0x0F) * 2;
+ wgo[ofs_dst + 1] = 0;
+ } else {
+ wg[ofs_dst + 1] = 0;
+ wgo[ofs_dst + 1] = r[ofs_src + 1] * 2;
+ }
+ wb[ofs_dst + 0] = 255;
+ wbo[ofs_dst + 0] = 255;
+ if (r[ofs_src + 2] > 0x0F) {
+ wb[ofs_dst + 1] = (r[ofs_src + 2] - 0x0F) * 2;
+ wbo[ofs_dst + 1] = 0;
+ } else {
+ wb[ofs_dst + 1] = 0;
+ wbo[ofs_dst + 1] = r[ofs_src + 2] * 2;
+ }
+ wa[ofs_dst + 0] = 255;
+ wao[ofs_dst + 0] = 255;
+ if (r[ofs_src + 3] > 0x0F) {
+ wa[ofs_dst + 1] = (r[ofs_src + 3] - 0x0F) * 2;
+ wao[ofs_dst + 1] = 0;
+ } else {
+ wa[ofs_dst + 1] = 0;
+ wao[ofs_dst + 1] = r[ofs_src + 3] * 2;
+ }
+ }
+ }
+ Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
+ Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
+ Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
+
+ Ref<Image> img_ro = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ro));
+ set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 0, img_ro);
+ Ref<Image> img_go = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_go));
+ set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 1, img_go);
+ Ref<Image> img_bo = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_bo));
+ set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 2, img_bo);
+ Ref<Image> img_ao = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ao));
+ set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao);
+}
+
+void FontData::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 4);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_o;
+ imgdata_o.resize(w * h * 4);
+ uint8_t *wo = imgdata_o.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = (i * w + j) * 4;
+
+ if (r[ofs + 0] > 0x7F) {
+ wg[ofs + 0] = r[ofs + 0];
+ wo[ofs + 0] = 0;
+ } else {
+ wg[ofs + 0] = 0;
+ wo[ofs + 0] = r[ofs + 0] * 2;
+ }
+ if (r[ofs + 1] > 0x7F) {
+ wg[ofs + 1] = r[ofs + 1];
+ wo[ofs + 1] = 0;
+ } else {
+ wg[ofs + 1] = 0;
+ wo[ofs + 1] = r[ofs + 1] * 2;
+ }
+ if (r[ofs + 2] > 0x7F) {
+ wg[ofs + 2] = r[ofs + 2];
+ wo[ofs + 2] = 0;
+ } else {
+ wg[ofs + 2] = 0;
+ wo[ofs + 2] = r[ofs + 2] * 2;
+ }
+ if (r[ofs + 3] > 0x7F) {
+ wg[ofs + 3] = r[ofs + 3];
+ wo[ofs + 3] = 0;
+ } else {
+ wg[ofs + 3] = 0;
+ wo[ofs + 3] = r[ofs + 3] * 2;
+ }
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_g));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
+
+ Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_o));
+ set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o);
+}
+
+void FontData::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ int size = 4;
+ if (p_source->get_format() == Image::FORMAT_L8) {
+ size = 1;
+ p_ch = 0;
+ }
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * size;
+ int ofs_dst = (i * w + j) * 2;
+ wg[ofs_dst + 0] = 255;
+ wg[ofs_dst + 1] = r[ofs_src + p_ch];
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g);
+}
+
+void FontData::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ int size = 4;
+ if (p_source->get_format() == Image::FORMAT_L8) {
+ size = 1;
+ p_ch = 0;
+ }
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_o;
+ imgdata_o.resize(w * h * 2);
+ uint8_t *wo = imgdata_o.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * size;
+ int ofs_dst = (i * w + j) * 2;
+ wg[ofs_dst + 0] = 255;
+ wo[ofs_dst + 0] = 255;
+ if (r[ofs_src + p_ch] > 0x7F) {
+ wg[ofs_dst + 1] = r[ofs_src + p_ch];
+ wo[ofs_dst + 1] = 0;
+ } else {
+ wg[ofs_dst + 1] = 0;
+ wo[ofs_dst + 1] = r[ofs_src + p_ch] * 2;
+ }
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
+
+ Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_o));
+ set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o);
+}
+
/*************************************************************************/
+Error FontData::load_bitmap_font(const String &p_path) {
+ reset_state();
+
+ antialiased = false;
+ msdf = false;
+ force_autohinter = false;
+ hinting = TextServer::HINTING_NONE;
+ oversampling = 1.0f;
+
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::READ);
+ if (f == nullptr) {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Cannot open font from file ") + "\"" + p_path + "\".");
+ }
+
+ int base_size = 16;
+ int height = 0;
+ int ascent = 0;
+ int outline = 0;
+ uint32_t st_flags = 0;
+ String font_name;
+
+ bool packed = false;
+ uint8_t ch[4] = { 0, 0, 0, 0 }; // RGBA
+ int first_gl_ch = -1;
+ int first_ol_ch = -1;
+ int first_cm_ch = -1;
+
+ unsigned char magic[4];
+ f->get_buffer((unsigned char *)&magic, 4);
+ if (magic[0] == 'B' && magic[1] == 'M' && magic[2] == 'F') {
+ // Binary BMFont file.
+ ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(TTR("Version %d of BMFont is not supported."), (int)magic[3]));
+
+ uint8_t block_type = f->get_8();
+ uint32_t block_size = f->get_32();
+ while (!f->eof_reached()) {
+ uint64_t off = f->get_position();
+ switch (block_type) {
+ case 1: /* info */ {
+ ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, TTR("Invalid BMFont info block size."));
+ base_size = f->get_16();
+ uint8_t flags = f->get_8();
+ ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
+ if (flags & (1 << 3)) {
+ st_flags |= TextServer::FONT_BOLD;
+ }
+ if (flags & (1 << 2)) {
+ st_flags |= TextServer::FONT_ITALIC;
+ }
+ f->get_8(); // non-unicode charset, skip
+ f->get_16(); // stretch_h, skip
+ f->get_8(); // aa, skip
+ f->get_32(); // padding, skip
+ f->get_16(); // spacing, skip
+ outline = f->get_8();
+ // font name
+ PackedByteArray name_data;
+ name_data.resize(block_size - 14);
+ f->get_buffer(name_data.ptrw(), block_size - 14);
+ font_name = String::utf8((const char *)name_data.ptr(), block_size - 14);
+ set_fixed_size(base_size);
+ } break;
+ case 2: /* common */ {
+ ERR_FAIL_COND_V_MSG(block_size != 15, ERR_CANT_CREATE, TTR("Invalid BMFont common block size."));
+ height = f->get_16();
+ ascent = f->get_16();
+ f->get_32(); // scale, skip
+ f->get_16(); // pages, skip
+ uint8_t flags = f->get_8();
+ packed = (flags & 0x01);
+ ch[3] = f->get_8();
+ ch[0] = f->get_8();
+ ch[1] = f->get_8();
+ ch[2] = f->get_8();
+ for (int i = 0; i < 4; i++) {
+ if (ch[i] == 0 && first_gl_ch == -1) {
+ first_gl_ch = i;
+ }
+ if (ch[i] == 1 && first_ol_ch == -1) {
+ first_ol_ch = i;
+ }
+ if (ch[i] == 2 && first_cm_ch == -1) {
+ first_cm_ch = i;
+ }
+ }
+ } break;
+ case 3: /* pages */ {
+ int page = 0;
+ CharString cs;
+ char32_t c = f->get_8();
+ while (!f->eof_reached() && f->get_position() <= off + block_size) {
+ if (c == '\0') {
+ String base_dir = p_path.get_base_dir();
+ String file = base_dir.plus_file(String::utf8(cs.ptr(), cs.length()));
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img;
+ img.instantiate();
+ Error err = ImageLoader::load_image(file, img);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\".");
+
+ if (packed) {
+ if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_8bit(img, page, base_size);
+ } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_4bit(img, page, base_size);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ } else {
+ if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ set_texture_image(0, Vector2i(base_size, 0), page, img);
+ } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_rgba_4bit(img, page, base_size);
+ } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
+ _convert_mono_8bit(img, page, first_ol_ch, base_size, 1);
+ } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_4bit(img, page, first_cm_ch, base_size, 1);
+ } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ }
+ }
+ page++;
+ cs = "";
+ } else {
+ cs += c;
+ }
+ c = f->get_8();
+ }
+ } break;
+ case 4: /* chars */ {
+ int char_count = block_size / 20;
+ for (int i = 0; i < char_count; i++) {
+ Vector2 advance;
+ Vector2 size;
+ Vector2 offset;
+ Rect2 uv_rect;
+
+ char32_t idx = f->get_32();
+ uv_rect.position.x = (int16_t)f->get_16();
+ uv_rect.position.y = (int16_t)f->get_16();
+ uv_rect.size.width = (int16_t)f->get_16();
+ size.width = uv_rect.size.width;
+ uv_rect.size.height = (int16_t)f->get_16();
+ size.height = uv_rect.size.height;
+ offset.x = (int16_t)f->get_16();
+ offset.y = (int16_t)f->get_16() - ascent;
+ advance.x = (int16_t)f->get_16();
+ if (advance.x < 0) {
+ advance.x = size.width + 1;
+ }
+
+ int texture_idx = f->get_8();
+ uint8_t channel = f->get_8();
+
+ ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel."));
+ int ch_off = 0;
+ switch (channel) {
+ case 1:
+ ch_off = 2;
+ break; // B
+ case 2:
+ ch_off = 1;
+ break; // G
+ case 4:
+ ch_off = 0;
+ break; // R
+ case 8:
+ ch_off = 3;
+ break; // A
+ default:
+ ch_off = 0;
+ break;
+ }
+ set_glyph_advance(0, base_size, idx, advance);
+ set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
+ set_glyph_size(0, Vector2i(base_size, 0), idx, size);
+ set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, uv_rect);
+ set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ if (outline > 0) {
+ set_glyph_offset(0, Vector2i(base_size, 1), idx, offset);
+ set_glyph_size(0, Vector2i(base_size, 1), idx, size);
+ set_glyph_uv_rect(0, Vector2i(base_size, 1), idx, uv_rect);
+ set_glyph_texture_idx(0, Vector2i(base_size, 1), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ }
+ }
+ } break;
+ case 5: /* kerning */ {
+ int pair_count = block_size / 10;
+ for (int i = 0; i < pair_count; i++) {
+ Vector2i kpk;
+ kpk.x = f->get_32();
+ kpk.y = f->get_32();
+ set_kerning(0, base_size, kpk, Vector2((int16_t)f->get_16(), 0));
+ }
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Invalid BMFont block type."));
+ } break;
+ }
+ f->seek(off + block_size);
+ block_type = f->get_8();
+ block_size = f->get_32();
+ }
+
+ } else {
+ // Text BMFont file.
+ f->seek(0);
+ while (true) {
+ String line = f->get_line();
+
+ int delimiter = line.find(" ");
+ String type = line.substr(0, delimiter);
+ int pos = delimiter + 1;
+ Map<String, String> keys;
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ while (pos < line.size()) {
+ int eq = line.find("=", pos);
+ if (eq == -1) {
+ break;
+ }
+ String key = line.substr(pos, eq - pos);
+ int end = -1;
+ String value;
+ if (line[eq + 1] == '"') {
+ end = line.find("\"", eq + 2);
+ if (end == -1) {
+ break;
+ }
+ value = line.substr(eq + 2, end - 1 - eq - 1);
+ pos = end + 1;
+ } else {
+ end = line.find(" ", eq + 1);
+ if (end == -1) {
+ end = line.size();
+ }
+ value = line.substr(eq + 1, end - eq);
+ pos = end;
+ }
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ keys[key] = value;
+ }
+
+ if (type == "info") {
+ if (keys.has("size")) {
+ base_size = keys["size"].to_int();
+ set_fixed_size(base_size);
+ }
+ if (keys.has("outline")) {
+ outline = keys["outline"].to_int();
+ }
+ if (keys.has("bold")) {
+ if (keys["bold"].to_int()) {
+ st_flags |= TextServer::FONT_BOLD;
+ }
+ }
+ if (keys.has("italic")) {
+ if (keys["italic"].to_int()) {
+ st_flags |= TextServer::FONT_ITALIC;
+ }
+ }
+ if (keys.has("face")) {
+ font_name = keys["face"];
+ }
+ ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
+ } else if (type == "common") {
+ if (keys.has("lineHeight")) {
+ height = keys["lineHeight"].to_int();
+ }
+ if (keys.has("base")) {
+ ascent = keys["base"].to_int();
+ }
+ if (keys.has("packed")) {
+ packed = (keys["packed"].to_int() == 1);
+ }
+ if (keys.has("alphaChnl")) {
+ ch[3] = keys["alphaChnl"].to_int();
+ }
+ if (keys.has("redChnl")) {
+ ch[0] = keys["redChnl"].to_int();
+ }
+ if (keys.has("greenChnl")) {
+ ch[1] = keys["greenChnl"].to_int();
+ }
+ if (keys.has("blueChnl")) {
+ ch[2] = keys["blueChnl"].to_int();
+ }
+ for (int i = 0; i < 4; i++) {
+ if (ch[i] == 0 && first_gl_ch == -1) {
+ first_gl_ch = i;
+ }
+ if (ch[i] == 1 && first_ol_ch == -1) {
+ first_ol_ch = i;
+ }
+ if (ch[i] == 2 && first_cm_ch == -1) {
+ first_cm_ch = i;
+ }
+ }
+ } else if (type == "page") {
+ int page = 0;
+ if (keys.has("id")) {
+ page = keys["id"].to_int();
+ }
+ if (keys.has("file")) {
+ String base_dir = p_path.get_base_dir();
+ String file = base_dir.plus_file(keys["file"]);
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img;
+ img.instantiate();
+ Error err = ImageLoader::load_image(file, img);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\".");
+ if (packed) {
+ if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_8bit(img, page, base_size);
+ } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_4bit(img, page, base_size);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ } else {
+ if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ set_texture_image(0, Vector2i(base_size, 0), page, img);
+ } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_rgba_4bit(img, page, base_size);
+ } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
+ _convert_mono_8bit(img, page, first_ol_ch, base_size, 1);
+ } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_4bit(img, page, first_cm_ch, base_size, 1);
+ } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ }
+ }
+ }
+ } else if (type == "char") {
+ char32_t idx = 0;
+ Vector2 advance;
+ Vector2 size;
+ Vector2 offset;
+ Rect2 uv_rect;
+ int texture_idx = -1;
+ uint8_t channel = 15;
+
+ if (keys.has("id")) {
+ idx = keys["id"].to_int();
+ }
+ if (keys.has("x")) {
+ uv_rect.position.x = keys["x"].to_int();
+ }
+ if (keys.has("y")) {
+ uv_rect.position.y = keys["y"].to_int();
+ }
+ if (keys.has("width")) {
+ uv_rect.size.width = keys["width"].to_int();
+ size.width = keys["width"].to_int();
+ }
+ if (keys.has("height")) {
+ uv_rect.size.height = keys["height"].to_int();
+ size.height = keys["height"].to_int();
+ }
+ if (keys.has("xoffset")) {
+ offset.x = keys["xoffset"].to_int();
+ }
+ if (keys.has("yoffset")) {
+ offset.y = keys["yoffset"].to_int() - ascent;
+ }
+ if (keys.has("page")) {
+ texture_idx = keys["page"].to_int();
+ }
+ if (keys.has("xadvance")) {
+ advance.x = keys["xadvance"].to_int();
+ }
+ if (advance.x < 0) {
+ advance.x = size.width + 1;
+ }
+ if (keys.has("chnl")) {
+ channel = keys["chnl"].to_int();
+ }
+
+ ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel."));
+ int ch_off = 0;
+ switch (channel) {
+ case 1:
+ ch_off = 2;
+ break; // B
+ case 2:
+ ch_off = 1;
+ break; // G
+ case 4:
+ ch_off = 0;
+ break; // R
+ case 8:
+ ch_off = 3;
+ break; // A
+ default:
+ ch_off = 0;
+ break;
+ }
+ set_glyph_advance(0, base_size, idx, advance);
+ set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
+ set_glyph_size(0, Vector2i(base_size, 0), idx, size);
+ set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, uv_rect);
+ set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ if (outline > 0) {
+ set_glyph_offset(0, Vector2i(base_size, 1), idx, offset);
+ set_glyph_size(0, Vector2i(base_size, 1), idx, size);
+ set_glyph_uv_rect(0, Vector2i(base_size, 1), idx, uv_rect);
+ set_glyph_texture_idx(0, Vector2i(base_size, 1), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ }
+ } else if (type == "kerning") {
+ Vector2i kpk;
+ if (keys.has("first")) {
+ kpk.x = keys["first"].to_int();
+ }
+ if (keys.has("second")) {
+ kpk.y = keys["second"].to_int();
+ }
+ if (keys.has("amount")) {
+ set_kerning(0, base_size, kpk, Vector2(keys["amount"].to_int(), 0));
+ }
+ }
+
+ if (f->eof_reached()) {
+ break;
+ }
+ }
+ }
+
+ set_font_name(font_name);
+ set_font_style(st_flags);
+ set_ascent(0, base_size, ascent);
+ set_descent(0, base_size, height - ascent);
+
+ return OK;
+}
+
+Error FontData::load_dynamic_font(const String &p_path) {
+ reset_state();
+
+ Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
+ set_data(data);
+
+ return OK;
+}
+
void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) {
data.clear();
data_ptr = p_data;
@@ -1585,8 +2344,9 @@ real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char,
bool Font::has_char(char32_t p_char) const {
for (int i = 0; i < data.size(); i++) {
- if (data[i]->has_char(p_char))
+ if (data[i]->has_char(p_char)) {
return true;
+ }
}
return false;
}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 1b4ecc73ce..93351a3493 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -63,6 +63,12 @@ class FontData : public Resource {
_FORCE_INLINE_ void _clear_cache();
_FORCE_INLINE_ void _ensure_rid(int p_cache_index) const;
+ void _convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz);
+ void _convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz);
+ void _convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz);
+ void _convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol);
+ void _convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol);
+
protected:
static void _bind_methods();
@@ -73,6 +79,9 @@ protected:
virtual void reset_state() override;
public:
+ Error load_bitmap_font(const String &p_path);
+ Error load_dynamic_font(const String &p_path);
+
// Font source data.
virtual void set_data_ptr(const uint8_t *p_data, size_t p_size);
virtual void set_data(const PackedByteArray &p_data);
diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp
index b9469803a0..28afef8638 100644
--- a/scene/resources/immediate_mesh.cpp
+++ b/scene/resources/immediate_mesh.cpp
@@ -382,7 +382,7 @@ AABB ImmediateMesh::get_aabb() const {
if (i == 0) {
aabb = surfaces[i].aabb;
} else {
- aabb.merge(surfaces[i].aabb);
+ aabb = aabb.merge(surfaces[i].aabb);
}
}
return aabb;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index d2d96b1f06..441e84eccc 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -1475,12 +1475,12 @@ void ArrayMesh::add_blend_shape(const StringName &p_name) {
StringName name = p_name;
- if (blend_shapes.find(name) != -1) {
+ if (blend_shapes.has(name)) {
int count = 2;
do {
name = String(p_name) + " " + itos(count);
count++;
- } while (blend_shapes.find(name) != -1);
+ } while (blend_shapes.has(name));
}
blend_shapes.push_back(name);
diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp
index 3db839a1d0..5168bf83eb 100644
--- a/scene/resources/mesh_library.cpp
+++ b/scene/resources/mesh_library.cpp
@@ -30,6 +30,8 @@
#include "mesh_library.h"
+#include "box_shape_3d.h"
+
bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
if (name.begins_with("item/")) {
@@ -255,12 +257,35 @@ int MeshLibrary::get_last_unused_item_id() const {
}
void MeshLibrary::_set_item_shapes(int p_item, const Array &p_shapes) {
- ERR_FAIL_COND(p_shapes.size() & 1);
+ Array arr_shapes = p_shapes;
+ int size = p_shapes.size();
+ if (size & 1) {
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
+ int prev_size = item_map[p_item].shapes.size() * 2;
+
+ if (prev_size < size) {
+ // Check if last element is a shape.
+ Ref<Shape3D> shape = arr_shapes[size - 1];
+ if (shape.is_null()) {
+ Ref<BoxShape3D> box_shape;
+ box_shape.instantiate();
+ arr_shapes[size - 1] = box_shape;
+ }
+
+ // Make sure the added element is a Transform3D.
+ arr_shapes.push_back(Transform3D());
+ size++;
+ } else {
+ size--;
+ arr_shapes.resize(size);
+ }
+ }
+
Vector<ShapeData> shapes;
- for (int i = 0; i < p_shapes.size(); i += 2) {
+ for (int i = 0; i < size; i += 2) {
ShapeData sd;
- sd.shape = p_shapes[i + 0];
- sd.local_transform = p_shapes[i + 1];
+ sd.shape = arr_shapes[i + 0];
+ sd.local_transform = arr_shapes[i + 1];
if (sd.shape.is_valid()) {
shapes.push_back(sd);
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 402e67a0f1..f9a4eba978 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -175,14 +175,12 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
#endif
}
} else {
- Object *obj = nullptr;
+ //node belongs to this scene and must be created
+ Object *obj = ClassDB::instantiate(snames[n.type]);
- if (ClassDB::is_class_enabled(snames[n.type])) {
- //node belongs to this scene and must be created
- obj = ClassDB::instantiate(snames[n.type]);
- }
+ node = Object::cast_to<Node>(obj);
- if (!Object::cast_to<Node>(obj)) {
+ if (!node) {
if (obj) {
memdelete(obj);
obj = nullptr;
@@ -203,9 +201,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
if (!obj) {
obj = memnew(Node);
}
- }
- node = Object::cast_to<Node>(obj);
+ node = Object::cast_to<Node>(obj);
+ }
}
if (node) {
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index aa9682bd80..3fc5fd4a16 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -106,8 +106,8 @@ class CapsuleMesh : public PrimitiveMesh {
GDCLASS(CapsuleMesh, PrimitiveMesh);
private:
- float radius = 1.0;
- float height = 3.0;
+ float radius = 0.5;
+ float height = 2.0;
int radial_segments = 64;
int rings = 8;
@@ -138,7 +138,7 @@ class BoxMesh : public PrimitiveMesh {
GDCLASS(BoxMesh, PrimitiveMesh);
private:
- Vector3 size = Vector3(2.0, 2.0, 2.0);
+ Vector3 size = Vector3(1, 1, 1);
int subdivide_w = 0;
int subdivide_h = 0;
int subdivide_d = 0;
@@ -171,8 +171,8 @@ class CylinderMesh : public PrimitiveMesh {
GDCLASS(CylinderMesh, PrimitiveMesh);
private:
- float top_radius = 1.0;
- float bottom_radius = 1.0;
+ float top_radius = 0.5;
+ float bottom_radius = 0.5;
float height = 2.0;
int radial_segments = 64;
int rings = 4;
diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp
index b5b3fd5e9f..2c0f6e779e 100644
--- a/scene/resources/skeleton_modification_3d.cpp
+++ b/scene/resources/skeleton_modification_3d.cpp
@@ -34,8 +34,9 @@
void SkeletonModification3D::_execute(real_t p_delta) {
GDVIRTUAL_CALL(_execute, p_delta);
- if (!enabled)
+ if (!enabled) {
return;
+ }
}
void SkeletonModification3D::_setup_modification(SkeletonModificationStack3D *p_stack) {
diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp
index d371598cc0..54ed71999c 100644
--- a/scene/resources/skin.cpp
+++ b/scene/resources/skin.cpp
@@ -143,6 +143,7 @@ void Skin::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bind_count"), &Skin::get_bind_count);
ClassDB::bind_method(D_METHOD("add_bind", "bone", "pose"), &Skin::add_bind);
+ ClassDB::bind_method(D_METHOD("add_named_bind", "name", "pose"), &Skin::add_named_bind);
ClassDB::bind_method(D_METHOD("set_bind_pose", "bind_index", "pose"), &Skin::set_bind_pose);
ClassDB::bind_method(D_METHOD("get_bind_pose", "bind_index"), &Skin::get_bind_pose);
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index 3918cc5ef8..c5d5ba2912 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -292,7 +292,6 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() {
}
ProceduralSkyMaterial::~ProceduralSkyMaterial() {
- RS::get_singleton()->material_set_shader(_get_material(), RID());
}
/////////////////////////////////////////
@@ -308,14 +307,30 @@ Ref<Texture2D> PanoramaSkyMaterial::get_panorama() const {
return panorama;
}
+void PanoramaSkyMaterial::set_filtering_enabled(bool p_enabled) {
+ filter = p_enabled;
+ notify_property_list_changed();
+ _update_shader();
+ // Only set if shader already compiled
+ if (shader_set) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(filter)]);
+ }
+}
+
+bool PanoramaSkyMaterial::is_filtering_enabled() const {
+ return filter;
+}
+
Shader::Mode PanoramaSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
RID PanoramaSkyMaterial::get_rid() const {
_update_shader();
+ // Don't compile shaders until first use, then compile both
if (!shader_set) {
- RS::get_singleton()->material_set_shader(_get_material(), shader);
+ RS::get_singleton()->material_set_shader(_get_material(), shader_cache[1 - int(filter)]);
+ RS::get_singleton()->material_set_shader(_get_material(), shader_cache[int(filter)]);
shader_set = true;
}
return _get_material();
@@ -323,42 +338,47 @@ RID PanoramaSkyMaterial::get_rid() const {
RID PanoramaSkyMaterial::get_shader_rid() const {
_update_shader();
- return shader;
+ return shader_cache[int(filter)];
}
void PanoramaSkyMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_panorama", "texture"), &PanoramaSkyMaterial::set_panorama);
ClassDB::bind_method(D_METHOD("get_panorama"), &PanoramaSkyMaterial::get_panorama);
+ ClassDB::bind_method(D_METHOD("set_filtering_enabled", "enabled"), &PanoramaSkyMaterial::set_filtering_enabled);
+ ClassDB::bind_method(D_METHOD("is_filtering_enabled"), &PanoramaSkyMaterial::is_filtering_enabled);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "panorama", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_panorama", "get_panorama");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter"), "set_filtering_enabled", "is_filtering_enabled");
}
Mutex PanoramaSkyMaterial::shader_mutex;
-RID PanoramaSkyMaterial::shader;
+RID PanoramaSkyMaterial::shader_cache[2];
void PanoramaSkyMaterial::cleanup_shader() {
- if (shader.is_valid()) {
- RS::get_singleton()->free(shader);
+ if (shader_cache[0].is_valid()) {
+ RS::get_singleton()->free(shader_cache[0]);
+ RS::get_singleton()->free(shader_cache[1]);
}
}
void PanoramaSkyMaterial::_update_shader() {
shader_mutex.lock();
- if (shader.is_null()) {
- shader = RS::get_singleton()->shader_create();
+ if (shader_cache[0].is_null()) {
+ for (int i = 0; i < 2; i++) {
+ shader_cache[i] = RS::get_singleton()->shader_create();
- // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
- RS::get_singleton()->shader_set_code(shader, R"(
+ // Add a comment to describe the shader origin (useful when converting to ShaderMaterial).
+ RS::get_singleton()->shader_set_code(shader_cache[i], vformat(R"(
// NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PanoramaSkyMaterial.
-
shader_type sky;
-
-uniform sampler2D source_panorama : filter_linear, hint_albedo;
-
+uniform sampler2D source_panorama : %s, hint_albedo;
void sky() {
COLOR = texture(source_panorama, SKY_COORDS).rgb;
}
-)");
+)",
+ i ? "filter_linear" : "filter_nearest"));
+ }
}
shader_mutex.unlock();
@@ -368,7 +388,6 @@ PanoramaSkyMaterial::PanoramaSkyMaterial() {
}
PanoramaSkyMaterial::~PanoramaSkyMaterial() {
- RS::get_singleton()->material_set_shader(_get_material(), RID());
}
//////////////////////////////////
diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h
index 74b2965ce8..7f421beb8d 100644
--- a/scene/resources/sky_material.h
+++ b/scene/resources/sky_material.h
@@ -110,10 +110,12 @@ private:
Ref<Texture2D> panorama;
static Mutex shader_mutex;
- static RID shader;
+ static RID shader_cache[2];
static void _update_shader();
mutable bool shader_set = false;
+ bool filter = true;
+
protected:
static void _bind_methods();
@@ -121,6 +123,9 @@ public:
void set_panorama(const Ref<Texture2D> &p_panorama);
Ref<Texture2D> get_panorama() const;
+ void set_filtering_enabled(bool p_enabled);
+ bool is_filtering_enabled() const;
+
virtual Shader::Mode get_shader_mode() const override;
virtual RID get_shader_rid() const override;
virtual RID get_rid() const override;
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index cc7322476f..0677d9c1a8 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -30,7 +30,6 @@
#include "surface_tool.h"
-#define _VERTEX_SNAP 0.0001
#define EQ_VERTEX_DIST 0.00001
SurfaceTool::OptimizeVertexCacheFunc SurfaceTool::optimize_vertex_cache_func = nullptr;
diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp
index 2efda08e08..e0aa21ac37 100644
--- a/scene/resources/syntax_highlighter.cpp
+++ b/scene/resources/syntax_highlighter.cpp
@@ -171,12 +171,15 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
/* color regions */
if (is_a_symbol || in_region != -1) {
int from = j;
- for (; from < line_length; from++) {
- if (str[from] == '\\') {
- from++;
- continue;
+
+ if (in_region == -1) {
+ for (; from < line_length; from++) {
+ if (str[from] == '\\') {
+ from++;
+ continue;
+ }
+ break;
}
- break;
}
if (from != line_length) {
@@ -208,6 +211,12 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
/* check if it's the whole line */
if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) {
+ if (from + end_key_length > line_length && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
+ // If it's key length and there is a '\', dont skip to highlight esc chars.
+ if (str.find("\\", from) >= 0) {
+ break;
+ }
+ }
prev_color = color_regions[in_region].color;
highlighter_info["color"] = color_regions[c].color;
color_map[j] = highlighter_info;
@@ -227,13 +236,23 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
/* if we are in one find the end key */
if (in_region != -1) {
+ bool is_string = (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'");
+
+ Color region_color = color_regions[in_region].color;
+ prev_color = region_color;
+ highlighter_info["color"] = region_color;
+ color_map[j] = highlighter_info;
+
/* search the line */
int region_end_index = -1;
int end_key_length = color_regions[in_region].end_key.length();
const char32_t *end_key = color_regions[in_region].end_key.get_data();
for (; from < line_length; from++) {
if (line_length - from < end_key_length) {
- break;
+ // Don't break if '\' to highlight esc chars.
+ if (!is_string || str.find("\\", from) < 0) {
+ break;
+ }
}
if (!is_symbol(str[from])) {
@@ -241,7 +260,20 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
}
if (str[from] == '\\') {
+ if (is_string) {
+ Dictionary escape_char_highlighter_info;
+ escape_char_highlighter_info["color"] = symbol_color;
+ color_map[from] = escape_char_highlighter_info;
+ }
+
from++;
+
+ if (is_string) {
+ Dictionary region_continue_highlighter_info;
+ prev_color = region_color;
+ region_continue_highlighter_info["color"] = region_color;
+ color_map[from + 1] = region_continue_highlighter_info;
+ }
continue;
}
@@ -258,10 +290,6 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
}
}
- prev_color = color_regions[in_region].color;
- highlighter_info["color"] = color_regions[in_region].color;
- color_map[j] = highlighter_info;
-
j = from + (end_key_length - 1);
if (region_end_index == -1) {
color_region_cache[p_line] = in_region;
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index f3752053c0..c3b5bd3564 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -55,7 +55,7 @@ void TextLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override);
- ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1));
ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER));
@@ -200,9 +200,9 @@ void TextLine::set_bidi_override(const Array &p_override) {
dirty = true;
}
-bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
ERR_FAIL_COND_V(p_fonts.is_null(), false);
- bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+ bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta);
spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
dirty = true;
diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h
index e68049ee45..c5762db0f2 100644
--- a/scene/resources/text_line.h
+++ b/scene/resources/text_line.h
@@ -86,7 +86,7 @@ public:
void set_preserve_control(bool p_enabled);
bool get_preserve_control() const;
- bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
+ bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant());
bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1);
bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER);
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index c3bdef7b01..4d75874199 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -63,7 +63,7 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL(""));
ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap);
- ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1));
ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER));
@@ -344,9 +344,9 @@ void TextParagraph::clear_dropcap() {
lines_dirty = true;
}
-bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
ERR_FAIL_COND_V(p_fonts.is_null(), false);
- bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
+ bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta);
spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
lines_dirty = true;
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index 773cc1a858..8a8a53943b 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -102,7 +102,7 @@ public:
bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
void clear_dropcap();
- bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
+ bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant());
bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1);
bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 28dc869c4f..ee66a61da8 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1156,7 +1156,7 @@ void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_m
rc.size.height = atlas->get_height();
}
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), atlas->get_rid(), rc, p_modulate, p_transpose, filter_clip);
+ atlas->draw_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), rc, p_modulate, p_transpose, filter_clip);
}
void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
@@ -1177,7 +1177,7 @@ void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile
Vector2 scale = p_rect.size / (region.size + margin.size);
Rect2 dr(p_rect.position + margin.position * scale, rc.size * scale);
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), rc, p_modulate, p_transpose, filter_clip);
+ atlas->draw_rect_region(p_canvas_item, dr, rc, p_modulate, p_transpose, filter_clip);
}
void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
@@ -1190,7 +1190,7 @@ void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons
Rect2 src_c;
get_rect_region(p_rect, p_src_rect, dr, src_c);
- RS::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dr, atlas->get_rid(), src_c, p_modulate, p_transpose, filter_clip);
+ atlas->draw_rect_region(p_canvas_item, dr, src_c, p_modulate, p_transpose, filter_clip);
}
bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
@@ -2103,8 +2103,8 @@ void GradientTexture2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update"), &GradientTexture2D::_update);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
ADD_GROUP("Fill", "fill_");
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index c3f29ad417..8075497c42 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -595,7 +595,7 @@ public:
private:
mutable RID _texture;
Ref<Curve> _curve;
- int _width = 2048;
+ int _width = 256;
int _current_width = 0;
TextureMode texture_mode = TEXTURE_MODE_RGB;
TextureMode _current_texture_mode = TEXTURE_MODE_RGB;
@@ -637,7 +637,7 @@ private:
Ref<Curve> _curve_x;
Ref<Curve> _curve_y;
Ref<Curve> _curve_z;
- int _width = 2048;
+ int _width = 256;
int _current_width = 0;
void _update();
@@ -685,7 +685,7 @@ private:
Ref<Gradient> gradient;
bool update_pending = false;
RID texture;
- int width = 2048;
+ int width = 256;
bool use_hdr = false;
void _queue_update();
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index ddb9cc7440..2854cbe30d 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -207,9 +207,6 @@ void TileMapPattern::_get_property_list(List<PropertyInfo> *p_list) const {
}
void TileMapPattern::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_set_tile_data", "data"), &TileMapPattern::_set_tile_data);
- ClassDB::bind_method(D_METHOD("_get_tile_data"), &TileMapPattern::_get_tile_data);
-
ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell);
ClassDB::bind_method(D_METHOD("remove_cell", "coords", "update_size"), &TileMapPattern::remove_cell);
@@ -403,7 +400,7 @@ void TileSet::_update_terrains_cache() {
int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
// Executed for each tile_data.
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+ TileData *tile_data = atlas_source->get_tile_data(tile_id, alternative_id);
int terrain_set = tile_data->get_terrain_set();
if (terrain_set >= 0) {
TileMapCell cell;
@@ -1377,7 +1374,7 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti
Ref<TileSetSource> source = sources[E->get().source_id];
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile);
sum += tile_data->get_probability();
} else {
sum += 1.0;
@@ -1398,7 +1395,7 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile);
count += tile_data->get_probability();
} else {
count += 1.0;
@@ -1663,7 +1660,7 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) {
int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+ TileData *tile_data = atlas_source->get_tile_data(tile_id, alternative_id);
int terrain_set = tile_data->get_terrain_set();
if (terrain_set >= 0) {
ERR_FAIL_INDEX_V(terrain_set, get_terrain_sets_count(), Vector<Vector<Ref<Texture2D>>>());
@@ -2399,7 +2396,7 @@ void TileSet::_compatibility_conversion() {
compatibility_tilemap_mapping[E.key][key_array] = value_array;
compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE;
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(coords, alternative_tile);
tile_data->set_flip_h(flip_h);
tile_data->set_flip_v(flip_v);
@@ -2491,7 +2488,7 @@ void TileSet::_compatibility_conversion() {
compatibility_tilemap_mapping[E.key][key_array] = value_array;
compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_ATLAS_TILE;
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
+ TileData *tile_data = atlas_source->get_tile_data(coords, alternative_tile);
tile_data->set_flip_h(flip_h);
tile_data->set_flip_v(flip_v);
@@ -4123,7 +4120,7 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_
Vector2 margin = (get_tile_texture_region(p_atlas_coords).size - tile_set->get_tile_size()) / 2;
margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y));
- Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset();
+ Vector2i effective_texture_offset = get_tile_data(p_atlas_coords, p_alternative_tile)->get_texture_offset();
if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) {
effective_texture_offset = effective_texture_offset.clamp(-margin, margin);
}
@@ -4262,7 +4259,7 @@ int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, i
return tiles[p_atlas_coords].alternatives_ids[p_index];
}
-Object *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const {
+TileData *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
@@ -5092,7 +5089,7 @@ int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_i
}
Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const {
- ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Ref<ConvexPolygonShape2D>());
ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref<ConvexPolygonShape2D>());
ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[p_polygon_index].shapes.size(), Ref<ConvexPolygonShape2D>());
return physics[p_layer_id].polygons[p_polygon_index].shapes[shape_index];
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 2673ca1cb6..95de46c9ab 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -693,7 +693,7 @@ public:
virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
// Get data associated to a tile.
- Object *get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const;
+ TileData *get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const;
// Helpers.
Vector2i get_atlas_grid_size() const;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index ece1ba1972..f41058d59c 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -53,8 +53,98 @@ int VisualShaderNode::get_output_port_for_preview() const {
return port_preview;
}
-void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p_value) {
- default_input_values[p_port] = p_value;
+void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p_value, const Variant &p_prev_value) {
+ Variant value = p_value;
+
+ if (p_prev_value.get_type() != Variant::NIL) {
+ switch (p_value.get_type()) {
+ case Variant::FLOAT: {
+ switch (p_prev_value.get_type()) {
+ case Variant::INT: {
+ value = (float)p_prev_value;
+ } break;
+ case Variant::FLOAT: {
+ value = p_prev_value;
+ } break;
+ case Variant::VECTOR2: {
+ Vector2 pv = p_prev_value;
+ value = pv.x;
+ } break;
+ case Variant::VECTOR3: {
+ Vector3 pv = p_prev_value;
+ value = pv.x;
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case Variant::INT: {
+ switch (p_prev_value.get_type()) {
+ case Variant::INT: {
+ value = p_prev_value;
+ } break;
+ case Variant::FLOAT: {
+ value = (int)p_prev_value;
+ } break;
+ case Variant::VECTOR2: {
+ Vector2 pv = p_prev_value;
+ value = (int)pv.x;
+ } break;
+ case Variant::VECTOR3: {
+ Vector3 pv = p_prev_value;
+ value = (int)pv.x;
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case Variant::VECTOR2: {
+ switch (p_prev_value.get_type()) {
+ case Variant::INT: {
+ float pv = (float)(int)p_prev_value;
+ value = Vector2(pv, pv);
+ } break;
+ case Variant::FLOAT: {
+ float pv = p_prev_value;
+ value = Vector2(pv, pv);
+ } break;
+ case Variant::VECTOR2: {
+ value = p_prev_value;
+ } break;
+ case Variant::VECTOR3: {
+ Vector3 pv = p_prev_value;
+ value = Vector2(pv.x, pv.y);
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case Variant::VECTOR3: {
+ switch (p_prev_value.get_type()) {
+ case Variant::INT: {
+ float pv = (float)(int)p_prev_value;
+ value = Vector3(pv, pv, pv);
+ } break;
+ case Variant::FLOAT: {
+ float pv = p_prev_value;
+ value = Vector3(pv, pv, pv);
+ } break;
+ case Variant::VECTOR2: {
+ Vector2 pv = p_prev_value;
+ value = Vector3(pv.x, pv.y, 0.0);
+ } break;
+ case Variant::VECTOR3: {
+ value = p_prev_value;
+ } break;
+ default:
+ break;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ default_input_values[p_port] = value;
emit_changed();
}
@@ -156,8 +246,15 @@ int VisualShaderNode::get_expanded_output_port_count() const {
int count2 = count;
for (int i = 0; i < count; i++) {
if (is_output_port_expandable(i) && _is_output_port_expanded(i)) {
- if (get_output_port_type(i) == PORT_TYPE_VECTOR) {
- count2 += 3;
+ switch (get_output_port_type(i)) {
+ case PORT_TYPE_VECTOR_2D: {
+ count2 += 2;
+ } break;
+ case PORT_TYPE_VECTOR: {
+ count2 += 3;
+ } break;
+ default:
+ break;
}
}
}
@@ -231,8 +328,8 @@ String VisualShaderNode::get_warning(Shader::Mode p_mode, VisualShader::Type p_t
return String();
}
-String VisualShaderNode::get_input_port_default_hint(int p_port) const {
- return "";
+bool VisualShaderNode::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ return false;
}
void VisualShaderNode::_bind_methods() {
@@ -245,7 +342,7 @@ void VisualShaderNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_output_ports_expanded", "values"), &VisualShaderNode::_set_output_ports_expanded);
ClassDB::bind_method(D_METHOD("_get_output_ports_expanded"), &VisualShaderNode::_get_output_ports_expanded);
- ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value"), &VisualShaderNode::set_input_port_default_value);
+ ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value", "prev_value"), &VisualShaderNode::set_input_port_default_value, DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("get_input_port_default_value", "port"), &VisualShaderNode::get_input_port_default_value);
ClassDB::bind_method(D_METHOD("remove_input_port_default_value", "port"), &VisualShaderNode::remove_input_port_default_value);
@@ -261,6 +358,7 @@ void VisualShaderNode::_bind_methods() {
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR);
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR_INT);
+ BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_2D);
BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR);
BIND_ENUM_CONSTANT(PORT_TYPE_BOOLEAN);
BIND_ENUM_CONSTANT(PORT_TYPE_TRANSFORM);
@@ -385,9 +483,9 @@ String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, Vis
return "";
}
-void VisualShaderNodeCustom::set_input_port_default_value(int p_port, const Variant &p_value) {
+void VisualShaderNodeCustom::set_input_port_default_value(int p_port, const Variant &p_value, const Variant &p_prev_value) {
if (!is_initialized) {
- VisualShaderNode::set_input_port_default_value(p_port, p_value);
+ VisualShaderNode::set_input_port_default_value(p_port, p_value, p_prev_value);
}
}
@@ -484,24 +582,48 @@ void VisualShader::update_engine_version(const Dictionary &p_new_version) {
if (expression.is_valid()) {
for (int j = 0; j < expression->get_input_port_count(); j++) {
int type = expression->get_input_port_type(j);
- if (type > 0) { // + PORT_TYPE_SCALAR_INT
- type += 1;
+ if (type > 0) { // + PORT_TYPE_SCALAR_INT + PORT_TYPE_VECTOR_2D
+ type += 2;
}
expression->set_input_port_type(j, type);
}
for (int j = 0; j < expression->get_output_port_count(); j++) {
int type = expression->get_output_port_type(j);
- if (type > 0) { // + PORT_TYPE_SCALAR_INT
- type += 1;
+ if (type > 0) { // + PORT_TYPE_SCALAR_INT + PORT_TYPE_VECTOR_2D
+ type += 2;
}
expression->set_output_port_type(j, type);
}
}
+ Ref<VisualShaderNodeStep> step = Object::cast_to<VisualShaderNodeStep>(E.value.node.ptr());
+ if (step.is_valid()) {
+ int op_type = int(step->get_op_type());
+ if (int(op_type) > 0) { // + OP_TYPE_VECTOR_2D + OP_TYPE_VECTOR_2D_SCALAR
+ op_type += 2;
+ }
+ step->set_op_type(VisualShaderNodeStep::OpType(op_type));
+ }
+ Ref<VisualShaderNodeSmoothStep> sstep = Object::cast_to<VisualShaderNodeSmoothStep>(E.value.node.ptr());
+ if (sstep.is_valid()) {
+ int op_type = int(sstep->get_op_type());
+ if (int(op_type) > 0) { // + OP_TYPE_VECTOR_2D + OP_TYPE_VECTOR_2D_SCALAR
+ op_type += 2;
+ }
+ sstep->set_op_type(VisualShaderNodeSmoothStep::OpType(op_type));
+ }
+ Ref<VisualShaderNodeMix> mix = Object::cast_to<VisualShaderNodeMix>(E.value.node.ptr());
+ if (mix.is_valid()) {
+ int op_type = int(mix->get_op_type());
+ if (int(op_type) > 0) { // + OP_TYPE_VECTOR_2D + OP_TYPE_VECTOR_2D_SCALAR
+ op_type += 2;
+ }
+ mix->set_op_type(VisualShaderNodeMix::OpType(op_type));
+ }
Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E.value.node.ptr());
if (compare.is_valid()) {
int ctype = int(compare->get_comparison_type());
- if (int(ctype) > 0) { // + PORT_TYPE_SCALAR_INT
- ctype += 1;
+ if (int(ctype) > 0) { // + CTYPE_SCALAR_INT + CTYPE_VECTOR_2D
+ ctype += 2;
}
compare->set_comparison_type(VisualShaderNodeCompare::ComparisonType(ctype));
}
@@ -719,7 +841,7 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
}
bool VisualShader::is_port_types_compatible(int p_a, int p_b) const {
- return MAX(0, p_a - 3) == (MAX(0, p_b - 3));
+ return MAX(0, p_a - 4) == (MAX(0, p_b - 4));
}
void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
@@ -953,15 +1075,27 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
Error err = _write_node(p_type, global_code, global_code_per_node, global_code_per_func, code, default_tex_params, input_connections, output_connections, p_node, processed, true, classes);
ERR_FAIL_COND_V(err != OK, String());
- if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR) {
- code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " );\n";
- } else if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
- code += " COLOR.rgb = vec3(float(n_out" + itos(p_node) + "p" + itos(p_port) + "));\n";
- } else if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " ? 1.0 : 0.0);\n";
- } else {
- code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n";
+ switch (node->get_output_port_type(p_port)) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + ");\n";
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ code += " COLOR.rgb = vec3(float(n_out" + itos(p_node) + "p" + itos(p_port) + "));\n";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + " ? 1.0 : 0.0);\n";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + ", 0.0);\n";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n";
+ } break;
+ default: {
+ code += " COLOR.rgb = vec3(0.0);\n";
+ } break;
}
+
code += "}\n";
//set code secretly
@@ -1406,30 +1540,102 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
}
} else if (in_type == out_type) {
inputs[i] = src_var;
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_VECTOR) {
- inputs[i] = "dot(" + src_var + ", vec3(0.333333, 0.333333, 0.333333))";
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR_INT && out_type == VisualShaderNode::PORT_TYPE_VECTOR) {
- inputs[i] = "dot(float(" + src_var + "), vec3(0.333333, 0.333333, 0.333333))";
- } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
- inputs[i] = "vec3(" + src_var + ")";
- } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
- inputs[i] = "vec3(float(" + src_var + "))";
- } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_VECTOR) {
- inputs[i] = "all(bvec3(" + src_var + "))";
- } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
- inputs[i] = src_var + " > 0.0 ? true : false";
- } else if (in_type == VisualShaderNode::PORT_TYPE_BOOLEAN && out_type == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
- inputs[i] = src_var + " > 0 ? true : false";
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- inputs[i] = "(" + src_var + " ? 1.0 : 0.0)";
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR_INT && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- inputs[i] = "(" + src_var + " ? 1 : 0)";
- } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_BOOLEAN) {
- inputs[i] = "vec3(" + src_var + " ? 1.0 : 0.0)";
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_SCALAR_INT) {
- inputs[i] = "float(" + src_var + ")";
- } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR_INT && out_type == VisualShaderNode::PORT_TYPE_SCALAR) {
- inputs[i] = "int(" + src_var + ")";
+ } else {
+ switch (in_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ inputs[i] = "float(" + src_var + ")";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ inputs[i] = "(" + src_var + " ? 1.0 : 0.0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ inputs[i] = "dot(" + src_var + ", vec2(0.333333, 0.333333))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ inputs[i] = "dot(" + src_var + ", vec3(0.333333, 0.333333, 0.333333))";
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ inputs[i] = "int(" + src_var + ")";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ inputs[i] = "(" + src_var + " ? 1 : 0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ inputs[i] = "dot(float(" + src_var + "), vec2(0.333333, 0.333333))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ inputs[i] = "dot(float(" + src_var + "), vec3(0.333333, 0.333333, 0.333333))";
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ inputs[i] = src_var + " > 0.0 ? true : false";
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ inputs[i] = src_var + " > 0 ? true : false";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ inputs[i] = "all(bvec2(" + src_var + "))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ inputs[i] = "all(bvec3(" + src_var + "))";
+ } break;
+ default:
+ break;
+ }
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ inputs[i] = "vec2(" + src_var + ")";
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ inputs[i] = "vec2(float(" + src_var + "))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ inputs[i] = "vec2(" + src_var + " ? 1.0 : 0.0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ inputs[i] = "vec2(" + src_var + ".xy)";
+ } break;
+ default:
+ break;
+ }
+ } break;
+
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ switch (out_type) {
+ case VisualShaderNode::PORT_TYPE_SCALAR: {
+ inputs[i] = "vec3(" + src_var + ")";
+ } break;
+ case VisualShaderNode::PORT_TYPE_SCALAR_INT: {
+ inputs[i] = "vec3(float(" + src_var + "))";
+ } break;
+ case VisualShaderNode::PORT_TYPE_BOOLEAN: {
+ inputs[i] = "vec3(" + src_var + " ? 1.0 : 0.0)";
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ inputs[i] = "vec3(" + src_var + ", 0.0)";
+ } break;
+ default:
+ break;
+ }
+ } break;
+ default:
+ break;
+ }
}
} else {
if (!vsnode->is_generate_input_var(i)) {
@@ -1449,6 +1655,10 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
bool val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
node_code += " bool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n";
+ } else if (defval.get_type() == Variant::VECTOR2) {
+ Vector2 val = defval;
+ inputs[i] = "n_in" + itos(node) + "p" + itos(i);
+ node_code += " vec2 " + inputs[i] + " = " + vformat("vec2(%.5f, %.5f);\n", val.x, val.y);
} else if (defval.get_type() == Variant::VECTOR3) {
Vector3 val = defval;
inputs[i] = "n_in" + itos(node) + "p" + itos(i);
@@ -1485,8 +1695,15 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
if (vsnode->is_output_port_expandable(i) && vsnode->_is_output_port_expanded(i)) {
expanded = true;
- if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
- output_count += 3;
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ output_count += 2;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ output_count += 3;
+ } break;
+ default:
+ break;
}
}
expanded_output_ports.insert(i, expanded);
@@ -1506,6 +1723,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_SCALAR_INT:
outputs[i] = "int " + var_name;
break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D:
+ outputs[i] = "vec2 " + var_name;
+ break;
case VisualShaderNode::PORT_TYPE_VECTOR:
outputs[i] = "vec3 " + var_name;
break;
@@ -1515,12 +1735,19 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_TRANSFORM:
outputs[i] = "mat4 " + var_name;
break;
- default: {
- }
+ default:
+ break;
}
if (expanded_output_ports[i]) {
- if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
- j += 3;
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ j += 2;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ j += 3;
+ } break;
+ default:
+ break;
}
}
}
@@ -1535,6 +1762,9 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_SCALAR_INT:
code += " int " + outputs[i] + ";\n";
break;
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D:
+ code += " vec2 " + outputs[i] + ";\n";
+ break;
case VisualShaderNode::PORT_TYPE_VECTOR:
code += " vec3 " + outputs[i] + ";\n";
break;
@@ -1544,12 +1774,19 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_TRANSFORM:
code += " mat4 " + outputs[i] + ";\n";
break;
- default: {
- }
+ default:
+ break;
}
if (expanded_output_ports[i]) {
- if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
- j += 3;
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ j += 2;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ j += 3;
+ } break;
+ default:
+ break;
}
}
}
@@ -1565,38 +1802,65 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
for (int i = 0; i < output_count; i++) {
bool new_line_inserted = false;
if (expanded_output_ports[i]) {
- if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
- if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component
- if (!new_line_inserted) {
- code += "\n";
- new_line_inserted = true;
+ switch (vsnode->get_output_port_type(i)) {
+ case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
+ if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String r = "n_out" + itos(node) + "p" + itos(i + 1);
+ code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n";
+ outputs[i + 1] = r;
}
- String r = "n_out" + itos(node) + "p" + itos(i + 1);
- code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n";
- outputs[i + 1] = r;
- }
- if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component
- if (!new_line_inserted) {
- code += "\n";
- new_line_inserted = true;
+ if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String g = "n_out" + itos(node) + "p" + itos(i + 2);
+ code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n";
+ outputs[i + 2] = g;
}
- String g = "n_out" + itos(node) + "p" + itos(i + 2);
- code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n";
- outputs[i + 2] = g;
- }
- if (vsnode->is_output_port_connected(i + 3) || (for_preview && vsnode->get_output_port_for_preview() == (i + 3))) { // blue-component
- if (!new_line_inserted) {
- code += "\n";
- new_line_inserted = true;
+ i += 2;
+ } break;
+ case VisualShaderNode::PORT_TYPE_VECTOR: {
+ if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String r = "n_out" + itos(node) + "p" + itos(i + 1);
+ code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n";
+ outputs[i + 1] = r;
+ }
+
+ if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String g = "n_out" + itos(node) + "p" + itos(i + 2);
+ code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n";
+ outputs[i + 2] = g;
}
- String b = "n_out" + itos(node) + "p" + itos(i + 3);
- code += " float " + b + " = n_out" + itos(node) + "p" + itos(i) + ".b;\n";
- outputs[i + 3] = b;
- }
- i += 3;
+ if (vsnode->is_output_port_connected(i + 3) || (for_preview && vsnode->get_output_port_for_preview() == (i + 3))) { // blue-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String b = "n_out" + itos(node) + "p" + itos(i + 3);
+ code += " float " + b + " = n_out" + itos(node) + "p" + itos(i) + ".b;\n";
+ outputs[i + 3] = b;
+ }
+
+ i += 3;
+ } break;
+ default:
+ break;
}
}
}
@@ -2066,8 +2330,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
@@ -2082,7 +2346,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "VIEWPORT_SIZE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
// Node3D, Fragment
@@ -2092,19 +2356,19 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "VIEWPORT_SIZE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "front_facing", "FRONT_FACING" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" },
@@ -2114,8 +2378,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
// Node3D, Light
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR" },
@@ -2132,18 +2396,18 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "VIEWPORT_SIZE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
// Canvas Item
// Canvas Item, Vertex
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "canvas", "CANVAS_MATRIX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen", "SCREEN_MATRIX" },
@@ -2154,13 +2418,13 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
// Canvas Item, Fragment
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_pixel_size", "vec3(SCREEN_PIXEL_SIZE, 1.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_pixel_size", "SCREEN_PIXEL_SIZE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
@@ -2169,11 +2433,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
// Canvas Item, Light
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
@@ -2185,9 +2449,9 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow", "SHADOW_MODULATE.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_MODULATE.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
@@ -2302,8 +2566,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "sky_coords", "vec3(SKY_COORDS, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "sky_coords", "SKY_COORDS" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Fog, Fog
@@ -2312,7 +2576,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "object_position", "OBJECT_POSITION" },
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "uvw", "UVW" },
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "extents", "EXTENTS" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "sdf", "SDF" },
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
@@ -2325,11 +2588,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Spatial, Fragment
@@ -2338,27 +2601,27 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Spatial, Light
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Canvas Item, Vertex
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
@@ -2366,20 +2629,20 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
// Canvas Item, Fragment
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Canvas Item, Light
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles
@@ -2392,7 +2655,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
// Sky
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Fog
@@ -2456,6 +2719,9 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
case PORT_TYPE_SCALAR_INT: {
code = " " + p_output_vars[0] + " = 0;\n";
} break;
+ case PORT_TYPE_VECTOR_2D: {
+ code = " " + p_output_vars[0] + " = vec2(0.0);\n";
+ } break;
case PORT_TYPE_VECTOR: {
code = " " + p_output_vars[0] + " = vec3(0.0);\n";
} break;
@@ -2671,7 +2937,9 @@ int VisualShaderNodeUniformRef::get_output_port_count() const {
return 1;
case UniformType::UNIFORM_TYPE_BOOLEAN:
return 1;
- case UniformType::UNIFORM_TYPE_VECTOR:
+ case UniformType::UNIFORM_TYPE_VECTOR2:
+ return 1;
+ case UniformType::UNIFORM_TYPE_VECTOR3:
return 1;
case UniformType::UNIFORM_TYPE_TRANSFORM:
return 1;
@@ -2693,7 +2961,9 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_output_port
return PortType::PORT_TYPE_SCALAR_INT;
case UniformType::UNIFORM_TYPE_BOOLEAN:
return PortType::PORT_TYPE_BOOLEAN;
- case UniformType::UNIFORM_TYPE_VECTOR:
+ case UniformType::UNIFORM_TYPE_VECTOR2:
+ return PortType::PORT_TYPE_VECTOR_2D;
+ case UniformType::UNIFORM_TYPE_VECTOR3:
return PortType::PORT_TYPE_VECTOR;
case UniformType::UNIFORM_TYPE_TRANSFORM:
return PortType::PORT_TYPE_TRANSFORM;
@@ -2720,7 +2990,9 @@ String VisualShaderNodeUniformRef::get_output_port_name(int p_port) const {
return "";
case UniformType::UNIFORM_TYPE_BOOLEAN:
return "";
- case UniformType::UNIFORM_TYPE_VECTOR:
+ case UniformType::UNIFORM_TYPE_VECTOR2:
+ return "";
+ case UniformType::UNIFORM_TYPE_VECTOR3:
return "";
case UniformType::UNIFORM_TYPE_TRANSFORM:
return "";
@@ -2790,7 +3062,9 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_port_type_b
return PORT_TYPE_SCALAR_INT;
case UniformType::UNIFORM_TYPE_SAMPLER:
return PORT_TYPE_SAMPLER;
- case UniformType::UNIFORM_TYPE_VECTOR:
+ case UniformType::UNIFORM_TYPE_VECTOR2:
+ return PORT_TYPE_VECTOR_2D;
+ case UniformType::UNIFORM_TYPE_VECTOR3:
return PORT_TYPE_VECTOR;
case UniformType::UNIFORM_TYPE_TRANSFORM:
return PORT_TYPE_TRANSFORM;
@@ -2809,26 +3083,19 @@ String VisualShaderNodeUniformRef::generate_code(Shader::Mode p_mode, VisualShad
if (uniform_name == "[None]") {
return " " + p_output_vars[0] + " = 0.0;\n";
}
- return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
- case UniformType::UNIFORM_TYPE_INT:
- return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
- case UniformType::UNIFORM_TYPE_BOOLEAN:
- return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
- case UniformType::UNIFORM_TYPE_VECTOR:
- return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
- case UniformType::UNIFORM_TYPE_TRANSFORM:
- return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+ break;
case UniformType::UNIFORM_TYPE_COLOR: {
String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n";
code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n";
return code;
} break;
case UniformType::UNIFORM_TYPE_SAMPLER:
- break;
+ return String();
default:
break;
}
- return "";
+
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
}
void VisualShaderNodeUniformRef::_set_uniform_type(int p_uniform_type) {
@@ -2872,8 +3139,8 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "UV2:xy" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
@@ -2898,7 +3165,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat", "CLEARCOAT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_gloss", "CLEARCOAT_GLOSS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "anisotropy_flow", "ANISOTROPY_FLOW:xy" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "anisotropy_flow", "ANISOTROPY_FLOW" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "backlight", "BACKLIGHT" },
@@ -2916,8 +3183,8 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
////////////////////////////////////////////////////////////////////////
// Canvas Item, Vertex.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX:xy" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
@@ -2930,7 +3197,7 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal_map", "NORMAL_MAP" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_vertex", "SHADOW_VERTEX:xy" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "shadow_vertex", "SHADOW_VERTEX" },
////////////////////////////////////////////////////////////////////////
// Canvas Item, Light.
////////////////////////////////////////////////////////////////////////
@@ -3402,7 +3669,6 @@ bool VisualShaderNodeGroupBase::is_valid_port_name(const String &p_name) const {
}
void VisualShaderNodeGroupBase::add_input_port(int p_id, int p_type, const String &p_name) {
- ERR_FAIL_COND(has_input_port(p_id));
ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
ERR_FAIL_COND(!is_valid_port_name(p_name));
@@ -3478,7 +3744,6 @@ bool VisualShaderNodeGroupBase::has_input_port(int p_id) const {
}
void VisualShaderNodeGroupBase::add_output_port(int p_id, int p_type, const String &p_name) {
- ERR_FAIL_COND(has_output_port(p_id));
ERR_FAIL_INDEX(p_type, int(PORT_TYPE_MAX));
ERR_FAIL_COND(!is_valid_port_name(p_name));
@@ -3883,6 +4148,9 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad
case PORT_TYPE_SCALAR_INT:
tk = "0";
break;
+ case PORT_TYPE_VECTOR_2D:
+ tk = "vec2(0.0, 0.0)";
+ break;
case PORT_TYPE_VECTOR:
tk = "vec3(0.0, 0.0, 0.0)";
break;
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 066c18e3bc..9f877e08a5 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -214,6 +214,7 @@ public:
enum PortType {
PORT_TYPE_SCALAR,
PORT_TYPE_SCALAR_INT,
+ PORT_TYPE_VECTOR_2D,
PORT_TYPE_VECTOR,
PORT_TYPE_BOOLEAN,
PORT_TYPE_TRANSFORM,
@@ -229,7 +230,7 @@ public:
virtual PortType get_input_port_type(int p_port) const = 0;
virtual String get_input_port_name(int p_port) const = 0;
- virtual void set_input_port_default_value(int p_port, const Variant &p_value);
+ virtual void set_input_port_default_value(int p_port, const Variant &p_value, const Variant &p_prev_value = Variant());
Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied)
Array get_default_input_values() const;
virtual void set_default_input_values(const Array &p_values);
@@ -240,7 +241,7 @@ public:
virtual PortType get_output_port_type(int p_port) const = 0;
virtual String get_output_port_name(int p_port) const = 0;
- virtual String get_input_port_default_hint(int p_port) const;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const;
void set_output_port_for_preview(int p_index);
int get_output_port_for_preview() const;
@@ -311,7 +312,7 @@ protected:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual void set_input_port_default_value(int p_port, const Variant &p_value) override;
+ virtual void set_input_port_default_value(int p_port, const Variant &p_value, const Variant &p_prev_value = Variant()) override;
virtual void set_default_input_values(const Array &p_values) override;
virtual void remove_input_port_default_value(int p_port) override;
virtual void clear_default_input_values() override;
@@ -492,7 +493,8 @@ public:
UNIFORM_TYPE_FLOAT,
UNIFORM_TYPE_INT,
UNIFORM_TYPE_BOOLEAN,
- UNIFORM_TYPE_VECTOR,
+ UNIFORM_TYPE_VECTOR2,
+ UNIFORM_TYPE_VECTOR3,
UNIFORM_TYPE_TRANSFORM,
UNIFORM_TYPE_COLOR,
UNIFORM_TYPE_SAMPLER,
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index b61e3f5d14..9a714fd3dd 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -30,6 +30,65 @@
#include "visual_shader_nodes.h"
+////////////// Vector Base
+
+VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_input_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_output_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
+ return PORT_TYPE_SCALAR;
+}
+
+void VisualShaderNodeVectorBase::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
+VisualShaderNodeVectorBase::OpType VisualShaderNodeVectorBase::get_op_type() const {
+ return op_type;
+}
+
+void VisualShaderNodeVectorBase::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeVectorBase::set_op_type);
+ ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeVectorBase::get_op_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Vector2,Vector3"), "set_op_type", "get_op_type");
+
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
+}
+
+Vector<StringName> VisualShaderNodeVectorBase::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("op_type");
+ return props;
+}
+
+VisualShaderNodeVectorBase::VisualShaderNodeVectorBase() {
+}
+
////////////// Constants Base
VisualShaderNodeConstant::VisualShaderNodeConstant() {
@@ -294,10 +353,72 @@ void VisualShaderNodeColorConstant::_bind_methods() {
VisualShaderNodeColorConstant::VisualShaderNodeColorConstant() {
}
-////////////// Vector
+////////////// Vector2
+
+String VisualShaderNodeVec2Constant::get_caption() const {
+ return "Vector2Constant";
+}
+
+int VisualShaderNodeVec2Constant::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeVec2Constant::PortType VisualShaderNodeVec2Constant::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_2D;
+}
+
+String VisualShaderNodeVec2Constant::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeVec2Constant::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVec2Constant::PortType VisualShaderNodeVec2Constant::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_2D;
+}
+
+String VisualShaderNodeVec2Constant::get_output_port_name(int p_port) const {
+ return ""; //no output port means the editor will be used as port
+}
+
+String VisualShaderNodeVec2Constant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return " " + p_output_vars[0] + " = " + vformat("vec2(%.6f, %.6f)", constant.x, constant.y) + ";\n";
+}
+
+void VisualShaderNodeVec2Constant::set_constant(const Vector2 &p_constant) {
+ if (constant.is_equal_approx(p_constant)) {
+ return;
+ }
+ constant = p_constant;
+ emit_changed();
+}
+
+Vector2 VisualShaderNodeVec2Constant::get_constant() const {
+ return constant;
+}
+
+Vector<StringName> VisualShaderNodeVec2Constant::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("constant");
+ return props;
+}
+
+void VisualShaderNodeVec2Constant::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeVec2Constant::set_constant);
+ ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec2Constant::get_constant);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant"), "set_constant", "get_constant");
+}
+
+VisualShaderNodeVec2Constant::VisualShaderNodeVec2Constant() {
+}
+
+////////////// Vector3
String VisualShaderNodeVec3Constant::get_caption() const {
- return "VectorConstant";
+ return "Vector3Constant";
}
int VisualShaderNodeVec3Constant::get_input_port_count() const {
@@ -439,7 +560,7 @@ int VisualShaderNodeTexture::get_input_port_count() const {
VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
case 1:
return PORT_TYPE_SCALAR;
case 2:
@@ -487,11 +608,13 @@ bool VisualShaderNodeTexture::is_output_port_expandable(int p_port) const {
return false;
}
-String VisualShaderNodeTexture::get_input_port_default_hint(int p_port) const {
- if (p_port == 0) {
- return "default";
+bool VisualShaderNodeTexture::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ if (p_port == 0) {
+ return true;
+ }
}
- return "";
+ return false;
}
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
@@ -526,8 +649,8 @@ String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShade
String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String default_uv;
- if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
- default_uv = "UV.xy";
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ default_uv = "UV";
} else {
default_uv = "vec2(0.0)";
}
@@ -545,9 +668,9 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
} else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
+ code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ");\n";
} else {
- code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n";
@@ -573,9 +696,9 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
} else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
+ code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n";
} else {
- code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
code += " " + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n";
@@ -597,9 +720,9 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
} else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, 0.0);\n";
+ code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", 0.0);\n";
} else {
- code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
code += " " + p_output_vars[0] + " = _tex_read.rgb;\n";
@@ -620,9 +743,9 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader:
} else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 _tex_read = texture(TEXTURE, " + p_input_vars[0] + ".xy);\n";
+ code += " vec4 _tex_read = texture(TEXTURE, " + p_input_vars[0] + ");\n";
} else {
- code += " vec4 _tex_read = textureLod(TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 _tex_read = textureLod(TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
code += " " + p_output_vars[0] + " = _tex_read.rgb;\n";
@@ -1052,16 +1175,18 @@ bool VisualShaderNodeSample3D::is_output_port_expandable(int p_port) const {
return false;
}
-String VisualShaderNodeSample3D::get_input_port_default_hint(int p_port) const {
- if (p_port == 0) {
- return "default";
+bool VisualShaderNodeSample3D::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ if (p_port == 0) {
+ return true;
+ }
}
- return "";
+ return false;
}
String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String default_uv;
- if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
default_uv = "vec3(UV, 0.0)";
} else {
default_uv = "vec3(0.0)";
@@ -1346,7 +1471,7 @@ String VisualShaderNodeCubemap::generate_global(Shader::Mode p_mode, VisualShade
String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String default_uv;
- if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
default_uv = "vec3(UV, 0.0)";
} else {
default_uv = "vec3(0.0)";
@@ -1393,11 +1518,13 @@ String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader:
return code;
}
-String VisualShaderNodeCubemap::get_input_port_default_hint(int p_port) const {
- if (p_port == 0) {
- return "default";
+bool VisualShaderNodeCubemap::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ if (p_port == 0) {
+ return true;
+ }
}
- return "";
+ return false;
}
void VisualShaderNodeCubemap::set_source(Source p_source) {
@@ -1724,10 +1851,6 @@ int VisualShaderNodeVectorOp::get_input_port_count() const {
return 2;
}
-VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorOp::get_input_port_name(int p_port) const {
return p_port == 0 ? "a" : "b";
}
@@ -1736,12 +1859,8 @@ int VisualShaderNodeVectorOp::get_output_port_count() const {
return 1;
}
-VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorOp::get_output_port_name(int p_port) const {
- return "op"; //no output port means the editor will be used as port
+ return "op";
}
String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
@@ -1772,13 +1891,21 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader
code += "min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
case OP_CROSS:
- code += "cross(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ if (op_type == OP_TYPE_VECTOR_2D) { // not supported
+ code += "vec2(0.0);\n";
+ } else {
+ code += "cross(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ }
break;
case OP_ATAN2:
code += "atan(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
break;
case OP_REFLECT:
- code += "reflect(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ if (op_type == OP_TYPE_VECTOR_2D) { // not supported
+ code += "vec2(0.0);\n";
+ } else {
+ code += "reflect(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ }
break;
case OP_STEP:
code += "step(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
@@ -1790,6 +1917,27 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader
return code;
}
+void VisualShaderNodeVectorOp::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
void VisualShaderNodeVectorOp::set_operator(Operator p_op) {
ERR_FAIL_INDEX(int(p_op), int(OP_ENUM_SIZE));
if (op == p_op) {
@@ -1804,11 +1952,27 @@ VisualShaderNodeVectorOp::Operator VisualShaderNodeVectorOp::get_operator() cons
}
Vector<StringName> VisualShaderNodeVectorOp::get_editable_properties() const {
- Vector<StringName> props;
+ Vector<StringName> props = VisualShaderNodeVectorBase::get_editable_properties();
props.push_back("operator");
return props;
}
+String VisualShaderNodeVectorOp::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ bool invalid_type = false;
+
+ if (op_type == OP_TYPE_VECTOR_2D) {
+ if (op == OP_CROSS || op == OP_REFLECT) {
+ invalid_type = true;
+ }
+ }
+
+ if (invalid_type) {
+ return TTR("Invalid operator for that type.");
+ }
+
+ return String();
+}
+
void VisualShaderNodeVectorOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeVectorOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeVectorOp::get_operator);
@@ -1831,8 +1995,18 @@ void VisualShaderNodeVectorOp::_bind_methods() {
}
VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() {
- set_input_port_default_value(0, Vector3());
- set_input_port_default_value(1, Vector3());
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2());
+ set_input_port_default_value(1, Vector2());
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(1, Vector3());
+ } break;
+ default:
+ break;
+ }
}
////////////// Color Op
@@ -2406,10 +2580,6 @@ int VisualShaderNodeVectorFunc::get_input_port_count() const {
return 1;
}
-VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorFunc::get_input_port_name(int p_port) const {
return "";
}
@@ -2418,22 +2588,18 @@ int VisualShaderNodeVectorFunc::get_output_port_count() const {
return 1;
}
-VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const {
- return ""; //no output port means the editor will be used as port
+ return "result";
}
String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *vec_func_id[FUNC_MAX] = {
+ static const char *funcs[FUNC_MAX] = {
"normalize($)",
- "max(min($, vec3(1.0)), vec3(0.0))",
+ "", // FUNC_SATURATE
"-($)",
"1.0 / ($)",
- "",
- "",
+ "", // FUNC_RGB2HSV
+ "", // FUNC_HSV2RGB
"abs($)",
"acos($)",
"acosh($)",
@@ -2462,12 +2628,37 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad
"tan($)",
"tanh($)",
"trunc($)",
- "vec3(1.0, 1.0, 1.0) - $"
+ "" // FUNC_ONEMINUS
};
+ if (func == FUNC_SATURATE) {
+ String code;
+
+ if (op_type == OP_TYPE_VECTOR_2D) {
+ code = "max(min($, vec2(1.0)), vec2(0.0))";
+ } else {
+ code = "max(min($, vec3(1.0)), vec3(0.0))";
+ }
+ return " " + p_output_vars[0] + " = " + code.replace("$", p_input_vars[0]) + ";\n";
+ }
+
+ if (func == FUNC_ONEMINUS) {
+ String code;
+
+ if (op_type == OP_TYPE_VECTOR_2D) {
+ code = "vec2(1.0, 1.0) - $";
+ } else {
+ code = "vec3(1.0, 1.0, 1.0) - $";
+ }
+ return " " + p_output_vars[0] + " = " + code.replace("$", p_input_vars[0]) + ";\n";
+ }
+
String code;
if (func == FUNC_RGB2HSV) {
+ if (op_type == OP_TYPE_VECTOR_2D) { // not supported
+ return " " + p_output_vars[0] + " = vec2(0.0);\n";
+ }
code += " {\n";
code += " vec3 c = " + p_input_vars[0] + ";\n";
code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n";
@@ -2478,6 +2669,9 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad
code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n";
code += " }\n";
} else if (func == FUNC_HSV2RGB) {
+ if (op_type == OP_TYPE_VECTOR_2D) { // not supported
+ return " " + p_output_vars[0] + " = vec2(0.0);\n";
+ }
code += " {\n";
code += " vec3 c = " + p_input_vars[0] + ";\n";
code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n";
@@ -2486,12 +2680,31 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad
code += " }\n";
} else {
- code += " " + p_output_vars[0] + " = " + String(vec_func_id[func]).replace("$", p_input_vars[0]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n";
}
return code;
}
+void VisualShaderNodeVectorFunc::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
void VisualShaderNodeVectorFunc::set_function(Function p_func) {
ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
if (func == p_func) {
@@ -2513,11 +2726,27 @@ VisualShaderNodeVectorFunc::Function VisualShaderNodeVectorFunc::get_function()
}
Vector<StringName> VisualShaderNodeVectorFunc::get_editable_properties() const {
- Vector<StringName> props;
+ Vector<StringName> props = VisualShaderNodeVectorBase::get_editable_properties();
props.push_back("function");
return props;
}
+String VisualShaderNodeVectorFunc::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
+ bool invalid_type = false;
+
+ if (op_type == OP_TYPE_VECTOR_2D) {
+ if (func == FUNC_RGB2HSV || func == FUNC_HSV2RGB) {
+ invalid_type = true;
+ }
+ }
+
+ if (invalid_type) {
+ return TTR("Invalid function for that type.");
+ }
+
+ return String();
+}
+
void VisualShaderNodeVectorFunc::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function);
ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function);
@@ -2563,7 +2792,16 @@ void VisualShaderNodeVectorFunc::_bind_methods() {
}
VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() {
- set_input_port_default_value(0, Vector3());
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2());
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3());
+ } break;
+ default:
+ break;
+ }
}
////////////// ColorFunc
@@ -2748,11 +2986,11 @@ int VisualShaderNodeUVFunc::get_input_port_count() const {
VisualShaderNodeUVFunc::PortType VisualShaderNodeUVFunc::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
- [[fallthrough]]; // uv
+ return PORT_TYPE_VECTOR_2D; // uv
case 1:
- return PORT_TYPE_VECTOR; // scale
+ return PORT_TYPE_VECTOR_2D; // scale
case 2:
- return PORT_TYPE_VECTOR; // offset & pivot
+ return PORT_TYPE_VECTOR_2D; // offset & pivot
default:
break;
}
@@ -2781,11 +3019,13 @@ String VisualShaderNodeUVFunc::get_input_port_name(int p_port) const {
return "";
}
-String VisualShaderNodeUVFunc::get_input_port_default_hint(int p_port) const {
- if (p_port == 0) {
- return "UV";
+bool VisualShaderNodeUVFunc::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ if (p_port == 0) {
+ return true;
+ }
}
- return "";
+ return false;
}
int VisualShaderNodeUVFunc::get_output_port_count() const {
@@ -2793,7 +3033,7 @@ int VisualShaderNodeUVFunc::get_output_port_count() const {
}
VisualShaderNodeUVFunc::PortType VisualShaderNodeUVFunc::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeUVFunc::get_output_port_name(int p_port) const {
@@ -2809,7 +3049,11 @@ String VisualShaderNodeUVFunc::generate_code(Shader::Mode p_mode, VisualShader::
String uv;
if (p_input_vars[0].is_empty()) {
- uv = "vec3(UV.xy, 0.0)";
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ uv = "UV";
+ } else {
+ uv = "vec2(0.0)";
+ }
} else {
uv = vformat("%s", p_input_vars[0]);
}
@@ -2835,9 +3079,9 @@ void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_fun
return;
}
if (p_func == FUNC_PANNING) {
- set_input_port_default_value(2, Vector3()); // offset
+ set_input_port_default_value(2, Vector2()); // offset
} else { // FUNC_SCALING
- set_input_port_default_value(2, Vector3(0.5, 0.5, 0.0)); // pivot
+ set_input_port_default_value(2, Vector2(0.5, 0.5)); // pivot
}
func = p_func;
emit_changed();
@@ -2865,8 +3109,8 @@ void VisualShaderNodeUVFunc::_bind_methods() {
}
VisualShaderNodeUVFunc::VisualShaderNodeUVFunc() {
- set_input_port_default_value(1, Vector3(1.0, 1.0, 0.0)); // scale
- set_input_port_default_value(2, Vector3()); // offset
+ set_input_port_default_value(1, Vector2(1.0, 1.0)); // scale
+ set_input_port_default_value(2, Vector2()); // offset
}
////////////// Dot Product
@@ -2918,10 +3162,6 @@ int VisualShaderNodeVectorLen::get_input_port_count() const {
return 1;
}
-VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorLen::get_input_port_name(int p_port) const {
return "";
}
@@ -2938,12 +3178,31 @@ String VisualShaderNodeVectorLen::get_output_port_name(int p_port) const {
return "length";
}
+void VisualShaderNodeVectorLen::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
String VisualShaderNodeVectorLen::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
return " " + p_output_vars[0] + " = length(" + p_input_vars[0] + ");\n";
}
VisualShaderNodeVectorLen::VisualShaderNodeVectorLen() {
- set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
}
////////////// Determinant
@@ -2984,37 +3243,53 @@ VisualShaderNodeDeterminant::VisualShaderNodeDeterminant() {
set_input_port_default_value(0, Transform3D());
}
-////////////// Scalar Derivative Function
+////////////// Derivative Function
-String VisualShaderNodeScalarDerivativeFunc::get_caption() const {
- return "ScalarDerivativeFunc";
+String VisualShaderNodeDerivativeFunc::get_caption() const {
+ return "DerivativeFunc";
}
-int VisualShaderNodeScalarDerivativeFunc::get_input_port_count() const {
+int VisualShaderNodeDerivativeFunc::get_input_port_count() const {
return 1;
}
-VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeScalarDerivativeFunc::get_input_port_type(int p_port) const {
+VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_input_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeScalarDerivativeFunc::get_input_port_name(int p_port) const {
- return "";
+String VisualShaderNodeDerivativeFunc::get_input_port_name(int p_port) const {
+ return "p";
}
-int VisualShaderNodeScalarDerivativeFunc::get_output_port_count() const {
+int VisualShaderNodeDerivativeFunc::get_output_port_count() const {
return 1;
}
-VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeScalarDerivativeFunc::get_output_port_type(int p_port) const {
+VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_output_port_type(int p_port) const {
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
+ }
return PORT_TYPE_SCALAR;
}
-String VisualShaderNodeScalarDerivativeFunc::get_output_port_name(int p_port) const {
- return "";
+String VisualShaderNodeDerivativeFunc::get_output_port_name(int p_port) const {
+ return "result";
}
-String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+String VisualShaderNodeDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
static const char *functions[FUNC_MAX] = {
"fwidth($)",
"dFdx($)",
@@ -3026,84 +3301,33 @@ String VisualShaderNodeScalarDerivativeFunc::generate_code(Shader::Mode p_mode,
return code;
}
-void VisualShaderNodeScalarDerivativeFunc::set_function(Function p_func) {
- ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
- if (func == p_func) {
+void VisualShaderNodeDerivativeFunc::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX((int)p_op_type, int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
return;
}
- func = p_func;
+ switch (p_op_type) {
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
emit_changed();
}
-VisualShaderNodeScalarDerivativeFunc::Function VisualShaderNodeScalarDerivativeFunc::get_function() const {
- return func;
-}
-
-Vector<StringName> VisualShaderNodeScalarDerivativeFunc::get_editable_properties() const {
- Vector<StringName> props;
- props.push_back("function");
- return props;
-}
-
-void VisualShaderNodeScalarDerivativeFunc::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarDerivativeFunc::set_function);
- ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarDerivativeFunc::get_function);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function");
-
- BIND_ENUM_CONSTANT(FUNC_SUM);
- BIND_ENUM_CONSTANT(FUNC_X);
- BIND_ENUM_CONSTANT(FUNC_Y);
- BIND_ENUM_CONSTANT(FUNC_MAX);
-}
-
-VisualShaderNodeScalarDerivativeFunc::VisualShaderNodeScalarDerivativeFunc() {
- set_input_port_default_value(0, 0.0);
-}
-
-////////////// Vector Derivative Function
-
-String VisualShaderNodeVectorDerivativeFunc::get_caption() const {
- return "VectorDerivativeFunc";
-}
-
-int VisualShaderNodeVectorDerivativeFunc::get_input_port_count() const {
- return 1;
-}
-
-VisualShaderNodeVectorDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
-String VisualShaderNodeVectorDerivativeFunc::get_input_port_name(int p_port) const {
- return "";
-}
-
-int VisualShaderNodeVectorDerivativeFunc::get_output_port_count() const {
- return 1;
-}
-
-VisualShaderNodeVectorDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
-String VisualShaderNodeVectorDerivativeFunc::get_output_port_name(int p_port) const {
- return "";
-}
-
-String VisualShaderNodeVectorDerivativeFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- static const char *functions[FUNC_MAX] = {
- "fwidth($)",
- "dFdx($)",
- "dFdy($)"
- };
-
- String code;
- code += " " + p_output_vars[0] + " = " + String(functions[func]).replace("$", p_input_vars[0]) + ";\n";
- return code;
+VisualShaderNodeDerivativeFunc::OpType VisualShaderNodeDerivativeFunc::get_op_type() const {
+ return op_type;
}
-void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) {
+void VisualShaderNodeDerivativeFunc::set_function(Function p_func) {
ERR_FAIL_INDEX(int(p_func), int(FUNC_MAX));
if (func == p_func) {
return;
@@ -3112,30 +3336,40 @@ void VisualShaderNodeVectorDerivativeFunc::set_function(Function p_func) {
emit_changed();
}
-VisualShaderNodeVectorDerivativeFunc::Function VisualShaderNodeVectorDerivativeFunc::get_function() const {
+VisualShaderNodeDerivativeFunc::Function VisualShaderNodeDerivativeFunc::get_function() const {
return func;
}
-Vector<StringName> VisualShaderNodeVectorDerivativeFunc::get_editable_properties() const {
+Vector<StringName> VisualShaderNodeDerivativeFunc::get_editable_properties() const {
Vector<StringName> props;
+ props.push_back("op_type");
props.push_back("function");
return props;
}
-void VisualShaderNodeVectorDerivativeFunc::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorDerivativeFunc::set_function);
- ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorDerivativeFunc::get_function);
+void VisualShaderNodeDerivativeFunc::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeDerivativeFunc::set_op_type);
+ ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeDerivativeFunc::get_op_type);
+
+ ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeDerivativeFunc::set_function);
+ ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeDerivativeFunc::get_function);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3"), "set_op_type", "get_op_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function");
+ BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
+
BIND_ENUM_CONSTANT(FUNC_SUM);
BIND_ENUM_CONSTANT(FUNC_X);
BIND_ENUM_CONSTANT(FUNC_Y);
BIND_ENUM_CONSTANT(FUNC_MAX);
}
-VisualShaderNodeVectorDerivativeFunc::VisualShaderNodeVectorDerivativeFunc() {
- set_input_port_default_value(0, Vector3());
+VisualShaderNodeDerivativeFunc::VisualShaderNodeDerivativeFunc() {
+ set_input_port_default_value(0, 0.0);
}
////////////// Clamp
@@ -3152,7 +3386,9 @@ VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_input_port_type(int p
switch (op_type) {
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
- case OP_TYPE_VECTOR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
default:
break;
@@ -3179,7 +3415,9 @@ VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_output_port_type(int
switch (op_type) {
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
- case OP_TYPE_VECTOR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
default:
break;
@@ -3202,19 +3440,24 @@ void VisualShaderNodeClamp::set_op_type(OpType p_op_type) {
}
switch (p_op_type) {
case OP_TYPE_FLOAT:
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 0.0);
- set_input_port_default_value(2, 0.0);
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2));
break;
case OP_TYPE_INT:
- set_input_port_default_value(0, 0);
- set_input_port_default_value(1, 0);
- set_input_port_default_value(2, 0);
+ set_input_port_default_value(0, 0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0, get_input_port_default_value(2));
break;
- case OP_TYPE_VECTOR:
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+ case OP_TYPE_VECTOR_2D:
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2));
+ break;
+ case OP_TYPE_VECTOR_3D:
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2));
break;
default:
break;
@@ -3237,11 +3480,12 @@ void VisualShaderNodeClamp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeClamp::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeClamp::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_FLOAT);
BIND_ENUM_CONSTANT(OP_TYPE_INT);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
@@ -3261,10 +3505,6 @@ int VisualShaderNodeFaceForward::get_input_port_count() const {
return 3;
}
-VisualShaderNodeFaceForward::PortType VisualShaderNodeFaceForward::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeFaceForward::get_input_port_name(int p_port) const {
switch (p_port) {
case 0:
@@ -3282,14 +3522,33 @@ int VisualShaderNodeFaceForward::get_output_port_count() const {
return 1;
}
-VisualShaderNodeFaceForward::PortType VisualShaderNodeFaceForward::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeFaceForward::get_output_port_name(int p_port) const {
return "";
}
+void VisualShaderNodeFaceForward::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
String VisualShaderNodeFaceForward::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
return " " + p_output_vars[0] + " = faceforward(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
}
@@ -3358,9 +3617,16 @@ int VisualShaderNodeStep::get_input_port_count() const {
VisualShaderNodeStep::PortType VisualShaderNodeStep::get_input_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ if (p_port == 1) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ break;
+ case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
+ case OP_TYPE_VECTOR_3D_SCALAR:
if (p_port == 1) {
return PORT_TYPE_VECTOR;
}
@@ -3372,12 +3638,13 @@ VisualShaderNodeStep::PortType VisualShaderNodeStep::get_input_port_type(int p_p
}
String VisualShaderNodeStep::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "edge";
- } else if (p_port == 1) {
- return "x";
+ switch (p_port) {
+ case 0:
+ return "edge";
+ case 1:
+ return "x";
}
- return "";
+ return String();
}
int VisualShaderNodeStep::get_output_port_count() const {
@@ -3386,9 +3653,13 @@ int VisualShaderNodeStep::get_output_port_count() const {
VisualShaderNodeStep::PortType VisualShaderNodeStep::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
+ case OP_TYPE_VECTOR_3D_SCALAR:
return PORT_TYPE_VECTOR;
default:
break;
@@ -3406,30 +3677,26 @@ void VisualShaderNodeStep::set_op_type(OpType p_op_type) {
return;
}
switch (p_op_type) {
- case OP_TYPE_SCALAR:
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(0, 0.0); // edge
- }
- if (op_type == OP_TYPE_VECTOR || op_type == OP_TYPE_VECTOR_SCALAR) {
- set_input_port_default_value(1, 0.0); // x
- }
- break;
- case OP_TYPE_VECTOR:
- if (op_type == OP_TYPE_SCALAR || op_type == OP_TYPE_VECTOR_SCALAR) {
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // edge
- }
- if (op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // x
- }
- break;
- case OP_TYPE_VECTOR_SCALAR:
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(0, 0.0); // edge
- }
- if (op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // x
- }
- break;
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_2D_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_3D_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ } break;
default:
break;
}
@@ -3455,11 +3722,13 @@ void VisualShaderNodeStep::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeStep::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeStep::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
@@ -3480,9 +3749,16 @@ int VisualShaderNodeSmoothStep::get_input_port_count() const {
VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_input_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ if (p_port == 2) {
+ return PORT_TYPE_VECTOR_2D; // x
+ }
+ break;
+ case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
+ case OP_TYPE_VECTOR_3D_SCALAR:
if (p_port == 2) {
return PORT_TYPE_VECTOR; // x
}
@@ -3494,14 +3770,15 @@ VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_input_port_
}
String VisualShaderNodeSmoothStep::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "edge0";
- } else if (p_port == 1) {
- return "edge1";
- } else if (p_port == 2) {
- return "x";
+ switch (p_port) {
+ case 0:
+ return "edge0";
+ case 1:
+ return "edge1";
+ case 2:
+ return "x";
}
- return "";
+ return String();
}
int VisualShaderNodeSmoothStep::get_output_port_count() const {
@@ -3510,9 +3787,13 @@ int VisualShaderNodeSmoothStep::get_output_port_count() const {
VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
+ case OP_TYPE_VECTOR_3D_SCALAR:
return PORT_TYPE_VECTOR;
default:
break;
@@ -3531,31 +3812,29 @@ void VisualShaderNodeSmoothStep::set_op_type(OpType p_op_type) {
}
switch (p_op_type) {
case OP_TYPE_SCALAR:
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(0, 0.0); // edge0
- set_input_port_default_value(1, 0.0); // edge1
- }
- if (op_type == OP_TYPE_VECTOR || op_type == OP_TYPE_VECTOR_SCALAR) {
- set_input_port_default_value(2, 0.0); // x
- }
- break;
- case OP_TYPE_VECTOR:
- if (op_type == OP_TYPE_SCALAR || op_type == OP_TYPE_VECTOR_SCALAR) {
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // edge0
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // edge1
- }
- if (op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); // x
- }
- break;
- case OP_TYPE_VECTOR_SCALAR:
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(0, 0.0); // edge0
- set_input_port_default_value(1, 0.0); // edge1
- }
- if (op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); // x
- }
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // x
+ break;
+ case OP_TYPE_VECTOR_2D:
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2)); // x
+ break;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2)); // x
+ break;
+ case OP_TYPE_VECTOR_3D:
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); // x
+ break;
+ case OP_TYPE_VECTOR_3D_SCALAR:
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // edge0
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); // x
break;
default:
break;
@@ -3582,18 +3861,20 @@ void VisualShaderNodeSmoothStep::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeSmoothStep::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSmoothStep::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
VisualShaderNodeSmoothStep::VisualShaderNodeSmoothStep() {
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 0.0);
- set_input_port_default_value(2, 0.0);
+ set_input_port_default_value(0, 0.0); // edge0
+ set_input_port_default_value(1, 1.0); // edge1
+ set_input_port_default_value(2, 0.5); // x
}
////////////// Distance
@@ -3606,17 +3887,14 @@ int VisualShaderNodeVectorDistance::get_input_port_count() const {
return 2;
}
-VisualShaderNodeVectorDistance::PortType VisualShaderNodeVectorDistance::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorDistance::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "p0";
- } else if (p_port == 1) {
- return "p1";
+ switch (p_port) {
+ case 0:
+ return "a";
+ case 1:
+ return "b";
}
- return "";
+ return String();
}
int VisualShaderNodeVectorDistance::get_output_port_count() const {
@@ -3631,13 +3909,34 @@ String VisualShaderNodeVectorDistance::get_output_port_name(int p_port) const {
return "";
}
+void VisualShaderNodeVectorDistance::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); // b
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // b
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
String VisualShaderNodeVectorDistance::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
return " " + p_output_vars[0] + " = distance(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
VisualShaderNodeVectorDistance::VisualShaderNodeVectorDistance() {
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // a
+ set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); // b
}
////////////// Refract Vector
@@ -3659,14 +3958,15 @@ VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_input
}
String VisualShaderNodeVectorRefract::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "I";
- } else if (p_port == 1) {
- return "N";
- } else if (p_port == 2) {
- return "eta";
+ switch (p_port) {
+ case 0:
+ return "I";
+ case 1:
+ return "N";
+ case 2:
+ return "eta";
}
- return "";
+ return String();
}
int VisualShaderNodeVectorRefract::get_output_port_count() const {
@@ -3703,9 +4003,16 @@ int VisualShaderNodeMix::get_input_port_count() const {
VisualShaderNodeMix::PortType VisualShaderNodeMix::get_input_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ if (p_port == 2) {
+ break;
+ }
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
+ case OP_TYPE_VECTOR_3D_SCALAR:
if (p_port == 2) {
break;
}
@@ -3732,9 +4039,13 @@ int VisualShaderNodeMix::get_output_port_count() const {
VisualShaderNodeMix::PortType VisualShaderNodeMix::get_output_port_type(int p_port) const {
switch (op_type) {
- case OP_TYPE_VECTOR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
- case OP_TYPE_VECTOR_SCALAR:
+ case OP_TYPE_VECTOR_3D_SCALAR:
return PORT_TYPE_VECTOR;
default:
break;
@@ -3752,27 +4063,31 @@ void VisualShaderNodeMix::set_op_type(OpType p_op_type) {
return;
}
switch (p_op_type) {
- case OP_TYPE_SCALAR:
- set_input_port_default_value(0, 0.0); // a
- set_input_port_default_value(1, 1.0); // b
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(2, 0.5); // weight
- }
- break;
- case OP_TYPE_VECTOR:
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // a
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0)); // b
- if (op_type == OP_TYPE_SCALAR || op_type == OP_TYPE_VECTOR_SCALAR) {
- set_input_port_default_value(2, Vector3(0.5, 0.5, 0.5)); // weight
- }
- break;
- case OP_TYPE_VECTOR_SCALAR:
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); // a
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0)); // b
- if (op_type == OP_TYPE_VECTOR) {
- set_input_port_default_value(2, 0.5); // weight
- }
- break;
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2)); // weight
+ } break;
+ case OP_TYPE_VECTOR_2D_SCALAR: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); // weight
+ } break;
+ case OP_TYPE_VECTOR_3D_SCALAR: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); // a
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // b
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight
+ } break;
default:
break;
}
@@ -3798,11 +4113,13 @@ void VisualShaderNodeMix::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeMix::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMix::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector,VectorScalar"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
@@ -3819,7 +4136,15 @@ String VisualShaderNodeVectorCompose::get_caption() const {
}
int VisualShaderNodeVectorCompose::get_input_port_count() const {
- return 3;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return 2;
+ case OP_TYPE_VECTOR_3D:
+ return 3;
+ default:
+ break;
+ }
+ return 0;
}
VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_input_port_type(int p_port) const {
@@ -3827,29 +4152,80 @@ VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_input
}
String VisualShaderNodeVectorCompose::get_input_port_name(int p_port) const {
- if (p_port == 0) {
- return "x";
- } else if (p_port == 1) {
- return "y";
- } else {
- return "z";
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ switch (p_port) {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ }
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ switch (p_port) {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ case 2:
+ return "z";
+ }
+ } break;
+ default:
+ break;
}
+ return String();
}
int VisualShaderNodeVectorCompose::get_output_port_count() const {
return 1;
}
-VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorCompose::get_output_port_name(int p_port) const {
return "vec";
}
+void VisualShaderNodeVectorCompose::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ float p1 = get_input_port_default_value(0);
+ float p2 = get_input_port_default_value(1);
+
+ set_input_port_default_value(0, p1);
+ set_input_port_default_value(1, p2);
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ float p1 = get_input_port_default_value(0);
+ float p2 = get_input_port_default_value(1);
+
+ set_input_port_default_value(0, p1);
+ set_input_port_default_value(1, p2);
+ set_input_port_default_value(2, 0.0);
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
String VisualShaderNodeVectorCompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- return " " + p_output_vars[0] + " = vec3(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ String code;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ code += " " + p_output_vars[0] + " = vec2(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ code += " " + p_output_vars[0] + " = vec3(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n";
+ } break;
+ default:
+ break;
+ }
+ return code;
}
VisualShaderNodeVectorCompose::VisualShaderNodeVectorCompose() {
@@ -3916,16 +4292,20 @@ int VisualShaderNodeVectorDecompose::get_input_port_count() const {
return 1;
}
-VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
-}
-
String VisualShaderNodeVectorDecompose::get_input_port_name(int p_port) const {
return "vec";
}
int VisualShaderNodeVectorDecompose::get_output_port_count() const {
- return 3;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return 2;
+ case OP_TYPE_VECTOR_3D:
+ return 3;
+ default:
+ break;
+ }
+ return 0;
}
VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_output_port_type(int p_port) const {
@@ -3933,25 +4313,70 @@ VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_o
}
String VisualShaderNodeVectorDecompose::get_output_port_name(int p_port) const {
- if (p_port == 0) {
- return "x";
- } else if (p_port == 1) {
- return "y";
- } else {
- return "z";
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ switch (p_port) {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ }
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ switch (p_port) {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ case 2:
+ return "z";
+ }
+ } break;
+ default:
+ break;
}
+ return String();
+}
+
+void VisualShaderNodeVectorDecompose::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
}
String VisualShaderNodeVectorDecompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
- code += " " + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
- code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
- code += " " + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n";
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D: {
+ code += " " + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
+ code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ code += " " + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n";
+ code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n";
+ code += " " + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n";
+ } break;
+ default:
+ break;
+ }
return code;
}
VisualShaderNodeVectorDecompose::VisualShaderNodeVectorDecompose() {
- set_input_port_default_value(0, Vector3());
+ set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
}
////////////// Transform Decompose
@@ -4601,10 +5026,110 @@ Vector<StringName> VisualShaderNodeColorUniform::get_editable_properties() const
VisualShaderNodeColorUniform::VisualShaderNodeColorUniform() {
}
-////////////// Vector Uniform
+////////////// Vector2 Uniform
+
+String VisualShaderNodeVec2Uniform::get_caption() const {
+ return "Vector2Uniform";
+}
+
+int VisualShaderNodeVec2Uniform::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeVec2Uniform::PortType VisualShaderNodeVec2Uniform::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_2D;
+}
+
+String VisualShaderNodeVec2Uniform::get_input_port_name(int p_port) const {
+ return String();
+}
+
+int VisualShaderNodeVec2Uniform::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeVec2Uniform::PortType VisualShaderNodeVec2Uniform::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_2D;
+}
+
+String VisualShaderNodeVec2Uniform::get_output_port_name(int p_port) const {
+ return String();
+}
+
+void VisualShaderNodeVec2Uniform::set_default_value_enabled(bool p_enabled) {
+ default_value_enabled = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeVec2Uniform::is_default_value_enabled() const {
+ return default_value_enabled;
+}
+
+void VisualShaderNodeVec2Uniform::set_default_value(const Vector2 &p_value) {
+ default_value = p_value;
+ emit_changed();
+}
+
+Vector2 VisualShaderNodeVec2Uniform::get_default_value() const {
+ return default_value;
+}
+
+String VisualShaderNodeVec2Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ String code = _get_qual_str() + "uniform vec2 " + get_uniform_name();
+ if (default_value_enabled) {
+ code += vformat(" = vec2(%.6f, %.6f)", default_value.x, default_value.y);
+ }
+ code += ";\n";
+ return code;
+}
+
+String VisualShaderNodeVec2Uniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n";
+}
+
+void VisualShaderNodeVec2Uniform::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec2Uniform::set_default_value_enabled);
+ ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec2Uniform::is_default_value_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec2Uniform::set_default_value);
+ ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec2Uniform::get_default_value);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "default_value"), "set_default_value", "get_default_value");
+}
+
+bool VisualShaderNodeVec2Uniform::is_show_prop_names() const {
+ return true;
+}
+
+bool VisualShaderNodeVec2Uniform::is_use_prop_slots() const {
+ return true;
+}
+
+bool VisualShaderNodeVec2Uniform::is_qualifier_supported(Qualifier p_qual) const {
+ return true; // all qualifiers are supported
+}
+
+bool VisualShaderNodeVec2Uniform::is_convertible_to_constant() const {
+ return true; // conversion is allowed
+}
+
+Vector<StringName> VisualShaderNodeVec2Uniform::get_editable_properties() const {
+ Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties();
+ props.push_back("default_value_enabled");
+ if (default_value_enabled) {
+ props.push_back("default_value");
+ }
+ return props;
+}
+
+VisualShaderNodeVec2Uniform::VisualShaderNodeVec2Uniform() {
+}
+
+////////////// Vector3 Uniform
String VisualShaderNodeVec3Uniform::get_caption() const {
- return "VectorUniform";
+ return "Vector3Uniform";
}
int VisualShaderNodeVec3Uniform::get_input_port_count() const {
@@ -4819,7 +5344,7 @@ int VisualShaderNodeTextureUniform::get_input_port_count() const {
}
VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return p_port == 0 ? PORT_TYPE_VECTOR_2D : PORT_TYPE_SCALAR;
}
String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const {
@@ -4966,8 +5491,8 @@ bool VisualShaderNodeTextureUniform::is_code_generated() const {
String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String default_uv;
- if (p_mode != Shader::MODE_PARTICLES && p_mode != Shader::MODE_SKY) {
- default_uv = "UV.xy";
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ default_uv = "UV";
} else {
default_uv = "vec2(0.0)";
}
@@ -4982,9 +5507,9 @@ String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, Visual
}
} else if (p_input_vars[1].is_empty()) {
//no lod
- code += " vec4 n_tex_read = texture(" + id + ", " + p_input_vars[0] + ".xy);\n";
+ code += " vec4 n_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n";
} else {
- code += " vec4 n_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n";
+ code += " vec4 n_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n";
}
code += " " + p_output_vars[0] + " = n_tex_read.rgb;\n";
@@ -5112,11 +5637,13 @@ void VisualShaderNodeTextureUniform::_bind_methods() {
BIND_ENUM_CONSTANT(REPEAT_MAX);
}
-String VisualShaderNodeTextureUniform::get_input_port_default_hint(int p_port) const {
- if (p_port == 0) {
- return "default";
+bool VisualShaderNodeTextureUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ if (p_port == 0) {
+ return true;
+ }
}
- return "";
+ return false;
}
bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) const {
@@ -5224,13 +5751,13 @@ String VisualShaderNodeTextureUniformTriplanar::generate_code(Shader::Mode p_mod
return code;
}
-String VisualShaderNodeTextureUniformTriplanar::get_input_port_default_hint(int p_port) const {
+bool VisualShaderNodeTextureUniformTriplanar::is_input_port_default(int p_port, Shader::Mode p_mode) const {
if (p_port == 0) {
- return "default";
+ return true;
} else if (p_port == 1) {
- return "default";
+ return true;
}
- return "";
+ return false;
}
VisualShaderNodeTextureUniformTriplanar::VisualShaderNodeTextureUniformTriplanar() {
@@ -5266,8 +5793,8 @@ String VisualShaderNodeTexture2DArrayUniform::get_input_port_name(int p_port) co
return "";
}
-String VisualShaderNodeTexture2DArrayUniform::get_input_port_default_hint(int p_port) const {
- return "";
+bool VisualShaderNodeTexture2DArrayUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ return false;
}
String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
@@ -5339,8 +5866,8 @@ String VisualShaderNodeTexture3DUniform::get_input_port_name(int p_port) const {
return "";
}
-String VisualShaderNodeTexture3DUniform::get_input_port_default_hint(int p_port) const {
- return "";
+bool VisualShaderNodeTexture3DUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ return false;
}
String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
@@ -5412,8 +5939,8 @@ String VisualShaderNodeCubemapUniform::get_input_port_name(int p_port) const {
return "";
}
-String VisualShaderNodeCubemapUniform::get_input_port_default_hint(int p_port) const {
- return "";
+bool VisualShaderNodeCubemapUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ return false;
}
String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
@@ -5548,7 +6075,9 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int
switch (op_type) {
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
- case OP_TYPE_VECTOR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
case OP_TYPE_BOOLEAN:
return PORT_TYPE_BOOLEAN;
@@ -5582,7 +6111,9 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(in
switch (op_type) {
case OP_TYPE_INT:
return PORT_TYPE_SCALAR_INT;
- case OP_TYPE_VECTOR:
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
case OP_TYPE_BOOLEAN:
return PORT_TYPE_BOOLEAN;
@@ -5605,16 +6136,20 @@ void VisualShaderNodeSwitch::set_op_type(OpType p_op_type) {
}
switch (p_op_type) {
case OP_TYPE_FLOAT:
- set_input_port_default_value(1, 1.0);
- set_input_port_default_value(2, 0.0);
+ set_input_port_default_value(1, 1.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2));
break;
case OP_TYPE_INT:
- set_input_port_default_value(1, 1);
- set_input_port_default_value(2, 0);
+ set_input_port_default_value(1, 1, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0, get_input_port_default_value(2));
break;
- case OP_TYPE_VECTOR:
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+ case OP_TYPE_VECTOR_2D:
+ set_input_port_default_value(1, Vector2(1.0, 1.0), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector2(0.0, 0.0), get_input_port_default_value(2));
+ break;
+ case OP_TYPE_VECTOR_3D:
+ set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0), get_input_port_default_value(2));
break;
case OP_TYPE_BOOLEAN:
set_input_port_default_value(1, true);
@@ -5645,11 +6180,12 @@ void VisualShaderNodeSwitch::_bind_methods() { // static
ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeSwitch::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSwitch::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector,Boolean,Transform"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Boolean,Transform"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_FLOAT);
BIND_ENUM_CONSTANT(OP_TYPE_INT);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
BIND_ENUM_CONSTANT(OP_TYPE_BOOLEAN);
BIND_ENUM_CONSTANT(OP_TYPE_TRANSFORM);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
@@ -5738,12 +6274,20 @@ String VisualShaderNodeFresnel::generate_code(Shader::Mode p_mode, VisualShader:
String normal;
String view;
if (p_input_vars[0].is_empty()) {
- normal = "NORMAL";
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ normal = "NORMAL";
+ } else {
+ normal = "vec3(0.0)";
+ }
} else {
normal = p_input_vars[0];
}
if (p_input_vars[1].is_empty()) {
- view = "VIEW";
+ if (p_mode == Shader::MODE_SPATIAL) {
+ view = "VIEW";
+ } else {
+ view = "vec3(0.0)";
+ }
} else {
view = p_input_vars[1];
}
@@ -5759,13 +6303,17 @@ String VisualShaderNodeFresnel::generate_code(Shader::Mode p_mode, VisualShader:
}
}
-String VisualShaderNodeFresnel::get_input_port_default_hint(int p_port) const {
+bool VisualShaderNodeFresnel::is_input_port_default(int p_port, Shader::Mode p_mode) const {
if (p_port == 0) {
- return "default";
+ if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) {
+ return true;
+ }
} else if (p_port == 1) {
- return "default";
+ if (p_mode == Shader::MODE_SPATIAL) {
+ return true;
+ }
}
- return "";
+ return false;
}
VisualShaderNodeFresnel::VisualShaderNodeFresnel() {
@@ -5867,7 +6415,9 @@ VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(i
return PORT_TYPE_SCALAR;
case CTYPE_SCALAR_INT:
return PORT_TYPE_SCALAR_INT;
- case CTYPE_VECTOR:
+ case CTYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case CTYPE_VECTOR_3D:
return PORT_TYPE_VECTOR;
case CTYPE_BOOLEAN:
return PORT_TYPE_BOOLEAN;
@@ -5939,7 +6489,7 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
String code;
switch (comparison_type) {
- case CTYPE_SCALAR:
+ case CTYPE_SCALAR: {
if (func == FUNC_EQUAL) {
code += " " + p_output_vars[0] + " = (abs(" + p_input_vars[0] + " - " + p_input_vars[1] + ") < " + p_input_vars[2] + ");";
} else if (func == FUNC_NOT_EQUAL) {
@@ -5947,33 +6497,34 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader:
} else {
code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
}
- break;
-
- case CTYPE_SCALAR_INT:
+ } break;
+ case CTYPE_SCALAR_INT: {
code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
- break;
-
- case CTYPE_VECTOR:
+ } break;
+ case CTYPE_VECTOR_2D: {
+ code += " {\n";
+ code += " bvec2 _bv = " + String(functions[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
+ code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n";
+ code += " }\n";
+ } break;
+ case CTYPE_VECTOR_3D: {
code += " {\n";
code += " bvec3 _bv = " + String(functions[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n";
code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n";
code += " }\n";
- break;
-
- case CTYPE_BOOLEAN:
+ } break;
+ case CTYPE_BOOLEAN: {
if (func > FUNC_NOT_EQUAL) {
return " " + p_output_vars[0] + " = false;\n";
}
code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
- break;
-
- case CTYPE_TRANSFORM:
+ } break;
+ case CTYPE_TRANSFORM: {
if (func > FUNC_NOT_EQUAL) {
return " " + p_output_vars[0] + " = false;\n";
}
code += " " + p_output_vars[0] + " = " + (p_input_vars[0] + " $ " + p_input_vars[1]).replace("$", operators[func]) + ";\n";
- break;
-
+ } break;
default:
break;
}
@@ -5987,18 +6538,23 @@ void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_comparison_ty
}
switch (p_comparison_type) {
case CTYPE_SCALAR:
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
simple_decl = true;
break;
case CTYPE_SCALAR_INT:
- set_input_port_default_value(0, 0);
- set_input_port_default_value(1, 0);
+ set_input_port_default_value(0, 0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0, get_input_port_default_value(1));
simple_decl = true;
break;
- case CTYPE_VECTOR:
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
+ case CTYPE_VECTOR_2D:
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ simple_decl = false;
+ break;
+ case CTYPE_VECTOR_3D:
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
simple_decl = false;
break;
case CTYPE_BOOLEAN:
@@ -6052,7 +6608,7 @@ Vector<StringName> VisualShaderNodeCompare::get_editable_properties() const {
Vector<StringName> props;
props.push_back("type");
props.push_back("function");
- if (comparison_type == CTYPE_VECTOR) {
+ if (comparison_type == CTYPE_VECTOR_2D || comparison_type == CTYPE_VECTOR_3D) {
props.push_back("condition");
}
return props;
@@ -6068,13 +6624,14 @@ void VisualShaderNodeCompare::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_condition", "condition"), &VisualShaderNodeCompare::set_condition);
ClassDB::bind_method(D_METHOD("get_condition"), &VisualShaderNodeCompare::get_condition);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Float,Int,Vector,Boolean,Transform"), "set_comparison_type", "get_comparison_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Boolean,Transform"), "set_comparison_type", "get_comparison_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "a == b,a != b,a > b,a >= b,a < b,a <= b"), "set_function", "get_function");
ADD_PROPERTY(PropertyInfo(Variant::INT, "condition", PROPERTY_HINT_ENUM, "All,Any"), "set_condition", "get_condition");
BIND_ENUM_CONSTANT(CTYPE_SCALAR);
BIND_ENUM_CONSTANT(CTYPE_SCALAR_INT);
- BIND_ENUM_CONSTANT(CTYPE_VECTOR);
+ BIND_ENUM_CONSTANT(CTYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(CTYPE_VECTOR_3D);
BIND_ENUM_CONSTANT(CTYPE_BOOLEAN);
BIND_ENUM_CONSTANT(CTYPE_TRANSFORM);
BIND_ENUM_CONSTANT(CTYPE_MAX);
@@ -6109,8 +6666,13 @@ int VisualShaderNodeMultiplyAdd::get_input_port_count() const {
}
VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_input_port_type(int p_port) const {
- if (op_type == OP_TYPE_VECTOR) {
- return PORT_TYPE_VECTOR;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
}
return PORT_TYPE_SCALAR;
}
@@ -6131,11 +6693,15 @@ int VisualShaderNodeMultiplyAdd::get_output_port_count() const {
}
VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_output_port_type(int p_port) const {
- if (op_type == OP_TYPE_SCALAR) {
- return PORT_TYPE_SCALAR;
- } else {
- return PORT_TYPE_VECTOR;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR;
+ default:
+ break;
}
+ return PORT_TYPE_SCALAR;
}
String VisualShaderNodeMultiplyAdd::get_output_port_name(int p_port) const {
@@ -6152,16 +6718,21 @@ void VisualShaderNodeMultiplyAdd::set_op_type(OpType p_op_type) {
return;
}
switch (p_op_type) {
- case OP_TYPE_SCALAR:
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 0.0);
- set_input_port_default_value(2, 0.0);
- break;
- case OP_TYPE_VECTOR:
- set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0));
- set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
- break;
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 0.0, get_input_port_default_value(2));
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector3(), get_input_port_default_value(2));
+ } break;
default:
break;
}
@@ -6183,10 +6754,11 @@ void VisualShaderNodeMultiplyAdd::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeMultiplyAdd::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMultiplyAdd::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index f1dda634f1..5c7a674d34 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -34,6 +34,49 @@
#include "scene/resources/visual_shader.h"
///////////////////////////////////////
+/// Vector Base Node
+///////////////////////////////////////
+
+class VisualShaderNodeVectorBase : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeVectorBase, VisualShaderNode);
+
+public:
+ enum OpType {
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_MAX,
+ };
+
+protected:
+ OpType op_type = OP_TYPE_VECTOR_3D;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override = 0;
+
+ virtual int get_input_port_count() const override = 0;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override = 0;
+
+ virtual int get_output_port_count() const override = 0;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override = 0;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override = 0;
+
+ virtual void set_op_type(OpType p_op_type);
+ OpType get_op_type() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeVectorBase();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeVectorBase::OpType)
+
+///////////////////////////////////////
/// CONSTANTS
///////////////////////////////////////
@@ -177,6 +220,36 @@ public:
///////////////////////////////////////
+class VisualShaderNodeVec2Constant : public VisualShaderNodeConstant {
+ GDCLASS(VisualShaderNodeVec2Constant, VisualShaderNodeConstant);
+ Vector2 constant;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ void set_constant(const Vector2 &p_constant);
+ Vector2 get_constant() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeVec2Constant();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeVec3Constant : public VisualShaderNodeConstant {
GDCLASS(VisualShaderNodeVec3Constant, VisualShaderNodeConstant);
Vector3 constant;
@@ -280,7 +353,7 @@ public:
virtual String get_output_port_name(int p_port) const override;
virtual bool is_output_port_expandable(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
@@ -392,7 +465,7 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
@@ -488,7 +561,7 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
@@ -615,8 +688,8 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeIntOp::Operator)
-class VisualShaderNodeVectorOp : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorOp, VisualShaderNode);
+class VisualShaderNodeVectorOp : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorOp, VisualShaderNodeVectorBase);
public:
enum Operator {
@@ -644,19 +717,20 @@ public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+ virtual void set_op_type(OpType p_op_type) override;
+
void set_operator(Operator p_op);
Operator get_operator() const;
virtual Vector<StringName> get_editable_properties() const override;
+ String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override;
VisualShaderNodeVectorOp();
};
@@ -923,8 +997,10 @@ VARIANT_ENUM_CAST(VisualShaderNodeIntFunc::Function)
/// VECTOR FUNC
///////////////////////////////////////
-class VisualShaderNodeVectorFunc : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorFunc, VisualShaderNode);
+class VisualShaderNodeVectorFunc : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorFunc, VisualShaderNodeVectorBase);
+
+ void _update_default_input_values();
public:
enum Function {
@@ -975,19 +1051,20 @@ public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+ virtual void set_op_type(OpType p_op_type) override;
+
void set_function(Function p_func);
Function get_function() const;
virtual Vector<StringName> get_editable_properties() const override;
+ String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override;
VisualShaderNodeVectorFunc();
};
@@ -1103,7 +1180,7 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
@@ -1150,20 +1227,20 @@ public:
/// LENGTH
///////////////////////////////////////
-class VisualShaderNodeVectorLen : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorLen, VisualShaderNode);
+class VisualShaderNodeVectorLen : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorLen, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_op_type(OpType p_op_type) override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeVectorLen();
@@ -1203,7 +1280,8 @@ public:
enum OpType {
OP_TYPE_FLOAT,
OP_TYPE_INT,
- OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
OP_TYPE_MAX,
};
@@ -1235,54 +1313,20 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeClamp::OpType)
///////////////////////////////////////
-/// DERIVATIVE FUNCTIONS
+/// DERIVATIVE FUNCTION
///////////////////////////////////////
-class VisualShaderNodeScalarDerivativeFunc : public VisualShaderNode {
- GDCLASS(VisualShaderNodeScalarDerivativeFunc, VisualShaderNode);
+class VisualShaderNodeDerivativeFunc : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeDerivativeFunc, VisualShaderNode);
public:
- enum Function {
- FUNC_SUM,
- FUNC_X,
- FUNC_Y,
- FUNC_MAX,
+ enum OpType {
+ OP_TYPE_SCALAR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_MAX,
};
-protected:
- Function func = FUNC_SUM;
-
- static void _bind_methods();
-
-public:
- virtual String get_caption() const override;
-
- virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
- virtual String get_input_port_name(int p_port) const override;
-
- virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
- virtual String get_output_port_name(int p_port) const override;
-
- virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
-
- void set_function(Function p_func);
- Function get_function() const;
-
- virtual Vector<StringName> get_editable_properties() const override;
-
- VisualShaderNodeScalarDerivativeFunc();
-};
-
-VARIANT_ENUM_CAST(VisualShaderNodeScalarDerivativeFunc::Function)
-
-///////////////////////////////////////
-
-class VisualShaderNodeVectorDerivativeFunc : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorDerivativeFunc, VisualShaderNode);
-
-public:
enum Function {
FUNC_SUM,
FUNC_X,
@@ -1291,8 +1335,10 @@ public:
};
protected:
+ OpType op_type = OP_TYPE_SCALAR;
Function func = FUNC_SUM;
+protected:
static void _bind_methods();
public:
@@ -1308,34 +1354,37 @@ public:
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+ void set_op_type(OpType p_op_type);
+ OpType get_op_type() const;
+
void set_function(Function p_func);
Function get_function() const;
virtual Vector<StringName> get_editable_properties() const override;
- VisualShaderNodeVectorDerivativeFunc();
+ VisualShaderNodeDerivativeFunc();
};
-VARIANT_ENUM_CAST(VisualShaderNodeVectorDerivativeFunc::Function)
+VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::OpType)
+VARIANT_ENUM_CAST(VisualShaderNodeDerivativeFunc::Function)
///////////////////////////////////////
/// FACEFORWARD
///////////////////////////////////////
-class VisualShaderNodeFaceForward : public VisualShaderNode {
- GDCLASS(VisualShaderNodeFaceForward, VisualShaderNode);
+class VisualShaderNodeFaceForward : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeFaceForward, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_op_type(OpType p_op_type) override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeFaceForward();
@@ -1374,8 +1423,10 @@ class VisualShaderNodeStep : public VisualShaderNode {
public:
enum OpType {
OP_TYPE_SCALAR,
- OP_TYPE_VECTOR,
- OP_TYPE_VECTOR_SCALAR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_2D_SCALAR,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_3D_SCALAR,
OP_TYPE_MAX,
};
@@ -1416,8 +1467,10 @@ class VisualShaderNodeSmoothStep : public VisualShaderNode {
public:
enum OpType {
OP_TYPE_SCALAR,
- OP_TYPE_VECTOR,
- OP_TYPE_VECTOR_SCALAR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_2D_SCALAR,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_3D_SCALAR,
OP_TYPE_MAX,
};
@@ -1452,20 +1505,20 @@ VARIANT_ENUM_CAST(VisualShaderNodeSmoothStep::OpType)
/// DISTANCE
///////////////////////////////////////
-class VisualShaderNodeVectorDistance : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorDistance, VisualShaderNode);
+class VisualShaderNodeVectorDistance : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorDistance, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_op_type(OpType p_op_type) override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeVectorDistance();
@@ -1504,8 +1557,10 @@ class VisualShaderNodeMix : public VisualShaderNode {
public:
enum OpType {
OP_TYPE_SCALAR,
- OP_TYPE_VECTOR,
- OP_TYPE_VECTOR_SCALAR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_2D_SCALAR,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_3D_SCALAR,
OP_TYPE_MAX,
};
@@ -1540,8 +1595,8 @@ VARIANT_ENUM_CAST(VisualShaderNodeMix::OpType)
/// COMPOSE
///////////////////////////////////////
-class VisualShaderNodeVectorCompose : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorCompose, VisualShaderNode);
+class VisualShaderNodeVectorCompose : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorCompose, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
@@ -1551,9 +1606,9 @@ public:
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
- virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_op_type(OpType p_op_type) override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeVectorCompose();
@@ -1584,20 +1639,20 @@ public:
/// DECOMPOSE
///////////////////////////////////////
-class VisualShaderNodeVectorDecompose : public VisualShaderNode {
- GDCLASS(VisualShaderNodeVectorDecompose, VisualShaderNode);
+class VisualShaderNodeVectorDecompose : public VisualShaderNodeVectorBase {
+ GDCLASS(VisualShaderNodeVectorDecompose, VisualShaderNodeVectorBase);
public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
- virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual void set_op_type(OpType p_op_type) override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeVectorDecompose();
@@ -1849,6 +1904,49 @@ public:
///////////////////////////////////////
+class VisualShaderNodeVec2Uniform : public VisualShaderNodeUniform {
+ GDCLASS(VisualShaderNodeVec2Uniform, VisualShaderNodeUniform);
+
+private:
+ bool default_value_enabled = false;
+ Vector2 default_value;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ virtual bool is_show_prop_names() const override;
+ virtual bool is_use_prop_slots() const override;
+
+ void set_default_value_enabled(bool p_enabled);
+ bool is_default_value_enabled() const;
+
+ void set_default_value(const Vector2 &p_value);
+ Vector2 get_default_value() const;
+
+ bool is_qualifier_supported(Qualifier p_qual) const override;
+ bool is_convertible_to_constant() const override;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeVec2Uniform();
+};
+
+///////////////////////////////////////
+
class VisualShaderNodeVec3Uniform : public VisualShaderNodeUniform {
GDCLASS(VisualShaderNodeVec3Uniform, VisualShaderNodeUniform);
@@ -1986,7 +2084,7 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
@@ -2036,7 +2134,7 @@ public:
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
@@ -2061,7 +2159,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -2084,7 +2182,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -2107,7 +2205,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -2148,7 +2246,8 @@ public:
enum OpType {
OP_TYPE_FLOAT,
OP_TYPE_INT,
- OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
OP_TYPE_BOOLEAN,
OP_TYPE_TRANSFORM,
OP_TYPE_MAX,
@@ -2200,7 +2299,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual bool is_generate_input_var(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -2261,7 +2360,8 @@ public:
enum ComparisonType {
CTYPE_SCALAR,
CTYPE_SCALAR_INT,
- CTYPE_VECTOR,
+ CTYPE_VECTOR_2D,
+ CTYPE_VECTOR_3D,
CTYPE_BOOLEAN,
CTYPE_TRANSFORM,
CTYPE_MAX,
@@ -2329,7 +2429,8 @@ class VisualShaderNodeMultiplyAdd : public VisualShaderNode {
public:
enum OpType {
OP_TYPE_SCALAR,
- OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
OP_TYPE_MAX,
};
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index c970b9c08b..fbac92a06d 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -1448,22 +1448,22 @@ bool VisualShaderNodeParticleEmit::is_generate_input_var(int p_port) const {
return true;
}
-String VisualShaderNodeParticleEmit::get_input_port_default_hint(int p_port) const {
+bool VisualShaderNodeParticleEmit::is_input_port_default(int p_port, Shader::Mode p_mode) const {
switch (p_port) {
case 1:
- return "default";
+ return true;
case 2:
- return "default";
+ return true;
case 3:
- return "default";
+ return true;
case 4:
- return "default";
+ return true;
case 5:
- return "default";
+ return true;
case 6:
- return "default";
+ return true;
}
- return String();
+ return false;
}
String VisualShaderNodeParticleEmit::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index add6928841..ce0d896c01 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -342,7 +342,7 @@ public:
virtual bool is_show_prop_names() const override;
virtual bool is_generate_input_var(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeParticleEmit();
diff --git a/scene/resources/visual_shader_sdf_nodes.cpp b/scene/resources/visual_shader_sdf_nodes.cpp
index 1b43fda4c5..6654e2319b 100644
--- a/scene/resources/visual_shader_sdf_nodes.cpp
+++ b/scene/resources/visual_shader_sdf_nodes.cpp
@@ -97,11 +97,11 @@ String VisualShaderNodeScreenUVToSDF::get_output_port_name(int p_port) const {
return "";
}
-String VisualShaderNodeScreenUVToSDF::get_input_port_default_hint(int p_port) const {
+bool VisualShaderNodeScreenUVToSDF::is_input_port_default(int p_port, Shader::Mode p_mode) const {
if (p_port == 0) {
- return "default";
+ return true;
}
- return "";
+ return false;
}
String VisualShaderNodeScreenUVToSDF::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
diff --git a/scene/resources/visual_shader_sdf_nodes.h b/scene/resources/visual_shader_sdf_nodes.h
index d2d1dde4ea..7c1f695423 100644
--- a/scene/resources/visual_shader_sdf_nodes.h
+++ b/scene/resources/visual_shader_sdf_nodes.h
@@ -66,7 +66,7 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
- virtual String get_input_port_default_hint(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeScreenUVToSDF();
diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp
index c937d988d2..9d8e0f7547 100644
--- a/scene/resources/world_2d.cpp
+++ b/scene/resources/world_2d.cpp
@@ -83,8 +83,8 @@ World2D::World2D() {
// Create and configure the navigation_map to be more friendly with pixels than meters.
navigation_map = NavigationServer2D::get_singleton()->map_create();
NavigationServer2D::get_singleton()->map_set_active(navigation_map, true);
- NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 10));
- NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 5));
+ NavigationServer2D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_DEF("navigation/2d/default_cell_size", 1));
+ NavigationServer2D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_DEF("navigation/2d/default_edge_connection_margin", 1));
}
World2D::~World2D() {
diff --git a/servers/audio/effects/audio_effect_record.cpp b/servers/audio/effects/audio_effect_record.cpp
index 569832b8a8..a5866bb380 100644
--- a/servers/audio/effects/audio_effect_record.cpp
+++ b/servers/audio/effects/audio_effect_record.cpp
@@ -112,7 +112,7 @@ void AudioEffectRecordInstance::init() {
ring_buffer_read_pos = 0;
//We start a new recording
- recording_data.resize(0); //Clear data completely and reset length
+ recording_data.clear(); //Clear data completely and reset length
is_recording = true;
#ifdef NO_THREADS
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index c89f811678..f00b8077d1 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -456,10 +456,12 @@ void AudioServer::_mix_step() {
case AudioStreamPlaybackListNode::AWAITING_DELETION:
case AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION:
playback_list.erase(playback, [](AudioStreamPlaybackListNode *p) {
- if (p->prev_bus_details)
+ if (p->prev_bus_details) {
delete p->prev_bus_details;
- if (p->bus_details)
+ }
+ if (p->bus_details) {
delete p->bus_details;
+ }
p->stream_playback.unref();
delete p;
});
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 01f58e37eb..5ded5cf214 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -139,10 +139,6 @@ void DisplayServer::mouse_warp_to_position(const Point2i &p_to) {
WARN_PRINT("Mouse warping is not supported by this display server.");
}
-Point2i DisplayServer::mouse_get_absolute_position() const {
- ERR_FAIL_V_MSG(Point2i(), "Mouse is not supported by this display server.");
-}
-
Point2i DisplayServer::mouse_get_position() const {
ERR_FAIL_V_MSG(Point2i(), "Mouse is not supported by this display server.");
}
@@ -159,6 +155,10 @@ String DisplayServer::clipboard_get() const {
ERR_FAIL_V_MSG(String(), "Clipboard is not supported by this display server.");
}
+bool DisplayServer::clipboard_has() const {
+ return !clipboard_get().is_empty();
+}
+
void DisplayServer::clipboard_set_primary(const String &p_text) {
WARN_PRINT("Primary clipboard is not supported by this display server.");
}
@@ -228,14 +228,6 @@ String DisplayServer::ime_get_text() const {
ERR_FAIL_V_MSG(String(), "IME or NOTIFICATION_WM_IME_UPDATEnot supported by this display server.");
}
-void DisplayServer::console_set_visible(bool p_enabled) {
- WARN_PRINT("Console window not supported by this display server.");
-}
-
-bool DisplayServer::is_console_visible() const {
- return false;
-}
-
void DisplayServer::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_length, int p_cursor_start, int p_cursor_end) {
WARN_PRINT("Virtual keyboard not supported by this display server.");
}
@@ -324,6 +316,11 @@ void DisplayServer::set_icon(const Ref<Image> &p_icon) {
WARN_PRINT("Icon not supported by this display server.");
}
+int64_t DisplayServer::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
+ WARN_PRINT("Native handle not supported by this display server.");
+ return 0;
+}
+
void DisplayServer::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
WARN_PRINT("Changing the VSync mode is not supported by this display server.");
}
@@ -367,11 +364,11 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("mouse_warp_to_position", "position"), &DisplayServer::mouse_warp_to_position);
ClassDB::bind_method(D_METHOD("mouse_get_position"), &DisplayServer::mouse_get_position);
- ClassDB::bind_method(D_METHOD("mouse_get_absolute_position"), &DisplayServer::mouse_get_absolute_position);
ClassDB::bind_method(D_METHOD("mouse_get_button_state"), &DisplayServer::mouse_get_button_state);
ClassDB::bind_method(D_METHOD("clipboard_set", "clipboard"), &DisplayServer::clipboard_set);
ClassDB::bind_method(D_METHOD("clipboard_get"), &DisplayServer::clipboard_get);
+ ClassDB::bind_method(D_METHOD("clipboard_has"), &DisplayServer::clipboard_has);
ClassDB::bind_method(D_METHOD("clipboard_set_primary", "clipboard_primary"), &DisplayServer::clipboard_set_primary);
ClassDB::bind_method(D_METHOD("clipboard_get_primary"), &DisplayServer::clipboard_get_primary);
@@ -396,6 +393,8 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_sub_window", "mode", "vsync_mode", "flags", "rect"), &DisplayServer::create_sub_window, DEFVAL(Rect2i()));
ClassDB::bind_method(D_METHOD("delete_sub_window", "window_id"), &DisplayServer::delete_sub_window);
+ ClassDB::bind_method(D_METHOD("window_get_native_handle", "handle_type", "window_id"), &DisplayServer::window_get_native_handle, DEFVAL(MAIN_WINDOW_ID));
+
ClassDB::bind_method(D_METHOD("window_set_title", "title", "window_id"), &DisplayServer::window_set_title, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_set_mouse_passthrough", "region", "window_id"), &DisplayServer::window_set_mouse_passthrough, DEFVAL(MAIN_WINDOW_ID));
@@ -446,9 +445,6 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("ime_get_selection"), &DisplayServer::ime_get_selection);
ClassDB::bind_method(D_METHOD("ime_get_text"), &DisplayServer::ime_get_text);
- ClassDB::bind_method(D_METHOD("console_set_visible", "console_visible"), &DisplayServer::console_set_visible);
- ClassDB::bind_method(D_METHOD("is_console_visible"), &DisplayServer::is_console_visible);
-
ClassDB::bind_method(D_METHOD("virtual_keyboard_show", "existing_text", "position", "multiline", "max_length", "cursor_start", "cursor_end"), &DisplayServer::virtual_keyboard_show, DEFVAL(Rect2i()), DEFVAL(false), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("virtual_keyboard_hide"), &DisplayServer::virtual_keyboard_hide);
@@ -493,7 +489,6 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_CURSOR_SHAPE);
BIND_ENUM_CONSTANT(FEATURE_CUSTOM_CURSOR_SHAPE);
BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG);
- BIND_ENUM_CONSTANT(FEATURE_CONSOLE_WINDOW);
BIND_ENUM_CONSTANT(FEATURE_IME);
BIND_ENUM_CONSTANT(FEATURE_WINDOW_TRANSPARENCY);
BIND_ENUM_CONSTANT(FEATURE_HIDPI);
@@ -564,6 +559,10 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(VSYNC_ENABLED);
BIND_ENUM_CONSTANT(VSYNC_ADAPTIVE);
BIND_ENUM_CONSTANT(VSYNC_MAILBOX);
+
+ BIND_ENUM_CONSTANT(DISPLAY_HANDLE);
+ BIND_ENUM_CONSTANT(WINDOW_HANDLE);
+ BIND_ENUM_CONSTANT(WINDOW_VIEW);
}
void DisplayServer::register_create_function(const char *p_name, CreateFunction p_function, GetRenderingDriversFunction p_get_drivers) {
diff --git a/servers/display_server.h b/servers/display_server.h
index a84290be77..8c6586dc20 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -65,6 +65,12 @@ public:
VSYNC_MAILBOX
};
+ enum HandleType {
+ DISPLAY_HANDLE,
+ WINDOW_HANDLE,
+ WINDOW_VIEW,
+ };
+
typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, VSyncMode, uint32_t, const Size2i &, Error &r_error);
typedef Vector<String> (*GetRenderingDriversFunction)();
@@ -105,7 +111,6 @@ public:
FEATURE_CURSOR_SHAPE,
FEATURE_CUSTOM_CURSOR_SHAPE,
FEATURE_NATIVE_DIALOG,
- FEATURE_CONSOLE_WINDOW,
FEATURE_IME,
FEATURE_WINDOW_TRANSPARENCY,
FEATURE_HIDPI,
@@ -157,11 +162,11 @@ public:
virtual void mouse_warp_to_position(const Point2i &p_to);
virtual Point2i mouse_get_position() const;
- virtual Point2i mouse_get_absolute_position() const;
virtual MouseButton mouse_get_button_state() const;
virtual void clipboard_set(const String &p_text);
virtual String clipboard_get() const;
+ virtual bool clipboard_has() const;
virtual void clipboard_set_primary(const String &p_text);
virtual String clipboard_get_primary() const;
@@ -233,6 +238,8 @@ public:
virtual void show_window(WindowID p_id);
virtual void delete_sub_window(WindowID p_id);
+ virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const;
+
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const = 0;
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) = 0;
@@ -304,9 +311,6 @@ public:
virtual Point2i ime_get_selection() const;
virtual String ime_get_text() const;
- virtual void console_set_visible(bool p_enabled);
- virtual bool is_console_visible() const;
-
virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1);
virtual void virtual_keyboard_hide();
@@ -391,6 +395,7 @@ VARIANT_ENUM_CAST(DisplayServer::MouseMode)
VARIANT_ENUM_CAST(DisplayServer::ScreenOrientation)
VARIANT_ENUM_CAST(DisplayServer::WindowMode)
VARIANT_ENUM_CAST(DisplayServer::WindowFlags)
+VARIANT_ENUM_CAST(DisplayServer::HandleType)
VARIANT_ENUM_CAST(DisplayServer::CursorShape)
VARIANT_ENUM_CAST(DisplayServer::VSyncMode)
diff --git a/servers/physics_3d/godot_shape_3d.cpp b/servers/physics_3d/godot_shape_3d.cpp
index 26fa470233..666e773c1c 100644
--- a/servers/physics_3d/godot_shape_3d.cpp
+++ b/servers/physics_3d/godot_shape_3d.cpp
@@ -52,11 +52,11 @@ subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.
*/
-#define _EDGE_IS_VALID_SUPPORT_THRESHOLD 0.0002
-#define _FACE_IS_VALID_SUPPORT_THRESHOLD 0.9998
+constexpr double edge_support_threshold = 0.0002;
+constexpr double face_support_threshold = 0.9998;
-#define _CYLINDER_EDGE_IS_VALID_SUPPORT_THRESHOLD 0.002
-#define _CYLINDER_FACE_IS_VALID_SUPPORT_THRESHOLD 0.999
+constexpr double cylinder_edge_support_threshold = 0.002;
+constexpr double cylinder_face_support_threshold = 0.999;
void GodotShape3D::configure(const AABB &p_aabb) {
aabb = p_aabb;
@@ -184,7 +184,7 @@ Vector3 GodotSeparationRayShape3D::get_support(const Vector3 &p_normal) const {
}
void GodotSeparationRayShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {
- if (Math::abs(p_normal.z) < _EDGE_IS_VALID_SUPPORT_THRESHOLD) {
+ if (Math::abs(p_normal.z) < edge_support_threshold) {
r_amount = 2;
r_type = FEATURE_EDGE;
r_supports[0] = Vector3(0, 0, 0);
@@ -335,7 +335,7 @@ void GodotBoxShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *
Vector3 axis;
axis[i] = 1.0;
real_t dot = p_normal.dot(axis);
- if (Math::abs(dot) > _FACE_IS_VALID_SUPPORT_THRESHOLD) {
+ if (Math::abs(dot) > face_support_threshold) {
//Vector3 axis_b;
bool neg = dot < 0;
@@ -376,7 +376,7 @@ void GodotBoxShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *
Vector3 axis;
axis[i] = 1.0;
- if (Math::abs(p_normal.dot(axis)) < _EDGE_IS_VALID_SUPPORT_THRESHOLD) {
+ if (Math::abs(p_normal.dot(axis)) < edge_support_threshold) {
r_amount = 2;
r_type = FEATURE_EDGE;
@@ -522,7 +522,7 @@ void GodotCapsuleShape3D::get_supports(const Vector3 &p_normal, int p_max, Vecto
real_t d = n.y;
- if (Math::abs(d) < _EDGE_IS_VALID_SUPPORT_THRESHOLD) {
+ if (Math::abs(d) < edge_support_threshold) {
// make it flat
n.y = 0.0;
n.normalize();
@@ -703,7 +703,7 @@ Vector3 GodotCylinderShape3D::get_support(const Vector3 &p_normal) const {
void GodotCylinderShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {
real_t d = p_normal.y;
- if (Math::abs(d) > _CYLINDER_FACE_IS_VALID_SUPPORT_THRESHOLD) {
+ if (Math::abs(d) > cylinder_face_support_threshold) {
real_t h = (d > 0) ? height : -height;
Vector3 n = p_normal;
@@ -718,7 +718,7 @@ void GodotCylinderShape3D::get_supports(const Vector3 &p_normal, int p_max, Vect
r_supports[1].x += radius;
r_supports[2] = n;
r_supports[2].z += radius;
- } else if (Math::abs(d) < _CYLINDER_EDGE_IS_VALID_SUPPORT_THRESHOLD) {
+ } else if (Math::abs(d) < cylinder_edge_support_threshold) {
// make it flat
Vector3 n = p_normal;
n.y = 0.0;
@@ -911,7 +911,7 @@ void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max,
}
for (int i = 0; i < fc; i++) {
- if (faces[i].plane.normal.dot(p_normal) > _FACE_IS_VALID_SUPPORT_THRESHOLD) {
+ if (faces[i].plane.normal.dot(p_normal) > face_support_threshold) {
int ic = faces[i].indices.size();
const int *ind = faces[i].indices.ptr();
@@ -940,7 +940,7 @@ void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max,
for (int i = 0; i < ec; i++) {
real_t dot = (vertices[edges[i].a] - vertices[edges[i].b]).normalized().dot(p_normal);
dot = ABS(dot);
- if (dot < _EDGE_IS_VALID_SUPPORT_THRESHOLD && (edges[i].a == vtx || edges[i].b == vtx)) {
+ if (dot < edge_support_threshold && (edges[i].a == vtx || edges[i].b == vtx)) {
r_amount = 2;
r_type = FEATURE_EDGE;
r_supports[0] = vertices[edges[i].a];
@@ -1140,7 +1140,7 @@ void GodotFaceShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3
Vector3 n = p_normal;
/** TEST FACE AS SUPPORT **/
- if (Math::abs(normal.dot(n)) > _FACE_IS_VALID_SUPPORT_THRESHOLD) {
+ if (Math::abs(normal.dot(n)) > face_support_threshold) {
r_amount = 3;
r_type = FEATURE_FACE;
for (int i = 0; i < 3; i++) {
@@ -1174,7 +1174,7 @@ void GodotFaceShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3
// check if edge is valid as a support
real_t dot = (vertex[i] - vertex[nx]).normalized().dot(n);
dot = ABS(dot);
- if (dot < _EDGE_IS_VALID_SUPPORT_THRESHOLD) {
+ if (dot < edge_support_threshold) {
r_amount = 2;
r_type = FEATURE_EDGE;
r_supports[0] = vertex[i];
diff --git a/servers/physics_3d/godot_soft_body_3d.cpp b/servers/physics_3d/godot_soft_body_3d.cpp
index 1de27760d5..095050b7f3 100644
--- a/servers/physics_3d/godot_soft_body_3d.cpp
+++ b/servers/physics_3d/godot_soft_body_3d.cpp
@@ -245,7 +245,7 @@ void GodotSoftBody3D::update_area() {
const Vector3 a = x1 - x0;
const Vector3 b = x2 - x0;
const Vector3 cr = vec3_cross(a, b);
- face.ra = cr.length();
+ face.ra = cr.length() * 0.5;
}
// Node area.
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 3fbf4fe436..bf8b6379d2 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -80,7 +80,7 @@
ShaderTypes *shader_types = nullptr;
PhysicsServer3D *_createGodotPhysics3DCallback() {
- bool using_threads = GLOBAL_GET("physics/3d/run_on_thread");
+ bool using_threads = GLOBAL_GET("physics/3d/run_on_separate_thread");
PhysicsServer3D *physics_server = memnew(GodotPhysicsServer3D(using_threads));
@@ -88,7 +88,7 @@ PhysicsServer3D *_createGodotPhysics3DCallback() {
}
PhysicsServer2D *_createGodotPhysics2DCallback() {
- bool using_threads = GLOBAL_GET("physics/2d/run_on_thread");
+ bool using_threads = GLOBAL_GET("physics/2d/run_on_separate_thread");
PhysicsServer2D *physics_server = memnew(GodotPhysicsServer2D(using_threads));
diff --git a/servers/rendering/rasterizer_dummy.h b/servers/rendering/rasterizer_dummy.h
index f02a01c97d..7032f3fb03 100644
--- a/servers/rendering/rasterizer_dummy.h
+++ b/servers/rendering/rasterizer_dummy.h
@@ -109,7 +109,7 @@ public:
void environment_set_canvas_max_layer(RID p_env, int p_max_layer) override {}
void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG) override {}
- void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) override {}
+ void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) override {}
void environment_glow_set_use_bicubic_upscale(bool p_enable) override {}
void environment_glow_set_use_high_quality(bool p_enable) override {}
@@ -120,7 +120,7 @@ public:
void environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) override {}
void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {}
- void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override {}
+ void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override {}
void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override {}
void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override {}
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index f5e2cbcd6c..418d2bc42e 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -1440,7 +1440,7 @@ void RendererCanvasCull::canvas_light_occluder_set_polygon(RID p_occluder, RID p
ERR_FAIL_COND(!occluder);
if (occluder->polygon.is_valid()) {
- LightOccluderPolygon *occluder_poly = canvas_light_occluder_polygon_owner.get_or_null(p_polygon);
+ LightOccluderPolygon *occluder_poly = canvas_light_occluder_polygon_owner.get_or_null(occluder->polygon);
if (occluder_poly) {
occluder_poly->owners.erase(occluder);
}
diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp
index 4ab50782df..25a366aa4b 100644
--- a/servers/rendering/renderer_rd/effects_rd.cpp
+++ b/servers/rendering/renderer_rd/effects_rd.cpp
@@ -115,6 +115,43 @@ RID EffectsRD::_get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps)
return uniform_set;
}
+RID EffectsRD::_get_uniform_set_from_texture_pair(RID p_texture1, RID p_texture2, bool p_use_mipmaps) {
+ TexturePair tp;
+ tp.texture1 = p_texture1;
+ tp.texture2 = p_texture2;
+
+ if (texture_pair_to_uniform_set_cache.has(tp)) {
+ RID uniform_set = texture_pair_to_uniform_set_cache[tp];
+ if (RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
+ return uniform_set;
+ }
+ }
+
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+ u.binding = 0;
+ u.ids.push_back(p_use_mipmaps ? default_mipmap_sampler : default_sampler);
+ u.ids.push_back(p_texture1);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
+ u.binding = 1;
+ u.ids.push_back(p_use_mipmaps ? default_mipmap_sampler : default_sampler);
+ u.ids.push_back(p_texture2);
+ uniforms.push_back(u);
+ }
+ // anything with the same configuration (one texture in binding 0 for set 0), is good
+ RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, tonemap.shader.version_get_shader(tonemap.shader_version, 0), 2);
+
+ texture_pair_to_uniform_set_cache[tp] = uniform_set;
+
+ return uniform_set;
+}
+
RID EffectsRD::_get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps) {
if (texture_to_compute_uniform_set_cache.has(p_texture)) {
RID uniform_set = texture_to_compute_uniform_set_cache[p_texture];
@@ -828,6 +865,7 @@ void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Tone
tonemap.push_constant.use_glow = p_settings.use_glow;
tonemap.push_constant.glow_intensity = p_settings.glow_intensity;
+ tonemap.push_constant.glow_map_strength = p_settings.glow_map_strength;
tonemap.push_constant.glow_levels[0] = p_settings.glow_levels[0]; // clean this up to just pass by pointer or something
tonemap.push_constant.glow_levels[1] = p_settings.glow_levels[1];
tonemap.push_constant.glow_levels[2] = p_settings.glow_levels[2];
@@ -867,7 +905,7 @@ void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Tone
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass()));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_color), 0);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_settings.exposure_texture), 1);
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_settings.glow_texture, true), 2);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture_pair(p_settings.glow_texture, p_settings.glow_map, true), 2);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_settings.color_correction_texture), 3);
RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
@@ -907,7 +945,7 @@ void EffectsRD::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_colo
RD::get_singleton()->draw_list_bind_render_pipeline(p_subpass_draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, p_dst_format_id, false, RD::get_singleton()->draw_list_get_current_pass()));
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_for_input(p_source_color), 0);
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_from_texture(p_settings.exposure_texture), 1); // should be set to a default texture, it's ignored
- RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_from_texture(p_settings.glow_texture, true), 2); // should be set to a default texture, it's ignored
+ RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_from_texture_pair(p_settings.glow_texture, p_settings.glow_map, true), 2); // should be set to a default texture, it's ignored
RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, _get_uniform_set_from_texture(p_settings.color_correction_texture), 3);
RD::get_singleton()->draw_list_bind_index_array(p_subpass_draw_list, index_array);
diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h
index 747f2c8941..1a080756a8 100644
--- a/servers/rendering/renderer_rd/effects_rd.h
+++ b/servers/rendering/renderer_rd/effects_rd.h
@@ -274,7 +274,7 @@ private:
uint32_t glow_texture_size[2]; // 8 - 40
float glow_intensity; // 4 - 44
- uint32_t pad3; // 4 - 48
+ float glow_map_strength; // 4 - 48
uint32_t glow_mode; // 4 - 52
float glow_levels[7]; // 28 - 80
@@ -728,7 +728,6 @@ private:
uint8_t metallic_mask[4];
float projection[16];
- float prev_projection[16];
};
struct ScreenSpaceReflection {
@@ -875,6 +874,7 @@ private:
}
};
+ Map<TexturePair, RID> texture_pair_to_uniform_set_cache;
Map<RID, RID> texture_to_compute_uniform_set_cache;
Map<TexturePair, RID> texture_pair_to_compute_uniform_set_cache;
Map<TexturePair, RID> image_pair_to_compute_uniform_set_cache;
@@ -883,6 +883,7 @@ private:
RID _get_uniform_set_from_image(RID p_texture);
RID _get_uniform_set_for_input(RID p_texture);
RID _get_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false);
+ RID _get_uniform_set_from_texture_pair(RID p_texture1, RID p_texture2, bool p_use_mipmaps = false);
RID _get_compute_uniform_set_from_texture(RID p_texture, bool p_use_mipmaps = false);
RID _get_compute_uniform_set_from_texture_and_sampler(RID p_texture, RID p_sampler);
RID _get_compute_uniform_set_from_texture_pair(RID p_texture, RID p_texture2, bool p_use_mipmaps = false);
@@ -944,10 +945,12 @@ public:
GlowMode glow_mode = GLOW_MODE_ADD;
float glow_intensity = 1.0;
+ float glow_map_strength = 0.0f;
float glow_levels[7] = { 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0 };
Vector2i glow_texture_size;
bool glow_use_bicubic_upscale = false;
RID glow_texture;
+ RID glow_map;
RS::EnvironmentToneMapper tonemap_mode = RS::ENV_TONE_MAPPER_LINEAR;
float exposure = 1.0;
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index 36604073cc..87301a9d3a 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -1081,6 +1081,10 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
distance = -distance_max;
}
+ if (p_render_data->cam_ortogonal) {
+ distance = 1.0;
+ }
+
uint32_t indices;
surf->sort.lod_index = storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, &indices);
if (p_render_data->render_info) {
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index a27ea75017..7987a98b0e 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -463,7 +463,6 @@ SceneShaderForwardClustered::MaterialData::~MaterialData() {
RendererStorageRD::MaterialData *SceneShaderForwardClustered::_create_material_func(ShaderData *p_shader) {
MaterialData *material_data = memnew(MaterialData);
material_data->shader_data = p_shader;
- material_data->last_frame = false;
//update will happen later anyway so do nothing.
return material_data;
}
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
index 8e7bbad63e..33049fad9c 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
@@ -189,7 +189,6 @@ public:
}
struct MaterialData : public RendererStorageRD::MaterialData {
- uint64_t last_frame;
ShaderData *shader_data;
RID uniform_set;
uint64_t last_pass = 0;
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 b9c51f5461..778d7baa5d 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -1042,7 +1042,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<GeometryInstance *> &p_in
RENDER_TIMESTAMP("Render Material");
{
- RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, rp_uniform_set, true, 0);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, rp_uniform_set, true, false);
//regular forward for now
Vector<Color> clear = {
Color(0, 0, 0, 0),
@@ -1429,6 +1429,10 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const
distance = -distance_max;
}
+ if (p_render_data->cam_ortogonal) {
+ distance = 1.0;
+ }
+
uint32_t indices;
surf->lod_index = storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, &indices);
if (p_render_data->render_info) {
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index 1613a307ec..0b99948063 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -452,7 +452,6 @@ SceneShaderForwardMobile::MaterialData::~MaterialData() {
RendererStorageRD::MaterialData *SceneShaderForwardMobile::_create_material_func(ShaderData *p_shader) {
MaterialData *material_data = memnew(MaterialData);
material_data->shader_data = p_shader;
- material_data->last_frame = false;
//update will happen later anyway so do nothing.
return material_data;
}
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
index c136afd9f3..92db15e3b0 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
@@ -163,7 +163,6 @@ public:
}
struct MaterialData : public RendererStorageRD::MaterialData {
- uint64_t last_frame;
ShaderData *shader_data;
RID uniform_set;
uint64_t last_pass = 0;
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 7e188926e0..0f3daef371 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1377,14 +1377,6 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
if (md->shader_data->uses_time) {
time_used = true;
}
- if (md->last_frame != RendererCompositorRD::singleton->get_frame_number()) {
- md->last_frame = RendererCompositorRD::singleton->get_frame_number();
- if (!RD::get_singleton()->uniform_set_is_valid(md->uniform_set)) {
- // uniform set may be gone because a dependency was erased. In this case, it will happen
- // if a texture is deleted, so just re-create it.
- storage->material_force_update_textures(material, RendererStorageRD::SHADER_TYPE_2D);
- }
- }
}
}
@@ -2240,7 +2232,6 @@ RendererCanvasRenderRD::MaterialData::~MaterialData() {
RendererStorageRD::MaterialData *RendererCanvasRenderRD::_create_material_func(ShaderData *p_shader) {
MaterialData *material_data = memnew(MaterialData);
material_data->shader_data = p_shader;
- material_data->last_frame = false;
//update will happen later anyway so do nothing.
return material_data;
}
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index b409264c9a..84f64b6fda 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -200,7 +200,6 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
}
struct MaterialData : public RendererStorageRD::MaterialData {
- uint64_t last_frame;
ShaderData *shader_data;
RID uniform_set;
diff --git a/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp
index 7ea117ef33..0d9477d850 100644
--- a/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp
@@ -54,7 +54,7 @@ void RendererSceneEnvironmentRD::set_tonemap(RS::EnvironmentToneMapper p_tone_ma
auto_exp_scale = p_auto_exp_scale;
}
-void RendererSceneEnvironmentRD::set_glow(bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) {
+void RendererSceneEnvironmentRD::set_glow(bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) {
ERR_FAIL_COND_MSG(p_levels.size() != 7, "Size of array of glow levels must be 7");
glow_enabled = p_enable;
glow_levels = p_levels;
@@ -66,9 +66,11 @@ void RendererSceneEnvironmentRD::set_glow(bool p_enable, Vector<float> p_levels,
glow_hdr_bleed_threshold = p_hdr_bleed_threshold;
glow_hdr_bleed_scale = p_hdr_bleed_scale;
glow_hdr_luminance_cap = p_hdr_luminance_cap;
+ glow_map_strength = p_glow_map_strength;
+ glow_map = p_glow_map;
}
-void RendererSceneEnvironmentRD::set_sdfgi(bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) {
+void RendererSceneEnvironmentRD::set_sdfgi(bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) {
sdfgi_enabled = p_enable;
sdfgi_cascades = p_cascades;
sdfgi_min_cell_size = p_min_cell_size;
diff --git a/servers/rendering/renderer_rd/renderer_scene_environment_rd.h b/servers/rendering/renderer_rd/renderer_scene_environment_rd.h
index 9e36a61870..ed26fd467b 100644
--- a/servers/rendering/renderer_rd/renderer_scene_environment_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_environment_rd.h
@@ -102,6 +102,8 @@ public:
float glow_hdr_bleed_threshold = 1.0;
float glow_hdr_luminance_cap = 12.0;
float glow_hdr_bleed_scale = 2.0;
+ float glow_map_strength = 0.0f;
+ RID glow_map = RID();
/// SSAO
@@ -133,7 +135,7 @@ public:
/// SDFGI
bool sdfgi_enabled = false;
- RS::EnvironmentSDFGICascades sdfgi_cascades;
+ int sdfgi_cascades = 6;
float sdfgi_min_cell_size = 0.2;
bool sdfgi_use_occlusion = false;
float sdfgi_bounce_feedback = 0.0;
@@ -154,8 +156,8 @@ public:
void set_ambient_light(const Color &p_color, RS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, RS::EnvironmentReflectionSource p_reflection_source);
void set_tonemap(RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale);
- void set_glow(bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap);
- void set_sdfgi(bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias);
+ void set_glow(bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map);
+ void set_sdfgi(bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias);
void set_fog(bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective);
void set_volumetric_fog(bool p_enable, float p_density, const Color &p_scatterin, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject);
void set_ssr(bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance);
diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp
index 73cb088f6a..3069b1c379 100644
--- a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp
@@ -42,14 +42,13 @@ const Vector3i RendererSceneGIRD::SDFGI::Cascade::DIRTY_ALL = Vector3i(0x7FFFFFF
void RendererSceneGIRD::SDFGI::create(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, RendererSceneGIRD *p_gi) {
storage = p_gi->storage;
gi = p_gi;
- cascade_mode = p_env->sdfgi_cascades;
+ num_cascades = p_env->sdfgi_cascades;
min_cell_size = p_env->sdfgi_min_cell_size;
uses_occlusion = p_env->sdfgi_use_occlusion;
y_scale_mode = p_env->sdfgi_y_scale;
static const float y_scale[3] = { 1.0, 1.5, 2.0 };
y_mult = y_scale[y_scale_mode];
- static const int cascasde_size[3] = { 4, 6, 8 };
- cascades.resize(cascasde_size[cascade_mode]);
+ cascades.resize(num_cascades);
probe_axis_count = SDFGI::PROBE_DIVISOR + 1;
solid_cell_ratio = gi->sdfgi_solid_cell_ratio;
solid_cell_count = uint32_t(float(cascade_size * cascade_size * cascade_size) * solid_cell_ratio);
@@ -716,7 +715,10 @@ void RendererSceneGIRD::SDFGI::create(RendererSceneEnvironmentRD *p_env, const V
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 13;
RID parent_average;
- if (i < cascades.size() - 1) {
+ if (cascades.size() == 1) {
+ // If there is only one SDFGI cascade, we can't use the previous cascade for blending.
+ parent_average = cascades[i].lightprobe_average_tex;
+ } else if (i < cascades.size() - 1) {
parent_average = cascades[i + 1].lightprobe_average_tex;
} else {
parent_average = cascades[i - 1].lightprobe_average_tex; //to use something, but it won't be used
diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.h b/servers/rendering/renderer_rd/renderer_scene_gi_rd.h
index a407199d0a..5e55262798 100644
--- a/servers/rendering/renderer_rd/renderer_scene_gi_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_gi_rd.h
@@ -495,7 +495,7 @@ public:
float solid_cell_ratio = 0;
uint32_t solid_cell_count = 0;
- RS::EnvironmentSDFGICascades cascade_mode;
+ int num_cascades = 6;
float min_cell_size = 0;
uint32_t probe_axis_count = 0; //amount of probes per axis, this is an odd number because it encloses endpoints
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index a499cedd2c..43a1812f89 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -65,7 +65,7 @@ void RendererSceneRenderRD::sdfgi_update(RID p_render_buffers, RID p_environment
static const uint32_t history_frames_to_converge[RS::ENV_SDFGI_CONVERGE_MAX] = { 5, 10, 15, 20, 25, 30 };
uint32_t requested_history_size = history_frames_to_converge[gi.sdfgi_frames_to_converge];
- if (rb->sdfgi && (rb->sdfgi->cascade_mode != env->sdfgi_cascades || rb->sdfgi->min_cell_size != env->sdfgi_min_cell_size || requested_history_size != rb->sdfgi->history_size || rb->sdfgi->uses_occlusion != env->sdfgi_use_occlusion || rb->sdfgi->y_scale_mode != env->sdfgi_y_scale)) {
+ if (rb->sdfgi && (rb->sdfgi->num_cascades != env->sdfgi_cascades || rb->sdfgi->min_cell_size != env->sdfgi_min_cell_size || requested_history_size != rb->sdfgi->history_size || rb->sdfgi->uses_occlusion != env->sdfgi_use_occlusion || rb->sdfgi->y_scale_mode != env->sdfgi_y_scale)) {
//configuration changed, erase
rb->sdfgi->erase();
memdelete(rb->sdfgi);
@@ -289,10 +289,10 @@ void RendererSceneRenderRD::environment_set_tonemap(RID p_env, RS::EnvironmentTo
env->set_tonemap(p_tone_mapper, p_exposure, p_white, p_auto_exposure, p_min_luminance, p_max_luminance, p_auto_exp_speed, p_auto_exp_scale);
}
-void RendererSceneRenderRD::environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) {
+void RendererSceneRenderRD::environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) {
RendererSceneEnvironmentRD *env = environment_owner.get_or_null(p_env);
ERR_FAIL_COND(!env);
- env->set_glow(p_enable, p_levels, p_intensity, p_strength, p_mix, p_bloom_threshold, p_blend_mode, p_hdr_bleed_threshold, p_hdr_bleed_scale, p_hdr_luminance_cap);
+ env->set_glow(p_enable, p_levels, p_intensity, p_strength, p_mix, p_bloom_threshold, p_blend_mode, p_hdr_bleed_threshold, p_hdr_bleed_scale, p_hdr_luminance_cap, p_glow_map_strength, p_glow_map);
}
void RendererSceneRenderRD::environment_glow_set_use_bicubic_upscale(bool p_enable) {
@@ -303,7 +303,7 @@ void RendererSceneRenderRD::environment_glow_set_use_high_quality(bool p_enable)
glow_high_quality = p_enable;
}
-void RendererSceneRenderRD::environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) {
+void RendererSceneRenderRD::environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) {
RendererSceneEnvironmentRD *env = environment_owner.get_or_null(p_env);
ERR_FAIL_COND(!env);
@@ -514,7 +514,7 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba
ambient_color_sky_mix = env->ambient_sky_contribution;
const float ambient_energy = env->ambient_light_energy;
ambient_color = env->ambient_light;
- ambient_color.to_linear();
+ ambient_color = ambient_color.to_linear();
ambient_color.r *= ambient_energy;
ambient_color.g *= ambient_energy;
ambient_color.b *= ambient_energy;
@@ -533,7 +533,7 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba
} else {
const float bg_energy = env->bg_energy;
Color panorama_color = ((environment_background == RS::ENV_BG_CLEAR_COLOR) ? storage->get_default_clear_color() : env->bg_color);
- panorama_color.to_linear();
+ panorama_color = panorama_color.to_linear();
panorama_color.r *= bg_energy;
panorama_color.g *= bg_energy;
panorama_color.b *= bg_energy;
@@ -925,7 +925,7 @@ void RendererSceneRenderRD::shadow_atlas_set_size(RID p_atlas, int p_size, bool
}
for (int i = 0; i < 4; i++) {
//clear subdivisions
- shadow_atlas->quadrants[i].shadows.resize(0);
+ shadow_atlas->quadrants[i].shadows.clear();
shadow_atlas->quadrants[i].shadows.resize(1 << shadow_atlas->quadrants[i].subdivision);
}
@@ -972,7 +972,7 @@ void RendererSceneRenderRD::shadow_atlas_set_quadrant_subdivision(RID p_atlas, i
}
}
- shadow_atlas->quadrants[p_quadrant].shadows.resize(0);
+ shadow_atlas->quadrants[p_quadrant].shadows.clear();
shadow_atlas->quadrants[p_quadrant].shadows.resize(subdiv * subdiv);
shadow_atlas->quadrants[p_quadrant].subdivision = subdiv;
@@ -1132,11 +1132,11 @@ bool RendererSceneRenderRD::_shadow_atlas_find_omni_shadows(ShadowAtlas *shadow_
return false;
}
-bool RendererSceneRenderRD::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) {
+bool RendererSceneRenderRD::shadow_atlas_update_light(RID p_atlas, RID p_light_instance, float p_coverage, uint64_t p_light_version) {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_COND_V(!shadow_atlas, false);
- LightInstance *li = light_instance_owner.get_or_null(p_light_intance);
+ LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_COND_V(!li, false);
if (shadow_atlas->size == 0 || shadow_atlas->smallest_subdiv == 0) {
@@ -1185,8 +1185,8 @@ bool RendererSceneRenderRD::shadow_atlas_update_light(RID p_atlas, RID p_light_i
bool should_realloc = false;
bool should_redraw = false;
- if (shadow_atlas->shadow_owners.has(p_light_intance)) {
- old_key = shadow_atlas->shadow_owners[p_light_intance];
+ if (shadow_atlas->shadow_owners.has(p_light_instance)) {
+ old_key = shadow_atlas->shadow_owners[p_light_instance];
old_quadrant = (old_key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3;
old_shadow = old_key & ShadowAtlas::SHADOW_INDEX_MASK;
@@ -1230,7 +1230,7 @@ bool RendererSceneRenderRD::shadow_atlas_update_light(RID p_atlas, RID p_light_i
ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow];
_shadow_atlas_invalidate_shadow(sh, p_atlas, shadow_atlas, new_quadrant, new_shadow);
- sh->owner = p_light_intance;
+ sh->owner = p_light_instance;
sh->alloc_tick = tick;
sh->version = p_light_version;
@@ -1241,7 +1241,7 @@ bool RendererSceneRenderRD::shadow_atlas_update_light(RID p_atlas, RID p_light_i
ShadowAtlas::Quadrant::Shadow *extra_sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_omni_shadow];
_shadow_atlas_invalidate_shadow(extra_sh, p_atlas, shadow_atlas, new_quadrant, new_omni_shadow);
- extra_sh->owner = p_light_intance;
+ extra_sh->owner = p_light_instance;
extra_sh->alloc_tick = tick;
extra_sh->version = p_light_version;
}
@@ -1249,7 +1249,7 @@ bool RendererSceneRenderRD::shadow_atlas_update_light(RID p_atlas, RID p_light_i
li->shadow_atlases.insert(p_atlas);
//update it in map
- shadow_atlas->shadow_owners[p_light_intance] = new_key;
+ shadow_atlas->shadow_owners[p_light_instance] = new_key;
//make it dirty, as it should redraw anyway
return true;
}
@@ -1270,10 +1270,10 @@ void RendererSceneRenderRD::_shadow_atlas_invalidate_shadow(RendererSceneRenderR
omni_shadow->owner = RID();
}
+ p_shadow_atlas->shadow_owners.erase(p_shadow->owner);
p_shadow->version = 0;
p_shadow->owner = RID();
sli->shadow_atlases.erase(p_atlas);
- p_shadow_atlas->shadow_owners.erase(p_shadow->owner);
}
}
@@ -1920,6 +1920,11 @@ void RendererSceneRenderRD::_free_render_buffer_data(RenderBuffers *rb) {
rb->ambient_buffer = RID();
rb->reflection_buffer = RID();
}
+
+ if (rb->gi.voxel_gi_buffer.is_valid()) {
+ RD::get_singleton()->free(rb->gi.voxel_gi_buffer);
+ rb->gi.voxel_gi_buffer = RID();
+ }
}
void RendererSceneRenderRD::_process_sss(RID p_render_buffers, const CameraMatrix &p_camera) {
@@ -2496,8 +2501,17 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
tonemap.glow_texture_size.y = rb->blur[1].mipmaps[0].height;
tonemap.glow_use_bicubic_upscale = glow_bicubic_upscale;
tonemap.glow_texture = rb->blur[1].texture;
+ if (env->glow_map.is_valid()) {
+ tonemap.glow_map_strength = env->glow_map_strength;
+ tonemap.glow_map = storage->texture_get_rd_texture(env->glow_map);
+ } else {
+ tonemap.glow_map_strength = 0.0f;
+ tonemap.glow_map = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
+ }
+
} else {
tonemap.glow_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+ tonemap.glow_map = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
}
if (rb->screen_space_aa == RS::VIEWPORT_SCREEN_SPACE_AA_FXAA) {
@@ -2590,6 +2604,7 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr
tonemap.use_glow = false;
tonemap.glow_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+ tonemap.glow_map = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
tonemap.use_auto_exposure = false;
tonemap.exposure_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_WHITE);
@@ -2636,8 +2651,12 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SHADOW_ATLAS) {
if (p_shadow_atlas.is_valid()) {
RID shadow_atlas_texture = shadow_atlas_get_texture(p_shadow_atlas);
- Size2 rtsize = storage->render_target_get_size(rb->render_target);
+ if (shadow_atlas_texture.is_null()) {
+ shadow_atlas_texture = storage->texture_rd_get_default(RendererStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+ }
+
+ Size2 rtsize = storage->render_target_get_size(rb->render_target);
effects->copy_to_fb_rect(shadow_atlas_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize / 2), false, true);
}
}
@@ -3993,7 +4012,6 @@ RendererStorageRD::ShaderData *RendererSceneRenderRD::_create_fog_shader_funcs()
RendererStorageRD::MaterialData *RendererSceneRenderRD::_create_fog_material_func(FogShaderData *p_shader) {
FogMaterialData *material_data = memnew(FogMaterialData);
material_data->shader_data = p_shader;
- material_data->last_frame = false;
//update will happen later anyway so do nothing.
return material_data;
}
@@ -4011,6 +4029,9 @@ void RendererSceneRenderRD::_volumetric_fog_erase(RenderBuffers *rb) {
RD::get_singleton()->free(rb->volumetric_fog->prev_light_density_map);
RD::get_singleton()->free(rb->volumetric_fog->light_density_map);
RD::get_singleton()->free(rb->volumetric_fog->fog_map);
+ RD::get_singleton()->free(rb->volumetric_fog->density_map);
+ RD::get_singleton()->free(rb->volumetric_fog->light_map);
+ RD::get_singleton()->free(rb->volumetric_fog->emissive_map);
if (rb->volumetric_fog->fog_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->fog_uniform_set)) {
RD::get_singleton()->free(rb->volumetric_fog->fog_uniform_set);
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index 6432ca99f0..899d2d763d 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -942,7 +942,6 @@ private:
};
struct FogMaterialData : public RendererStorageRD::MaterialData {
- uint64_t last_frame;
FogShaderData *shader_data;
RID uniform_set;
bool uniform_set_updated;
@@ -984,7 +983,7 @@ public:
virtual RID shadow_atlas_create() override;
virtual void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = false) override;
virtual void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override;
- virtual bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override;
+ virtual bool shadow_atlas_update_light(RID p_atlas, RID p_light_instance, float p_coverage, uint64_t p_light_version) override;
_FORCE_INLINE_ bool shadow_atlas_owns_light_instance(RID p_atlas, RID p_light_intance) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_COND_V(!atlas, false);
@@ -1062,7 +1061,7 @@ public:
virtual bool is_environment(RID p_env) const override;
- virtual void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) override;
+ virtual void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) override;
virtual void environment_glow_set_use_bicubic_upscale(bool p_enable) override;
virtual void environment_glow_set_use_high_quality(bool p_enable) override;
@@ -1093,7 +1092,7 @@ public:
bool environment_is_ssr_enabled(RID p_env) const;
bool environment_is_sdfgi_enabled(RID p_env) const;
- virtual void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override;
+ virtual void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override;
virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override;
virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override;
virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override;
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
index f0419b7907..f6f39230f8 100644
--- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
@@ -590,6 +590,7 @@ void RendererSceneSkyRD::Sky::free(RendererStorageRD *p_storage) {
if (material.is_valid()) {
p_storage->free(material);
+ material = RID();
}
}
@@ -750,7 +751,6 @@ RendererStorageRD::ShaderData *RendererSceneSkyRD::_create_sky_shader_funcs() {
RendererStorageRD::MaterialData *RendererSceneSkyRD::_create_sky_material_func(SkyShaderData *p_shader) {
SkyMaterialData *material_data = memnew(SkyMaterialData);
material_data->shader_data = p_shader;
- material_data->last_frame = false;
//update will happen later anyway so do nothing.
return material_data;
}
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h
index 46d376e667..d81a415c2d 100644
--- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h
@@ -228,7 +228,6 @@ public:
} sky_shader;
struct SkyMaterialData : public RendererStorageRD::MaterialData {
- uint64_t last_frame;
SkyShaderData *shader_data;
RID uniform_set;
bool uniform_set_updated;
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index af025dc7bc..145c4f902e 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -324,75 +324,6 @@ Ref<Image> RendererStorageRD::_validate_texture_format(const Ref<Image> &p_image
r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
} break; //unsigned float bc6hu
- case Image::FORMAT_PVRTC1_2: {
- //this is not properly supported by MoltekVK it seems, so best to use ETC2
- if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
- r_format.format = RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG;
- r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
- } else {
- //not supported, reconvert
- r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
- image->decompress();
- image->convert(Image::FORMAT_RGBA8);
- }
- r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
- r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
- r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
- r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
-
- } break; //pvrtc
- case Image::FORMAT_PVRTC1_2A: {
- //this is not properly supported by MoltekVK it seems, so best to use ETC2
- if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
- r_format.format = RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG;
- r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
- } else {
- //not supported, reconvert
- r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
- image->decompress();
- image->convert(Image::FORMAT_RGBA8);
- }
- r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
- r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
- r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
- r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
- } break;
- case Image::FORMAT_PVRTC1_4: {
- //this is not properly supported by MoltekVK it seems, so best to use ETC2
- if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
- r_format.format = RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
- r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG;
- } else {
- //not supported, reconvert
- r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
- image->decompress();
- image->convert(Image::FORMAT_RGBA8);
- }
- r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
- r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
- r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
- r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
- } break;
- case Image::FORMAT_PVRTC1_4A: {
- //this is not properly supported by MoltekVK it seems, so best to use ETC2
- if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
- r_format.format = RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
- r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG;
- } else {
- //not supported, reconvert
- r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
- image->decompress();
- image->convert(Image::FORMAT_RGBA8);
- }
- r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
- r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
- r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
- r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
- } break;
case Image::FORMAT_ETC2_R11: {
if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_EAC_R11_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
r_format.format = RD::DATA_FORMAT_EAC_R11_UNORM_BLOCK;
@@ -2612,7 +2543,7 @@ void RendererStorageRD::MaterialData::update_uniform_buffer(const Map<StringName
}
if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
- continue; //instance uniforms don't appear in the bufferr
+ continue; //instance uniforms don't appear in the buffer
}
if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL) {
@@ -3031,24 +2962,19 @@ bool RendererStorageRD::MaterialData::update_parameters_uniform_set(const Map<St
return true;
}
-void RendererStorageRD::_material_uniform_set_erased(const RID &p_set, void *p_material) {
+void RendererStorageRD::_material_uniform_set_erased(void *p_material) {
RID rid = *(RID *)p_material;
Material *material = base_singleton->material_owner.get_or_null(rid);
if (material) {
+ if (material->data) {
+ // Uniform set may be gone because a dependency was erased. This happens
+ // if a texture is deleted, so re-create it.
+ base_singleton->_material_queue_update(material, false, true);
+ }
material->dependency.changed_notify(DEPENDENCY_CHANGED_MATERIAL);
}
}
-void RendererStorageRD::material_force_update_textures(RID p_material, ShaderType p_shader_type) {
- Material *material = material_owner.get_or_null(p_material);
- if (material->shader_type != p_shader_type) {
- return;
- }
- if (material->data) {
- material->data->update_parameters(material->params, false, true);
- }
-}
-
void RendererStorageRD::_update_queued_materials() {
while (material_update_list.first()) {
Material *material = material_update_list.first()->self();
@@ -5951,8 +5877,6 @@ RendererStorageRD::ShaderData *RendererStorageRD::_create_particles_shader_func(
}
bool RendererStorageRD::ParticlesMaterialData::update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
- uniform_set_updated = true;
-
return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, base_singleton->particles_shader.shader.version_get_shader(shader_data->version, 0), 3);
}
@@ -5963,7 +5887,6 @@ RendererStorageRD::ParticlesMaterialData::~ParticlesMaterialData() {
RendererStorageRD::MaterialData *RendererStorageRD::_create_particles_material_func(ParticlesShaderData *p_shader) {
ParticlesMaterialData *material_data = memnew(ParticlesMaterialData);
material_data->shader_data = p_shader;
- material_data->last_frame = false;
//update will happen later anyway so do nothing.
return material_data;
}
@@ -6315,7 +6238,7 @@ void RendererStorageRD::skeleton_allocate_data(RID p_skeleton, int p_bones, bool
if (skeleton->buffer.is_valid()) {
RD::get_singleton()->free(skeleton->buffer);
skeleton->buffer = RID();
- skeleton->data.resize(0);
+ skeleton->data.clear();
skeleton->uniform_set_mi = RID();
}
@@ -9284,10 +9207,6 @@ bool RendererStorageRD::has_os_feature(const String &p_feature) const {
return true;
}
- if (p_feature == "pvrtc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT)) {
- return true;
- }
-
return false;
}
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h
index 8c04274c3f..43bbcf6520 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.h
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.h
@@ -177,7 +177,7 @@ public:
Vector<RID> texture_cache;
};
typedef MaterialData *(*MaterialDataRequestFunction)(ShaderData *);
- static void _material_uniform_set_erased(const RID &p_set, void *p_material);
+ static void _material_uniform_set_erased(void *p_material);
enum DefaultRDTexture {
DEFAULT_RD_TEXTURE_WHITE,
@@ -910,10 +910,8 @@ private:
}
struct ParticlesMaterialData : public MaterialData {
- uint64_t last_frame = 0;
ParticlesShaderData *shader_data = nullptr;
RID uniform_set;
- bool uniform_set_updated = false;
virtual void set_render_priority(int p_priority) {}
virtual void set_next_pass(RID p_pass) {}
@@ -1448,7 +1446,6 @@ public:
void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters);
void material_update_dependency(RID p_material, DependencyTracker *p_instance);
- void material_force_update_textures(RID p_material, ShaderType p_shader_type);
void material_set_data_request_function(ShaderType p_shader_type, MaterialDataRequestFunction p_function);
diff --git a/servers/rendering/renderer_rd/shaders/light_data_inc.glsl b/servers/rendering/renderer_rd/shaders/light_data_inc.glsl
index fdc7729338..52787bb204 100644
--- a/servers/rendering/renderer_rd/shaders/light_data_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/light_data_inc.glsl
@@ -1,6 +1,6 @@
#define LIGHT_BAKE_DISABLED 0
-#define LIGHT_BAKE_DYNAMIC 1
-#define LIGHT_BAKE_STATIC 2
+#define LIGHT_BAKE_STATIC 1
+#define LIGHT_BAKE_DYNAMIC 2
struct LightData { //this structure needs to be as packed as possible
highp vec3 position;
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
index 608b76b108..97f7e0a6e6 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
@@ -575,7 +575,7 @@ void main() {
uint instance_index = instance_index_interp;
- //lay out everything, whathever is unused is optimized away anyway
+ //lay out everything, whatever is unused is optimized away anyway
vec3 vertex = vertex_interp;
vec3 view = -normalize(vertex_interp);
vec3 albedo = vec3(1.0);
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
index 9e3732fd2b..4d6a3b5864 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
@@ -581,7 +581,7 @@ void main() {
discard;
#endif
- //lay out everything, whathever is unused is optimized away anyway
+ //lay out everything, whatever is unused is optimized away anyway
vec3 vertex = vertex_interp;
vec3 view = -normalize(vertex_interp);
vec3 albedo = vec3(1.0);
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl b/servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl
index 4d9fa85a74..f6ec249b5e 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl
+++ b/servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl
@@ -102,7 +102,7 @@ dispatch_data;
struct ProcessVoxel {
uint position; // xyz 7 bit packed, extra 11 bits for neighbors.
- uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neibhbours
+ uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neighbours
uint light; //rgbe8985 encoded total saved light, extra 2 bits for neighbours
uint light_aniso; //55555 light anisotropy, extra 2 bits for neighbours
//total neighbours: 26
@@ -135,7 +135,7 @@ dispatch_data;
struct ProcessVoxel {
uint position; // xyz 7 bit packed, extra 11 bits for neighbors.
- uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neibhbours
+ uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neighbours
uint light; //rgbe8985 encoded total saved light, extra 2 bits for neighbours
uint light_aniso; //55555 light anisotropy, extra 2 bits for neighbours
//total neighbours: 26
diff --git a/servers/rendering/renderer_rd/shaders/tonemap.glsl b/servers/rendering/renderer_rd/shaders/tonemap.glsl
index 948c6e1e39..41d41758f4 100644
--- a/servers/rendering/renderer_rd/shaders/tonemap.glsl
+++ b/servers/rendering/renderer_rd/shaders/tonemap.glsl
@@ -45,6 +45,7 @@ layout(set = 0, binding = 0) uniform sampler2D source_color;
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
layout(set = 2, binding = 0) uniform sampler2D source_glow;
+layout(set = 2, binding = 1) uniform sampler2D glow_map;
#ifdef USE_1D_LUT
layout(set = 3, binding = 0) uniform sampler2D source_color_correction;
@@ -63,7 +64,7 @@ layout(push_constant, binding = 1, std430) uniform Params {
uvec2 glow_texture_size;
float glow_intensity;
- uint pad3;
+ float glow_map_strength;
uint glow_mode;
float glow_levels[7];
@@ -405,6 +406,9 @@ void main() {
#ifndef SUBPASS
if (params.use_glow && params.glow_mode == GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp) * params.luminance_multiplier;
+ if (params.glow_map_strength > 0.001) {
+ glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
+ }
color.rgb = mix(color.rgb, glow, params.glow_intensity);
}
@@ -425,9 +429,11 @@ void main() {
#ifndef SUBPASS
// Glow
-
if (params.use_glow && params.glow_mode != GLOW_MODE_MIX) {
vec3 glow = gather_glow(source_glow, uv_interp) * params.glow_intensity * params.luminance_multiplier;
+ if (params.glow_map_strength > 0.001) {
+ glow = mix(glow, texture(glow_map, uv_interp).rgb * glow, params.glow_map_strength);
+ }
// high dynamic range -> SRGB
glow = apply_tonemapping(glow, params.white);
diff --git a/servers/rendering/renderer_scene.h b/servers/rendering/renderer_scene.h
index 20ca49cd71..406d946e12 100644
--- a/servers/rendering/renderer_scene.h
+++ b/servers/rendering/renderer_scene.h
@@ -131,7 +131,7 @@ public:
virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0;
virtual void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG) = 0;
- virtual void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) = 0;
+ virtual void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) = 0;
virtual void environment_glow_set_use_bicubic_upscale(bool p_enable) = 0;
virtual void environment_glow_set_use_high_quality(bool p_enable) = 0;
@@ -149,7 +149,7 @@ public:
virtual void environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) = 0;
virtual void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) = 0;
- virtual void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0;
+ virtual void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0;
virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) = 0;
virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0;
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 071d88233f..ed0229f0f9 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -1113,7 +1113,7 @@ public:
PASS6(environment_set_ssil, RID, bool, float, float, float, float)
PASS6(environment_set_ssil_quality, RS::EnvironmentSSILQuality, bool, float, int, float, float)
- PASS11(environment_set_glow, RID, bool, Vector<float>, float, float, float, float, RS::EnvironmentGlowBlendMode, float, float, float)
+ PASS13(environment_set_glow, RID, bool, Vector<float>, float, float, float, float, RS::EnvironmentGlowBlendMode, float, float, float, float, RID)
PASS1(environment_glow_set_use_bicubic_upscale, bool)
PASS1(environment_glow_set_use_high_quality, bool)
@@ -1127,7 +1127,7 @@ public:
PASS2(environment_set_volumetric_fog_volume_size, int, int)
PASS1(environment_set_volumetric_fog_filter_active, bool)
- PASS11(environment_set_sdfgi, RID, bool, RS::EnvironmentSDFGICascades, float, RS::EnvironmentSDFGIYScale, bool, float, bool, float, float, float)
+ PASS11(environment_set_sdfgi, RID, bool, int, float, RS::EnvironmentSDFGIYScale, bool, float, bool, float, float, float)
PASS1(environment_set_sdfgi_ray_count, RS::EnvironmentSDFGIRayCount)
PASS1(environment_set_sdfgi_frames_to_converge, RS::EnvironmentSDFGIFramesToConverge)
PASS1(environment_set_sdfgi_frames_to_update_light, RS::EnvironmentSDFGIFramesToUpdateLight)
diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h
index f99d34d292..3eb90ccc4d 100644
--- a/servers/rendering/renderer_scene_render.h
+++ b/servers/rendering/renderer_scene_render.h
@@ -123,7 +123,7 @@ public:
virtual void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) = 0;
#endif
- virtual void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) = 0;
+ virtual void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) = 0;
virtual void environment_glow_set_use_bicubic_upscale(bool p_enable) = 0;
virtual void environment_glow_set_use_high_quality(bool p_enable) = 0;
@@ -140,7 +140,7 @@ public:
virtual void environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) = 0;
virtual void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) = 0;
- virtual void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0;
+ virtual void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0;
virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) = 0;
virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0;
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 17a665922f..5a84bace2d 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -549,8 +549,13 @@ void RendererViewport::draw_viewports() {
// get our xr interface in case we need it
Ref<XRInterface> xr_interface;
- if (XRServer::get_singleton() != nullptr) {
- xr_interface = XRServer::get_singleton()->get_primary_interface();
+ XRServer *xr_server = XRServer::get_singleton();
+ if (xr_server != nullptr) {
+ // let our XR server know we're about to render our frames so we can get our frame timing
+ xr_server->pre_render();
+
+ // retrieve the interface responsible for rendering
+ xr_interface = xr_server->get_primary_interface();
}
if (Engine::get_singleton()->is_editor_hint()) {
@@ -582,19 +587,26 @@ void RendererViewport::draw_viewports() {
bool visible = vp->viewport_to_screen_rect != Rect2();
- if (vp->use_xr && xr_interface.is_valid()) {
- visible = true; // XR viewport is always visible regardless of update mode, output is sent to HMD.
-
- // Override our size, make sure it matches our required size and is created as a stereo target
- Size2 xr_size = xr_interface->get_render_target_size();
-
- // Would have been nice if we could call viewport_set_size here,
- // but alas that takes our RID and we now have our pointer,
- // also we only check if view_count changes in render_target_set_size so we need to call that for this to reliably change
- vp->occlusion_buffer_dirty = vp->occlusion_buffer_dirty || (vp->size != xr_size);
- vp->size = xr_size;
- uint32_t view_count = xr_interface->get_view_count();
- RSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y, view_count);
+ if (vp->use_xr) {
+ if (xr_interface.is_valid()) {
+ // Override our size, make sure it matches our required size and is created as a stereo target
+ Size2 xr_size = xr_interface->get_render_target_size();
+
+ // Would have been nice if we could call viewport_set_size here,
+ // but alas that takes our RID and we now have our pointer,
+ // also we only check if view_count changes in render_target_set_size so we need to call that for this to reliably change
+ vp->occlusion_buffer_dirty = vp->occlusion_buffer_dirty || (vp->size != xr_size);
+ vp->size = xr_size;
+ uint32_t view_count = xr_interface->get_view_count();
+ RSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y, view_count);
+
+ // Inform xr interface we're about to render its viewport, if this returns false we don't render
+ visible = xr_interface->pre_draw_viewport(vp->render_target);
+ } else {
+ // don't render anything
+ visible = false;
+ vp->size = Size2();
+ }
}
if (vp->update_mode == RS::VIEWPORT_UPDATE_ALWAYS || vp->update_mode == RS::VIEWPORT_UPDATE_ONCE) {
@@ -647,7 +659,7 @@ void RendererViewport::draw_viewports() {
// measure
// commit our eyes
- Vector<BlitToScreen> blits = xr_interface->commit_views(vp->render_target, vp->viewport_to_screen_rect);
+ Vector<BlitToScreen> blits = xr_interface->post_draw_viewport(vp->render_target, vp->viewport_to_screen_rect);
if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && blits.size() > 0) {
if (!blit_to_screen_list.has(vp->viewport_to_screen)) {
blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>();
@@ -657,9 +669,6 @@ void RendererViewport::draw_viewports() {
blit_to_screen_list[vp->viewport_to_screen].push_back(blits[b]);
}
}
-
- // and for our frame timing, mark when we've finished committing our eyes
- XRServer::get_singleton()->_mark_commit();
} else {
RSG::storage->render_target_set_external_texture(vp->render_target, 0);
@@ -813,7 +822,7 @@ void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) {
ERR_FAIL_COND(!viewport);
if (p_active) {
- ERR_FAIL_COND_MSG(active_viewports.find(viewport) != -1, "Can't make active a Viewport that is already active.");
+ ERR_FAIL_COND_MSG(active_viewports.has(viewport), "Can't make active a Viewport that is already active.");
viewport->occlusion_buffer_dirty = true;
active_viewports.push_back(viewport);
} else {
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 88a8dfe694..6fc5d0b3e8 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -451,7 +451,7 @@ void RenderingDevice::_bind_methods() {
ClassDB::bind_method(D_METHOD("compute_list_add_barrier", "compute_list"), &RenderingDevice::compute_list_add_barrier);
ClassDB::bind_method(D_METHOD("compute_list_end", "post_barrier"), &RenderingDevice::compute_list_end, DEFVAL(BARRIER_MASK_ALL));
- ClassDB::bind_method(D_METHOD("free", "rid"), &RenderingDevice::free);
+ ClassDB::bind_method(D_METHOD("free_rid", "rid"), &RenderingDevice::free);
ClassDB::bind_method(D_METHOD("capture_timestamp", "name"), &RenderingDevice::capture_timestamp);
ClassDB::bind_method(D_METHOD("get_captured_timestamps_count"), &RenderingDevice::get_captured_timestamps_count);
@@ -729,14 +729,6 @@ void RenderingDevice::_bind_methods() {
BIND_ENUM_CONSTANT(DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM);
BIND_ENUM_CONSTANT(DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM);
BIND_ENUM_CONSTANT(DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM);
- BIND_ENUM_CONSTANT(DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG);
- BIND_ENUM_CONSTANT(DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG);
- BIND_ENUM_CONSTANT(DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG);
- BIND_ENUM_CONSTANT(DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG);
- BIND_ENUM_CONSTANT(DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG);
- BIND_ENUM_CONSTANT(DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG);
- BIND_ENUM_CONSTANT(DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG);
- BIND_ENUM_CONSTANT(DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG);
BIND_ENUM_CONSTANT(DATA_FORMAT_MAX);
BIND_ENUM_CONSTANT(TEXTURE_TYPE_1D);
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 313c0e11b2..655a32a805 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -392,14 +392,6 @@ public:
DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM,
DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM,
DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM,
- DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG,
- DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG,
- DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG,
- DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG,
- DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG,
- DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG,
- DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG,
- DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG,
DATA_FORMAT_MAX
};
@@ -750,7 +742,7 @@ public:
virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set) = 0;
virtual bool uniform_set_is_valid(RID p_uniform_set) = 0;
- typedef void (*UniformSetInvalidatedCallback)(const RID &, void *);
+ typedef void (*UniformSetInvalidatedCallback)(void *);
virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata) = 0;
virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL) = 0;
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index d7e9d210db..d93aad5d7b 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -93,6 +93,12 @@ void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) {
RSG::rasterizer->end_frame(p_swap_buffers);
+ XRServer *xr_server = XRServer::get_singleton();
+ if (xr_server != nullptr) {
+ // let our XR server know we're done so we can get our frame timing
+ xr_server->end_frame();
+ }
+
RSG::canvas->update_visibility_notifiers();
RSG::scene->update_visibility_notifiers();
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index ead49f053c..6d2c36537b 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -259,7 +259,6 @@ public:
command_queue.push(RSG::storage, &RendererStorage::mesh_initialize, mesh);
command_queue.push(RSG::storage, &RendererStorage::mesh_set_blend_shape_count, mesh, p_blend_shape_count);
for (int i = 0; i < p_surfaces.size(); i++) {
- RSG::storage->mesh_add_surface(mesh, p_surfaces[i]);
command_queue.push(RSG::storage, &RendererStorage::mesh_add_surface, mesh, p_surfaces[i]);
}
}
@@ -629,7 +628,7 @@ public:
FUNC6(environment_set_ssil, RID, bool, float, float, float, float)
FUNC6(environment_set_ssil_quality, EnvironmentSSILQuality, bool, float, int, float, float)
- FUNC11(environment_set_glow, RID, bool, Vector<float>, float, float, float, float, EnvironmentGlowBlendMode, float, float, float)
+ FUNC13(environment_set_glow, RID, bool, Vector<float>, float, float, float, float, EnvironmentGlowBlendMode, float, float, float, float, RID)
FUNC1(environment_glow_set_use_bicubic_upscale, bool)
FUNC1(environment_glow_set_use_high_quality, bool)
@@ -643,7 +642,7 @@ public:
FUNC2(environment_set_volumetric_fog_volume_size, int, int)
FUNC1(environment_set_volumetric_fog_filter_active, bool)
- FUNC11(environment_set_sdfgi, RID, bool, EnvironmentSDFGICascades, float, EnvironmentSDFGIYScale, bool, float, bool, float, float, float)
+ FUNC11(environment_set_sdfgi, RID, bool, int, float, EnvironmentSDFGIYScale, bool, float, bool, float, float, float)
FUNC1(environment_set_sdfgi_ray_count, EnvironmentSDFGIRayCount)
FUNC1(environment_set_sdfgi_frames_to_converge, EnvironmentSDFGIFramesToConverge)
FUNC1(environment_set_sdfgi_frames_to_update_light, EnvironmentSDFGIFramesToUpdateLight)
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 114e7e66cb..43acae42f8 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -692,17 +692,36 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
vcode += _prestr(varying.precision, ShaderLanguage::is_float_type(varying.type));
vcode += _typestr(varying.type);
vcode += " " + _mkid(varying_name);
+ uint32_t inc = 1U;
+
if (varying.array_size > 0) {
+ inc = (uint32_t)varying.array_size;
+
vcode += "[";
vcode += itos(varying.array_size);
vcode += "]";
}
+
+ switch (varying.type) {
+ case SL::TYPE_MAT2:
+ inc *= 2U;
+ break;
+ case SL::TYPE_MAT3:
+ inc *= 3U;
+ break;
+ case SL::TYPE_MAT4:
+ inc *= 4U;
+ break;
+ default:
+ break;
+ }
+
vcode += ";\n";
r_gen_code.stage_globals[STAGE_VERTEX] += "layout(location=" + itos(index) + ") " + interp_mode + "out " + vcode;
r_gen_code.stage_globals[STAGE_FRAGMENT] += "layout(location=" + itos(index) + ") " + interp_mode + "in " + vcode;
- index++;
+ index += inc;
}
if (var_frag_to_light.size() > 0) {
@@ -794,6 +813,9 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
if (bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW || bnode->single_statement) {
code += scode; //use directly
+ if (bnode->use_comma_between_statements && i + 1 < bnode->statements.size()) {
+ code += ",";
+ }
} else {
code += _mktab(p_level) + scode + ";\n";
}
@@ -813,16 +835,49 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
} else {
declaration += _prestr(vdnode->precision) + _typestr(vdnode->datatype);
}
+ declaration += " ";
for (int i = 0; i < vdnode->declarations.size(); i++) {
+ bool is_array = vdnode->declarations[i].size > 0;
if (i > 0) {
declaration += ",";
- } else {
- declaration += " ";
}
declaration += _mkid(vdnode->declarations[i].name);
- if (vdnode->declarations[i].initializer) {
- declaration += "=";
- declaration += _dump_node_code(vdnode->declarations[i].initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+ if (is_array) {
+ declaration += "[";
+ if (vdnode->declarations[i].size_expression != nullptr) {
+ declaration += _dump_node_code(vdnode->declarations[i].size_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+ } else {
+ declaration += itos(vdnode->declarations[i].size);
+ }
+ declaration += "]";
+ }
+
+ if (!is_array || vdnode->declarations[i].single_expression) {
+ if (!vdnode->declarations[i].initializer.is_empty()) {
+ declaration += "=";
+ declaration += _dump_node_code(vdnode->declarations[i].initializer[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+ }
+ } else {
+ int size = vdnode->declarations[i].initializer.size();
+ if (size > 0) {
+ declaration += "=";
+ if (vdnode->datatype == SL::TYPE_STRUCT) {
+ declaration += _mkid(vdnode->struct_name);
+ } else {
+ declaration += _typestr(vdnode->datatype);
+ }
+ declaration += "[";
+ declaration += itos(size);
+ declaration += "]";
+ declaration += "(";
+ for (int j = 0; j < size; j++) {
+ if (j > 0) {
+ declaration += ",";
+ }
+ declaration += _dump_node_code(vdnode->declarations[i].initializer[j], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+ }
+ declaration += ")";
+ }
}
}
@@ -924,58 +979,6 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
code += ")";
} break;
- case SL::Node::TYPE_ARRAY_DECLARATION: {
- SL::ArrayDeclarationNode *adnode = (SL::ArrayDeclarationNode *)p_node;
- String declaration;
- declaration += _constr(adnode->is_const);
- if (adnode->datatype == SL::TYPE_STRUCT) {
- declaration += _mkid(adnode->struct_name);
- } else {
- declaration += _prestr(adnode->precision) + _typestr(adnode->datatype);
- }
- for (int i = 0; i < adnode->declarations.size(); i++) {
- if (i > 0) {
- declaration += ",";
- } else {
- declaration += " ";
- }
- declaration += _mkid(adnode->declarations[i].name);
- declaration += "[";
- if (adnode->size_expression != nullptr) {
- declaration += _dump_node_code(adnode->size_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
- } else {
- declaration += itos(adnode->declarations[i].size);
- }
- declaration += "]";
- if (adnode->declarations[i].single_expression) {
- declaration += "=";
- declaration += _dump_node_code(adnode->declarations[i].initializer[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
- } else {
- int sz = adnode->declarations[i].initializer.size();
- if (sz > 0) {
- declaration += "=";
- if (adnode->datatype == SL::TYPE_STRUCT) {
- declaration += _mkid(adnode->struct_name);
- } else {
- declaration += _typestr(adnode->datatype);
- }
- declaration += "[";
- declaration += itos(sz);
- declaration += "]";
- declaration += "(";
- for (int j = 0; j < sz; j++) {
- declaration += _dump_node_code(adnode->declarations[i].initializer[j], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
- if (j != sz - 1) {
- declaration += ", ";
- }
- }
- declaration += ")";
- }
- }
- }
-
- code += declaration;
- } break;
case SL::Node::TYPE_ARRAY: {
SL::ArrayNode *anode = (SL::ArrayNode *)p_node;
bool use_fragment_varying = false;
@@ -1287,10 +1290,10 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
} else if (cfnode->flow_op == SL::FLOW_OP_FOR) {
String left = _dump_node_code(cfnode->blocks[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
- String middle = _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
- String right = _dump_node_code(cfnode->expressions[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+ String middle = _dump_node_code(cfnode->blocks[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+ String right = _dump_node_code(cfnode->blocks[2], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
code += _mktab(p_level) + "for (" + left + ";" + middle + ";" + right + ")\n";
- code += _dump_node_code(cfnode->blocks[1], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
+ code += _dump_node_code(cfnode->blocks[3], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
} else if (cfnode->flow_op == SL::FLOW_OP_RETURN) {
if (cfnode->expressions.size()) {
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 8a65faea61..2e1ee41406 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -2742,13 +2742,13 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
//stage based function
const StageFunctionInfo &sf = p_function_info.stage_functions[name];
if (argcount != sf.arguments.size()) {
- _set_error(vformat("Invalid number of arguments when calling stage function '%s', which expects %d arguments.", String(name), sf.arguments.size()));
+ _set_error(vformat(RTR("Invalid number of arguments when calling stage function '%s', which expects %d arguments."), String(name), sf.arguments.size()));
return false;
}
//validate arguments
for (int i = 0; i < argcount; i++) {
if (args[i] != sf.arguments[i].type) {
- _set_error(vformat("Invalid argument type when calling stage function '%s', type expected is '%s'.", String(name), String(get_datatype_name(sf.arguments[i].type))));
+ _set_error(vformat(RTR("Invalid argument type when calling stage function '%s', type expected is '%s'."), String(name), get_datatype_name(sf.arguments[i].type)));
return false;
}
}
@@ -2852,7 +2852,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
}
if (error) {
- _set_error(vformat("Expected integer constant within %s..%s range.", min, max));
+ _set_error(vformat(RTR("Expected integer constant within [%d..%d] range."), min, max));
return false;
}
}
@@ -2871,7 +2871,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
if (arg_idx < argcount) {
if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) {
- _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member.");
+ _set_error(vformat(RTR("Argument %d of function '%s' is not a variable, array, or member."), arg_idx + 1, String(name)));
return false;
}
@@ -2895,7 +2895,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
fail = true;
} else {
if (shader->varyings.has(varname)) {
- _set_error(vformat("Varyings cannot be passed for '%s' parameter!", "out"));
+ _set_error(vformat(RTR("Varyings cannot be passed for the '%s' parameter."), "out"));
return false;
}
if (p_function_info.built_ins.has(varname)) {
@@ -2908,7 +2908,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
}
if (fail) {
- _set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out"));
+ _set_error(vformat(RTR("A constant value cannot be passed for the '%s' parameter."), "out"));
return false;
}
@@ -2921,7 +2921,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
n = static_cast<const MemberNode *>(n)->owner;
}
if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) {
- _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member.");
+ _set_error(vformat(RTR("Argument %d of function '%s' is not a variable, array, or member."), arg_idx + 1, String(name)));
return false;
}
if (n->type == Node::TYPE_VARIABLE) {
@@ -2951,7 +2951,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
if (!valid) {
- _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member.");
+ _set_error(vformat(RTR("Argument %d of function '%s' can only take a local variable, array, or member."), arg_idx + 1, String(name)));
return false;
}
}
@@ -2998,16 +2998,15 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
arglist += get_datatype_name(builtin_func_defs[builtin_idx].args[i]);
}
- String err = "Built-in function \"" + String(name) + "(" + arglist + ")\" is supported only on high-end platform!";
- _set_error(err);
+ _set_error(vformat(RTR("Built-in function \"%s(%s)\" is only supported on high-end platforms."), String(name), arglist));
return false;
}
if (failed_builtin) {
- String err = "Invalid arguments for built-in function: " + String(name) + "(";
+ String arg_list;
for (int i = 0; i < argcount; i++) {
if (i > 0) {
- err += ",";
+ arg_list += ",";
}
String arg_name;
@@ -3021,10 +3020,9 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
arg_name += itos(args3[i]);
arg_name += "]";
}
- err += arg_name;
+ arg_list += arg_name;
}
- err += ")";
- _set_error(err);
+ _set_error(vformat(RTR("Invalid arguments for the built-in function: \"%s(%s)\"."), String(name), arg_list));
return false;
}
@@ -3041,7 +3039,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
if (name == exclude_function) {
- _set_error("Recursion is not allowed");
+ _set_error(RTR("Recursion is not allowed."));
return false;
}
@@ -3054,7 +3052,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
if (!shader->functions[i].callable) {
- _set_error("Function '" + String(name) + " can't be called from source code.");
+ _set_error(vformat(RTR("Function '%s' can't be called from source code."), String(name)));
return false;
}
@@ -3113,7 +3111,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
arg_name += "]";
}
- _set_error(vformat("Invalid argument for \"%s(%s)\" function: argument %s should be %s but is %s.", String(name), arg_list, j + 1, func_arg_name, arg_name));
+ _set_error(vformat(RTR("Invalid argument for \"%s(%s)\" function: argument %d should be %s but is %s."), String(name), arg_list, j + 1, func_arg_name, arg_name));
fail = true;
break;
}
@@ -3150,9 +3148,9 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
if (last_arg_count > args.size()) {
- _set_error(vformat("Too few arguments for \"%s(%s)\" call. Expected at least %s but received %s.", String(name), arg_list, last_arg_count, args.size()));
+ _set_error(vformat(RTR("Too few arguments for \"%s(%s)\" call. Expected at least %d but received %d."), String(name), arg_list, last_arg_count, args.size()));
} else if (last_arg_count < args.size()) {
- _set_error(vformat("Too many arguments for \"%s(%s)\" call. Expected at most %s but received %s.", String(name), arg_list, last_arg_count, args.size()));
+ _set_error(vformat(RTR("Too many arguments for \"%s(%s)\" call. Expected at most %d but received %d."), String(name), arg_list, last_arg_count, args.size()));
}
return false;
@@ -3190,7 +3188,7 @@ bool ShaderLanguage::_compare_datatypes(DataType p_datatype_a, String p_datatype
type_name2 += "]";
}
- _set_error("Invalid assignment of '" + type_name2 + "' to '" + type_name + "'");
+ _set_error(vformat(RTR("Invalid assignment of '%s' to '%s'."), type_name2, type_name));
}
return result;
}
@@ -3235,7 +3233,7 @@ bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Functio
return true;
} else if (tk.type != TK_COMMA) {
// something is broken
- _set_error("Expected ',' or ')' after argument");
+ _set_error(RTR("Expected ',' or ')' after argument."));
return false;
}
}
@@ -4247,14 +4245,14 @@ bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(StringNam
ERR_FAIL_INDEX_V(p_argument, shader->functions[i].function->arguments.size(), false);
FunctionNode::Argument *arg = &shader->functions[i].function->arguments.write[p_argument];
if (arg->tex_builtin_check) {
- _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other).");
+ _set_error(vformat(RTR("Sampler argument %d of function '%s' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other)."), p_argument, String(p_name)));
return false;
} else if (arg->tex_argument_check) {
//was checked, verify that filter and repeat are the same
if (arg->tex_argument_filter == p_filter && arg->tex_argument_repeat == p_repeat) {
return true;
} else {
- _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using textures that differ in either filter or repeat setting.");
+ _set_error(vformat(RTR("Sampler argument %d of function '%s' called more than once using textures that differ in either filter or repeat setting."), p_argument, String(p_name)));
return false;
}
} else {
@@ -4281,14 +4279,14 @@ bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringNa
ERR_FAIL_INDEX_V(p_argument, shader->functions[i].function->arguments.size(), false);
FunctionNode::Argument *arg = &shader->functions[i].function->arguments.write[p_argument];
if (arg->tex_argument_check) {
- _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other).");
+ _set_error(vformat(RTR("Sampler argument %d of function '%s' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other)."), p_argument, String(p_name)));
return false;
} else if (arg->tex_builtin_check) {
//was checked, verify that the built-in is the same
if (arg->tex_builtin == p_builtin) {
return true;
} else {
- _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using different built-ins. Only calling with the same built-in is supported.");
+ _set_error(vformat(RTR("Sampler argument %d of function '%s' called more than once using different built-ins. Only calling with the same built-in is supported."), p_argument, String(p_name)));
return false;
}
} else {
@@ -4309,114 +4307,87 @@ bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringNa
ERR_FAIL_V(false); //bug? function not found
}
-ShaderLanguage::Node *ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, int &r_array_size) {
- int array_size = 0;
-
- Node *n = _parse_and_reduce_expression(p_block, p_function_info);
- if (n) {
- if (n->type == Node::TYPE_VARIABLE) {
- VariableNode *vn = static_cast<VariableNode *>(n);
- if (vn) {
- ConstantNode::Value v;
- DataType data_type;
- bool is_const = false;
-
- _find_identifier(p_block, false, p_function_info, vn->name, &data_type, nullptr, &is_const, nullptr, nullptr, &v);
-
- if (is_const) {
- if (data_type == TYPE_INT) {
- int32_t value = v.sint;
- if (value > 0) {
- array_size = value;
- }
- } else if (data_type == TYPE_UINT) {
- uint32_t value = v.uint;
- if (value > 0U) {
- array_size = value;
- }
- }
- }
- }
- } else if (n->type == Node::TYPE_OPERATOR) {
- _set_error("Array size expressions are not yet implemented.");
- return nullptr;
- }
+Error ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_forbid_unknown_size, Node **r_size_expression, int *r_array_size, bool *r_unknown_size) {
+ bool error = false;
+ if (r_array_size != nullptr && *r_array_size > 0) {
+ error = true;
}
-
- r_array_size = array_size;
- return n;
-}
-
-Error ShaderLanguage::_parse_global_array_size(int &r_array_size, const FunctionInfo &p_function_info) {
- if (r_array_size > 0) {
- _set_error("Array size is already defined!");
- return ERR_PARSE_ERROR;
+ if (r_unknown_size != nullptr && *r_unknown_size) {
+ error = true;
}
- TkPos pos = _get_tkpos();
- Token tk = _get_token();
-
- int array_size = 0;
-
- if (!tk.is_integer_constant() || ((int)tk.constant) <= 0) {
- _set_tkpos(pos);
- Node *n = _parse_array_size(nullptr, p_function_info, array_size);
- if (!n) {
- return ERR_PARSE_ERROR;
- }
- } else if (((int)tk.constant) > 0) {
- array_size = (uint32_t)tk.constant;
- }
-
- if (array_size <= 0) {
- _set_error("Expected single integer constant > 0");
+ if (error) {
+ _set_error(vformat(RTR("Array size is already defined.")));
return ERR_PARSE_ERROR;
}
- tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return ERR_PARSE_ERROR;
- }
-
- r_array_size = array_size;
- return OK;
-}
-
-Error ShaderLanguage::_parse_local_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, Node *&r_size_expression, int &r_array_size, bool &r_is_unknown_size) {
TkPos pos = _get_tkpos();
Token tk = _get_token();
if (tk.type == TK_BRACKET_CLOSE) {
- r_is_unknown_size = true;
+ if (p_forbid_unknown_size) {
+ _set_error(vformat(RTR("Unknown array size is forbidden in that context.")));
+ return ERR_PARSE_ERROR;
+ }
+ if (r_unknown_size != nullptr) {
+ *r_unknown_size = true;
+ }
} else {
- int size = 0;
+ int array_size = 0;
+
if (!tk.is_integer_constant() || ((int)tk.constant) <= 0) {
_set_tkpos(pos);
- int array_size = 0;
- Node *n = _parse_array_size(p_block, p_function_info, array_size);
- if (!n) {
- return ERR_PARSE_ERROR;
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
+ if (n) {
+ if (n->type == Node::TYPE_VARIABLE) {
+ VariableNode *vn = static_cast<VariableNode *>(n);
+ if (vn) {
+ ConstantNode::Value v;
+ DataType data_type;
+ bool is_const = false;
+
+ _find_identifier(p_block, false, p_function_info, vn->name, &data_type, nullptr, &is_const, nullptr, nullptr, &v);
+
+ if (is_const) {
+ if (data_type == TYPE_INT) {
+ int32_t value = v.sint;
+ if (value > 0) {
+ array_size = value;
+ }
+ } else if (data_type == TYPE_UINT) {
+ uint32_t value = v.uint;
+ if (value > 0U) {
+ array_size = value;
+ }
+ }
+ }
+ }
+ } else if (n->type == Node::TYPE_OPERATOR) {
+ _set_error(vformat(RTR("Array size expressions are not supported.")));
+ return ERR_PARSE_ERROR;
+ }
+ if (r_size_expression != nullptr) {
+ *r_size_expression = n;
+ }
}
- size = array_size;
- r_size_expression = n;
} else if (((int)tk.constant) > 0) {
- size = (uint32_t)tk.constant;
+ array_size = (uint32_t)tk.constant;
}
- if (size <= 0) {
- _set_error("Expected single integer constant > 0");
+ if (array_size <= 0) {
+ _set_error(RTR("Expected a positive integer constant."));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
+ _set_expected_error("]");
return ERR_PARSE_ERROR;
}
- r_array_size = size;
+ if (r_array_size != nullptr) {
+ *r_array_size = array_size;
+ }
}
-
return OK;
}
@@ -4436,49 +4407,20 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
struct_name = tk.text;
} else {
if (!is_token_variable_datatype(tk.type)) {
- _set_error("Invalid data type for array");
+ _set_error(RTR("Invalid data type for the array."));
return nullptr;
}
type = get_token_datatype(tk.type);
}
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- TkPos pos = _get_tkpos();
- tk = _get_token();
- if (tk.type == TK_BRACKET_CLOSE) {
- undefined_size = true;
- tk = _get_token();
- } else {
- _set_tkpos(pos);
-
- Node *n = _parse_and_reduce_expression(p_block, p_function_info);
- if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
- _set_error("Expected single integer constant > 0");
- return nullptr;
- }
-
- ConstantNode *cnode = (ConstantNode *)n;
- if (cnode->values.size() == 1) {
- array_size = cnode->values[0].sint;
- if (array_size <= 0) {
- _set_error("Expected single integer constant > 0");
- return nullptr;
- }
- } else {
- _set_error("Expected single integer constant > 0");
- return nullptr;
- }
-
- tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return nullptr;
- } else {
- tk = _get_token();
- }
+ Error error = _parse_array_size(p_block, p_function_info, false, nullptr, &array_size, &undefined_size);
+ if (error != OK) {
+ return nullptr;
}
+ tk = _get_token();
} else {
- _set_error("Expected '['");
+ _set_expected_error("[");
return nullptr;
}
}
@@ -4516,20 +4458,20 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
break;
} else {
if (auto_size) {
- _set_error("Expected '}' or ','");
+ _set_expected_error("}", ",");
} else {
- _set_error("Expected ')' or ','");
+ _set_expected_error(")", ",");
}
return nullptr;
}
idx++;
}
if (!auto_size && !undefined_size && an->initializer.size() != array_size) {
- _set_error("Array size mismatch");
+ _set_error(RTR("Array size mismatch."));
return nullptr;
}
} else {
- _set_error("Expected array initialization!");
+ _set_error(RTR("Expected array initialization."));
return nullptr;
}
@@ -4559,7 +4501,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
- _set_error("Invalid data type for array");
+ _set_error(RTR("Invalid data type for the array."));
return nullptr;
}
@@ -4572,65 +4514,42 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
}
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- TkPos pos = _get_tkpos();
- tk = _get_token();
- if (tk.type == TK_BRACKET_CLOSE) {
+ bool is_unknown_size = false;
+ Error error = _parse_array_size(p_block, p_function_info, false, nullptr, &array_size, &is_unknown_size);
+ if (error != OK) {
+ return nullptr;
+ }
+ if (is_unknown_size) {
array_size = p_array_size;
- tk = _get_token();
- } else {
- _set_tkpos(pos);
-
- Node *n = _parse_and_reduce_expression(p_block, p_function_info);
- if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
- _set_error("Expected single integer constant > 0");
- return nullptr;
- }
-
- ConstantNode *cnode = (ConstantNode *)n;
- if (cnode->values.size() == 1) {
- array_size = cnode->values[0].sint;
- if (array_size <= 0) {
- _set_error("Expected single integer constant > 0");
- return nullptr;
- }
- } else {
- _set_error("Expected single integer constant > 0");
- return nullptr;
- }
-
- tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return nullptr;
- } else {
- tk = _get_token();
- }
}
+ tk = _get_token();
} else {
- _set_error("Expected '['");
+ _set_expected_error("[");
return nullptr;
}
if (type != p_type || struct_name != p_struct_name || array_size != p_array_size) {
- String error_str = "Cannot convert from '";
+ String from;
if (type == TYPE_STRUCT) {
- error_str += struct_name;
+ from += struct_name;
} else {
- error_str += get_datatype_name(type);
+ from += get_datatype_name(type);
}
- error_str += "[";
- error_str += itos(array_size);
- error_str += "]'";
- error_str += " to '";
+ from += "[";
+ from += itos(array_size);
+ from += "]'";
+
+ String to;
if (type == TYPE_STRUCT) {
- error_str += p_struct_name;
+ to += p_struct_name;
} else {
- error_str += get_datatype_name(p_type);
+ to += get_datatype_name(p_type);
}
- error_str += "[";
- error_str += itos(p_array_size);
- error_str += "]'";
- _set_error(error_str);
+ to += "[";
+ to += itos(p_array_size);
+ to += "]'";
+
+ _set_error(vformat(RTR("Cannot convert from '%s' to '%s'."), from, to));
return nullptr;
}
}
@@ -4661,19 +4580,19 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
break;
} else {
if (auto_size) {
- _set_error("Expected '}' or ','");
+ _set_expected_error("}", ",");
} else {
- _set_error("Expected ')' or ','");
+ _set_expected_error(")", ",");
}
return nullptr;
}
}
if (an->initializer.size() != p_array_size) {
- _set_error("Array size mismatch");
+ _set_error(RTR("Array size mismatch."));
return nullptr;
}
} else {
- _set_error("Expected array initialization!");
+ _set_error(RTR("Expected array initialization."));
return nullptr;
}
@@ -4704,7 +4623,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
tk = _get_token();
if (tk.type != TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')' in expression");
+ _set_error(RTR("Expected ')' in expression."));
return nullptr;
}
@@ -4752,7 +4671,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_TYPE_VOID) {
//make sure void is not used in expression
- _set_error("Void value not allowed in Expression");
+ _set_error(RTR("Void value not allowed in expression."));
return nullptr;
} else if (is_token_nonvoid_datatype(tk.type) || tk.type == TK_CURLY_BRACKET_OPEN) {
if (tk.type == TK_CURLY_BRACKET_OPEN) {
@@ -4781,7 +4700,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expr = _parse_array_constructor(p_block, p_function_info);
} else {
if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after type name");
+ _set_error(RTR("Expected '(' after the type name."));
return nullptr;
}
//basic type constructor
@@ -4814,7 +4733,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) {
- _set_error("No matching constructor found for: '" + String(funcname->name) + "'");
+ _set_error(vformat(RTR("No matching constructor found for: '%s'."), String(funcname->name)));
return nullptr;
}
@@ -4871,7 +4790,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (i + 1 < pstruct->members.size()) {
tk = _get_token();
if (tk.type != TK_COMMA) {
- _set_error("Expected ','");
+ _set_expected_error(",");
return nullptr;
}
}
@@ -4879,7 +4798,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')'");
+ _set_expected_error(")");
return nullptr;
}
@@ -4903,7 +4822,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
ShaderLanguage::BlockNode *bnode = p_block;
while (bnode) {
if (bnode->variables.has(name)) {
- _set_error("Expected function name");
+ _set_error(RTR("Expected a function name."));
return nullptr;
}
bnode = bnode->parent_block;
@@ -4940,7 +4859,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) {
- _set_error("No matching function found for: '" + String(funcname->name) + "'");
+ _set_error(vformat(RTR("No matching function found for: '%s'."), String(funcname->name)));
return nullptr;
}
completion_class = TAG_GLOBAL; // reset sub-class
@@ -4993,7 +4912,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (shader->varyings.has(varname)) {
switch (shader->varyings[varname].stage) {
case ShaderNode::Varying::STAGE_UNKNOWN: {
- _set_error(vformat("Varying '%s' must be assigned in the vertex or fragment function first!", varname));
+ _set_error(vformat(RTR("Varying '%s' must be assigned in the vertex or fragment function first."), varname));
return nullptr;
}
case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT:
@@ -5019,7 +4938,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (error) {
- _set_error(vformat("Varying '%s' cannot be passed for the '%s' parameter in that context!", varname, _get_qualifier_str(arg_qual)));
+ _set_error(vformat(RTR("Varying '%s' cannot be passed for the '%s' parameter in that context."), varname, _get_qualifier_str(arg_qual)));
return nullptr;
}
}
@@ -5066,7 +4985,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (error) {
- _set_error(vformat("Constant value cannot be passed for '%s' parameter!", _get_qualifier_str(arg_qual)));
+ _set_error(vformat(RTR("A constant value cannot be passed for '%s' parameter."), _get_qualifier_str(arg_qual)));
return nullptr;
}
}
@@ -5148,12 +5067,12 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
idx++;
}
if (!found) {
- _set_error("Unknown identifier in expression: " + String(identifier));
+ _set_error(vformat(RTR("Unknown identifier in expression: '%s'."), String(identifier)));
return nullptr;
}
} else {
if (!_find_identifier(p_block, false, p_function_info, identifier, &data_type, &ident_type, &is_const, &array_size, &struct_name)) {
- _set_error("Unknown identifier in expression: " + String(identifier));
+ _set_error(vformat(RTR("Unknown identifier in expression: '%s'."), String(identifier)));
return nullptr;
}
if (ident_type == IDENTIFIER_VARYING) {
@@ -5195,7 +5114,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (ident_type == IDENTIFIER_FUNCTION) {
- _set_error("Can't use function as identifier: " + String(identifier));
+ _set_error(vformat(RTR("Can't use function as identifier: '%s'."), String(identifier)));
return nullptr;
}
if (is_const) {
@@ -5217,7 +5136,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (tk.type == TK_OP_ASSIGN) {
if (is_const) {
- _set_error("Constants cannot be modified.");
+ _set_error(RTR("Constants cannot be modified."));
return nullptr;
}
assign_expression = _parse_array_constructor(p_block, p_function_info, data_type, struct_name, array_size);
@@ -5240,7 +5159,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (index_expression->get_array_size() != 0 || (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT)) {
- _set_error("Only integer expressions are allowed for indexing.");
+ _set_error(RTR("Only integer expressions are allowed for indexing."));
return nullptr;
}
@@ -5250,7 +5169,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (!cnode->values.is_empty()) {
int value = cnode->values[0].sint;
if (value < 0 || value >= array_size) {
- _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1));
+ _set_error(vformat(RTR("Index [%d] out of range [%d..%d]."), value, 0, array_size - 1));
return nullptr;
}
}
@@ -5259,7 +5178,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
tk = _get_token();
if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
+ _set_expected_error("]");
return nullptr;
}
} else {
@@ -5289,9 +5208,15 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
#ifdef DEBUG_ENABLED
if (check_warnings) {
StringName func_name;
+ BlockNode *b = p_block;
- if (p_block && p_block->parent_function) {
- func_name = p_block->parent_function->name;
+ while (b) {
+ if (b->parent_function) {
+ func_name = b->parent_function->name;
+ break;
+ } else {
+ b = b->parent_block;
+ }
}
_parse_used_identifier(identifier, ident_type, func_name);
@@ -5327,21 +5252,32 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expression.push_back(e);
continue;
} else {
- if (tk.type != TK_SEMICOLON) {
- _set_error("Expected expression, found: " + get_token_text(tk));
- return nullptr;
- } else {
-#ifdef DEBUG_ENABLED
- if (check_warnings && HAS_WARNING(ShaderWarning::FORMATTING_ERROR_FLAG)) {
- _add_line_warning(ShaderWarning::FORMATTING_ERROR, "Empty statement. Remove ';' to fix this warning.");
- }
-#endif // DEBUG_ENABLED
+ bool valid = false;
+ if (p_block && p_block->block_type == BlockNode::BLOCK_TYPE_FOR_EXPRESSION && tk.type == TK_PARENTHESIS_CLOSE) {
+ valid = true;
_set_tkpos(prepos);
OperatorNode *func = alloc_node<OperatorNode>();
func->op = OP_EMPTY;
expr = func;
}
+ if (!valid) {
+ if (tk.type != TK_SEMICOLON) {
+ _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk)));
+ return nullptr;
+ } else {
+#ifdef DEBUG_ENABLED
+ if (check_warnings && HAS_WARNING(ShaderWarning::FORMATTING_ERROR_FLAG)) {
+ _add_line_warning(ShaderWarning::FORMATTING_ERROR, RTR("Empty statement. Remove ';' to fix this warning."));
+ }
+#endif // DEBUG_ENABLED
+ _set_tkpos(prepos);
+
+ OperatorNode *func = alloc_node<OperatorNode>();
+ func->op = OP_EMPTY;
+ expr = func;
+ }
+ }
}
ERR_FAIL_COND_V(!expr, nullptr);
@@ -5380,7 +5316,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (identifier == StringName()) {
- _set_error("Expected identifier as member");
+ _set_error(RTR("Expected an identifier as a member."));
return nullptr;
}
String ident = identifier;
@@ -5625,12 +5561,12 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (mix_error) {
- _set_error("Cannot combine symbols from different sets in expression ." + ident);
+ _set_error(vformat(RTR("Cannot combine symbols from different sets in expression '.%s'."), ident));
return nullptr;
}
if (!ok) {
- _set_error("Invalid member for " + (dt == TYPE_STRUCT ? st : get_datatype_name(dt)) + " expression: ." + ident);
+ _set_error(vformat(RTR("Invalid member for '%s' expression: '.%s'."), (dt == TYPE_STRUCT ? st : get_datatype_name(dt)), ident));
return nullptr;
}
@@ -5650,7 +5586,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
tk = _get_token();
if (tk.type == TK_OP_ASSIGN) {
if (last_type == IDENTIFIER_CONSTANT) {
- _set_error("Constants cannot be modified.");
+ _set_error(RTR("Constants cannot be modified."));
return nullptr;
}
Node *assign_expression = _parse_array_constructor(p_block, p_function_info, member_type, member_struct_name, array_size);
@@ -5675,7 +5611,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (index_expression->get_array_size() != 0 || (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT)) {
- _set_error("Only integer expressions are allowed for indexing.");
+ _set_error(RTR("Only integer expressions are allowed for indexing."));
return nullptr;
}
@@ -5685,7 +5621,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (!cnode->values.is_empty()) {
int value = cnode->values[0].sint;
if (value < 0 || value >= array_size) {
- _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1));
+ _set_error(vformat(RTR("Index [%d] out of range [%d..%d]."), value, 0, array_size - 1));
return nullptr;
}
}
@@ -5694,7 +5630,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
tk = _get_token();
if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
+ _set_expected_error("]");
return nullptr;
}
mn->index_expression = index_expression;
@@ -5721,7 +5657,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (index->get_array_size() != 0 || (index->get_datatype() != TYPE_INT && index->get_datatype() != TYPE_UINT)) {
- _set_error("Only integer expressions are allowed for indexing.");
+ _set_error(RTR("Only integer expressions are allowed for indexing."));
return nullptr;
}
@@ -5732,7 +5668,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (index->type == Node::TYPE_CONSTANT) {
uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
if (index_constant >= (uint32_t)expr->get_array_size()) {
- _set_error(vformat("Index [%s] out of range [%s..%s]", index_constant, 0, expr->get_array_size() - 1));
+ _set_error(vformat(RTR("Index [%d] out of range [%d..%d]."), index_constant, 0, expr->get_array_size() - 1));
return nullptr;
}
}
@@ -5750,7 +5686,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (index->type == Node::TYPE_CONSTANT) {
uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
if (index_constant >= 2) {
- _set_error("Index out of range (0-1)");
+ _set_error(vformat(RTR("Index [%d] out of range [%d..%d]."), index_constant, 0, 1));
return nullptr;
}
}
@@ -5784,7 +5720,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (index->type == Node::TYPE_CONSTANT) {
uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
if (index_constant >= 3) {
- _set_error("Index out of range (0-2)");
+ _set_error(vformat(RTR("Index [%d] out of range [%d..%d]."), index_constant, 0, 2));
return nullptr;
}
}
@@ -5817,7 +5753,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (index->type == Node::TYPE_CONSTANT) {
uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
if (index_constant >= 4) {
- _set_error("Index out of range (0-3)");
+ _set_error(vformat(RTR("Index [%d] out of range [%d..%d]."), index_constant, 0, 3));
return nullptr;
}
}
@@ -5843,7 +5779,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
break;
default: {
- _set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed");
+ _set_error(vformat(RTR("An object of type '%s' can't be indexed."), (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype()))));
return nullptr;
}
}
@@ -5858,7 +5794,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
tk = _get_token();
if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']' after indexing expression");
+ _set_expected_error("]");
return nullptr;
}
@@ -5868,12 +5804,12 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
op->arguments.push_back(expr);
if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
- _set_error("Invalid base type for increment/decrement operator");
+ _set_error(RTR("Invalid base type for increment/decrement operator."));
return nullptr;
}
if (!_validate_assign(expr, p_function_info)) {
- _set_error("Invalid use of increment/decrement operator in constant expression.");
+ _set_error(RTR("Invalid use of increment/decrement operator in a constant expression."));
return nullptr;
}
expr = op;
@@ -5990,7 +5926,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
o.op = OP_SELECT_ELSE;
break;
default: {
- _set_error("Invalid token for operator: " + get_token_text(tk));
+ _set_error(vformat(RTR("Invalid token for the operator: '%s'."), get_token_text(tk)));
return nullptr;
}
}
@@ -6169,7 +6105,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expr_pos++;
if (expr_pos == expression.size()) {
//can happen..
- _set_error("Unexpected end of expression...");
+ _set_error(RTR("Unexpected end of expression."));
return nullptr;
}
}
@@ -6179,7 +6115,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
OperatorNode *op = alloc_node<OperatorNode>();
op->op = expression[i].op;
if ((op->op == OP_INCREMENT || op->op == OP_DECREMENT) && !_validate_assign(expression[i + 1].node, p_function_info)) {
- _set_error("Can't use increment/decrement operator in constant expression.");
+ _set_error(RTR("Can't use increment/decrement operator in a constant expression."));
return nullptr;
}
op->arguments.push_back(expression[i + 1].node);
@@ -6191,7 +6127,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
String at;
for (int j = 0; j < op->arguments.size(); j++) {
if (j > 0) {
- at += " and ";
+ at += ", ";
}
at += get_datatype_name(op->arguments[j]->get_datatype());
if (!op->arguments[j]->is_indexed() && op->arguments[j]->get_array_size() > 0) {
@@ -6200,7 +6136,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
at += "]";
}
}
- _set_error("Invalid arguments to unary operator '" + get_operator_text(op->op) + "' :" + at);
+ _set_error(vformat(RTR("Invalid arguments to unary operator '%s': %s."), get_operator_text(op->op), at));
return nullptr;
}
expression.remove_at(i + 1);
@@ -6208,12 +6144,12 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (is_ternary) {
if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug...");
+ _set_parsing_error();
ERR_FAIL_V(nullptr);
}
if (next_op + 2 >= expression.size() || !expression[next_op + 2].is_op || expression[next_op + 2].op != OP_SELECT_ELSE) {
- _set_error("Missing matching ':' for select operator");
+ _set_error(RTR("Missing matching ':' for select operator."));
return nullptr;
}
@@ -6229,7 +6165,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
String at;
for (int i = 0; i < op->arguments.size(); i++) {
if (i > 0) {
- at += " and ";
+ at += ", ";
}
at += get_datatype_name(op->arguments[i]->get_datatype());
if (!op->arguments[i]->is_indexed() && op->arguments[i]->get_array_size() > 0) {
@@ -6238,7 +6174,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
at += "]";
}
}
- _set_error("Invalid argument to ternary ?: operator: " + at);
+ _set_error(vformat(RTR("Invalid argument to ternary operator: '%s'."), at));
return nullptr;
}
@@ -6248,7 +6184,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else {
if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug...");
+ _set_parsing_error();
ERR_FAIL_V(nullptr);
}
@@ -6256,7 +6192,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
op->op = expression[next_op].op;
if (expression[next_op - 1].is_op) {
- _set_error("Parser bug...");
+ _set_parsing_error();
ERR_FAIL_V(nullptr);
}
@@ -6274,7 +6210,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
// can be followed by a unary op in a valid combination,
// due to how precedence works, unaries will always disappear first
- _set_error("Parser bug...");
+ _set_parsing_error();
}
op->arguments.push_back(expression[next_op - 1].node); //expression goes as left
@@ -6287,7 +6223,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
String at;
for (int i = 0; i < op->arguments.size(); i++) {
if (i > 0) {
- at += " and ";
+ at += ", ";
}
if (op->arguments[i]->get_datatype() == TYPE_STRUCT) {
at += op->arguments[i]->get_datatype_name();
@@ -6300,7 +6236,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
at += "]";
}
}
- _set_error("Invalid arguments to operator '" + get_operator_text(op->op) + "' :" + at);
+ _set_error(vformat(RTR("Invalid arguments to operator '%s': '%s'."), get_operator_text(op->op), at));
return nullptr;
}
@@ -6440,7 +6376,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (p_block && p_block->block_type == BlockNode::BLOCK_TYPE_SWITCH) {
if (tk.type != TK_CF_CASE && tk.type != TK_CF_DEFAULT && tk.type != TK_CURLY_BRACKET_CLOSE) {
- _set_error("Switch may contains only case and default blocks");
+ _set_error(vformat(RTR("A switch may only contain '%s' and '%s' blocks."), "case", "default"));
return ERR_PARSE_ERROR;
}
}
@@ -6449,7 +6385,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (tk.type == TK_CURLY_BRACKET_CLOSE) { //end of block
if (p_just_one) {
- _set_error("Unexpected '}'");
+ _set_expected_error("}");
return ERR_PARSE_ERROR;
}
@@ -6487,18 +6423,18 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
is_struct = shader->structs.has(tk.text); // check again.
}
if (is_struct && precision != PRECISION_DEFAULT) {
- _set_error("Precision modifier cannot be used on structs.");
+ _set_error(RTR("The precision modifier cannot be used on structs."));
return ERR_PARSE_ERROR;
}
if (!is_token_nonvoid_datatype(tk.type)) {
- _set_error("Expected datatype after precision");
+ _set_error(RTR("Expected variable type after precision modifier."));
return ERR_PARSE_ERROR;
}
}
if (!is_struct) {
if (!is_token_variable_datatype(tk.type)) {
- _set_error("Invalid data type for variable (samplers not allowed)");
+ _set_error(RTR("Invalid variable type (samplers are not allowed)."));
return ERR_PARSE_ERROR;
}
}
@@ -6509,17 +6445,23 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
- Node *vardecl = nullptr;
int array_size = 0;
bool fixed_array_size = false;
bool first = true;
+ VariableDeclarationNode *vdnode = alloc_node<VariableDeclarationNode>();
+ vdnode->precision = precision;
+ if (is_struct) {
+ vdnode->struct_name = struct_name;
+ vdnode->datatype = TYPE_STRUCT;
+ } else {
+ vdnode->datatype = type;
+ };
+ vdnode->is_const = is_const;
+
do {
bool unknown_size = false;
- Node *size_expr = nullptr;
-
- ArrayDeclarationNode *anode = nullptr;
- ArrayDeclarationNode::Declaration adecl;
+ VariableDeclarationNode::Declaration decl;
tk = _get_token();
@@ -6527,17 +6469,16 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
first = false;
if (tk.type != TK_IDENTIFIER && tk.type != TK_BRACKET_OPEN) {
- _set_error("Expected identifier or '[' after datatype.");
+ _set_error(RTR("Expected an identifier or '[' after type."));
return ERR_PARSE_ERROR;
}
if (tk.type == TK_BRACKET_OPEN) {
- Error error = _parse_local_array_size(p_block, p_function_info, size_expr, array_size, unknown_size);
+ Error error = _parse_array_size(p_block, p_function_info, false, &decl.size_expression, &array_size, &unknown_size);
if (error != OK) {
return error;
}
- adecl.single_expression = false;
- adecl.size = array_size;
+ decl.size = array_size;
fixed_array_size = true;
tk = _get_token();
@@ -6545,7 +6486,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
if (tk.type != TK_IDENTIFIER) {
- _set_error("Expected identifier!");
+ _set_error(RTR("Expected an identifier."));
return ERR_PARSE_ERROR;
}
@@ -6553,12 +6494,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
ShaderLanguage::IdentifierType itype;
if (_find_identifier(p_block, true, p_function_info, name, (ShaderLanguage::DataType *)nullptr, &itype)) {
if (itype != IDENTIFIER_FUNCTION) {
- _set_error("Redefinition of '" + String(name) + "'");
+ _set_redefinition_error(String(name));
return ERR_PARSE_ERROR;
}
}
-
- adecl.name = name;
+ decl.name = name;
#ifdef DEBUG_ENABLED
if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_LOCAL_VARIABLE_FLAG)) {
@@ -6584,55 +6524,29 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
tk = _get_token();
- bool is_array_decl = var.array_size > 0 || unknown_size;
-
if (tk.type == TK_BRACKET_OPEN) {
- if (is_array_decl) {
- _set_error("Array size is already defined!");
- return ERR_PARSE_ERROR;
- }
-
if (RenderingServer::get_singleton()->is_low_end() && is_const) {
- _set_error("Local const arrays are supported only on high-end platform!");
+ _set_error(RTR("Local const arrays are only supported on high-end platforms."));
return ERR_PARSE_ERROR;
}
- Error error = _parse_local_array_size(p_block, p_function_info, size_expr, var.array_size, unknown_size);
+ Error error = _parse_array_size(p_block, p_function_info, false, &decl.size_expression, &var.array_size, &unknown_size);
if (error != OK) {
return error;
}
- adecl.single_expression = false;
- adecl.size = var.array_size;
+ decl.size = var.array_size;
array_size = var.array_size;
- is_array_decl = true;
tk = _get_token();
}
- if (is_array_decl) {
- {
- anode = alloc_node<ArrayDeclarationNode>();
-
- if (is_struct) {
- anode->struct_name = struct_name;
- anode->datatype = TYPE_STRUCT;
- } else {
- anode->datatype = type;
- }
-
- anode->precision = precision;
- anode->is_const = is_const;
- anode->size_expression = size_expr;
-
- vardecl = (Node *)anode;
- }
-
+ if (var.array_size > 0 || unknown_size) {
bool full_def = false;
if (tk.type == TK_OP_ASSIGN) {
if (RenderingServer::get_singleton()->is_low_end()) {
- _set_error("Array initialization is supported only on high-end platform!");
+ _set_error(RTR("Array initialization is only supported on high-end platforms."));
return ERR_PARSE_ERROR;
}
@@ -6644,11 +6558,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
- _set_error("Expected correct array initializer!");
+ _set_error(RTR("Expected array initializer."));
return ERR_PARSE_ERROR;
} else {
if (unknown_size) {
- adecl.size = n->get_array_size();
+ decl.size = n->get_array_size();
var.array_size = n->get_array_size();
}
@@ -6656,15 +6570,15 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
- adecl.single_expression = true;
- adecl.initializer.push_back(n);
+ decl.single_expression = true;
+ decl.initializer.push_back(n);
}
tk = _get_token();
} else {
if (tk.type != TK_CURLY_BRACKET_OPEN) {
if (unknown_size) {
- _set_error("Expected '{'");
+ _set_expected_error("{");
return ERR_PARSE_ERROR;
}
@@ -6675,11 +6589,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
precision2 = get_token_precision(tk.type);
tk = _get_token();
if (shader->structs.has(tk.text)) {
- _set_error("Precision modifier cannot be used on structs.");
+ _set_error(RTR("The precision modifier cannot be used on structs."));
return ERR_PARSE_ERROR;
}
if (!is_token_nonvoid_datatype(tk.type)) {
- _set_error("Expected datatype after precision");
+ _set_error(RTR("Expected data type after precision modifier."));
return ERR_PARSE_ERROR;
}
}
@@ -6692,7 +6606,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
struct_name2 = tk.text;
} else {
if (!is_token_variable_datatype(tk.type)) {
- _set_error("Invalid data type for array");
+ _set_error(RTR("Invalid data type for the array."));
return ERR_PARSE_ERROR;
}
type2 = get_token_datatype(tk.type);
@@ -6702,73 +6616,50 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- TkPos pos2 = _get_tkpos();
- tk = _get_token();
- if (tk.type == TK_BRACKET_CLOSE) {
+ bool is_unknown_size = false;
+ Error error = _parse_array_size(p_block, p_function_info, false, nullptr, &array_size2, &is_unknown_size);
+ if (error != OK) {
+ return error;
+ }
+ if (is_unknown_size) {
array_size2 = var.array_size;
- tk = _get_token();
- } else {
- _set_tkpos(pos2);
-
- Node *n = _parse_and_reduce_expression(p_block, p_function_info);
- if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
- _set_error("Expected single integer constant > 0");
- return ERR_PARSE_ERROR;
- }
-
- ConstantNode *cnode = (ConstantNode *)n;
- if (cnode->values.size() == 1) {
- array_size2 = cnode->values[0].sint;
- if (array_size2 <= 0) {
- _set_error("Expected single integer constant > 0");
- return ERR_PARSE_ERROR;
- }
- } else {
- _set_error("Expected single integer constant > 0");
- return ERR_PARSE_ERROR;
- }
-
- tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return ERR_PARSE_ERROR;
- } else {
- tk = _get_token();
- }
}
+ tk = _get_token();
} else {
- _set_error("Expected '['");
+ _set_expected_error("[");
return ERR_PARSE_ERROR;
}
if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) {
- String error_str = "Cannot convert from '";
+ String from;
if (precision2 != PRECISION_DEFAULT) {
- error_str += get_precision_name(precision2);
- error_str += " ";
+ from += get_precision_name(precision2);
+ from += " ";
}
if (type2 == TYPE_STRUCT) {
- error_str += struct_name2;
+ from += struct_name2;
} else {
- error_str += get_datatype_name(type2);
+ from += get_datatype_name(type2);
}
- error_str += "[";
- error_str += itos(array_size2);
- error_str += "]'";
- error_str += " to '";
+ from += "[";
+ from += itos(array_size2);
+ from += "]'";
+
+ String to;
if (precision != PRECISION_DEFAULT) {
- error_str += get_precision_name(precision);
- error_str += " ";
+ to += get_precision_name(precision);
+ to += " ";
}
if (type == TYPE_STRUCT) {
- error_str += struct_name;
+ to += struct_name;
} else {
- error_str += get_datatype_name(type);
+ to += get_datatype_name(type);
}
- error_str += "[";
- error_str += itos(var.array_size);
- error_str += "]'";
- _set_error(error_str);
+ to += "[";
+ to += itos(var.array_size);
+ to += "]'";
+
+ _set_error(vformat(RTR("Cannot convert from '%s' to '%s'."), from, to));
return ERR_PARSE_ERROR;
}
}
@@ -6777,13 +6668,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (unknown_size) {
if (!curly) {
- _set_error("Expected '{'");
+ _set_expected_error("{");
return ERR_PARSE_ERROR;
}
} else {
if (full_def) {
if (curly) {
- _set_error("Expected '('");
+ _set_expected_error("(");
return ERR_PARSE_ERROR;
}
}
@@ -6796,8 +6687,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
- if (anode->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) {
- _set_error("Expected constant expression");
+ if (is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) {
+ _set_error(RTR("Expected a constant expression."));
return ERR_PARSE_ERROR;
}
@@ -6807,28 +6698,28 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
tk = _get_token();
if (tk.type == TK_COMMA) {
- adecl.initializer.push_back(n);
+ decl.initializer.push_back(n);
continue;
} else if (!curly && tk.type == TK_PARENTHESIS_CLOSE) {
- adecl.initializer.push_back(n);
+ decl.initializer.push_back(n);
break;
} else if (curly && tk.type == TK_CURLY_BRACKET_CLOSE) {
- adecl.initializer.push_back(n);
+ decl.initializer.push_back(n);
break;
} else {
if (curly) {
- _set_error("Expected '}' or ','");
+ _set_expected_error("}", ",");
} else {
- _set_error("Expected ')' or ','");
+ _set_expected_error(")", ",");
}
return ERR_PARSE_ERROR;
}
}
if (unknown_size) {
- adecl.size = adecl.initializer.size();
- var.array_size = adecl.initializer.size();
- } else if (adecl.initializer.size() != var.array_size) {
- _set_error("Array size mismatch");
+ decl.size = decl.initializer.size();
+ var.array_size = decl.initializer.size();
+ } else if (decl.initializer.size() != var.array_size) {
+ _set_error(RTR("Array size mismatch."));
return ERR_PARSE_ERROR;
}
tk = _get_token();
@@ -6836,48 +6727,31 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
} else {
if (unknown_size) {
- _set_error("Expected array initialization");
+ _set_error(RTR("Expected array initialization."));
return ERR_PARSE_ERROR;
}
- if (anode->is_const) {
- _set_error("Expected initialization of constant");
+ if (is_const) {
+ _set_error(RTR("Expected initialization of constant."));
return ERR_PARSE_ERROR;
}
}
array_size = var.array_size;
- anode->declarations.push_back(adecl);
} else if (tk.type == TK_OP_ASSIGN) {
- VariableDeclarationNode *node = alloc_node<VariableDeclarationNode>();
- if (is_struct) {
- node->struct_name = struct_name;
- node->datatype = TYPE_STRUCT;
- } else {
- node->datatype = type;
- }
- node->precision = precision;
- node->is_const = is_const;
- vardecl = (Node *)node;
-
- VariableDeclarationNode::Declaration decl;
- decl.name = name;
- decl.initializer = nullptr;
-
//variable created with assignment! must parse an expression
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
return ERR_PARSE_ERROR;
}
- if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) {
+ if (is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) {
OperatorNode *op = ((OperatorNode *)n);
for (int i = 1; i < op->arguments.size(); i++) {
if (!_check_node_constness(op->arguments[i])) {
- _set_error("Expected constant expression for argument '" + itos(i - 1) + "' of function call after '='");
+ _set_error(vformat(RTR("Expected constant expression for argument %d of function call after '='."), i - 1));
return ERR_PARSE_ERROR;
}
}
}
- decl.initializer = n;
if (n->type == Node::TYPE_CONSTANT) {
ConstantNode *const_node = static_cast<ConstantNode *>(n);
@@ -6889,49 +6763,32 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) {
return ERR_PARSE_ERROR;
}
+
+ decl.initializer.push_back(n);
tk = _get_token();
- node->declarations.push_back(decl);
} else {
if (is_const) {
- _set_error("Expected initialization of constant");
+ _set_error(RTR("Expected initialization of constant."));
return ERR_PARSE_ERROR;
}
-
- VariableDeclarationNode *node = alloc_node<VariableDeclarationNode>();
- if (is_struct) {
- node->struct_name = struct_name;
- node->datatype = TYPE_STRUCT;
- } else {
- node->datatype = type;
- }
- node->precision = precision;
- vardecl = (Node *)node;
-
- VariableDeclarationNode::Declaration decl;
- decl.name = name;
- decl.initializer = nullptr;
- node->declarations.push_back(decl);
}
- p_block->statements.push_back(vardecl);
+ vdnode->declarations.push_back(decl);
p_block->variables[name] = var;
if (!fixed_array_size) {
array_size = 0;
}
- if (tk.type == TK_COMMA) {
- if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR) {
- _set_error("Multiple declarations in 'for' loop are not implemented yet.");
- return ERR_PARSE_ERROR;
- }
- } else if (tk.type == TK_SEMICOLON) {
+ if (tk.type == TK_SEMICOLON) {
break;
- } else {
- _set_error("Expected ',' or ';' after variable");
+ } else if (tk.type != TK_COMMA) {
+ _set_expected_error(",", ";");
return ERR_PARSE_ERROR;
}
} while (tk.type == TK_COMMA); //another variable
+
+ p_block->statements.push_back((Node *)vdnode);
} else if (tk.type == TK_CURLY_BRACKET_OPEN) {
//a sub block, just because..
BlockNode *block = alloc_node<BlockNode>();
@@ -6944,7 +6801,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
//if () {}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after if");
+ _set_expected_after_error("(", "if");
return ERR_PARSE_ERROR;
}
@@ -6956,13 +6813,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
if (n->get_datatype() != TYPE_BOOL) {
- _set_error("Expected boolean expression");
+ _set_error(RTR("Expected a boolean expression."));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')' after expression");
+ _set_expected_error(")");
return ERR_PARSE_ERROR;
}
@@ -6992,14 +6849,14 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
} else if (tk.type == TK_CF_SWITCH) {
if (RenderingServer::get_singleton()->is_low_end()) {
- _set_error("\"switch\" operator is supported only on high-end platform!");
+ _set_error(vformat(RTR("The '%s' operator is only supported on high-end platforms."), "switch"));
return ERR_PARSE_ERROR;
}
// switch() {}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after switch");
+ _set_expected_after_error("(", "switch");
return ERR_PARSE_ERROR;
}
ControlFlowNode *cf = alloc_node<ControlFlowNode>();
@@ -7009,17 +6866,17 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
if (n->get_datatype() != TYPE_INT) {
- _set_error("Expected integer expression");
+ _set_error(RTR("Expected an integer expression."));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')' after expression");
+ _set_expected_error(")");
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_CURLY_BRACKET_OPEN) {
- _set_error("Expected '{' after switch statement");
+ _set_expected_after_error("{", "switch");
return ERR_PARSE_ERROR;
}
BlockNode *switch_block = alloc_node<BlockNode>();
@@ -7040,10 +6897,10 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (tk.type == TK_CF_CASE || tk.type == TK_CF_DEFAULT) {
if (prev_type == TK_CF_DEFAULT) {
if (tk.type == TK_CF_CASE) {
- _set_error("Cases must be defined before default case.");
+ _set_error(RTR("Cases must be defined before default case."));
return ERR_PARSE_ERROR;
} else if (prev_type == TK_CF_DEFAULT) {
- _set_error("Default case must be defined only once.");
+ _set_error(RTR("Default case must be defined only once."));
return ERR_PARSE_ERROR;
}
}
@@ -7062,7 +6919,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
if (constants.has(cn->values[0].sint)) {
- _set_error("Duplicated case label: '" + itos(cn->values[0].sint) + "'");
+ _set_error(vformat(RTR("Duplicated case label: %d."), cn->values[0].sint));
return ERR_PARSE_ERROR;
}
constants.insert(cn->values[0].sint);
@@ -7074,7 +6931,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
ConstantNode::Value v;
_find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, nullptr, nullptr, nullptr, &v);
if (constants.has(v.sint)) {
- _set_error("Duplicated case label: '" + itos(v.sint) + "'");
+ _set_error(vformat(RTR("Duplicated case label: %d."), v.sint));
return ERR_PARSE_ERROR;
}
constants.insert(v.sint);
@@ -7101,7 +6958,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
if (!p_block || (p_block->block_type != BlockNode::BLOCK_TYPE_SWITCH)) {
- _set_error("case must be placed within switch block");
+ _set_error(vformat(RTR("'%s' must be placed within a '%s' block."), "case", "switch"));
return ERR_PARSE_ERROR;
}
@@ -7130,7 +6987,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
}
if (!correct_constant_expression) {
- _set_error("Expected integer constant");
+ _set_error(RTR("Expected an integer constant."));
return ERR_PARSE_ERROR;
}
@@ -7154,7 +7011,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
tk = _get_token();
if (tk.type != TK_COLON) {
- _set_error("Expected ':'");
+ _set_expected_error(":");
return ERR_PARSE_ERROR;
}
@@ -7182,14 +7039,14 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
if (!p_block || (p_block->block_type != BlockNode::BLOCK_TYPE_SWITCH)) {
- _set_error("default must be placed within switch block");
+ _set_error(vformat(RTR("'%s' must be placed within a '%s' block."), "default", "switch"));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_COLON) {
- _set_error("Expected ':'");
+ _set_expected_error(":");
return ERR_PARSE_ERROR;
}
@@ -7226,14 +7083,14 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
tk = _get_token();
if (tk.type != TK_CF_WHILE) {
- _set_error("Expected while after do");
+ _set_expected_after_error("while", "do");
return ERR_PARSE_ERROR;
}
}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after while");
+ _set_expected_after_error("(", "while");
return ERR_PARSE_ERROR;
}
@@ -7250,7 +7107,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
tk = _get_token();
if (tk.type != TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')' after expression");
+ _set_expected_error(")");
return ERR_PARSE_ERROR;
}
if (!is_do) {
@@ -7271,7 +7128,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
tk = _get_token();
if (tk.type != TK_SEMICOLON) {
- _set_error("Expected ';'");
+ _set_expected_error(";");
return ERR_PARSE_ERROR;
}
}
@@ -7279,7 +7136,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
// for() {}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after for");
+ _set_expected_after_error("(", "for");
return ERR_PARSE_ERROR;
}
@@ -7287,43 +7144,35 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
cf->flow_op = FLOW_OP_FOR;
BlockNode *init_block = alloc_node<BlockNode>();
- init_block->block_type = BlockNode::BLOCK_TYPE_FOR;
+ init_block->block_type = BlockNode::BLOCK_TYPE_FOR_INIT;
init_block->parent_block = p_block;
init_block->single_statement = true;
cf->blocks.push_back(init_block);
- if (_parse_block(init_block, p_function_info, true, false, false) != OK) {
- return ERR_PARSE_ERROR;
- }
-
- Node *n = _parse_and_reduce_expression(init_block, p_function_info);
- if (!n) {
- return ERR_PARSE_ERROR;
- }
-
- if (n->get_datatype() != TYPE_BOOL) {
- _set_error("Middle expression is expected to be boolean.");
- return ERR_PARSE_ERROR;
- }
-
- tk = _get_token();
- if (tk.type != TK_SEMICOLON) {
- _set_error("Expected ';' after middle expression");
- return ERR_PARSE_ERROR;
+ Error err = _parse_block(init_block, p_function_info, true, false, false);
+ if (err != OK) {
+ return err;
}
- cf->expressions.push_back(n);
-
- n = _parse_and_reduce_expression(init_block, p_function_info);
- if (!n) {
- return ERR_PARSE_ERROR;
+ BlockNode *condition_block = alloc_node<BlockNode>();
+ condition_block->block_type = BlockNode::BLOCK_TYPE_FOR_CONDITION;
+ condition_block->parent_block = init_block;
+ condition_block->single_statement = true;
+ condition_block->use_comma_between_statements = true;
+ cf->blocks.push_back(condition_block);
+ err = _parse_block(condition_block, p_function_info, true, false, false);
+ if (err != OK) {
+ return err;
}
- cf->expressions.push_back(n);
-
- tk = _get_token();
- if (tk.type != TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')' after third expression");
- return ERR_PARSE_ERROR;
+ BlockNode *expression_block = alloc_node<BlockNode>();
+ expression_block->block_type = BlockNode::BLOCK_TYPE_FOR_EXPRESSION;
+ expression_block->parent_block = init_block;
+ expression_block->single_statement = true;
+ expression_block->use_comma_between_statements = true;
+ cf->blocks.push_back(expression_block);
+ err = _parse_block(expression_block, p_function_info, true, false, false);
+ if (err != OK) {
+ return err;
}
BlockNode *block = alloc_node<BlockNode>();
@@ -7331,8 +7180,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
cf->blocks.push_back(block);
p_block->statements.push_back(cf);
- Error err = _parse_block(block, p_function_info, true, true, true);
- if (err) {
+ err = _parse_block(block, p_function_info, true, true, true);
+ if (err != OK) {
return err;
}
@@ -7345,12 +7194,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
if (!b) {
- _set_error("Bug");
+ _set_parsing_error();
return ERR_BUG;
}
if (b && b->parent_function && p_function_info.main_function) {
- _set_error(vformat("Using 'return' in '%s' processor function results in undefined behavior!", b->parent_function->name));
+ _set_error(vformat(RTR("Using '%s' in the '%s' processor function is incorrect."), "return", b->parent_function->name));
return ERR_PARSE_ERROR;
}
@@ -7369,7 +7218,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (tk.type == TK_SEMICOLON) {
//all is good
if (b->parent_function->return_type != TYPE_VOID) {
- _set_error("Expected return with an expression of type '" + (!return_struct_name.is_empty() ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string + "'");
+ _set_error(vformat(RTR("Expected '%s' with an expression of type '%s'."), "return", (!return_struct_name.is_empty() ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string));
return ERR_PARSE_ERROR;
}
} else {
@@ -7381,13 +7230,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
if (b->parent_function->return_type != expr->get_datatype() || b->parent_function->return_array_size != expr->get_array_size() || return_struct_name != expr->get_datatype_name()) {
- _set_error("Expected return with an expression of type '" + (!return_struct_name.is_empty() ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string + "'");
+ _set_error(vformat(RTR("Expected return with an expression of type '%s'."), (!return_struct_name.is_empty() ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_SEMICOLON) {
- _set_error("Expected ';' after return expression");
+ _set_expected_after_error(";", "return");
return ERR_PARSE_ERROR;
}
@@ -7410,12 +7259,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
b = b->parent_block;
}
if (!b) {
- _set_error("Bug");
+ _set_parsing_error();
return ERR_BUG;
}
if (!b->parent_function->can_discard) {
- _set_error("Use of 'discard' is not allowed here.");
+ _set_error(vformat(RTR("Use of '%s' is not allowed here."), "discard"));
return ERR_PARSE_ERROR;
}
@@ -7425,14 +7274,14 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
pos = _get_tkpos();
tk = _get_token();
if (tk.type != TK_SEMICOLON) {
- _set_error("Expected ';' after discard");
+ _set_expected_after_error(";", "discard");
return ERR_PARSE_ERROR;
}
p_block->statements.push_back(flow);
} else if (tk.type == TK_CF_BREAK) {
if (!p_can_break) {
- _set_error("'break' is not allowed outside of a loop or 'switch' statement");
+ _set_error(vformat(RTR("'%s' is not allowed outside of a loop or '%s' statement."), "break", "switch"));
return ERR_PARSE_ERROR;
}
@@ -7442,7 +7291,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
pos = _get_tkpos();
tk = _get_token();
if (tk.type != TK_SEMICOLON) {
- _set_error("Expected ';' after break");
+ _set_expected_after_error(";", "break");
return ERR_PARSE_ERROR;
}
@@ -7458,7 +7307,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
} else if (tk.type == TK_CF_CONTINUE) {
if (!p_can_continue) {
- _set_error("'continue' is not allowed outside of a loop");
+ _set_error(vformat(RTR("'%s' is not allowed outside of a loop."), "continue"));
return ERR_PARSE_ERROR;
}
@@ -7469,7 +7318,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
tk = _get_token();
if (tk.type != TK_SEMICOLON) {
//all is good
- _set_error("Expected ';' after continue");
+ _set_expected_after_error(";", "continue");
return ERR_PARSE_ERROR;
}
@@ -7482,11 +7331,49 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (!expr) {
return ERR_PARSE_ERROR;
}
+
+ bool empty = false;
+
+ if (expr->type == Node::TYPE_OPERATOR) {
+ OperatorNode *op = static_cast<OperatorNode *>(expr);
+ if (op->op == OP_EMPTY) {
+ empty = true;
+ }
+ }
+ if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_INIT) {
+ if (!empty && expr->type != BlockNode::TYPE_VARIABLE_DECLARATION) {
+ _set_error(RTR("The left expression is expected to be a variable declaration."));
+ return ERR_PARSE_ERROR;
+ }
+ }
+ if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION) {
+ if (!empty && expr->get_datatype() != TYPE_BOOL) {
+ _set_error(RTR("The middle expression is expected to be boolean."));
+ return ERR_PARSE_ERROR;
+ }
+ }
+
p_block->statements.push_back(expr);
tk = _get_token();
- if (tk.type != TK_SEMICOLON) {
- _set_error("Expected ';' after statement");
+ if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION) {
+ if (tk.type == TK_COMMA) {
+ continue;
+ }
+ if (tk.type != TK_SEMICOLON) {
+ _set_expected_error(",", ";");
+ return ERR_PARSE_ERROR;
+ }
+ } else if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_EXPRESSION) {
+ if (tk.type == TK_COMMA) {
+ continue;
+ }
+ if (tk.type != TK_PARENTHESIS_CLOSE) {
+ _set_expected_error(",", ")");
+ return ERR_PARSE_ERROR;
+ }
+ } else if (tk.type != TK_SEMICOLON) {
+ _set_expected_error(";");
return ERR_PARSE_ERROR;
}
}
@@ -7547,7 +7434,7 @@ Error ShaderLanguage::_validate_datatype(DataType p_type) {
}
if (invalid_type) {
- _set_error(vformat("\"%s\" type is supported only on high-end platform!", get_datatype_name(p_type)));
+ _set_error(vformat(RTR("The \"%s\" type is only supported on high-end platforms."), get_datatype_name(p_type)));
return ERR_UNAVAILABLE;
}
}
@@ -7559,7 +7446,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
TkPos prev_pos;
if (tk.type != TK_SHADER_TYPE) {
- _set_error("Expected 'shader_type' at the beginning of shader. Valid types are: " + _get_shader_type_list(p_shader_types));
+ _set_error(vformat(RTR("Expected '%s' at the beginning of shader. Valid types are: %s."), "shader_type", _get_shader_type_list(p_shader_types)));
return ERR_PARSE_ERROR;
}
@@ -7567,11 +7454,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
_get_completable_identifier(nullptr, COMPLETION_SHADER_TYPE, shader_type_identifier);
if (shader_type_identifier == StringName()) {
- _set_error("Expected identifier after 'shader_type', indicating type of shader. Valid types are: " + _get_shader_type_list(p_shader_types));
+ _set_error(vformat(RTR("Expected an identifier after '%s', indicating the type of shader. Valid types are: %s."), "shader_type", _get_shader_type_list(p_shader_types)));
return ERR_PARSE_ERROR;
}
if (!p_shader_types.has(shader_type_identifier)) {
- _set_error("Invalid shader type. Valid types are: " + _get_shader_type_list(p_shader_types));
+ _set_error(vformat(RTR("Invalid shader type. Valid types are: %s"), _get_shader_type_list(p_shader_types)));
return ERR_PARSE_ERROR;
}
prev_pos = _get_tkpos();
@@ -7579,7 +7466,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (tk.type != TK_SEMICOLON) {
_set_tkpos(prev_pos);
- _set_error("Expected ';' after 'shader_type <type>'.");
+ _set_expected_after_error(";", "shader_type " + String(shader_type_identifier));
return ERR_PARSE_ERROR;
}
@@ -7619,14 +7506,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
_get_completable_identifier(nullptr, COMPLETION_RENDER_MODE, mode);
if (mode == StringName()) {
- _set_error("Expected identifier for render mode");
+ _set_error(RTR("Expected an identifier for render mode."));
return ERR_PARSE_ERROR;
}
const String smode = String(mode);
- if (shader->render_modes.find(mode) != -1) {
- _set_error(vformat("Duplicated render mode: '%s'.", smode));
+ if (shader->render_modes.has(mode)) {
+ _set_error(vformat(RTR("Duplicated render mode: '%s'."), smode));
return ERR_PARSE_ERROR;
}
@@ -7638,11 +7525,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (smode.begins_with(name)) {
if (!info.options.is_empty()) {
- if (info.options.find(smode.substr(name.length() + 1)) != -1) {
+ if (info.options.has(smode.substr(name.length() + 1))) {
found = true;
if (defined_modes.has(name)) {
- _set_error(vformat("Redefinition of render mode: '%s'. The %s mode has already been set to '%s'.", smode, name, defined_modes[name]));
+ _set_error(vformat(RTR("Redefinition of render mode: '%s'. The '%s' mode has already been set to '%s'."), smode, name, defined_modes[name]));
return ERR_PARSE_ERROR;
}
defined_modes.insert(name, smode);
@@ -7656,7 +7543,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
if (!found) {
- _set_error(vformat("Invalid render mode: '%s'.", smode));
+ _set_error(vformat(RTR("Invalid render mode: '%s'."), smode));
return ERR_PARSE_ERROR;
}
@@ -7668,7 +7555,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
} else if (tk.type == TK_SEMICOLON) {
break; //done
} else {
- _set_error("Unexpected token: " + get_token_text(tk));
+ _set_error(vformat(RTR("Unexpected token: '%s'."), get_token_text(tk)));
return ERR_PARSE_ERROR;
}
}
@@ -7681,16 +7568,16 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (tk.type == TK_IDENTIFIER) {
st.name = tk.text;
if (shader->constants.has(st.name) || shader->structs.has(st.name)) {
- _set_error("Redefinition of '" + String(st.name) + "'");
+ _set_redefinition_error(String(st.name));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_CURLY_BRACKET_OPEN) {
- _set_error("Expected '{'");
+ _set_expected_error("{");
return ERR_PARSE_ERROR;
}
} else {
- _set_error("Expected struct identifier!");
+ _set_error(RTR("Expected a struct identifier."));
return ERR_PARSE_ERROR;
}
@@ -7710,7 +7597,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
DataPrecision precision = DataPrecision::PRECISION_DEFAULT;
if (tk.type == TK_STRUCT) {
- _set_error("nested structs are not allowed!");
+ _set_error(RTR("Nested structs are not allowed."));
return ERR_PARSE_ERROR;
}
@@ -7729,22 +7616,19 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
#endif // DEBUG_ENABLED
struct_dt = true;
if (use_precision) {
- _set_error("Precision modifier cannot be used on structs.");
+ _set_error(RTR("The precision modifier cannot be used on structs."));
return ERR_PARSE_ERROR;
}
}
if (!is_token_datatype(tk.type) && !struct_dt) {
- _set_error("Expected datatype.");
+ _set_error(RTR("Expected data type."));
return ERR_PARSE_ERROR;
} else {
type = struct_dt ? TYPE_STRUCT : get_token_datatype(tk.type);
- if (is_sampler_type(type)) {
- _set_error("sampler datatype not allowed here");
- return ERR_PARSE_ERROR;
- } else if (type == TYPE_VOID) {
- _set_error("void datatype not allowed here");
+ if (type == TYPE_VOID || is_sampler_type(type)) {
+ _set_error(vformat(RTR("A '%s' data type is not allowed here."), get_datatype_name(type)));
return ERR_PARSE_ERROR;
}
@@ -7759,12 +7643,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
first = false;
if (tk.type != TK_IDENTIFIER && tk.type != TK_BRACKET_OPEN) {
- _set_error("Expected identifier or '['.");
+ _set_error(RTR("Expected an identifier or '['."));
return ERR_PARSE_ERROR;
}
if (tk.type == TK_BRACKET_OPEN) {
- Error error = _parse_global_array_size(array_size, constants);
+ Error error = _parse_array_size(nullptr, constants, true, nullptr, &array_size, nullptr);
if (error != OK) {
return error;
}
@@ -7774,7 +7658,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
if (tk.type != TK_IDENTIFIER) {
- _set_error("Expected identifier!");
+ _set_error(RTR("Expected an identifier."));
return ERR_PARSE_ERROR;
}
@@ -7786,14 +7670,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
member->array_size = array_size;
if (member_names.has(member->name)) {
- _set_error("Redefinition of '" + String(member->name) + "'");
+ _set_redefinition_error(String(member->name));
return ERR_PARSE_ERROR;
}
member_names.insert(member->name);
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- Error error = _parse_global_array_size(member->array_size, constants);
+ Error error = _parse_array_size(nullptr, constants, true, nullptr, &member->array_size, nullptr);
if (error != OK) {
return error;
}
@@ -7805,7 +7689,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
if (tk.type != TK_SEMICOLON && tk.type != TK_COMMA) {
- _set_error("Expected ',' or ';' after struct member.");
+ _set_expected_error(",", ";");
return ERR_PARSE_ERROR;
}
@@ -7815,13 +7699,13 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
}
if (member_count == 0) {
- _set_error("Empty structs are not allowed!");
+ _set_error(RTR("Empty structs are not allowed."));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_SEMICOLON) {
- _set_error("Expected ';'");
+ _set_expected_error(";");
return ERR_PARSE_ERROR;
}
shader->structs[st.name] = st;
@@ -7835,7 +7719,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
case TK_GLOBAL: {
tk = _get_token();
if (tk.type != TK_UNIFORM) {
- _set_error("Expected 'uniform' after 'global'");
+ _set_expected_after_error("uniform", "global");
return ERR_PARSE_ERROR;
}
uniform_scope = ShaderNode::Uniform::SCOPE_GLOBAL;
@@ -7845,7 +7729,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL) {
tk = _get_token();
if (tk.type != TK_UNIFORM) {
- _set_error("Expected 'uniform' after 'instance'");
+ _set_expected_after_error("uniform", "instance");
return ERR_PARSE_ERROR;
}
uniform_scope = ShaderNode::Uniform::SCOPE_INSTANCE;
@@ -7858,7 +7742,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (!uniform) {
if (shader_type_identifier == "particles" || shader_type_identifier == "sky" || shader_type_identifier == "fog") {
- _set_error(vformat("Varyings cannot be used in '%s' shaders!", shader_type_identifier));
+ _set_error(vformat(RTR("Varyings cannot be used in '%s' shaders."), shader_type_identifier));
return ERR_PARSE_ERROR;
}
}
@@ -7873,7 +7757,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (is_token_interpolation(tk.type)) {
if (uniform) {
- _set_error("Interpolation qualifiers are not supported for uniforms!");
+ _set_error(RTR("Interpolation qualifiers are not supported for uniforms."));
return ERR_PARSE_ERROR;
}
interpolation = get_token_interpolation(tk.type);
@@ -7889,43 +7773,43 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (shader->structs.has(tk.text)) {
if (uniform) {
if (precision_defined) {
- _set_error("Precision modifier cannot be used on structs.");
+ _set_error(RTR("The precision modifier cannot be used on structs."));
return ERR_PARSE_ERROR;
}
- _set_error("struct datatype is not yet supported for uniforms!");
+ _set_error(vformat(RTR("The '%s' data type is not supported for uniforms."), "struct"));
return ERR_PARSE_ERROR;
} else {
- _set_error("struct datatype not allowed here");
+ _set_error(vformat(RTR("The '%s' data type not allowed here."), "struct"));
return ERR_PARSE_ERROR;
}
}
if (!is_token_datatype(tk.type)) {
- _set_error("Expected datatype. ");
+ _set_error(RTR("Expected data type."));
return ERR_PARSE_ERROR;
}
type = get_token_datatype(tk.type);
if (type == TYPE_VOID) {
- _set_error("void datatype not allowed here");
+ _set_error(vformat(RTR("The '%s' data type is not allowed here."), "void"));
return ERR_PARSE_ERROR;
}
if (!uniform && (type < TYPE_FLOAT || type > TYPE_MAT4)) {
- _set_error("Invalid type for varying, only float,vec2,vec3,vec4,mat2,mat3,mat4 or array of these types allowed.");
+ _set_error(RTR("Invalid type for varying, only 'float', 'vec2', 'vec3', 'vec4', 'mat2', 'mat3', 'mat4', or arrays of these types are allowed."));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_IDENTIFIER && tk.type != TK_BRACKET_OPEN) {
- _set_error("Expected identifier or '['.");
+ _set_error(RTR("Expected an identifier or '['."));
return ERR_PARSE_ERROR;
}
if (tk.type == TK_BRACKET_OPEN) {
- Error error = _parse_global_array_size(array_size, constants);
+ Error error = _parse_array_size(nullptr, constants, true, nullptr, &array_size, nullptr);
if (error != OK) {
return error;
}
@@ -7933,7 +7817,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
if (tk.type != TK_IDENTIFIER) {
- _set_error("Expected identifier!");
+ _set_error(RTR("Expected an identifier."));
return ERR_PARSE_ERROR;
}
@@ -7941,26 +7825,26 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
name = tk.text;
if (_find_identifier(nullptr, false, constants, name)) {
- _set_error("Redefinition of '" + String(name) + "'");
+ _set_redefinition_error(String(name));
return ERR_PARSE_ERROR;
}
if (has_builtin(p_functions, name)) {
- _set_error("Redefinition of '" + String(name) + "'");
+ _set_redefinition_error(String(name));
return ERR_PARSE_ERROR;
}
if (uniform) {
- if (uniform_scope == ShaderNode::Uniform::SCOPE_GLOBAL) {
+ if (uniform_scope == ShaderNode::Uniform::SCOPE_GLOBAL && Engine::get_singleton()->is_editor_hint()) { // Type checking for global uniforms is not allowed outside the editor.
//validate global uniform
DataType gvtype = global_var_get_type_func(name);
if (gvtype == TYPE_MAX) {
- _set_error("Global uniform '" + String(name) + "' does not exist. Create it in Project Settings.");
+ _set_error(vformat(RTR("Global uniform '%s' does not exist. Create it in Project Settings."), String(name)));
return ERR_PARSE_ERROR;
}
if (type != gvtype) {
- _set_error("Global uniform '" + String(name) + "' must be of type '" + get_datatype_name(gvtype) + "'.");
+ _set_error(vformat(RTR("Global uniform '%s' must be of type '%s'."), String(name), get_datatype_name(gvtype)));
return ERR_PARSE_ERROR;
}
}
@@ -7973,7 +7857,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- Error error = _parse_global_array_size(uniform2.array_size, constants);
+ Error error = _parse_array_size(nullptr, constants, true, nullptr, &uniform2.array_size, nullptr);
if (error != OK) {
return error;
}
@@ -7982,7 +7866,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (is_sampler_type(type)) {
if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE) {
- _set_error("Uniforms with 'instance' qualifiers can't be of sampler type.");
+ _set_error(vformat(RTR("Uniforms with '%s' qualifiers can't be of sampler type.", "instance")));
return ERR_PARSE_ERROR;
}
uniform2.texture_order = texture_uniforms++;
@@ -7998,7 +7882,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
} else {
if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE && (type == TYPE_MAT2 || type == TYPE_MAT3 || type == TYPE_MAT4)) {
- _set_error("Uniforms with 'instance' qualifiers can't be of matrix type.");
+ _set_error(vformat(RTR("Uniforms with '%s' qualifier can't be of matrix type.", "instance")));
return ERR_PARSE_ERROR;
}
uniform2.texture_order = -1;
@@ -8027,11 +7911,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (uniform2.array_size > 0) {
if (uniform_scope == ShaderNode::Uniform::SCOPE_GLOBAL) {
- _set_error("'SCOPE_GLOBAL' qualifier is not yet supported for uniform array!");
+ _set_error(vformat(RTR("The '%s' qualifier is not supported for uniform arrays."), "SCOPE_GLOBAL"));
return ERR_PARSE_ERROR;
}
if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE) {
- _set_error("'SCOPE_INSTANCE' qualifier is not yet supported for uniform array!");
+ _set_error(vformat(RTR("The '%s' qualifier is not supported for uniform arrays."), "SCOPE_INSTANCE"));
return ERR_PARSE_ERROR;
}
}
@@ -8049,13 +7933,13 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
completion_line = tk.line;
if (!is_token_hint(tk.type)) {
- _set_error("Expected valid type hint after ':'.");
+ _set_error(RTR("Expected valid type hint after ':'."));
return ERR_PARSE_ERROR;
}
if (uniform2.array_size > 0) {
if (tk.type != TK_HINT_COLOR) {
- _set_error("This hint is not yet supported for uniform arrays!");
+ _set_error(RTR("This hint is not supported for uniform arrays."));
return ERR_PARSE_ERROR;
}
}
@@ -8086,20 +7970,20 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
uniform2.hint = ShaderNode::Uniform::HINT_BLACK_ALBEDO;
} else if (tk.type == TK_HINT_COLOR) {
if (type != TYPE_VEC4) {
- _set_error("Color hint is for vec4 only");
+ _set_error(vformat(RTR("Color hint is for '%s' only."), "vec4"));
return ERR_PARSE_ERROR;
}
uniform2.hint = ShaderNode::Uniform::HINT_COLOR;
} else if (tk.type == TK_HINT_RANGE) {
uniform2.hint = ShaderNode::Uniform::HINT_RANGE;
if (type != TYPE_FLOAT && type != TYPE_INT) {
- _set_error("Range hint is for float and int only");
+ _set_error(vformat(RTR("Range hint is for '%s' and '%s' only."), "float", "int"));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after hint_range");
+ _set_expected_after_error("(", "hint_range");
return ERR_PARSE_ERROR;
}
@@ -8113,7 +7997,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) {
- _set_error("Expected integer constant");
+ _set_error(RTR("Expected an integer constant."));
return ERR_PARSE_ERROR;
}
@@ -8123,7 +8007,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type != TK_COMMA) {
- _set_error("Expected ',' after integer constant");
+ _set_error(RTR("Expected ',' after integer constant."));
return ERR_PARSE_ERROR;
}
@@ -8137,7 +8021,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) {
- _set_error("Expected integer constant after ','");
+ _set_error(RTR("Expected an integer constant after ','."));
return ERR_PARSE_ERROR;
}
@@ -8150,7 +8034,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) {
- _set_error("Expected integer constant after ','");
+ _set_error(RTR("Expected an integer constant after ','."));
return ERR_PARSE_ERROR;
}
@@ -8165,44 +8049,44 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
if (tk.type != TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')'");
+ _set_expected_error(")");
return ERR_PARSE_ERROR;
}
} else if (tk.type == TK_HINT_INSTANCE_INDEX) {
if (custom_instance_index != -1) {
- _set_error("Can only specify 'instance_index' once.");
+ _set_error(vformat(RTR("Can only specify '%s' once."), "instance_index"));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after 'instance_index'");
+ _set_expected_after_error("(", "instance_index");
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type == TK_OP_SUB) {
- _set_error("The instance index can't be negative.");
+ _set_error(RTR("The instance index can't be negative."));
return ERR_PARSE_ERROR;
}
if (!tk.is_integer_constant()) {
- _set_error("Expected integer constant");
+ _set_error(RTR("Expected an integer constant."));
return ERR_PARSE_ERROR;
}
custom_instance_index = tk.constant;
if (custom_instance_index >= MAX_INSTANCE_UNIFORM_INDICES) {
- _set_error("Allowed instance uniform indices are 0-" + itos(MAX_INSTANCE_UNIFORM_INDICES - 1));
+ _set_error(vformat(RTR("Allowed instance uniform indices must be within [0..%d] range."), MAX_INSTANCE_UNIFORM_INDICES - 1));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')'");
+ _set_expected_error(")");
return ERR_PARSE_ERROR;
}
} else if (tk.type == TK_FILTER_LINEAR) {
@@ -8224,7 +8108,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
if (uniform2.hint != ShaderNode::Uniform::HINT_RANGE && uniform2.hint != ShaderNode::Uniform::HINT_NONE && uniform2.hint != ShaderNode::Uniform::HINT_COLOR && type <= TYPE_MAT4) {
- _set_error("This hint is only for sampler types");
+ _set_error(RTR("This hint is only for sampler types."));
return ERR_PARSE_ERROR;
}
@@ -8239,7 +8123,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
} else {
uniform2.instance_index = instance_index++;
if (instance_index > MAX_INSTANCE_UNIFORM_INDICES) {
- _set_error("Too many 'instance' uniforms in shader, maximum supported is " + itos(MAX_INSTANCE_UNIFORM_INDICES));
+ _set_error(vformat(RTR("Too many '%s' uniforms in shader, maximum supported is %d."), "instance", MAX_INSTANCE_UNIFORM_INDICES));
return ERR_PARSE_ERROR;
}
}
@@ -8249,7 +8133,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (tk.type == TK_OP_ASSIGN) {
if (uniform2.array_size > 0) {
- _set_error("Setting default value to a uniform array is not yet supported!");
+ _set_error(RTR("Setting default values to uniform arrays is not supported."));
return ERR_PARSE_ERROR;
}
@@ -8258,7 +8142,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
return ERR_PARSE_ERROR;
}
if (expr->type != Node::TYPE_CONSTANT) {
- _set_error("Expected constant expression after '='");
+ _set_error(RTR("Expected constant expression after '='."));
return ERR_PARSE_ERROR;
}
@@ -8267,7 +8151,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
uniform2.default_value.resize(cn->values.size());
if (!convert_constant(cn, uniform2.type, uniform2.default_value.ptrw())) {
- _set_error("Can't convert constant to " + get_datatype_name(uniform2.type));
+ _set_error(vformat(RTR("Can't convert constant to '%s'."), get_datatype_name(uniform2.type)));
return ERR_PARSE_ERROR;
}
tk = _get_token();
@@ -8284,7 +8168,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL;
if (tk.type != TK_SEMICOLON) {
- _set_error("Expected ';'");
+ _set_expected_error(";");
return ERR_PARSE_ERROR;
}
@@ -8300,37 +8184,19 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type != TK_SEMICOLON && tk.type != TK_BRACKET_OPEN) {
if (array_size == 0) {
- _set_error("Expected ';' or '['");
+ _set_expected_error(";", "[");
} else {
- _set_error("Expected ';'");
+ _set_expected_error(";");
}
return ERR_PARSE_ERROR;
}
if (tk.type == TK_BRACKET_OPEN) {
- if (array_size > 0) {
- _set_error("Array size is already defined!");
- return ERR_PARSE_ERROR;
+ Error error = _parse_array_size(nullptr, constants, true, nullptr, &varying.array_size, nullptr);
+ if (error != OK) {
+ return error;
}
tk = _get_token();
- if (tk.is_integer_constant() && tk.constant > 0) {
- varying.array_size = (int)tk.constant;
-
- tk = _get_token();
- if (tk.type == TK_BRACKET_CLOSE) {
- tk = _get_token();
- if (tk.type != TK_SEMICOLON) {
- _set_error("Expected ';'");
- return ERR_PARSE_ERROR;
- }
- } else {
- _set_error("Expected ']'");
- return ERR_PARSE_ERROR;
- }
- } else {
- _set_error("Expected integer constant > 0");
- return ERR_PARSE_ERROR;
- }
}
shader->varyings[name] = varying;
@@ -8343,7 +8209,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
} break;
case TK_SHADER_TYPE: {
- _set_error("Shader type is already defined.");
+ _set_error(RTR("Shader type is already defined."));
return ERR_PARSE_ERROR;
} break;
default: {
@@ -8369,19 +8235,23 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (shader->structs.has(tk.text)) {
if (precision != PRECISION_DEFAULT) {
- _set_error("Precision modifier cannot be used on structs.");
+ _set_error(RTR("The precision modifier cannot be used on structs."));
return ERR_PARSE_ERROR;
}
is_struct = true;
struct_name = tk.text;
} else {
if (!is_token_datatype(tk.type)) {
- _set_error("Expected constant, function, uniform or varying");
+ _set_error(RTR("Expected constant, function, uniform or varying."));
return ERR_PARSE_ERROR;
}
if (!is_token_variable_datatype(tk.type)) {
- _set_error("Invalid data type for constants or function return (samplers not allowed)");
+ if (is_constant) {
+ _set_error(RTR("Invalid constant type (samplers are not allowed)."));
+ } else {
+ _set_error(RTR("Invalid function type (samplers are not allowed)."));
+ }
return ERR_PARSE_ERROR;
}
}
@@ -8395,36 +8265,18 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
bool unknown_size = false;
+ bool fixed_array_size = false;
if (tk.type == TK_BRACKET_OPEN) {
if (is_constant && RenderingServer::get_singleton()->is_low_end()) {
- _set_error("Global const arrays are only supported on high-end platform!");
+ _set_error(RTR("Global constant arrays are only supported on high-end platforms."));
return ERR_PARSE_ERROR;
}
- bool error = false;
- tk = _get_token();
-
- if (tk.is_integer_constant()) {
- array_size = (int)tk.constant;
- if (array_size > 0) {
- tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return ERR_PARSE_ERROR;
- }
- } else {
- error = true;
- }
- } else if (tk.type == TK_BRACKET_CLOSE) {
- unknown_size = true;
- } else {
- error = true;
- }
- if (error) {
- _set_error("Expected integer constant > 0 or ']'");
- return ERR_PARSE_ERROR;
+ Error error = _parse_array_size(nullptr, constants, !is_constant, nullptr, &array_size, &unknown_size);
+ if (error != OK) {
+ return error;
}
-
+ fixed_array_size = true;
prev_pos = _get_tkpos();
}
@@ -8434,27 +8286,26 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (name == StringName()) {
if (is_constant) {
- _set_error("Expected identifier or '[' after datatype.");
+ _set_error(RTR("Expected an identifier or '[' after type."));
} else {
- _set_error("Expected function name after datatype.");
+ _set_error(RTR("Expected a function name after type."));
}
return ERR_PARSE_ERROR;
}
if (shader->structs.has(name) || _find_identifier(nullptr, false, constants, name) || has_builtin(p_functions, name)) {
- _set_error("Redefinition of '" + String(name) + "'");
+ _set_redefinition_error(String(name));
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_OPEN) {
if (type == TYPE_VOID) {
- _set_error("Expected '(' after function identifier");
+ _set_error(RTR("Expected '(' after function identifier."));
return ERR_PARSE_ERROR;
}
//variable
- bool first = true;
while (true) {
ShaderNode::Constant constant;
constant.name = name;
@@ -8462,46 +8313,30 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
constant.type_str = struct_name;
constant.precision = precision;
constant.initializer = nullptr;
- constant.array_size = (first ? array_size : 0);
- first = false;
+ constant.array_size = array_size;
if (tk.type == TK_BRACKET_OPEN) {
if (RenderingServer::get_singleton()->is_low_end()) {
- _set_error("Global const arrays are only supported on high-end platform!");
+ _set_error(RTR("Global const arrays are only supported on high-end platforms."));
return ERR_PARSE_ERROR;
}
- if (constant.array_size > 0 || unknown_size) {
- _set_error("Array size is already defined!");
- return ERR_PARSE_ERROR;
+ Error error = _parse_array_size(nullptr, constants, false, nullptr, &constant.array_size, &unknown_size);
+ if (error != OK) {
+ return error;
}
tk = _get_token();
- if (tk.type == TK_BRACKET_CLOSE) {
- unknown_size = true;
- tk = _get_token();
- } else if (tk.is_integer_constant() && ((int)tk.constant) > 0) {
- constant.array_size = (int)tk.constant;
- tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return ERR_PARSE_ERROR;
- }
- tk = _get_token();
- } else {
- _set_error("Expected integer constant > 0 or ']'");
- return ERR_PARSE_ERROR;
- }
}
if (tk.type == TK_OP_ASSIGN) {
if (!is_constant) {
- _set_error("Expected 'const' keyword before constant definition");
+ _set_error(vformat(RTR("Global non-constant variables are not supported. Expected '%s' keyword before constant definition."), "const"));
return ERR_PARSE_ERROR;
}
if (constant.array_size > 0 || unknown_size) {
bool full_def = false;
- ArrayDeclarationNode::Declaration decl;
+ VariableDeclarationNode::Declaration decl;
decl.name = name;
decl.size = constant.array_size;
@@ -8509,7 +8344,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (tk.type != TK_CURLY_BRACKET_OPEN) {
if (unknown_size) {
- _set_error("Expected '{'");
+ _set_expected_error("{");
return ERR_PARSE_ERROR;
}
@@ -8520,7 +8355,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
precision2 = get_token_precision(tk.type);
tk = _get_token();
if (!is_token_nonvoid_datatype(tk.type)) {
- _set_error("Expected datatype after precision");
+ _set_error(RTR("Expected data type after precision modifier."));
return ERR_PARSE_ERROR;
}
}
@@ -8533,83 +8368,60 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
struct_name2 = tk.text;
} else {
if (!is_token_variable_datatype(tk.type)) {
- _set_error("Invalid data type for array");
+ _set_error(RTR("Invalid data type for the array."));
return ERR_PARSE_ERROR;
}
type2 = get_token_datatype(tk.type);
}
int array_size2 = 0;
-
tk = _get_token();
+
if (tk.type == TK_BRACKET_OPEN) {
- prev_pos = _get_tkpos();
- tk = _get_token();
- if (tk.type == TK_BRACKET_CLOSE) {
+ bool is_unknown_size = false;
+ Error error = _parse_array_size(nullptr, constants, false, nullptr, &array_size2, &is_unknown_size);
+ if (error != OK) {
+ return error;
+ }
+ if (is_unknown_size) {
array_size2 = constant.array_size;
- tk = _get_token();
- } else {
- _set_tkpos(prev_pos);
-
- Node *n = _parse_and_reduce_expression(nullptr, constants);
- if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
- _set_error("Expected single integer constant > 0");
- return ERR_PARSE_ERROR;
- }
-
- ConstantNode *cnode = (ConstantNode *)n;
- if (cnode->values.size() == 1) {
- array_size2 = cnode->values[0].sint;
- if (array_size2 <= 0) {
- _set_error("Expected single integer constant > 0");
- return ERR_PARSE_ERROR;
- }
- } else {
- _set_error("Expected single integer constant > 0");
- return ERR_PARSE_ERROR;
- }
-
- tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']");
- return ERR_PARSE_ERROR;
- } else {
- tk = _get_token();
- }
}
+ tk = _get_token();
} else {
- _set_error("Expected '[");
+ _set_expected_error("[");
return ERR_PARSE_ERROR;
}
if (constant.precision != precision2 || constant.type != type2 || struct_name != struct_name2 || constant.array_size != array_size2) {
- String error_str = "Cannot convert from '";
+ String from;
if (type2 == TYPE_STRUCT) {
- error_str += struct_name2;
+ from += struct_name2;
} else {
if (precision2 != PRECISION_DEFAULT) {
- error_str += get_precision_name(precision2);
- error_str += " ";
+ from += get_precision_name(precision2);
+ from += " ";
}
- error_str += get_datatype_name(type2);
+ from += get_datatype_name(type2);
}
- error_str += "[";
- error_str += itos(array_size2);
- error_str += "]'";
- error_str += " to '";
+ from += "[";
+ from += itos(array_size2);
+ from += "]'";
+
+ String to;
if (type == TYPE_STRUCT) {
- error_str += struct_name;
+ to += struct_name;
} else {
if (precision != PRECISION_DEFAULT) {
- error_str += get_precision_name(precision);
- error_str += " ";
+ to += get_precision_name(precision);
+ to += " ";
}
- error_str += get_datatype_name(type);
+ to += get_datatype_name(type);
}
- error_str += "[";
- error_str += itos(constant.array_size);
- error_str += "]'";
- _set_error(error_str);
+ to += "[";
+ to += itos(constant.array_size);
+ to += "]'";
+
+ _set_error(vformat(RTR("Cannot convert from '%s' to '%s'."), from, to));
return ERR_PARSE_ERROR;
}
}
@@ -8618,13 +8430,13 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (unknown_size) {
if (!curly) {
- _set_error("Expected '{'");
+ _set_expected_error("{");
return ERR_PARSE_ERROR;
}
} else {
if (full_def) {
if (curly) {
- _set_error("Expected '('");
+ _set_expected_error("(");
return ERR_PARSE_ERROR;
}
}
@@ -8638,7 +8450,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
if (n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) {
- _set_error("Expected constant expression");
+ _set_error(RTR("Expected constant expression."));
return ERR_PARSE_ERROR;
}
@@ -8658,9 +8470,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
break;
} else {
if (curly) {
- _set_error("Expected '}' or ','");
+ _set_expected_error("}", ",");
} else {
- _set_error("Expected ')' or ','");
+ _set_expected_error(")", ",");
}
return ERR_PARSE_ERROR;
}
@@ -8669,11 +8481,13 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
decl.size = decl.initializer.size();
constant.array_size = decl.initializer.size();
} else if (decl.initializer.size() != constant.array_size) {
- _set_error("Array size mismatch");
+ _set_error(RTR("Array size mismatch."));
return ERR_PARSE_ERROR;
}
}
+ array_size = constant.array_size;
+
ConstantNode *expr = memnew(ConstantNode);
expr->datatype = constant.type;
@@ -8695,7 +8509,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
OperatorNode *op = ((OperatorNode *)expr);
for (int i = 1; i < op->arguments.size(); i++) {
if (!_check_node_constness(op->arguments[i])) {
- _set_error("Expected constant expression for argument '" + itos(i - 1) + "' of function call after '='");
+ _set_error(vformat(RTR("Expected constant expression for argument %d of function call after '='."), i - 1));
return ERR_PARSE_ERROR;
}
}
@@ -8710,10 +8524,10 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
} else {
if (constant.array_size > 0 || unknown_size) {
- _set_error("Expected array initialization");
+ _set_error(RTR("Expected array initialization."));
return ERR_PARSE_ERROR;
} else {
- _set_error("Expected initialization of constant");
+ _set_error(RTR("Expected initialization of constant."));
return ERR_PARSE_ERROR;
}
}
@@ -8729,27 +8543,32 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (tk.type == TK_COMMA) {
tk = _get_token();
if (tk.type != TK_IDENTIFIER) {
- _set_error("Expected identifier after type");
+ _set_error(RTR("Expected an identifier after type."));
return ERR_PARSE_ERROR;
}
name = tk.text;
if (_find_identifier(nullptr, false, constants, name)) {
- _set_error("Redefinition of '" + String(name) + "'");
+ _set_redefinition_error(String(name));
return ERR_PARSE_ERROR;
}
if (has_builtin(p_functions, name)) {
- _set_error("Redefinition of '" + String(name) + "'");
+ _set_redefinition_error(String(name));
return ERR_PARSE_ERROR;
}
tk = _get_token();
+ if (!fixed_array_size) {
+ array_size = 0;
+ }
+ unknown_size = false;
+
} else if (tk.type == TK_SEMICOLON) {
break;
} else {
- _set_error("Expected ',' or ';' after constant");
+ _set_expected_error(",", ";");
return ERR_PARSE_ERROR;
}
}
@@ -8776,7 +8595,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
for (int i = 0; i < shader->functions.size(); i++) {
if (!shader->functions[i].callable && shader->functions[i].name == name) {
- _set_error("Redefinition of '" + String(name) + "'");
+ _set_redefinition_error(String(name));
return ERR_PARSE_ERROR;
}
}
@@ -8831,14 +8650,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
} else if (tk.type == TK_ARG_OUT) {
if (is_const) {
- _set_error("'out' qualifier cannot be used within a function parameter declared with 'const'.");
+ _set_error(vformat(RTR("The '%s' qualifier cannot be used within a function parameter declared with '%s'."), "out", "const"));
return ERR_PARSE_ERROR;
}
qualifier = ARGUMENT_QUALIFIER_OUT;
tk = _get_token();
} else if (tk.type == TK_ARG_INOUT) {
if (is_const) {
- _set_error("'inout' qualifier cannot be used within a function parameter declared with 'const'.");
+ _set_error(vformat(RTR("The '%s' qualifier cannot be used within a function parameter declared with '%s'."), "inout", "const"));
return ERR_PARSE_ERROR;
}
qualifier = ARGUMENT_QUALIFIER_INOUT;
@@ -8869,19 +8688,19 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
#endif // DEBUG_ENABLED
if (use_precision) {
- _set_error("Precision modifier cannot be used on structs.");
+ _set_error(RTR("The precision modifier cannot be used on structs."));
return ERR_PARSE_ERROR;
}
}
if (!is_struct && !is_token_datatype(tk.type)) {
- _set_error("Expected a valid datatype for argument");
+ _set_error(RTR("Expected a valid data type for argument."));
return ERR_PARSE_ERROR;
}
if (qualifier == ARGUMENT_QUALIFIER_OUT || qualifier == ARGUMENT_QUALIFIER_INOUT) {
if (is_sampler_type(get_token_datatype(tk.type))) {
- _set_error("Opaque types cannot be output parameters.");
+ _set_error(RTR("Opaque types cannot be output parameters."));
return ERR_PARSE_ERROR;
}
}
@@ -8894,7 +8713,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
return ERR_PARSE_ERROR;
}
if (ptype == TYPE_VOID) {
- _set_error("void not allowed in argument");
+ _set_error(RTR("Void type not allowed as argument."));
return ERR_PARSE_ERROR;
}
}
@@ -8902,32 +8721,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- bool error = false;
- tk = _get_token();
-
- if (tk.is_integer_constant()) {
- arg_array_size = (int)tk.constant;
-
- if (arg_array_size > 0) {
- tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return ERR_PARSE_ERROR;
- }
- } else {
- error = true;
- }
- } else {
- error = true;
- }
- if (error) {
- _set_error("Expected integer constant > 0");
- return ERR_PARSE_ERROR;
+ Error error = _parse_array_size(nullptr, constants, true, nullptr, &arg_array_size, nullptr);
+ if (error != OK) {
+ return error;
}
tk = _get_token();
}
if (tk.type != TK_IDENTIFIER) {
- _set_error("Expected identifier for argument name");
+ _set_error(RTR("Expected an identifier for argument name."));
return ERR_PARSE_ERROR;
}
@@ -8936,13 +8737,13 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
ShaderLanguage::IdentifierType itype;
if (_find_identifier(func_node->body, false, builtins, pname, (ShaderLanguage::DataType *)nullptr, &itype)) {
if (itype != IDENTIFIER_FUNCTION) {
- _set_error("Redefinition of '" + String(pname) + "'");
+ _set_redefinition_error(String(pname));
return ERR_PARSE_ERROR;
}
}
if (has_builtin(p_functions, pname)) {
- _set_error("Redefinition of '" + String(pname) + "'");
+ _set_redefinition_error(String(pname));
return ERR_PARSE_ERROR;
}
@@ -8960,32 +8761,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- if (arg_array_size > 0) {
- _set_error("Array size is already defined!");
- return ERR_PARSE_ERROR;
- }
- bool error = false;
- tk = _get_token();
-
- if (tk.is_integer_constant()) {
- arg_array_size = (int)tk.constant;
-
- if (arg_array_size > 0) {
- tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return ERR_PARSE_ERROR;
- }
- } else {
- error = true;
- }
- } else {
- error = true;
- }
-
- if (error) {
- _set_error("Expected integer constant > 0");
- return ERR_PARSE_ERROR;
+ Error error = _parse_array_size(nullptr, constants, true, nullptr, &arg_array_size, nullptr);
+ if (error != OK) {
+ return error;
}
tk = _get_token();
}
@@ -8997,7 +8775,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
//do none and go on
} else if (tk.type != TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ',' or ')' after identifier");
+ _set_expected_error(",", ")");
return ERR_PARSE_ERROR;
}
}
@@ -9005,11 +8783,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (p_functions.has(name)) {
//if one of the core functions, make sure they are of the correct form
if (func_node->arguments.size() > 0) {
- _set_error("Function '" + String(name) + "' expects no arguments.");
+ _set_error(vformat(RTR("Function '%s' expects no arguments."), String(name)));
return ERR_PARSE_ERROR;
}
if (func_node->return_type != TYPE_VOID) {
- _set_error("Function '" + String(name) + "' must be of void return type.");
+ _set_error(vformat(RTR("Function '%s' must be of '%s' return type."), String(name), "void"));
return ERR_PARSE_ERROR;
}
}
@@ -9017,7 +8795,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
//all good let's parse inside the function!
tk = _get_token();
if (tk.type != TK_CURLY_BRACKET_OPEN) {
- _set_error("Expected '{' to begin function");
+ _set_error(RTR("Expected a '{' to begin function."));
return ERR_PARSE_ERROR;
}
@@ -9031,7 +8809,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (func_node->return_type != DataType::TYPE_VOID) {
BlockNode *block = func_node->body;
if (_find_last_flow_op_in_block(block, FlowOperation::FLOW_OP_RETURN) != OK) {
- _set_error("Expected at least one return statement in a non-void function.");
+ _set_error(vformat(RTR("Expected at least one '%s' statement in a non-void function."), "return"));
return ERR_PARSE_ERROR;
}
}
@@ -9044,7 +8822,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
#ifdef DEBUG_ENABLED
if (check_device_limit_warnings && uniform_buffer_exceeded_line != -1) {
- _add_warning(ShaderWarning::DEVICE_LIMIT_EXCEEDED, uniform_buffer_exceeded_line, "uniform buffer", { uniform_buffer_size, max_uniform_buffer_size });
+ _add_warning(ShaderWarning::DEVICE_LIMIT_EXCEEDED, uniform_buffer_exceeded_line, RTR("uniform buffer"), { uniform_buffer_size, max_uniform_buffer_size });
}
#endif // DEBUG_ENABLED
return OK;
@@ -9318,6 +9096,19 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
} break;
case COMPLETION_MAIN_FUNCTION: {
for (const KeyValue<StringName, FunctionInfo> &E : p_info.functions) {
+ if (!E.value.main_function) {
+ continue;
+ }
+ bool found = false;
+ for (int i = 0; i < shader->functions.size(); i++) {
+ if (shader->functions[i].name == E.key) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ continue;
+ }
ScriptCodeCompletionOption option(E.key, ScriptCodeCompletionOption::KIND_FUNCTION);
r_options->push_back(option);
}
@@ -9383,6 +9174,9 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
}
}
+ for (const KeyValue<StringName, ShaderNode::Constant> &E : shader->constants) {
+ matches.insert(E.key, ScriptCodeCompletionOption::KIND_CONSTANT);
+ }
for (const KeyValue<StringName, ShaderNode::Varying> &E : shader->varyings) {
matches.insert(E.key, ScriptCodeCompletionOption::KIND_VARIABLE);
}
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index bc6dae7fa2..f39b21621d 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -362,7 +362,6 @@ public:
TYPE_CONTROL_FLOW,
TYPE_MEMBER,
TYPE_ARRAY,
- TYPE_ARRAY_DECLARATION,
TYPE_ARRAY_CONSTRUCT,
TYPE_STRUCT,
};
@@ -428,7 +427,10 @@ public:
struct Declaration {
StringName name;
- Node *initializer;
+ uint32_t size = 0U;
+ Node *size_expression = nullptr;
+ Vector<Node *> initializer;
+ bool single_expression = false;
};
Vector<Declaration> declarations;
@@ -471,27 +473,6 @@ public:
Node(TYPE_ARRAY_CONSTRUCT) {}
};
- struct ArrayDeclarationNode : public Node {
- DataPrecision precision = PRECISION_DEFAULT;
- DataType datatype = TYPE_VOID;
- String struct_name;
- bool is_const = false;
- Node *size_expression = nullptr;
-
- struct Declaration {
- StringName name;
- uint32_t size;
- Vector<Node *> initializer;
- bool single_expression;
- };
- Vector<Declaration> declarations;
-
- virtual DataType get_datatype() const override { return datatype; }
-
- ArrayDeclarationNode() :
- Node(TYPE_ARRAY_DECLARATION) {}
- };
-
struct ConstantNode : public Node {
DataType datatype = TYPE_VOID;
String struct_name = "";
@@ -505,7 +486,7 @@ public:
};
Vector<Value> values;
- Vector<ArrayDeclarationNode::Declaration> array_declarations;
+ Vector<VariableDeclarationNode::Declaration> array_declarations;
virtual DataType get_datatype() const override { return datatype; }
virtual String get_datatype_name() const override { return struct_name; }
@@ -523,7 +504,9 @@ public:
enum BlockType {
BLOCK_TYPE_STANDART,
- BLOCK_TYPE_FOR,
+ BLOCK_TYPE_FOR_INIT,
+ BLOCK_TYPE_FOR_CONDITION,
+ BLOCK_TYPE_FOR_EXPRESSION,
BLOCK_TYPE_SWITCH,
BLOCK_TYPE_CASE,
BLOCK_TYPE_DEFAULT,
@@ -545,6 +528,7 @@ public:
Map<StringName, Variable> variables;
List<Node *> statements;
bool single_statement = false;
+ bool use_comma_between_statements = false;
BlockNode() :
Node(TYPE_BLOCK) {}
@@ -964,6 +948,26 @@ private:
error_str = p_str;
}
+ void _set_expected_error(const String &p_what) {
+ _set_error(vformat(RTR("Expected a '%s'."), p_what));
+ }
+
+ void _set_expected_error(const String &p_first, const String p_second) {
+ _set_error(vformat(RTR("Expected a '%s' or '%s'."), p_first, p_second));
+ }
+
+ void _set_expected_after_error(const String &p_what, const String &p_after) {
+ _set_error(vformat(RTR("Expected a '%s' after '%s'."), p_what, p_after));
+ }
+
+ void _set_redefinition_error(const String &p_what) {
+ _set_error(vformat(RTR("Redefinition of '%s'."), p_what));
+ }
+
+ void _set_parsing_error() {
+ _set_error("Parser bug.");
+ }
+
static const char *token_names[TK_MAX];
Token _make_token(TokenType p_type, const StringName &p_text = StringName());
@@ -1045,11 +1049,8 @@ private:
bool _validate_varying_assign(ShaderNode::Varying &p_varying, String *r_message);
bool _check_node_constness(const Node *p_node) const;
- Node *_parse_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, int &r_array_size);
- Error _parse_global_array_size(int &r_array_size, const FunctionInfo &p_function_info);
- Error _parse_local_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, Node *&r_size_expression, int &r_array_size, bool &r_is_unknown_size);
-
Node *_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info);
+ Error _parse_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_forbid_unknown_size, Node **r_size_expression, int *r_array_size, bool *r_unknown_size);
Node *_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info);
Node *_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size);
ShaderLanguage::Node *_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node);
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index 9ae60c14cb..b8bb211a7a 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -435,7 +435,6 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["OBJECT_POSITION"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["UVW"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["EXTENTS"] = constt(ShaderLanguage::TYPE_VEC3);
- shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4);
shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["SDF"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["ALBEDO"] = ShaderLanguage::TYPE_VEC3;
shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["DENSITY"] = ShaderLanguage::TYPE_FLOAT;
diff --git a/servers/rendering/shader_warnings.cpp b/servers/rendering/shader_warnings.cpp
index f2e74c4d78..639b9bd165 100644
--- a/servers/rendering/shader_warnings.cpp
+++ b/servers/rendering/shader_warnings.cpp
@@ -48,23 +48,23 @@ const StringName &ShaderWarning::get_subject() const {
String ShaderWarning::get_message() const {
switch (code) {
case FLOAT_COMPARISON:
- return vformat("Direct floating-point comparison (this may not evaluate to `true` as you expect). Instead, use `abs(a - b) < 0.0001` for an approximate but predictable comparison.");
+ return vformat(RTR("Direct floating-point comparison (this may not evaluate to `true` as you expect). Instead, use `abs(a - b) < 0.0001` for an approximate but predictable comparison."));
case UNUSED_CONSTANT:
- return vformat("The const '%s' is declared but never used.", subject);
+ return vformat(RTR("The const '%s' is declared but never used."), subject);
case UNUSED_FUNCTION:
- return vformat("The function '%s' is declared but never used.", subject);
+ return vformat(RTR("The function '%s' is declared but never used."), subject);
case UNUSED_STRUCT:
- return vformat("The struct '%s' is declared but never used.", subject);
+ return vformat(RTR("The struct '%s' is declared but never used."), subject);
case UNUSED_UNIFORM:
- return vformat("The uniform '%s' is declared but never used.", subject);
+ return vformat(RTR("The uniform '%s' is declared but never used."), subject);
case UNUSED_VARYING:
- return vformat("The varying '%s' is declared but never used.", subject);
+ return vformat(RTR("The varying '%s' is declared but never used."), subject);
case UNUSED_LOCAL_VARIABLE:
- return vformat("The local variable '%s' is declared but never used.", subject);
+ return vformat(RTR("The local variable '%s' is declared but never used."), subject);
case FORMATTING_ERROR:
return subject;
case DEVICE_LIMIT_EXCEEDED:
- return vformat("The total size of the %s for this shader on this device has been exceeded (%s/%s). The shader may not work correctly.", subject, (int)extra_args[0], (int)extra_args[1]);
+ return vformat(RTR("The total size of the %s for this shader on this device has been exceeded (%d/%d). The shader may not work correctly."), subject, (int)extra_args[0], (int)extra_args[1]);
default:
break;
}
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 793c6f1379..584abbc351 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -1927,8 +1927,8 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(LIGHT_PARAM_MAX);
BIND_ENUM_CONSTANT(LIGHT_BAKE_DISABLED);
- BIND_ENUM_CONSTANT(LIGHT_BAKE_DYNAMIC);
BIND_ENUM_CONSTANT(LIGHT_BAKE_STATIC);
+ BIND_ENUM_CONSTANT(LIGHT_BAKE_DYNAMIC);
BIND_ENUM_CONSTANT(LIGHT_OMNI_SHADOW_DUAL_PARABOLOID);
BIND_ENUM_CONSTANT(LIGHT_OMNI_SHADOW_CUBE);
@@ -2317,7 +2317,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("environment_set_bg_energy", "env", "energy"), &RenderingServer::environment_set_bg_energy);
ClassDB::bind_method(D_METHOD("environment_set_canvas_max_layer", "env", "max_layer"), &RenderingServer::environment_set_canvas_max_layer);
ClassDB::bind_method(D_METHOD("environment_set_ambient_light", "env", "color", "ambient", "energy", "sky_contibution", "reflection_source"), &RenderingServer::environment_set_ambient_light, DEFVAL(RS::ENV_AMBIENT_SOURCE_BG), DEFVAL(1.0), DEFVAL(0.0), DEFVAL(RS::ENV_REFLECTION_SOURCE_BG));
- ClassDB::bind_method(D_METHOD("environment_set_glow", "env", "enable", "levels", "intensity", "strength", "mix", "bloom_threshold", "blend_mode", "hdr_bleed_threshold", "hdr_bleed_scale", "hdr_luminance_cap"), &RenderingServer::environment_set_glow);
+ ClassDB::bind_method(D_METHOD("environment_set_glow", "env", "enable", "levels", "intensity", "strength", "mix", "bloom_threshold", "blend_mode", "hdr_bleed_threshold", "hdr_bleed_scale", "hdr_luminance_cap", "glow_map_strength", "glow_map"), &RenderingServer::environment_set_glow);
ClassDB::bind_method(D_METHOD("environment_set_tonemap", "env", "tone_mapper", "exposure", "white", "auto_exposure", "min_luminance", "max_luminance", "auto_exp_speed", "auto_exp_grey"), &RenderingServer::environment_set_tonemap);
ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "use_1d_color_correction", "color_correction"), &RenderingServer::environment_set_adjustment);
ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance"), &RenderingServer::environment_set_ssr);
@@ -2388,10 +2388,6 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(ENV_SSIL_QUALITY_HIGH);
BIND_ENUM_CONSTANT(ENV_SSIL_QUALITY_ULTRA);
- BIND_ENUM_CONSTANT(ENV_SDFGI_CASCADES_4);
- BIND_ENUM_CONSTANT(ENV_SDFGI_CASCADES_6);
- BIND_ENUM_CONSTANT(ENV_SDFGI_CASCADES_8);
-
BIND_ENUM_CONSTANT(ENV_SDFGI_Y_SCALE_DISABLED);
BIND_ENUM_CONSTANT(ENV_SDFGI_Y_SCALE_75_PERCENT);
BIND_ENUM_CONSTANT(ENV_SDFGI_Y_SCALE_50_PERCENT);
@@ -2775,13 +2771,14 @@ void RenderingServer::mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry
const Geometry3D::MeshData::Face &f = p_mesh_data.faces[i];
for (int j = 2; j < f.indices.size(); j++) {
-#define _ADD_VERTEX(m_idx) \
- vertices.push_back(p_mesh_data.vertices[f.indices[m_idx]]); \
- normals.push_back(f.plane.normal);
+ vertices.push_back(p_mesh_data.vertices[f.indices[0]]);
+ normals.push_back(f.plane.normal);
+
+ vertices.push_back(p_mesh_data.vertices[f.indices[j - 1]]);
+ normals.push_back(f.plane.normal);
- _ADD_VERTEX(0);
- _ADD_VERTEX(j - 1);
- _ADD_VERTEX(j);
+ vertices.push_back(p_mesh_data.vertices[f.indices[j]]);
+ normals.push_back(f.plane.normal);
}
}
@@ -2822,7 +2819,6 @@ RenderingServer::RenderingServer() {
GLOBAL_DEF_RST("rendering/textures/vram_compression/import_s3tc", true);
GLOBAL_DEF_RST("rendering/textures/vram_compression/import_etc", false);
GLOBAL_DEF_RST("rendering/textures/vram_compression/import_etc2", true);
- GLOBAL_DEF_RST("rendering/textures/vram_compression/import_pvrtc", false);
GLOBAL_DEF("rendering/textures/lossless_compression/force_png", false);
GLOBAL_DEF("rendering/textures/lossless_compression/webp_compression_level", 2);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 5dbec04665..472fff1bf1 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -447,8 +447,8 @@ public:
enum LightBakeMode {
LIGHT_BAKE_DISABLED,
- LIGHT_BAKE_DYNAMIC,
LIGHT_BAKE_STATIC,
+ LIGHT_BAKE_DYNAMIC,
};
virtual void light_set_bake_mode(RID p_light, LightBakeMode p_bake_mode) = 0;
@@ -991,7 +991,7 @@ public:
ENV_GLOW_BLEND_MODE_MIX,
};
- virtual void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) = 0;
+ virtual void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) = 0;
virtual void environment_glow_set_use_bicubic_upscale(bool p_enable) = 0;
virtual void environment_glow_set_use_high_quality(bool p_enable) = 0;
@@ -1041,19 +1041,13 @@ public:
virtual void environment_set_ssil_quality(EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) = 0;
- enum EnvironmentSDFGICascades {
- ENV_SDFGI_CASCADES_4,
- ENV_SDFGI_CASCADES_6,
- ENV_SDFGI_CASCADES_8,
- };
-
enum EnvironmentSDFGIYScale {
ENV_SDFGI_Y_SCALE_DISABLED,
ENV_SDFGI_Y_SCALE_75_PERCENT,
ENV_SDFGI_Y_SCALE_50_PERCENT
};
- virtual void environment_set_sdfgi(RID p_env, bool p_enable, EnvironmentSDFGICascades p_cascades, float p_min_cell_size, EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0;
+ virtual void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0;
enum EnvironmentSDFGIRayCount {
ENV_SDFGI_RAY_COUNT_4,
@@ -1608,7 +1602,6 @@ VARIANT_ENUM_CAST(RenderingServer::EnvironmentToneMapper);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSRRoughnessQuality);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSAOQuality);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSILQuality);
-VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGICascades);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGIFramesToConverge);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGIRayCount);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGIFramesToUpdateLight);
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index a2195d1ddf..db1f589334 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -210,10 +210,14 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_shaped_text_set_preserve_control, "shaped", "enabled");
GDVIRTUAL_BIND(_shaped_text_get_preserve_control, "shaped");
- GDVIRTUAL_BIND(_shaped_text_add_string, "shaped", "text", "fonts", "size", "opentype_features", "language");
+ GDVIRTUAL_BIND(_shaped_text_add_string, "shaped", "text", "fonts", "size", "opentype_features", "language", "meta");
GDVIRTUAL_BIND(_shaped_text_add_object, "shaped", "key", "size", "inline_align", "length");
GDVIRTUAL_BIND(_shaped_text_resize_object, "shaped", "key", "size", "inline_align");
+ GDVIRTUAL_BIND(_shaped_get_span_count, "shaped");
+ GDVIRTUAL_BIND(_shaped_get_span_meta, "shaped", "index");
+ GDVIRTUAL_BIND(_shaped_set_span_update_font, "shaped", "index", "fonts", "size", "opentype_features");
+
GDVIRTUAL_BIND(_shaped_text_substr, "shaped", "start", "length");
GDVIRTUAL_BIND(_shaped_text_get_parent, "shaped");
@@ -271,6 +275,9 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_format_number, "string", "language");
GDVIRTUAL_BIND(_parse_number, "string", "language");
GDVIRTUAL_BIND(_percent_sign, "language");
+
+ GDVIRTUAL_BIND(_string_to_upper, "string", "language");
+ GDVIRTUAL_BIND(_string_to_lower, "string", "language");
}
bool TextServerExtension::has_feature(Feature p_feature) const {
@@ -1015,13 +1022,13 @@ bool TextServerExtension::shaped_text_get_preserve_control(RID p_shaped) const {
return false;
}
-bool TextServerExtension::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+bool TextServerExtension::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
bool ret;
Array fonts;
for (int i = 0; i < p_fonts.size(); i++) {
fonts.push_back(p_fonts[i]);
}
- if (GDVIRTUAL_CALL(_shaped_text_add_string, p_shaped, p_text, fonts, p_size, p_opentype_features, p_language, ret)) {
+ if (GDVIRTUAL_CALL(_shaped_text_add_string, p_shaped, p_text, fonts, p_size, p_opentype_features, p_language, p_meta, ret)) {
return ret;
}
return false;
@@ -1043,6 +1050,30 @@ bool TextServerExtension::shaped_text_resize_object(RID p_shaped, Variant p_key,
return false;
}
+int TextServerExtension::shaped_get_span_count(RID p_shaped) const {
+ int ret;
+ if (GDVIRTUAL_CALL(_shaped_get_span_count, p_shaped, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+Variant TextServerExtension::shaped_get_span_meta(RID p_shaped, int p_index) const {
+ Variant ret;
+ if (GDVIRTUAL_CALL(_shaped_get_span_meta, p_shaped, p_index, ret)) {
+ return ret;
+ }
+ return false;
+}
+
+void TextServerExtension::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) {
+ Array fonts;
+ for (int i = 0; i < p_fonts.size(); i++) {
+ fonts.push_back(p_fonts[i]);
+ }
+ GDVIRTUAL_CALL(_shaped_set_span_update_font, p_shaped, p_index, fonts, p_size, p_opentype_features);
+}
+
RID TextServerExtension::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
RID ret;
if (GDVIRTUAL_CALL(_shaped_text_substr, p_shaped, p_start, p_length, ret)) {
@@ -1365,6 +1396,22 @@ String TextServerExtension::percent_sign(const String &p_language) const {
return TextServer::percent_sign(p_language);
}
+String TextServerExtension::string_to_upper(const String &p_string, const String &p_language) const {
+ String ret;
+ if (GDVIRTUAL_CALL(_string_to_upper, p_string, p_language, ret)) {
+ return ret;
+ }
+ return p_string;
+}
+
+String TextServerExtension::string_to_lower(const String &p_string, const String &p_language) const {
+ String ret;
+ if (GDVIRTUAL_CALL(_string_to_lower, p_string, p_language, ret)) {
+ return ret;
+ }
+ return p_string;
+}
+
TextServerExtension::TextServerExtension() {
//NOP
}
diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h
index 77f2ced951..df1f1d7a70 100644
--- a/servers/text/text_server_extension.h
+++ b/servers/text/text_server_extension.h
@@ -343,13 +343,20 @@ public:
GDVIRTUAL2(_shaped_text_set_preserve_control, RID, bool);
GDVIRTUAL1RC(bool, _shaped_text_get_preserve_control, RID);
- virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
+ virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override;
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
- GDVIRTUAL6R(bool, _shaped_text_add_string, RID, const String &, const Array &, int, const Dictionary &, const String &);
+ GDVIRTUAL7R(bool, _shaped_text_add_string, RID, const String &, const Array &, int, const Dictionary &, const String &, const Variant &);
GDVIRTUAL5R(bool, _shaped_text_add_object, RID, Variant, const Size2 &, InlineAlignment, int);
GDVIRTUAL4R(bool, _shaped_text_resize_object, RID, Variant, const Size2 &, InlineAlignment);
+ virtual int shaped_get_span_count(RID p_shaped) const override;
+ virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override;
+ virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override;
+ GDVIRTUAL1RC(int, _shaped_get_span_count, RID);
+ GDVIRTUAL2RC(Variant, _shaped_get_span_meta, RID, int);
+ GDVIRTUAL5(_shaped_set_span_update_font, RID, int, const Array &, int, const Dictionary &);
+
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
GDVIRTUAL3RC(RID, _shaped_text_substr, RID, int, int);
@@ -449,6 +456,11 @@ public:
GDVIRTUAL2RC(String, _parse_number, const String &, const String &);
GDVIRTUAL1RC(String, _percent_sign, const String &);
+ virtual String string_to_upper(const String &p_string, const String &p_language = "") const override;
+ virtual String string_to_lower(const String &p_string, const String &p_language = "") const override;
+ GDVIRTUAL2RC(String, _string_to_upper, const String &, const String &);
+ GDVIRTUAL2RC(String, _string_to_lower, const String &, const String &);
+
TextServerExtension();
~TextServerExtension();
};
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index 0cc4358785..b7cd39b9b2 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -363,10 +363,14 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("shaped_text_set_preserve_control", "shaped", "enabled"), &TextServer::shaped_text_set_preserve_control);
ClassDB::bind_method(D_METHOD("shaped_text_get_preserve_control", "shaped"), &TextServer::shaped_text_get_preserve_control);
- ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1));
ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER));
+ ClassDB::bind_method(D_METHOD("shaped_get_span_count", "shaped"), &TextServer::shaped_get_span_count);
+ ClassDB::bind_method(D_METHOD("shaped_get_span_meta", "shaped", "index"), &TextServer::shaped_get_span_meta);
+ ClassDB::bind_method(D_METHOD("shaped_set_span_update_font", "shaped", "index", "fonts", "size", "opentype_features"), &TextServer::shaped_set_span_update_font, DEFVAL(Dictionary()));
+
ClassDB::bind_method(D_METHOD("shaped_text_substr", "shaped", "start", "length"), &TextServer::shaped_text_substr);
ClassDB::bind_method(D_METHOD("shaped_text_get_parent", "shaped"), &TextServer::shaped_text_get_parent);
ClassDB::bind_method(D_METHOD("shaped_text_fit_to_width", "shaped", "width", "jst_flags"), &TextServer::shaped_text_fit_to_width, DEFVAL(JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA));
@@ -422,6 +426,9 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("strip_diacritics", "string"), &TextServer::strip_diacritics);
+ ClassDB::bind_method(D_METHOD("string_to_upper", "string", "language"), &TextServer::string_to_upper, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("string_to_lower", "string", "language"), &TextServer::string_to_lower, DEFVAL(""));
+
/* Direction */
BIND_ENUM_CONSTANT(DIRECTION_AUTO);
BIND_ENUM_CONSTANT(DIRECTION_LTR);
@@ -480,6 +487,7 @@ void TextServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_BREAK_ITERATORS);
BIND_ENUM_CONSTANT(FEATURE_FONT_SYSTEM);
BIND_ENUM_CONSTANT(FEATURE_FONT_VARIABLE);
+ BIND_ENUM_CONSTANT(FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION);
BIND_ENUM_CONSTANT(FEATURE_USE_SUPPORT_DATA);
/* FT Contour Point Types */
diff --git a/servers/text_server.h b/servers/text_server.h
index 07f0ae5045..6724c02caf 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -109,7 +109,8 @@ public:
FEATURE_BREAK_ITERATORS = 1 << 4,
FEATURE_FONT_SYSTEM = 1 << 5,
FEATURE_FONT_VARIABLE = 1 << 6,
- FEATURE_USE_SUPPORT_DATA = 1 << 7
+ FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION = 1 << 7,
+ FEATURE_USE_SUPPORT_DATA = 1 << 8,
};
enum ContourPointTag {
@@ -154,20 +155,6 @@ protected:
TextServer::Direction direction = DIRECTION_LTR; // Desired text direction.
TextServer::Orientation orientation = ORIENTATION_HORIZONTAL;
- struct Span {
- int start = -1;
- int end = -1;
-
- Vector<RID> fonts;
- int font_size = 0;
-
- Variant embedded_key;
-
- String language;
- Dictionary features;
- };
- Vector<Span> spans;
-
struct EmbeddedObject {
int pos = 0;
InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
@@ -386,10 +373,14 @@ public:
virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) = 0;
virtual bool shaped_text_get_preserve_control(RID p_shaped) const = 0;
- virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") = 0;
+ virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) = 0;
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) = 0;
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) = 0;
+ virtual int shaped_get_span_count(RID p_shaped) const = 0;
+ virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const = 0;
+ virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) = 0;
+
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const = 0; // Copy shaped substring (e.g. line break) without reshaping, but correctly reordered, preservers range.
virtual RID shaped_text_get_parent(RID p_shaped) const = 0;
@@ -457,6 +448,10 @@ public:
virtual String strip_diacritics(const String &p_string) const;
+ // Other string operations.
+ virtual String string_to_upper(const String &p_string, const String &p_language = "") const = 0;
+ virtual String string_to_lower(const String &p_string, const String &p_language = "") const = 0;
+
TextServer();
~TextServer();
};
diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp
index 758f2416ec..7ae111b5e7 100644
--- a/servers/xr/xr_interface.cpp
+++ b/servers/xr/xr_interface.cpp
@@ -168,8 +168,5 @@ XRInterface::TrackingStatus XRInterface::get_tracking_status() const {
return XR_UNKNOWN_TRACKING;
}
-void XRInterface::notification(int p_what) {
-}
-
void XRInterface::trigger_haptic_pulse(const String &p_action_name, const StringName &p_tracker_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec) {
}
diff --git a/servers/xr/xr_interface.h b/servers/xr/xr_interface.h
index aee98f8fee..6e105ffc26 100644
--- a/servers/xr/xr_interface.h
+++ b/servers/xr/xr_interface.h
@@ -123,10 +123,13 @@ public:
// note, external color/depth/vrs texture support will be added here soon.
- virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) = 0; /* commit rendered views to the XR interface */
-
virtual void process() = 0;
- virtual void notification(int p_what);
+ virtual void pre_render(){};
+ virtual bool pre_draw_viewport(RID p_render_target) { return true; }; /* inform XR interface we are about to start our viewport draw process */
+ virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) = 0; /* inform XR interface we finished our viewport draw process */
+ virtual void end_frame(){};
+
+ virtual void notification(int p_what){};
XRInterface();
~XRInterface();
diff --git a/servers/xr/xr_interface_extension.cpp b/servers/xr/xr_interface_extension.cpp
index 9dae3b162d..18131c1e89 100644
--- a/servers/xr/xr_interface_extension.cpp
+++ b/servers/xr/xr_interface_extension.cpp
@@ -52,9 +52,12 @@ void XRInterfaceExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_transform_for_view, "view", "cam_transform");
GDVIRTUAL_BIND(_get_projection_for_view, "view", "aspect", "z_near", "z_far");
- GDVIRTUAL_BIND(_commit_views, "render_target", "screen_rect");
-
GDVIRTUAL_BIND(_process);
+ GDVIRTUAL_BIND(_pre_render);
+ GDVIRTUAL_BIND(_pre_draw_viewport, "render_target");
+ GDVIRTUAL_BIND(_post_draw_viewport, "render_target", "screen_rect");
+ GDVIRTUAL_BIND(_end_frame);
+
GDVIRTUAL_BIND(_notification, "what");
/** input and output **/
@@ -274,7 +277,7 @@ CameraMatrix XRInterfaceExtension::get_projection_for_view(uint32_t p_view, doub
void XRInterfaceExtension::add_blit(RID p_render_target, Rect2 p_src_rect, Rect2i p_dst_rect, bool p_use_layer, uint32_t p_layer, bool p_apply_lens_distortion, Vector2 p_eye_center, double p_k1, double p_k2, double p_upscale, double p_aspect_ratio) {
BlitToScreen blit;
- ERR_FAIL_COND_MSG(!can_add_blits, "add_blit can only be called from an XR plugin from within _commit_views!");
+ ERR_FAIL_COND_MSG(!can_add_blits, "add_blit can only be called from an XR plugin from within _post_draw_viewport!");
blit.render_target = p_render_target;
blit.src_rect = p_src_rect;
@@ -293,12 +296,31 @@ void XRInterfaceExtension::add_blit(RID p_render_target, Rect2 p_src_rect, Rect2
blits.push_back(blit);
}
-Vector<BlitToScreen> XRInterfaceExtension::commit_views(RID p_render_target, const Rect2 &p_screen_rect) {
+void XRInterfaceExtension::process() {
+ GDVIRTUAL_CALL(_process);
+}
+
+void XRInterfaceExtension::pre_render() {
+ GDVIRTUAL_CALL(_pre_render);
+}
+
+bool XRInterfaceExtension::pre_draw_viewport(RID p_render_target) {
+ bool do_render = true;
+
+ if (GDVIRTUAL_CALL(_pre_draw_viewport, p_render_target, do_render)) {
+ return do_render;
+ } else {
+ // if not implemented we're returning true
+ return true;
+ }
+}
+
+Vector<BlitToScreen> XRInterfaceExtension::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
// This is just so our XR plugin can add blits...
blits.clear();
can_add_blits = true;
- if (GDVIRTUAL_CALL(_commit_views, p_render_target, p_screen_rect)) {
+ if (GDVIRTUAL_CALL(_post_draw_viewport, p_render_target, p_screen_rect)) {
return blits;
}
@@ -306,8 +328,8 @@ Vector<BlitToScreen> XRInterfaceExtension::commit_views(RID p_render_target, con
return blits;
}
-void XRInterfaceExtension::process() {
- GDVIRTUAL_CALL(_process);
+void XRInterfaceExtension::end_frame() {
+ GDVIRTUAL_CALL(_end_frame);
}
void XRInterfaceExtension::notification(int p_what) {
diff --git a/servers/xr/xr_interface_extension.h b/servers/xr/xr_interface_extension.h
index e22ec2b872..5a436b9fd0 100644
--- a/servers/xr/xr_interface_extension.h
+++ b/servers/xr/xr_interface_extension.h
@@ -109,13 +109,20 @@ public:
GDVIRTUAL4R(PackedFloat64Array, _get_projection_for_view, uint32_t, double, double, double);
void add_blit(RID p_render_target, Rect2 p_src_rect, Rect2i p_dst_rect, bool p_use_layer = false, uint32_t p_layer = 0, bool p_apply_lens_distortion = false, Vector2 p_eye_center = Vector2(), double p_k1 = 0.0, double p_k2 = 0.0, double p_upscale = 1.0, double p_aspect_ratio = 1.0);
- virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override;
- GDVIRTUAL2(_commit_views, RID, const Rect2 &);
virtual void process() override;
+ virtual void pre_render() override;
+ virtual bool pre_draw_viewport(RID p_render_target) override;
+ virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
+ virtual void end_frame() override;
virtual void notification(int p_what) override;
GDVIRTUAL0(_process);
+ GDVIRTUAL0(_pre_render);
+ GDVIRTUAL1R(bool, _pre_draw_viewport, RID);
+ GDVIRTUAL2(_post_draw_viewport, RID, const Rect2 &);
+ GDVIRTUAL0(_end_frame);
+
GDVIRTUAL1(_notification, int);
/* access to some internals we need */
diff --git a/servers/xr/xr_pose.cpp b/servers/xr/xr_pose.cpp
index 0862fefef5..400f13b9c2 100644
--- a/servers/xr/xr_pose.cpp
+++ b/servers/xr/xr_pose.cpp
@@ -33,6 +33,10 @@
#include "servers/xr_server.h"
void XRPose::_bind_methods() {
+ BIND_ENUM_CONSTANT(XR_TRACKING_CONFIDENCE_NONE);
+ BIND_ENUM_CONSTANT(XR_TRACKING_CONFIDENCE_LOW);
+ BIND_ENUM_CONSTANT(XR_TRACKING_CONFIDENCE_HIGH);
+
ClassDB::bind_method(D_METHOD("set_has_tracking_data", "has_tracking_data"), &XRPose::set_has_tracking_data);
ClassDB::bind_method(D_METHOD("get_has_tracking_data"), &XRPose::get_has_tracking_data);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "has_tracking_data"), "set_has_tracking_data", "get_has_tracking_data");
@@ -53,6 +57,10 @@ void XRPose::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_angular_velocity", "velocity"), &XRPose::set_angular_velocity);
ClassDB::bind_method(D_METHOD("get_angular_velocity"), &XRPose::get_angular_velocity);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
+
+ ClassDB::bind_method(D_METHOD("set_tracking_confidence", "tracking_confidence"), &XRPose::set_tracking_confidence);
+ ClassDB::bind_method(D_METHOD("get_tracking_confidence"), &XRPose::get_tracking_confidence);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tracking_confidence"), "set_tracking_confidence", "get_tracking_confidence");
}
void XRPose::set_has_tracking_data(const bool p_has_tracking_data) {
@@ -108,3 +116,11 @@ void XRPose::set_angular_velocity(const Vector3 p_velocity) {
Vector3 XRPose::get_angular_velocity() const {
return angular_velocity;
}
+
+void XRPose::set_tracking_confidence(const XRPose::TrackingConfidence p_tracking_confidence) {
+ tracking_confidence = p_tracking_confidence;
+}
+
+XRPose::TrackingConfidence XRPose::get_tracking_confidence() const {
+ return tracking_confidence;
+}
diff --git a/servers/xr/xr_pose.h b/servers/xr/xr_pose.h
index 8c1fc63360..f306c22390 100644
--- a/servers/xr/xr_pose.h
+++ b/servers/xr/xr_pose.h
@@ -37,12 +37,20 @@ class XRPose : public RefCounted {
GDCLASS(XRPose, RefCounted);
public:
+ // TrackingConfidence gives an indication of how reliable our transform data is.
+ enum TrackingConfidence {
+ XR_TRACKING_CONFIDENCE_NONE, // No tracking information is available for this pose.
+ XR_TRACKING_CONFIDENCE_LOW, // Tracking information may be inaccurate or estimated.
+ XR_TRACKING_CONFIDENCE_HIGH // Tracking information is deemed accurate and up to date.
+ };
+
private:
bool has_tracking_data = false;
StringName name;
Transform3D transform;
Vector3 linear_velocity;
Vector3 angular_velocity;
+ TrackingConfidence tracking_confidence = XR_TRACKING_CONFIDENCE_NONE;
protected:
static void _bind_methods();
@@ -63,6 +71,11 @@ public:
void set_angular_velocity(const Vector3 p_velocity);
Vector3 get_angular_velocity() const;
+
+ void set_tracking_confidence(const TrackingConfidence p_tracking_confidence);
+ TrackingConfidence get_tracking_confidence() const;
};
+VARIANT_ENUM_CAST(XRPose::TrackingConfidence);
+
#endif
diff --git a/servers/xr/xr_positional_tracker.cpp b/servers/xr/xr_positional_tracker.cpp
index 74907a5675..62e011654e 100644
--- a/servers/xr/xr_positional_tracker.cpp
+++ b/servers/xr/xr_positional_tracker.cpp
@@ -56,7 +56,7 @@ void XRPositionalTracker::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_pose", "name"), &XRPositionalTracker::has_pose);
ClassDB::bind_method(D_METHOD("get_pose", "name"), &XRPositionalTracker::get_pose);
ClassDB::bind_method(D_METHOD("invalidate_pose", "name"), &XRPositionalTracker::invalidate_pose);
- ClassDB::bind_method(D_METHOD("set_pose", "name", "transform", "linear_velocity", "angular_velocity"), &XRPositionalTracker::set_pose);
+ ClassDB::bind_method(D_METHOD("set_pose", "name", "transform", "linear_velocity", "angular_velocity", "tracking_confidence"), &XRPositionalTracker::set_pose);
ADD_SIGNAL(MethodInfo("pose_changed", PropertyInfo(Variant::OBJECT, "pose", PROPERTY_HINT_RESOURCE_TYPE, "XRPose")));
ClassDB::bind_method(D_METHOD("get_input", "name"), &XRPositionalTracker::get_input);
@@ -137,7 +137,7 @@ void XRPositionalTracker::invalidate_pose(const StringName &p_action_name) {
}
}
-void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity) {
+void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity, const XRPose::TrackingConfidence p_tracking_confidence) {
Ref<XRPose> new_pose;
new_pose.instantiate();
@@ -146,6 +146,7 @@ void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transf
new_pose->set_transform(p_transform);
new_pose->set_linear_velocity(p_linear_velocity);
new_pose->set_angular_velocity(p_angular_velocity);
+ new_pose->set_tracking_confidence(p_tracking_confidence);
poses[p_action_name] = new_pose;
emit_signal("pose_changed", new_pose);
diff --git a/servers/xr/xr_positional_tracker.h b/servers/xr/xr_positional_tracker.h
index 2bcbf2c018..2f358cbb21 100644
--- a/servers/xr/xr_positional_tracker.h
+++ b/servers/xr/xr_positional_tracker.h
@@ -82,7 +82,7 @@ public:
bool has_pose(const StringName &p_action_name) const;
Ref<XRPose> get_pose(const StringName &p_action_name) const;
void invalidate_pose(const StringName &p_action_name);
- void set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity);
+ void set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity, const XRPose::TrackingConfidence p_tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH);
Variant get_input(const StringName &p_action_name) const;
void set_input(const StringName &p_action_name, const Variant &p_value);
diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp
index 69f4c69b53..dbfe76a127 100644
--- a/servers/xr_server.cpp
+++ b/servers/xr_server.cpp
@@ -65,10 +65,6 @@ void XRServer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "primary_interface"), "set_primary_interface", "get_primary_interface");
- ClassDB::bind_method(D_METHOD("get_last_process_usec"), &XRServer::get_last_process_usec);
- ClassDB::bind_method(D_METHOD("get_last_commit_usec"), &XRServer::get_last_commit_usec);
- ClassDB::bind_method(D_METHOD("get_last_frame_usec"), &XRServer::get_last_frame_usec);
-
BIND_ENUM_CONSTANT(TRACKER_HEAD);
BIND_ENUM_CONSTANT(TRACKER_CONTROLLER);
BIND_ENUM_CONSTANT(TRACKER_BASESTATION);
@@ -351,24 +347,9 @@ PackedStringArray XRServer::get_suggested_pose_names(const StringName &p_tracker
return arr;
}
-uint64_t XRServer::get_last_process_usec() {
- return last_process_usec;
-};
-
-uint64_t XRServer::get_last_commit_usec() {
- return last_commit_usec;
-};
-
-uint64_t XRServer::get_last_frame_usec() {
- return last_frame_usec;
-};
-
void XRServer::_process() {
/* called from renderer_viewport.draw_viewports right before we start drawing our viewports */
- /* mark for our frame timing */
- last_process_usec = OS::get_singleton()->get_ticks_usec();
-
/* process all active interfaces */
for (int i = 0; i < interfaces.size(); i++) {
if (!interfaces[i].is_valid()) {
@@ -379,13 +360,32 @@ void XRServer::_process() {
};
};
-void XRServer::_mark_commit() {
- /* time this */
- last_commit_usec = OS::get_singleton()->get_ticks_usec();
+void XRServer::pre_render() {
+ // called from RendererViewport.draw_viewports right before we start drawing our viewports
+ // note that we can have multiple interfaces active if we have interfaces that purely handle tracking
- /* now store our difference as we may overwrite last_process_usec before this is accessed */
- last_frame_usec = last_commit_usec - last_process_usec;
-};
+ // process all active interfaces
+ for (int i = 0; i < interfaces.size(); i++) {
+ if (!interfaces[i].is_valid()) {
+ // ignore, not a valid reference
+ } else if (interfaces[i]->is_initialized()) {
+ interfaces.write[i]->pre_render();
+ };
+ };
+}
+
+void XRServer::end_frame() {
+ // called from RenderingServerDefault after Vulkan queues have been submitted
+
+ // process all active interfaces
+ for (int i = 0; i < interfaces.size(); i++) {
+ if (!interfaces[i].is_valid()) {
+ // ignore, not a valid reference
+ } else if (interfaces[i]->is_initialized()) {
+ interfaces.write[i]->end_frame();
+ };
+ };
+}
XRServer::XRServer() {
singleton = this;
diff --git a/servers/xr_server.h b/servers/xr_server.h
index a820634bd9..d9188d2de1 100644
--- a/servers/xr_server.h
+++ b/servers/xr_server.h
@@ -84,10 +84,6 @@ private:
Transform3D world_origin; /* our world origin point, maps a location in our virtual world to the origin point in our real world tracking volume */
Transform3D reference_frame; /* our reference frame */
- uint64_t last_process_usec; /* for frame timing, usec when we did our processing */
- uint64_t last_commit_usec; /* for frame timing, usec when we finished committing both eyes */
- uint64_t last_frame_usec; /* time it took between process and committing, we should probably average this over the last x frames */
-
protected:
static XRServer *singleton;
@@ -175,12 +171,16 @@ public:
PackedStringArray get_suggested_pose_names(const StringName &p_tracker_name) const;
// Q: Should we add get_suggested_input_names and get_suggested_haptic_names even though we don't use them for the IDE?
- uint64_t get_last_process_usec();
- uint64_t get_last_commit_usec();
- uint64_t get_last_frame_usec();
-
+ // Process is called before we handle our physics process and game process. This is where our interfaces will update controller data and such.
void _process();
- void _mark_commit();
+
+ // Pre-render is called right before we're rendering our viewports.
+ // This is where interfaces such as OpenVR and OpenXR will update positioning data.
+ // Many of these interfaces will also do a predictive sync which ensures we run at a steady framerate.
+ void pre_render();
+
+ // End-frame is called right after Godot has finished its rendering bits.
+ void end_frame();
XRServer();
~XRServer();
diff --git a/tests/core/math/test_math.cpp b/tests/core/math/test_math.cpp
index 7b1f3af2b9..a24a8fde2b 100644
--- a/tests/core/math/test_math.cpp
+++ b/tests/core/math/test_math.cpp
@@ -308,7 +308,7 @@ public:
curly_stack++;
break;
} else {
- break; //whathever else
+ break; //whatever else
}
}
diff --git a/tests/core/math/test_rect2.h b/tests/core/math/test_rect2.h
index e07250a8a2..d98a94b1b5 100644
--- a/tests/core/math/test_rect2.h
+++ b/tests/core/math/test_rect2.h
@@ -439,6 +439,9 @@ TEST_CASE("[Rect2i] Enclosing") {
CHECK_MESSAGE(
!Rect2i(0, 100, 1280, 720).encloses(Rect2i(-4000, -4000, 100, 100)),
"encloses() with non-contained Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).encloses(Rect2i(0, 100, 1280, 720)),
+ "encloses() with identical Rect2i should return the expected result.");
}
TEST_CASE("[Rect2i] Expanding") {
@@ -557,6 +560,9 @@ TEST_CASE("[Rect2i] Intersection") {
CHECK_MESSAGE(
!Rect2i(0, 100, 1280, 720).intersects(Rect2i(-4000, -4000, 100, 100)),
"intersects() with non-enclosed Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ !Rect2i(0, 0, 2, 2).intersects(Rect2i(2, 2, 2, 2)),
+ "intersects() with adjacent Rect2i should return the expected result.");
}
TEST_CASE("[Rect2i] Merging") {
diff --git a/tests/core/math/test_vector2.h b/tests/core/math/test_vector2.h
new file mode 100644
index 0000000000..cb447acd17
--- /dev/null
+++ b/tests/core/math/test_vector2.h
@@ -0,0 +1,385 @@
+/*************************************************************************/
+/* test_vector2.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_VECTOR2_H
+#define TEST_VECTOR2_H
+
+#include "core/math/vector2.h"
+#include "tests/test_macros.h"
+
+namespace TestVector2 {
+
+TEST_CASE("[Vector2] Angle methods") {
+ const Vector2 vector_x = Vector2(1, 0);
+ const Vector2 vector_y = Vector2(0, 1);
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_x.angle_to(vector_y), (real_t)Math_TAU / 4),
+ "Vector2 angle_to should work as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_y.angle_to(vector_x), (real_t)-Math_TAU / 4),
+ "Vector2 angle_to should work as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_x.angle_to_point(vector_y), (real_t)Math_TAU * 3 / 8),
+ "Vector2 angle_to_point should work as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_y.angle_to_point(vector_x), (real_t)-Math_TAU / 8),
+ "Vector2 angle_to_point should work as expected.");
+}
+
+TEST_CASE("[Vector2] Axis methods") {
+ Vector2 vector = Vector2(1.2, 3.4);
+ CHECK_MESSAGE(
+ vector.max_axis_index() == Vector2::Axis::AXIS_Y,
+ "Vector2 max_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.min_axis_index() == Vector2::Axis::AXIS_X,
+ "Vector2 min_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector[vector.min_axis_index()] == (real_t)1.2,
+ "Vector2 array operator should work as expected.");
+ vector[Vector2::Axis::AXIS_Y] = 3.7;
+ CHECK_MESSAGE(
+ vector[Vector2::Axis::AXIS_Y] == (real_t)3.7,
+ "Vector2 array operator setter should work as expected.");
+}
+
+TEST_CASE("[Vector2] Interpolation methods") {
+ const Vector2 vector1 = Vector2(1, 2);
+ const Vector2 vector2 = Vector2(4, 5);
+ CHECK_MESSAGE(
+ vector1.lerp(vector2, 0.5) == Vector2(2.5, 3.5),
+ "Vector2 lerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.lerp(vector2, 1.0 / 3.0).is_equal_approx(Vector2(2, 3)),
+ "Vector2 lerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.normalized().slerp(vector2.normalized(), 0.5).is_equal_approx(Vector2(0.538953602313995361, 0.84233558177947998)),
+ "Vector2 slerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.normalized().slerp(vector2.normalized(), 1.0 / 3.0).is_equal_approx(Vector2(0.508990883827209473, 0.860771894454956055)),
+ "Vector2 slerp should work as expected.");
+ CHECK_MESSAGE(
+ Vector2(5, 0).slerp(Vector2(0, 5), 0.5).is_equal_approx(Vector2(5, 5) * Math_SQRT12),
+ "Vector2 slerp with non-normalized values should work as expected.");
+ CHECK_MESSAGE(
+ Vector2().slerp(Vector2(), 0.5) == Vector2(),
+ "Vector2 slerp with both inputs as zero vectors should return a zero vector.");
+ CHECK_MESSAGE(
+ Vector2().slerp(Vector2(1, 1), 0.5) == Vector2(0.5, 0.5),
+ "Vector2 slerp with one input as zero should behave like a regular lerp.");
+ CHECK_MESSAGE(
+ Vector2(1, 1).slerp(Vector2(), 0.5) == Vector2(0.5, 0.5),
+ "Vector2 slerp with one input as zero should behave like a regular lerp.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.slerp(vector2, 0.5).length(), (real_t)4.31959610746631919),
+ "Vector2 slerp with different length input should return a vector with an interpolated length.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.angle_to(vector1.slerp(vector2, 0.5)) * 2, vector1.angle_to(vector2)),
+ "Vector2 slerp with different length input should return a vector with an interpolated angle.");
+ CHECK_MESSAGE(
+ vector1.cubic_interpolate(vector2, Vector2(), Vector2(7, 7), 0.5) == Vector2(2.375, 3.5),
+ "Vector2 cubic_interpolate should work as expected.");
+ CHECK_MESSAGE(
+ vector1.cubic_interpolate(vector2, Vector2(), Vector2(7, 7), 1.0 / 3.0).is_equal_approx(Vector2(1.851851940155029297, 2.962963104248046875)),
+ "Vector2 cubic_interpolate should work as expected.");
+ CHECK_MESSAGE(
+ Vector2(1, 0).move_toward(Vector2(10, 0), 3) == Vector2(4, 0),
+ "Vector2 move_toward should work as expected.");
+}
+
+TEST_CASE("[Vector2] Length methods") {
+ const Vector2 vector1 = Vector2(10, 10);
+ const Vector2 vector2 = Vector2(20, 30);
+ CHECK_MESSAGE(
+ vector1.length_squared() == 200,
+ "Vector2 length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.length(), 10 * (real_t)Math_SQRT2),
+ "Vector2 length should work as expected.");
+ CHECK_MESSAGE(
+ vector2.length_squared() == 1300,
+ "Vector2 length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector2.length(), (real_t)36.05551275463989293119),
+ "Vector2 length should work as expected.");
+ CHECK_MESSAGE(
+ vector1.distance_squared_to(vector2) == 500,
+ "Vector2 distance_squared_to should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.distance_to(vector2), (real_t)22.36067977499789696409),
+ "Vector2 distance_to should work as expected.");
+}
+
+TEST_CASE("[Vector2] Limiting methods") {
+ const Vector2 vector = Vector2(10, 10);
+ CHECK_MESSAGE(
+ vector.limit_length().is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)),
+ "Vector2 limit_length should work as expected.");
+ CHECK_MESSAGE(
+ vector.limit_length(5).is_equal_approx(5 * Vector2(Math_SQRT12, Math_SQRT12)),
+ "Vector2 limit_length should work as expected.");
+
+ CHECK_MESSAGE(
+ Vector2(-5, 15).clamp(Vector2(), vector).is_equal_approx(Vector2(0, 10)),
+ "Vector2 clamp should work as expected.");
+ CHECK_MESSAGE(
+ vector.clamp(Vector2(0, 15), Vector2(5, 20)).is_equal_approx(Vector2(5, 15)),
+ "Vector2 clamp should work as expected.");
+}
+
+TEST_CASE("[Vector2] Normalization methods") {
+ CHECK_MESSAGE(
+ Vector2(1, 0).is_normalized() == true,
+ "Vector2 is_normalized should return true for a normalized vector.");
+ CHECK_MESSAGE(
+ Vector2(1, 1).is_normalized() == false,
+ "Vector2 is_normalized should return false for a non-normalized vector.");
+ CHECK_MESSAGE(
+ Vector2(1, 0).normalized() == Vector2(1, 0),
+ "Vector2 normalized should return the same vector for a normalized vector.");
+ CHECK_MESSAGE(
+ Vector2(1, 1).normalized().is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)),
+ "Vector2 normalized should work as expected.");
+}
+
+TEST_CASE("[Vector2] Operators") {
+ const Vector2 decimal1 = Vector2(2.3, 4.9);
+ const Vector2 decimal2 = Vector2(1.2, 3.4);
+ const Vector2 power1 = Vector2(0.75, 1.5);
+ const Vector2 power2 = Vector2(0.5, 0.125);
+ const Vector2 int1 = Vector2(4, 5);
+ const Vector2 int2 = Vector2(1, 2);
+
+ CHECK_MESSAGE(
+ (decimal1 + decimal2).is_equal_approx(Vector2(3.5, 8.3)),
+ "Vector2 addition should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 + power2) == Vector2(1.25, 1.625),
+ "Vector2 addition with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 + int2) == Vector2(5, 7),
+ "Vector2 addition with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 - decimal2).is_equal_approx(Vector2(1.1, 1.5)),
+ "Vector2 subtraction should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 - power2) == Vector2(0.25, 1.375),
+ "Vector2 subtraction with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 - int2) == Vector2(3, 3),
+ "Vector2 subtraction with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 * decimal2).is_equal_approx(Vector2(2.76, 16.66)),
+ "Vector2 multiplication should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 * power2) == Vector2(0.375, 0.1875),
+ "Vector2 multiplication with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 * int2) == Vector2(4, 10),
+ "Vector2 multiplication with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 / decimal2).is_equal_approx(Vector2(1.91666666666666666, 1.44117647058823529)),
+ "Vector2 division should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 / power2) == Vector2(1.5, 12.0),
+ "Vector2 division with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 / int2) == Vector2(4, 2.5),
+ "Vector2 division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 * 2).is_equal_approx(Vector2(4.6, 9.8)),
+ "Vector2 multiplication should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 * 2) == Vector2(1.5, 3),
+ "Vector2 multiplication with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 * 2) == Vector2(8, 10),
+ "Vector2 multiplication with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 / 2).is_equal_approx(Vector2(1.15, 2.45)),
+ "Vector2 division should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 / 2) == Vector2(0.375, 0.75),
+ "Vector2 division with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 / 2) == Vector2(2, 2.5),
+ "Vector2 division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ ((Vector2i)decimal1) == Vector2i(2, 4),
+ "Vector2 cast to Vector2i should work as expected.");
+ CHECK_MESSAGE(
+ ((Vector2i)decimal2) == Vector2i(1, 3),
+ "Vector2 cast to Vector2i should work as expected.");
+ CHECK_MESSAGE(
+ Vector2(Vector2i(1, 2)) == Vector2(1, 2),
+ "Vector2 constructed from Vector2i should work as expected.");
+
+ CHECK_MESSAGE(
+ ((String)decimal1) == "(2.3, 4.9)",
+ "Vector2 cast to String should work as expected.");
+ CHECK_MESSAGE(
+ ((String)decimal2) == "(1.2, 3.4)",
+ "Vector2 cast to String should work as expected.");
+ CHECK_MESSAGE(
+ ((String)Vector2(9.8, 9.9)) == "(9.8, 9.9)",
+ "Vector2 cast to String should work as expected.");
+#ifdef REAL_T_IS_DOUBLE
+ CHECK_MESSAGE(
+ ((String)Vector2(Math_PI, Math_TAU)) == "(3.14159265358979, 6.28318530717959)",
+ "Vector2 cast to String should print the correct amount of digits for real_t = double.");
+#else
+ CHECK_MESSAGE(
+ ((String)Vector2(Math_PI, Math_TAU)) == "(3.141593, 6.283185)",
+ "Vector2 cast to String should print the correct amount of digits for real_t = float.");
+#endif // REAL_T_IS_DOUBLE
+}
+
+TEST_CASE("[Vector2] Other methods") {
+ const Vector2 vector = Vector2(1.2, 3.4);
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector.aspect(), (real_t)1.2 / (real_t)3.4),
+ "Vector2 aspect should work as expected.");
+ CHECK_MESSAGE(
+ vector.direction_to(Vector2()).is_equal_approx(-vector.normalized()),
+ "Vector2 direction_to should work as expected.");
+ CHECK_MESSAGE(
+ Vector2(1, 1).direction_to(Vector2(2, 2)).is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)),
+ "Vector2 direction_to should work as expected.");
+ CHECK_MESSAGE(
+ vector.posmod(2).is_equal_approx(Vector2(1.2, 1.4)),
+ "Vector2 posmod should work as expected.");
+ CHECK_MESSAGE(
+ (-vector).posmod(2).is_equal_approx(Vector2(0.8, 0.6)),
+ "Vector2 posmod should work as expected.");
+ CHECK_MESSAGE(
+ vector.posmodv(Vector2(1, 2)).is_equal_approx(Vector2(0.2, 1.4)),
+ "Vector2 posmodv should work as expected.");
+ CHECK_MESSAGE(
+ (-vector).posmodv(Vector2(2, 3)).is_equal_approx(Vector2(0.8, 2.6)),
+ "Vector2 posmodv should work as expected.");
+ CHECK_MESSAGE(
+ vector.rotated(Math_TAU / 4).is_equal_approx(Vector2(-3.4, 1.2)),
+ "Vector2 rotated should work as expected.");
+ CHECK_MESSAGE(
+ vector.snapped(Vector2(1, 1)) == Vector2(1, 3),
+ "Vector2 snapped to integers should be the same as rounding.");
+ CHECK_MESSAGE(
+ Vector2(3.4, 5.6).snapped(Vector2(1, 1)) == Vector2(3, 6),
+ "Vector2 snapped to integers should be the same as rounding.");
+ CHECK_MESSAGE(
+ vector.snapped(Vector2(0.25, 0.25)) == Vector2(1.25, 3.5),
+ "Vector2 snapped to 0.25 should give exact results.");
+}
+
+TEST_CASE("[Vector2] Plane methods") {
+ const Vector2 vector = Vector2(1.2, 3.4);
+ const Vector2 vector_y = Vector2(0, 1);
+ CHECK_MESSAGE(
+ vector.bounce(vector_y) == Vector2(1.2, -3.4),
+ "Vector2 bounce on a plane with normal of the Y axis should.");
+ CHECK_MESSAGE(
+ vector.reflect(vector_y) == Vector2(-1.2, 3.4),
+ "Vector2 reflect on a plane with normal of the Y axis should.");
+ CHECK_MESSAGE(
+ vector.project(vector_y) == Vector2(0, 3.4),
+ "Vector2 projected on the X axis should only give the Y component.");
+ CHECK_MESSAGE(
+ vector.slide(vector_y) == Vector2(1.2, 0),
+ "Vector2 slide on a plane with normal of the Y axis should set the Y to zero.");
+}
+
+TEST_CASE("[Vector2] Rounding methods") {
+ const Vector2 vector1 = Vector2(1.2, 5.6);
+ const Vector2 vector2 = Vector2(1.2, -5.6);
+ CHECK_MESSAGE(
+ vector1.abs() == vector1,
+ "Vector2 abs should work as expected.");
+ CHECK_MESSAGE(
+ vector2.abs() == vector1,
+ "Vector2 abs should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.ceil() == Vector2(2, 6),
+ "Vector2 ceil should work as expected.");
+ CHECK_MESSAGE(
+ vector2.ceil() == Vector2(2, -5),
+ "Vector2 ceil should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.floor() == Vector2(1, 5),
+ "Vector2 floor should work as expected.");
+ CHECK_MESSAGE(
+ vector2.floor() == Vector2(1, -6),
+ "Vector2 floor should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.round() == Vector2(1, 6),
+ "Vector2 round should work as expected.");
+ CHECK_MESSAGE(
+ vector2.round() == Vector2(1, -6),
+ "Vector2 round should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.sign() == Vector2(1, 1),
+ "Vector2 sign should work as expected.");
+ CHECK_MESSAGE(
+ vector2.sign() == Vector2(1, -1),
+ "Vector2 sign should work as expected.");
+}
+
+TEST_CASE("[Vector2] Linear algebra methods") {
+ const Vector2 vector_x = Vector2(1, 0);
+ const Vector2 vector_y = Vector2(0, 1);
+ CHECK_MESSAGE(
+ vector_x.cross(vector_y) == 1,
+ "Vector2 cross product of X and Y should give 1.");
+ CHECK_MESSAGE(
+ vector_y.cross(vector_x) == -1,
+ "Vector2 cross product of Y and X should give negative 1.");
+
+ CHECK_MESSAGE(
+ vector_x.dot(vector_y) == 0.0,
+ "Vector2 dot product of perpendicular vectors should be zero.");
+ CHECK_MESSAGE(
+ vector_x.dot(vector_x) == 1.0,
+ "Vector2 dot product of identical unit vectors should be one.");
+ CHECK_MESSAGE(
+ (vector_x * 10).dot(vector_x * 10) == 100.0,
+ "Vector2 dot product of same direction vectors should behave as expected.");
+}
+} // namespace TestVector2
+
+#endif // TEST_VECTOR2_H
diff --git a/tests/core/math/test_vector2i.h b/tests/core/math/test_vector2i.h
new file mode 100644
index 0000000000..86e254654d
--- /dev/null
+++ b/tests/core/math/test_vector2i.h
@@ -0,0 +1,144 @@
+/*************************************************************************/
+/* test_vector2i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_VECTOR2I_H
+#define TEST_VECTOR2I_H
+
+#include "core/math/vector2.h"
+#include "tests/test_macros.h"
+
+namespace TestVector2i {
+
+TEST_CASE("[Vector2i] Axis methods") {
+ Vector2i vector = Vector2i(2, 3);
+ CHECK_MESSAGE(
+ vector.max_axis_index() == Vector2i::Axis::AXIS_Y,
+ "Vector2i max_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.min_axis_index() == Vector2i::Axis::AXIS_X,
+ "Vector2i min_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector[vector.min_axis_index()] == 2,
+ "Vector2i array operator should work as expected.");
+ vector[Vector2i::Axis::AXIS_Y] = 5;
+ CHECK_MESSAGE(
+ vector[Vector2i::Axis::AXIS_Y] == 5,
+ "Vector2i array operator setter should work as expected.");
+}
+
+TEST_CASE("[Vector2i] Clamp method") {
+ const Vector2i vector = Vector2i(10, 10);
+ CHECK_MESSAGE(
+ Vector2i(-5, 15).clamp(Vector2i(), vector) == Vector2i(0, 10),
+ "Vector2i clamp should work as expected.");
+ CHECK_MESSAGE(
+ vector.clamp(Vector2i(0, 15), Vector2i(5, 20)) == Vector2i(5, 15),
+ "Vector2i clamp should work as expected.");
+}
+
+TEST_CASE("[Vector2i] Length methods") {
+ const Vector2i vector1 = Vector2i(10, 10);
+ const Vector2i vector2 = Vector2i(20, 30);
+ CHECK_MESSAGE(
+ vector1.length_squared() == 200,
+ "Vector2i length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.length(), 10 * Math_SQRT2),
+ "Vector2i length should work as expected.");
+ CHECK_MESSAGE(
+ vector2.length_squared() == 1300,
+ "Vector2i length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector2.length(), 36.05551275463989293119),
+ "Vector2i length should work as expected.");
+}
+
+TEST_CASE("[Vector2i] Operators") {
+ const Vector2i vector1 = Vector2i(5, 9);
+ const Vector2i vector2 = Vector2i(2, 3);
+
+ CHECK_MESSAGE(
+ (vector1 + vector2) == Vector2i(7, 12),
+ "Vector2i addition with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 - vector2) == Vector2i(3, 6),
+ "Vector2i subtraction with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 * vector2) == Vector2i(10, 27),
+ "Vector2i multiplication with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 / vector2) == Vector2i(2, 3),
+ "Vector2i division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (vector1 * 2) == Vector2i(10, 18),
+ "Vector2i multiplication with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 / 2) == Vector2i(2, 4),
+ "Vector2i division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ ((Vector2)vector1) == Vector2(5, 9),
+ "Vector2i cast to Vector2 should work as expected.");
+ CHECK_MESSAGE(
+ ((Vector2)vector2) == Vector2(2, 3),
+ "Vector2i cast to Vector2 should work as expected.");
+ CHECK_MESSAGE(
+ Vector2i(Vector2(1.1, 2.9)) == Vector2i(1, 2),
+ "Vector2i constructed from Vector2 should work as expected.");
+}
+
+TEST_CASE("[Vector2i] Other methods") {
+ const Vector2i vector = Vector2i(1, 3);
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector.aspect(), (real_t)1.0 / (real_t)3.0),
+ "Vector2i aspect should work as expected.");
+}
+
+TEST_CASE("[Vector2i] Abs and sign methods") {
+ const Vector2i vector1 = Vector2i(1, 3);
+ const Vector2i vector2 = Vector2i(1, -3);
+ CHECK_MESSAGE(
+ vector1.abs() == vector1,
+ "Vector2i abs should work as expected.");
+ CHECK_MESSAGE(
+ vector2.abs() == vector1,
+ "Vector2i abs should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.sign() == Vector2i(1, 1),
+ "Vector2i sign should work as expected.");
+ CHECK_MESSAGE(
+ vector2.sign() == Vector2i(1, -1),
+ "Vector2i sign should work as expected.");
+}
+} // namespace TestVector2i
+
+#endif // TEST_VECTOR2I_H
diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h
new file mode 100644
index 0000000000..136a531946
--- /dev/null
+++ b/tests/core/math/test_vector3.h
@@ -0,0 +1,414 @@
+/*************************************************************************/
+/* test_vector3.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_VECTOR3_H
+#define TEST_VECTOR3_H
+
+#include "core/math/vector3.h"
+#include "tests/test_macros.h"
+
+#define Math_SQRT13 0.57735026918962576450914878050196
+#define Math_SQRT3 1.7320508075688772935274463415059
+
+namespace TestVector3 {
+
+TEST_CASE("[Vector3] Angle methods") {
+ const Vector3 vector_x = Vector3(1, 0, 0);
+ const Vector3 vector_y = Vector3(0, 1, 0);
+ const Vector3 vector_yz = Vector3(0, 1, 1);
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_x.angle_to(vector_y), (real_t)Math_TAU / 4),
+ "Vector3 angle_to should work as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_x.angle_to(vector_yz), (real_t)Math_TAU / 4),
+ "Vector3 angle_to should work as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_yz.angle_to(vector_x), (real_t)Math_TAU / 4),
+ "Vector3 angle_to should work as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_y.angle_to(vector_yz), (real_t)Math_TAU / 8),
+ "Vector3 angle_to should work as expected.");
+
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_x.signed_angle_to(vector_y, vector_y), (real_t)Math_TAU / 4),
+ "Vector3 signed_angle_to edge case should be postiive.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_x.signed_angle_to(vector_yz, vector_y), (real_t)Math_TAU / -4),
+ "Vector3 signed_angle_to should work as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector_yz.signed_angle_to(vector_x, vector_y), (real_t)Math_TAU / 4),
+ "Vector3 signed_angle_to should work as expected.");
+}
+
+TEST_CASE("[Vector3] Axis methods") {
+ Vector3 vector = Vector3(1.2, 3.4, 5.6);
+ CHECK_MESSAGE(
+ vector.max_axis_index() == Vector3::Axis::AXIS_Z,
+ "Vector3 max_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.min_axis_index() == Vector3::Axis::AXIS_X,
+ "Vector3 min_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.get_axis(vector.max_axis_index()) == (real_t)5.6,
+ "Vector3 get_axis should work as expected.");
+ CHECK_MESSAGE(
+ vector[vector.min_axis_index()] == (real_t)1.2,
+ "Vector3 array operator should work as expected.");
+
+ vector.set_axis(Vector3::Axis::AXIS_Y, 4.7);
+ CHECK_MESSAGE(
+ vector.get_axis(Vector3::Axis::AXIS_Y) == (real_t)4.7,
+ "Vector3 set_axis should work as expected.");
+ vector[Vector3::Axis::AXIS_Y] = 3.7;
+ CHECK_MESSAGE(
+ vector[Vector3::Axis::AXIS_Y] == (real_t)3.7,
+ "Vector3 array operator setter should work as expected.");
+}
+
+TEST_CASE("[Vector3] Interpolation methods") {
+ const Vector3 vector1 = Vector3(1, 2, 3);
+ const Vector3 vector2 = Vector3(4, 5, 6);
+ CHECK_MESSAGE(
+ vector1.lerp(vector2, 0.5) == Vector3(2.5, 3.5, 4.5),
+ "Vector3 lerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.lerp(vector2, 1.0 / 3.0).is_equal_approx(Vector3(2, 3, 4)),
+ "Vector3 lerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.normalized().slerp(vector2.normalized(), 0.5).is_equal_approx(Vector3(0.363866806030273438, 0.555698215961456299, 0.747529566287994385)),
+ "Vector3 slerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.normalized().slerp(vector2.normalized(), 1.0 / 3.0).is_equal_approx(Vector3(0.332119762897491455, 0.549413740634918213, 0.766707837581634521)),
+ "Vector3 slerp should work as expected.");
+ CHECK_MESSAGE(
+ Vector3(5, 0, 0).slerp(Vector3(0, 3, 4), 0.5).is_equal_approx(Vector3(3.535533905029296875, 2.121320486068725586, 2.828427314758300781)),
+ "Vector3 slerp with non-normalized values should work as expected.");
+ CHECK_MESSAGE(
+ Vector3().slerp(Vector3(), 0.5) == Vector3(),
+ "Vector3 slerp with both inputs as zero vectors should return a zero vector.");
+ CHECK_MESSAGE(
+ Vector3().slerp(Vector3(1, 1, 1), 0.5) == Vector3(0.5, 0.5, 0.5),
+ "Vector3 slerp with one input as zero should behave like a regular lerp.");
+ CHECK_MESSAGE(
+ Vector3(1, 1, 1).slerp(Vector3(), 0.5) == Vector3(0.5, 0.5, 0.5),
+ "Vector3 slerp with one input as zero should behave like a regular lerp.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.slerp(vector2, 0.5).length(), (real_t)6.25831088708303172),
+ "Vector3 slerp with different length input should return a vector with an interpolated length.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.angle_to(vector1.slerp(vector2, 0.5)) * 2, vector1.angle_to(vector2)),
+ "Vector3 slerp with different length input should return a vector with an interpolated angle.");
+ CHECK_MESSAGE(
+ vector1.cubic_interpolate(vector2, Vector3(), Vector3(7, 7, 7), 0.5) == Vector3(2.375, 3.5, 4.625),
+ "Vector3 cubic_interpolate should work as expected.");
+ CHECK_MESSAGE(
+ vector1.cubic_interpolate(vector2, Vector3(), Vector3(7, 7, 7), 1.0 / 3.0).is_equal_approx(Vector3(1.851851940155029297, 2.962963104248046875, 4.074074268341064453)),
+ "Vector3 cubic_interpolate should work as expected.");
+ CHECK_MESSAGE(
+ Vector3(1, 0, 0).move_toward(Vector3(10, 0, 0), 3) == Vector3(4, 0, 0),
+ "Vector3 move_toward should work as expected.");
+}
+
+TEST_CASE("[Vector3] Length methods") {
+ const Vector3 vector1 = Vector3(10, 10, 10);
+ const Vector3 vector2 = Vector3(20, 30, 40);
+ CHECK_MESSAGE(
+ vector1.length_squared() == 300,
+ "Vector3 length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.length(), 10 * (real_t)Math_SQRT3),
+ "Vector3 length should work as expected.");
+ CHECK_MESSAGE(
+ vector2.length_squared() == 2900,
+ "Vector3 length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector2.length(), (real_t)53.8516480713450403125),
+ "Vector3 length should work as expected.");
+ CHECK_MESSAGE(
+ vector1.distance_squared_to(vector2) == 1400,
+ "Vector3 distance_squared_to should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.distance_to(vector2), (real_t)37.41657386773941385584),
+ "Vector3 distance_to should work as expected.");
+}
+
+TEST_CASE("[Vector3] Limiting methods") {
+ const Vector3 vector = Vector3(10, 10, 10);
+ CHECK_MESSAGE(
+ vector.limit_length().is_equal_approx(Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)),
+ "Vector3 limit_length should work as expected.");
+ CHECK_MESSAGE(
+ vector.limit_length(5).is_equal_approx(5 * Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)),
+ "Vector3 limit_length should work as expected.");
+
+ CHECK_MESSAGE(
+ Vector3(-5, 5, 15).clamp(Vector3(), vector) == Vector3(0, 5, 10),
+ "Vector3 clamp should work as expected.");
+ CHECK_MESSAGE(
+ vector.clamp(Vector3(0, 10, 15), Vector3(5, 10, 20)) == Vector3(5, 10, 15),
+ "Vector3 clamp should work as expected.");
+}
+
+TEST_CASE("[Vector3] Normalization methods") {
+ CHECK_MESSAGE(
+ Vector3(1, 0, 0).is_normalized() == true,
+ "Vector3 is_normalized should return true for a normalized vector.");
+ CHECK_MESSAGE(
+ Vector3(1, 1, 1).is_normalized() == false,
+ "Vector3 is_normalized should return false for a non-normalized vector.");
+ CHECK_MESSAGE(
+ Vector3(1, 0, 0).normalized() == Vector3(1, 0, 0),
+ "Vector3 normalized should return the same vector for a normalized vector.");
+ CHECK_MESSAGE(
+ Vector3(1, 1, 0).normalized().is_equal_approx(Vector3(Math_SQRT12, Math_SQRT12, 0)),
+ "Vector3 normalized should work as expected.");
+ CHECK_MESSAGE(
+ Vector3(1, 1, 1).normalized().is_equal_approx(Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)),
+ "Vector3 normalized should work as expected.");
+}
+
+TEST_CASE("[Vector3] Operators") {
+ const Vector3 decimal1 = Vector3(2.3, 4.9, 7.8);
+ const Vector3 decimal2 = Vector3(1.2, 3.4, 5.6);
+ const Vector3 power1 = Vector3(0.75, 1.5, 0.625);
+ const Vector3 power2 = Vector3(0.5, 0.125, 0.25);
+ const Vector3 int1 = Vector3(4, 5, 9);
+ const Vector3 int2 = Vector3(1, 2, 3);
+
+ CHECK_MESSAGE(
+ (decimal1 + decimal2).is_equal_approx(Vector3(3.5, 8.3, 13.4)),
+ "Vector3 addition should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 + power2) == Vector3(1.25, 1.625, 0.875),
+ "Vector3 addition with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 + int2) == Vector3(5, 7, 12),
+ "Vector3 addition with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 - decimal2).is_equal_approx(Vector3(1.1, 1.5, 2.2)),
+ "Vector3 subtraction should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 - power2) == Vector3(0.25, 1.375, 0.375),
+ "Vector3 subtraction with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 - int2) == Vector3(3, 3, 6),
+ "Vector3 subtraction with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 * decimal2).is_equal_approx(Vector3(2.76, 16.66, 43.68)),
+ "Vector3 multiplication should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 * power2) == Vector3(0.375, 0.1875, 0.15625),
+ "Vector3 multiplication with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 * int2) == Vector3(4, 10, 27),
+ "Vector3 multiplication with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 / decimal2).is_equal_approx(Vector3(1.91666666666666666, 1.44117647058823529, 1.39285714285714286)),
+ "Vector3 division should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 / power2) == Vector3(1.5, 12.0, 2.5),
+ "Vector3 division with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 / int2) == Vector3(4, 2.5, 3),
+ "Vector3 division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 * 2).is_equal_approx(Vector3(4.6, 9.8, 15.6)),
+ "Vector3 multiplication should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 * 2) == Vector3(1.5, 3, 1.25),
+ "Vector3 multiplication with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 * 2) == Vector3(8, 10, 18),
+ "Vector3 multiplication with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 / 2).is_equal_approx(Vector3(1.15, 2.45, 3.9)),
+ "Vector3 division should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 / 2) == Vector3(0.375, 0.75, 0.3125),
+ "Vector3 division with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 / 2) == Vector3(2, 2.5, 4.5),
+ "Vector3 division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ ((Vector3i)decimal1) == Vector3i(2, 4, 7),
+ "Vector3 cast to Vector3i should work as expected.");
+ CHECK_MESSAGE(
+ ((Vector3i)decimal2) == Vector3i(1, 3, 5),
+ "Vector3 cast to Vector3i should work as expected.");
+ CHECK_MESSAGE(
+ Vector3(Vector3i(1, 2, 3)) == Vector3(1, 2, 3),
+ "Vector3 constructed from Vector3i should work as expected.");
+
+ CHECK_MESSAGE(
+ ((String)decimal1) == "(2.3, 4.9, 7.8)",
+ "Vector3 cast to String should work as expected.");
+ CHECK_MESSAGE(
+ ((String)decimal2) == "(1.2, 3.4, 5.6)",
+ "Vector3 cast to String should work as expected.");
+ CHECK_MESSAGE(
+ ((String)Vector3(9.7, 9.8, 9.9)) == "(9.7, 9.8, 9.9)",
+ "Vector3 cast to String should work as expected.");
+#ifdef REAL_T_IS_DOUBLE
+ CHECK_MESSAGE(
+ ((String)Vector3(Math_E, Math_SQRT2, Math_SQRT3)) == "(2.71828182845905, 1.4142135623731, 1.73205080756888)",
+ "Vector3 cast to String should print the correct amount of digits for real_t = double.");
+#else
+ CHECK_MESSAGE(
+ ((String)Vector3(Math_E, Math_SQRT2, Math_SQRT3)) == "(2.718282, 1.414214, 1.732051)",
+ "Vector3 cast to String should print the correct amount of digits for real_t = float.");
+#endif // REAL_T_IS_DOUBLE
+}
+
+TEST_CASE("[Vector3] Other methods") {
+ const Vector3 vector = Vector3(1.2, 3.4, 5.6);
+ CHECK_MESSAGE(
+ vector.direction_to(Vector3()).is_equal_approx(-vector.normalized()),
+ "Vector3 direction_to should work as expected.");
+ CHECK_MESSAGE(
+ Vector3(1, 1, 1).direction_to(Vector3(2, 2, 2)).is_equal_approx(Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)),
+ "Vector3 direction_to should work as expected.");
+ CHECK_MESSAGE(
+ vector.inverse().is_equal_approx(Vector3(1 / 1.2, 1 / 3.4, 1 / 5.6)),
+ "Vector3 inverse should work as expected.");
+ CHECK_MESSAGE(
+ vector.posmod(2).is_equal_approx(Vector3(1.2, 1.4, 1.6)),
+ "Vector3 posmod should work as expected.");
+ CHECK_MESSAGE(
+ (-vector).posmod(2).is_equal_approx(Vector3(0.8, 0.6, 0.4)),
+ "Vector3 posmod should work as expected.");
+ CHECK_MESSAGE(
+ vector.posmodv(Vector3(1, 2, 3)).is_equal_approx(Vector3(0.2, 1.4, 2.6)),
+ "Vector3 posmodv should work as expected.");
+ CHECK_MESSAGE(
+ (-vector).posmodv(Vector3(2, 3, 4)).is_equal_approx(Vector3(0.8, 2.6, 2.4)),
+ "Vector3 posmodv should work as expected.");
+ CHECK_MESSAGE(
+ vector.rotated(Vector3(0, 1, 0), Math_TAU / 4).is_equal_approx(Vector3(5.6, 3.4, -1.2)),
+ "Vector3 rotated should work as expected.");
+ CHECK_MESSAGE(
+ vector.snapped(Vector3(1, 1, 1)) == Vector3(1, 3, 6),
+ "Vector3 snapped to integers should be the same as rounding.");
+ CHECK_MESSAGE(
+ vector.snapped(Vector3(0.25, 0.25, 0.25)) == Vector3(1.25, 3.5, 5.5),
+ "Vector3 snapped to 0.25 should give exact results.");
+}
+
+TEST_CASE("[Vector3] Plane methods") {
+ const Vector3 vector = Vector3(1.2, 3.4, 5.6);
+ const Vector3 vector_y = Vector3(0, 1, 0);
+ CHECK_MESSAGE(
+ vector.bounce(vector_y) == Vector3(1.2, -3.4, 5.6),
+ "Vector3 bounce on a plane with normal of the Y axis should.");
+ CHECK_MESSAGE(
+ vector.reflect(vector_y) == Vector3(-1.2, 3.4, -5.6),
+ "Vector3 reflect on a plane with normal of the Y axis should.");
+ CHECK_MESSAGE(
+ vector.project(vector_y) == Vector3(0, 3.4, 0),
+ "Vector3 projected on the X axis should only give the Y component.");
+ CHECK_MESSAGE(
+ vector.slide(vector_y) == Vector3(1.2, 0, 5.6),
+ "Vector3 slide on a plane with normal of the Y axis should set the Y to zero.");
+}
+
+TEST_CASE("[Vector3] Rounding methods") {
+ const Vector3 vector1 = Vector3(1.2, 3.4, 5.6);
+ const Vector3 vector2 = Vector3(1.2, -3.4, -5.6);
+ CHECK_MESSAGE(
+ vector1.abs() == vector1,
+ "Vector3 abs should work as expected.");
+ CHECK_MESSAGE(
+ vector2.abs() == vector1,
+ "Vector3 abs should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.ceil() == Vector3(2, 4, 6),
+ "Vector3 ceil should work as expected.");
+ CHECK_MESSAGE(
+ vector2.ceil() == Vector3(2, -3, -5),
+ "Vector3 ceil should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.floor() == Vector3(1, 3, 5),
+ "Vector3 floor should work as expected.");
+ CHECK_MESSAGE(
+ vector2.floor() == Vector3(1, -4, -6),
+ "Vector3 floor should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.round() == Vector3(1, 3, 6),
+ "Vector3 round should work as expected.");
+ CHECK_MESSAGE(
+ vector2.round() == Vector3(1, -3, -6),
+ "Vector3 round should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.sign() == Vector3(1, 1, 1),
+ "Vector3 sign should work as expected.");
+ CHECK_MESSAGE(
+ vector2.sign() == Vector3(1, -1, -1),
+ "Vector3 sign should work as expected.");
+}
+
+TEST_CASE("[Vector3] Linear algebra methods") {
+ const Vector3 vector_x = Vector3(1, 0, 0);
+ const Vector3 vector_y = Vector3(0, 1, 0);
+ const Vector3 vector_z = Vector3(0, 0, 1);
+ CHECK_MESSAGE(
+ vector_x.cross(vector_y) == vector_z,
+ "Vector3 cross product of X and Y should give Z.");
+ CHECK_MESSAGE(
+ vector_y.cross(vector_x) == -vector_z,
+ "Vector3 cross product of Y and X should give negative Z.");
+ CHECK_MESSAGE(
+ vector_y.cross(vector_z) == vector_x,
+ "Vector3 cross product of Y and Z should give X.");
+ CHECK_MESSAGE(
+ vector_z.cross(vector_x) == vector_y,
+ "Vector3 cross product of Z and X should give Y.");
+
+ CHECK_MESSAGE(
+ vector_x.dot(vector_y) == 0.0,
+ "Vector3 dot product of perpendicular vectors should be zero.");
+ CHECK_MESSAGE(
+ vector_x.dot(vector_x) == 1.0,
+ "Vector3 dot product of identical unit vectors should be one.");
+ CHECK_MESSAGE(
+ (vector_x * 10).dot(vector_x * 10) == 100.0,
+ "Vector3 dot product of same direction vectors should behave as expected.");
+}
+} // namespace TestVector3
+
+#endif // TEST_VECTOR3_H
diff --git a/tests/core/math/test_vector3i.h b/tests/core/math/test_vector3i.h
new file mode 100644
index 0000000000..b1c6944eba
--- /dev/null
+++ b/tests/core/math/test_vector3i.h
@@ -0,0 +1,145 @@
+/*************************************************************************/
+/* test_vector3i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_VECTOR3I_H
+#define TEST_VECTOR3I_H
+
+#include "core/math/vector3i.h"
+#include "tests/test_macros.h"
+
+namespace TestVector3i {
+
+TEST_CASE("[Vector3i] Axis methods") {
+ Vector3i vector = Vector3i(1, 2, 3);
+ CHECK_MESSAGE(
+ vector.max_axis_index() == Vector3i::Axis::AXIS_Z,
+ "Vector3i max_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.min_axis_index() == Vector3i::Axis::AXIS_X,
+ "Vector3i min_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.get_axis(vector.max_axis_index()) == 3,
+ "Vector3i get_axis should work as expected.");
+ CHECK_MESSAGE(
+ vector[vector.min_axis_index()] == 1,
+ "Vector3i array operator should work as expected.");
+
+ vector.set_axis(Vector3i::Axis::AXIS_Y, 4);
+ CHECK_MESSAGE(
+ vector.get_axis(Vector3i::Axis::AXIS_Y) == 4,
+ "Vector3i set_axis should work as expected.");
+ vector[Vector3i::Axis::AXIS_Y] = 5;
+ CHECK_MESSAGE(
+ vector[Vector3i::Axis::AXIS_Y] == 5,
+ "Vector3i array operator setter should work as expected.");
+}
+
+TEST_CASE("[Vector3i] Clamp method") {
+ const Vector3i vector = Vector3i(10, 10, 10);
+ CHECK_MESSAGE(
+ Vector3i(-5, 5, 15).clamp(Vector3i(), vector) == Vector3i(0, 5, 10),
+ "Vector3i clamp should work as expected.");
+ CHECK_MESSAGE(
+ vector.clamp(Vector3i(0, 10, 15), Vector3i(5, 10, 20)) == Vector3i(5, 10, 15),
+ "Vector3i clamp should work as expected.");
+}
+
+TEST_CASE("[Vector3i] Length methods") {
+ const Vector3i vector1 = Vector3i(10, 10, 10);
+ const Vector3i vector2 = Vector3i(20, 30, 40);
+ CHECK_MESSAGE(
+ vector1.length_squared() == 300,
+ "Vector3i length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.length(), 10 * Math_SQRT3),
+ "Vector3i length should work as expected.");
+ CHECK_MESSAGE(
+ vector2.length_squared() == 2900,
+ "Vector3i length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector2.length(), 53.8516480713450403125),
+ "Vector3i length should work as expected.");
+}
+
+TEST_CASE("[Vector3i] Operators") {
+ const Vector3i vector1 = Vector3i(4, 5, 9);
+ const Vector3i vector2 = Vector3i(1, 2, 3);
+
+ CHECK_MESSAGE(
+ (vector1 + vector2) == Vector3i(5, 7, 12),
+ "Vector3i addition with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 - vector2) == Vector3i(3, 3, 6),
+ "Vector3i subtraction with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 * vector2) == Vector3i(4, 10, 27),
+ "Vector3i multiplication with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 / vector2) == Vector3i(4, 2, 3),
+ "Vector3i division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (vector1 * 2) == Vector3i(8, 10, 18),
+ "Vector3i multiplication with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 / 2) == Vector3i(2, 2, 4),
+ "Vector3i division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ ((Vector3)vector1) == Vector3(4, 5, 9),
+ "Vector3i cast to Vector3 should work as expected.");
+ CHECK_MESSAGE(
+ ((Vector3)vector2) == Vector3(1, 2, 3),
+ "Vector3i cast to Vector3 should work as expected.");
+ CHECK_MESSAGE(
+ Vector3i(Vector3(1.1, 2.9, 3.9)) == Vector3i(1, 2, 3),
+ "Vector3i constructed from Vector3 should work as expected.");
+}
+
+TEST_CASE("[Vector3i] Abs and sign methods") {
+ const Vector3i vector1 = Vector3i(1, 3, 5);
+ const Vector3i vector2 = Vector3i(1, -3, -5);
+ CHECK_MESSAGE(
+ vector1.abs() == vector1,
+ "Vector3i abs should work as expected.");
+ CHECK_MESSAGE(
+ vector2.abs() == vector1,
+ "Vector3i abs should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.sign() == Vector3i(1, 1, 1),
+ "Vector3i sign should work as expected.");
+ CHECK_MESSAGE(
+ vector2.sign() == Vector3i(1, -1, -1),
+ "Vector3i sign should work as expected.");
+}
+} // namespace TestVector3i
+
+#endif // TEST_VECTOR3I_H
diff --git a/tests/core/object/test_method_bind.h b/tests/core/object/test_method_bind.h
index 0c7e47fc89..350a08b6e2 100644
--- a/tests/core/object/test_method_bind.h
+++ b/tests/core/object/test_method_bind.h
@@ -51,9 +51,15 @@ public:
TEST_METHODRC,
TEST_METHODRC_ARGS,
TEST_METHOD_DEFARGS,
+ TEST_METHOD_OBJECT_CAST,
TEST_MAX
};
+ class ObjectSubclass : public Object {
+ public:
+ int value = 1;
+ };
+
int test_num = 0;
bool test_valid[TEST_MAX];
@@ -98,6 +104,10 @@ public:
test_valid[TEST_METHOD_DEFARGS] = p_arg1 == 1 && p_arg2 == 2 && p_arg3 == 3 && p_arg4 == 4 && p_arg5 == 5; //temporary
}
+ void test_method_object_cast(ObjectSubclass *p_object) {
+ test_valid[TEST_METHOD_OBJECT_CAST] = p_object->value == 1;
+ }
+
static void _bind_methods() {
ClassDB::bind_method(D_METHOD("test_method"), &MethodBindTester::test_method);
ClassDB::bind_method(D_METHOD("test_method_args"), &MethodBindTester::test_method_args);
@@ -108,6 +118,7 @@ public:
ClassDB::bind_method(D_METHOD("test_methodrc"), &MethodBindTester::test_methodrc);
ClassDB::bind_method(D_METHOD("test_methodrc_args"), &MethodBindTester::test_methodrc_args);
ClassDB::bind_method(D_METHOD("test_method_default_args"), &MethodBindTester::test_method_default_args, DEFVAL(9) /* wrong on purpose */, DEFVAL(4), DEFVAL(5));
+ ClassDB::bind_method(D_METHOD("test_method_object_cast", "object"), &MethodBindTester::test_method_object_cast);
}
virtual void run_tests() {
@@ -134,6 +145,10 @@ public:
test_valid[TEST_METHODRC_ARGS] = int(call("test_methodrc_args", test_num)) == test_num && test_valid[TEST_METHODRC_ARGS];
call("test_method_default_args", 1, 2, 3, 4);
+
+ ObjectSubclass *obj = memnew(ObjectSubclass);
+ call("test_method_object_cast", obj);
+ memdelete(obj);
}
};
@@ -152,6 +167,7 @@ TEST_CASE("[MethodBind] check all method binds") {
CHECK(mbt->test_valid[MethodBindTester::TEST_METHODRC]);
CHECK(mbt->test_valid[MethodBindTester::TEST_METHODRC_ARGS]);
CHECK(mbt->test_valid[MethodBindTester::TEST_METHOD_DEFARGS]);
+ CHECK(mbt->test_valid[MethodBindTester::TEST_METHOD_OBJECT_CAST]);
memdelete(mbt);
}
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 2f611c26a9..0446f749cf 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -38,8 +38,9 @@
namespace TestString {
int u32scmp(const char32_t *l, const char32_t *r) {
- for (; *l == *r && *l && *r; l++, r++)
+ for (; *l == *r && *l && *r; l++, r++) {
;
+ }
return *l - *r;
}
@@ -355,11 +356,17 @@ TEST_CASE("[String] Number to string") {
CHECK(String::num(42.100023, 4) == "42.1"); // No trailing zeros.
// String::num_real tests.
+ CHECK(String::num_real(1.0) == "1.0");
+ CHECK(String::num_real(1.0, false) == "1");
+ CHECK(String::num_real(9.9) == "9.9");
+ CHECK(String::num_real(9.99) == "9.99");
+ CHECK(String::num_real(9.999) == "9.999");
+ CHECK(String::num_real(9.9999) == "9.9999");
CHECK(String::num_real(3.141593) == "3.141593");
CHECK(String::num_real(3.141) == "3.141"); // No trailing zeros.
#ifdef REAL_T_IS_DOUBLE
CHECK_MESSAGE(String::num_real(Math_PI) == "3.14159265358979", "Prints the appropriate amount of digits for real_t = double.");
- CHECK_MESSAGE(String::num_real(3.1415f) == "3.14149999618530", "Prints more digits of 32-bit float when real_t = double (ones that would be reliable for double).");
+ CHECK_MESSAGE(String::num_real(3.1415f) == "3.1414999961853", "Prints more digits of 32-bit float when real_t = double (ones that would be reliable for double) and no trailing zero.");
#else
CHECK_MESSAGE(String::num_real(Math_PI) == "3.141593", "Prints the appropriate amount of digits for real_t = float.");
CHECK_MESSAGE(String::num_real(3.1415f) == "3.1415", "Prints only reliable digits of 32-bit float when real_t = float.");
@@ -871,7 +878,7 @@ TEST_CASE("[String] is_subsequence_of") {
String a = "is subsequence of";
CHECK(String("sub").is_subsequence_of(a));
CHECK(!String("Sub").is_subsequence_of(a));
- CHECK(String("Sub").is_subsequence_ofi(a));
+ CHECK(String("Sub").is_subsequence_ofn(a));
}
TEST_CASE("[String] match") {
diff --git a/tests/core/templates/test_vector.h b/tests/core/templates/test_vector.h
index 24b3547256..f27d6a332e 100644
--- a/tests/core/templates/test_vector.h
+++ b/tests/core/templates/test_vector.h
@@ -257,27 +257,42 @@ TEST_CASE("[Vector] Slice") {
vector.push_back(3);
vector.push_back(4);
+ Vector<int> slice0 = vector.slice(0, 0);
+ CHECK(slice0.size() == 0);
+
Vector<int> slice1 = vector.slice(1, 3);
CHECK(slice1.size() == 2);
CHECK(slice1[0] == 1);
CHECK(slice1[1] == 2);
Vector<int> slice2 = vector.slice(1, -1);
- CHECK(slice2.size() == 4);
+ CHECK(slice2.size() == 3);
CHECK(slice2[0] == 1);
CHECK(slice2[1] == 2);
CHECK(slice2[2] == 3);
- CHECK(slice2[3] == 4);
- Vector<int> slice3 = vector.slice(3, -1);
+ Vector<int> slice3 = vector.slice(3);
CHECK(slice3.size() == 2);
CHECK(slice3[0] == 3);
CHECK(slice3[1] == 4);
Vector<int> slice4 = vector.slice(2, -2);
- CHECK(slice4.size() == 2);
+ CHECK(slice4.size() == 1);
CHECK(slice4[0] == 2);
- CHECK(slice4[1] == 3);
+
+ Vector<int> slice5 = vector.slice(-2);
+ CHECK(slice5.size() == 2);
+ CHECK(slice5[0] == 3);
+ CHECK(slice5[1] == 4);
+
+ Vector<int> slice6 = vector.slice(2, 42);
+ CHECK(slice6.size() == 3);
+ CHECK(slice6[0] == 2);
+ CHECK(slice6[1] == 3);
+ CHECK(slice6[2] == 4);
+
+ Vector<int> slice7 = vector.slice(5, 1);
+ CHECK(slice7.size() == 0);
}
TEST_CASE("[Vector] Find, has") {
diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h
index 205e34daea..6093048307 100644
--- a/tests/core/variant/test_array.h
+++ b/tests/core/variant/test_array.h
@@ -254,27 +254,52 @@ TEST_CASE("[Array] slice()") {
array.push_back(3);
array.push_back(4);
+ Array slice0 = array.slice(0, 0);
+ CHECK(slice0.size() == 0);
+
Array slice1 = array.slice(1, 3);
CHECK(slice1.size() == 2);
CHECK(slice1[0] == Variant(1));
CHECK(slice1[1] == Variant(2));
Array slice2 = array.slice(1, -1);
- CHECK(slice2.size() == 4);
+ CHECK(slice2.size() == 3);
CHECK(slice2[0] == Variant(1));
CHECK(slice2[1] == Variant(2));
CHECK(slice2[2] == Variant(3));
- CHECK(slice2[3] == Variant(4));
- Array slice3 = array.slice(3, -1);
+ Array slice3 = array.slice(3);
CHECK(slice3.size() == 2);
CHECK(slice3[0] == Variant(3));
CHECK(slice3[1] == Variant(4));
Array slice4 = array.slice(2, -2);
- CHECK(slice4.size() == 2);
+ CHECK(slice4.size() == 1);
CHECK(slice4[0] == Variant(2));
- CHECK(slice4[1] == Variant(3));
+
+ Array slice5 = array.slice(-2);
+ CHECK(slice5.size() == 2);
+ CHECK(slice5[0] == Variant(3));
+ CHECK(slice5[1] == Variant(4));
+
+ Array slice6 = array.slice(2, 42);
+ CHECK(slice6.size() == 3);
+ CHECK(slice6[0] == Variant(2));
+ CHECK(slice6[1] == Variant(3));
+ CHECK(slice6[2] == Variant(4));
+
+ Array slice7 = array.slice(4, 0, -2);
+ CHECK(slice7.size() == 2);
+ CHECK(slice7[0] == Variant(4));
+ CHECK(slice7[1] == Variant(2));
+
+ ERR_PRINT_OFF;
+ Array slice8 = array.slice(4, 1);
+ CHECK(slice8.size() == 0);
+
+ Array slice9 = array.slice(3, -4);
+ CHECK(slice9.size() == 0);
+ ERR_PRINT_ON;
}
TEST_CASE("[Array] Duplicate array") {
diff --git a/tests/core/variant/test_dictionary.h b/tests/core/variant/test_dictionary.h
index 79d53fa64e..729035919d 100644
--- a/tests/core/variant/test_dictionary.h
+++ b/tests/core/variant/test_dictionary.h
@@ -196,7 +196,7 @@ TEST_CASE("[Dictionary] Duplicate dictionary") {
Dictionary shallow_d = d.duplicate(false);
CHECK_MESSAGE(shallow_d.id() != d.id(), "Should create a new array");
CHECK_MESSAGE(Dictionary(shallow_d[1]).id() == Dictionary(d[1]).id(), "Should keep nested dictionary");
- CHECK_MESSAGE(Array(shallow_d[2]).id() == Array(d[2]).id(), "Should keep nested array");
+ CHECK_MESSAGE(Array(shallow_d[k2]).id() == Array(d[k2]).id(), "Should keep nested array");
CHECK_EQ(shallow_d, d);
shallow_d[0] = 0;
CHECK_NE(shallow_d, d);
diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h
index 52d3d5c340..8bd35df107 100644
--- a/tests/scene/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -2786,6 +2786,52 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
SEND_GUI_KEY_EVENT(code_edit, Key::APOSTROPHE);
SEND_GUI_KEY_EVENT(code_edit, Key::QUOTEDBL);
CHECK(code_edit->get_line(0) == "'\"'");
+
+ /* Wrap single line selection with brackets */
+ code_edit->clear();
+ code_edit->insert_text_at_caret("abc");
+ code_edit->select_all();
+ SEND_GUI_KEY_EVENT(code_edit, Key::BRACKETLEFT);
+ CHECK(code_edit->get_line(0) == "[abc]");
+
+ /* Caret should be after the last character of the single line selection */
+ CHECK(code_edit->get_caret_column() == 4);
+
+ /* Wrap multi line selection with brackets */
+ code_edit->clear();
+ code_edit->insert_text_at_caret("abc\nabc");
+ code_edit->select_all();
+ SEND_GUI_KEY_EVENT(code_edit, Key::BRACKETLEFT);
+ CHECK(code_edit->get_text() == "[abc\nabc]");
+
+ /* Caret should be after the last character of the multi line selection */
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 3);
+
+ /* If inserted character is not a auto brace completion open key, replace selected text with the inserted character */
+ code_edit->clear();
+ code_edit->insert_text_at_caret("abc");
+ code_edit->select_all();
+ SEND_GUI_KEY_EVENT(code_edit, Key::KEY_1);
+ CHECK(code_edit->get_text() == "1");
+
+ /* If potential multichar and single brace completion is matched, it should wrap the single. */
+ code_edit->clear();
+ code_edit->insert_text_at_caret("\'\'abc");
+ code_edit->select(0, 2, 0, 5);
+ SEND_GUI_KEY_EVENT(code_edit, Key::APOSTROPHE);
+ CHECK(code_edit->get_text() == "\'\'\'abc\'");
+
+ /* If only the potential multichar brace completion is matched, it does not wrap or complete. */
+ auto_brace_completion_pairs.erase("\'");
+ code_edit->set_auto_brace_completion_pairs(auto_brace_completion_pairs);
+ CHECK_FALSE(code_edit->has_auto_brace_completion_open_key("\'"));
+
+ code_edit->clear();
+ code_edit->insert_text_at_caret("\'\'abc");
+ code_edit->select(0, 2, 0, 5);
+ SEND_GUI_KEY_EVENT(code_edit, Key::APOSTROPHE);
+ CHECK(code_edit->get_text() == "\'\'\'");
}
SUBCASE("[CodeEdit] autocomplete") {
@@ -2812,7 +2858,7 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
}
SUBCASE("[CodeEdit] autocomplete request") {
- SIGNAL_WATCH(code_edit, "request_code_completion");
+ SIGNAL_WATCH(code_edit, "code_completion_requested");
code_edit->set_code_completion_enabled(true);
Array signal_args;
@@ -2820,13 +2866,13 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
/* Force request. */
code_edit->request_code_completion();
- SIGNAL_CHECK_FALSE("request_code_completion");
+ SIGNAL_CHECK_FALSE("code_completion_requested");
code_edit->request_code_completion(true);
- SIGNAL_CHECK("request_code_completion", signal_args);
+ SIGNAL_CHECK("code_completion_requested", signal_args);
/* Manual request should force. */
SEND_GUI_ACTION(code_edit, "ui_text_completion_query");
- SIGNAL_CHECK("request_code_completion", signal_args);
+ SIGNAL_CHECK("code_completion_requested", signal_args);
/* Insert prefix. */
TypedArray<String> completion_prefixes;
@@ -2835,12 +2881,12 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
code_edit->insert_text_at_caret(".");
code_edit->request_code_completion();
- SIGNAL_CHECK("request_code_completion", signal_args);
+ SIGNAL_CHECK("code_completion_requested", signal_args);
/* Should work with space too. */
code_edit->insert_text_at_caret(" ");
code_edit->request_code_completion();
- SIGNAL_CHECK("request_code_completion", signal_args);
+ SIGNAL_CHECK("code_completion_requested", signal_args);
/* Should work when complete ends with prefix. */
code_edit->clear();
@@ -2849,9 +2895,9 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
code_edit->update_code_completion_options();
code_edit->confirm_code_completion();
CHECK(code_edit->get_line(0) == "test.");
- SIGNAL_CHECK("request_code_completion", signal_args);
+ SIGNAL_CHECK("code_completion_requested", signal_args);
- SIGNAL_UNWATCH(code_edit, "request_code_completion");
+ SIGNAL_UNWATCH(code_edit, "code_completion_requested");
}
SUBCASE("[CodeEdit] autocomplete completion") {
@@ -3158,7 +3204,7 @@ TEST_CASE("[SceneTree][CodeEdit] symbol lookup") {
code_edit->set_text("this is some text");
Point2 caret_pos = code_edit->get_caret_draw_pos();
- caret_pos.x += 55;
+ caret_pos.x += 58;
SEND_GUI_MOUSE_EVENT(code_edit, caret_pos, MouseButton::NONE, MouseButton::NONE);
CHECK(code_edit->get_text_for_symbol_lookup() == "this is s" + String::chr(0xFFFF) + "ome text");
@@ -3220,7 +3266,7 @@ TEST_CASE("[SceneTree][CodeEdit] Backspace delete") {
code_edit->backspace();
CHECK(code_edit->get_line(0) == "backspace");
- /* Move caret up to the previous line on backspace if carret is at the first column. */
+ /* Move caret up to the previous line on backspace if caret is at the first column. */
code_edit->set_text("");
code_edit->insert_text_at_caret("line 1\nline 2");
code_edit->set_caret_line(1);
@@ -3248,6 +3294,55 @@ TEST_CASE("[SceneTree][CodeEdit] Backspace delete") {
memdelete(code_edit);
}
+TEST_CASE("[SceneTree][CodeEdit] New Line") {
+ CodeEdit *code_edit = memnew(CodeEdit);
+ SceneTree::get_singleton()->get_root()->add_child(code_edit);
+
+ /* Add a new line. */
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("test new line");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(13);
+ SEND_GUI_ACTION(code_edit, "ui_text_newline");
+ CHECK(code_edit->get_line(0) == "test new line");
+ CHECK(code_edit->get_line(1) == "");
+
+ /* Split line with new line. */
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("test new line");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(5);
+ SEND_GUI_ACTION(code_edit, "ui_text_newline");
+ CHECK(code_edit->get_line(0) == "test ");
+ CHECK(code_edit->get_line(1) == "new line");
+
+ /* Delete selection and split with new line. */
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("test new line");
+ code_edit->select(0, 0, 0, 5);
+ SEND_GUI_ACTION(code_edit, "ui_text_newline");
+ CHECK(code_edit->get_line(0) == "");
+ CHECK(code_edit->get_line(1) == "new line");
+
+ /* Blank new line below with selection should not split. */
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("test new line");
+ code_edit->select(0, 0, 0, 5);
+ SEND_GUI_ACTION(code_edit, "ui_text_newline_blank");
+ CHECK(code_edit->get_line(0) == "test new line");
+ CHECK(code_edit->get_line(1) == "");
+
+ /* Blank new line above with selection should not split. */
+ code_edit->set_text("");
+ code_edit->insert_text_at_caret("test new line");
+ code_edit->select(0, 0, 0, 5);
+ SEND_GUI_ACTION(code_edit, "ui_text_newline_above");
+ CHECK(code_edit->get_line(0) == "");
+ CHECK(code_edit->get_line(1) == "test new line");
+
+ memdelete(code_edit);
+}
+
} // namespace TestCodeEdit
#endif // TEST_CODE_EDIT_H
diff --git a/tests/servers/test_shader_lang.cpp b/tests/servers/test_shader_lang.cpp
index acc7e32441..06e28212d2 100644
--- a/tests/servers/test_shader_lang.cpp
+++ b/tests/servers/test_shader_lang.cpp
@@ -211,9 +211,6 @@ static String dump_node_code(SL::Node *p_node, int p_level) {
SL::ArrayNode *vnode = (SL::ArrayNode *)p_node;
code = vnode->name;
} break;
- case SL::Node::TYPE_ARRAY_DECLARATION: {
- // FIXME: Implement
- } break;
case SL::Node::TYPE_ARRAY_CONSTRUCT: {
// FIXME: Implement
} break;
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 2b2c89fbf1..0190fa5184 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -48,6 +48,10 @@
#include "tests/core/math/test_math.h"
#include "tests/core/math/test_random_number_generator.h"
#include "tests/core/math/test_rect2.h"
+#include "tests/core/math/test_vector2.h"
+#include "tests/core/math/test_vector2i.h"
+#include "tests/core/math/test_vector3.h"
+#include "tests/core/math/test_vector3i.h"
#include "tests/core/object/test_class_db.h"
#include "tests/core/object/test_method_bind.h"
#include "tests/core/object/test_object.h"
@@ -199,7 +203,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
memnew(InputMap);
InputMap::get_singleton()->load_default();
- make_default_theme(false, Ref<Font>());
+ make_default_theme(1.0, Ref<Font>());
memnew(SceneTree);
SceneTree::get_singleton()->initialize();
diff --git a/thirdparty/README.md b/thirdparty/README.md
index dd49d80f50..e1f911a9f9 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -138,10 +138,10 @@ Files extracted from upstream source:
* License: OFL-1.1
* Comment: Use UI font variant if available, because it has tight vertical metrics and
good for UI.
-- `Hack_Regular.ttf`:
- * Upstream: https://github.com/source-foundry/Hack
- * Version: 3.003 (2018)
- * License: MIT + Bitstream Vera License
+- `JetBrainsMono_Regular.ttf`:
+ * Upstream: https://github.com/JetBrains/JetBrainsMono
+ * Version: 2.242
+ * License: OFL-1.1
- `DroidSans*.ttf`:
* Upstream: https://android.googlesource.com/platform/frameworks/base/+/master/data/fonts/
* Version: ? (pre-2014 commit when DroidSansJapanese.ttf was obsoleted)
@@ -152,6 +152,7 @@ Files extracted from upstream source:
* License: Apache 2.0
+
## freetype
- Upstream: https://www.freetype.org
@@ -205,7 +206,7 @@ Files extracted from upstream source:
## harfbuzz
- Upstream: https://github.com/harfbuzz/harfbuzz
-- Version: 3.2.0 (be91d2917d9860326cb5fd1d03ffe1042a72f6d3, 2021)
+- Version: 3.3.1 (45df259538c204540819d74456d30ffb40df488a, 2022)
- License: MIT
Files extracted from upstream source:
@@ -308,7 +309,7 @@ Files extracted from upstream source:
## libwebp
- Upstream: https://chromium.googlesource.com/webm/libwebp/
-- Version: 1.2.1 (9ce5843dbabcfd3f7c39ec7ceba9cbeb213cbfdf, 2021)
+- Version: 1.2.2 (b0a860891dcd4c0c2d7c6149e5cccb6eb881cc21, 2022)
- License: BSD-3-Clause
Files extracted from upstream source:
@@ -316,10 +317,6 @@ Files extracted from upstream source:
- `src/*` except from: `.am`, `.rc` and `.in` files
- `AUTHORS`, `COPYING`, `PATENTS`
-Important: The files `utils/bit_reader_utils.{c,h}` have Godot-made
-changes to ensure they build for Javascript/HTML5. Those
-changes are marked with `// -- GODOT --` comments.
-
## mbedtls
@@ -350,7 +347,7 @@ Files extracted from upstream repository:
- `LICENSE.md`.
An [experimental upstream feature](https://github.com/zeux/meshoptimizer/tree/simplify-attr),
-has been backported. On top of that, it was modified to report only distance error metrics
+has been backported. On top of that, it was modified to report only distance error metrics
instead of a combination of distance and attribute errors. Patches for both changes can be
found in the `patches` directory.
@@ -477,19 +474,6 @@ Files extracted from the upstream source:
- Files in `core/` folder.
- `LICENSE.txt` and `CHANGELOG.md`
-
-## nanosvg
-
-- Upstream: https://github.com/memononen/nanosvg
-- Version: git (ccdb1995134d340a93fb20e3a3d323ccb3838dd0, 2021)
-- License: zlib
-
-Files extracted from the upstream source:
-
-- All .h files in `src/`
-- LICENSE.txt
-
-
## oidn
- Upstream: https://github.com/OpenImageDenoise/oidn
@@ -537,19 +521,6 @@ Files extracted from upstream source:
- AUTHORS and LICENCE
-## pvrtccompressor
-
-- Upstream: https://bitbucket.org/jthlim/pvrtccompressor (dead link)
- Unofficial backup fork: https://github.com/LibreGamesArchive/PVRTCCompressor
-- Version: hg (cf7177748ee0dcdccfe89716dc11a47d2dc81af5, 2015)
-- License: BSD-3-Clause
-
-Files extracted from upstream source:
-
-- all .cpp and .h files apart from `main.cpp`
-- LICENSE.TXT
-
-
## recastnavigation
- Upstream: https://github.com/recastnavigation/recastnavigation
@@ -628,6 +599,18 @@ The `tinyexr.cc` file was modified to include `zlib.h` which we provide,
instead of `miniz.h` as an external dependency.
+## thorvg
+
+- Upstream: https://github.com/Samsung/thorvg
+- Version: 0.7.1 (d53eb2a880002cb770ace1c1ace9c5dfcfc28252, 2022)
+- License: MIT
+
+Files extracted from upstream source:
+
+See `thorvg/update-thorvg.sh` for extraction instructions. Set the version
+number and run the script.
+
+
## vhacd
- Upstream: https://github.com/kmammou/v-hacd
@@ -738,4 +721,3 @@ Files extracted from upstream source:
- `lib/{common/,compress/,decompress/,zstd.h,zstd_errors.h}`
- `LICENSE`
-
diff --git a/thirdparty/fonts/Hack_Regular.ttf b/thirdparty/fonts/Hack_Regular.ttf
deleted file mode 100644
index 92a90cb06e..0000000000
--- a/thirdparty/fonts/Hack_Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/thirdparty/fonts/JetBrainsMono_Regular.ttf b/thirdparty/fonts/JetBrainsMono_Regular.ttf
new file mode 100644
index 0000000000..8da8aa4051
--- /dev/null
+++ b/thirdparty/fonts/JetBrainsMono_Regular.ttf
Binary files differ
diff --git a/thirdparty/fonts/LICENSE.JetBrainsMono.txt b/thirdparty/fonts/LICENSE.JetBrainsMono.txt
new file mode 100644
index 0000000000..e5f5dd62fc
--- /dev/null
+++ b/thirdparty/fonts/LICENSE.JetBrainsMono.txt
@@ -0,0 +1,93 @@
+Copyright 2020, The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+
+This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/thirdparty/fonts/LICENSE_Hack.md b/thirdparty/fonts/LICENSE_Hack.md
deleted file mode 100644
index 08927e504f..0000000000
--- a/thirdparty/fonts/LICENSE_Hack.md
+++ /dev/null
@@ -1,45 +0,0 @@
-The work in the Hack project is Copyright 2018 Source Foundry Authors and licensed under the MIT License
-
-The work in the DejaVu project was committed to the public domain.
-
-Bitstream Vera Sans Mono Copyright 2003 Bitstream Inc. and licensed under the Bitstream Vera License with Reserved Font Names "Bitstream" and "Vera"
-
-### MIT License
-
-Copyright (c) 2018 Source Foundry Authors
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-### BITSTREAM VERA LICENSE
-
-Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:
-
-The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.
-
-The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera".
-
-This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names.
-
-The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
-
-Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.
diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-common.hh b/thirdparty/harfbuzz/src/hb-aat-layout-common.hh
index 1dcbe92904..1db0f1df92 100644
--- a/thirdparty/harfbuzz/src/hb-aat-layout-common.hh
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-common.hh
@@ -839,7 +839,7 @@ struct StateTableDriver
}
if (!c->in_place)
- buffer->swap_buffers ();
+ buffer->sync ();
}
public:
diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh
index d745c11431..0bf9bd2912 100644
--- a/thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh
@@ -146,7 +146,7 @@ struct DuctileGlyphAction
HBUINT32 variationAxis; /* The 4-byte tag identifying the ductile axis.
* This would normally be 0x64756374 ('duct'),
* but you may use any axis the font contains. */
- HBFixed minimumLimit; /* The lowest value for the ductility axis tha
+ HBFixed minimumLimit; /* The lowest value for the ductility axis that
* still yields an acceptable appearance. Normally
* this will be 1.0. */
HBFixed noStretchValue; /* This is the default value that corresponds to
diff --git a/thirdparty/harfbuzz/src/hb-algs.hh b/thirdparty/harfbuzz/src/hb-algs.hh
index 446d87e28b..3a3ab08046 100644
--- a/thirdparty/harfbuzz/src/hb-algs.hh
+++ b/thirdparty/harfbuzz/src/hb-algs.hh
@@ -36,6 +36,7 @@
#include <algorithm>
#include <initializer_list>
+#include <functional>
#include <new>
/*
@@ -210,12 +211,23 @@ struct
}
HB_FUNCOBJ (hb_bool);
+template <typename T>
+static inline
+T hb_coerce (const T v) { return v; }
+template <typename T, typename V,
+ hb_enable_if (!hb_is_same (hb_decay<T>, hb_decay<V>) && std::is_pointer<V>::value)>
+static inline
+T hb_coerce (const V v) { return *v; }
+
struct
{
private:
template <typename T> constexpr auto
- impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
+ impl (const T& v, hb_priority<2>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
+
+ template <typename T> constexpr auto
+ impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v))>>{} (hb_deref (v)))
template <typename T,
hb_enable_if (std::is_integral<T>::value)> constexpr auto
@@ -435,23 +447,29 @@ struct
private:
template <typename T1, typename T2> auto
- impl (T1&& v1, T2 &&v2, hb_priority<2>) const HB_AUTO_RETURN
+ impl (T1&& v1, T2 &&v2, hb_priority<3>) const HB_AUTO_RETURN
(
std::forward<T2> (v2).cmp (std::forward<T1> (v1)) == 0
)
template <typename T1, typename T2> auto
- impl (T1&& v1, T2 &&v2, hb_priority<1>) const HB_AUTO_RETURN
+ impl (T1&& v1, T2 &&v2, hb_priority<2>) const HB_AUTO_RETURN
(
std::forward<T1> (v1).cmp (std::forward<T2> (v2)) == 0
)
template <typename T1, typename T2> auto
- impl (T1&& v1, T2 &&v2, hb_priority<0>) const HB_AUTO_RETURN
+ impl (T1&& v1, T2 &&v2, hb_priority<1>) const HB_AUTO_RETURN
(
std::forward<T1> (v1) == std::forward<T2> (v2)
)
+ template <typename T1, typename T2> auto
+ impl (T1&& v1, T2 &&v2, hb_priority<0>) const HB_AUTO_RETURN
+ (
+ std::forward<T2> (v2) == std::forward<T1> (v1)
+ )
+
public:
template <typename T1, typename T2> auto
@@ -472,6 +490,10 @@ struct hb_pair_t
typedef T2 second_t;
typedef hb_pair_t<T1, T2> pair_t;
+ template <typename U1 = T1, typename U2 = T2,
+ hb_enable_if (std::is_default_constructible<U1>::value &&
+ std::is_default_constructible<U2>::value)>
+ hb_pair_t () : first (), second () {}
hb_pair_t (T1 a, T2 b) : first (a), second (b) {}
template <typename Q1, typename Q2,
@@ -870,7 +892,7 @@ hb_bsearch_impl (unsigned *pos, /* Out */
#pragma GCC diagnostic ignored "-Wcast-align"
V* p = (V*) (((const char *) base) + (mid * stride));
#pragma GCC diagnostic pop
- int c = compar ((const void *) hb_addressof (key), (const void *) p, ds...);
+ int c = compar ((const void *) std::addressof (key), (const void *) p, ds...);
if (c < 0)
max = mid - 1;
else if (c > 0)
diff --git a/thirdparty/harfbuzz/src/hb-array.hh b/thirdparty/harfbuzz/src/hb-array.hh
index 0beffb078f..1d1476d7cd 100644
--- a/thirdparty/harfbuzz/src/hb-array.hh
+++ b/thirdparty/harfbuzz/src/hb-array.hh
@@ -412,7 +412,7 @@ bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
return true;
}
-/* TODO Specialize opeator== for hb_bytes_t and hb_ubytes_t. */
+/* TODO Specialize operator== for hb_bytes_t and hb_ubytes_t. */
template <>
inline uint32_t hb_array_t<const char>::hash () const {
diff --git a/thirdparty/harfbuzz/src/hb-bimap.hh b/thirdparty/harfbuzz/src/hb-bimap.hh
index d466af8b60..a9e1278de7 100644
--- a/thirdparty/harfbuzz/src/hb-bimap.hh
+++ b/thirdparty/harfbuzz/src/hb-bimap.hh
@@ -33,20 +33,6 @@
/* Bi-directional map */
struct hb_bimap_t
{
- /* XXX(remove) */
- void init ()
- {
- forw_map.init ();
- back_map.init ();
- }
-
- /* XXX(remove) */
- void fini ()
- {
- forw_map.fini ();
- back_map.fini ();
- }
-
void reset ()
{
forw_map.reset ();
diff --git a/thirdparty/harfbuzz/src/hb-buffer.cc b/thirdparty/harfbuzz/src/hb-buffer.cc
index be3161a54d..e50afcb203 100644
--- a/thirdparty/harfbuzz/src/hb-buffer.cc
+++ b/thirdparty/harfbuzz/src/hb-buffer.cc
@@ -86,7 +86,46 @@ hb_segment_properties_hash (const hb_segment_properties_t *p)
(intptr_t) (p->language);
}
+/**
+ * hb_segment_properties_overlay:
+ * @p: #hb_segment_properties_t to fill in.
+ * @src: #hb_segment_properties_t to fill in from.
+ *
+ * Fills in missing fields of @p from @src in a considered manner.
+ *
+ * First, if @p does not have direction set, direction is copied from @src.
+ *
+ * Next, if @p and @src have the same direction (which can be unset), if @p
+ * does not have script set, script is copied from @src.
+ *
+ * Finally, if @p and @src have the same direction and script (which either
+ * can be unset), if @p does not have language set, language is copied from
+ * @src.
+ *
+ * Since: 3.3.0
+ **/
+void
+hb_segment_properties_overlay (hb_segment_properties_t *p,
+ const hb_segment_properties_t *src)
+{
+ if (unlikely (!p || !src))
+ return;
+ if (!p->direction)
+ p->direction = src->direction;
+
+ if (p->direction != src->direction)
+ return;
+
+ if (!p->script)
+ p->script = src->script;
+
+ if (p->script != src->script)
+ return;
+
+ if (!p->language)
+ p->language = src->language;
+}
/* Here is how the buffer works internally:
*
@@ -96,14 +135,14 @@ hb_segment_properties_hash (const hb_segment_properties_t *p)
* As an optimization, both info and out_info may point to the
* same piece of memory, which is owned by info. This remains the
* case as long as out_len doesn't exceed i at any time.
- * In that case, swap_buffers() is mostly no-op and the glyph operations
+ * In that case, sync() is mostly no-op and the glyph operations
* operate mostly in-place.
*
* As soon as out_info gets longer than info, out_info is moved over
* to an alternate buffer (which we reuse the pos buffer for), and its
* current contents (out_len entries) are copied to the new place.
*
- * This should all remain transparent to the user. swap_buffers() then
+ * This should all remain transparent to the user. sync() then
* switches info over to out_info and does housekeeping.
*/
@@ -217,11 +256,24 @@ hb_buffer_t::get_scratch_buffer (unsigned int *size)
/* HarfBuzz-Internal API */
void
+hb_buffer_t::similar (const hb_buffer_t &src)
+{
+ hb_unicode_funcs_destroy (unicode);
+ unicode = hb_unicode_funcs_reference (src.unicode);
+ flags = src.flags;
+ cluster_level = src.cluster_level;
+ replacement = src.invisible;
+ invisible = src.invisible;
+ not_found = src.not_found;
+}
+
+void
hb_buffer_t::reset ()
{
hb_unicode_funcs_destroy (unicode);
unicode = hb_unicode_funcs_reference (hb_unicode_funcs_get_default ());
flags = HB_BUFFER_FLAG_DEFAULT;
+ cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT;
replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
invisible = 0;
not_found = 0;
@@ -232,11 +284,10 @@ hb_buffer_t::reset ()
void
hb_buffer_t::clear ()
{
+ content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
props = default_props;
- scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
- content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
successful = true;
have_output = false;
have_positions = false;
@@ -244,16 +295,44 @@ hb_buffer_t::clear ()
idx = 0;
len = 0;
out_len = 0;
- out_info = info;
- serial = 0;
+ out_info = info;
memset (context, 0, sizeof context);
memset (context_len, 0, sizeof context_len);
deallocate_var_all ();
+ serial = 0;
+ scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
+}
+
+void
+hb_buffer_t::enter ()
+{
+ deallocate_var_all ();
+ serial = 0;
+ scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
+ if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR)))
+ {
+ max_len = hb_max (len * HB_BUFFER_MAX_LEN_FACTOR,
+ (unsigned) HB_BUFFER_MAX_LEN_MIN);
+ }
+ if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_OPS_FACTOR)))
+ {
+ max_ops = hb_max (len * HB_BUFFER_MAX_OPS_FACTOR,
+ (unsigned) HB_BUFFER_MAX_OPS_MIN);
+ }
+}
+void
+hb_buffer_t::leave ()
+{
+ max_len = HB_BUFFER_MAX_LEN_DEFAULT;
+ max_ops = HB_BUFFER_MAX_OPS_DEFAULT;
+ deallocate_var_all ();
+ serial = 0;
}
+
void
hb_buffer_t::add (hb_codepoint_t codepoint,
unsigned int cluster)
@@ -307,7 +386,7 @@ hb_buffer_t::clear_positions ()
}
void
-hb_buffer_t::swap_buffers ()
+hb_buffer_t::sync ()
{
assert (have_output);
@@ -494,33 +573,6 @@ done:
}
void
-hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end)
-{
- unsigned int cluster = UINT_MAX;
- cluster = _infos_find_min_cluster (info, start, end, cluster);
- _unsafe_to_break_set_mask (info, start, end, cluster);
-}
-void
-hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end)
-{
- if (!have_output)
- {
- unsafe_to_break_impl (start, end);
- return;
- }
-
- assert (start <= out_len);
- assert (idx <= end);
-
- unsigned int cluster = UINT_MAX;
- cluster = _infos_find_min_cluster (out_info, start, out_len, cluster);
- cluster = _infos_find_min_cluster (info, idx, end, cluster);
-
- _unsafe_to_break_set_mask (out_info, start, out_len, cluster);
- _unsafe_to_break_set_mask (info, idx, end, cluster);
-}
-
-void
hb_buffer_t::guess_segment_properties ()
{
assert_unicode ();
@@ -565,12 +617,11 @@ DEFINE_NULL_INSTANCE (hb_buffer_t) =
HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
0, /* invisible */
0, /* not_found */
- HB_BUFFER_SCRATCH_FLAG_DEFAULT,
- HB_BUFFER_MAX_LEN_DEFAULT,
- HB_BUFFER_MAX_OPS_DEFAULT,
+
HB_BUFFER_CONTENT_TYPE_INVALID,
HB_SEGMENT_PROPERTIES_DEFAULT,
+
false, /* successful */
false, /* have_output */
true /* have_positions */
@@ -610,6 +661,46 @@ hb_buffer_create ()
}
/**
+ * hb_buffer_create_similar:
+ * @src: An #hb_buffer_t
+ *
+ * Creates a new #hb_buffer_t, similar to hb_buffer_create(). The only
+ * difference is that the buffer is configured similarly to @src.
+ *
+ * Return value: (transfer full):
+ * A newly allocated #hb_buffer_t, similar to hb_buffer_create().
+ *
+ * Since: 3.3.0
+ **/
+hb_buffer_t *
+hb_buffer_create_similar (const hb_buffer_t *src)
+{
+ hb_buffer_t *buffer = hb_buffer_create ();
+
+ buffer->similar (*src);
+
+ return buffer;
+}
+
+/**
+ * hb_buffer_reset:
+ * @buffer: An #hb_buffer_t
+ *
+ * Resets the buffer to its initial status, as if it was just newly created
+ * with hb_buffer_create().
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_buffer_reset (hb_buffer_t *buffer)
+{
+ if (unlikely (hb_object_is_immutable (buffer)))
+ return;
+
+ buffer->reset ();
+}
+
+/**
* hb_buffer_get_empty:
*
* Fetches an empty #hb_buffer_t.
@@ -1157,24 +1248,6 @@ hb_buffer_get_not_found_glyph (hb_buffer_t *buffer)
/**
- * hb_buffer_reset:
- * @buffer: An #hb_buffer_t
- *
- * Resets the buffer to its initial status, as if it was just newly created
- * with hb_buffer_create().
- *
- * Since: 0.9.2
- **/
-void
-hb_buffer_reset (hb_buffer_t *buffer)
-{
- if (unlikely (hb_object_is_immutable (buffer)))
- return;
-
- buffer->reset ();
-}
-
-/**
* hb_buffer_clear_contents:
* @buffer: An #hb_buffer_t
*
@@ -1749,6 +1822,8 @@ hb_buffer_append (hb_buffer_t *buffer,
if (!buffer->have_positions && source->have_positions)
buffer->clear_positions ();
+ hb_segment_properties_overlay (&buffer->props, &source->props);
+
memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0]));
if (buffer->have_positions)
memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0]));
diff --git a/thirdparty/harfbuzz/src/hb-buffer.h b/thirdparty/harfbuzz/src/hb-buffer.h
index a183cb9d4a..9fbd7b1ec3 100644
--- a/thirdparty/harfbuzz/src/hb-buffer.h
+++ b/thirdparty/harfbuzz/src/hb-buffer.h
@@ -76,18 +76,68 @@ typedef struct hb_glyph_info_t {
* @HB_GLYPH_FLAG_UNSAFE_TO_BREAK: Indicates that if input text is broken at the
* beginning of the cluster this glyph is part of,
* then both sides need to be re-shaped, as the
- * result might be different. On the flip side,
- * it means that when this flag is not present,
- * then it's safe to break the glyph-run at the
- * beginning of this cluster, and the two sides
- * represent the exact same result one would get
- * if breaking input text at the beginning of
- * this cluster and shaping the two sides
- * separately. This can be used to optimize
- * paragraph layout, by avoiding re-shaping
- * of each line after line-breaking, or limiting
- * the reshaping to a small piece around the
- * breaking point only.
+ * result might be different.
+ * On the flip side, it means that when this
+ * flag is not present, then it is safe to break
+ * the glyph-run at the beginning of this
+ * cluster, and the two sides will represent the
+ * exact same result one would get if breaking
+ * input text at the beginning of this cluster
+ * and shaping the two sides separately.
+ * This can be used to optimize paragraph
+ * layout, by avoiding re-shaping of each line
+ * after line-breaking.
+ * @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT: Indicates that if input text is changed on one
+ * side of the beginning of the cluster this glyph
+ * is part of, then the shaping results for the
+ * other side might change.
+ * Note that the absence of this flag will NOT by
+ * itself mean that it IS safe to concat text.
+ * Only two pieces of text both of which clear of
+ * this flag can be concatenated safely.
+ * This can be used to optimize paragraph
+ * layout, by avoiding re-shaping of each line
+ * after line-breaking, by limiting the
+ * reshaping to a small piece around the
+ * breaking positin only, even if the breaking
+ * position carries the
+ * #HB_GLYPH_FLAG_UNSAFE_TO_BREAK or when
+ * hyphenation or other text transformation
+ * happens at line-break position, in the following
+ * way:
+ * 1. Iterate back from the line-break position
+ * until the first cluster start position that is
+ * NOT unsafe-to-concat, 2. shape the segment from
+ * there till the end of line, 3. check whether the
+ * resulting glyph-run also is clear of the
+ * unsafe-to-concat at its start-of-text position;
+ * if it is, just splice it into place and the line
+ * is shaped; If not, move on to a position further
+ * back that is clear of unsafe-to-concat and retry
+ * from there, and repeat.
+ * At the start of next line a similar algorithm can
+ * be implemented. That is: 1. Iterate forward from
+ * the line-break position untill the first cluster
+ * start position that is NOT unsafe-to-concat, 2.
+ * shape the segment from beginning of the line to
+ * that position, 3. check whether the resulting
+ * glyph-run also is clear of the unsafe-to-concat
+ * at its end-of-text position; if it is, just splice
+ * it into place and the beginning is shaped; If not,
+ * move on to a position further forward that is clear
+ * of unsafe-to-concat and retry up to there, and repeat.
+ * A slight complication will arise in the
+ * implementation of the algorithm above,
+ * because while our buffer API has a way to
+ * return flags for position corresponding to
+ * start-of-text, there is currently no position
+ * corresponding to end-of-text. This limitation
+ * can be alleviated by shaping more text than needed
+ * and looking for unsafe-to-concat flag within text
+ * clusters.
+ * The #HB_GLYPH_FLAG_UNSAFE_TO_BREAK flag will
+ * always imply this flag.
+ * Since: 3.3.0
* @HB_GLYPH_FLAG_DEFINED: All the currently defined flags.
*
* Flags for #hb_glyph_info_t.
@@ -96,8 +146,9 @@ typedef struct hb_glyph_info_t {
*/
typedef enum { /*< flags >*/
HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001,
+ HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002,
- HB_GLYPH_FLAG_DEFINED = 0x00000001 /* OR of all defined flags */
+ HB_GLYPH_FLAG_DEFINED = 0x00000003 /* OR of all defined flags */
} hb_glyph_flags_t;
HB_EXTERN hb_glyph_flags_t
@@ -170,6 +221,9 @@ hb_segment_properties_equal (const hb_segment_properties_t *a,
HB_EXTERN unsigned int
hb_segment_properties_hash (const hb_segment_properties_t *p);
+HB_EXTERN void
+hb_segment_properties_overlay (hb_segment_properties_t *p,
+ const hb_segment_properties_t *src);
/**
@@ -185,6 +239,13 @@ HB_EXTERN hb_buffer_t *
hb_buffer_create (void);
HB_EXTERN hb_buffer_t *
+hb_buffer_create_similar (const hb_buffer_t *src);
+
+HB_EXTERN void
+hb_buffer_reset (hb_buffer_t *buffer);
+
+
+HB_EXTERN hb_buffer_t *
hb_buffer_get_empty (void);
HB_EXTERN hb_buffer_t *
@@ -391,8 +452,9 @@ HB_EXTERN hb_codepoint_t
hb_buffer_get_not_found_glyph (hb_buffer_t *buffer);
-HB_EXTERN void
-hb_buffer_reset (hb_buffer_t *buffer);
+/*
+ * Content API.
+ */
HB_EXTERN void
hb_buffer_clear_contents (hb_buffer_t *buffer);
diff --git a/thirdparty/harfbuzz/src/hb-buffer.hh b/thirdparty/harfbuzz/src/hb-buffer.hh
index 0f8140f1b3..adf4aa2b6f 100644
--- a/thirdparty/harfbuzz/src/hb-buffer.hh
+++ b/thirdparty/harfbuzz/src/hb-buffer.hh
@@ -67,8 +67,8 @@ enum hb_buffer_scratch_flags_t {
HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u,
HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u,
HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u,
- HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK = 0x00000010u,
- HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000020u,
+ HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000010u,
+ HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS = 0x00000020u,
/* Reserved for complex shapers' internal use. */
HB_BUFFER_SCRATCH_FLAG_COMPLEX0 = 0x01000000u,
@@ -87,18 +87,21 @@ struct hb_buffer_t
{
hb_object_header_t header;
- /* Information about how the text in the buffer should be treated */
+ /*
+ * Information about how the text in the buffer should be treated.
+ */
+
hb_unicode_funcs_t *unicode; /* Unicode functions */
hb_buffer_flags_t flags; /* BOT / EOT / etc. */
hb_buffer_cluster_level_t cluster_level;
hb_codepoint_t replacement; /* U+FFFD or something else. */
hb_codepoint_t invisible; /* 0 or something else. */
hb_codepoint_t not_found; /* 0 or something else. */
- hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */
- unsigned int max_len; /* Maximum allowed len. */
- int max_ops; /* Maximum allowed operations. */
- /* Buffer contents */
+ /*
+ * Buffer contents
+ */
+
hb_buffer_content_type_t content_type;
hb_segment_properties_t props; /* Script, language, direction */
@@ -115,8 +118,6 @@ struct hb_buffer_t
hb_glyph_info_t *out_info;
hb_glyph_position_t *pos;
- unsigned int serial;
-
/* Text before / after the main buffer contents.
* Always in Unicode, and ordered outward.
* Index 0 is for "pre-context", 1 for "post-context". */
@@ -124,7 +125,25 @@ struct hb_buffer_t
hb_codepoint_t context[2][CONTEXT_LENGTH];
unsigned int context_len[2];
- /* Debugging API */
+
+ /*
+ * Managed by enter / leave
+ */
+
+#ifndef HB_NDEBUG
+ uint8_t allocated_var_bits;
+#endif
+ uint8_t serial;
+ hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */
+ unsigned int max_len; /* Maximum allowed len. */
+ int max_ops; /* Maximum allowed operations. */
+ /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */
+
+
+ /*
+ * Messaging callback
+ */
+
#ifndef HB_NO_BUFFER_MESSAGE
hb_buffer_message_func_t message_func;
void *message_data;
@@ -134,11 +153,6 @@ struct hb_buffer_t
static constexpr unsigned message_depth = 0u;
#endif
- /* Internal debugging. */
- /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */
-#ifndef HB_NDEBUG
- uint8_t allocated_var_bits;
-#endif
/* Methods */
@@ -190,12 +204,17 @@ struct hb_buffer_t
hb_glyph_info_t &prev () { return out_info[out_len ? out_len - 1 : 0]; }
hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; }
+ HB_INTERNAL void similar (const hb_buffer_t &src);
HB_INTERNAL void reset ();
HB_INTERNAL void clear ();
+ /* Called around shape() */
+ HB_INTERNAL void enter ();
+ HB_INTERNAL void leave ();
+
unsigned int backtrack_len () const { return have_output ? out_len : idx; }
unsigned int lookahead_len () const { return len - idx; }
- unsigned int next_serial () { return serial++; }
+ uint8_t next_serial () { return ++serial ? serial : ++serial; }
HB_INTERNAL void add (hb_codepoint_t codepoint,
unsigned int cluster);
@@ -252,7 +271,7 @@ struct hb_buffer_t
HB_INTERNAL void guess_segment_properties ();
- HB_INTERNAL void swap_buffers ();
+ HB_INTERNAL void sync ();
HB_INTERNAL void clear_output ();
HB_INTERNAL void clear_positions ();
@@ -366,15 +385,80 @@ struct hb_buffer_t
/* Merge clusters for deleting current glyph, and skip it. */
HB_INTERNAL void delete_glyph ();
- void unsafe_to_break (unsigned int start,
- unsigned int end)
+
+ void set_glyph_flags (hb_mask_t mask,
+ unsigned start = 0,
+ unsigned end = (unsigned) -1,
+ bool interior = false,
+ bool from_out_buffer = false)
{
- if (end - start < 2)
+ end = hb_min (end, len);
+
+ if (interior && !from_out_buffer && end - start < 2)
return;
- unsafe_to_break_impl (start, end);
+
+ scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS;
+
+ if (!from_out_buffer || !have_output)
+ {
+ if (!interior)
+ {
+ for (unsigned i = start; i < end; i++)
+ info[i].mask |= mask;
+ }
+ else
+ {
+ unsigned cluster = _infos_find_min_cluster (info, start, end);
+ _infos_set_glyph_flags (info, start, end, cluster, mask);
+ }
+ }
+ else
+ {
+ assert (start <= out_len);
+ assert (idx <= end);
+
+ if (!interior)
+ {
+ for (unsigned i = start; i < out_len; i++)
+ out_info[i].mask |= mask;
+ for (unsigned i = idx; i < end; i++)
+ info[i].mask |= mask;
+ }
+ else
+ {
+ unsigned cluster = _infos_find_min_cluster (info, idx, end);
+ cluster = _infos_find_min_cluster (out_info, start, out_len, cluster);
+
+ _infos_set_glyph_flags (out_info, start, out_len, cluster, mask);
+ _infos_set_glyph_flags (info, idx, end, cluster, mask);
+ }
+ }
+ }
+
+ void unsafe_to_break (unsigned int start = 0, unsigned int end = -1)
+ {
+ set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT,
+ start, end,
+ true);
+ }
+ void unsafe_to_concat (unsigned int start = 0, unsigned int end = -1)
+ {
+ set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_CONCAT,
+ start, end,
+ true);
+ }
+ void unsafe_to_break_from_outbuffer (unsigned int start = 0, unsigned int end = -1)
+ {
+ set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT,
+ start, end,
+ true, true);
+ }
+ void unsafe_to_concat_from_outbuffer (unsigned int start = 0, unsigned int end = -1)
+ {
+ set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_CONCAT,
+ start, end,
+ false, true);
}
- HB_INTERNAL void unsafe_to_break_impl (unsigned int start, unsigned int end);
- HB_INTERNAL void unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end);
/* Internal methods */
@@ -465,36 +549,31 @@ struct hb_buffer_t
set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0)
{
if (inf.cluster != cluster)
- {
- if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
- inf.mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
- else
- inf.mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
- }
+ inf.mask = (inf.mask & ~HB_GLYPH_FLAG_DEFINED) | (mask & HB_GLYPH_FLAG_DEFINED);
inf.cluster = cluster;
}
-
+ void
+ _infos_set_glyph_flags (hb_glyph_info_t *infos,
+ unsigned int start, unsigned int end,
+ unsigned int cluster,
+ hb_mask_t mask)
+ {
+ for (unsigned int i = start; i < end; i++)
+ if (cluster != infos[i].cluster)
+ {
+ scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS;
+ infos[i].mask |= mask;
+ }
+ }
static unsigned
_infos_find_min_cluster (const hb_glyph_info_t *infos,
unsigned start, unsigned end,
- unsigned cluster)
+ unsigned cluster = UINT_MAX)
{
for (unsigned int i = start; i < end; i++)
cluster = hb_min (cluster, infos[i].cluster);
return cluster;
}
- void
- _unsafe_to_break_set_mask (hb_glyph_info_t *infos,
- unsigned int start, unsigned int end,
- unsigned int cluster)
- {
- for (unsigned int i = start; i < end; i++)
- if (cluster != infos[i].cluster)
- {
- scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK;
- infos[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
- }
- }
void clear_glyph_flags (hb_mask_t mask = 0)
{
diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh
index c251e2d0ed..641de0eff2 100644
--- a/thirdparty/harfbuzz/src/hb-cff-interp-common.hh
+++ b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh
@@ -217,9 +217,6 @@ inline unsigned int OpCode_Size (op_code_t op) { return Is_OpCode_ESC (op) ? 2:
struct number_t
{
- void init () { set_real (0.0); }
- void fini () {}
-
void set_int (int v) { value = v; }
int to_int () const { return value; }
@@ -245,7 +242,7 @@ struct number_t
}
protected:
- double value;
+ double value = 0.;
};
/* byte string */
@@ -380,10 +377,8 @@ struct cff_stack_t
count = 0;
elements.init ();
elements.resize (kSizeLimit);
- for (unsigned int i = 0; i < elements.length; i++)
- elements[i].init ();
}
- void fini () { elements.fini_deep (); }
+ void fini () { elements.fini (); }
ELEM& operator [] (unsigned int i)
{
@@ -523,9 +518,6 @@ struct arg_stack_t : cff_stack_t<ARG, 513>
/* an operator prefixed by its operands in a byte string */
struct op_str_t
{
- void init () {}
- void fini () {}
-
op_code_t op;
byte_str_t str;
};
@@ -553,7 +545,7 @@ struct parsed_values_t
opStart = 0;
values.init ();
}
- void fini () { values.fini_deep (); }
+ void fini () { values.fini (); }
void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ())
{
diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh
index 52d778ffe2..ef299369b5 100644
--- a/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh
+++ b/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh
@@ -94,12 +94,6 @@ struct biased_subrs_t
struct point_t
{
- void init ()
- {
- x.init ();
- y.init ();
- }
-
void set_int (int _x, int _y)
{
x.set_int (_x);
@@ -128,7 +122,7 @@ struct cs_interp_env_t : interp_env_t<ARG>
hstem_count = 0;
vstem_count = 0;
hintmask_size = 0;
- pt.init ();
+ pt.set_int (0, 0);
callStack.init ();
globalSubrs.init (globalSubrs_);
localSubrs.init (localSubrs_);
@@ -841,7 +835,6 @@ struct path_procs_t
if (likely (env.argStack.get_count () == 11))
{
point_t d;
- d.init ();
for (unsigned int i = 0; i < 10; i += 2)
d.move (env.eval_arg (i), env.eval_arg (i+1));
diff --git a/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh
index d961566447..766183760e 100644
--- a/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh
+++ b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh
@@ -35,18 +35,6 @@ using namespace OT;
struct blend_arg_t : number_t
{
- void init ()
- {
- number_t::init ();
- deltas.init ();
- }
-
- void fini ()
- {
- number_t::fini ();
- deltas.fini_deep ();
- }
-
void set_int (int v) { reset_blends (); number_t::set_int (v); }
void set_fixed (int32_t v) { reset_blends (); number_t::set_fixed (v); }
void set_real (double v) { reset_blends (); number_t::set_real (v); }
@@ -202,7 +190,7 @@ struct cff2_cs_opset_t : cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PA
switch (op) {
case OpCode_callsubr:
case OpCode_callgsubr:
- /* a subroutine number shoudln't be a blended value */
+ /* a subroutine number shouldn't be a blended value */
if (unlikely (env.argStack.peek ().blending ()))
{
env.set_error ();
diff --git a/thirdparty/harfbuzz/src/hb-common.cc b/thirdparty/harfbuzz/src/hb-common.cc
index 26c8ad0f49..249a8a8010 100644
--- a/thirdparty/harfbuzz/src/hb-common.cc
+++ b/thirdparty/harfbuzz/src/hb-common.cc
@@ -29,10 +29,31 @@
#include "hb.hh"
#include "hb-machinery.hh"
+#if !defined(HB_NO_SETLOCALE) && (!defined(HAVE_NEWLOCALE) || !defined(HAVE_USELOCALE))
+#define HB_NO_SETLOCALE 1
+#endif
+
+#ifndef HB_NO_SETLOCALE
+
#include <locale.h>
+#ifdef HAVE_XLOCALE_H
+#include <xlocale.h> // Needed on BSD/OS X for uselocale
+#endif
+
+#ifdef WIN32
+#define hb_locale_t _locale_t
+#else
+#define hb_locale_t locale_t
+#endif
+#define hb_setlocale setlocale
+#define hb_uselocale uselocale
+
+#else
+
+#define hb_locale_t void *
+#define hb_setlocale(Category, Locale) "C"
+#define hb_uselocale(Locale) ((hb_locale_t) 0)
-#ifdef HB_NO_SETLOCALE
-#define setlocale(Category, Locale) "C"
#endif
/**
@@ -122,7 +143,7 @@ hb_tag_from_string (const char *str, int len)
* @tag: #hb_tag_t to convert
* @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string
*
- * Converts an #hb_tag_t to a string and returns it in @buf.
+ * Converts an #hb_tag_t to a string and returns it in @buf.
* Strings will be four characters long.
*
* Since: 0.9.5
@@ -151,13 +172,13 @@ const char direction_strings[][4] = {
* @str: (array length=len) (element-type uint8_t): String to convert
* @len: Length of @str, or -1 if it is %NULL-terminated
*
- * Converts a string to an #hb_direction_t.
+ * Converts a string to an #hb_direction_t.
*
* Matching is loose and applies only to the first letter. For
* examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.
*
* Unmatched strings will return #HB_DIRECTION_INVALID.
- *
+ *
* Return value: The #hb_direction_t matching @str
*
* Since: 0.9.2
@@ -413,7 +434,7 @@ hb_language_get_default ()
hb_language_t language = default_language;
if (unlikely (language == HB_LANGUAGE_INVALID))
{
- language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
+ language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1);
(void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
}
@@ -1039,6 +1060,47 @@ hb_variation_from_string (const char *str, int len,
return false;
}
+#ifndef HB_NO_SETLOCALE
+
+static inline void free_static_C_locale ();
+
+static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,
+ hb_C_locale_lazy_loader_t>
+{
+ static hb_locale_t create ()
+ {
+ hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
+ if (!l)
+ return l;
+
+ hb_atexit (free_static_C_locale);
+
+ return l;
+ }
+ static void destroy (hb_locale_t l)
+ {
+ freelocale (l);
+ }
+ static hb_locale_t get_null ()
+ {
+ return (hb_locale_t) 0;
+ }
+} static_C_locale;
+
+static inline
+void free_static_C_locale ()
+{
+ static_C_locale.free_instance ();
+}
+
+static hb_locale_t
+get_C_locale ()
+{
+ return static_C_locale.get_unconst ();
+}
+
+#endif
+
/**
* hb_variation_to_string:
* @variation: an #hb_variation_t to convert
@@ -1064,7 +1126,11 @@ hb_variation_to_string (hb_variation_t *variation,
while (len && s[len - 1] == ' ')
len--;
s[len++] = '=';
+
+ hb_locale_t oldlocale HB_UNUSED;
+ oldlocale = hb_uselocale (get_C_locale ());
len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
+ (void) hb_uselocale (oldlocale);
assert (len < ARRAY_LENGTH (s));
len = hb_min (len, size - 1);
diff --git a/thirdparty/harfbuzz/src/hb-coretext.cc b/thirdparty/harfbuzz/src/hb-coretext.cc
index a512f3b8b7..5f383064c4 100644
--- a/thirdparty/harfbuzz/src/hb-coretext.cc
+++ b/thirdparty/harfbuzz/src/hb-coretext.cc
@@ -481,8 +481,8 @@ struct active_feature_t {
a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 :
0;
}
- bool operator== (const active_feature_t *f) {
- return cmp (this, f) == 0;
+ bool operator== (const active_feature_t& f) const {
+ return cmp (this, &f) == 0;
}
};
@@ -677,7 +677,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
{
active_features.push (event->feature);
} else {
- active_feature_t *feature = active_features.find (&event->feature);
+ active_feature_t *feature = active_features.lsearch (event->feature);
if (feature)
active_features.remove (feature - active_features.arrayZ);
}
@@ -1213,7 +1213,8 @@ resize_and_retry:
}
}
- buffer->clear_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK);
+ buffer->clear_glyph_flags ();
+ buffer->unsafe_to_break ();
#undef FAIL
diff --git a/thirdparty/harfbuzz/src/hb-directwrite.cc b/thirdparty/harfbuzz/src/hb-directwrite.cc
index dea87b8cd0..f177ff31c0 100644
--- a/thirdparty/harfbuzz/src/hb-directwrite.cc
+++ b/thirdparty/harfbuzz/src/hb-directwrite.cc
@@ -762,7 +762,8 @@ retry_getglyphs:
if (isRightToLeft) hb_buffer_reverse (buffer);
- buffer->clear_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK);
+ buffer->clear_glyph_flags ();
+ buffer->unsafe_to_break ();
delete [] clusterMap;
delete [] glyphIndices;
diff --git a/thirdparty/harfbuzz/src/hb-draw.h b/thirdparty/harfbuzz/src/hb-draw.h
index bddc876399..f82cc34842 100644
--- a/thirdparty/harfbuzz/src/hb-draw.h
+++ b/thirdparty/harfbuzz/src/hb-draw.h
@@ -50,7 +50,7 @@ typedef void (*hb_draw_close_path_func_t) (void *user_data);
*
* Glyph draw callbacks.
*
- * _move_to, _line_to and _cubic_to calls are nessecary to be defined but we
+ * _move_to, _line_to and _cubic_to calls are necessary to be defined but we
* translate _quadratic_to calls to _cubic_to if the callback isn't defined.
*
* Since: EXPERIMENTAL
diff --git a/thirdparty/harfbuzz/src/hb-face.cc b/thirdparty/harfbuzz/src/hb-face.cc
index 2c0087370c..5365598636 100644
--- a/thirdparty/harfbuzz/src/hb-face.cc
+++ b/thirdparty/harfbuzz/src/hb-face.cc
@@ -143,7 +143,7 @@ hb_face_create_for_tables (hb_reference_table_func_t reference_table_func,
typedef struct hb_face_for_data_closure_t {
hb_blob_t *blob;
- unsigned int index;
+ uint16_t index;
} hb_face_for_data_closure_t;
static hb_face_for_data_closure_t *
@@ -156,7 +156,7 @@ _hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index)
return nullptr;
closure->blob = blob;
- closure->index = index;
+ closure->index = (uint16_t) (index & 0xFFFFu);
return closure;
}
@@ -195,9 +195,19 @@ _hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void
* @index: The index of the face within @blob
*
* Constructs a new face object from the specified blob and
- * a face index into that blob. This is used for blobs of
- * file formats such as Dfont and TTC that can contain more
- * than one face.
+ * a face index into that blob.
+ *
+ * The face index is used for blobs of file formats such as TTC and
+ * and DFont that can contain more than one face. Face indices within
+ * such collections are zero-based.
+ *
+ * <note>Note: If the blob font format is not a collection, @index
+ * is ignored. Otherwise, only the lower 16-bits of @index are used.
+ * The unmodified @index can be accessed via hb_face_get_index().</note>
+ *
+ * <note>Note: The high 16-bits of @index, if non-zero, are used by
+ * hb_font_create() to load named-instances in variable fonts. See
+ * hb_font_create() for details.</note>
*
* Return value: (transfer full): The new face object
*
@@ -420,7 +430,8 @@ hb_face_reference_blob (hb_face_t *face)
* Assigns the specified face-index to @face. Fails if the
* face is immutable.
*
- * <note>Note: face indices within a collection are zero-based.</note>
+ * <note>Note: changing the index has no effect on the face itself
+ * This only changes the value returned by hb_face_get_index().</note>
*
* Since: 0.9.2
**/
diff --git a/thirdparty/harfbuzz/src/hb-font.cc b/thirdparty/harfbuzz/src/hb-font.cc
index fa8da96395..350fcac139 100644
--- a/thirdparty/harfbuzz/src/hb-font.cc
+++ b/thirdparty/harfbuzz/src/hb-font.cc
@@ -631,7 +631,7 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs)
* @destroy: (nullable): A callback to call when @data is not needed anymore
* @replace: Whether to replace an existing data with the same key
*
- * Attaches a user-data key/data pair to the specified font-functions structure.
+ * Attaches a user-data key/data pair to the specified font-functions structure.
*
* Return value: %true if success, %false otherwise
*
@@ -821,7 +821,7 @@ hb_font_get_glyph (hb_font_t *font,
* @glyph: (out): The glyph ID retrieved
*
* Fetches the nominal glyph ID for a Unicode code point in the
- * specified font.
+ * specified font.
*
* This version of the function should not be used to fetch glyph IDs
* for code points modified by variation selectors. For variation-selector
@@ -940,7 +940,7 @@ hb_font_get_glyph_v_advance (hb_font_t *font,
* @advance_stride: The stride between successive advances
*
* Fetches the advances for a sequence of glyph IDs in the specified
- * font, for horizontal text segments.
+ * font, for horizontal text segments.
*
* Since: 1.8.6
**/
@@ -964,7 +964,7 @@ hb_font_get_glyph_h_advances (hb_font_t* font,
* @advance_stride: (out): The stride between successive advances
*
* Fetches the advances for a sequence of glyph IDs in the specified
- * font, for vertical text segments.
+ * font, for vertical text segments.
*
* Since: 1.8.6
**/
@@ -1278,7 +1278,7 @@ hb_font_get_glyph_origin_for_direction (hb_font_t *font,
* @font: #hb_font_t to work upon
* @glyph: The glyph ID to query
* @direction: The direction of the text segment
- * @x: (inout): Input = The original X coordinate
+ * @x: (inout): Input = The original X coordinate
* Output = The X coordinate plus the X-coordinate of the origin
* @y: (inout): Input = The original Y coordinate
* Output = The Y coordinate plus the Y-coordinate of the origin
@@ -1306,7 +1306,7 @@ hb_font_add_glyph_origin_for_direction (hb_font_t *font,
* @font: #hb_font_t to work upon
* @glyph: The glyph ID to query
* @direction: The direction of the text segment
- * @x: (inout): Input = The original X coordinate
+ * @x: (inout): Input = The original X coordinate
* Output = The X coordinate minus the X-coordinate of the origin
* @y: (inout): Input = The original Y coordinate
* Output = The Y coordinate minus the Y-coordinate of the origin
@@ -1477,6 +1477,8 @@ DEFINE_NULL_INSTANCE (hb_font_t) =
1000, /* x_scale */
1000, /* y_scale */
+ 0., /* slant */
+ 0., /* slant_xy; */
1<<16, /* x_mult */
1<<16, /* y_mult */
@@ -1521,6 +1523,13 @@ _hb_font_create (hb_face_t *face)
*
* Constructs a new font object from the specified face.
*
+ * <note>Note: If @face's index value (as passed to hb_face_create()
+ * has non-zero top 16-bits, those bits minus one are passed to
+ * hb_font_set_var_named_instance(), effectively loading a named-instance
+ * of a variable font, instead of the default-instance. This allows
+ * specifying which named-instance to load by default when creating the
+ * face.</note>
+ *
* Return value: (transfer full): The new font object
*
* Since: 0.9.2
@@ -1535,6 +1544,11 @@ hb_font_create (hb_face_t *face)
hb_ot_font_set_funcs (font);
#endif
+#ifndef HB_NO_VAR
+ if (face && face->index >> 16)
+ hb_font_set_var_named_instance (font, (face->index >> 16) - 1);
+#endif
+
return font;
}
@@ -1578,6 +1592,7 @@ hb_font_create_sub_font (hb_font_t *parent)
font->x_scale = parent->x_scale;
font->y_scale = parent->y_scale;
+ font->slant = parent->slant;
font->mults_changed ();
font->x_ppem = parent->x_ppem;
font->y_ppem = parent->y_ppem;
@@ -1668,12 +1683,12 @@ hb_font_destroy (hb_font_t *font)
/**
* hb_font_set_user_data: (skip)
* @font: #hb_font_t to work upon
- * @key: The user-data key
+ * @key: The user-data key
* @data: A pointer to the user data
* @destroy: (nullable): A callback to call when @data is not needed anymore
* @replace: Whether to replace an existing data with the same key
*
- * Attaches a user-data key/data pair to the specified font object.
+ * Attaches a user-data key/data pair to the specified font object.
*
* Return value: %true if success, %false otherwise
*
@@ -1875,7 +1890,7 @@ hb_font_set_funcs (hb_font_t *font,
* @font_data: (destroy destroy) (scope notified): Data to attach to @font
* @destroy: (nullable): The function to call when @font_data is not needed anymore
*
- * Replaces the user data attached to a font, updating the font's
+ * Replaces the user data attached to a font, updating the font's
* @destroy callback.
*
* Since: 0.9.2
@@ -1949,7 +1964,7 @@ hb_font_get_scale (hb_font_t *font,
* @x_ppem: Horizontal ppem value to assign
* @y_ppem: Vertical ppem value to assign
*
- * Sets the horizontal and vertical pixels-per-em (ppem) of a font.
+ * Sets the horizontal and vertical pixels-per-em (ppem) of a font.
*
* Since: 0.9.2
**/
@@ -1971,7 +1986,7 @@ hb_font_set_ppem (hb_font_t *font,
* @x_ppem: (out): Horizontal ppem value
* @y_ppem: (out): Vertical ppem value
*
- * Fetches the horizontal and vertical points-per-em (ppem) of a font.
+ * Fetches the horizontal and vertical points-per-em (ppem) of a font.
*
* Since: 0.9.2
**/
@@ -2015,7 +2030,7 @@ hb_font_set_ptem (hb_font_t *font,
*
* Return value: Point size. A value of zero means "not set."
*
- * Since: 0.9.2
+ * Since: 1.6.0
**/
float
hb_font_get_ptem (hb_font_t *font)
@@ -2023,6 +2038,49 @@ hb_font_get_ptem (hb_font_t *font)
return font->ptem;
}
+/**
+ * hb_font_set_synthetic_slant:
+ * @font: #hb_font_t to work upon
+ * @slant: synthetic slant value.
+ *
+ * Sets the "synthetic slant" of a font. By default is zero.
+ * Synthetic slant is the graphical skew that the renderer
+ * applies to the font at rendering time.
+ *
+ * HarfBuzz needs to know this value to adjust shaping results,
+ * metrics, and style values to match the slanted rendering.
+ *
+ * <note>Note: The slant value is a ratio. For example, a
+ * 20% slant would be represented as a 0.2 value.</note>
+ *
+ * Since: 3.3.0
+ **/
+HB_EXTERN void
+hb_font_set_synthetic_slant (hb_font_t *font, float slant)
+{
+ if (hb_object_is_immutable (font))
+ return;
+
+ font->slant = slant;
+ font->mults_changed ();
+}
+
+/**
+ * hb_font_get_synthetic_slant:
+ * @font: #hb_font_t to work upon
+ *
+ * Fetches the "synthetic slant" of a font.
+ *
+ * Return value: Synthetic slant. By default is zero.
+ *
+ * Since: 3.3.0
+ **/
+HB_EXTERN float
+hb_font_get_synthetic_slant (hb_font_t *font)
+{
+ return font->slant;
+}
+
#ifndef HB_NO_VAR
/*
* Variations
@@ -2036,6 +2094,10 @@ hb_font_get_ptem (hb_font_t *font)
*
* Applies a list of font-variation settings to a font.
*
+ * Note that this overrides all existing variations set on @font.
+ * Axes not included in @variations will be effectively set to their
+ * default values.
+ *
* Since: 1.4.2
*/
void
@@ -2091,6 +2153,10 @@ hb_font_set_variations (hb_font_t *font,
* Applies a list of variation coordinates (in design-space units)
* to a font.
*
+ * Note that this overrides all existing variations set on @font.
+ * Axes not included in @coords will be effectively set to their
+ * default values.
+ *
* Since: 1.4.2
*/
void
@@ -2154,6 +2220,10 @@ hb_font_set_var_named_instance (hb_font_t *font,
* Applies a list of variation coordinates (in normalized units)
* to a font.
*
+ * Note that this overrides all existing variations set on @font.
+ * Axes not included in @coords will be effectively set to their
+ * default values.
+ *
* <note>Note: Coordinates should be normalized to 2.14.</note>
*
* Since: 1.4.2
@@ -2196,14 +2266,19 @@ hb_font_set_var_coords_normalized (hb_font_t *font,
/**
* hb_font_get_var_coords_normalized:
* @font: #hb_font_t to work upon
- * @length: Number of coordinates retrieved
+ * @length: (out): Number of coordinates retrieved
*
* Fetches the list of normalized variation coordinates currently
* set on a font.
*
+ * Note that this returned array may only contain values for some
+ * (or none) of the axes; omitted axes effectively have zero values.
+ *
* Return value is valid as long as variation coordinates of the font
* are not modified.
*
+ * Return value: coordinates array
+ *
* Since: 1.4.2
*/
const int *
@@ -2216,18 +2291,24 @@ hb_font_get_var_coords_normalized (hb_font_t *font,
return font->coords;
}
-#ifdef HB_EXPERIMENTAL_API
/**
* hb_font_get_var_coords_design:
* @font: #hb_font_t to work upon
- * @length: (out): number of coordinates
+ * @length: (out): Number of coordinates retrieved
+ *
+ * Fetches the list of variation coordinates (in design-space units) currently
+ * set on a font.
+ *
+ * Note that this returned array may only contain values for some
+ * (or none) of the axes; omitted axes effectively have their default
+ * values.
*
* Return value is valid as long as variation coordinates of the font
* are not modified.
*
* Return value: coordinates array
*
- * Since: EXPERIMENTAL
+ * Since: 3.3.0
*/
const float *
hb_font_get_var_coords_design (hb_font_t *font,
@@ -2239,7 +2320,6 @@ hb_font_get_var_coords_design (hb_font_t *font,
return font->design_coords;
}
#endif
-#endif
#ifndef HB_DISABLE_DEPRECATED
/*
diff --git a/thirdparty/harfbuzz/src/hb-font.h b/thirdparty/harfbuzz/src/hb-font.h
index 15dc126523..a3bbb2e37b 100644
--- a/thirdparty/harfbuzz/src/hb-font.h
+++ b/thirdparty/harfbuzz/src/hb-font.h
@@ -1024,6 +1024,12 @@ HB_EXTERN float
hb_font_get_ptem (hb_font_t *font);
HB_EXTERN void
+hb_font_set_synthetic_slant (hb_font_t *font, float slant);
+
+HB_EXTERN float
+hb_font_get_synthetic_slant (hb_font_t *font);
+
+HB_EXTERN void
hb_font_set_variations (hb_font_t *font,
const hb_variation_t *variations,
unsigned int variations_length);
@@ -1033,11 +1039,9 @@ hb_font_set_var_coords_design (hb_font_t *font,
const float *coords,
unsigned int coords_length);
-#ifdef HB_EXPERIMENTAL_API
HB_EXTERN const float *
hb_font_get_var_coords_design (hb_font_t *font,
unsigned int *length);
-#endif
HB_EXTERN void
hb_font_set_var_coords_normalized (hb_font_t *font,
diff --git a/thirdparty/harfbuzz/src/hb-font.hh b/thirdparty/harfbuzz/src/hb-font.hh
index 1b7f445e8b..0d73589e8c 100644
--- a/thirdparty/harfbuzz/src/hb-font.hh
+++ b/thirdparty/harfbuzz/src/hb-font.hh
@@ -109,6 +109,8 @@ struct hb_font_t
int32_t x_scale;
int32_t y_scale;
+ float slant;
+ float slant_xy;
int64_t x_mult;
int64_t y_mult;
@@ -617,6 +619,7 @@ struct hb_font_t
signed upem = face->get_upem ();
x_mult = ((int64_t) x_scale << 16) / upem;
y_mult = ((int64_t) y_scale << 16) / upem;
+ slant_xy = y_scale ? slant * x_scale / y_scale : 0.f;
}
hb_position_t em_mult (int16_t v, int64_t mult)
diff --git a/thirdparty/harfbuzz/src/hb-graphite2.cc b/thirdparty/harfbuzz/src/hb-graphite2.cc
index 42420ac0b0..63dc18b466 100644
--- a/thirdparty/harfbuzz/src/hb-graphite2.cc
+++ b/thirdparty/harfbuzz/src/hb-graphite2.cc
@@ -439,7 +439,8 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED,
if (feats) gr_featureval_destroy (feats);
gr_seg_destroy (seg);
- buffer->clear_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK);
+ buffer->clear_glyph_flags ();
+ buffer->unsafe_to_break ();
return true;
}
diff --git a/thirdparty/harfbuzz/src/hb-iter.hh b/thirdparty/harfbuzz/src/hb-iter.hh
index ad2e45e3c5..43a3098f65 100644
--- a/thirdparty/harfbuzz/src/hb-iter.hh
+++ b/thirdparty/harfbuzz/src/hb-iter.hh
@@ -90,8 +90,8 @@ struct hb_iter_t
* it will be returning pointer to temporary rvalue.
* TODO Use a wrapper return type to fix for non-reference type. */
template <typename T = item_t,
- hb_enable_if (hb_is_reference (T))>
- hb_remove_reference<item_t>* operator -> () const { return hb_addressof (**thiz()); }
+ hb_enable_if (std::is_reference<T>::value)>
+ hb_remove_reference<item_t>* operator -> () const { return std::addressof (**thiz()); }
item_t operator * () const { return thiz()->__item__ (); }
item_t operator * () { return thiz()->__item__ (); }
item_t operator [] (unsigned i) const { return thiz()->__item_at__ (i); }
@@ -289,7 +289,7 @@ struct hb_is_source_of
{
private:
template <typename Iter2 = Iter,
- hb_enable_if (hb_is_convertible (typename Iter2::item_t, hb_add_lvalue_reference<hb_add_const<Item>>))>
+ hb_enable_if (hb_is_convertible (typename Iter2::item_t, hb_add_lvalue_reference<const Item>))>
static hb_true_type impl (hb_priority<2>);
template <typename Iter2 = Iter>
static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) >> hb_declval (Item &), hb_true_type ());
diff --git a/thirdparty/harfbuzz/src/hb-kern.hh b/thirdparty/harfbuzz/src/hb-kern.hh
index 3f952fe7fc..9ea945caed 100644
--- a/thirdparty/harfbuzz/src/hb-kern.hh
+++ b/thirdparty/harfbuzz/src/hb-kern.hh
@@ -49,6 +49,10 @@ struct hb_kern_machine_t
hb_mask_t kern_mask,
bool scale = true) const
{
+ if (!buffer->message (font, "start kern"))
+ return;
+
+ buffer->unsafe_to_concat ();
OT::hb_ot_apply_context_t c (1, font, buffer);
c.set_lookup_mask (kern_mask);
c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
@@ -67,7 +71,8 @@ struct hb_kern_machine_t
}
skippy_iter.reset (idx, 1);
- if (!skippy_iter.next ())
+ unsigned unsafe_to;
+ if (!skippy_iter.next (&unsafe_to))
{
idx++;
continue;
@@ -125,6 +130,8 @@ struct hb_kern_machine_t
skip:
idx = skippy_iter.idx;
}
+
+ (void) buffer->message (font, "end kern");
}
const Driver &driver;
diff --git a/thirdparty/harfbuzz/src/hb-machinery.hh b/thirdparty/harfbuzz/src/hb-machinery.hh
index 010c2570d7..5046ac1933 100644
--- a/thirdparty/harfbuzz/src/hb-machinery.hh
+++ b/thirdparty/harfbuzz/src/hb-machinery.hh
@@ -244,19 +244,19 @@ struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData>
{
Stored *p = (Stored *) hb_calloc (1, sizeof (Stored));
if (likely (p))
- p->init (data);
+ p = new (p) Stored (data);
return p;
}
static Stored *create ()
{
Stored *p = (Stored *) hb_calloc (1, sizeof (Stored));
if (likely (p))
- p->init ();
+ p = new (p) Stored ();
return p;
}
static void destroy (Stored *p)
{
- p->fini ();
+ p->~Stored ();
hb_free (p);
}
diff --git a/thirdparty/harfbuzz/src/hb-map.hh b/thirdparty/harfbuzz/src/hb-map.hh
index 793dcf22ca..9341637eac 100644
--- a/thirdparty/harfbuzz/src/hb-map.hh
+++ b/thirdparty/harfbuzz/src/hb-map.hh
@@ -37,13 +37,10 @@
template <typename K, typename V,
typename k_invalid_t = K,
typename v_invalid_t = V,
- k_invalid_t kINVALID = hb_is_pointer (K) ? 0 : std::is_signed<K>::value ? hb_int_min (K) : (K) -1,
- v_invalid_t vINVALID = hb_is_pointer (V) ? 0 : std::is_signed<V>::value ? hb_int_min (V) : (V) -1>
+ k_invalid_t kINVALID = std::is_pointer<K>::value ? 0 : std::is_signed<K>::value ? hb_int_min (K) : (K) -1,
+ v_invalid_t vINVALID = std::is_pointer<V>::value ? 0 : std::is_signed<V>::value ? hb_int_min (V) : (V) -1>
struct hb_hashmap_t
{
- static constexpr K INVALID_KEY = kINVALID;
- static constexpr V INVALID_VALUE = vINVALID;
-
hb_hashmap_t () { init (); }
~hb_hashmap_t () { fini (); }
@@ -64,24 +61,40 @@ struct hb_hashmap_t
hb_copy (o, *this);
}
- static_assert (std::is_trivially_copyable<K>::value, "");
- static_assert (std::is_trivially_copyable<V>::value, "");
- static_assert (std::is_trivially_destructible<K>::value, "");
- static_assert (std::is_trivially_destructible<V>::value, "");
-
struct item_t
{
K key;
V value;
uint32_t hash;
- void clear () { key = kINVALID; value = vINVALID; hash = 0; }
+ void clear ()
+ {
+ new (std::addressof (key)) K ();
+ key = hb_coerce<K> (kINVALID);
+ new (std::addressof (value)) V ();
+ value = hb_coerce<V> (vINVALID);
+ hash = 0;
+ }
bool operator == (const K &o) { return hb_deref (key) == hb_deref (o); }
bool operator == (const item_t &o) { return *this == o.key; }
- bool is_unused () const { return key == kINVALID; }
- bool is_tombstone () const { return key != kINVALID && value == vINVALID; }
- bool is_real () const { return key != kINVALID && value != vINVALID; }
+ bool is_unused () const
+ {
+ const K inv = hb_coerce<K> (kINVALID);
+ return key == inv;
+ }
+ bool is_tombstone () const
+ {
+ const K kinv = hb_coerce<K> (kINVALID);
+ const V vinv = hb_coerce<V> (vINVALID);
+ return key != kinv && value == vinv;
+ }
+ bool is_real () const
+ {
+ const K kinv = hb_coerce<K> (kINVALID);
+ const V vinv = hb_coerce<V> (vINVALID);
+ return key != kinv && value != vinv;
+ }
hb_pair_t<K, V> get_pair() const { return hb_pair_t<K, V> (key, value); }
};
@@ -118,8 +131,13 @@ struct hb_hashmap_t
}
void fini_shallow ()
{
- hb_free (items);
- items = nullptr;
+ if (likely (items)) {
+ unsigned size = mask + 1;
+ for (unsigned i = 0; i < size; i++)
+ items[i].~item_t ();
+ hb_free (items);
+ items = nullptr;
+ }
population = occupancy = 0;
}
void fini ()
@@ -163,10 +181,15 @@ struct hb_hashmap_t
/* Insert back old items. */
if (old_items)
for (unsigned int i = 0; i < old_size; i++)
+ {
if (old_items[i].is_real ())
+ {
set_with_hash (old_items[i].key,
old_items[i].hash,
std::move (old_items[i].value));
+ }
+ old_items[i].~item_t ();
+ }
hb_free (old_items);
@@ -178,22 +201,22 @@ struct hb_hashmap_t
V get (K key) const
{
- if (unlikely (!items)) return vINVALID;
+ if (unlikely (!items)) return hb_coerce<V> (vINVALID);
unsigned int i = bucket_for (key);
- return items[i].is_real () && items[i] == key ? items[i].value : vINVALID;
+ return items[i].is_real () && items[i] == key ? items[i].value : hb_coerce<V> (vINVALID);
}
- void del (K key) { set (key, vINVALID); }
+ void del (K key) { set (key, hb_coerce<V> (vINVALID)); }
/* Has interface. */
- static constexpr V SENTINEL = vINVALID;
typedef V value_t;
value_t operator [] (K k) const { return get (k); }
bool has (K k, V *vp = nullptr) const
{
V v = (*this)[k];
if (vp) *vp = v;
- return v != SENTINEL;
+ const V vinv = hb_coerce<V> (vINVALID);
+ return v != vinv;
}
/* Projection. */
V operator () (K k) const { return get (k); }
@@ -248,11 +271,13 @@ struct hb_hashmap_t
bool set_with_hash (K key, uint32_t hash, VV&& value)
{
if (unlikely (!successful)) return false;
- if (unlikely (key == kINVALID)) return true;
+ const K kinv = hb_coerce<K> (kINVALID);
+ if (unlikely (key == kinv)) return true;
if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false;
unsigned int i = bucket_for_hash (key, hash);
- if (value == vINVALID && items[i].key != key)
+ const V vinv = hb_coerce<V> (vINVALID);
+ if (value == vinv && items[i].key != key)
return true; /* Trying to delete non-existent key. */
if (!items[i].is_unused ())
diff --git a/thirdparty/harfbuzz/src/hb-meta.hh b/thirdparty/harfbuzz/src/hb-meta.hh
index 0ea5774a9f..3fea5d995e 100644
--- a/thirdparty/harfbuzz/src/hb-meta.hh
+++ b/thirdparty/harfbuzz/src/hb-meta.hh
@@ -29,6 +29,7 @@
#include "hb.hh"
+#include <memory>
#include <type_traits>
#include <utility>
@@ -85,30 +86,13 @@ template <> struct hb_priority<0> {};
template <typename T> struct hb_type_identity_t { typedef T type; };
template <typename T> using hb_type_identity = typename hb_type_identity_t<T>::type;
-struct
-{
- template <typename T> constexpr T*
- operator () (T& arg) const
- {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wcast-align"
- /* https://en.cppreference.com/w/cpp/memory/addressof */
- return reinterpret_cast<T*> (
- &const_cast<char&> (
- reinterpret_cast<const volatile char&> (arg)));
-#pragma GCC diagnostic pop
- }
-}
-HB_FUNCOBJ (hb_addressof);
-
template <typename T> static inline T hb_declval ();
#define hb_declval(T) (hb_declval<T> ())
template <typename T> struct hb_match_const : hb_type_identity_t<T>, hb_false_type {};
template <typename T> struct hb_match_const<const T> : hb_type_identity_t<T>, hb_true_type {};
template <typename T> using hb_remove_const = typename hb_match_const<T>::type;
-template <typename T> using hb_add_const = const T;
-#define hb_is_const(T) hb_match_const<T>::value
+
template <typename T> struct hb_match_reference : hb_type_identity_t<T>, hb_false_type {};
template <typename T> struct hb_match_reference<T &> : hb_type_identity_t<T>, hb_true_type {};
template <typename T> struct hb_match_reference<T &&> : hb_type_identity_t<T>, hb_true_type {};
@@ -119,14 +103,13 @@ template <typename T> using hb_add_lvalue_reference = decltype (_hb_try_add_lval
template <typename T> auto _hb_try_add_rvalue_reference (hb_priority<1>) -> hb_type_identity<T&&>;
template <typename T> auto _hb_try_add_rvalue_reference (hb_priority<0>) -> hb_type_identity<T>;
template <typename T> using hb_add_rvalue_reference = decltype (_hb_try_add_rvalue_reference<T> (hb_prioritize));
-#define hb_is_reference(T) hb_match_reference<T>::value
+
template <typename T> struct hb_match_pointer : hb_type_identity_t<T>, hb_false_type {};
template <typename T> struct hb_match_pointer<T *> : hb_type_identity_t<T>, hb_true_type {};
template <typename T> using hb_remove_pointer = typename hb_match_pointer<T>::type;
template <typename T> auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity<hb_remove_reference<T>*>;
template <typename T> auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity<T>;
template <typename T> using hb_add_pointer = decltype (_hb_try_add_pointer<T> (hb_prioritize));
-#define hb_is_pointer(T) hb_match_pointer<T>::value
/* TODO Add feature-parity to std::decay. */
@@ -137,8 +120,8 @@ template <typename T> using hb_decay = hb_remove_const<hb_remove_reference<T>>;
template <typename From, typename To>
using hb_is_cr_convertible = hb_bool_constant<
hb_is_same (hb_decay<From>, hb_decay<To>) &&
- (!hb_is_const (From) || hb_is_const (To)) &&
- (!hb_is_reference (To) || hb_is_const (To) || hb_is_reference (To))
+ (!std::is_const<From>::value || std::is_const<To>::value) &&
+ (!std::is_reference<To>::value || std::is_const<To>::value || std::is_reference<To>::value)
>;
#define hb_is_cr_convertible(From,To) hb_is_cr_convertible<From, To>::value
@@ -153,16 +136,6 @@ struct
}
HB_FUNCOBJ (hb_deref);
-struct
-{
- template <typename T> constexpr auto
- operator () (T&& v) const HB_AUTO_RETURN (std::forward<T> (v))
-
- template <typename T> constexpr auto
- operator () (T& v) const HB_AUTO_RETURN (hb_addressof (v))
-}
-HB_FUNCOBJ (hb_ref);
-
template <typename T>
struct hb_reference_wrapper
{
@@ -176,7 +149,7 @@ struct hb_reference_wrapper
template <typename T>
struct hb_reference_wrapper<T&>
{
- hb_reference_wrapper (T& v) : v (hb_addressof (v)) {}
+ hb_reference_wrapper (T& v) : v (std::addressof (v)) {}
bool operator == (const hb_reference_wrapper& o) const { return v == o.v; }
bool operator != (const hb_reference_wrapper& o) const { return v != o.v; }
operator T& () const { return *v; }
diff --git a/thirdparty/harfbuzz/src/hb-ms-feature-ranges.cc b/thirdparty/harfbuzz/src/hb-ms-feature-ranges.cc
deleted file mode 100644
index 6d09b252d8..0000000000
--- a/thirdparty/harfbuzz/src/hb-ms-feature-ranges.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright © 2011,2012,2013 Google, Inc.
- * Copyright © 2021 Khaled Hosny
- *
- * This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#include "hb-ms-feature-ranges.hh"
-
-bool
-hb_ms_setup_features (const hb_feature_t *features,
- unsigned int num_features,
- hb_vector_t<hb_ms_feature_t> &feature_records, /* OUT */
- hb_vector_t<hb_ms_range_record_t> &range_records /* OUT */)
-{
- feature_records.shrink(0);
- range_records.shrink(0);
-
- /* Sort features by start/end events. */
- hb_vector_t<hb_ms_feature_event_t> feature_events;
- for (unsigned int i = 0; i < num_features; i++)
- {
- hb_ms_active_feature_t feature;
- feature.fea.tag_le = hb_uint32_swap (features[i].tag);
- feature.fea.value = features[i].value;
- feature.order = i;
-
- hb_ms_feature_event_t *event;
-
- event = feature_events.push ();
- event->index = features[i].start;
- event->start = true;
- event->feature = feature;
-
- event = feature_events.push ();
- event->index = features[i].end;
- event->start = false;
- event->feature = feature;
- }
- feature_events.qsort ();
- /* Add a strategic final event. */
- {
- hb_ms_active_feature_t feature;
- feature.fea.tag_le = 0;
- feature.fea.value = 0;
- feature.order = num_features + 1;
-
- auto *event = feature_events.push ();
- event->index = 0; /* This value does magic. */
- event->start = false;
- event->feature = feature;
- }
-
- /* Scan events and save features for each range. */
- hb_vector_t<hb_ms_active_feature_t> active_features;
- unsigned int last_index = 0;
- for (unsigned int i = 0; i < feature_events.length; i++)
- {
- auto *event = &feature_events[i];
-
- if (event->index != last_index)
- {
- /* Save a snapshot of active features and the range. */
- auto *range = range_records.push ();
- auto offset = feature_records.length;
-
- active_features.qsort ();
- for (unsigned int j = 0; j < active_features.length; j++)
- {
- if (!j || active_features[j].fea.tag_le != feature_records[feature_records.length - 1].tag_le)
- {
- feature_records.push (active_features[j].fea);
- }
- else
- {
- /* Overrides value for existing feature. */
- feature_records[feature_records.length - 1].value = active_features[j].fea.value;
- }
- }
-
- /* Will convert to pointer after all is ready, since feature_records.array
- * may move as we grow it. */
- range->features.features = reinterpret_cast<hb_ms_feature_t *> (offset);
- range->features.num_features = feature_records.length - offset;
- range->index_first = last_index;
- range->index_last = event->index - 1;
-
- last_index = event->index;
- }
-
- if (event->start)
- {
- active_features.push (event->feature);
- }
- else
- {
- auto *feature = active_features.find (&event->feature);
- if (feature)
- active_features.remove (feature - active_features.arrayZ);
- }
- }
-
- if (!range_records.length) /* No active feature found. */
- num_features = 0;
-
- /* Fixup the pointers. */
- for (unsigned int i = 0; i < range_records.length; i++)
- {
- auto *range = &range_records[i];
- range->features.features = (hb_ms_feature_t *) feature_records + reinterpret_cast<uintptr_t> (range->features.features);
- }
-
- return !!num_features;
-}
-
-void
-hb_ms_make_feature_ranges (hb_vector_t<hb_ms_feature_t> &feature_records,
- hb_vector_t<hb_ms_range_record_t> &range_records,
- unsigned int chars_offset,
- unsigned int chars_len,
- uint16_t *log_clusters,
- hb_vector_t<hb_ms_features_t*> &range_features, /* OUT */
- hb_vector_t<uint32_t> &range_counts /* OUT */)
-{
- range_features.shrink (0);
- range_counts.shrink (0);
-
- auto *last_range = &range_records[0];
- for (unsigned int i = chars_offset; i < chars_len; i++)
- {
- auto *range = last_range;
- while (log_clusters[i] < range->index_first)
- range--;
- while (log_clusters[i] > range->index_last)
- range++;
- if (!range_features.length ||
- &range->features != range_features[range_features.length - 1])
- {
- auto **features = range_features.push ();
- auto *c = range_counts.push ();
- if (unlikely (!features || !c))
- {
- range_features.shrink (0);
- range_counts.shrink (0);
- break;
- }
- *features = &range->features;
- *c = 1;
- }
- else
- {
- range_counts[range_counts.length - 1]++;
- }
-
- last_range = range;
- }
-}
diff --git a/thirdparty/harfbuzz/src/hb-ms-feature-ranges.hh b/thirdparty/harfbuzz/src/hb-ms-feature-ranges.hh
index 401d1e1d97..d40fdeaa82 100644
--- a/thirdparty/harfbuzz/src/hb-ms-feature-ranges.hh
+++ b/thirdparty/harfbuzz/src/hb-ms-feature-ranges.hh
@@ -52,8 +52,8 @@ struct hb_ms_active_feature_t {
a->fea.value < b->fea.value ? -1 : a->fea.value > b->fea.value ? 1 :
0;
}
- bool operator== (const hb_ms_active_feature_t *f)
- { return cmp (this, f) == 0; }
+ bool operator== (const hb_ms_active_feature_t& f) const
+ { return cmp (this, &f) == 0; }
};
struct hb_ms_feature_event_t {
@@ -77,20 +77,153 @@ struct hb_ms_range_record_t {
unsigned int index_last; /* == end - 1 */
};
-HB_INTERNAL bool
+static inline bool
hb_ms_setup_features (const hb_feature_t *features,
unsigned int num_features,
hb_vector_t<hb_ms_feature_t> &feature_records, /* OUT */
- hb_vector_t<hb_ms_range_record_t> &range_records /* OUT */);
+ hb_vector_t<hb_ms_range_record_t> &range_records /* OUT */)
+{
+ feature_records.shrink(0);
+ range_records.shrink(0);
+ /* Sort features by start/end events. */
+ hb_vector_t<hb_ms_feature_event_t> feature_events;
+ for (unsigned int i = 0; i < num_features; i++)
+ {
+ hb_ms_active_feature_t feature;
+ feature.fea.tag_le = hb_uint32_swap (features[i].tag);
+ feature.fea.value = features[i].value;
+ feature.order = i;
+
+ hb_ms_feature_event_t *event;
+
+ event = feature_events.push ();
+ event->index = features[i].start;
+ event->start = true;
+ event->feature = feature;
+
+ event = feature_events.push ();
+ event->index = features[i].end;
+ event->start = false;
+ event->feature = feature;
+ }
+ feature_events.qsort ();
+ /* Add a strategic final event. */
+ {
+ hb_ms_active_feature_t feature;
+ feature.fea.tag_le = 0;
+ feature.fea.value = 0;
+ feature.order = num_features + 1;
+
+ auto *event = feature_events.push ();
+ event->index = 0; /* This value does magic. */
+ event->start = false;
+ event->feature = feature;
+ }
+
+ /* Scan events and save features for each range. */
+ hb_vector_t<hb_ms_active_feature_t> active_features;
+ unsigned int last_index = 0;
+ for (unsigned int i = 0; i < feature_events.length; i++)
+ {
+ auto *event = &feature_events[i];
+
+ if (event->index != last_index)
+ {
+ /* Save a snapshot of active features and the range. */
+ auto *range = range_records.push ();
+ auto offset = feature_records.length;
+
+ active_features.qsort ();
+ for (unsigned int j = 0; j < active_features.length; j++)
+ {
+ if (!j || active_features[j].fea.tag_le != feature_records[feature_records.length - 1].tag_le)
+ {
+ feature_records.push (active_features[j].fea);
+ }
+ else
+ {
+ /* Overrides value for existing feature. */
+ feature_records[feature_records.length - 1].value = active_features[j].fea.value;
+ }
+ }
+
+ /* Will convert to pointer after all is ready, since feature_records.array
+ * may move as we grow it. */
+ range->features.features = reinterpret_cast<hb_ms_feature_t *> (offset);
+ range->features.num_features = feature_records.length - offset;
+ range->index_first = last_index;
+ range->index_last = event->index - 1;
-HB_INTERNAL void
+ last_index = event->index;
+ }
+
+ if (event->start)
+ {
+ active_features.push (event->feature);
+ }
+ else
+ {
+ auto *feature = active_features.lsearch (event->feature);
+ if (feature)
+ active_features.remove (feature - active_features.arrayZ);
+ }
+ }
+
+ if (!range_records.length) /* No active feature found. */
+ num_features = 0;
+
+ /* Fixup the pointers. */
+ for (unsigned int i = 0; i < range_records.length; i++)
+ {
+ auto *range = &range_records[i];
+ range->features.features = (hb_ms_feature_t *) feature_records + reinterpret_cast<uintptr_t> (range->features.features);
+ }
+
+ return !!num_features;
+}
+
+static inline void
hb_ms_make_feature_ranges (hb_vector_t<hb_ms_feature_t> &feature_records,
hb_vector_t<hb_ms_range_record_t> &range_records,
unsigned int chars_offset,
unsigned int chars_len,
uint16_t *log_clusters,
hb_vector_t<hb_ms_features_t*> &range_features, /* OUT */
- hb_vector_t<uint32_t> &range_counts /* OUT */);
+ hb_vector_t<uint32_t> &range_counts /* OUT */)
+{
+ range_features.shrink (0);
+ range_counts.shrink (0);
+
+ auto *last_range = &range_records[0];
+ for (unsigned int i = chars_offset; i < chars_len; i++)
+ {
+ auto *range = last_range;
+ while (log_clusters[i] < range->index_first)
+ range--;
+ while (log_clusters[i] > range->index_last)
+ range++;
+ if (!range_features.length ||
+ &range->features != range_features[range_features.length - 1])
+ {
+ auto **features = range_features.push ();
+ auto *c = range_counts.push ();
+ if (unlikely (!features || !c))
+ {
+ range_features.shrink (0);
+ range_counts.shrink (0);
+ break;
+ }
+ *features = &range->features;
+ *c = 1;
+ }
+ else
+ {
+ range_counts[range_counts.length - 1]++;
+ }
+
+ last_range = range;
+ }
+}
#endif /* HB_MS_FEATURE_RANGES_HH */
diff --git a/thirdparty/harfbuzz/src/hb-object.hh b/thirdparty/harfbuzz/src/hb-object.hh
index 0e15cb12c4..4b5bc32ade 100644
--- a/thirdparty/harfbuzz/src/hb-object.hh
+++ b/thirdparty/harfbuzz/src/hb-object.hh
@@ -53,7 +53,7 @@ struct hb_lockable_set_t
item_t *replace_or_insert (T v, lock_t &l, bool replace)
{
l.lock ();
- item_t *item = items.find (v);
+ item_t *item = items.lsearch (v);
if (item) {
if (replace) {
item_t old = *item;
@@ -76,7 +76,7 @@ struct hb_lockable_set_t
void remove (T v, lock_t &l)
{
l.lock ();
- item_t *item = items.find (v);
+ item_t *item = items.lsearch (v);
if (item)
{
item_t old = *item;
@@ -93,7 +93,7 @@ struct hb_lockable_set_t
bool find (T v, item_t *i, lock_t &l)
{
l.lock ();
- item_t *item = items.find (v);
+ item_t *item = items.lsearch (v);
if (item)
*i = *item;
l.unlock ();
diff --git a/thirdparty/harfbuzz/src/hb-ot-cff-common.hh b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh
index 180c87cb89..c102c15173 100644
--- a/thirdparty/harfbuzz/src/hb-ot-cff-common.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh
@@ -68,8 +68,6 @@ struct code_pair_t
typedef hb_vector_t<unsigned char> str_buff_t;
struct str_buff_vec_t : hb_vector_t<str_buff_t>
{
- void fini () { SUPER::fini_deep (); }
-
unsigned int total_size () const
{
unsigned int size = 0;
diff --git a/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh
index 5dd183e3a0..6fb59315c9 100644
--- a/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh
@@ -1144,8 +1144,8 @@ struct cff1
{
sc.end_processing ();
topDict.fini ();
- fontDicts.fini_deep ();
- privateDicts.fini_deep ();
+ fontDicts.fini ();
+ privateDicts.fini ();
hb_blob_destroy (blob);
blob = nullptr;
}
@@ -1245,32 +1245,32 @@ struct cff1
}
protected:
- hb_blob_t *blob;
+ hb_blob_t *blob = nullptr;
hb_sanitize_context_t sc;
public:
- const Encoding *encoding;
- const Charset *charset;
- const CFF1NameIndex *nameIndex;
- const CFF1TopDictIndex *topDictIndex;
- const CFF1StringIndex *stringIndex;
- const CFF1Subrs *globalSubrs;
- const CFF1CharStrings *charStrings;
- const CFF1FDArray *fdArray;
- const CFF1FDSelect *fdSelect;
- unsigned int fdCount;
+ const Encoding *encoding = nullptr;
+ const Charset *charset = nullptr;
+ const CFF1NameIndex *nameIndex = nullptr;
+ const CFF1TopDictIndex *topDictIndex = nullptr;
+ const CFF1StringIndex *stringIndex = nullptr;
+ const CFF1Subrs *globalSubrs = nullptr;
+ const CFF1CharStrings *charStrings = nullptr;
+ const CFF1FDArray *fdArray = nullptr;
+ const CFF1FDSelect *fdSelect = nullptr;
+ unsigned int fdCount = 0;
cff1_top_dict_values_t topDict;
hb_vector_t<cff1_font_dict_values_t>
fontDicts;
hb_vector_t<PRIVDICTVAL> privateDicts;
- unsigned int num_glyphs;
+ unsigned int num_glyphs = 0;
};
struct accelerator_t : accelerator_templ_t<cff1_private_dict_opset_t, cff1_private_dict_values_t>
{
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{
SUPER::init (face);
@@ -1295,8 +1295,7 @@ struct cff1
}
glyph_names.qsort ();
}
-
- void fini ()
+ ~accelerator_t ()
{
glyph_names.fini ();
@@ -1398,7 +1397,10 @@ struct cff1
DEFINE_SIZE_STATIC (4);
};
-struct cff1_accelerator_t : cff1::accelerator_t {};
+struct cff1_accelerator_t : cff1::accelerator_t {
+ cff1_accelerator_t (hb_face_t *face) : cff1::accelerator_t (face) {}
+};
+
} /* namespace OT */
#endif /* HB_OT_CFF1_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh
index 829217feaa..6e1b01c8fe 100644
--- a/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh
@@ -397,7 +397,7 @@ struct cff2
template <typename PRIVOPSET, typename PRIVDICTVAL>
struct accelerator_templ_t
{
- void init (hb_face_t *face)
+ accelerator_templ_t (hb_face_t *face)
{
topDict.init ();
fontDicts.init ();
@@ -412,15 +412,15 @@ struct cff2
const OT::cff2 *cff2 = this->blob->template as<OT::cff2> ();
if (cff2 == &Null (OT::cff2))
- { fini (); return; }
+ goto fail;
{ /* parse top dict */
byte_str_t topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
- if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!topDictStr.sanitize (&sc))) goto fail;
cff2_top_dict_interpreter_t top_interp;
top_interp.env.init (topDictStr);
topDict.init ();
- if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
+ if (unlikely (!top_interp.interpret (topDict))) goto fail;
}
globalSubrs = &StructAtOffset<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize);
@@ -434,49 +434,55 @@ struct cff2
(globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) ||
(fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) ||
(((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count)))))
- { fini (); return; }
+ goto fail;
num_glyphs = charStrings->count;
if (num_glyphs != sc.get_num_glyphs ())
- { fini (); return; }
+ goto fail;
fdCount = fdArray->count;
if (!privateDicts.resize (fdCount))
- { fini (); return; }
+ goto fail;
/* parse font dicts and gather private dicts */
for (unsigned int i = 0; i < fdCount; i++)
{
const byte_str_t fontDictStr = (*fdArray)[i];
- if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!fontDictStr.sanitize (&sc))) goto fail;
cff2_font_dict_values_t *font;
cff2_font_dict_interpreter_t font_interp;
font_interp.env.init (fontDictStr);
font = fontDicts.push ();
- if (unlikely (font == &Crap (cff2_font_dict_values_t))) { fini (); return; }
+ if (unlikely (font == &Crap (cff2_font_dict_values_t))) goto fail;
font->init ();
- if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
+ if (unlikely (!font_interp.interpret (*font))) goto fail;
const byte_str_t privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
- if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!privDictStr.sanitize (&sc))) goto fail;
dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t> priv_interp;
priv_interp.env.init(privDictStr);
privateDicts[i].init ();
- if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; }
+ if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail;
privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (&privDictStr[0], privateDicts[i].subrsOffset);
if (privateDicts[i].localSubrs != &Null (CFF2Subrs) &&
unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
- { fini (); return; }
+ goto fail;
}
- }
- void fini ()
+
+ return;
+
+ fail:
+ _fini ();
+ }
+ ~accelerator_templ_t () { _fini (); }
+ void _fini ()
{
sc.end_processing ();
topDict.fini ();
- fontDicts.fini_deep ();
- privateDicts.fini_deep ();
+ fontDicts.fini ();
+ privateDicts.fini ();
hb_blob_destroy (blob);
blob = nullptr;
}
@@ -484,26 +490,28 @@ struct cff2
bool is_valid () const { return blob; }
protected:
- hb_blob_t *blob;
+ hb_blob_t *blob = nullptr;
hb_sanitize_context_t sc;
public:
cff2_top_dict_values_t topDict;
- const CFF2Subrs *globalSubrs;
- const CFF2VariationStore *varStore;
- const CFF2CharStrings *charStrings;
- const CFF2FDArray *fdArray;
- const CFF2FDSelect *fdSelect;
- unsigned int fdCount;
+ const CFF2Subrs *globalSubrs = nullptr;
+ const CFF2VariationStore *varStore = nullptr;
+ const CFF2CharStrings *charStrings = nullptr;
+ const CFF2FDArray *fdArray = nullptr;
+ const CFF2FDSelect *fdSelect = nullptr;
+ unsigned int fdCount = 0;
hb_vector_t<cff2_font_dict_values_t> fontDicts;
hb_vector_t<PRIVDICTVAL> privateDicts;
- unsigned int num_glyphs;
+ unsigned int num_glyphs = 0;
};
struct accelerator_t : accelerator_templ_t<cff2_private_dict_opset_t, cff2_private_dict_values_t>
{
+ accelerator_t (hb_face_t *face) : accelerator_templ_t (face) {}
+
HB_INTERNAL bool get_extents (hb_font_t *font,
hb_codepoint_t glyph,
hb_glyph_extents_t *extents) const;
@@ -525,7 +533,10 @@ struct cff2
DEFINE_SIZE_STATIC (5);
};
-struct cff2_accelerator_t : cff2::accelerator_t {};
+struct cff2_accelerator_t : cff2::accelerator_t {
+ cff2_accelerator_t (hb_face_t *face) : cff2::accelerator_t (face) {}
+};
+
} /* namespace OT */
#endif /* HB_OT_CFF2_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh
index d837adc788..fde57cdc5b 100644
--- a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh
@@ -369,7 +369,6 @@ struct CmapSubtableFormat4
{
accelerator_t () {}
accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); }
- ~accelerator_t () { fini (); }
void init (const CmapSubtableFormat4 *subtable)
{
@@ -381,7 +380,6 @@ struct CmapSubtableFormat4
glyphIdArray = idRangeOffset + segCount;
glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2;
}
- void fini () {}
bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
{
@@ -1607,7 +1605,7 @@ struct cmap
unsigned format = (this + _.subtable).u.format;
if (format == 12) has_format12 = true;
- const EncodingRecord *table = hb_addressof (_);
+ const EncodingRecord *table = std::addressof (_);
if (_.platformID == 0 && _.encodingID == 3) unicode_bmp = table;
else if (_.platformID == 0 && _.encodingID == 4) unicode_ucs4 = table;
else if (_.platformID == 3 && _.encodingID == 1) ms_bmp = table;
@@ -1665,7 +1663,7 @@ struct cmap
struct accelerator_t
{
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{
this->table = hb_sanitize_context_t ().reference_table<cmap> (face);
bool symbol;
@@ -1700,8 +1698,7 @@ struct cmap
}
}
}
-
- void fini () { this->table.destroy (); }
+ ~accelerator_t () { this->table.destroy (); }
bool get_nominal_glyph (hb_codepoint_t unicode,
hb_codepoint_t *glyph) const
@@ -1863,7 +1860,9 @@ struct cmap
DEFINE_SIZE_ARRAY (4, encodingRecord);
};
-struct cmap_accelerator_t : cmap::accelerator_t {};
+struct cmap_accelerator_t : cmap::accelerator_t {
+ cmap_accelerator_t (hb_face_t *face) : cmap::accelerator_t (face) {}
+};
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh
index 14459914ee..23fa56c4f6 100644
--- a/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh
@@ -360,6 +360,16 @@ struct IndexSubtable
struct IndexSubtableRecord
{
+ /* XXX Remove this and fix by not inserting it into vector. */
+ IndexSubtableRecord& operator = (const IndexSubtableRecord &o)
+ {
+ firstGlyphIndex = o.firstGlyphIndex;
+ lastGlyphIndex = o.lastGlyphIndex;
+ offsetToSubtable = (unsigned) o.offsetToSubtable;
+ assert (offsetToSubtable.is_null ());
+ return *this;
+ }
+
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
@@ -809,15 +819,14 @@ struct CBDT
struct accelerator_t
{
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{
- cblc = hb_sanitize_context_t ().reference_table<CBLC> (face);
- cbdt = hb_sanitize_context_t ().reference_table<CBDT> (face);
+ this->cblc = hb_sanitize_context_t ().reference_table<CBLC> (face);
+ this->cbdt = hb_sanitize_context_t ().reference_table<CBDT> (face);
upem = hb_face_get_upem (face);
}
-
- void fini ()
+ ~accelerator_t ()
{
this->cblc.destroy ();
this->cbdt.destroy ();
@@ -978,7 +987,10 @@ CBLC::subset (hb_subset_context_t *c) const
return_trace (CBLC::sink_cbdt (c, &cbdt_prime));
}
-struct CBDT_accelerator_t : CBDT::accelerator_t {};
+struct CBDT_accelerator_t : CBDT::accelerator_t {
+ CBDT_accelerator_t (hb_face_t *face) : CBDT::accelerator_t (face) {}
+};
+
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh
index 008422d089..dac755c02c 100644
--- a/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh
@@ -71,7 +71,7 @@ struct hb_colrv1_closure_context_t :
bool paint_visited (const void *paint)
{
hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) paint - (uintptr_t) base);
- if (visited_paint.has (delta))
+ if (visited_paint.in_error() || visited_paint.has (delta))
return true;
visited_paint.add (delta);
@@ -1270,13 +1270,9 @@ struct COLR
struct accelerator_t
{
- accelerator_t () {}
- ~accelerator_t () { fini (); }
-
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{ colr = hb_sanitize_context_t ().reference_table<COLR> (face); }
-
- void fini () { this->colr.destroy (); }
+ ~accelerator_t () { this->colr.destroy (); }
bool is_valid () { return colr.get_blob ()->length; }
@@ -1535,6 +1531,10 @@ struct COLR
DEFINE_SIZE_MIN (14);
};
+struct COLR_accelerator_t : COLR::accelerator_t {
+ COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {}
+};
+
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color-colrv1-closure.hh b/thirdparty/harfbuzz/src/hb-ot-color-colrv1-closure.hh
index ca85ba6ad6..fbaf2ec26b 100644
--- a/thirdparty/harfbuzz/src/hb-ot-color-colrv1-closure.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-color-colrv1-closure.hh
@@ -43,7 +43,7 @@ HB_INTERNAL void PaintColrLayers::closurev1 (hb_colrv1_closure_context_t* c) con
const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
{
- const Paint &paint = hb_addressof (paint_offset_lists) + paint_offset_lists[i];
+ const Paint &paint = std::addressof (paint_offset_lists) + paint_offset_lists[i];
paint.dispatch (c);
}
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh
index d2911f19e6..9741ebd450 100644
--- a/thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh
@@ -202,12 +202,12 @@ struct sbix
struct accelerator_t
{
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{
table = hb_sanitize_context_t ().reference_table<sbix> (face);
num_glyphs = face->get_num_glyphs ();
}
- void fini () { table.destroy (); }
+ ~accelerator_t () { table.destroy (); }
bool has_data () const { return table->has_data (); }
@@ -407,7 +407,10 @@ struct sbix
DEFINE_SIZE_ARRAY (8, strikes);
};
-struct sbix_accelerator_t : sbix::accelerator_t {};
+struct sbix_accelerator_t : sbix::accelerator_t {
+ sbix_accelerator_t (hb_face_t *face) : sbix::accelerator_t (face) {}
+};
+
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh
index e022ef43b7..fc649f1006 100644
--- a/thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh
@@ -79,9 +79,9 @@ struct SVG
struct accelerator_t
{
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{ table = hb_sanitize_context_t ().reference_table<SVG> (face); }
- void fini () { table.destroy (); }
+ ~accelerator_t () { table.destroy (); }
hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const
{
@@ -116,7 +116,9 @@ struct SVG
DEFINE_SIZE_STATIC (10);
};
-struct SVG_accelerator_t : SVG::accelerator_t {};
+struct SVG_accelerator_t : SVG::accelerator_t {
+ SVG_accelerator_t (hb_face_t *face) : SVG::accelerator_t (face) {}
+};
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color.cc b/thirdparty/harfbuzz/src/hb-ot-color.cc
index 4170b71317..16077765bd 100644
--- a/thirdparty/harfbuzz/src/hb-ot-color.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-color.cc
@@ -90,15 +90,15 @@ hb_ot_color_palette_get_count (hb_face_t *face)
/**
* hb_ot_color_palette_get_name_id:
* @face: #hb_face_t to work upon
- * @palette_index: The index of the color palette
+ * @palette_index: The index of the color palette
*
* Fetches the `name` table Name ID that provides display names for
- * a `CPAL` color palette.
+ * a `CPAL` color palette.
*
* Palette display names can be generic (e.g., "Default") or provide
* specific, themed names (e.g., "Spring", "Summer", "Fall", and "Winter").
*
- * Return value: the Named ID found for the palette.
+ * Return value: the Named ID found for the palette.
* If the requested palette has no name the result is #HB_OT_NAME_ID_INVALID.
*
* Since: 2.1.0
@@ -116,7 +116,7 @@ hb_ot_color_palette_get_name_id (hb_face_t *face,
* @color_index: The index of the color
*
* Fetches the `name` table Name ID that provides display names for
- * the specificed color in a face's `CPAL` color palette.
+ * the specified color in a face's `CPAL` color palette.
*
* Display names can be generic (e.g., "Background") or specific
* (e.g., "Eye color").
@@ -256,6 +256,8 @@ hb_ot_color_has_svg (hb_face_t *face)
*
* Fetches the SVG document for a glyph. The blob may be either plain text or gzip-encoded.
*
+ * If the glyph has no SVG document, the singleton empty blob is returned.
+ *
* Return value: (transfer full): An #hb_blob_t containing the SVG document of the glyph, if available
*
* Since: 2.1.0
@@ -296,6 +298,8 @@ hb_ot_color_has_png (hb_face_t *face)
* as input. To get an optimally sized PNG blob, the UPEM value must be set on the @font
* object. If UPEM is unset, the blob returned will be the largest PNG available.
*
+ * If the glyph has no PNG image, the singleton empty blob is returned.
+ *
* Return value: (transfer full): An #hb_blob_t containing the PNG image for the glyph, if available
*
* Since: 2.1.0
diff --git a/thirdparty/harfbuzz/src/hb-ot-glyf-table.hh b/thirdparty/harfbuzz/src/hb-ot-glyf-table.hh
index 6aa34295c7..9bac30fff3 100644
--- a/thirdparty/harfbuzz/src/hb-ot-glyf-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-glyf-table.hh
@@ -207,8 +207,7 @@ struct glyf
_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_vector_t<SubsetGlyph> *glyphs /* OUT */) const
{
- OT::glyf::accelerator_t glyf;
- glyf.init (plan->source);
+ OT::glyf::accelerator_t glyf (plan->source);
+ hb_range (plan->num_output_glyphs ())
| hb_map ([&] (hb_codepoint_t new_gid)
@@ -233,8 +232,6 @@ struct glyf
})
| hb_sink (glyphs)
;
-
- glyf.fini ();
}
static bool
@@ -595,7 +592,7 @@ struct glyf
if (unlikely (!header.numberOfContours)) return;
unsigned flags_offset = length (instructions_length ());
- if (unlikely (length (flags_offset + 1) > bytes.length)) return;
+ if (unlikely (flags_offset + 1 > bytes.length)) return;
HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset<HBUINT16> (&bytes, flags_offset);
first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE;
@@ -920,7 +917,7 @@ struct glyf
struct accelerator_t
{
- void init (hb_face_t *face_)
+ accelerator_t (hb_face_t *face_)
{
short_offset = false;
num_glyphs = 0;
@@ -953,8 +950,7 @@ struct glyf
num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ());
}
-
- void fini ()
+ ~accelerator_t ()
{
loca_table.destroy ();
glyf_table.destroy ();
@@ -1358,7 +1354,10 @@ struct glyf
* defining it _MIN instead. */
};
-struct glyf_accelerator_t : glyf::accelerator_t {};
+struct glyf_accelerator_t : glyf::accelerator_t {
+ glyf_accelerator_t (hb_face_t *face) : glyf::accelerator_t (face) {}
+};
+
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh b/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh
index 7d2d2d3eb8..36bffa70a5 100644
--- a/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh
@@ -127,8 +127,7 @@ struct hmtxvmtx
T *table_prime = c->serializer->start_embed <T> ();
if (unlikely (!table_prime)) return_trace (false);
- accelerator_t _mtx;
- _mtx.init (c->plan->source);
+ accelerator_t _mtx (c->plan->source);
unsigned num_advances = _mtx.num_advances_for_subset (c->plan);
auto it =
@@ -144,8 +143,6 @@ struct hmtxvmtx
table_prime->serialize (c->serializer, it, num_advances);
- _mtx.fini ();
-
if (unlikely (c->serializer->in_error ()))
return_trace (false);
@@ -160,8 +157,8 @@ struct hmtxvmtx
{
friend struct hmtxvmtx;
- void init (hb_face_t *face,
- unsigned int default_advance_ = 0)
+ accelerator_t (hb_face_t *face,
+ unsigned int default_advance_ = 0)
{
default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
@@ -193,8 +190,7 @@ struct hmtxvmtx
var_table = hb_sanitize_context_t ().reference_table<HVARVVAR> (face, T::variationsTag);
}
-
- void fini ()
+ ~accelerator_t ()
{
table.destroy ();
var_table.destroy ();
@@ -338,8 +334,12 @@ struct vmtx : hmtxvmtx<vmtx, vhea> {
static constexpr bool is_horizontal = false;
};
-struct hmtx_accelerator_t : hmtx::accelerator_t {};
-struct vmtx_accelerator_t : vmtx::accelerator_t {};
+struct hmtx_accelerator_t : hmtx::accelerator_t {
+ hmtx_accelerator_t (hb_face_t *face) : hmtx::accelerator_t (face) {}
+};
+struct vmtx_accelerator_t : vmtx::accelerator_t {
+ vmtx_accelerator_t (hb_face_t *face) : vmtx::accelerator_t (face) {}
+};
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh
index 4fb1893435..60a1906155 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh
@@ -128,7 +128,7 @@ struct hb_prune_langsys_context_t
bool visited (const T *p, hb_set_t &visited_set)
{
hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) p - (uintptr_t) table);
- if (visited_set.has (delta))
+ if (visited_set.in_error () || visited_set.has (delta))
return true;
visited_set.add (delta);
@@ -655,7 +655,6 @@ struct LangSys
void collect_features (hb_prune_langsys_context_t *c) const
{
if (!has_required_feature () && !get_feature_count ()) return;
- if (c->visitedLangsys (this)) return;
if (has_required_feature () &&
c->duplicate_feature_map->has (reqFeatureIndex))
c->new_feature_indexes->add (get_required_feature_index ());
@@ -750,11 +749,15 @@ struct Script
{
//only collect features from non-redundant langsys
const LangSys& d = get_default_lang_sys ();
- d.collect_features (c);
+ if (!c->visitedLangsys (&d)) {
+ d.collect_features (c);
+ }
for (auto _ : + hb_zip (langSys, hb_range (langsys_count)))
{
+
const LangSys& l = this+_.first.offset;
+ if (c->visitedLangsys (&l)) continue;
if (l.compare (d, c->duplicate_feature_map)) continue;
l.collect_features (c);
@@ -766,6 +769,7 @@ struct Script
for (auto _ : + hb_zip (langSys, hb_range (langsys_count)))
{
const LangSys& l = this+_.first.offset;
+ if (c->visitedLangsys (&l)) continue;
l.collect_features (c);
c->script_langsys_map->get (script_index)->add (_.second);
}
@@ -845,7 +849,7 @@ struct FeatureParamsSize
if (unlikely (!c->check_struct (this))) return_trace (false);
/* This subtable has some "history", if you will. Some earlier versions of
- * Adobe tools calculated the offset of the FeatureParams sutable from the
+ * Adobe tools calculated the offset of the FeatureParams subtable from the
* beginning of the FeatureList table! Now, that is dealt with in the
* Feature implementation. But we still need to be able to tell junk from
* real data. Note: We don't check that the nameID actually exists.
@@ -2926,8 +2930,6 @@ struct VariationStore
hb_vector_t<hb_inc_bimap_t> inner_maps;
inner_maps.resize ((unsigned) dataSets.len);
- for (unsigned i = 0; i < inner_maps.length; i++)
- inner_maps[i].init ();
for (unsigned idx : c->plan->layout_variation_indices->iter ())
{
@@ -2935,18 +2937,11 @@ struct VariationStore
uint16_t minor = idx & 0xFFFF;
if (major >= inner_maps.length)
- {
- for (unsigned i = 0; i < inner_maps.length; i++)
- inner_maps[i].fini ();
return_trace (false);
- }
inner_maps[major].add (minor);
}
varstore_prime->serialize (c->serializer, this, inner_maps.as_array ());
- for (unsigned i = 0; i < inner_maps.length; i++)
- inner_maps[i].fini ();
-
return_trace (
!c->serializer->in_error()
&& varstore_prime->dataSets);
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh
index aea644f3e1..a76d644c4b 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh
@@ -585,17 +585,16 @@ struct GDEF
struct accelerator_t
{
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{
- this->table = hb_sanitize_context_t ().reference_table<GDEF> (face);
- if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face)))
+ table = hb_sanitize_context_t ().reference_table<GDEF> (face);
+ if (unlikely (table->is_blocklisted (table.get_blob (), face)))
{
- hb_blob_destroy (this->table.get_blob ());
- this->table = hb_blob_get_empty ();
+ hb_blob_destroy (table.get_blob ());
+ table = hb_blob_get_empty ();
}
}
-
- void fini () { this->table.destroy (); }
+ ~accelerator_t () { table.destroy (); }
hb_blob_ptr_t<GDEF> table;
};
@@ -715,7 +714,9 @@ struct GDEF
DEFINE_SIZE_MIN (12);
};
-struct GDEF_accelerator_t : GDEF::accelerator_t {};
+struct GDEF_accelerator_t : GDEF::accelerator_t {
+ GDEF_accelerator_t (hb_face_t *face) : GDEF::accelerator_t (face) {}
+};
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gpos-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gpos-table.hh
index 6db3e08940..e28c951f3f 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -706,7 +706,7 @@ struct MarkArray : Array16Of<MarkRecord> /* Array of MarkRecords--in Coverage or
float mark_x, mark_y, base_x, base_y;
- buffer->unsafe_to_break (glyph_pos, buffer->idx);
+ buffer->unsafe_to_break (glyph_pos, buffer->idx + 1);
mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
@@ -1235,6 +1235,7 @@ struct PairSet
buffer->idx = pos;
return_trace (true);
}
+ buffer->unsafe_to_concat (buffer->idx, pos + 1);
return_trace (false);
}
@@ -1362,7 +1363,12 @@ struct PairPosFormat1
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
- if (!skippy_iter.next ()) return_trace (false);
+ unsigned unsafe_to;
+ if (!skippy_iter.next (&unsafe_to))
+ {
+ buffer->unsafe_to_concat (buffer->idx, unsafe_to);
+ return_trace (false);
+ }
return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
}
@@ -1555,7 +1561,12 @@ struct PairPosFormat2
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
- if (!skippy_iter.next ()) return_trace (false);
+ unsigned unsafe_to;
+ if (!skippy_iter.next (&unsafe_to))
+ {
+ buffer->unsafe_to_concat (buffer->idx, unsafe_to);
+ return_trace (false);
+ }
unsigned int len1 = valueFormat1.get_len ();
unsigned int len2 = valueFormat2.get_len ();
@@ -1563,13 +1574,81 @@ struct PairPosFormat2
unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
- if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
+ if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
+ {
+ buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
+ return_trace (false);
+ }
const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
- bool applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos());
- bool applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
+
+ bool applied_first = false, applied_second = false;
+
+
+ /* Isolate simple kerning and apply it half to each side.
+ * Results in better cursor positinoing / underline drawing. */
+ {
+ if (!len2)
+ {
+ const hb_direction_t dir = buffer->props.direction;
+ const bool horizontal = HB_DIRECTION_IS_HORIZONTAL (dir);
+ const bool backward = HB_DIRECTION_IS_BACKWARD (dir);
+ unsigned mask = horizontal ? ValueFormat::xAdvance : ValueFormat::yAdvance;
+ if (backward)
+ mask |= mask >> 2; /* Add eg. xPlacement in RTL. */
+ /* Add Devices. */
+ mask |= mask << 4;
+
+ if (valueFormat1 & ~mask)
+ goto bail;
+
+ /* Is simple kern. Apply value on an empty position slot,
+ * then split it between sides. */
+
+ hb_glyph_position_t pos{};
+ if (valueFormat1.apply_value (c, this, v, pos))
+ {
+ hb_position_t *src = &pos.x_advance;
+ hb_position_t *dst1 = &buffer->cur_pos().x_advance;
+ hb_position_t *dst2 = &buffer->pos[skippy_iter.idx].x_advance;
+ unsigned i = horizontal ? 0 : 1;
+
+ hb_position_t kern = src[i];
+ hb_position_t kern1 = kern >> 1;
+ hb_position_t kern2 = kern - kern1;
+
+ if (!backward)
+ {
+ dst1[i] += kern1;
+ dst2[i] += kern2;
+ dst2[i + 2] += kern2;
+ }
+ else
+ {
+ dst1[i] += kern1;
+ dst1[i + 2] += src[i + 2] - kern2;
+ dst2[i] += kern2;
+ }
+
+ applied_first = applied_second = kern != 0;
+ goto success;
+ }
+ goto boring;
+ }
+ }
+ bail:
+
+
+ applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos());
+ applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
+
+ success:
if (applied_first || applied_second)
buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
+ else
+ boring:
+ buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
+
buffer->idx = skippy_iter.idx;
if (len2)
@@ -1799,10 +1878,19 @@ struct CursivePosFormat1
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
- if (!skippy_iter.prev ()) return_trace (false);
+ unsigned unsafe_from;
+ if (!skippy_iter.prev (&unsafe_from))
+ {
+ buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
+ return_trace (false);
+ }
const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)];
- if (!prev_record.exitAnchor) return_trace (false);
+ if (!prev_record.exitAnchor)
+ {
+ buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+ return_trace (false);
+ }
unsigned int i = skippy_iter.idx;
unsigned int j = buffer->idx;
@@ -2066,7 +2154,13 @@ struct MarkBasePosFormat1
skippy_iter.reset (buffer->idx, 1);
skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
do {
- if (!skippy_iter.prev ()) return_trace (false);
+ unsigned unsafe_from;
+ if (!skippy_iter.prev (&unsafe_from))
+ {
+ buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
+ return_trace (false);
+ }
+
/* We only want to attach to the first of a MultipleSubst sequence.
* https://github.com/harfbuzz/harfbuzz/issues/740
* Reject others...
@@ -2089,7 +2183,11 @@ struct MarkBasePosFormat1
//if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint);
- if (base_index == NOT_COVERED) return_trace (false);
+ if (base_index == NOT_COVERED)
+ {
+ buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+ return_trace (false);
+ }
return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
}
@@ -2320,21 +2418,34 @@ struct MarkLigPosFormat1
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
- if (!skippy_iter.prev ()) return_trace (false);
+ unsigned unsafe_from;
+ if (!skippy_iter.prev (&unsafe_from))
+ {
+ buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
+ return_trace (false);
+ }
/* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
//if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); }
unsigned int j = skippy_iter.idx;
unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint);
- if (lig_index == NOT_COVERED) return_trace (false);
+ if (lig_index == NOT_COVERED)
+ {
+ buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+ return_trace (false);
+ }
const LigatureArray& lig_array = this+ligatureArray;
const LigatureAttach& lig_attach = lig_array[lig_index];
/* Find component to attach to */
unsigned int comp_count = lig_attach.rows;
- if (unlikely (!comp_count)) return_trace (false);
+ if (unlikely (!comp_count))
+ {
+ buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+ return_trace (false);
+ }
/* We must now check whether the ligature ID of the current mark glyph
* is identical to the ligature ID of the found ligature. If yes, we
@@ -2517,9 +2628,18 @@ struct MarkMarkPosFormat1
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
- if (!skippy_iter.prev ()) return_trace (false);
+ unsigned unsafe_from;
+ if (!skippy_iter.prev (&unsafe_from))
+ {
+ buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
+ return_trace (false);
+ }
- if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) { return_trace (false); }
+ if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx]))
+ {
+ buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+ return_trace (false);
+ }
unsigned int j = skippy_iter.idx;
@@ -2544,11 +2664,16 @@ struct MarkMarkPosFormat1
}
/* Didn't match. */
+ buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
return_trace (false);
good:
unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint);
- if (mark2_index == NOT_COVERED) return_trace (false);
+ if (mark2_index == NOT_COVERED)
+ {
+ buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+ return_trace (false);
+ }
return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
}
@@ -2951,7 +3076,7 @@ GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer H
}
void
-GPOS::position_finish_offsets (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
+GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
{
_hb_buffer_assert_gsubgpos_vars (buffer);
@@ -2961,12 +3086,21 @@ GPOS::position_finish_offsets (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
/* Handle attachments */
if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT)
- for (unsigned int i = 0; i < len; i++)
+ for (unsigned i = 0; i < len; i++)
propagate_attachment_offsets (pos, len, i, direction);
+
+ if (unlikely (font->slant))
+ {
+ for (unsigned i = 0; i < len; i++)
+ if (unlikely (pos[i].y_offset))
+ pos[i].x_offset += _hb_roundf (font->slant_xy * pos[i].y_offset);
+ }
}
-struct GPOS_accelerator_t : GPOS::accelerator_t {};
+struct GPOS_accelerator_t : GPOS::accelerator_t {
+ GPOS_accelerator_t (hb_face_t *face) : GPOS::accelerator_t (face) {}
+};
/* Out-of-class implementation for methods recursing */
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gsub-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gsub-table.hh
index b7ce30135e..0b0bc547bd 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -826,22 +826,25 @@ struct Ligature
unsigned int total_component_count = 0;
- unsigned int match_length = 0;
+ unsigned int match_end = 0;
unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
if (likely (!match_input (c, count,
&component[1],
match_glyph,
nullptr,
- &match_length,
+ &match_end,
match_positions,
&total_component_count)))
+ {
+ c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
return_trace (false);
+ }
ligate_input (c,
count,
match_positions,
- match_length,
+ match_end,
ligGlyph,
total_component_count);
@@ -1296,7 +1299,7 @@ struct ReverseChainSingleSubstFormat1
match_lookahead (c,
lookahead.len, (HBUINT16 *) lookahead.arrayZ,
match_coverage, this,
- 1, &end_index))
+ c->buffer->idx + 1, &end_index))
{
c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
c->replace_glyph_inplace (substitute[index]);
@@ -1305,8 +1308,11 @@ struct ReverseChainSingleSubstFormat1
* calls us through a Context lookup. */
return_trace (true);
}
-
- return_trace (false);
+ else
+ {
+ c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index);
+ return_trace (false);
+ }
}
template<typename Iterator,
@@ -1739,7 +1745,9 @@ struct GSUB : GSUBGPOS
};
-struct GSUB_accelerator_t : GSUB::accelerator_t {};
+struct GSUB_accelerator_t : GSUB::accelerator_t {
+ GSUB_accelerator_t (hb_face_t *face) : GSUB::accelerator_t (face) {}
+};
/* Out-of-class implementation for methods recursing */
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh
index 191d3bebc5..65de131f85 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh
@@ -125,24 +125,31 @@ struct hb_closure_context_t :
hb_set_t *covered_glyph_set = done_lookups_glyph_set->get (lookup_index);
if (unlikely (covered_glyph_set->in_error ()))
return true;
- if (parent_active_glyphs ()->is_subset (*covered_glyph_set))
+ if (parent_active_glyphs ().is_subset (*covered_glyph_set))
return true;
- hb_set_union (covered_glyph_set, parent_active_glyphs ());
+ covered_glyph_set->union_ (parent_active_glyphs ());
return false;
}
- hb_set_t* parent_active_glyphs ()
+ const hb_set_t& previous_parent_active_glyphs () {
+ if (active_glyphs_stack.length <= 1)
+ return *glyphs;
+
+ return active_glyphs_stack[active_glyphs_stack.length - 2];
+ }
+
+ const hb_set_t& parent_active_glyphs ()
{
- if (active_glyphs_stack.length < 1)
- return glyphs;
+ if (!active_glyphs_stack)
+ return *glyphs;
return active_glyphs_stack.tail ();
}
- void push_cur_active_glyphs (hb_set_t* cur_active_glyph_set)
+ hb_set_t& push_cur_active_glyphs ()
{
- active_glyphs_stack.push (cur_active_glyph_set);
+ return *active_glyphs_stack.push ();
}
bool pop_cur_done_glyphs ()
@@ -156,29 +163,24 @@ struct hb_closure_context_t :
hb_face_t *face;
hb_set_t *glyphs;
- hb_set_t *cur_intersected_glyphs;
hb_set_t output[1];
- hb_vector_t<hb_set_t *> active_glyphs_stack;
+ hb_vector_t<hb_set_t> active_glyphs_stack;
recurse_func_t recurse_func;
unsigned int nesting_level_left;
hb_closure_context_t (hb_face_t *face_,
hb_set_t *glyphs_,
- hb_set_t *cur_intersected_glyphs_,
hb_map_t *done_lookups_glyph_count_,
hb_hashmap_t<unsigned, hb_set_t *> *done_lookups_glyph_set_,
unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
face (face_),
glyphs (glyphs_),
- cur_intersected_glyphs (cur_intersected_glyphs_),
recurse_func (nullptr),
nesting_level_left (nesting_level_left_),
done_lookups_glyph_count (done_lookups_glyph_count_),
done_lookups_glyph_set (done_lookups_glyph_set_),
lookup_count (0)
- {
- push_cur_active_glyphs (glyphs_);
- }
+ {}
~hb_closure_context_t () { flush (); }
@@ -186,11 +188,11 @@ struct hb_closure_context_t :
void flush ()
{
- hb_set_del_range (output, face->get_num_glyphs (), HB_SET_VALUE_INVALID); /* Remove invalid glyphs. */
- hb_set_union (glyphs, output);
- hb_set_clear (output);
+ output->del_range (face->get_num_glyphs (), HB_SET_VALUE_INVALID); /* Remove invalid glyphs. */
+ glyphs->union_ (*output);
+ output->clear ();
active_glyphs_stack.pop ();
- active_glyphs_stack.fini ();
+ active_glyphs_stack.reset ();
}
private:
@@ -520,7 +522,7 @@ struct hb_ot_apply_context_t :
may_skip (const hb_glyph_info_t &info) const
{ return matcher.may_skip (c, info); }
- bool next ()
+ bool next (unsigned *unsafe_to = nullptr)
{
assert (num_items > 0);
while (idx + num_items < end)
@@ -543,11 +545,17 @@ struct hb_ot_apply_context_t :
}
if (skip == matcher_t::SKIP_NO)
+ {
+ if (unsafe_to)
+ *unsafe_to = idx + 1;
return false;
+ }
}
+ if (unsafe_to)
+ *unsafe_to = end;
return false;
}
- bool prev ()
+ bool prev (unsigned *unsafe_from = nullptr)
{
assert (num_items > 0);
while (idx > num_items - 1)
@@ -570,8 +578,14 @@ struct hb_ot_apply_context_t :
}
if (skip == matcher_t::SKIP_NO)
+ {
+ if (unsafe_from)
+ *unsafe_from = hb_max (1u, idx) - 1u;
return false;
+ }
}
+ if (unsafe_from)
+ *unsafe_from = 0;
return false;
}
@@ -712,53 +726,60 @@ struct hb_ot_apply_context_t :
return true;
}
- void _set_glyph_props (hb_codepoint_t glyph_index,
+ void _set_glyph_class (hb_codepoint_t glyph_index,
unsigned int class_guess = 0,
bool ligature = false,
bool component = false) const
{
- unsigned int add_in = _hb_glyph_info_get_glyph_props (&buffer->cur()) &
- HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
- add_in |= HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED;
+ unsigned int props = _hb_glyph_info_get_glyph_props (&buffer->cur());
+ props |= HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED;
if (ligature)
{
- add_in |= HB_OT_LAYOUT_GLYPH_PROPS_LIGATED;
+ props |= HB_OT_LAYOUT_GLYPH_PROPS_LIGATED;
/* In the only place that the MULTIPLIED bit is used, Uniscribe
* seems to only care about the "last" transformation between
* Ligature and Multiple substitutions. Ie. if you ligate, expand,
* and ligate again, it forgives the multiplication and acts as
* if only ligation happened. As such, clear MULTIPLIED bit.
*/
- add_in &= ~HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
+ props &= ~HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
}
if (component)
- add_in |= HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
+ props |= HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
if (likely (has_glyph_classes))
- _hb_glyph_info_set_glyph_props (&buffer->cur(), add_in | gdef.get_glyph_props (glyph_index));
+ {
+ props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
+ _hb_glyph_info_set_glyph_props (&buffer->cur(), props | gdef.get_glyph_props (glyph_index));
+ }
else if (class_guess)
- _hb_glyph_info_set_glyph_props (&buffer->cur(), add_in | class_guess);
+ {
+ props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
+ _hb_glyph_info_set_glyph_props (&buffer->cur(), props | class_guess);
+ }
+ else
+ _hb_glyph_info_set_glyph_props (&buffer->cur(), props);
}
void replace_glyph (hb_codepoint_t glyph_index) const
{
- _set_glyph_props (glyph_index);
+ _set_glyph_class (glyph_index);
(void) buffer->replace_glyph (glyph_index);
}
void replace_glyph_inplace (hb_codepoint_t glyph_index) const
{
- _set_glyph_props (glyph_index);
+ _set_glyph_class (glyph_index);
buffer->cur().codepoint = glyph_index;
}
void replace_glyph_with_ligature (hb_codepoint_t glyph_index,
unsigned int class_guess) const
{
- _set_glyph_props (glyph_index, class_guess, true);
+ _set_glyph_class (glyph_index, class_guess, true);
(void) buffer->replace_glyph (glyph_index);
}
void output_glyph_for_component (hb_codepoint_t glyph_index,
unsigned int class_guess) const
{
- _set_glyph_props (glyph_index, class_guess, false, true);
+ _set_glyph_class (glyph_index, class_guess, false, true);
(void) buffer->output_glyph (glyph_index);
}
};
@@ -948,7 +969,7 @@ static inline bool match_input (hb_ot_apply_context_t *c,
const HBUINT16 input[], /* Array of input values--start with second glyph */
match_func_t match_func,
const void *match_data,
- unsigned int *end_offset,
+ unsigned int *end_position,
unsigned int match_positions[HB_MAX_CONTEXT_LENGTH],
unsigned int *p_total_component_count = nullptr)
{
@@ -1001,7 +1022,12 @@ static inline bool match_input (hb_ot_apply_context_t *c,
match_positions[0] = buffer->idx;
for (unsigned int i = 1; i < count; i++)
{
- if (!skippy_iter.next ()) return_trace (false);
+ unsigned unsafe_to;
+ if (!skippy_iter.next (&unsafe_to))
+ {
+ *end_position = unsafe_to;
+ return_trace (false);
+ }
match_positions[i] = skippy_iter.idx;
@@ -1055,7 +1081,7 @@ static inline bool match_input (hb_ot_apply_context_t *c,
total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->info[skippy_iter.idx]);
}
- *end_offset = skippy_iter.idx - buffer->idx + 1;
+ *end_position = skippy_iter.idx + 1;
if (p_total_component_count)
*p_total_component_count = total_component_count;
@@ -1065,7 +1091,7 @@ static inline bool match_input (hb_ot_apply_context_t *c,
static inline bool ligate_input (hb_ot_apply_context_t *c,
unsigned int count, /* Including the first glyph */
const unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */
- unsigned int match_length,
+ unsigned int match_end,
hb_codepoint_t lig_glyph,
unsigned int total_component_count)
{
@@ -1073,7 +1099,7 @@ static inline bool ligate_input (hb_ot_apply_context_t *c,
hb_buffer_t *buffer = c->buffer;
- buffer->merge_clusters (buffer->idx, buffer->idx + match_length);
+ buffer->merge_clusters (buffer->idx, match_end);
/* - If a base and one or more marks ligate, consider that as a base, NOT
* ligature, such that all following marks can still attach to it.
@@ -1190,11 +1216,16 @@ static inline bool match_backtrack (hb_ot_apply_context_t *c,
skippy_iter.set_match_func (match_func, match_data, backtrack);
for (unsigned int i = 0; i < count; i++)
- if (!skippy_iter.prev ())
+ {
+ unsigned unsafe_from;
+ if (!skippy_iter.prev (&unsafe_from))
+ {
+ *match_start = unsafe_from;
return_trace (false);
+ }
+ }
*match_start = skippy_iter.idx;
-
return_trace (true);
}
@@ -1203,21 +1234,26 @@ static inline bool match_lookahead (hb_ot_apply_context_t *c,
const HBUINT16 lookahead[],
match_func_t match_func,
const void *match_data,
- unsigned int offset,
+ unsigned int start_index,
unsigned int *end_index)
{
TRACE_APPLY (nullptr);
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
- skippy_iter.reset (c->buffer->idx + offset - 1, count);
+ skippy_iter.reset (start_index - 1, count);
skippy_iter.set_match_func (match_func, match_data, lookahead);
for (unsigned int i = 0; i < count; i++)
- if (!skippy_iter.next ())
+ {
+ unsigned unsafe_to;
+ if (!skippy_iter.next (&unsafe_to))
+ {
+ *end_index = unsafe_to;
return_trace (false);
+ }
+ }
*end_index = skippy_iter.idx + 1;
-
return_trace (true);
}
@@ -1284,22 +1320,23 @@ static void context_closure_recurse_lookups (hb_closure_context_t *c,
unsigned seqIndex = lookupRecord[i].sequenceIndex;
if (seqIndex >= inputCount) continue;
- hb_set_t *pos_glyphs = nullptr;
+ bool has_pos_glyphs = false;
+ hb_set_t pos_glyphs;
if (hb_set_is_empty (covered_seq_indicies) || !hb_set_has (covered_seq_indicies, seqIndex))
{
- pos_glyphs = hb_set_create ();
+ has_pos_glyphs = true;
if (seqIndex == 0)
{
switch (context_format) {
case ContextFormat::SimpleContext:
- pos_glyphs->add (value);
+ pos_glyphs.add (value);
break;
case ContextFormat::ClassBasedContext:
- intersected_glyphs_func (c->cur_intersected_glyphs, data, value, pos_glyphs);
+ intersected_glyphs_func (&c->parent_active_glyphs (), data, value, &pos_glyphs);
break;
case ContextFormat::CoverageBasedContext:
- hb_set_set (pos_glyphs, c->cur_intersected_glyphs);
+ pos_glyphs.set (c->parent_active_glyphs ());
break;
}
}
@@ -1313,12 +1350,16 @@ static void context_closure_recurse_lookups (hb_closure_context_t *c,
input_value = input[seqIndex - 1];
}
- intersected_glyphs_func (c->glyphs, input_data, input_value, pos_glyphs);
+ intersected_glyphs_func (c->glyphs, input_data, input_value, &pos_glyphs);
}
}
- hb_set_add (covered_seq_indicies, seqIndex);
- c->push_cur_active_glyphs (pos_glyphs ? pos_glyphs : c->glyphs);
+ covered_seq_indicies->add (seqIndex);
+ if (has_pos_glyphs) {
+ c->push_cur_active_glyphs () = pos_glyphs;
+ } else {
+ c->push_cur_active_glyphs ().set (*c->glyphs);
+ }
unsigned endIndex = inputCount;
if (context_format == ContextFormat::CoverageBasedContext)
@@ -1327,8 +1368,6 @@ static void context_closure_recurse_lookups (hb_closure_context_t *c,
c->recurse (lookupRecord[i].lookupListIndex, covered_seq_indicies, seqIndex, endIndex);
c->pop_cur_done_glyphs ();
- if (pos_glyphs)
- hb_set_destroy (pos_glyphs);
}
hb_set_destroy (covered_seq_indicies);
@@ -1343,15 +1382,13 @@ static inline void recurse_lookups (context_t *c,
c->recurse (lookupRecord[i].lookupListIndex);
}
-static inline bool apply_lookup (hb_ot_apply_context_t *c,
+static inline void apply_lookup (hb_ot_apply_context_t *c,
unsigned int count, /* Including the first glyph */
unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */
unsigned int lookupCount,
const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
- unsigned int match_length)
+ unsigned int match_end)
{
- TRACE_APPLY (nullptr);
-
hb_buffer_t *buffer = c->buffer;
int end;
@@ -1359,7 +1396,7 @@ static inline bool apply_lookup (hb_ot_apply_context_t *c,
* Adjust. */
{
unsigned int bl = buffer->backtrack_len ();
- end = bl + match_length;
+ end = bl + match_end - buffer->idx;
int delta = bl - buffer->idx;
/* Convert positions to new indexing. */
@@ -1461,8 +1498,6 @@ static inline bool apply_lookup (hb_ot_apply_context_t *c,
}
(void) buffer->move_to (end);
-
- return_trace (true);
}
@@ -1550,17 +1585,25 @@ static inline bool context_apply_lookup (hb_ot_apply_context_t *c,
const LookupRecord lookupRecord[],
ContextApplyLookupContext &lookup_context)
{
- unsigned int match_length = 0;
- unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
- return match_input (c,
- inputCount, input,
- lookup_context.funcs.match, lookup_context.match_data,
- &match_length, match_positions)
- && (c->buffer->unsafe_to_break (c->buffer->idx, c->buffer->idx + match_length),
- apply_lookup (c,
- inputCount, match_positions,
- lookupCount, lookupRecord,
- match_length));
+ unsigned match_end = 0;
+ unsigned match_positions[HB_MAX_CONTEXT_LENGTH];
+ if (match_input (c,
+ inputCount, input,
+ lookup_context.funcs.match, lookup_context.match_data,
+ &match_end, match_positions))
+ {
+ c->buffer->unsafe_to_break (c->buffer->idx, match_end);
+ apply_lookup (c,
+ inputCount, match_positions,
+ lookupCount, lookupRecord,
+ match_end);
+ return true;
+ }
+ else
+ {
+ c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
+ return false;
+ }
}
struct Rule
@@ -1828,8 +1871,9 @@ struct ContextFormat1
void closure (hb_closure_context_t *c) const
{
- c->cur_intersected_glyphs->clear ();
- get_coverage ().intersected_coverage_glyphs (c->parent_active_glyphs (), c->cur_intersected_glyphs);
+ hb_set_t* cur_active_glyphs = &c->push_cur_active_glyphs ();
+ get_coverage ().intersected_coverage_glyphs (&c->previous_parent_active_glyphs (),
+ cur_active_glyphs);
struct ContextClosureLookupContext lookup_context = {
{intersects_glyph, intersected_glyph},
@@ -1838,10 +1882,14 @@ struct ContextFormat1
};
+ hb_zip (this+coverage, hb_range ((unsigned) ruleSet.len))
- | hb_filter (c->parent_active_glyphs (), hb_first)
+ | hb_filter ([&] (hb_codepoint_t _) {
+ return c->previous_parent_active_glyphs ().has (_);
+ }, hb_first)
| hb_map ([&](const hb_pair_t<hb_codepoint_t, unsigned> _) { return hb_pair_t<unsigned, const RuleSet&> (_.first, this+ruleSet[_.second]); })
| hb_apply ([&] (const hb_pair_t<unsigned, const RuleSet&>& _) { _.second.closure (c, _.first, lookup_context); })
;
+
+ c->pop_cur_done_glyphs ();
}
void closure_lookups (hb_closure_lookups_context_t *c) const
@@ -1989,8 +2037,9 @@ struct ContextFormat2
if (!(this+coverage).intersects (c->glyphs))
return;
- c->cur_intersected_glyphs->clear ();
- get_coverage ().intersected_coverage_glyphs (c->parent_active_glyphs (), c->cur_intersected_glyphs);
+ hb_set_t* cur_active_glyphs = &c->push_cur_active_glyphs ();
+ get_coverage ().intersected_coverage_glyphs (&c->previous_parent_active_glyphs (),
+ cur_active_glyphs);
const ClassDef &class_def = this+classDef;
@@ -2000,10 +2049,9 @@ struct ContextFormat2
&class_def
};
- return
+ hb_enumerate (ruleSet)
| hb_filter ([&] (unsigned _)
- { return class_def.intersects_class (c->cur_intersected_glyphs, _); },
+ { return class_def.intersects_class (&c->parent_active_glyphs (), _); },
hb_first)
| hb_apply ([&] (const hb_pair_t<unsigned, const Offset16To<RuleSet>&> _)
{
@@ -2011,6 +2059,8 @@ struct ContextFormat2
rule_set.closure (c, _.first, lookup_context);
})
;
+
+ c->pop_cur_done_glyphs ();
}
void closure_lookups (hb_closure_lookups_context_t *c) const
@@ -2183,8 +2233,10 @@ struct ContextFormat3
if (!(this+coverageZ[0]).intersects (c->glyphs))
return;
- c->cur_intersected_glyphs->clear ();
- get_coverage ().intersected_coverage_glyphs (c->parent_active_glyphs (), c->cur_intersected_glyphs);
+ hb_set_t* cur_active_glyphs = &c->push_cur_active_glyphs ();
+ get_coverage ().intersected_coverage_glyphs (&c->previous_parent_active_glyphs (),
+ cur_active_glyphs);
+
const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
struct ContextClosureLookupContext lookup_context = {
@@ -2196,6 +2248,8 @@ struct ContextFormat3
glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1),
lookupCount, lookupRecord,
0, lookup_context);
+
+ c->pop_cur_done_glyphs ();
}
void closure_lookups (hb_closure_lookups_context_t *c) const
@@ -2452,25 +2506,38 @@ static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c,
const LookupRecord lookupRecord[],
ChainContextApplyLookupContext &lookup_context)
{
- unsigned int start_index = 0, match_length = 0, end_index = 0;
- unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
- return match_input (c,
- inputCount, input,
- lookup_context.funcs.match, lookup_context.match_data[1],
- &match_length, match_positions)
- && match_backtrack (c,
- backtrackCount, backtrack,
- lookup_context.funcs.match, lookup_context.match_data[0],
- &start_index)
- && match_lookahead (c,
- lookaheadCount, lookahead,
- lookup_context.funcs.match, lookup_context.match_data[2],
- match_length, &end_index)
- && (c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index),
- apply_lookup (c,
- inputCount, match_positions,
- lookupCount, lookupRecord,
- match_length));
+ unsigned end_index = c->buffer->idx;
+ unsigned match_end = 0;
+ unsigned match_positions[HB_MAX_CONTEXT_LENGTH];
+ if (!(match_input (c,
+ inputCount, input,
+ lookup_context.funcs.match, lookup_context.match_data[1],
+ &match_end, match_positions) && (end_index = match_end)
+ && match_lookahead (c,
+ lookaheadCount, lookahead,
+ lookup_context.funcs.match, lookup_context.match_data[2],
+ match_end, &end_index)))
+ {
+ c->buffer->unsafe_to_concat (c->buffer->idx, end_index);
+ return false;
+ }
+
+ unsigned start_index = c->buffer->out_len;
+ if (!match_backtrack (c,
+ backtrackCount, backtrack,
+ lookup_context.funcs.match, lookup_context.match_data[0],
+ &start_index))
+ {
+ c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index);
+ return false;
+ }
+
+ c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
+ apply_lookup (c,
+ inputCount, match_positions,
+ lookupCount, lookupRecord,
+ match_end);
+ return true;
}
struct ChainRule
@@ -2802,8 +2869,9 @@ struct ChainContextFormat1
void closure (hb_closure_context_t *c) const
{
- c->cur_intersected_glyphs->clear ();
- get_coverage ().intersected_coverage_glyphs (c->parent_active_glyphs (), c->cur_intersected_glyphs);
+ hb_set_t* cur_active_glyphs = &c->push_cur_active_glyphs ();
+ get_coverage ().intersected_coverage_glyphs (&c->previous_parent_active_glyphs (),
+ cur_active_glyphs);
struct ChainContextClosureLookupContext lookup_context = {
{intersects_glyph, intersected_glyph},
@@ -2812,10 +2880,14 @@ struct ChainContextFormat1
};
+ hb_zip (this+coverage, hb_range ((unsigned) ruleSet.len))
- | hb_filter (c->parent_active_glyphs (), hb_first)
+ | hb_filter ([&] (hb_codepoint_t _) {
+ return c->previous_parent_active_glyphs ().has (_);
+ }, hb_first)
| hb_map ([&](const hb_pair_t<hb_codepoint_t, unsigned> _) { return hb_pair_t<unsigned, const ChainRuleSet&> (_.first, this+ruleSet[_.second]); })
| hb_apply ([&] (const hb_pair_t<unsigned, const ChainRuleSet&>& _) { _.second.closure (c, _.first, lookup_context); })
;
+
+ c->pop_cur_done_glyphs ();
}
void closure_lookups (hb_closure_lookups_context_t *c) const
@@ -2964,8 +3036,10 @@ struct ChainContextFormat2
if (!(this+coverage).intersects (c->glyphs))
return;
- c->cur_intersected_glyphs->clear ();
- get_coverage ().intersected_coverage_glyphs (c->parent_active_glyphs (), c->cur_intersected_glyphs);
+ hb_set_t* cur_active_glyphs = &c->push_cur_active_glyphs ();
+ get_coverage ().intersected_coverage_glyphs (&c->previous_parent_active_glyphs (),
+ cur_active_glyphs);
+
const ClassDef &backtrack_class_def = this+backtrackClassDef;
const ClassDef &input_class_def = this+inputClassDef;
@@ -2979,10 +3053,9 @@ struct ChainContextFormat2
&lookahead_class_def}
};
- return
+ hb_enumerate (ruleSet)
| hb_filter ([&] (unsigned _)
- { return input_class_def.intersects_class (c->cur_intersected_glyphs, _); },
+ { return input_class_def.intersects_class (&c->parent_active_glyphs (), _); },
hb_first)
| hb_apply ([&] (const hb_pair_t<unsigned, const Offset16To<ChainRuleSet>&> _)
{
@@ -2990,6 +3063,8 @@ struct ChainContextFormat2
chainrule_set.closure (c, _.first, lookup_context);
})
;
+
+ c->pop_cur_done_glyphs ();
}
void closure_lookups (hb_closure_lookups_context_t *c) const
@@ -3216,8 +3291,10 @@ struct ChainContextFormat3
if (!(this+input[0]).intersects (c->glyphs))
return;
- c->cur_intersected_glyphs->clear ();
- get_coverage ().intersected_coverage_glyphs (c->parent_active_glyphs (), c->cur_intersected_glyphs);
+ hb_set_t* cur_active_glyphs = &c->push_cur_active_glyphs ();
+ get_coverage ().intersected_coverage_glyphs (&c->previous_parent_active_glyphs (),
+ cur_active_glyphs);
+
const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (input);
const Array16Of<LookupRecord> &lookup = StructAfter<Array16Of<LookupRecord>> (lookahead);
@@ -3232,6 +3309,8 @@ struct ChainContextFormat3
lookahead.len, (const HBUINT16 *) lookahead.arrayZ,
lookup.len, lookup.arrayZ,
0, lookup_context);
+
+ c->pop_cur_done_glyphs ();
}
void closure_lookups (hb_closure_lookups_context_t *c) const
@@ -3706,7 +3785,7 @@ struct GSUBGPOS
for (unsigned i : feature_indices->iter ())
{
hb_tag_t t = get_feature_tag (i);
- if (t == unique_features.INVALID_KEY) continue;
+ if (t == HB_MAP_VALUE_INVALID) continue;
if (!unique_features.has (t))
{
hb_set_t* indices = hb_set_create ();
@@ -3839,7 +3918,7 @@ struct GSUBGPOS
template <typename T>
struct accelerator_t
{
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{
this->table = hb_sanitize_context_t ().reference_table<T> (face);
if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face)))
@@ -3861,8 +3940,7 @@ struct GSUBGPOS
for (unsigned int i = 0; i < this->lookup_count; i++)
this->accels[i].init (table->get_lookup (i));
}
-
- void fini ()
+ ~accelerator_t ()
{
for (unsigned int i = 0; i < this->lookup_count; i++)
this->accels[i].fini ();
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout.cc b/thirdparty/harfbuzz/src/hb-ot-layout.cc
index 60733648c1..a599eea6e9 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-layout.cc
@@ -1491,10 +1491,9 @@ hb_ot_layout_lookup_substitute_closure (hb_face_t *face,
unsigned int lookup_index,
hb_set_t *glyphs /* OUT */)
{
- hb_set_t cur_intersected_glyphs;
hb_map_t done_lookups_glyph_count;
hb_hashmap_t<unsigned, hb_set_t *> done_lookups_glyph_set;
- OT::hb_closure_context_t c (face, glyphs, &cur_intersected_glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set);
+ OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set);
const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
@@ -1520,10 +1519,9 @@ hb_ot_layout_lookups_substitute_closure (hb_face_t *face,
const hb_set_t *lookups,
hb_set_t *glyphs /* OUT */)
{
- hb_set_t cur_intersected_glyphs;
hb_map_t done_lookups_glyph_count;
hb_hashmap_t<unsigned, hb_set_t *> done_lookups_glyph_set;
- OT::hb_closure_context_t c (face, glyphs, &cur_intersected_glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set);
+ OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set);
const OT::GSUB& gsub = *face->table.GSUB->table;
unsigned int iteration_count = 0;
@@ -1890,7 +1888,7 @@ apply_string (OT::hb_ot_apply_context_t *c,
apply_forward (c, accel);
if (!Proxy::inplace)
- buffer->swap_buffers ();
+ buffer->sync ();
}
else
{
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout.hh b/thirdparty/harfbuzz/src/hb-ot-layout.hh
index 2c825e0c81..ede8f007db 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-layout.hh
@@ -482,10 +482,9 @@ _hb_glyph_info_get_lig_num_comps (const hb_glyph_info_t *info)
}
static inline uint8_t
-_hb_allocate_lig_id (hb_buffer_t *buffer) {
+_hb_allocate_lig_id (hb_buffer_t *buffer)
+{
uint8_t lig_id = buffer->next_serial () & 0x07;
- if (unlikely (!lig_id))
- lig_id = _hb_allocate_lig_id (buffer); /* in case of overflow */
return lig_id;
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-meta-table.hh b/thirdparty/harfbuzz/src/hb-ot-meta-table.hh
index e31447f8fc..93e64c5327 100644
--- a/thirdparty/harfbuzz/src/hb-ot-meta-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-meta-table.hh
@@ -71,9 +71,9 @@ struct meta
struct accelerator_t
{
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{ table = hb_sanitize_context_t ().reference_table<meta> (face); }
- void fini () { table.destroy (); }
+ ~accelerator_t () { table.destroy (); }
hb_blob_t *reference_entry (hb_tag_t tag) const
{ return table->dataMaps.lsearch (tag).reference_entry (table.get_blob ()); }
@@ -119,7 +119,9 @@ struct meta
DEFINE_SIZE_ARRAY (16, dataMaps);
};
-struct meta_accelerator_t : meta::accelerator_t {};
+struct meta_accelerator_t : meta::accelerator_t {
+ meta_accelerator_t (hb_face_t *face) : meta::accelerator_t (face) {}
+};
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-metrics.cc b/thirdparty/harfbuzz/src/hb-ot-metrics.cc
index dbd4a1ffbe..103808cf91 100644
--- a/thirdparty/harfbuzz/src/hb-ot-metrics.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-metrics.cc
@@ -160,9 +160,50 @@ hb_ot_metrics_get_position (hb_font_t *font,
(position && (*position = font->em_scalef_y (face->table.TABLE->ATTR + GET_VAR)), true))
case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: return GET_METRIC_Y (OS2, usWinAscent);
case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: return GET_METRIC_Y (OS2, usWinDescent);
- case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: return GET_METRIC_Y (hhea, caretSlopeRise);
- case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: return GET_METRIC_X (hhea, caretSlopeRun);
+
+ case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE:
+ case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN:
+ {
+ unsigned mult = 1u;
+
+ if (font->slant)
+ {
+ unsigned rise = face->table.hhea->caretSlopeRise;
+ unsigned upem = face->get_upem ();
+ mult = (rise && rise < upem) ? hb_min (upem / rise, 256u) : 1u;
+ }
+
+ if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE)
+ {
+ bool ret = GET_METRIC_Y (hhea, caretSlopeRise);
+
+ if (position)
+ *position *= mult;
+
+ return ret;
+ }
+ else
+ {
+ hb_position_t rise = 0;
+
+ if (font->slant && position && GET_METRIC_Y (hhea, caretSlopeRise))
+ rise = *position;
+
+ bool ret = GET_METRIC_X (hhea, caretSlopeRun);
+
+ if (position)
+ {
+ *position *= mult;
+
+ if (font->slant)
+ *position += _hb_roundf (mult * font->slant_xy * rise);
+ }
+
+ return ret;
+ }
+ }
case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: return GET_METRIC_X (hhea, caretOffset);
+
#ifndef HB_NO_VERTICAL
case HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: return GET_METRIC_X (vhea, caretSlopeRise);
case HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: return GET_METRIC_Y (vhea, caretSlopeRun);
diff --git a/thirdparty/harfbuzz/src/hb-ot-name-table.hh b/thirdparty/harfbuzz/src/hb-ot-name-table.hh
index c17bb4abb8..d52367e9b1 100644
--- a/thirdparty/harfbuzz/src/hb-ot-name-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-name-table.hh
@@ -256,7 +256,7 @@ struct name
})
;
- name_prime->serialize (c->serializer, it, hb_addressof (this + stringOffset));
+ name_prime->serialize (c->serializer, it, std::addressof (this + stringOffset));
return_trace (name_prime->count);
}
@@ -279,7 +279,7 @@ struct name
struct accelerator_t
{
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{
this->table = hb_sanitize_context_t ().reference_table<name> (face);
assert (this->table.get_length () >= this->table->stringOffset);
@@ -288,7 +288,6 @@ struct name
const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ,
this->table->count);
- this->names.init ();
this->names.alloc (all_names.length);
for (unsigned int i = 0; i < all_names.length; i++)
@@ -318,10 +317,8 @@ struct name
}
this->names.resize (j);
}
-
- void fini ()
+ ~accelerator_t ()
{
- this->names.fini ();
this->table.destroy ();
}
@@ -373,7 +370,9 @@ struct name
#undef entry_index
#undef entry_score
-struct name_accelerator_t : name::accelerator_t {};
+struct name_accelerator_t : name::accelerator_t {
+ name_accelerator_t (hb_face_t *face) : name::accelerator_t (face) {}
+};
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh b/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh
index 504de2de74..0f3cd8e24f 100644
--- a/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh
@@ -76,8 +76,7 @@ HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const
hb_map_t old_new_index_map, old_gid_new_index_map;
unsigned i = 0;
- post::accelerator_t _post;
- _post.init (c->plan->source);
+ post::accelerator_t _post (c->plan->source);
hb_hashmap_t<hb_bytes_t, unsigned, std::nullptr_t, unsigned, nullptr, (unsigned)-1> glyph_name_to_new_index;
for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++)
@@ -128,9 +127,7 @@ HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const
})
;
- bool ret = serialize (c->serializer, index_iter, &_post);
- _post.fini ();
- return_trace (ret);
+ return_trace (serialize (c->serializer, index_iter, &_post));
}
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-post-table.hh b/thirdparty/harfbuzz/src/hb-ot-post-table.hh
index 39de671707..a4844e94bc 100644
--- a/thirdparty/harfbuzz/src/hb-ot-post-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-post-table.hh
@@ -111,10 +111,9 @@ struct post
struct accelerator_t
{
friend struct postV2Tail;
- void init (hb_face_t *face)
- {
- index_to_offset.init ();
+ accelerator_t (hb_face_t *face)
+ {
table = hb_sanitize_context_t ().reference_table<post> (face);
unsigned int table_length = table.get_length ();
@@ -132,9 +131,8 @@ struct post
data += 1 + *data)
index_to_offset.push (data - pool);
}
- void fini ()
+ ~accelerator_t ()
{
- index_to_offset.fini ();
hb_free (gids_sorted_by_name.get ());
table.destroy ();
}
@@ -254,9 +252,9 @@ struct post
private:
uint32_t version;
- const Array16Of<HBUINT16> *glyphNameIndex;
+ const Array16Of<HBUINT16> *glyphNameIndex = nullptr;
hb_vector_t<uint32_t> index_to_offset;
- const uint8_t *pool;
+ const uint8_t *pool = nullptr;
hb_atomic_ptr_t<uint16_t *> gids_sorted_by_name;
};
@@ -307,7 +305,10 @@ struct post
DEFINE_SIZE_MIN (32);
};
-struct post_accelerator_t : post::accelerator_t {};
+struct post_accelerator_t : post::accelerator_t {
+ post_accelerator_t (hb_face_t *face) : post::accelerator_t (face) {}
+};
+
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh
index 41e3dd38ab..429974d05b 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh
@@ -87,6 +87,8 @@
#define OT_GLYPHID /* GlyphID */ \
OT_UINT16
+/* Shorthand. */
+#define G OT_GLYPHID
#define OT_UARRAY(Name, Items) \
OT_LABEL_START(Name) \
@@ -183,8 +185,6 @@
Tag \
OT_OFFSET(manifest, Name)
-/* Shorthand. */
-#define G OT_GLYPHID
/*
* Table Start
@@ -300,14 +300,40 @@ OT_TABLE_END
/*
* Clean up
*/
+
+#undef MANIFEST
+#undef MANIFEST_LOOKUP
+
#undef OT_TABLE_START
#undef OT_TABLE_END
#undef OT_LABEL_START
#undef OT_LABEL_END
#undef OT_UINT8
#undef OT_UINT16
-#undef OT_DISTANCE
#undef OT_COUNT
+#undef OT_DISTANCE
+
+#undef OT_LABEL
+#undef OT_LIST
+
+#undef OT_TAG
+#undef OT_OFFSET
+#undef OT_GLYPHID
+#undef G
+#undef OT_UARRAY
+#undef OT_UHEADLESSARRAY
+
+#undef OT_LOOKUP_FLAG_IGNORE_MARKS
+#undef OT_LOOKUP
+#undef OT_SUBLOOKUP
+#undef OT_COVERAGE1
+#undef OT_LOOKUP_TYPE_SUBST_SINGLE
+#undef OT_LOOKUP_TYPE_SUBST_LIGATURE
+#undef OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2
+#undef OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1
+#undef OT_LIGATURE_SET
+#undef OT_LIGATURE
+
/*
* Include a second time to get the table data...
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.cc
index 222c5d6b71..2298aa92f2 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -321,6 +321,20 @@ arabic_joining (hb_buffer_t *buffer)
info[prev].arabic_shaping_action() = entry->prev_action;
buffer->unsafe_to_break (prev, i + 1);
}
+ else
+ {
+ if (prev == UINT_MAX)
+ {
+ if (this_type >= JOINING_TYPE_R)
+ buffer->unsafe_to_concat_from_outbuffer (0, i + 1);
+ }
+ else
+ {
+ if (this_type >= JOINING_TYPE_R ||
+ (2 <= state && state <= 5) /* States that have a possible prev_action. */)
+ buffer->unsafe_to_concat (prev, i + 1);
+ }
+ }
info[i].arabic_shaping_action() = entry->curr_action;
@@ -337,7 +351,14 @@ arabic_joining (hb_buffer_t *buffer)
const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
if (entry->prev_action != NONE && prev != UINT_MAX)
+ {
info[prev].arabic_shaping_action() = entry->prev_action;
+ buffer->unsafe_to_break (prev, buffer->len);
+ }
+ else if (2 <= state && state <= 5) /* States that have a possible prev_action. */
+ {
+ buffer->unsafe_to_concat (prev, buffer->len);
+ }
break;
}
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-hangul.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-hangul.cc
index 0d84a76b85..3bc9e9b961 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shape-complex-hangul.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-hangul.cc
@@ -140,7 +140,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED,
*
* - LV can be precomposed, or decomposed. Lets call those
* <LV> and <L,V>,
- * - LVT can be fully precomposed, partically precomposed, or
+ * - LVT can be fully precomposed, partially precomposed, or
* fully decomposed. Ie. <LVT>, <LV,T>, or <L,V,T>.
*
* The composition / decomposition is mechanical. However, not
@@ -392,7 +392,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED,
*/
(void) buffer->next_glyph ();
}
- buffer->swap_buffers ();
+ buffer->sync ();
}
static void
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-syllabic.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-syllabic.cc
index 5a08f878dc..76092c7f38 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shape-complex-syllabic.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-syllabic.cc
@@ -96,7 +96,7 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font,
else
(void) buffer->next_glyph ();
}
- buffer->swap_buffers ();
+ buffer->sync ();
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-thai.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-thai.cc
index 4c3068173b..a1e27a83be 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shape-complex-thai.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-thai.cc
@@ -364,7 +364,7 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan,
buffer->merge_out_clusters (start - 1, end);
}
}
- buffer->swap_buffers ();
+ buffer->sync ();
/* If font has Thai GSUB, we are done. */
if (plan->props.script == HB_SCRIPT_THAI && !plan->map.found_script[0])
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc
index 045731dfb4..d2cca105a4 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc
@@ -435,7 +435,7 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED,
default:
break;
}
- buffer->swap_buffers ();
+ buffer->sync ();
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-fallback.cc b/thirdparty/harfbuzz/src/hb-ot-shape-fallback.cc
index eb1bc79768..671f30327f 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shape-fallback.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-fallback.cc
@@ -446,6 +446,9 @@ _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan,
return;
#endif
+ if (!buffer->message (font, "start fallback mark"))
+ return;
+
_hb_buffer_assert_gsubgpos_vars (buffer);
unsigned int start = 0;
@@ -457,6 +460,8 @@ _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan,
start = i;
}
position_cluster (plan, font, buffer, start, count, adjust_offsets_when_zeroing);
+
+ (void) buffer->message (font, "end fallback mark");
}
@@ -492,6 +497,9 @@ _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
#endif
#ifndef HB_DISABLE_DEPRECATED
+ if (!buffer->message (font, "start fallback kern"))
+ return;
+
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction) ?
!font->has_glyph_h_kerning_func () :
!font->has_glyph_v_kerning_func ())
@@ -508,6 +516,8 @@ _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
if (reverse)
buffer->reverse ();
+
+ (void) buffer->message (font, "end fallback kern");
#endif
}
@@ -525,6 +535,15 @@ _hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan HB_UNUSED,
for (unsigned int i = 0; i < count; i++)
if (_hb_glyph_info_is_unicode_space (&info[i]) && !_hb_glyph_info_ligated (&info[i]))
{
+ /* If font had no ASCII space and we used the invisible glyph, give it a 1/4 EM default advance. */
+ if (buffer->invisible && info[i].codepoint == buffer->invisible)
+ {
+ if (horizontal)
+ pos[i].x_advance = +font->x_scale / 4;
+ else
+ pos[i].y_advance = -font->y_scale / 4;
+ }
+
hb_unicode_funcs_t::space_t space_type = _hb_glyph_info_get_unicode_space_fallback_type (&info[i]);
hb_codepoint_t glyph;
typedef hb_unicode_funcs_t t;
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc b/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc
index 839cc9122c..aa5a8eeaa3 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc
@@ -193,7 +193,8 @@ decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shor
{
hb_codepoint_t space_glyph;
hb_unicode_funcs_t::space_t space_type = buffer->unicode->space_fallback_type (u);
- if (space_type != hb_unicode_funcs_t::NOT_SPACE && c->font->get_nominal_glyph (0x0020u, &space_glyph))
+ if (space_type != hb_unicode_funcs_t::NOT_SPACE &&
+ (c->font->get_nominal_glyph (0x0020, &space_glyph) || (space_glyph = buffer->invisible)))
{
_hb_glyph_info_set_unicode_space_fallback_type (&buffer->cur(), space_type);
next_char (buffer, space_glyph);
@@ -374,7 +375,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
decompose_multi_char_cluster (&c, end, always_short_circuit);
}
while (buffer->idx < count && buffer->successful);
- buffer->swap_buffers ();
+ buffer->sync ();
}
@@ -477,7 +478,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
if (info_cc (buffer->prev()) == 0)
starter = buffer->out_len - 1;
}
- buffer->swap_buffers ();
+ buffer->sync ();
}
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape.cc b/thirdparty/harfbuzz/src/hb-ot-shape.cc
index 4dde3520d8..4bd8aaf03b 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shape.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shape.cc
@@ -566,7 +566,7 @@ hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font)
info.mask = buffer->cur().mask;
(void) buffer->output_info (info);
- buffer->swap_buffers ();
+ buffer->sync ();
}
static void
@@ -1034,7 +1034,7 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c)
* hanging over the next glyph after the final reordering.
*
* Note: If fallback positinoing happens, we don't care about
- * this as it will be overriden.
+ * this as it will be overridden.
*/
bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing &&
HB_DIRECTION_IS_FORWARD (c->buffer->props.direction);
@@ -1120,7 +1120,7 @@ hb_propagate_flags (hb_buffer_t *buffer)
/* Propagate cluster-level glyph flags to be the same on all cluster glyphs.
* Simplifies using them. */
- if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK))
+ if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS))
return;
hb_glyph_info_t *info = buffer->info;
@@ -1129,11 +1129,7 @@ hb_propagate_flags (hb_buffer_t *buffer)
{
unsigned int mask = 0;
for (unsigned int i = start; i < end; i++)
- if (info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
- {
- mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
- break;
- }
+ mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED;
if (mask)
for (unsigned int i = start; i < end; i++)
info[i].mask |= mask;
@@ -1145,18 +1141,7 @@ hb_propagate_flags (hb_buffer_t *buffer)
static void
hb_ot_shape_internal (hb_ot_shape_context_t *c)
{
- c->buffer->deallocate_var_all ();
- c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
- if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR)))
- {
- c->buffer->max_len = hb_max (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR,
- (unsigned) HB_BUFFER_MAX_LEN_MIN);
- }
- if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR)))
- {
- c->buffer->max_ops = hb_max (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR,
- (unsigned) HB_BUFFER_MAX_OPS_MIN);
- }
+ c->buffer->enter ();
/* Save the original direction, we use it later. */
c->target_direction = c->buffer->props.direction;
@@ -1188,9 +1173,7 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c)
c->buffer->props.direction = c->target_direction;
- c->buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT;
- c->buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT;
- c->buffer->deallocate_var_all ();
+ c->buffer->leave ();
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-tag-table.hh b/thirdparty/harfbuzz/src/hb-ot-tag-table.hh
index 2c6316df4f..61d2814e93 100644
--- a/thirdparty/harfbuzz/src/hb-ot-tag-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-tag-table.hh
@@ -6,8 +6,8 @@
*
* on files with these headers:
*
- * <meta name="updated_at" content="2021-12-09 12:01 AM" />
- * File-Date: 2021-08-06
+ * <meta name="updated_at" content="2022-01-28 10:00 PM" />
+ * File-Date: 2021-12-29
*/
#ifndef HB_OT_TAG_TABLE_HH
@@ -66,7 +66,7 @@ static const LangTag ot_languages[] = {
{"an", HB_TAG('A','R','G',' ')}, /* Aragonese */
/*{"ang", HB_TAG('A','N','G',' ')},*/ /* Old English (ca. 450-1100) -> Anglo-Saxon */
{"aoa", HB_TAG('C','P','P',' ')}, /* Angolar -> Creoles */
- {"apa", HB_TAG('A','T','H',' ')}, /* Apache [family] -> Athapaskan */
+ {"apa", HB_TAG('A','T','H',' ')}, /* Apache [collection] -> Athapaskan */
{"apc", HB_TAG('A','R','A',' ')}, /* North Levantine Arabic -> Arabic */
{"apd", HB_TAG('A','R','A',' ')}, /* Sudanese Arabic -> Arabic */
{"apj", HB_TAG('A','T','H',' ')}, /* Jicarilla Apache -> Athapaskan */
@@ -86,7 +86,7 @@ static const LangTag ot_languages[] = {
{"arz", HB_TAG('A','R','A',' ')}, /* Egyptian Arabic -> Arabic */
{"as", HB_TAG('A','S','M',' ')}, /* Assamese */
/*{"ast", HB_TAG('A','S','T',' ')},*/ /* Asturian */
-/*{"ath", HB_TAG('A','T','H',' ')},*/ /* Athapascan [family] -> Athapaskan */
+/*{"ath", HB_TAG('A','T','H',' ')},*/ /* Athapascan [collection] -> Athapaskan */
{"atj", HB_TAG('R','C','R',' ')}, /* Atikamekw -> R-Cree */
{"atv", HB_TAG('A','L','T',' ')}, /* Northern Altai -> Altai */
{"auj", HB_TAG('B','B','R',' ')}, /* Awjilah -> Berber */
@@ -110,10 +110,10 @@ static const LangTag ot_languages[] = {
{"azn", HB_TAG('N','A','H',' ')}, /* Western Durango Nahuatl -> Nahuatl */
{"azz", HB_TAG('N','A','H',' ')}, /* Highland Puebla Nahuatl -> Nahuatl */
{"ba", HB_TAG('B','S','H',' ')}, /* Bashkir */
- {"bad", HB_TAG('B','A','D','0')}, /* Banda [family] */
+ {"bad", HB_TAG('B','A','D','0')}, /* Banda [collection] */
{"bag", HB_TAG_NONE }, /* Tuki != Baghelkhandi */
{"bah", HB_TAG('C','P','P',' ')}, /* Bahamas Creole English -> Creoles */
- {"bai", HB_TAG('B','M','L',' ')}, /* Bamileke [family] */
+ {"bai", HB_TAG('B','M','L',' ')}, /* Bamileke [collection] */
{"bal", HB_TAG('B','L','I',' ')}, /* Baluchi [macrolanguage] */
/*{"ban", HB_TAG('B','A','N',' ')},*/ /* Balinese */
/*{"bar", HB_TAG('B','A','R',' ')},*/ /* Bavarian */
@@ -135,7 +135,7 @@ static const LangTag ot_languages[] = {
{"bea", HB_TAG('A','T','H',' ')}, /* Beaver -> Athapaskan */
{"beb", HB_TAG('B','T','I',' ')}, /* Bebele -> Beti */
/*{"bem", HB_TAG('B','E','M',' ')},*/ /* Bemba (Zambia) */
- {"ber", HB_TAG('B','B','R',' ')}, /* Berber [family] */
+ {"ber", HB_TAG('B','B','R',' ')}, /* Berber [collection] */
{"bew", HB_TAG('C','P','P',' ')}, /* Betawi -> Creoles */
{"bfl", HB_TAG('B','A','D','0')}, /* Banda-Ndélé -> Banda */
{"bfq", HB_TAG('B','A','D',' ')}, /* Badaga */
@@ -203,7 +203,7 @@ static const LangTag ot_languages[] = {
{"btd", HB_TAG('B','T','K',' ')}, /* Batak Dairi -> Batak */
{"bti", HB_TAG_NONE }, /* Burate != Beti */
{"btj", HB_TAG('M','L','Y',' ')}, /* Bacanese Malay -> Malay */
-/*{"btk", HB_TAG('B','T','K',' ')},*/ /* Batak [family] */
+/*{"btk", HB_TAG('B','T','K',' ')},*/ /* Batak [collection] */
{"btm", HB_TAG('B','T','M',' ')}, /* Batak Mandailing */
{"btm", HB_TAG('B','T','K',' ')}, /* Batak Mandailing -> Batak */
{"bto", HB_TAG('B','I','K',' ')}, /* Rinconada Bikol -> Bikol */
@@ -256,6 +256,8 @@ static const LangTag ot_languages[] = {
{"chh", HB_TAG_NONE }, /* Chinook != Chattisgarhi */
{"chj", HB_TAG('C','C','H','N')}, /* Ojitlán Chinantec -> Chinantec */
{"chk", HB_TAG('C','H','K','0')}, /* Chuukese */
+ {"chm", HB_TAG('H','M','A',' ')}, /* Mari (Russia) [macrolanguage] -> High Mari */
+ {"chm", HB_TAG('L','M','A',' ')}, /* Mari (Russia) [macrolanguage] -> Low Mari */
{"chn", HB_TAG('C','P','P',' ')}, /* Chinook jargon -> Creoles */
/*{"cho", HB_TAG('C','H','O',' ')},*/ /* Choctaw */
{"chp", HB_TAG('C','H','P',' ')}, /* Chipewyan */
@@ -297,10 +299,10 @@ static const LangTag ot_languages[] = {
/*{"cop", HB_TAG('C','O','P',' ')},*/ /* Coptic */
{"coq", HB_TAG('A','T','H',' ')}, /* Coquille -> Athapaskan */
{"cpa", HB_TAG('C','C','H','N')}, /* Palantla Chinantec -> Chinantec */
- {"cpe", HB_TAG('C','P','P',' ')}, /* English-based creoles and pidgins [family] -> Creoles */
- {"cpf", HB_TAG('C','P','P',' ')}, /* French-based creoles and pidgins [family] -> Creoles */
+ {"cpe", HB_TAG('C','P','P',' ')}, /* English-based creoles and pidgins [collection] -> Creoles */
+ {"cpf", HB_TAG('C','P','P',' ')}, /* French-based creoles and pidgins [collection] -> Creoles */
{"cpi", HB_TAG('C','P','P',' ')}, /* Chinese Pidgin English -> Creoles */
-/*{"cpp", HB_TAG('C','P','P',' ')},*/ /* Portuguese-based creoles and pidgins [family] -> Creoles */
+/*{"cpp", HB_TAG('C','P','P',' ')},*/ /* Portuguese-based creoles and pidgins [collection] -> Creoles */
{"cpx", HB_TAG('Z','H','S',' ')}, /* Pu-Xian Chinese -> Chinese, Simplified */
{"cqd", HB_TAG('H','M','N',' ')}, /* Chuanqiandian Cluster Miao -> Hmong */
{"cqu", HB_TAG('Q','U','H',' ')}, /* Chilean Quechua (retired code) -> Quechua (Bolivia) */
@@ -320,7 +322,7 @@ static const LangTag ot_languages[] = {
{"crm", HB_TAG('M','C','R',' ')}, /* Moose Cree */
{"crm", HB_TAG('L','C','R',' ')}, /* Moose Cree -> L-Cree */
{"crm", HB_TAG('C','R','E',' ')}, /* Moose Cree -> Cree */
- {"crp", HB_TAG('C','P','P',' ')}, /* Creoles and pidgins [family] -> Creoles */
+ {"crp", HB_TAG('C','P','P',' ')}, /* Creoles and pidgins [collection] -> Creoles */
{"crr", HB_TAG_NONE }, /* Carolina Algonquian != Carrier */
{"crs", HB_TAG('C','P','P',' ')}, /* Seselwa Creole French -> Creoles */
{"crt", HB_TAG_NONE }, /* Iyojwa'ja Chorote != Crimean Tatar */
@@ -431,7 +433,7 @@ static const LangTag ot_languages[] = {
{"et", HB_TAG('E','T','I',' ')}, /* Estonian [macrolanguage] */
{"eto", HB_TAG('B','T','I',' ')}, /* Eton (Cameroon) -> Beti */
{"eu", HB_TAG('E','U','Q',' ')}, /* Basque */
- {"euq", HB_TAG_NONE }, /* Basque [family] != Basque */
+ {"euq", HB_TAG_NONE }, /* Basque [collection] != Basque */
{"eve", HB_TAG('E','V','N',' ')}, /* Even */
{"evn", HB_TAG('E','V','K',' ')}, /* Evenki */
{"ewo", HB_TAG('B','T','I',' ')}, /* Ewondo -> Beti */
@@ -620,10 +622,11 @@ static const LangTag ot_languages[] = {
{"ijc", HB_TAG('I','J','O',' ')}, /* Izon -> Ijo */
{"ije", HB_TAG('I','J','O',' ')}, /* Biseni -> Ijo */
{"ijn", HB_TAG('I','J','O',' ')}, /* Kalabari -> Ijo */
-/*{"ijo", HB_TAG('I','J','O',' ')},*/ /* Ijo [family] */
+/*{"ijo", HB_TAG('I','J','O',' ')},*/ /* Ijo [collection] */
{"ijs", HB_TAG('I','J','O',' ')}, /* Southeast Ijo -> Ijo */
{"ik", HB_TAG('I','P','K',' ')}, /* Inupiaq [macrolanguage] -> Inupiat */
{"ike", HB_TAG('I','N','U',' ')}, /* Eastern Canadian Inuktitut -> Inuktitut */
+ {"ike", HB_TAG('I','N','U','K')}, /* Eastern Canadian Inuktitut -> Nunavik Inuktitut */
{"ikt", HB_TAG('I','N','U',' ')}, /* Inuinnaqtun -> Inuktitut */
/*{"ilo", HB_TAG('I','L','O',' ')},*/ /* Iloko -> Ilokano */
{"in", HB_TAG('I','N','D',' ')}, /* Indonesian (retired code) */
@@ -638,6 +641,7 @@ static const LangTag ot_languages[] = {
{"it", HB_TAG('I','T','A',' ')}, /* Italian */
{"itz", HB_TAG('M','Y','N',' ')}, /* Itzá -> Mayan */
{"iu", HB_TAG('I','N','U',' ')}, /* Inuktitut [macrolanguage] */
+ {"iu", HB_TAG('I','N','U','K')}, /* Inuktitut [macrolanguage] -> Nunavik Inuktitut */
{"iw", HB_TAG('I','W','R',' ')}, /* Hebrew (retired code) */
{"ixl", HB_TAG('M','Y','N',' ')}, /* Ixil -> Mayan */
{"ja", HB_TAG('J','A','N',' ')}, /* Japanese */
@@ -667,7 +671,7 @@ static const LangTag ot_languages[] = {
{"kab", HB_TAG('B','B','R',' ')}, /* Kabyle -> Berber */
{"kac", HB_TAG_NONE }, /* Kachin != Kachchi */
{"kam", HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */
- {"kar", HB_TAG('K','R','N',' ')}, /* Karen [family] */
+ {"kar", HB_TAG('K','R','N',' ')}, /* Karen [collection] */
/*{"kaw", HB_TAG('K','A','W',' ')},*/ /* Kawi (Old Javanese) */
{"kbd", HB_TAG('K','A','B',' ')}, /* Kabardian */
{"kby", HB_TAG('K','N','R',' ')}, /* Manga Kanuri -> Kanuri */
@@ -876,7 +880,7 @@ static const LangTag ot_languages[] = {
{"mam", HB_TAG('M','A','M',' ')}, /* Mam */
{"mam", HB_TAG('M','Y','N',' ')}, /* Mam -> Mayan */
{"man", HB_TAG('M','N','K',' ')}, /* Mandingo [macrolanguage] -> Maninka */
- {"map", HB_TAG_NONE }, /* Austronesian [family] != Mapudungun */
+ {"map", HB_TAG_NONE }, /* Austronesian [collection] != Mapudungun */
{"maw", HB_TAG_NONE }, /* Mampruli != Marwari */
{"max", HB_TAG('M','L','Y',' ')}, /* North Moluccan Malay -> Malay */
{"max", HB_TAG('C','P','P',' ')}, /* North Moluccan Malay -> Creoles */
@@ -936,6 +940,7 @@ static const LangTag ot_languages[] = {
{"mnw", HB_TAG('M','O','N','T')}, /* Mon -> Thailand Mon */
{"mnx", HB_TAG_NONE }, /* Manikion != Manx */
{"mo", HB_TAG('M','O','L',' ')}, /* Moldavian (retired code) */
+ {"mo", HB_TAG('R','O','M',' ')}, /* Moldavian (retired code) -> Romanian */
{"mod", HB_TAG('C','P','P',' ')}, /* Mobilian -> Creoles */
/*{"moh", HB_TAG('M','O','H',' ')},*/ /* Mohawk */
{"mok", HB_TAG_NONE }, /* Morori != Moksha */
@@ -958,7 +963,7 @@ static const LangTag ot_languages[] = {
{"mts", HB_TAG_NONE }, /* Yora != Maltese */
{"mud", HB_TAG('C','P','P',' ')}, /* Mednyj Aleut -> Creoles */
{"mui", HB_TAG('M','L','Y',' ')}, /* Musi -> Malay */
- {"mun", HB_TAG_NONE }, /* Munda [family] != Mundari */
+ {"mun", HB_TAG_NONE }, /* Munda [collection] != Mundari */
{"mup", HB_TAG('R','A','J',' ')}, /* Malvi -> Rajasthani */
{"muq", HB_TAG('H','M','N',' ')}, /* Eastern Xiangxi Miao -> Hmong */
/*{"mus", HB_TAG('M','U','S',' ')},*/ /* Creek -> Muscogee */
@@ -973,7 +978,7 @@ static const LangTag ot_languages[] = {
{"mww", HB_TAG('H','M','N',' ')}, /* Hmong Daw -> Hmong */
{"my", HB_TAG('B','R','M',' ')}, /* Burmese */
{"mym", HB_TAG('M','E','N',' ')}, /* Me’en */
-/*{"myn", HB_TAG('M','Y','N',' ')},*/ /* Mayan [family] */
+/*{"myn", HB_TAG('M','Y','N',' ')},*/ /* Mayan [collection] */
{"myq", HB_TAG('M','N','K',' ')}, /* Forest Maninka (retired code) -> Maninka */
{"myv", HB_TAG('E','R','Z',' ')}, /* Erzya */
{"mzb", HB_TAG('B','B','R',' ')}, /* Tumzabt -> Berber */
@@ -982,7 +987,7 @@ static const LangTag ot_languages[] = {
{"na", HB_TAG('N','A','U',' ')}, /* Nauru -> Nauruan */
{"nag", HB_TAG('N','A','G',' ')}, /* Naga Pidgin -> Naga-Assamese */
{"nag", HB_TAG('C','P','P',' ')}, /* Naga Pidgin -> Creoles */
-/*{"nah", HB_TAG('N','A','H',' ')},*/ /* Nahuatl [family] */
+/*{"nah", HB_TAG('N','A','H',' ')},*/ /* Nahuatl [collection] */
{"nan", HB_TAG('Z','H','S',' ')}, /* Min Nan Chinese -> Chinese, Simplified */
/*{"nap", HB_TAG('N','A','P',' ')},*/ /* Neapolitan */
{"nas", HB_TAG_NONE }, /* Naasioi != Naskapi */
@@ -1039,7 +1044,6 @@ static const LangTag ot_languages[] = {
{"nln", HB_TAG('N','A','H',' ')}, /* Durango Nahuatl (retired code) -> Nahuatl */
{"nlv", HB_TAG('N','A','H',' ')}, /* Orizaba Nahuatl -> Nahuatl */
{"nn", HB_TAG('N','Y','N',' ')}, /* Norwegian Nynorsk (Nynorsk, Norwegian) */
- {"nn", HB_TAG('N','O','R',' ')}, /* Norwegian Nynorsk -> Norwegian */
{"nnh", HB_TAG('B','M','L',' ')}, /* Ngiemboon -> Bamileke */
{"nnz", HB_TAG('B','M','L',' ')}, /* Nda'nda' -> Bamileke */
{"no", HB_TAG('N','O','R',' ')}, /* Norwegian [macrolanguage] */
@@ -1093,7 +1097,7 @@ static const LangTag ot_languages[] = {
{"otw", HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */
{"oua", HB_TAG('B','B','R',' ')}, /* Tagargrent -> Berber */
{"pa", HB_TAG('P','A','N',' ')}, /* Punjabi */
- {"paa", HB_TAG_NONE }, /* Papuan [family] != Palestinian Aramaic */
+ {"paa", HB_TAG_NONE }, /* Papuan [collection] != Palestinian Aramaic */
/*{"pag", HB_TAG('P','A','G',' ')},*/ /* Pangasinan */
{"pal", HB_TAG_NONE }, /* Pahlavi != Pali */
/*{"pam", HB_TAG('P','A','M',' ')},*/ /* Pampanga -> Pampangan */
@@ -1308,6 +1312,9 @@ static const LangTag ot_languages[] = {
{"sgo", HB_TAG_NONE }, /* Songa (retired code) != Sango */
/*{"sgs", HB_TAG('S','G','S',' ')},*/ /* Samogitian */
{"sgw", HB_TAG('C','H','G',' ')}, /* Sebat Bet Gurage -> Chaha Gurage */
+ {"sh", HB_TAG('B','O','S',' ')}, /* Serbo-Croatian [macrolanguage] -> Bosnian */
+ {"sh", HB_TAG('H','R','V',' ')}, /* Serbo-Croatian [macrolanguage] -> Croatian */
+ {"sh", HB_TAG('S','R','B',' ')}, /* Serbo-Croatian [macrolanguage] -> Serbian */
{"shi", HB_TAG('S','H','I',' ')}, /* Tachelhit */
{"shi", HB_TAG('B','B','R',' ')}, /* Tachelhit -> Berber */
{"shl", HB_TAG('Q','I','N',' ')}, /* Shendu -> Chin */
@@ -1329,7 +1336,7 @@ static const LangTag ot_languages[] = {
{"skw", HB_TAG('C','P','P',' ')}, /* Skepi Creole Dutch -> Creoles */
{"sky", HB_TAG_NONE }, /* Sikaiana != Slovak */
{"sl", HB_TAG('S','L','V',' ')}, /* Slovenian */
- {"sla", HB_TAG_NONE }, /* Slavic [family] != Slavey */
+ {"sla", HB_TAG_NONE }, /* Slavic [collection] != Slavey */
{"sm", HB_TAG('S','M','O',' ')}, /* Samoan */
{"sma", HB_TAG('S','S','M',' ')}, /* Southern Sami */
{"smj", HB_TAG('L','S','M',' ')}, /* Lule Sami */
@@ -1451,7 +1458,7 @@ static const LangTag ot_languages[] = {
{"tpi", HB_TAG('C','P','P',' ')}, /* Tok Pisin -> Creoles */
{"tr", HB_TAG('T','R','K',' ')}, /* Turkish */
{"trf", HB_TAG('C','P','P',' ')}, /* Trinidadian Creole English -> Creoles */
- {"trk", HB_TAG_NONE }, /* Turkic [family] != Turkish */
+ {"trk", HB_TAG_NONE }, /* Turkic [collection] != Turkish */
{"tru", HB_TAG('T','U','A',' ')}, /* Turoyo -> Turoyo Aramaic */
{"tru", HB_TAG('S','Y','R',' ')}, /* Turoyo -> Syriac */
{"ts", HB_TAG('T','S','G',' ')}, /* Tsonga */
@@ -1593,7 +1600,7 @@ static const LangTag ot_languages[] = {
{"zlq", HB_TAG('Z','H','A',' ')}, /* Liuqian Zhuang -> Zhuang */
{"zmi", HB_TAG('M','L','Y',' ')}, /* Negeri Sembilan Malay -> Malay */
{"zmz", HB_TAG('B','A','D','0')}, /* Mbandja -> Banda */
- {"znd", HB_TAG_NONE }, /* Zande [family] != Zande */
+ {"znd", HB_TAG_NONE }, /* Zande [collection] != Zande */
{"zne", HB_TAG('Z','N','D',' ')}, /* Zande */
{"zom", HB_TAG('Q','I','N',' ')}, /* Zou -> Chin */
{"zqe", HB_TAG('Z','H','A',' ')}, /* Qiubei Zhuang -> Zhuang */
@@ -2607,14 +2614,8 @@ hb_ot_tags_from_complex_language (const char *lang_str,
if (0 == strcmp (&lang_str[1], "o-nyn"))
{
/* Norwegian Nynorsk (retired code) */
- unsigned int i;
- hb_tag_t possible_tags[] = {
- HB_TAG('N','Y','N',' '), /* Norwegian Nynorsk (Nynorsk, Norwegian) */
- HB_TAG('N','O','R',' '), /* Norwegian */
- };
- for (i = 0; i < 2 && i < *count; i++)
- tags[i] = possible_tags[i];
- *count = i;
+ tags[0] = HB_TAG('N','Y','N',' '); /* Norwegian Nynorsk (Nynorsk, Norwegian) */
+ *count = 1;
return true;
}
break;
@@ -2623,8 +2624,14 @@ hb_ot_tags_from_complex_language (const char *lang_str,
&& subtag_matches (lang_str, limit, "-md"))
{
/* Romanian; Moldova */
- tags[0] = HB_TAG('M','O','L',' '); /* Moldavian */
- *count = 1;
+ unsigned int i;
+ hb_tag_t possible_tags[] = {
+ HB_TAG('M','O','L',' '), /* Moldavian */
+ HB_TAG('R','O','M',' '), /* Romanian */
+ };
+ for (i = 0; i < 2 && i < *count; i++)
+ tags[i] = possible_tags[i];
+ *count = i;
return true;
}
break;
@@ -2813,15 +2820,15 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag)
case HB_TAG('A','R','K',' '): /* Rakhine */
return hb_language_from_string ("rki", -1); /* Rakhine */
case HB_TAG('A','T','H',' '): /* Athapaskan */
- return hb_language_from_string ("ath", -1); /* Athapascan [family] */
+ return hb_language_from_string ("ath", -1); /* Athapascan [collection] */
case HB_TAG('B','B','R',' '): /* Berber */
- return hb_language_from_string ("ber", -1); /* Berber [family] */
+ return hb_language_from_string ("ber", -1); /* Berber [collection] */
case HB_TAG('B','I','K',' '): /* Bikol */
return hb_language_from_string ("bik", -1); /* Bikol [macrolanguage] */
case HB_TAG('B','T','K',' '): /* Batak */
- return hb_language_from_string ("btk", -1); /* Batak [family] */
+ return hb_language_from_string ("btk", -1); /* Batak [collection] */
case HB_TAG('C','P','P',' '): /* Creoles */
- return hb_language_from_string ("crp", -1); /* Creoles and pidgins [family] */
+ return hb_language_from_string ("crp", -1); /* Creoles and pidgins [collection] */
case HB_TAG('C','R','R',' '): /* Carrier */
return hb_language_from_string ("crx", -1); /* Carrier */
case HB_TAG('D','G','R',' '): /* Dogri (macrolanguage) */
@@ -2838,6 +2845,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag)
return hb_language_from_string ("fa", -1); /* Persian [macrolanguage] */
case HB_TAG('G','O','N',' '): /* Gondi */
return hb_language_from_string ("gon", -1); /* Gondi [macrolanguage] */
+ case HB_TAG('H','M','A',' '): /* High Mari */
+ return hb_language_from_string ("mrj", -1); /* Western Mari */
case HB_TAG('H','M','N',' '): /* Hmong */
return hb_language_from_string ("hmn", -1); /* Hmong [macrolanguage] */
case HB_TAG('H','N','D',' '): /* Hindko */
@@ -2847,7 +2856,7 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag)
case HB_TAG('I','B','A',' '): /* Iban */
return hb_language_from_string ("iba", -1); /* Iban */
case HB_TAG('I','J','O',' '): /* Ijo */
- return hb_language_from_string ("ijo", -1); /* Ijo [family] */
+ return hb_language_from_string ("ijo", -1); /* Ijo [collection] */
case HB_TAG('I','N','U',' '): /* Inuktitut */
return hb_language_from_string ("iu", -1); /* Inuktitut [macrolanguage] */
case HB_TAG('I','P','K',' '): /* Inupiat */
@@ -2873,11 +2882,13 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag)
case HB_TAG('K','P','L',' '): /* Kpelle */
return hb_language_from_string ("kpe", -1); /* Kpelle [macrolanguage] */
case HB_TAG('K','R','N',' '): /* Karen */
- return hb_language_from_string ("kar", -1); /* Karen [family] */
+ return hb_language_from_string ("kar", -1); /* Karen [collection] */
case HB_TAG('K','U','I',' '): /* Kui */
return hb_language_from_string ("uki", -1); /* Kui (India) */
case HB_TAG('K','U','R',' '): /* Kurdish */
return hb_language_from_string ("ku", -1); /* Kurdish [macrolanguage] */
+ case HB_TAG('L','M','A',' '): /* Low Mari */
+ return hb_language_from_string ("mhr", -1); /* Eastern Mari */
case HB_TAG('L','U','H',' '): /* Luyia */
return hb_language_from_string ("luy", -1); /* Luyia [macrolanguage] */
case HB_TAG('L','V','I',' '): /* Latvian */
@@ -2897,9 +2908,9 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag)
case HB_TAG('M','O','N','T'): /* Thailand Mon */
return hb_language_from_string ("mnw-TH", -1); /* Mon; Thailand */
case HB_TAG('M','Y','N',' '): /* Mayan */
- return hb_language_from_string ("myn", -1); /* Mayan [family] */
+ return hb_language_from_string ("myn", -1); /* Mayan [collection] */
case HB_TAG('N','A','H',' '): /* Nahuatl */
- return hb_language_from_string ("nah", -1); /* Nahuatl [family] */
+ return hb_language_from_string ("nah", -1); /* Nahuatl [collection] */
case HB_TAG('N','E','P',' '): /* Nepali */
return hb_language_from_string ("ne", -1); /* Nepali [macrolanguage] */
case HB_TAG('N','I','S',' '): /* Nisi */
@@ -2926,6 +2937,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag)
return hb_language_from_string ("qwh", -1); /* Huaylas Ancash Quechua */
case HB_TAG('R','A','J',' '): /* Rajasthani */
return hb_language_from_string ("raj", -1); /* Rajasthani [macrolanguage] */
+ case HB_TAG('R','O','M',' '): /* Romanian */
+ return hb_language_from_string ("ro", -1); /* Romanian */
case HB_TAG('R','O','Y',' '): /* Romany */
return hb_language_from_string ("rom", -1); /* Romany [macrolanguage] */
case HB_TAG('S','Q','I',' '): /* Albanian */
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh
index 05f289db26..e066558683 100644
--- a/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh
@@ -263,7 +263,7 @@ struct fvar
if (coords_length && *coords_length)
{
hb_array_t<const HBFixed> instanceCoords = instance->get_coordinates (axisCount)
- .sub_array (0, *coords_length);
+ .sub_array (0, coords_length);
for (unsigned int i = 0; i < instanceCoords.length; i++)
coords[i] = instanceCoords.arrayZ[i].to_float ();
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh
index 49b5532d40..539213c339 100644
--- a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh
@@ -399,7 +399,7 @@ struct gvar
get_offset (glyphCount) - get_offset (0)));
}
- /* GlyphVariationData not sanitized here; must be checked while accessing each glyph varation data */
+ /* GlyphVariationData not sanitized here; must be checked while accessing each glyph variation data */
bool sanitize (hb_sanitize_context_t *c) const
{ return sanitize_shallow (c); }
@@ -498,9 +498,9 @@ struct gvar
public:
struct accelerator_t
{
- void init (hb_face_t *face)
+ accelerator_t (hb_face_t *face)
{ table = hb_sanitize_context_t ().reference_table<gvar> (face); }
- void fini () { table.destroy (); }
+ ~accelerator_t () { table.destroy (); }
private:
struct x_getter { static float get (const contour_point_t &p) { return p.x; } };
@@ -698,7 +698,9 @@ no_more_gaps:
DEFINE_SIZE_MIN (20);
};
-struct gvar_accelerator_t : gvar::accelerator_t {};
+struct gvar_accelerator_t : gvar::accelerator_t {
+ gvar_accelerator_t (hb_face_t *face) : gvar::accelerator_t (face) {}
+};
} /* namespace OT */
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh
index 074b6a3785..e9d90352f0 100644
--- a/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh
@@ -177,9 +177,6 @@ struct hvarvvar_subset_plan_t
inner_maps.resize (var_store->get_sub_table_count ());
- for (unsigned int i = 0; i < inner_maps.length; i++)
- inner_maps[i].init ();
-
if (unlikely (!index_map_plans.length || !inner_sets.length || !inner_maps.length)) return;
bool retain_adv_map = false;
@@ -229,8 +226,8 @@ struct hvarvvar_subset_plan_t
for (unsigned int i = 0; i < inner_sets.length; i++)
hb_set_destroy (inner_sets[i]);
hb_set_destroy (adv_set);
- inner_maps.fini_deep ();
- index_map_plans.fini_deep ();
+ inner_maps.fini ();
+ index_map_plans.fini ();
}
hb_inc_bimap_t outer_map;
diff --git a/thirdparty/harfbuzz/src/hb-ot-var.cc b/thirdparty/harfbuzz/src/hb-ot-var.cc
index 6b42b45cd9..0376e26b4a 100644
--- a/thirdparty/harfbuzz/src/hb-ot-var.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-var.cc
@@ -303,6 +303,9 @@ hb_ot_var_normalize_variations (hb_face_t *face,
* values for the axis are mapped to the interval [-1,1], with the default
* axis value mapped to 0.
*
+ * The normalized values have 14 bits of fixed-point sub-integer precision as per
+ * OpenType specification.
+ *
* Any additional scaling defined in the face's `avar` table is also
* applied, as described at https://docs.microsoft.com/en-us/typography/opentype/spec/avar
*
diff --git a/thirdparty/harfbuzz/src/hb-ot-var.h b/thirdparty/harfbuzz/src/hb-ot-var.h
index ce201d3b4f..05147cc25e 100644
--- a/thirdparty/harfbuzz/src/hb-ot-var.h
+++ b/thirdparty/harfbuzz/src/hb-ot-var.h
@@ -109,7 +109,7 @@ typedef enum { /*< flags >*/
* @tag: The #hb_tag_t tag identifying the design variation of the axis
* @name_id: The `name` table Name ID that provides display names for the axis
* @flags: The #hb_ot_var_axis_flags_t flags for the axis
- * @min_value: The mininum value on the variation axis that the font covers
+ * @min_value: The minimum value on the variation axis that the font covers
* @default_value: The position on the variation axis corresponding to the font's defaults
* @max_value: The maximum value on the variation axis that the font covers
*
diff --git a/thirdparty/harfbuzz/src/hb-repacker.hh b/thirdparty/harfbuzz/src/hb-repacker.hh
index 5c46b4cccc..b1726d8beb 100644
--- a/thirdparty/harfbuzz/src/hb-repacker.hh
+++ b/thirdparty/harfbuzz/src/hb-repacker.hh
@@ -42,26 +42,13 @@ struct graph_t
{
struct vertex_t
{
- vertex_t () :
- distance (0),
- space (0),
- parents (),
- start (0),
- end (0),
- priority(0) {}
-
- void fini () {
- obj.fini ();
- parents.fini ();
- }
-
hb_serialize_context_t::object_t obj;
- int64_t distance;
- int64_t space;
+ int64_t distance = 0 ;
+ int64_t space = 0 ;
hb_vector_t<unsigned> parents;
- unsigned start;
- unsigned end;
- unsigned priority;
+ unsigned start = 0;
+ unsigned end = 0;
+ unsigned priority = 0;
bool is_shared () const
{
@@ -186,7 +173,7 @@ struct graph_t
~graph_t ()
{
- vertices_.fini_deep ();
+ vertices_.fini ();
}
bool in_error () const
@@ -309,7 +296,7 @@ struct graph_t
remap_all_obj_indices (id_map, &sorted_graph);
hb_swap (vertices_, sorted_graph);
- sorted_graph.fini_deep ();
+ sorted_graph.fini ();
}
/*
@@ -369,7 +356,7 @@ struct graph_t
remap_all_obj_indices (id_map, &sorted_graph);
hb_swap (vertices_, sorted_graph);
- sorted_graph.fini_deep ();
+ sorted_graph.fini ();
}
/*
@@ -402,11 +389,15 @@ struct graph_t
while (roots)
{
unsigned next = HB_SET_VALUE_INVALID;
+ if (unlikely (!check_success (!roots.in_error ()))) break;
if (!roots.next (&next)) break;
hb_set_t connected_roots;
find_connected_nodes (next, roots, visited, connected_roots);
+ if (unlikely (!check_success (!connected_roots.in_error ()))) break;
+
isolate_subgraph (connected_roots);
+ if (unlikely (!check_success (!connected_roots.in_error ()))) break;
unsigned next_space = this->next_space ();
num_roots_for_space_.push (0);
@@ -423,6 +414,8 @@ struct graph_t
// into the 32 bit space as needed, instead of using isolation.
}
+
+
return true;
}
@@ -865,7 +858,7 @@ struct graph_t
// Redundant ones are filtered out later on by the visited set.
// According to https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf
// for practical performance this is faster then using a more advanced queue
- // (such as a fibonaacci queue) with a fast decrease priority.
+ // (such as a fibonacci queue) with a fast decrease priority.
for (unsigned i = 0; i < vertices_.length; i++)
{
if (i == vertices_.length - 1)
@@ -1074,6 +1067,7 @@ struct graph_t
hb_set_t& visited,
hb_set_t& connected)
{
+ if (unlikely (!check_success (!visited.in_error ()))) return;
if (visited.has (start_idx)) return;
visited.add (start_idx);
diff --git a/thirdparty/harfbuzz/src/hb-serialize.hh b/thirdparty/harfbuzz/src/hb-serialize.hh
index 823c0be8b5..6615f033c5 100644
--- a/thirdparty/harfbuzz/src/hb-serialize.hh
+++ b/thirdparty/harfbuzz/src/hb-serialize.hh
@@ -279,7 +279,7 @@ struct hb_serialize_context_t
object_pool.release (obj);
}
- /* Set share to false when an object is unlikely sharable with others
+ /* Set share to false when an object is unlikely shareable with others
* so not worth an attempt, or a contiguous table is serialized as
* multiple consecutive objects in the reverse order so can't be shared.
*/
@@ -381,7 +381,7 @@ struct hb_serialize_context_t
// Adding a virtual link from object a to object b will ensure that object b is always packed after
// object a in the final serialized order.
//
- // This is useful in certain situtations where there needs to be a specific ordering in the
+ // This is useful in certain situations where there needs to be a specific ordering in the
// final serialization. Such as when platform bugs require certain orderings, or to provide
// guidance to the repacker for better offset overflow resolution.
void add_virtual_link (objidx_t objidx)
@@ -510,7 +510,7 @@ struct hb_serialize_context_t
{ return reinterpret_cast<Type *> (this->head); }
template <typename Type>
Type *start_embed (const Type &obj) const
- { return start_embed (hb_addressof (obj)); }
+ { return start_embed (std::addressof (obj)); }
bool err (hb_serialize_error_t err_type)
{
@@ -548,7 +548,7 @@ struct hb_serialize_context_t
}
template <typename Type>
Type *embed (const Type &obj)
- { return embed (hb_addressof (obj)); }
+ { return embed (std::addressof (obj)); }
template <typename Type, typename ...Ts> auto
_copy (const Type &src, hb_priority<1>, Ts&&... ds) HB_RETURN
@@ -595,19 +595,19 @@ struct hb_serialize_context_t
}
template <typename Type>
Type *extend_size (Type &obj, size_t size)
- { return extend_size (hb_addressof (obj), size); }
+ { return extend_size (std::addressof (obj), size); }
template <typename Type>
Type *extend_min (Type *obj) { return extend_size (obj, obj->min_size); }
template <typename Type>
- Type *extend_min (Type &obj) { return extend_min (hb_addressof (obj)); }
+ Type *extend_min (Type &obj) { return extend_min (std::addressof (obj)); }
template <typename Type, typename ...Ts>
Type *extend (Type *obj, Ts&&... ds)
{ return extend_size (obj, obj->get_size (std::forward<Ts> (ds)...)); }
template <typename Type, typename ...Ts>
Type *extend (Type &obj, Ts&&... ds)
- { return extend (hb_addressof (obj), std::forward<Ts> (ds)...); }
+ { return extend (std::addressof (obj), std::forward<Ts> (ds)...); }
/* Output routines. */
hb_bytes_t copy_bytes () const
diff --git a/thirdparty/harfbuzz/src/hb-style.cc b/thirdparty/harfbuzz/src/hb-style.cc
index f1b44cea53..c0c5c4832c 100644
--- a/thirdparty/harfbuzz/src/hb-style.cc
+++ b/thirdparty/harfbuzz/src/hb-style.cc
@@ -48,13 +48,12 @@ _hb_angle_to_ratio (float a)
{
return tanf (a * float (M_PI / 180.));
}
-#if 0
+
static inline float
_hb_ratio_to_angle (float r)
{
return atanf (r) * float (180. / M_PI);
}
-#endif
/**
* hb_style_get_value:
@@ -73,7 +72,8 @@ float
hb_style_get_value (hb_font_t *font, hb_style_tag_t style_tag)
{
if (unlikely (style_tag == HB_STYLE_TAG_SLANT_RATIO))
- return _hb_angle_to_ratio (hb_style_get_value (font, HB_STYLE_TAG_SLANT_ANGLE));
+ return _hb_angle_to_ratio (hb_style_get_value (font, HB_STYLE_TAG_SLANT_ANGLE))
+ + font->slant;
hb_face_t *face = font->face;
@@ -109,7 +109,14 @@ hb_style_get_value (hb_font_t *font, hb_style_tag_t style_tag)
: 12.f;
}
case HB_STYLE_TAG_SLANT_ANGLE:
- return face->table.post->table->italicAngle.to_float ();
+ {
+ float angle = face->table.post->table->italicAngle.to_float ();
+
+ if (font->slant)
+ angle = _hb_ratio_to_angle (font->slant + _hb_angle_to_ratio (angle));
+
+ return angle;
+ }
case HB_STYLE_TAG_WIDTH:
return face->table.OS2->has_data ()
? face->table.OS2->get_width ()
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff-common.hh b/thirdparty/harfbuzz/src/hb-subset-cff-common.hh
index 7fd96ca86d..18657705fa 100644
--- a/thirdparty/harfbuzz/src/hb-subset-cff-common.hh
+++ b/thirdparty/harfbuzz/src/hb-subset-cff-common.hh
@@ -275,60 +275,36 @@ struct subr_flattener_t
struct subr_closures_t
{
- subr_closures_t () : valid (false), global_closure (nullptr)
- { local_closures.init (); }
-
- void init (unsigned int fd_count)
+ subr_closures_t (unsigned int fd_count) : valid (false), global_closure (), local_closures ()
{
valid = true;
- global_closure = hb_set_create ();
- if (global_closure == hb_set_get_empty ())
- valid = false;
if (!local_closures.resize (fd_count))
valid = false;
-
- for (unsigned int i = 0; i < local_closures.length; i++)
- {
- local_closures[i] = hb_set_create ();
- if (local_closures[i] == hb_set_get_empty ())
- valid = false;
- }
- }
-
- void fini ()
- {
- hb_set_destroy (global_closure);
- for (unsigned int i = 0; i < local_closures.length; i++)
- hb_set_destroy (local_closures[i]);
- local_closures.fini ();
}
void reset ()
{
- hb_set_clear (global_closure);
+ global_closure.clear();
for (unsigned int i = 0; i < local_closures.length; i++)
- hb_set_clear (local_closures[i]);
+ local_closures[i].clear();
}
bool is_valid () const { return valid; }
bool valid;
- hb_set_t *global_closure;
- hb_vector_t<hb_set_t *> local_closures;
+ hb_set_t global_closure;
+ hb_vector_t<hb_set_t> local_closures;
};
struct parsed_cs_op_t : op_str_t
{
void init (unsigned int subr_num_ = 0)
{
- op_str_t::init ();
subr_num = subr_num_;
drop_flag = false;
keep_flag = false;
skip_flag = false;
}
- void fini () { op_str_t::fini (); }
-
bool for_drop () const { return drop_flag; }
void set_drop () { if (!for_keep ()) drop_flag = true; }
@@ -416,16 +392,6 @@ struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t>
struct parsed_cs_str_vec_t : hb_vector_t<parsed_cs_str_t>
{
- void init (unsigned int len_ = 0)
- {
- SUPER::init ();
- if (unlikely (!resize (len_)))
- return;
- for (unsigned int i = 0; i < length; i++)
- (*this)[i].init ();
- }
- void fini () { SUPER::fini_deep (); }
-
private:
typedef hb_vector_t<parsed_cs_str_t> SUPER;
};
@@ -496,7 +462,7 @@ struct subr_subset_param_t
struct subr_remap_t : hb_inc_bimap_t
{
- void create (hb_set_t *closure)
+ void create (const hb_set_t *closure)
{
/* create a remapping of subroutine numbers from old to new.
* no optimization based on usage counts. fonttools doesn't appear doing that either.
@@ -526,19 +492,9 @@ struct subr_remap_t : hb_inc_bimap_t
struct subr_remaps_t
{
- subr_remaps_t ()
+ subr_remaps_t (unsigned int fdCount)
{
- global_remap.init ();
- local_remaps.init ();
- }
-
- ~subr_remaps_t () { fini (); }
-
- void init (unsigned int fdCount)
- {
- if (unlikely (!local_remaps.resize (fdCount))) return;
- for (unsigned int i = 0; i < fdCount; i++)
- local_remaps[i].init ();
+ local_remaps.resize (fdCount);
}
bool in_error()
@@ -548,15 +504,9 @@ struct subr_remaps_t
void create (subr_closures_t& closures)
{
- global_remap.create (closures.global_closure);
+ global_remap.create (&closures.global_closure);
for (unsigned int i = 0; i < local_remaps.length; i++)
- local_remaps[i].create (closures.local_closures[i]);
- }
-
- void fini ()
- {
- global_remap.fini ();
- local_remaps.fini_deep ();
+ local_remaps[i].create (&closures.local_closures[i]);
}
subr_remap_t global_remap;
@@ -567,21 +517,8 @@ template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typena
struct subr_subsetter_t
{
subr_subsetter_t (ACC &acc_, const hb_subset_plan_t *plan_)
- : acc (acc_), plan (plan_)
- {
- parsed_charstrings.init ();
- parsed_global_subrs.init ();
- parsed_local_subrs.init ();
- }
-
- ~subr_subsetter_t ()
- {
- closures.fini ();
- remaps.fini ();
- parsed_charstrings.fini_deep ();
- parsed_global_subrs.fini_deep ();
- parsed_local_subrs.fini_deep ();
- }
+ : acc (acc_), plan (plan_), closures(acc_.fdCount), remaps(acc_.fdCount)
+ {}
/* Subroutine subsetting with --no-desubroutinize runs in phases:
*
@@ -599,11 +536,8 @@ struct subr_subsetter_t
*/
bool subset (void)
{
- closures.init (acc.fdCount);
- remaps.init (acc.fdCount);
-
- parsed_charstrings.init (plan->num_output_glyphs ());
- parsed_global_subrs.init (acc.globalSubrs->count);
+ parsed_charstrings.resize (plan->num_output_glyphs ());
+ parsed_global_subrs.resize (acc.globalSubrs->count);
if (unlikely (remaps.in_error()
|| parsed_charstrings.in_error ()
@@ -615,7 +549,7 @@ struct subr_subsetter_t
for (unsigned int i = 0; i < acc.fdCount; i++)
{
- parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count);
+ parsed_local_subrs[i].resize (acc.privateDicts[i].localSubrs->count);
if (unlikely (parsed_local_subrs[i].in_error ())) return false;
}
if (unlikely (!closures.valid))
@@ -638,7 +572,7 @@ struct subr_subsetter_t
subr_subset_param_t param;
param.init (&parsed_charstrings[i],
&parsed_global_subrs, &parsed_local_subrs[fd],
- closures.global_closure, closures.local_closures[fd],
+ &closures.global_closure, &closures.local_closures[fd],
plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
if (unlikely (!interp.interpret (param)))
@@ -662,7 +596,7 @@ struct subr_subsetter_t
subr_subset_param_t param;
param.init (&parsed_charstrings[i],
&parsed_global_subrs, &parsed_local_subrs[fd],
- closures.global_closure, closures.local_closures[fd],
+ &closures.global_closure, &closures.local_closures[fd],
plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
drop_hints_param_t drop;
@@ -687,7 +621,7 @@ struct subr_subsetter_t
subr_subset_param_t param;
param.init (&parsed_charstrings[i],
&parsed_global_subrs, &parsed_local_subrs[fd],
- closures.global_closure, closures.local_closures[fd],
+ &closures.global_closure, &closures.local_closures[fd],
plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
collect_subr_refs_in_str (parsed_charstrings[i], param);
}
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff1.cc b/thirdparty/harfbuzz/src/hb-subset-cff1.cc
index b4e24122c9..35fecd67bc 100644
--- a/thirdparty/harfbuzz/src/hb-subset-cff1.cc
+++ b/thirdparty/harfbuzz/src/hb-subset-cff1.cc
@@ -362,43 +362,11 @@ struct cff1_subr_subsetter_t : subr_subsetter_t<cff1_subr_subsetter_t, CFF1Subrs
struct cff_subset_plan {
cff_subset_plan ()
- : info (),
- orig_fdcount (0),
- subset_fdcount (1),
- subset_fdselect_format (0),
- drop_hints (false),
- desubroutinize(false)
{
- topdict_mod.init ();
- subset_fdselect_ranges.init ();
- fdmap.init ();
- subset_charstrings.init ();
- subset_globalsubrs.init ();
- subset_localsubrs.init ();
- fontdicts_mod.init ();
- subset_enc_code_ranges.init ();
- subset_enc_supp_codes.init ();
- subset_charset_ranges.init ();
- sidmap.init ();
for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
topDictModSIDs[i] = CFF_UNDEF_SID;
}
- ~cff_subset_plan ()
- {
- topdict_mod.fini ();
- subset_fdselect_ranges.fini ();
- fdmap.fini ();
- subset_charstrings.fini_deep ();
- subset_globalsubrs.fini_deep ();
- subset_localsubrs.fini_deep ();
- fontdicts_mod.fini ();
- subset_enc_code_ranges.fini ();
- subset_enc_supp_codes.fini ();
- subset_charset_ranges.fini ();
- sidmap.fini ();
- }
-
void plan_subset_encoding (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
{
const Encoding *encoding = acc.encoding;
@@ -672,9 +640,9 @@ struct cff_subset_plan {
cff1_sub_table_info_t info;
unsigned int num_glyphs;
- unsigned int orig_fdcount;
- unsigned int subset_fdcount;
- unsigned int subset_fdselect_format;
+ unsigned int orig_fdcount = 0;
+ unsigned int subset_fdcount = 1;
+ unsigned int subset_fdselect_format = 0;
hb_vector_t<code_pair_t> subset_fdselect_ranges;
/* font dict index remap table from fullset FDArray to subset FDArray.
@@ -686,7 +654,7 @@ struct cff_subset_plan {
hb_vector_t<str_buff_vec_t> subset_localsubrs;
hb_vector_t<cff1_font_dict_values_mod_t> fontdicts_mod;
- bool drop_hints;
+ bool drop_hints = false;
bool gid_renum;
bool subset_encoding;
@@ -702,7 +670,7 @@ struct cff_subset_plan {
remap_sid_t sidmap;
unsigned int topDictModSIDs[name_dict_values_t::ValCount];
- bool desubroutinize;
+ bool desubroutinize = false;
};
static bool _serialize_cff1 (hb_serialize_context_t *c,
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff2.cc b/thirdparty/harfbuzz/src/hb-subset-cff2.cc
index 896ae64016..92dd6b1d2c 100644
--- a/thirdparty/harfbuzz/src/hb-subset-cff2.cc
+++ b/thirdparty/harfbuzz/src/hb-subset-cff2.cc
@@ -233,29 +233,6 @@ struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs
};
struct cff2_subset_plan {
- cff2_subset_plan ()
- : orig_fdcount (0),
- subset_fdcount(1),
- subset_fdselect_size (0),
- subset_fdselect_format (0),
- drop_hints (false),
- desubroutinize (false)
- {
- subset_fdselect_ranges.init ();
- fdmap.init ();
- subset_charstrings.init ();
- subset_globalsubrs.init ();
- subset_localsubrs.init ();
- }
-
- ~cff2_subset_plan ()
- {
- subset_fdselect_ranges.fini ();
- fdmap.fini ();
- subset_charstrings.fini_deep ();
- subset_globalsubrs.fini_deep ();
- subset_localsubrs.fini_deep ();
- }
bool create (const OT::cff2::accelerator_subset_t &acc,
hb_subset_plan_t *plan)
@@ -320,10 +297,10 @@ struct cff2_subset_plan {
cff2_sub_table_info_t info;
- unsigned int orig_fdcount;
- unsigned int subset_fdcount;
- unsigned int subset_fdselect_size;
- unsigned int subset_fdselect_format;
+ unsigned int orig_fdcount = 0;
+ unsigned int subset_fdcount = 1;
+ unsigned int subset_fdselect_size = 0;
+ unsigned int subset_fdselect_format = 0;
hb_vector_t<code_pair_t> subset_fdselect_ranges;
hb_inc_bimap_t fdmap;
@@ -332,8 +309,8 @@ struct cff2_subset_plan {
str_buff_vec_t subset_globalsubrs;
hb_vector_t<str_buff_vec_t> subset_localsubrs;
- bool drop_hints;
- bool desubroutinize;
+ bool drop_hints = false;
+ bool desubroutinize = false;
};
static bool _serialize_cff2 (hb_serialize_context_t *c,
@@ -473,12 +450,8 @@ _hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc,
bool
hb_subset_cff2 (hb_subset_context_t *c)
{
- OT::cff2::accelerator_subset_t acc;
- acc.init (c->plan->source);
- bool result = likely (acc.is_valid ()) && _hb_subset_cff2 (acc, c);
- acc.fini ();
-
- return result;
+ OT::cff2::accelerator_subset_t acc (c->plan->source);
+ return acc.is_valid () && _hb_subset_cff2 (acc, c);
}
#endif
diff --git a/thirdparty/harfbuzz/src/hb-subset-plan.cc b/thirdparty/harfbuzz/src/hb-subset-plan.cc
index 883ab82093..af4fcb8137 100644
--- a/thirdparty/harfbuzz/src/hb-subset-plan.cc
+++ b/thirdparty/harfbuzz/src/hb-subset-plan.cc
@@ -228,10 +228,8 @@ _cmap_closure (hb_face_t *face,
const hb_set_t *unicodes,
hb_set_t *glyphset)
{
- OT::cmap::accelerator_t cmap;
- cmap.init (face);
+ OT::cmap::accelerator_t cmap (face);
cmap.table->closure_glyphs (unicodes, glyphset);
- cmap.fini ();
}
static void _colr_closure (hb_face_t *face,
@@ -239,8 +237,7 @@ static void _colr_closure (hb_face_t *face,
hb_map_t *palettes_map,
hb_set_t *glyphs_colred)
{
- OT::COLR::accelerator_t colr;
- colr.init (face);
+ OT::COLR::accelerator_t colr (face);
if (!colr.is_valid ()) return;
unsigned iteration_count = 0;
@@ -263,7 +260,6 @@ static void _colr_closure (hb_face_t *face,
colr.closure_V0palette_indices (glyphs_colred, &palette_indices);
_remap_indexes (&layer_indices, layers_map);
_remap_palette_indexes (&palette_indices, palettes_map);
- colr.fini ();
}
static inline void
@@ -294,8 +290,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
const hb_set_t *glyphs,
hb_subset_plan_t *plan)
{
- OT::cmap::accelerator_t cmap;
- cmap.init (plan->source);
+ OT::cmap::accelerator_t cmap (plan->source);
constexpr static const int size_threshold = 4096;
@@ -343,8 +338,6 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
+ plan->codepoint_to_glyph->keys () | hb_sink (plan->unicodes);
+ plan->codepoint_to_glyph->values () | hb_sink (plan->_glyphset_gsub);
-
- cmap.fini ();
}
static void
@@ -353,13 +346,9 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
bool close_over_gpos,
bool close_over_gdef)
{
- OT::glyf::accelerator_t glyf;
-#ifndef HB_NO_SUBSET_CFF
- OT::cff1::accelerator_t cff;
-#endif
- glyf.init (plan->source);
+ OT::glyf::accelerator_t glyf (plan->source);
#ifndef HB_NO_SUBSET_CFF
- cff.init (plan->source);
+ OT::cff1::accelerator_t cff (plan->source);
#endif
plan->_glyphset_gsub->add (0); // Not-def
@@ -419,11 +408,6 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
plan->layout_variation_indices,
plan->layout_variation_idx_map);
#endif
-
-#ifndef HB_NO_SUBSET_CFF
- cff.fini ();
-#endif
- glyf.fini ();
}
static void
diff --git a/thirdparty/harfbuzz/src/hb-uniscribe.cc b/thirdparty/harfbuzz/src/hb-uniscribe.cc
index 0e5a114f7d..50f71ce9ce 100644
--- a/thirdparty/harfbuzz/src/hb-uniscribe.cc
+++ b/thirdparty/harfbuzz/src/hb-uniscribe.cc
@@ -878,7 +878,8 @@ retry:
if (backward)
hb_buffer_reverse (buffer);
- buffer->clear_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK);
+ buffer->clear_glyph_flags ();
+ buffer->unsafe_to_break ();
/* Wow, done! */
return true;
diff --git a/thirdparty/harfbuzz/src/hb-vector.hh b/thirdparty/harfbuzz/src/hb-vector.hh
index b0a1e5e966..6c7d32e49d 100644
--- a/thirdparty/harfbuzz/src/hb-vector.hh
+++ b/thirdparty/harfbuzz/src/hb-vector.hh
@@ -32,11 +32,14 @@
#include "hb-null.hh"
-template <typename Type>
-struct hb_vector_t
+template <typename Type,
+ bool sorted=false>
+struct hb_vector_t : std::conditional<sorted, hb_vector_t<Type, false>, hb_empty_t>::type
{
typedef Type item_t;
static constexpr unsigned item_size = hb_static_size (Type);
+ using array_t = typename std::conditional<sorted, hb_sorted_array_t<Type>, hb_array_t<Type>>::type;
+ using c_array_t = typename std::conditional<sorted, hb_sorted_array_t<const Type>, hb_array_t<const Type>>::type;
hb_vector_t () = default;
hb_vector_t (std::initializer_list<Type> lst) : hb_vector_t ()
@@ -82,16 +85,10 @@ struct hb_vector_t
void fini ()
{
+ shrink_vector (0);
hb_free (arrayZ);
init ();
}
- void fini_deep ()
- {
- unsigned int count = length;
- for (unsigned int i = 0; i < count; i++)
- arrayZ[i].fini ();
- fini ();
- }
void reset ()
{
@@ -152,24 +149,24 @@ struct hb_vector_t
template <typename T>
hb_vector_t& operator << (T&& v) { push (std::forward<T> (v)); return *this; }
- hb_array_t< Type> as_array () { return hb_array (arrayZ, length); }
- hb_array_t<const Type> as_array () const { return hb_array (arrayZ, length); }
+ array_t as_array () { return hb_array (arrayZ, length); }
+ c_array_t as_array () const { return hb_array (arrayZ, length); }
/* Iterator. */
- typedef hb_array_t<const Type> iter_t;
- typedef hb_array_t< Type> writer_t;
+ typedef c_array_t iter_t;
+ typedef array_t writer_t;
iter_t iter () const { return as_array (); }
writer_t writer () { return as_array (); }
operator iter_t () const { return iter (); }
operator writer_t () { return writer (); }
- hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
+ c_array_t sub_array (unsigned int start_offset, unsigned int count) const
{ return as_array ().sub_array (start_offset, count); }
- hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
+ c_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
{ return as_array ().sub_array (start_offset, count); }
- hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
+ array_t sub_array (unsigned int start_offset, unsigned int count)
{ return as_array ().sub_array (start_offset, count); }
- hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
+ array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
{ return as_array ().sub_array (start_offset, count); }
hb_sorted_array_t<Type> as_sorted_array ()
@@ -192,6 +189,7 @@ struct hb_vector_t
template <typename T>
Type *push (T&& v)
{
+ /* TODO Emplace? */
Type *p = push ();
if (p == &Crap (Type))
// If push failed to allocate then don't copy v, since this may cause
@@ -204,6 +202,92 @@ struct hb_vector_t
bool in_error () const { return allocated < 0; }
+ template <typename T = Type,
+ hb_enable_if (std::is_trivially_copy_assignable<T>::value)>
+ Type *
+ realloc_vector (unsigned new_allocated)
+ {
+ return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type));
+ }
+ template <typename T = Type,
+ hb_enable_if (!std::is_trivially_copy_assignable<T>::value)>
+ Type *
+ realloc_vector (unsigned new_allocated)
+ {
+ Type *new_array = (Type *) hb_malloc (new_allocated * sizeof (Type));
+ if (likely (new_array))
+ {
+ for (unsigned i = 0; i < length; i++)
+ new (std::addressof (new_array[i])) Type ();
+ for (unsigned i = 0; i < (unsigned) length; i++)
+ new_array[i] = std::move (arrayZ[i]);
+ unsigned old_length = length;
+ shrink_vector (0);
+ length = old_length;
+ hb_free (arrayZ);
+ }
+ return new_array;
+ }
+
+ template <typename T = Type,
+ hb_enable_if (std::is_trivially_constructible<T>::value ||
+ !std::is_default_constructible<T>::value)>
+ void
+ grow_vector (unsigned size)
+ {
+ memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
+ length = size;
+ }
+ template <typename T = Type,
+ hb_enable_if (!std::is_trivially_constructible<T>::value &&
+ std::is_default_constructible<T>::value)>
+ void
+ grow_vector (unsigned size)
+ {
+ while (length < size)
+ {
+ length++;
+ new (std::addressof (arrayZ[length - 1])) Type ();
+ }
+ }
+
+ template <typename T = Type,
+ hb_enable_if (std::is_trivially_destructible<T>::value)>
+ void
+ shrink_vector (unsigned size)
+ {
+ length = size;
+ }
+ template <typename T = Type,
+ hb_enable_if (!std::is_trivially_destructible<T>::value)>
+ void
+ shrink_vector (unsigned size)
+ {
+ while ((unsigned) length > size)
+ {
+ arrayZ[(unsigned) length - 1].~Type ();
+ length--;
+ }
+ }
+
+ template <typename T = Type,
+ hb_enable_if (std::is_trivially_copy_assignable<T>::value)>
+ void
+ shift_down_vector (unsigned i)
+ {
+ memmove (static_cast<void *> (&arrayZ[i - 1]),
+ static_cast<void *> (&arrayZ[i]),
+ (length - i) * sizeof (Type));
+ }
+ template <typename T = Type,
+ hb_enable_if (!std::is_trivially_copy_assignable<T>::value)>
+ void
+ shift_down_vector (unsigned i)
+ {
+ for (; i < length; i++)
+ arrayZ[i - 1] = std::move (arrayZ[i]);
+ }
+
/* Allocate for size but don't adjust length. */
bool alloc (unsigned int size)
{
@@ -225,7 +309,7 @@ struct hb_vector_t
(new_allocated < (unsigned) allocated) ||
hb_unsigned_mul_overflows (new_allocated, sizeof (Type));
if (likely (!overflows))
- new_array = (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type));
+ new_array = realloc_vector (new_allocated);
if (unlikely (!new_array))
{
@@ -246,7 +330,9 @@ struct hb_vector_t
return false;
if (size > length)
- memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
+ grow_vector (size);
+ else if (size < length)
+ shrink_vector (size);
length = size;
return true;
@@ -255,48 +341,38 @@ struct hb_vector_t
Type pop ()
{
if (!length) return Null (Type);
- return std::move (arrayZ[--length]); /* Does this move actually work? */
+ Type v = std::move (arrayZ[length - 1]);
+ arrayZ[length - 1].~Type ();
+ length--;
+ return v;
}
void remove (unsigned int i)
{
if (unlikely (i >= length))
return;
- memmove (static_cast<void *> (&arrayZ[i]),
- static_cast<void *> (&arrayZ[i + 1]),
- (length - i - 1) * sizeof (Type));
+ arrayZ[i].~Type ();
+ shift_down_vector (i + 1);
length--;
}
void shrink (int size_)
{
unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
- if (size < length)
- length = size;
- }
+ if (size >= length)
+ return;
- template <typename T>
- Type *find (T v)
- {
- for (unsigned int i = 0; i < length; i++)
- if (arrayZ[i] == v)
- return &arrayZ[i];
- return nullptr;
- }
- template <typename T>
- const Type *find (T v) const
- {
- for (unsigned int i = 0; i < length; i++)
- if (arrayZ[i] == v)
- return &arrayZ[i];
- return nullptr;
+ shrink_vector (size);
}
+
+ /* Sorting API. */
void qsort (int (*cmp)(const void*, const void*))
{ as_array ().qsort (cmp); }
void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1)
{ as_array ().qsort (start, end); }
+ /* Unsorted search API. */
template <typename T>
Type *lsearch (const T &x, Type *not_found = nullptr)
{ return as_array ().lsearch (x, not_found); }
@@ -306,47 +382,25 @@ struct hb_vector_t
template <typename T>
bool lfind (const T &x, unsigned *pos = nullptr) const
{ return as_array ().lfind (x, pos); }
-};
-template <typename Type>
-struct hb_sorted_vector_t : hb_vector_t<Type>
-{
- hb_sorted_vector_t () = default;
- ~hb_sorted_vector_t () = default;
- hb_sorted_vector_t (hb_sorted_vector_t& o) = default;
- hb_sorted_vector_t (hb_sorted_vector_t &&o) = default;
- hb_sorted_vector_t (std::initializer_list<Type> lst) : hb_vector_t<Type> (lst) {}
- template <typename Iterable,
- hb_requires (hb_is_iterable (Iterable))>
- hb_sorted_vector_t (const Iterable &o) : hb_vector_t<Type> (o) {}
- hb_sorted_vector_t& operator = (const hb_sorted_vector_t &o) = default;
- hb_sorted_vector_t& operator = (hb_sorted_vector_t &&o) = default;
- friend void swap (hb_sorted_vector_t& a, hb_sorted_vector_t& b)
- { hb_swap ((hb_vector_t<Type>&) (a), (hb_vector_t<Type>&) (b)); }
-
- hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->length); }
- hb_sorted_array_t<const Type> as_array () const { return hb_sorted_array (this->arrayZ, this->length); }
-
- /* Iterator. */
- typedef hb_sorted_array_t<const Type> const_iter_t;
- typedef hb_sorted_array_t< Type> iter_t;
- const_iter_t iter () const { return as_array (); }
- const_iter_t citer () const { return as_array (); }
- iter_t iter () { return as_array (); }
- operator iter_t () { return iter (); }
- operator const_iter_t () const { return iter (); }
-
- template <typename T>
+ /* Sorted search API. */
+ template <typename T,
+ bool Sorted=sorted, hb_enable_if (Sorted)>
Type *bsearch (const T &x, Type *not_found = nullptr)
{ return as_array ().bsearch (x, not_found); }
- template <typename T>
+ template <typename T,
+ bool Sorted=sorted, hb_enable_if (Sorted)>
const Type *bsearch (const T &x, const Type *not_found = nullptr) const
{ return as_array ().bsearch (x, not_found); }
- template <typename T>
+ template <typename T,
+ bool Sorted=sorted, hb_enable_if (Sorted)>
bool bfind (const T &x, unsigned int *i = nullptr,
hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE,
unsigned int to_store = (unsigned int) -1) const
{ return as_array ().bfind (x, i, not_found, to_store); }
};
+template <typename Type>
+using hb_sorted_vector_t = hb_vector_t<Type, true>;
+
#endif /* HB_VECTOR_HH */
diff --git a/thirdparty/harfbuzz/src/hb-version.h b/thirdparty/harfbuzz/src/hb-version.h
index 52b124b745..91ccb3dcde 100644
--- a/thirdparty/harfbuzz/src/hb-version.h
+++ b/thirdparty/harfbuzz/src/hb-version.h
@@ -47,20 +47,20 @@ HB_BEGIN_DECLS
*
* The minor component of the library version available at compile-time.
*/
-#define HB_VERSION_MINOR 2
+#define HB_VERSION_MINOR 3
/**
* HB_VERSION_MICRO:
*
* The micro component of the library version available at compile-time.
*/
-#define HB_VERSION_MICRO 0
+#define HB_VERSION_MICRO 1
/**
* HB_VERSION_STRING:
*
* A string literal containing the library version available at compile-time.
*/
-#define HB_VERSION_STRING "3.2.0"
+#define HB_VERSION_STRING "3.3.1"
/**
* HB_VERSION_ATLEAST:
diff --git a/thirdparty/libwebp/AUTHORS b/thirdparty/libwebp/AUTHORS
index 30abde0326..8307c2099d 100644
--- a/thirdparty/libwebp/AUTHORS
+++ b/thirdparty/libwebp/AUTHORS
@@ -32,6 +32,7 @@ Contributors:
- Pascal Massimino (pascal dot massimino at gmail dot com)
- Paweł Hajdan, Jr (phajdan dot jr at chromium dot org)
- Pierre Joye (pierre dot php at gmail dot com)
+- Roberto Alanis (alanisbaez at google dot com)
- Sam Clegg (sbc at chromium dot org)
- Scott Hancher (seh at google dot com)
- Scott LaVarnway (slavarnway at google dot com)
diff --git a/thirdparty/libwebp/src/dec/vp8_dec.c b/thirdparty/libwebp/src/dec/vp8_dec.c
index 5f405e4c2a..2003935ec4 100644
--- a/thirdparty/libwebp/src/dec/vp8_dec.c
+++ b/thirdparty/libwebp/src/dec/vp8_dec.c
@@ -403,7 +403,7 @@ static const uint8_t kZigzag[16] = {
0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
};
-// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
+// See section 13-2: https://datatracker.ietf.org/doc/html/rfc6386#section-13.2
static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
int v;
if (!VP8GetBit(br, p[3], "coeffs")) {
diff --git a/thirdparty/libwebp/src/dec/vp8i_dec.h b/thirdparty/libwebp/src/dec/vp8i_dec.h
index 20526a87c4..9af22f8cc6 100644
--- a/thirdparty/libwebp/src/dec/vp8i_dec.h
+++ b/thirdparty/libwebp/src/dec/vp8i_dec.h
@@ -32,7 +32,7 @@ extern "C" {
// version numbers
#define DEC_MAJ_VERSION 1
#define DEC_MIN_VERSION 2
-#define DEC_REV_VERSION 1
+#define DEC_REV_VERSION 2
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
// Constraints are: We need to store one 16x16 block of luma samples (y),
diff --git a/thirdparty/libwebp/src/dec/vp8l_dec.c b/thirdparty/libwebp/src/dec/vp8l_dec.c
index 73c3b54fff..78db014030 100644
--- a/thirdparty/libwebp/src/dec/vp8l_dec.c
+++ b/thirdparty/libwebp/src/dec/vp8l_dec.c
@@ -84,7 +84,7 @@ static const uint8_t kCodeToPlane[CODE_TO_PLANE_CODES] = {
// to 256 (green component values) + 24 (length prefix values)
// + color_cache_size (between 0 and 2048).
// All values computed for 8-bit first level lookup with Mark Adler's tool:
-// http://www.hdfgroup.org/ftp/lib-external/zlib/zlib-1.2.5/examples/enough.c
+// https://github.com/madler/zlib/blob/v1.2.5/examples/enough.c
#define FIXED_TABLE_SIZE (630 * 3 + 410)
static const uint16_t kTableSize[12] = {
FIXED_TABLE_SIZE + 654,
diff --git a/thirdparty/libwebp/src/demux/anim_decode.c b/thirdparty/libwebp/src/demux/anim_decode.c
index 2bf4dcffe0..e077ffb536 100644
--- a/thirdparty/libwebp/src/demux/anim_decode.c
+++ b/thirdparty/libwebp/src/demux/anim_decode.c
@@ -23,6 +23,14 @@
#define NUM_CHANNELS 4
+// Channel extraction from a uint32_t representation of a uint8_t RGBA/BGRA
+// buffer.
+#ifdef WORDS_BIGENDIAN
+#define CHANNEL_SHIFT(i) (24 - (i) * 8)
+#else
+#define CHANNEL_SHIFT(i) ((i) * 8)
+#endif
+
typedef void (*BlendRowFunc)(uint32_t* const, const uint32_t* const, int);
static void BlendPixelRowNonPremult(uint32_t* const src,
const uint32_t* const dst, int num_pixels);
@@ -209,35 +217,35 @@ static uint8_t BlendChannelNonPremult(uint32_t src, uint8_t src_a,
const uint8_t dst_channel = (dst >> shift) & 0xff;
const uint32_t blend_unscaled = src_channel * src_a + dst_channel * dst_a;
assert(blend_unscaled < (1ULL << 32) / scale);
- return (blend_unscaled * scale) >> 24;
+ return (blend_unscaled * scale) >> CHANNEL_SHIFT(3);
}
// Blend 'src' over 'dst' assuming they are NOT pre-multiplied by alpha.
static uint32_t BlendPixelNonPremult(uint32_t src, uint32_t dst) {
- const uint8_t src_a = (src >> 24) & 0xff;
+ const uint8_t src_a = (src >> CHANNEL_SHIFT(3)) & 0xff;
if (src_a == 0) {
return dst;
} else {
- const uint8_t dst_a = (dst >> 24) & 0xff;
+ const uint8_t dst_a = (dst >> CHANNEL_SHIFT(3)) & 0xff;
// This is the approximate integer arithmetic for the actual formula:
// dst_factor_a = (dst_a * (255 - src_a)) / 255.
const uint8_t dst_factor_a = (dst_a * (256 - src_a)) >> 8;
const uint8_t blend_a = src_a + dst_factor_a;
const uint32_t scale = (1UL << 24) / blend_a;
- const uint8_t blend_r =
- BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 0);
- const uint8_t blend_g =
- BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 8);
- const uint8_t blend_b =
- BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 16);
+ const uint8_t blend_r = BlendChannelNonPremult(
+ src, src_a, dst, dst_factor_a, scale, CHANNEL_SHIFT(0));
+ const uint8_t blend_g = BlendChannelNonPremult(
+ src, src_a, dst, dst_factor_a, scale, CHANNEL_SHIFT(1));
+ const uint8_t blend_b = BlendChannelNonPremult(
+ src, src_a, dst, dst_factor_a, scale, CHANNEL_SHIFT(2));
assert(src_a + dst_factor_a < 256);
- return (blend_r << 0) |
- (blend_g << 8) |
- (blend_b << 16) |
- ((uint32_t)blend_a << 24);
+ return ((uint32_t)blend_r << CHANNEL_SHIFT(0)) |
+ ((uint32_t)blend_g << CHANNEL_SHIFT(1)) |
+ ((uint32_t)blend_b << CHANNEL_SHIFT(2)) |
+ ((uint32_t)blend_a << CHANNEL_SHIFT(3));
}
}
@@ -247,7 +255,7 @@ static void BlendPixelRowNonPremult(uint32_t* const src,
const uint32_t* const dst, int num_pixels) {
int i;
for (i = 0; i < num_pixels; ++i) {
- const uint8_t src_alpha = (src[i] >> 24) & 0xff;
+ const uint8_t src_alpha = (src[i] >> CHANNEL_SHIFT(3)) & 0xff;
if (src_alpha != 0xff) {
src[i] = BlendPixelNonPremult(src[i], dst[i]);
}
@@ -264,7 +272,7 @@ static WEBP_INLINE uint32_t ChannelwiseMultiply(uint32_t pix, uint32_t scale) {
// Blend 'src' over 'dst' assuming they are pre-multiplied by alpha.
static uint32_t BlendPixelPremult(uint32_t src, uint32_t dst) {
- const uint8_t src_a = (src >> 24) & 0xff;
+ const uint8_t src_a = (src >> CHANNEL_SHIFT(3)) & 0xff;
return src + ChannelwiseMultiply(dst, 256 - src_a);
}
@@ -274,7 +282,7 @@ static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
int num_pixels) {
int i;
for (i = 0; i < num_pixels; ++i) {
- const uint8_t src_alpha = (src[i] >> 24) & 0xff;
+ const uint8_t src_alpha = (src[i] >> CHANNEL_SHIFT(3)) & 0xff;
if (src_alpha != 0xff) {
src[i] = BlendPixelPremult(src[i], dst[i]);
}
diff --git a/thirdparty/libwebp/src/demux/demux.c b/thirdparty/libwebp/src/demux/demux.c
index 547a7725de..f04a2b8450 100644
--- a/thirdparty/libwebp/src/demux/demux.c
+++ b/thirdparty/libwebp/src/demux/demux.c
@@ -25,7 +25,7 @@
#define DMUX_MAJ_VERSION 1
#define DMUX_MIN_VERSION 2
-#define DMUX_REV_VERSION 1
+#define DMUX_REV_VERSION 2
typedef struct {
size_t start_; // start location of the data
diff --git a/thirdparty/libwebp/src/dsp/dsp.h b/thirdparty/libwebp/src/dsp/dsp.h
index 513e159bb3..c4f57e4d5b 100644
--- a/thirdparty/libwebp/src/dsp/dsp.h
+++ b/thirdparty/libwebp/src/dsp/dsp.h
@@ -119,7 +119,12 @@ extern "C" {
#define WEBP_USE_NEON
#endif
-#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
+// Note: ARM64 is supported in Visual Studio 2017, but requires the direct
+// inclusion of arm64_neon.h; Visual Studio 2019 includes this file in
+// arm_neon.h.
+#if defined(_MSC_VER) && \
+ ((_MSC_VER >= 1700 && defined(_M_ARM)) || \
+ (_MSC_VER >= 1920 && defined(_M_ARM64)))
#define WEBP_USE_NEON
#define WEBP_USE_INTRINSICS
#endif
diff --git a/thirdparty/libwebp/src/dsp/enc_neon.c b/thirdparty/libwebp/src/dsp/enc_neon.c
index 43bf1245c5..601962ba76 100644
--- a/thirdparty/libwebp/src/dsp/enc_neon.c
+++ b/thirdparty/libwebp/src/dsp/enc_neon.c
@@ -9,7 +9,7 @@
//
// ARM NEON version of speed-critical encoding functions.
//
-// adapted from libvpx (http://www.webmproject.org/code/)
+// adapted from libvpx (https://www.webmproject.org/code/)
#include "src/dsp/dsp.h"
diff --git a/thirdparty/libwebp/src/dsp/lossless.c b/thirdparty/libwebp/src/dsp/lossless.c
index d8bbb02b35..84a54296fd 100644
--- a/thirdparty/libwebp/src/dsp/lossless.c
+++ b/thirdparty/libwebp/src/dsp/lossless.c
@@ -107,63 +107,77 @@ static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) {
//------------------------------------------------------------------------------
// Predictors
-uint32_t VP8LPredictor0_C(uint32_t left, const uint32_t* const top) {
+uint32_t VP8LPredictor0_C(const uint32_t* const left,
+ const uint32_t* const top) {
(void)top;
(void)left;
return ARGB_BLACK;
}
-uint32_t VP8LPredictor1_C(uint32_t left, const uint32_t* const top) {
+uint32_t VP8LPredictor1_C(const uint32_t* const left,
+ const uint32_t* const top) {
(void)top;
- return left;
+ return *left;
}
-uint32_t VP8LPredictor2_C(uint32_t left, const uint32_t* const top) {
+uint32_t VP8LPredictor2_C(const uint32_t* const left,
+ const uint32_t* const top) {
(void)left;
return top[0];
}
-uint32_t VP8LPredictor3_C(uint32_t left, const uint32_t* const top) {
+uint32_t VP8LPredictor3_C(const uint32_t* const left,
+ const uint32_t* const top) {
(void)left;
return top[1];
}
-uint32_t VP8LPredictor4_C(uint32_t left, const uint32_t* const top) {
+uint32_t VP8LPredictor4_C(const uint32_t* const left,
+ const uint32_t* const top) {
(void)left;
return top[-1];
}
-uint32_t VP8LPredictor5_C(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average3(left, top[0], top[1]);
+uint32_t VP8LPredictor5_C(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = Average3(*left, top[0], top[1]);
return pred;
}
-uint32_t VP8LPredictor6_C(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average2(left, top[-1]);
+uint32_t VP8LPredictor6_C(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = Average2(*left, top[-1]);
return pred;
}
-uint32_t VP8LPredictor7_C(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average2(left, top[0]);
+uint32_t VP8LPredictor7_C(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = Average2(*left, top[0]);
return pred;
}
-uint32_t VP8LPredictor8_C(uint32_t left, const uint32_t* const top) {
+uint32_t VP8LPredictor8_C(const uint32_t* const left,
+ const uint32_t* const top) {
const uint32_t pred = Average2(top[-1], top[0]);
(void)left;
return pred;
}
-uint32_t VP8LPredictor9_C(uint32_t left, const uint32_t* const top) {
+uint32_t VP8LPredictor9_C(const uint32_t* const left,
+ const uint32_t* const top) {
const uint32_t pred = Average2(top[0], top[1]);
(void)left;
return pred;
}
-uint32_t VP8LPredictor10_C(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average4(left, top[-1], top[0], top[1]);
+uint32_t VP8LPredictor10_C(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = Average4(*left, top[-1], top[0], top[1]);
return pred;
}
-uint32_t VP8LPredictor11_C(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Select(top[0], left, top[-1]);
+uint32_t VP8LPredictor11_C(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = Select(top[0], *left, top[-1]);
return pred;
}
-uint32_t VP8LPredictor12_C(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]);
+uint32_t VP8LPredictor12_C(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = ClampedAddSubtractFull(*left, top[0], top[-1]);
return pred;
}
-uint32_t VP8LPredictor13_C(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]);
+uint32_t VP8LPredictor13_C(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = ClampedAddSubtractHalf(*left, top[0], top[-1]);
return pred;
}
diff --git a/thirdparty/libwebp/src/dsp/lossless.h b/thirdparty/libwebp/src/dsp/lossless.h
index ebd316d1ed..c26c6bca07 100644
--- a/thirdparty/libwebp/src/dsp/lossless.h
+++ b/thirdparty/libwebp/src/dsp/lossless.h
@@ -28,23 +28,38 @@ extern "C" {
//------------------------------------------------------------------------------
// Decoding
-typedef uint32_t (*VP8LPredictorFunc)(uint32_t left, const uint32_t* const top);
+typedef uint32_t (*VP8LPredictorFunc)(const uint32_t* const left,
+ const uint32_t* const top);
extern VP8LPredictorFunc VP8LPredictors[16];
-uint32_t VP8LPredictor0_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor1_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor2_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor3_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor4_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor5_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor6_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor7_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor8_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor9_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor10_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor11_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor12_C(uint32_t left, const uint32_t* const top);
-uint32_t VP8LPredictor13_C(uint32_t left, const uint32_t* const top);
+uint32_t VP8LPredictor0_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor1_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor2_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor3_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor4_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor5_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor6_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor7_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor8_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor9_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor10_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor11_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor12_C(const uint32_t* const left,
+ const uint32_t* const top);
+uint32_t VP8LPredictor13_C(const uint32_t* const left,
+ const uint32_t* const top);
// These Add/Sub function expects upper[-1] and out[-1] to be readable.
typedef void (*VP8LPredictorAddSubFunc)(const uint32_t* in,
diff --git a/thirdparty/libwebp/src/dsp/lossless_common.h b/thirdparty/libwebp/src/dsp/lossless_common.h
index 96a106f9ee..6a2f736b5e 100644
--- a/thirdparty/libwebp/src/dsp/lossless_common.h
+++ b/thirdparty/libwebp/src/dsp/lossless_common.h
@@ -179,7 +179,7 @@ static void PREDICTOR_ADD(const uint32_t* in, const uint32_t* upper, \
int x; \
assert(upper != NULL); \
for (x = 0; x < num_pixels; ++x) { \
- const uint32_t pred = (PREDICTOR)(out[x - 1], upper + x); \
+ const uint32_t pred = (PREDICTOR)(&out[x - 1], upper + x); \
out[x] = VP8LAddPixels(in[x], pred); \
} \
}
diff --git a/thirdparty/libwebp/src/dsp/lossless_enc.c b/thirdparty/libwebp/src/dsp/lossless_enc.c
index c3e8537ade..1580631e38 100644
--- a/thirdparty/libwebp/src/dsp/lossless_enc.c
+++ b/thirdparty/libwebp/src/dsp/lossless_enc.c
@@ -745,7 +745,7 @@ static void PredictorSub##PREDICTOR_I##_C(const uint32_t* in, \
assert(upper != NULL); \
for (x = 0; x < num_pixels; ++x) { \
const uint32_t pred = \
- VP8LPredictor##PREDICTOR_I##_C(in[x - 1], upper + x); \
+ VP8LPredictor##PREDICTOR_I##_C(&in[x - 1], upper + x); \
out[x] = VP8LSubPixels(in[x], pred); \
} \
}
diff --git a/thirdparty/libwebp/src/dsp/lossless_mips_dsp_r2.c b/thirdparty/libwebp/src/dsp/lossless_mips_dsp_r2.c
index 9888854d57..bfe5ea6b38 100644
--- a/thirdparty/libwebp/src/dsp/lossless_mips_dsp_r2.c
+++ b/thirdparty/libwebp/src/dsp/lossless_mips_dsp_r2.c
@@ -188,46 +188,51 @@ static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1,
return Average2(Average2(a0, a1), Average2(a2, a3));
}
-static uint32_t Predictor5_MIPSdspR2(uint32_t left, const uint32_t* const top) {
- return Average3(left, top[0], top[1]);
+static uint32_t Predictor5_MIPSdspR2(const uint32_t* const left,
+ const uint32_t* const top) {
+ return Average3(*left, top[0], top[1]);
}
-static uint32_t Predictor6_MIPSdspR2(uint32_t left, const uint32_t* const top) {
- return Average2(left, top[-1]);
+static uint32_t Predictor6_MIPSdspR2(const uint32_t* const left,
+ const uint32_t* const top) {
+ return Average2(*left, top[-1]);
}
-static uint32_t Predictor7_MIPSdspR2(uint32_t left, const uint32_t* const top) {
- return Average2(left, top[0]);
+static uint32_t Predictor7_MIPSdspR2(const uint32_t* const left,
+ const uint32_t* const top) {
+ return Average2(*left, top[0]);
}
-static uint32_t Predictor8_MIPSdspR2(uint32_t left, const uint32_t* const top) {
+static uint32_t Predictor8_MIPSdspR2(const uint32_t* const left,
+ const uint32_t* const top) {
(void)left;
return Average2(top[-1], top[0]);
}
-static uint32_t Predictor9_MIPSdspR2(uint32_t left, const uint32_t* const top) {
+static uint32_t Predictor9_MIPSdspR2(const uint32_t* const left,
+ const uint32_t* const top) {
(void)left;
return Average2(top[0], top[1]);
}
-static uint32_t Predictor10_MIPSdspR2(uint32_t left,
+static uint32_t Predictor10_MIPSdspR2(const uint32_t* const left,
const uint32_t* const top) {
- return Average4(left, top[-1], top[0], top[1]);
+ return Average4(*left, top[-1], top[0], top[1]);
}
-static uint32_t Predictor11_MIPSdspR2(uint32_t left,
+static uint32_t Predictor11_MIPSdspR2(const uint32_t* const left,
const uint32_t* const top) {
- return Select(top[0], left, top[-1]);
+ return Select(top[0], *left, top[-1]);
}
-static uint32_t Predictor12_MIPSdspR2(uint32_t left,
+static uint32_t Predictor12_MIPSdspR2(const uint32_t* const left,
const uint32_t* const top) {
- return ClampedAddSubtractFull(left, top[0], top[-1]);
+ return ClampedAddSubtractFull(*left, top[0], top[-1]);
}
-static uint32_t Predictor13_MIPSdspR2(uint32_t left,
+static uint32_t Predictor13_MIPSdspR2(const uint32_t* const left,
const uint32_t* const top) {
- return ClampedAddSubtractHalf(left, top[0], top[-1]);
+ return ClampedAddSubtractHalf(*left, top[0], top[-1]);
}
// Add green to blue and red channels (i.e. perform the inverse transform of
diff --git a/thirdparty/libwebp/src/dsp/lossless_neon.c b/thirdparty/libwebp/src/dsp/lossless_neon.c
index 76a1b6f873..89e3e013a0 100644
--- a/thirdparty/libwebp/src/dsp/lossless_neon.c
+++ b/thirdparty/libwebp/src/dsp/lossless_neon.c
@@ -188,17 +188,21 @@ static WEBP_INLINE uint32_t Average3_NEON(uint32_t a0, uint32_t a1,
return avg;
}
-static uint32_t Predictor5_NEON(uint32_t left, const uint32_t* const top) {
- return Average3_NEON(left, top[0], top[1]);
+static uint32_t Predictor5_NEON(const uint32_t* const left,
+ const uint32_t* const top) {
+ return Average3_NEON(*left, top[0], top[1]);
}
-static uint32_t Predictor6_NEON(uint32_t left, const uint32_t* const top) {
- return Average2_NEON(left, top[-1]);
+static uint32_t Predictor6_NEON(const uint32_t* const left,
+ const uint32_t* const top) {
+ return Average2_NEON(*left, top[-1]);
}
-static uint32_t Predictor7_NEON(uint32_t left, const uint32_t* const top) {
- return Average2_NEON(left, top[0]);
+static uint32_t Predictor7_NEON(const uint32_t* const left,
+ const uint32_t* const top) {
+ return Average2_NEON(*left, top[0]);
}
-static uint32_t Predictor13_NEON(uint32_t left, const uint32_t* const top) {
- return ClampedAddSubtractHalf_NEON(left, top[0], top[-1]);
+static uint32_t Predictor13_NEON(const uint32_t* const left,
+ const uint32_t* const top) {
+ return ClampedAddSubtractHalf_NEON(*left, top[0], top[-1]);
}
// Batch versions of those functions.
diff --git a/thirdparty/libwebp/src/dsp/lossless_sse2.c b/thirdparty/libwebp/src/dsp/lossless_sse2.c
index 3a0eb440db..396cb0bdfc 100644
--- a/thirdparty/libwebp/src/dsp/lossless_sse2.c
+++ b/thirdparty/libwebp/src/dsp/lossless_sse2.c
@@ -138,42 +138,51 @@ static WEBP_INLINE uint32_t Average4_SSE2(uint32_t a0, uint32_t a1,
return output;
}
-static uint32_t Predictor5_SSE2(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average3_SSE2(left, top[0], top[1]);
+static uint32_t Predictor5_SSE2(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = Average3_SSE2(*left, top[0], top[1]);
return pred;
}
-static uint32_t Predictor6_SSE2(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average2_SSE2(left, top[-1]);
+static uint32_t Predictor6_SSE2(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = Average2_SSE2(*left, top[-1]);
return pred;
}
-static uint32_t Predictor7_SSE2(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average2_SSE2(left, top[0]);
+static uint32_t Predictor7_SSE2(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = Average2_SSE2(*left, top[0]);
return pred;
}
-static uint32_t Predictor8_SSE2(uint32_t left, const uint32_t* const top) {
+static uint32_t Predictor8_SSE2(const uint32_t* const left,
+ const uint32_t* const top) {
const uint32_t pred = Average2_SSE2(top[-1], top[0]);
(void)left;
return pred;
}
-static uint32_t Predictor9_SSE2(uint32_t left, const uint32_t* const top) {
+static uint32_t Predictor9_SSE2(const uint32_t* const left,
+ const uint32_t* const top) {
const uint32_t pred = Average2_SSE2(top[0], top[1]);
(void)left;
return pred;
}
-static uint32_t Predictor10_SSE2(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average4_SSE2(left, top[-1], top[0], top[1]);
+static uint32_t Predictor10_SSE2(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = Average4_SSE2(*left, top[-1], top[0], top[1]);
return pred;
}
-static uint32_t Predictor11_SSE2(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Select_SSE2(top[0], left, top[-1]);
+static uint32_t Predictor11_SSE2(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = Select_SSE2(top[0], *left, top[-1]);
return pred;
}
-static uint32_t Predictor12_SSE2(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = ClampedAddSubtractFull_SSE2(left, top[0], top[-1]);
+static uint32_t Predictor12_SSE2(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = ClampedAddSubtractFull_SSE2(*left, top[0], top[-1]);
return pred;
}
-static uint32_t Predictor13_SSE2(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = ClampedAddSubtractHalf_SSE2(left, top[0], top[-1]);
+static uint32_t Predictor13_SSE2(const uint32_t* const left,
+ const uint32_t* const top) {
+ const uint32_t pred = ClampedAddSubtractHalf_SSE2(*left, top[0], top[-1]);
return pred;
}
diff --git a/thirdparty/libwebp/src/dsp/msa_macro.h b/thirdparty/libwebp/src/dsp/msa_macro.h
index de026a1d9e..51f6c643ab 100644
--- a/thirdparty/libwebp/src/dsp/msa_macro.h
+++ b/thirdparty/libwebp/src/dsp/msa_macro.h
@@ -14,6 +14,10 @@
#ifndef WEBP_DSP_MSA_MACRO_H_
#define WEBP_DSP_MSA_MACRO_H_
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MSA)
+
#include <stdint.h>
#include <msa.h>
@@ -1389,4 +1393,5 @@ static WEBP_INLINE uint32_t func_hadd_uh_u32(v8u16 in) {
} while (0)
#define AVER_UB2_UB(...) AVER_UB2(v16u8, __VA_ARGS__)
+#endif // WEBP_USE_MSA
#endif // WEBP_DSP_MSA_MACRO_H_
diff --git a/thirdparty/libwebp/src/dsp/neon.h b/thirdparty/libwebp/src/dsp/neon.h
index aa1dea1301..c591f9b9a7 100644
--- a/thirdparty/libwebp/src/dsp/neon.h
+++ b/thirdparty/libwebp/src/dsp/neon.h
@@ -12,10 +12,12 @@
#ifndef WEBP_DSP_NEON_H_
#define WEBP_DSP_NEON_H_
-#include <arm_neon.h>
-
#include "src/dsp/dsp.h"
+#if defined(WEBP_USE_NEON)
+
+#include <arm_neon.h>
+
// Right now, some intrinsics functions seem slower, so we disable them
// everywhere except newer clang/gcc or aarch64 where the inline assembly is
// incompatible.
@@ -98,4 +100,5 @@ static WEBP_INLINE int32x4x4_t Transpose4x4_NEON(const int32x4x4_t rows) {
} while (0)
#endif
+#endif // WEBP_USE_NEON
#endif // WEBP_DSP_NEON_H_
diff --git a/thirdparty/libwebp/src/dsp/yuv.h b/thirdparty/libwebp/src/dsp/yuv.h
index c12be1d094..66a397d117 100644
--- a/thirdparty/libwebp/src/dsp/yuv.h
+++ b/thirdparty/libwebp/src/dsp/yuv.h
@@ -10,7 +10,7 @@
// inline YUV<->RGB conversion function
//
// The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
-// More information at: http://en.wikipedia.org/wiki/YCbCr
+// More information at: https://en.wikipedia.org/wiki/YCbCr
// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
diff --git a/thirdparty/libwebp/src/enc/frame_enc.c b/thirdparty/libwebp/src/enc/frame_enc.c
index af538d83ba..b93d9e5b99 100644
--- a/thirdparty/libwebp/src/enc/frame_enc.c
+++ b/thirdparty/libwebp/src/enc/frame_enc.c
@@ -778,6 +778,7 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
// Roughly refresh the proba eight times per pass
int max_count = (enc->mb_w_ * enc->mb_h_) >> 3;
int num_pass_left = enc->config_->pass;
+ int remaining_progress = 40; // percents
const int do_search = enc->do_search_;
VP8EncIterator it;
VP8EncProba* const proba = &enc->proba_;
@@ -805,6 +806,9 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
uint64_t size_p0 = 0;
uint64_t distortion = 0;
int cnt = max_count;
+ // The final number of passes is not trivial to know in advance.
+ const int pass_progress = remaining_progress / (2 + num_pass_left);
+ remaining_progress -= pass_progress;
VP8IteratorInit(enc, &it);
SetLoopParams(enc, stats.q);
if (is_last_pass) {
@@ -832,7 +836,7 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
StoreSideInfo(&it);
VP8StoreFilterStats(&it);
VP8IteratorExport(&it);
- ok = VP8IteratorProgress(&it, 20);
+ ok = VP8IteratorProgress(&it, pass_progress);
}
VP8IteratorSaveBoundary(&it);
} while (ok && VP8IteratorNext(&it));
@@ -878,7 +882,8 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0,
(const uint8_t*)proba->coeffs_, 1);
}
- ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
+ ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + remaining_progress,
+ &enc->percent_);
return PostLoopFinalize(&it, ok);
}
diff --git a/thirdparty/libwebp/src/enc/predictor_enc.c b/thirdparty/libwebp/src/enc/predictor_enc.c
index 2e6762ea0d..2b5c767280 100644
--- a/thirdparty/libwebp/src/enc/predictor_enc.c
+++ b/thirdparty/libwebp/src/enc/predictor_enc.c
@@ -249,7 +249,7 @@ static WEBP_INLINE void GetResidual(
} else if (x == 0) {
predict = upper_row[x]; // Top.
} else {
- predict = pred_func(current_row[x - 1], upper_row + x);
+ predict = pred_func(&current_row[x - 1], upper_row + x);
}
#if (WEBP_NEAR_LOSSLESS == 1)
if (max_quantization == 1 || mode == 0 || y == 0 || y == height - 1 ||
diff --git a/thirdparty/libwebp/src/enc/quant_enc.c b/thirdparty/libwebp/src/enc/quant_enc.c
index 01eb565c7f..6cede28ab4 100644
--- a/thirdparty/libwebp/src/enc/quant_enc.c
+++ b/thirdparty/libwebp/src/enc/quant_enc.c
@@ -585,6 +585,9 @@ static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate,
return rate * lambda + RD_DISTO_MULT * distortion;
}
+// Coefficient type.
+enum { TYPE_I16_AC = 0, TYPE_I16_DC = 1, TYPE_CHROMA_A = 2, TYPE_I4_AC = 3 };
+
static int TrellisQuantizeBlock(const VP8Encoder* const enc,
int16_t in[16], int16_t out[16],
int ctx0, int coeff_type,
@@ -593,7 +596,7 @@ static int TrellisQuantizeBlock(const VP8Encoder* const enc,
const ProbaArray* const probas = enc->proba_.coeffs_[coeff_type];
CostArrayPtr const costs =
(CostArrayPtr)enc->proba_.remapped_costs_[coeff_type];
- const int first = (coeff_type == 0) ? 1 : 0;
+ const int first = (coeff_type == TYPE_I16_AC) ? 1 : 0;
Node nodes[16][NUM_NODES];
ScoreState score_states[2][NUM_NODES];
ScoreState* ss_cur = &SCORE_STATE(0, MIN_DELTA);
@@ -657,16 +660,17 @@ static int TrellisQuantizeBlock(const VP8Encoder* const enc,
// test all alternate level values around level0.
for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) {
Node* const cur = &NODE(n, m);
- int level = level0 + m;
+ const int level = level0 + m;
const int ctx = (level > 2) ? 2 : level;
const int band = VP8EncBands[n + 1];
score_t base_score;
- score_t best_cur_score = MAX_COST;
- int best_prev = 0; // default, in case
+ score_t best_cur_score;
+ int best_prev;
+ score_t cost, score;
- ss_cur[m].score = MAX_COST;
ss_cur[m].costs = costs[n + 1][ctx];
if (level < 0 || level > thresh_level) {
+ ss_cur[m].score = MAX_COST;
// Node is dead.
continue;
}
@@ -682,18 +686,24 @@ static int TrellisQuantizeBlock(const VP8Encoder* const enc,
}
// Inspect all possible non-dead predecessors. Retain only the best one.
- for (p = -MIN_DELTA; p <= MAX_DELTA; ++p) {
+ // The base_score is added to all scores so it is only added for the final
+ // value after the loop.
+ cost = VP8LevelCost(ss_prev[-MIN_DELTA].costs, level);
+ best_cur_score =
+ ss_prev[-MIN_DELTA].score + RDScoreTrellis(lambda, cost, 0);
+ best_prev = -MIN_DELTA;
+ for (p = -MIN_DELTA + 1; p <= MAX_DELTA; ++p) {
// Dead nodes (with ss_prev[p].score >= MAX_COST) are automatically
// eliminated since their score can't be better than the current best.
- const score_t cost = VP8LevelCost(ss_prev[p].costs, level);
+ cost = VP8LevelCost(ss_prev[p].costs, level);
// Examine node assuming it's a non-terminal one.
- const score_t score =
- base_score + ss_prev[p].score + RDScoreTrellis(lambda, cost, 0);
+ score = ss_prev[p].score + RDScoreTrellis(lambda, cost, 0);
if (score < best_cur_score) {
best_cur_score = score;
best_prev = p;
}
}
+ best_cur_score += base_score;
// Store best finding in current node.
cur->sign = sign;
cur->level = level;
@@ -701,11 +711,11 @@ static int TrellisQuantizeBlock(const VP8Encoder* const enc,
ss_cur[m].score = best_cur_score;
// Now, record best terminal node (and thus best entry in the graph).
- if (level != 0) {
+ if (level != 0 && best_cur_score < best_score) {
const score_t last_pos_cost =
(n < 15) ? VP8BitCost(0, probas[band][ctx][0]) : 0;
const score_t last_pos_score = RDScoreTrellis(lambda, last_pos_cost, 0);
- const score_t score = best_cur_score + last_pos_score;
+ score = best_cur_score + last_pos_score;
if (score < best_score) {
best_score = score;
best_path[0] = n; // best eob position
@@ -717,10 +727,16 @@ static int TrellisQuantizeBlock(const VP8Encoder* const enc,
}
// Fresh start
- memset(in + first, 0, (16 - first) * sizeof(*in));
- memset(out + first, 0, (16 - first) * sizeof(*out));
+ // Beware! We must preserve in[0]/out[0] value for TYPE_I16_AC case.
+ if (coeff_type == TYPE_I16_AC) {
+ memset(in + 1, 0, 15 * sizeof(*in));
+ memset(out + 1, 0, 15 * sizeof(*out));
+ } else {
+ memset(in, 0, 16 * sizeof(*in));
+ memset(out, 0, 16 * sizeof(*out));
+ }
if (best_path[0] == -1) {
- return 0; // skip!
+ return 0; // skip!
}
{
@@ -775,9 +791,9 @@ static int ReconstructIntra16(VP8EncIterator* const it,
for (y = 0, n = 0; y < 4; ++y) {
for (x = 0; x < 4; ++x, ++n) {
const int ctx = it->top_nz_[x] + it->left_nz_[y];
- const int non_zero =
- TrellisQuantizeBlock(enc, tmp[n], rd->y_ac_levels[n], ctx, 0,
- &dqm->y1_, dqm->lambda_trellis_i16_);
+ const int non_zero = TrellisQuantizeBlock(
+ enc, tmp[n], rd->y_ac_levels[n], ctx, TYPE_I16_AC, &dqm->y1_,
+ dqm->lambda_trellis_i16_);
it->top_nz_[x] = it->left_nz_[y] = non_zero;
rd->y_ac_levels[n][0] = 0;
nz |= non_zero << n;
@@ -818,7 +834,7 @@ static int ReconstructIntra4(VP8EncIterator* const it,
if (DO_TRELLIS_I4 && it->do_trellis_) {
const int x = it->i4_ & 3, y = it->i4_ >> 2;
const int ctx = it->top_nz_[x] + it->left_nz_[y];
- nz = TrellisQuantizeBlock(enc, tmp, levels, ctx, 3, &dqm->y1_,
+ nz = TrellisQuantizeBlock(enc, tmp, levels, ctx, TYPE_I4_AC, &dqm->y1_,
dqm->lambda_trellis_i4_);
} else {
nz = VP8EncQuantizeBlock(tmp, levels, &dqm->y1_);
@@ -927,9 +943,9 @@ static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
for (y = 0; y < 2; ++y) {
for (x = 0; x < 2; ++x, ++n) {
const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
- const int non_zero =
- TrellisQuantizeBlock(enc, tmp[n], rd->uv_levels[n], ctx, 2,
- &dqm->uv_, dqm->lambda_trellis_uv_);
+ const int non_zero = TrellisQuantizeBlock(
+ enc, tmp[n], rd->uv_levels[n], ctx, TYPE_CHROMA_A, &dqm->uv_,
+ dqm->lambda_trellis_uv_);
it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = non_zero;
nz |= non_zero << n;
}
diff --git a/thirdparty/libwebp/src/enc/vp8i_enc.h b/thirdparty/libwebp/src/enc/vp8i_enc.h
index 67e9509367..b4bba08f27 100644
--- a/thirdparty/libwebp/src/enc/vp8i_enc.h
+++ b/thirdparty/libwebp/src/enc/vp8i_enc.h
@@ -32,7 +32,7 @@ extern "C" {
// version numbers
#define ENC_MAJ_VERSION 1
#define ENC_MIN_VERSION 2
-#define ENC_REV_VERSION 1
+#define ENC_REV_VERSION 2
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/mux/muxi.h b/thirdparty/libwebp/src/mux/muxi.h
index 330da66754..d9bf9b3770 100644
--- a/thirdparty/libwebp/src/mux/muxi.h
+++ b/thirdparty/libwebp/src/mux/muxi.h
@@ -29,7 +29,7 @@ extern "C" {
#define MUX_MAJ_VERSION 1
#define MUX_MIN_VERSION 2
-#define MUX_REV_VERSION 1
+#define MUX_REV_VERSION 2
// Chunk object.
typedef struct WebPChunk WebPChunk;
diff --git a/thirdparty/libwebp/src/utils/huffman_encode_utils.c b/thirdparty/libwebp/src/utils/huffman_encode_utils.c
index fd7a47d8f7..585db91951 100644
--- a/thirdparty/libwebp/src/utils/huffman_encode_utils.c
+++ b/thirdparty/libwebp/src/utils/huffman_encode_utils.c
@@ -161,7 +161,7 @@ static void SetBitDepths(const HuffmanTree* const tree,
// especially when population counts are longer than 2**tree_limit, but
// we are not planning to use this with extremely long blocks.
//
-// See http://en.wikipedia.org/wiki/Huffman_coding
+// See https://en.wikipedia.org/wiki/Huffman_coding
static void GenerateOptimalTree(const uint32_t* const histogram,
int histogram_size,
HuffmanTree* tree, int tree_depth_limit,
diff --git a/thirdparty/libwebp/src/utils/quant_levels_dec_utils.c b/thirdparty/libwebp/src/utils/quant_levels_dec_utils.c
index f65b6cdbb6..97e7893704 100644
--- a/thirdparty/libwebp/src/utils/quant_levels_dec_utils.c
+++ b/thirdparty/libwebp/src/utils/quant_levels_dec_utils.c
@@ -30,7 +30,7 @@
#define DFIX 4 // extra precision for ordered dithering
#define DSIZE 4 // dithering size (must be a power of two)
-// cf. http://en.wikipedia.org/wiki/Ordered_dithering
+// cf. https://en.wikipedia.org/wiki/Ordered_dithering
static const uint8_t kOrderedDither[DSIZE][DSIZE] = {
{ 0, 8, 2, 10 }, // coefficients are in DFIX fixed-point precision
{ 12, 4, 14, 6 },
diff --git a/thirdparty/libwebp/src/utils/utils.c b/thirdparty/libwebp/src/utils/utils.c
index 9e464c16ce..a7c3a70fef 100644
--- a/thirdparty/libwebp/src/utils/utils.c
+++ b/thirdparty/libwebp/src/utils/utils.c
@@ -23,7 +23,7 @@
// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
// and not multi-thread safe!).
// An interesting alternative is valgrind's 'massif' tool:
-// http://valgrind.org/docs/manual/ms-manual.html
+// https://valgrind.org/docs/manual/ms-manual.html
// Here is an example command line:
/* valgrind --tool=massif --massif-out-file=massif.out \
--stacks=yes --alloc-fn=WebPSafeMalloc --alloc-fn=WebPSafeCalloc
diff --git a/thirdparty/libwebp/src/webp/decode.h b/thirdparty/libwebp/src/webp/decode.h
index 44fcd64a84..d98247509a 100644
--- a/thirdparty/libwebp/src/webp/decode.h
+++ b/thirdparty/libwebp/src/webp/decode.h
@@ -85,7 +85,7 @@ WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
// Upon return, the Y buffer has a stride returned as '*stride', while U and V
// have a common stride returned as '*uv_stride'.
// Return NULL in case of error.
-// (*) Also named Y'CbCr. See: http://en.wikipedia.org/wiki/YCbCr
+// (*) 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,
diff --git a/thirdparty/nanosvg/LICENSE.txt b/thirdparty/nanosvg/LICENSE.txt
deleted file mode 100644
index f896f2eb0f..0000000000
--- a/thirdparty/nanosvg/LICENSE.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Copyright (c) 2013-14 Mikko Mononen memon@inside.org
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
-claim that you wrote the original software. If you use this software
-in a product, an acknowledgment in the product documentation would be
-appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
-misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
diff --git a/thirdparty/nanosvg/nanosvg.cc b/thirdparty/nanosvg/nanosvg.cc
deleted file mode 100644
index 3e8e86c792..0000000000
--- a/thirdparty/nanosvg/nanosvg.cc
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "stdio.h"
-#include "string.h"
-#include "math.h"
-#define NANOSVG_ALL_COLOR_KEYWORDS
-#define NANOSVG_IMPLEMENTATION
-#include "nanosvg.h"
-#define NANOSVGRAST_IMPLEMENTATION
-#include "nanosvgrast.h"
diff --git a/thirdparty/nanosvg/nanosvg.h b/thirdparty/nanosvg/nanosvg.h
deleted file mode 100644
index f5058b179a..0000000000
--- a/thirdparty/nanosvg/nanosvg.h
+++ /dev/null
@@ -1,3008 +0,0 @@
-/*
- * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- *
- * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example
- * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
- *
- * Arc calculation code based on canvg (https://code.google.com/p/canvg/)
- *
- * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
- *
- */
-
-#ifndef NANOSVG_H
-#define NANOSVG_H
-
-#ifndef NANOSVG_CPLUSPLUS
-#ifdef __cplusplus
-extern "C" {
-#endif
-#endif
-
-// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
-//
-// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
-//
-// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
-//
-// The shapes in the SVG images are transformed by the viewBox and converted to specified units.
-// That is, you should get the same looking data as your designed in your favorite app.
-//
-// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
-// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
-//
-// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
-// DPI (dots-per-inch) controls how the unit conversion is done.
-//
-// If you don't know or care about the units stuff, "px" and 96 should get you going.
-
-
-/* Example Usage:
- // Load SVG
- NSVGimage* image;
- image = nsvgParseFromFile("test.svg", "px", 96);
- printf("size: %f x %f\n", image->width, image->height);
- // Use...
- for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) {
- for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
- for (int i = 0; i < path->npts-1; i += 3) {
- float* p = &path->pts[i*2];
- drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
- }
- }
- }
- // Delete
- nsvgDelete(image);
-*/
-
-enum NSVGpaintType {
- NSVG_PAINT_NONE = 0,
- NSVG_PAINT_COLOR = 1,
- NSVG_PAINT_LINEAR_GRADIENT = 2,
- NSVG_PAINT_RADIAL_GRADIENT = 3
-};
-
-enum NSVGspreadType {
- NSVG_SPREAD_PAD = 0,
- NSVG_SPREAD_REFLECT = 1,
- NSVG_SPREAD_REPEAT = 2
-};
-
-enum NSVGlineJoin {
- NSVG_JOIN_MITER = 0,
- NSVG_JOIN_ROUND = 1,
- NSVG_JOIN_BEVEL = 2
-};
-
-enum NSVGlineCap {
- NSVG_CAP_BUTT = 0,
- NSVG_CAP_ROUND = 1,
- NSVG_CAP_SQUARE = 2
-};
-
-enum NSVGfillRule {
- NSVG_FILLRULE_NONZERO = 0,
- NSVG_FILLRULE_EVENODD = 1
-};
-
-enum NSVGflags {
- NSVG_FLAGS_VISIBLE = 0x01
-};
-
-typedef struct NSVGgradientStop {
- unsigned int color;
- float offset;
-} NSVGgradientStop;
-
-typedef struct NSVGgradient {
- float xform[6];
- char spread;
- float fx, fy;
- int nstops;
- NSVGgradientStop stops[1];
-} NSVGgradient;
-
-typedef struct NSVGpaint {
- char type;
- union {
- unsigned int color;
- NSVGgradient* gradient;
- };
-} NSVGpaint;
-
-typedef struct NSVGpath
-{
- float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
- int npts; // Total number of bezier points.
- char closed; // Flag indicating if shapes should be treated as closed.
- float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
- struct NSVGpath* next; // Pointer to next path, or NULL if last element.
-} NSVGpath;
-
-typedef struct NSVGshape
-{
- char id[64]; // Optional 'id' attr of the shape or its group
- NSVGpaint fill; // Fill paint
- NSVGpaint stroke; // Stroke paint
- float opacity; // Opacity of the shape.
- float strokeWidth; // Stroke width (scaled).
- float strokeDashOffset; // Stroke dash offset (scaled).
- float strokeDashArray[8]; // Stroke dash array (scaled).
- char strokeDashCount; // Number of dash values in dash array.
- char strokeLineJoin; // Stroke join type.
- char strokeLineCap; // Stroke cap type.
- float miterLimit; // Miter limit
- char fillRule; // Fill rule, see NSVGfillRule.
- unsigned char flags; // Logical or of NSVG_FLAGS_* flags
- float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
- NSVGpath* paths; // Linked list of paths in the image.
- struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
-} NSVGshape;
-
-typedef struct NSVGimage
-{
- float width; // Width of the image.
- float height; // Height of the image.
- NSVGshape* shapes; // Linked list of shapes in the image.
-} NSVGimage;
-
-// Parses SVG file from a file, returns SVG image as paths.
-NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
-
-// Parses SVG file from a null terminated string, returns SVG image as paths.
-// Important note: changes the string.
-NSVGimage* nsvgParse(char* input, const char* units, float dpi);
-
-// Duplicates a path.
-NSVGpath* nsvgDuplicatePath(NSVGpath* p);
-
-// Deletes an image.
-void nsvgDelete(NSVGimage* image);
-
-#ifndef NANOSVG_CPLUSPLUS
-#ifdef __cplusplus
-}
-#endif
-#endif
-
-#endif // NANOSVG_H
-
-#ifdef NANOSVG_IMPLEMENTATION
-
-#include <string.h>
-#include <stdlib.h>
-#include <math.h>
-
-#define NSVG_PI (3.14159265358979323846264338327f)
-#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs.
-
-#define NSVG_ALIGN_MIN 0
-#define NSVG_ALIGN_MID 1
-#define NSVG_ALIGN_MAX 2
-#define NSVG_ALIGN_NONE 0
-#define NSVG_ALIGN_MEET 1
-#define NSVG_ALIGN_SLICE 2
-
-#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0)
-#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
-
-#ifdef _MSC_VER
- #pragma warning (disable: 4996) // Switch off security warnings
- #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
- #ifdef __cplusplus
- #define NSVG_INLINE inline
- #else
- #define NSVG_INLINE
- #endif
-#else
- #define NSVG_INLINE inline
-#endif
-
-
-static int nsvg__isspace(char c)
-{
- return strchr(" \t\n\v\f\r", c) != 0;
-}
-
-static int nsvg__isdigit(char c)
-{
- return c >= '0' && c <= '9';
-}
-
-static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; }
-static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; }
-
-
-// Simple XML parser
-
-#define NSVG_XML_TAG 1
-#define NSVG_XML_CONTENT 2
-#define NSVG_XML_MAX_ATTRIBS 256
-
-static void nsvg__parseContent(char* s,
- void (*contentCb)(void* ud, const char* s),
- void* ud)
-{
- // Trim start white spaces
- while (*s && nsvg__isspace(*s)) s++;
- if (!*s) return;
-
- if (contentCb)
- (*contentCb)(ud, s);
-}
-
-static void nsvg__parseElement(char* s,
- void (*startelCb)(void* ud, const char* el, const char** attr),
- void (*endelCb)(void* ud, const char* el),
- void* ud)
-{
- const char* attr[NSVG_XML_MAX_ATTRIBS];
- int nattr = 0;
- char* name;
- int start = 0;
- int end = 0;
- char quote;
-
- // Skip white space after the '<'
- while (*s && nsvg__isspace(*s)) s++;
-
- // Check if the tag is end tag
- if (*s == '/') {
- s++;
- end = 1;
- } else {
- start = 1;
- }
-
- // Skip comments, data and preprocessor stuff.
- if (!*s || *s == '?' || *s == '!')
- return;
-
- // Get tag name
- name = s;
- while (*s && !nsvg__isspace(*s)) s++;
- if (*s) { *s++ = '\0'; }
-
- // Get attribs
- while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) {
- char* name = NULL;
- char* value = NULL;
-
- // Skip white space before the attrib name
- while (*s && nsvg__isspace(*s)) s++;
- if (!*s) break;
- if (*s == '/') {
- end = 1;
- break;
- }
- name = s;
- // Find end of the attrib name.
- while (*s && !nsvg__isspace(*s) && *s != '=') s++;
- if (*s) { *s++ = '\0'; }
- // Skip until the beginning of the value.
- while (*s && *s != '\"' && *s != '\'') s++;
- if (!*s) break;
- quote = *s;
- s++;
- // Store value and find the end of it.
- value = s;
- while (*s && *s != quote) s++;
- if (*s) { *s++ = '\0'; }
-
- // Store only well formed attributes
- if (name && value) {
- attr[nattr++] = name;
- attr[nattr++] = value;
- }
- }
-
- // List terminator
- attr[nattr++] = 0;
- attr[nattr++] = 0;
-
- // Call callbacks.
- if (start && startelCb)
- (*startelCb)(ud, name, attr);
- if (end && endelCb)
- (*endelCb)(ud, name);
-}
-
-int nsvg__parseXML(char* input,
- void (*startelCb)(void* ud, const char* el, const char** attr),
- void (*endelCb)(void* ud, const char* el),
- void (*contentCb)(void* ud, const char* s),
- void* ud)
-{
- char* s = input;
- char* mark = s;
- int state = NSVG_XML_CONTENT;
- while (*s) {
- if (*s == '<' && state == NSVG_XML_CONTENT) {
- // Start of a tag
- *s++ = '\0';
- nsvg__parseContent(mark, contentCb, ud);
- mark = s;
- state = NSVG_XML_TAG;
- } else if (*s == '>' && state == NSVG_XML_TAG) {
- // Start of a content or new tag.
- *s++ = '\0';
- nsvg__parseElement(mark, startelCb, endelCb, ud);
- mark = s;
- state = NSVG_XML_CONTENT;
- } else {
- s++;
- }
- }
-
- return 1;
-}
-
-
-/* Simple SVG parser. */
-
-#define NSVG_MAX_ATTR 128
-
-enum NSVGgradientUnits {
- NSVG_USER_SPACE = 0,
- NSVG_OBJECT_SPACE = 1
-};
-
-#define NSVG_MAX_DASHES 8
-
-enum NSVGunits {
- NSVG_UNITS_USER,
- NSVG_UNITS_PX,
- NSVG_UNITS_PT,
- NSVG_UNITS_PC,
- NSVG_UNITS_MM,
- NSVG_UNITS_CM,
- NSVG_UNITS_IN,
- NSVG_UNITS_PERCENT,
- NSVG_UNITS_EM,
- NSVG_UNITS_EX
-};
-
-typedef struct NSVGcoordinate {
- float value;
- int units;
-} NSVGcoordinate;
-
-typedef struct NSVGlinearData {
- NSVGcoordinate x1, y1, x2, y2;
-} NSVGlinearData;
-
-typedef struct NSVGradialData {
- NSVGcoordinate cx, cy, r, fx, fy;
-} NSVGradialData;
-
-typedef struct NSVGgradientData
-{
- char id[64];
- char ref[64];
- char type;
- union {
- NSVGlinearData linear;
- NSVGradialData radial;
- };
- char spread;
- char units;
- float xform[6];
- int nstops;
- NSVGgradientStop* stops;
- struct NSVGgradientData* next;
-} NSVGgradientData;
-
-typedef struct NSVGattrib
-{
- char id[64];
- float xform[6];
- unsigned int fillColor;
- unsigned int strokeColor;
- float opacity;
- float fillOpacity;
- float strokeOpacity;
- char fillGradient[64];
- char strokeGradient[64];
- float strokeWidth;
- float strokeDashOffset;
- float strokeDashArray[NSVG_MAX_DASHES];
- int strokeDashCount;
- char strokeLineJoin;
- char strokeLineCap;
- float miterLimit;
- char fillRule;
- float fontSize;
- unsigned int stopColor;
- float stopOpacity;
- float stopOffset;
- char hasFill;
- char hasStroke;
- char visible;
-} NSVGattrib;
-
-typedef struct NSVGparser
-{
- NSVGattrib attr[NSVG_MAX_ATTR];
- int attrHead;
- float* pts;
- int npts;
- int cpts;
- NSVGpath* plist;
- NSVGimage* image;
- NSVGgradientData* gradients;
- NSVGshape* shapesTail;
- float viewMinx, viewMiny, viewWidth, viewHeight;
- int alignX, alignY, alignType;
- float dpi;
- char pathFlag;
- char defsFlag;
-} NSVGparser;
-
-static void nsvg__xformIdentity(float* t)
-{
- t[0] = 1.0f; t[1] = 0.0f;
- t[2] = 0.0f; t[3] = 1.0f;
- t[4] = 0.0f; t[5] = 0.0f;
-}
-
-static void nsvg__xformSetTranslation(float* t, float tx, float ty)
-{
- t[0] = 1.0f; t[1] = 0.0f;
- t[2] = 0.0f; t[3] = 1.0f;
- t[4] = tx; t[5] = ty;
-}
-
-static void nsvg__xformSetScale(float* t, float sx, float sy)
-{
- t[0] = sx; t[1] = 0.0f;
- t[2] = 0.0f; t[3] = sy;
- t[4] = 0.0f; t[5] = 0.0f;
-}
-
-static void nsvg__xformSetSkewX(float* t, float a)
-{
- t[0] = 1.0f; t[1] = 0.0f;
- t[2] = tanf(a); t[3] = 1.0f;
- t[4] = 0.0f; t[5] = 0.0f;
-}
-
-static void nsvg__xformSetSkewY(float* t, float a)
-{
- t[0] = 1.0f; t[1] = tanf(a);
- t[2] = 0.0f; t[3] = 1.0f;
- t[4] = 0.0f; t[5] = 0.0f;
-}
-
-static void nsvg__xformSetRotation(float* t, float a)
-{
- float cs = cosf(a), sn = sinf(a);
- t[0] = cs; t[1] = sn;
- t[2] = -sn; t[3] = cs;
- t[4] = 0.0f; t[5] = 0.0f;
-}
-
-static void nsvg__xformMultiply(float* t, float* s)
-{
- float t0 = t[0] * s[0] + t[1] * s[2];
- float t2 = t[2] * s[0] + t[3] * s[2];
- float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
- t[1] = t[0] * s[1] + t[1] * s[3];
- t[3] = t[2] * s[1] + t[3] * s[3];
- t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
- t[0] = t0;
- t[2] = t2;
- t[4] = t4;
-}
-
-static void nsvg__xformInverse(float* inv, float* t)
-{
- double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
- if (det > -1e-6 && det < 1e-6) {
- nsvg__xformIdentity(t);
- return;
- }
- invdet = 1.0 / det;
- inv[0] = (float)(t[3] * invdet);
- inv[2] = (float)(-t[2] * invdet);
- inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
- inv[1] = (float)(-t[1] * invdet);
- inv[3] = (float)(t[0] * invdet);
- inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
-}
-
-static void nsvg__xformPremultiply(float* t, float* s)
-{
- float s2[6];
- memcpy(s2, s, sizeof(float)*6);
- nsvg__xformMultiply(s2, t);
- memcpy(t, s2, sizeof(float)*6);
-}
-
-static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t)
-{
- *dx = x*t[0] + y*t[2] + t[4];
- *dy = x*t[1] + y*t[3] + t[5];
-}
-
-static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t)
-{
- *dx = x*t[0] + y*t[2];
- *dy = x*t[1] + y*t[3];
-}
-
-#define NSVG_EPSILON (1e-12)
-
-static int nsvg__ptInBounds(float* pt, float* bounds)
-{
- return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3];
-}
-
-
-static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3)
-{
- double it = 1.0-t;
- return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3;
-}
-
-static void nsvg__curveBounds(float* bounds, float* curve)
-{
- int i, j, count;
- double roots[2], a, b, c, b2ac, t, v;
- float* v0 = &curve[0];
- float* v1 = &curve[2];
- float* v2 = &curve[4];
- float* v3 = &curve[6];
-
- // Start the bounding box by end points
- bounds[0] = nsvg__minf(v0[0], v3[0]);
- bounds[1] = nsvg__minf(v0[1], v3[1]);
- bounds[2] = nsvg__maxf(v0[0], v3[0]);
- bounds[3] = nsvg__maxf(v0[1], v3[1]);
-
- // Bezier curve fits inside the convex hull of it's control points.
- // If control points are inside the bounds, we're done.
- if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds))
- return;
-
- // Add bezier curve inflection points in X and Y.
- for (i = 0; i < 2; i++) {
- a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i];
- b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i];
- c = 3.0 * v1[i] - 3.0 * v0[i];
- count = 0;
- if (fabs(a) < NSVG_EPSILON) {
- if (fabs(b) > NSVG_EPSILON) {
- t = -c / b;
- if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
- roots[count++] = t;
- }
- } else {
- b2ac = b*b - 4.0*c*a;
- if (b2ac > NSVG_EPSILON) {
- t = (-b + sqrt(b2ac)) / (2.0 * a);
- if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
- roots[count++] = t;
- t = (-b - sqrt(b2ac)) / (2.0 * a);
- if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
- roots[count++] = t;
- }
- }
- for (j = 0; j < count; j++) {
- v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]);
- bounds[0+i] = nsvg__minf(bounds[0+i], (float)v);
- bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v);
- }
- }
-}
-
-static NSVGparser* nsvg__createParser()
-{
- NSVGparser* p;
- p = (NSVGparser*)malloc(sizeof(NSVGparser));
- if (p == NULL) goto error;
- memset(p, 0, sizeof(NSVGparser));
-
- p->image = (NSVGimage*)malloc(sizeof(NSVGimage));
- if (p->image == NULL) goto error;
- memset(p->image, 0, sizeof(NSVGimage));
-
- // Init style
- nsvg__xformIdentity(p->attr[0].xform);
- memset(p->attr[0].id, 0, sizeof p->attr[0].id);
- p->attr[0].fillColor = NSVG_RGB(0,0,0);
- p->attr[0].strokeColor = NSVG_RGB(0,0,0);
- p->attr[0].opacity = 1;
- p->attr[0].fillOpacity = 1;
- p->attr[0].strokeOpacity = 1;
- p->attr[0].stopOpacity = 1;
- p->attr[0].strokeWidth = 1;
- p->attr[0].strokeLineJoin = NSVG_JOIN_MITER;
- p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
- p->attr[0].miterLimit = 4;
- p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
- p->attr[0].hasFill = 1;
- p->attr[0].visible = 1;
-
- return p;
-
-error:
- if (p) {
- if (p->image) free(p->image);
- free(p);
- }
- return NULL;
-}
-
-static void nsvg__deletePaths(NSVGpath* path)
-{
- while (path) {
- NSVGpath *next = path->next;
- if (path->pts != NULL)
- free(path->pts);
- free(path);
- path = next;
- }
-}
-
-static void nsvg__deletePaint(NSVGpaint* paint)
-{
- if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT)
- free(paint->gradient);
-}
-
-static void nsvg__deleteGradientData(NSVGgradientData* grad)
-{
- NSVGgradientData* next;
- while (grad != NULL) {
- next = grad->next;
- free(grad->stops);
- free(grad);
- grad = next;
- }
-}
-
-static void nsvg__deleteParser(NSVGparser* p)
-{
- if (p != NULL) {
- nsvg__deletePaths(p->plist);
- nsvg__deleteGradientData(p->gradients);
- nsvgDelete(p->image);
- free(p->pts);
- free(p);
- }
-}
-
-static void nsvg__resetPath(NSVGparser* p)
-{
- p->npts = 0;
-}
-
-static void nsvg__addPoint(NSVGparser* p, float x, float y)
-{
- if (p->npts+1 > p->cpts) {
- p->cpts = p->cpts ? p->cpts*2 : 8;
- p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float));
- if (!p->pts) return;
- }
- p->pts[p->npts*2+0] = x;
- p->pts[p->npts*2+1] = y;
- p->npts++;
-}
-
-static void nsvg__moveTo(NSVGparser* p, float x, float y)
-{
- if (p->npts > 0) {
- p->pts[(p->npts-1)*2+0] = x;
- p->pts[(p->npts-1)*2+1] = y;
- } else {
- nsvg__addPoint(p, x, y);
- }
-}
-
-static void nsvg__lineTo(NSVGparser* p, float x, float y)
-{
- float px,py, dx,dy;
- if (p->npts > 0) {
- px = p->pts[(p->npts-1)*2+0];
- py = p->pts[(p->npts-1)*2+1];
- dx = x - px;
- dy = y - py;
- nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f);
- nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f);
- nsvg__addPoint(p, x, y);
- }
-}
-
-static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
-{
- if (p->npts > 0) {
- nsvg__addPoint(p, cpx1, cpy1);
- nsvg__addPoint(p, cpx2, cpy2);
- nsvg__addPoint(p, x, y);
- }
-}
-
-static NSVGattrib* nsvg__getAttr(NSVGparser* p)
-{
- return &p->attr[p->attrHead];
-}
-
-static void nsvg__pushAttr(NSVGparser* p)
-{
- if (p->attrHead < NSVG_MAX_ATTR-1) {
- p->attrHead++;
- memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib));
- }
-}
-
-static void nsvg__popAttr(NSVGparser* p)
-{
- if (p->attrHead > 0)
- p->attrHead--;
-}
-
-static float nsvg__actualOrigX(NSVGparser* p)
-{
- return p->viewMinx;
-}
-
-static float nsvg__actualOrigY(NSVGparser* p)
-{
- return p->viewMiny;
-}
-
-static float nsvg__actualWidth(NSVGparser* p)
-{
- return p->viewWidth;
-}
-
-static float nsvg__actualHeight(NSVGparser* p)
-{
- return p->viewHeight;
-}
-
-static float nsvg__actualLength(NSVGparser* p)
-{
- float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p);
- return sqrtf(w*w + h*h) / sqrtf(2.0f);
-}
-
-static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length)
-{
- NSVGattrib* attr = nsvg__getAttr(p);
- switch (c.units) {
- case NSVG_UNITS_USER: return c.value;
- case NSVG_UNITS_PX: return c.value;
- case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi;
- case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi;
- case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi;
- case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi;
- case NSVG_UNITS_IN: return c.value * p->dpi;
- case NSVG_UNITS_EM: return c.value * attr->fontSize;
- case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica.
- case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length;
- default: return c.value;
- }
- return c.value;
-}
-
-static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
-{
- NSVGgradientData* grad = p->gradients;
- if (id == NULL || *id == '\0')
- return NULL;
- while (grad != NULL) {
- if (strcmp(grad->id, id) == 0)
- return grad;
- grad = grad->next;
- }
- return NULL;
-}
-
-static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType)
-{
- NSVGattrib* attr = nsvg__getAttr(p);
- NSVGgradientData* data = NULL;
- NSVGgradientData* ref = NULL;
- NSVGgradientStop* stops = NULL;
- NSVGgradient* grad;
- float ox, oy, sw, sh, sl;
- int nstops = 0;
- int refIter;
-
- data = nsvg__findGradientData(p, id);
- if (data == NULL) return NULL;
-
- // TODO: use ref to fill in all unset values too.
- ref = data;
- refIter = 0;
- while (ref != NULL) {
- NSVGgradientData* nextRef = NULL;
- if (stops == NULL && ref->stops != NULL) {
- stops = ref->stops;
- nstops = ref->nstops;
- break;
- }
- nextRef = nsvg__findGradientData(p, ref->ref);
- if (nextRef == ref) break; // prevent infite loops on malformed data
- ref = nextRef;
- refIter++;
- if (refIter > 32) break; // prevent infite loops on malformed data
- }
- if (stops == NULL) return NULL;
-
- grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1));
- if (grad == NULL) return NULL;
-
- // The shape width and height.
- if (data->units == NSVG_OBJECT_SPACE) {
- ox = localBounds[0];
- oy = localBounds[1];
- sw = localBounds[2] - localBounds[0];
- sh = localBounds[3] - localBounds[1];
- } else {
- ox = nsvg__actualOrigX(p);
- oy = nsvg__actualOrigY(p);
- sw = nsvg__actualWidth(p);
- sh = nsvg__actualHeight(p);
- }
- sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f);
-
- if (data->type == NSVG_PAINT_LINEAR_GRADIENT) {
- float x1, y1, x2, y2, dx, dy;
- x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw);
- y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh);
- x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw);
- y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh);
- // Calculate transform aligned to the line
- dx = x2 - x1;
- dy = y2 - y1;
- grad->xform[0] = dy; grad->xform[1] = -dx;
- grad->xform[2] = dx; grad->xform[3] = dy;
- grad->xform[4] = x1; grad->xform[5] = y1;
- } else {
- float cx, cy, fx, fy, r;
- cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw);
- cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh);
- fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw);
- fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh);
- r = nsvg__convertToPixels(p, data->radial.r, 0, sl);
- // Calculate transform aligned to the circle
- grad->xform[0] = r; grad->xform[1] = 0;
- grad->xform[2] = 0; grad->xform[3] = r;
- grad->xform[4] = cx; grad->xform[5] = cy;
- grad->fx = fx / r;
- grad->fy = fy / r;
- }
-
- nsvg__xformMultiply(grad->xform, data->xform);
- nsvg__xformMultiply(grad->xform, attr->xform);
-
- grad->spread = data->spread;
- memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
- grad->nstops = nstops;
-
- *paintType = data->type;
-
- return grad;
-}
-
-static float nsvg__getAverageScale(float* t)
-{
- float sx = sqrtf(t[0]*t[0] + t[2]*t[2]);
- float sy = sqrtf(t[1]*t[1] + t[3]*t[3]);
- return (sx + sy) * 0.5f;
-}
-
-static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform)
-{
- NSVGpath* path;
- float curve[4*2], curveBounds[4];
- int i, first = 1;
- for (path = shape->paths; path != NULL; path = path->next) {
- nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform);
- for (i = 0; i < path->npts-1; i += 3) {
- nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform);
- nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform);
- nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform);
- nsvg__curveBounds(curveBounds, curve);
- if (first) {
- bounds[0] = curveBounds[0];
- bounds[1] = curveBounds[1];
- bounds[2] = curveBounds[2];
- bounds[3] = curveBounds[3];
- first = 0;
- } else {
- bounds[0] = nsvg__minf(bounds[0], curveBounds[0]);
- bounds[1] = nsvg__minf(bounds[1], curveBounds[1]);
- bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]);
- bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]);
- }
- curve[0] = curve[6];
- curve[1] = curve[7];
- }
- }
-}
-
-static void nsvg__addShape(NSVGparser* p)
-{
- NSVGattrib* attr = nsvg__getAttr(p);
- float scale = 1.0f;
- NSVGshape* shape;
- NSVGpath* path;
- int i;
-
- if (p->plist == NULL)
- return;
-
- shape = (NSVGshape*)malloc(sizeof(NSVGshape));
- if (shape == NULL) goto error;
- memset(shape, 0, sizeof(NSVGshape));
-
- memcpy(shape->id, attr->id, sizeof shape->id);
- scale = nsvg__getAverageScale(attr->xform);
- shape->strokeWidth = attr->strokeWidth * scale;
- shape->strokeDashOffset = attr->strokeDashOffset * scale;
- shape->strokeDashCount = (char)attr->strokeDashCount;
- for (i = 0; i < attr->strokeDashCount; i++)
- shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
- shape->strokeLineJoin = attr->strokeLineJoin;
- shape->strokeLineCap = attr->strokeLineCap;
- shape->miterLimit = attr->miterLimit;
- shape->fillRule = attr->fillRule;
- shape->opacity = attr->opacity;
-
- shape->paths = p->plist;
- p->plist = NULL;
-
- // Calculate shape bounds
- shape->bounds[0] = shape->paths->bounds[0];
- shape->bounds[1] = shape->paths->bounds[1];
- shape->bounds[2] = shape->paths->bounds[2];
- shape->bounds[3] = shape->paths->bounds[3];
- for (path = shape->paths->next; path != NULL; path = path->next) {
- shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]);
- shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]);
- shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]);
- shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]);
- }
-
- // Set fill
- if (attr->hasFill == 0) {
- shape->fill.type = NSVG_PAINT_NONE;
- } else if (attr->hasFill == 1) {
- shape->fill.type = NSVG_PAINT_COLOR;
- shape->fill.color = attr->fillColor;
- shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
- } else if (attr->hasFill == 2) {
- float inv[6], localBounds[4];
- nsvg__xformInverse(inv, attr->xform);
- nsvg__getLocalBounds(localBounds, shape, inv);
- shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type);
- if (shape->fill.gradient == NULL) {
- shape->fill.type = NSVG_PAINT_NONE;
- }
- }
-
- // Set stroke
- if (attr->hasStroke == 0) {
- shape->stroke.type = NSVG_PAINT_NONE;
- } else if (attr->hasStroke == 1) {
- shape->stroke.type = NSVG_PAINT_COLOR;
- shape->stroke.color = attr->strokeColor;
- shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
- } else if (attr->hasStroke == 2) {
- float inv[6], localBounds[4];
- nsvg__xformInverse(inv, attr->xform);
- nsvg__getLocalBounds(localBounds, shape, inv);
- shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type);
- if (shape->stroke.gradient == NULL)
- shape->stroke.type = NSVG_PAINT_NONE;
- }
-
- // Set flags
- shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00);
-
- // Add to tail
- if (p->image->shapes == NULL)
- p->image->shapes = shape;
- else
- p->shapesTail->next = shape;
- p->shapesTail = shape;
-
- return;
-
-error:
- if (shape) free(shape);
-}
-
-static void nsvg__addPath(NSVGparser* p, char closed)
-{
- NSVGattrib* attr = nsvg__getAttr(p);
- NSVGpath* path = NULL;
- float bounds[4];
- float* curve;
- int i;
-
- if (p->npts < 4)
- return;
-
- if (closed)
- nsvg__lineTo(p, p->pts[0], p->pts[1]);
-
- // Expect 1 + N*3 points (N = number of cubic bezier segments).
- if ((p->npts % 3) != 1)
- return;
-
- path = (NSVGpath*)malloc(sizeof(NSVGpath));
- if (path == NULL) goto error;
- memset(path, 0, sizeof(NSVGpath));
-
- path->pts = (float*)malloc(p->npts*2*sizeof(float));
- if (path->pts == NULL) goto error;
- path->closed = closed;
- path->npts = p->npts;
-
- // Transform path.
- for (i = 0; i < p->npts; ++i)
- nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform);
-
- // Find bounds
- for (i = 0; i < path->npts-1; i += 3) {
- curve = &path->pts[i*2];
- nsvg__curveBounds(bounds, curve);
- if (i == 0) {
- path->bounds[0] = bounds[0];
- path->bounds[1] = bounds[1];
- path->bounds[2] = bounds[2];
- path->bounds[3] = bounds[3];
- } else {
- path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]);
- path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]);
- path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]);
- path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]);
- }
- }
-
- path->next = p->plist;
- p->plist = path;
-
- return;
-
-error:
- if (path != NULL) {
- if (path->pts != NULL) free(path->pts);
- free(path);
- }
-}
-
-// We roll our own string to float because the std library one uses locale and messes things up.
-static double nsvg__atof(const char* s)
-{
- char* cur = (char*)s;
- char* end = NULL;
- double res = 0.0, sign = 1.0;
- long long intPart = 0, fracPart = 0;
- char hasIntPart = 0, hasFracPart = 0;
-
- // Parse optional sign
- if (*cur == '+') {
- cur++;
- } else if (*cur == '-') {
- sign = -1;
- cur++;
- }
-
- // Parse integer part
- if (nsvg__isdigit(*cur)) {
- // Parse digit sequence
- intPart = strtoll(cur, &end, 10);
- if (cur != end) {
- res = (double)intPart;
- hasIntPart = 1;
- cur = end;
- }
- }
-
- // Parse fractional part.
- if (*cur == '.') {
- cur++; // Skip '.'
- if (nsvg__isdigit(*cur)) {
- // Parse digit sequence
- fracPart = strtoll(cur, &end, 10);
- if (cur != end) {
- res += (double)fracPart / pow(10.0, (double)(end - cur));
- hasFracPart = 1;
- cur = end;
- }
- }
- }
-
- // A valid number should have integer or fractional part.
- if (!hasIntPart && !hasFracPart)
- return 0.0;
-
- // Parse optional exponent
- if (*cur == 'e' || *cur == 'E') {
- long expPart = 0;
- cur++; // skip 'E'
- expPart = strtol(cur, &end, 10); // Parse digit sequence with sign
- if (cur != end) {
- res *= pow(10.0, (double)expPart);
- }
- }
-
- return res * sign;
-}
-
-
-static const char* nsvg__parseNumber(const char* s, char* it, const int size)
-{
- const int last = size-1;
- int i = 0;
-
- // sign
- if (*s == '-' || *s == '+') {
- if (i < last) it[i++] = *s;
- s++;
- }
- // integer part
- while (*s && nsvg__isdigit(*s)) {
- if (i < last) it[i++] = *s;
- s++;
- }
- if (*s == '.') {
- // decimal point
- if (i < last) it[i++] = *s;
- s++;
- // fraction part
- while (*s && nsvg__isdigit(*s)) {
- if (i < last) it[i++] = *s;
- s++;
- }
- }
- // exponent
- if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) {
- if (i < last) it[i++] = *s;
- s++;
- if (*s == '-' || *s == '+') {
- if (i < last) it[i++] = *s;
- s++;
- }
- while (*s && nsvg__isdigit(*s)) {
- if (i < last) it[i++] = *s;
- s++;
- }
- }
- it[i] = '\0';
-
- return s;
-}
-
-static const char* nsvg__getNextPathItem(const char* s, char* it)
-{
- it[0] = '\0';
- // Skip white spaces and commas
- while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
- if (!*s) return s;
- if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
- s = nsvg__parseNumber(s, it, 64);
- } else {
- // Parse command
- it[0] = *s++;
- it[1] = '\0';
- return s;
- }
-
- return s;
-}
-
-static unsigned int nsvg__parseColorHex(const char* str)
-{
- unsigned int r=0, g=0, b=0;
- if (sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3 ) // 2 digit hex
- return NSVG_RGB(r, g, b);
- if (sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3 ) // 1 digit hex, e.g. #abc -> 0xccbbaa
- return NSVG_RGB(r*17, g*17, b*17); // same effect as (r<<4|r), (g<<4|g), ..
- return NSVG_RGB(128, 128, 128);
-}
-
-static unsigned int nsvg__parseColorRGB(const char* str)
-{
- unsigned int r=0, g=0, b=0;
- if (sscanf(str, "rgb(%u, %u, %u)", &r, &g, &b) == 3) // decimal integers
- return NSVG_RGB(r, g, b);
- if (sscanf(str, "rgb(%u%%, %u%%, %u%%)", &r, &g, &b) == 3) // decimal integer percentage
- return NSVG_RGB(r*255/100, g*255/100, b*255/100);
- return NSVG_RGB(128, 128, 128);
-}
-
-typedef struct NSVGNamedColor {
- const char* name;
- unsigned int color;
-} NSVGNamedColor;
-
-NSVGNamedColor nsvg__colors[] = {
-
- { "red", NSVG_RGB(255, 0, 0) },
- { "green", NSVG_RGB( 0, 128, 0) },
- { "blue", NSVG_RGB( 0, 0, 255) },
- { "yellow", NSVG_RGB(255, 255, 0) },
- { "cyan", NSVG_RGB( 0, 255, 255) },
- { "magenta", NSVG_RGB(255, 0, 255) },
- { "black", NSVG_RGB( 0, 0, 0) },
- { "grey", NSVG_RGB(128, 128, 128) },
- { "gray", NSVG_RGB(128, 128, 128) },
- { "white", NSVG_RGB(255, 255, 255) },
-
-#ifdef NANOSVG_ALL_COLOR_KEYWORDS
- { "aliceblue", NSVG_RGB(240, 248, 255) },
- { "antiquewhite", NSVG_RGB(250, 235, 215) },
- { "aqua", NSVG_RGB( 0, 255, 255) },
- { "aquamarine", NSVG_RGB(127, 255, 212) },
- { "azure", NSVG_RGB(240, 255, 255) },
- { "beige", NSVG_RGB(245, 245, 220) },
- { "bisque", NSVG_RGB(255, 228, 196) },
- { "blanchedalmond", NSVG_RGB(255, 235, 205) },
- { "blueviolet", NSVG_RGB(138, 43, 226) },
- { "brown", NSVG_RGB(165, 42, 42) },
- { "burlywood", NSVG_RGB(222, 184, 135) },
- { "cadetblue", NSVG_RGB( 95, 158, 160) },
- { "chartreuse", NSVG_RGB(127, 255, 0) },
- { "chocolate", NSVG_RGB(210, 105, 30) },
- { "coral", NSVG_RGB(255, 127, 80) },
- { "cornflowerblue", NSVG_RGB(100, 149, 237) },
- { "cornsilk", NSVG_RGB(255, 248, 220) },
- { "crimson", NSVG_RGB(220, 20, 60) },
- { "darkblue", NSVG_RGB( 0, 0, 139) },
- { "darkcyan", NSVG_RGB( 0, 139, 139) },
- { "darkgoldenrod", NSVG_RGB(184, 134, 11) },
- { "darkgray", NSVG_RGB(169, 169, 169) },
- { "darkgreen", NSVG_RGB( 0, 100, 0) },
- { "darkgrey", NSVG_RGB(169, 169, 169) },
- { "darkkhaki", NSVG_RGB(189, 183, 107) },
- { "darkmagenta", NSVG_RGB(139, 0, 139) },
- { "darkolivegreen", NSVG_RGB( 85, 107, 47) },
- { "darkorange", NSVG_RGB(255, 140, 0) },
- { "darkorchid", NSVG_RGB(153, 50, 204) },
- { "darkred", NSVG_RGB(139, 0, 0) },
- { "darksalmon", NSVG_RGB(233, 150, 122) },
- { "darkseagreen", NSVG_RGB(143, 188, 143) },
- { "darkslateblue", NSVG_RGB( 72, 61, 139) },
- { "darkslategray", NSVG_RGB( 47, 79, 79) },
- { "darkslategrey", NSVG_RGB( 47, 79, 79) },
- { "darkturquoise", NSVG_RGB( 0, 206, 209) },
- { "darkviolet", NSVG_RGB(148, 0, 211) },
- { "deeppink", NSVG_RGB(255, 20, 147) },
- { "deepskyblue", NSVG_RGB( 0, 191, 255) },
- { "dimgray", NSVG_RGB(105, 105, 105) },
- { "dimgrey", NSVG_RGB(105, 105, 105) },
- { "dodgerblue", NSVG_RGB( 30, 144, 255) },
- { "firebrick", NSVG_RGB(178, 34, 34) },
- { "floralwhite", NSVG_RGB(255, 250, 240) },
- { "forestgreen", NSVG_RGB( 34, 139, 34) },
- { "fuchsia", NSVG_RGB(255, 0, 255) },
- { "gainsboro", NSVG_RGB(220, 220, 220) },
- { "ghostwhite", NSVG_RGB(248, 248, 255) },
- { "gold", NSVG_RGB(255, 215, 0) },
- { "goldenrod", NSVG_RGB(218, 165, 32) },
- { "greenyellow", NSVG_RGB(173, 255, 47) },
- { "honeydew", NSVG_RGB(240, 255, 240) },
- { "hotpink", NSVG_RGB(255, 105, 180) },
- { "indianred", NSVG_RGB(205, 92, 92) },
- { "indigo", NSVG_RGB( 75, 0, 130) },
- { "ivory", NSVG_RGB(255, 255, 240) },
- { "khaki", NSVG_RGB(240, 230, 140) },
- { "lavender", NSVG_RGB(230, 230, 250) },
- { "lavenderblush", NSVG_RGB(255, 240, 245) },
- { "lawngreen", NSVG_RGB(124, 252, 0) },
- { "lemonchiffon", NSVG_RGB(255, 250, 205) },
- { "lightblue", NSVG_RGB(173, 216, 230) },
- { "lightcoral", NSVG_RGB(240, 128, 128) },
- { "lightcyan", NSVG_RGB(224, 255, 255) },
- { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) },
- { "lightgray", NSVG_RGB(211, 211, 211) },
- { "lightgreen", NSVG_RGB(144, 238, 144) },
- { "lightgrey", NSVG_RGB(211, 211, 211) },
- { "lightpink", NSVG_RGB(255, 182, 193) },
- { "lightsalmon", NSVG_RGB(255, 160, 122) },
- { "lightseagreen", NSVG_RGB( 32, 178, 170) },
- { "lightskyblue", NSVG_RGB(135, 206, 250) },
- { "lightslategray", NSVG_RGB(119, 136, 153) },
- { "lightslategrey", NSVG_RGB(119, 136, 153) },
- { "lightsteelblue", NSVG_RGB(176, 196, 222) },
- { "lightyellow", NSVG_RGB(255, 255, 224) },
- { "lime", NSVG_RGB( 0, 255, 0) },
- { "limegreen", NSVG_RGB( 50, 205, 50) },
- { "linen", NSVG_RGB(250, 240, 230) },
- { "maroon", NSVG_RGB(128, 0, 0) },
- { "mediumaquamarine", NSVG_RGB(102, 205, 170) },
- { "mediumblue", NSVG_RGB( 0, 0, 205) },
- { "mediumorchid", NSVG_RGB(186, 85, 211) },
- { "mediumpurple", NSVG_RGB(147, 112, 219) },
- { "mediumseagreen", NSVG_RGB( 60, 179, 113) },
- { "mediumslateblue", NSVG_RGB(123, 104, 238) },
- { "mediumspringgreen", NSVG_RGB( 0, 250, 154) },
- { "mediumturquoise", NSVG_RGB( 72, 209, 204) },
- { "mediumvioletred", NSVG_RGB(199, 21, 133) },
- { "midnightblue", NSVG_RGB( 25, 25, 112) },
- { "mintcream", NSVG_RGB(245, 255, 250) },
- { "mistyrose", NSVG_RGB(255, 228, 225) },
- { "moccasin", NSVG_RGB(255, 228, 181) },
- { "navajowhite", NSVG_RGB(255, 222, 173) },
- { "navy", NSVG_RGB( 0, 0, 128) },
- { "oldlace", NSVG_RGB(253, 245, 230) },
- { "olive", NSVG_RGB(128, 128, 0) },
- { "olivedrab", NSVG_RGB(107, 142, 35) },
- { "orange", NSVG_RGB(255, 165, 0) },
- { "orangered", NSVG_RGB(255, 69, 0) },
- { "orchid", NSVG_RGB(218, 112, 214) },
- { "palegoldenrod", NSVG_RGB(238, 232, 170) },
- { "palegreen", NSVG_RGB(152, 251, 152) },
- { "paleturquoise", NSVG_RGB(175, 238, 238) },
- { "palevioletred", NSVG_RGB(219, 112, 147) },
- { "papayawhip", NSVG_RGB(255, 239, 213) },
- { "peachpuff", NSVG_RGB(255, 218, 185) },
- { "peru", NSVG_RGB(205, 133, 63) },
- { "pink", NSVG_RGB(255, 192, 203) },
- { "plum", NSVG_RGB(221, 160, 221) },
- { "powderblue", NSVG_RGB(176, 224, 230) },
- { "purple", NSVG_RGB(128, 0, 128) },
- { "rosybrown", NSVG_RGB(188, 143, 143) },
- { "royalblue", NSVG_RGB( 65, 105, 225) },
- { "saddlebrown", NSVG_RGB(139, 69, 19) },
- { "salmon", NSVG_RGB(250, 128, 114) },
- { "sandybrown", NSVG_RGB(244, 164, 96) },
- { "seagreen", NSVG_RGB( 46, 139, 87) },
- { "seashell", NSVG_RGB(255, 245, 238) },
- { "sienna", NSVG_RGB(160, 82, 45) },
- { "silver", NSVG_RGB(192, 192, 192) },
- { "skyblue", NSVG_RGB(135, 206, 235) },
- { "slateblue", NSVG_RGB(106, 90, 205) },
- { "slategray", NSVG_RGB(112, 128, 144) },
- { "slategrey", NSVG_RGB(112, 128, 144) },
- { "snow", NSVG_RGB(255, 250, 250) },
- { "springgreen", NSVG_RGB( 0, 255, 127) },
- { "steelblue", NSVG_RGB( 70, 130, 180) },
- { "tan", NSVG_RGB(210, 180, 140) },
- { "teal", NSVG_RGB( 0, 128, 128) },
- { "thistle", NSVG_RGB(216, 191, 216) },
- { "tomato", NSVG_RGB(255, 99, 71) },
- { "turquoise", NSVG_RGB( 64, 224, 208) },
- { "violet", NSVG_RGB(238, 130, 238) },
- { "wheat", NSVG_RGB(245, 222, 179) },
- { "whitesmoke", NSVG_RGB(245, 245, 245) },
- { "yellowgreen", NSVG_RGB(154, 205, 50) },
-#endif
-};
-
-static unsigned int nsvg__parseColorName(const char* str)
-{
- int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor);
-
- for (i = 0; i < ncolors; i++) {
- if (strcmp(nsvg__colors[i].name, str) == 0) {
- return nsvg__colors[i].color;
- }
- }
-
- return NSVG_RGB(128, 128, 128);
-}
-
-static unsigned int nsvg__parseColor(const char* str)
-{
- size_t len = 0;
- while(*str == ' ') ++str;
- len = strlen(str);
- if (len >= 1 && *str == '#')
- return nsvg__parseColorHex(str);
- else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(')
- return nsvg__parseColorRGB(str);
- return nsvg__parseColorName(str);
-}
-
-static float nsvg__parseOpacity(const char* str)
-{
- float val = nsvg__atof(str);
- if (val < 0.0f) val = 0.0f;
- if (val > 1.0f) val = 1.0f;
- return val;
-}
-
-static float nsvg__parseMiterLimit(const char* str)
-{
- float val = nsvg__atof(str);
- if (val < 0.0f) val = 0.0f;
- return val;
-}
-
-static int nsvg__parseUnits(const char* units)
-{
- if (units[0] == 'p' && units[1] == 'x')
- return NSVG_UNITS_PX;
- else if (units[0] == 'p' && units[1] == 't')
- return NSVG_UNITS_PT;
- else if (units[0] == 'p' && units[1] == 'c')
- return NSVG_UNITS_PC;
- else if (units[0] == 'm' && units[1] == 'm')
- return NSVG_UNITS_MM;
- else if (units[0] == 'c' && units[1] == 'm')
- return NSVG_UNITS_CM;
- else if (units[0] == 'i' && units[1] == 'n')
- return NSVG_UNITS_IN;
- else if (units[0] == '%')
- return NSVG_UNITS_PERCENT;
- else if (units[0] == 'e' && units[1] == 'm')
- return NSVG_UNITS_EM;
- else if (units[0] == 'e' && units[1] == 'x')
- return NSVG_UNITS_EX;
- return NSVG_UNITS_USER;
-}
-
-static int nsvg__isCoordinate(const char* s)
-{
- // optional sign
- if (*s == '-' || *s == '+')
- s++;
- // must have at least one digit, or start by a dot
- return (nsvg__isdigit(*s) || *s == '.');
-}
-
-static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str)
-{
- NSVGcoordinate coord = {0, NSVG_UNITS_USER};
- char buf[64];
- coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64));
- coord.value = nsvg__atof(buf);
- return coord;
-}
-
-static NSVGcoordinate nsvg__coord(float v, int units)
-{
- NSVGcoordinate coord = {v, units};
- return coord;
-}
-
-static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length)
-{
- NSVGcoordinate coord = nsvg__parseCoordinateRaw(str);
- return nsvg__convertToPixels(p, coord, orig, length);
-}
-
-static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na)
-{
- const char* end;
- const char* ptr;
- char it[64];
-
- *na = 0;
- ptr = str;
- while (*ptr && *ptr != '(') ++ptr;
- if (*ptr == 0)
- return 1;
- end = ptr;
- while (*end && *end != ')') ++end;
- if (*end == 0)
- return 1;
-
- while (ptr < end) {
- if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) {
- if (*na >= maxNa) return 0;
- ptr = nsvg__parseNumber(ptr, it, 64);
- args[(*na)++] = (float)nsvg__atof(it);
- } else {
- ++ptr;
- }
- }
- return (int)(end - str);
-}
-
-
-static int nsvg__parseMatrix(float* xform, const char* str)
-{
- float t[6];
- int na = 0;
- int len = nsvg__parseTransformArgs(str, t, 6, &na);
- if (na != 6) return len;
- memcpy(xform, t, sizeof(float)*6);
- return len;
-}
-
-static int nsvg__parseTranslate(float* xform, const char* str)
-{
- float args[2];
- float t[6];
- int na = 0;
- int len = nsvg__parseTransformArgs(str, args, 2, &na);
- if (na == 1) args[1] = 0.0;
-
- nsvg__xformSetTranslation(t, args[0], args[1]);
- memcpy(xform, t, sizeof(float)*6);
- return len;
-}
-
-static int nsvg__parseScale(float* xform, const char* str)
-{
- float args[2];
- int na = 0;
- float t[6];
- int len = nsvg__parseTransformArgs(str, args, 2, &na);
- if (na == 1) args[1] = args[0];
- nsvg__xformSetScale(t, args[0], args[1]);
- memcpy(xform, t, sizeof(float)*6);
- return len;
-}
-
-static int nsvg__parseSkewX(float* xform, const char* str)
-{
- float args[1];
- int na = 0;
- float t[6];
- int len = nsvg__parseTransformArgs(str, args, 1, &na);
- nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI);
- memcpy(xform, t, sizeof(float)*6);
- return len;
-}
-
-static int nsvg__parseSkewY(float* xform, const char* str)
-{
- float args[1];
- int na = 0;
- float t[6];
- int len = nsvg__parseTransformArgs(str, args, 1, &na);
- nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI);
- memcpy(xform, t, sizeof(float)*6);
- return len;
-}
-
-static int nsvg__parseRotate(float* xform, const char* str)
-{
- float args[3];
- int na = 0;
- float m[6];
- float t[6];
- int len = nsvg__parseTransformArgs(str, args, 3, &na);
- if (na == 1)
- args[1] = args[2] = 0.0f;
- nsvg__xformIdentity(m);
-
- if (na > 1) {
- nsvg__xformSetTranslation(t, -args[1], -args[2]);
- nsvg__xformMultiply(m, t);
- }
-
- nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI);
- nsvg__xformMultiply(m, t);
-
- if (na > 1) {
- nsvg__xformSetTranslation(t, args[1], args[2]);
- nsvg__xformMultiply(m, t);
- }
-
- memcpy(xform, m, sizeof(float)*6);
-
- return len;
-}
-
-static void nsvg__parseTransform(float* xform, const char* str)
-{
- float t[6];
- int len;
- nsvg__xformIdentity(xform);
- while (*str)
- {
- if (strncmp(str, "matrix", 6) == 0)
- len = nsvg__parseMatrix(t, str);
- else if (strncmp(str, "translate", 9) == 0)
- len = nsvg__parseTranslate(t, str);
- else if (strncmp(str, "scale", 5) == 0)
- len = nsvg__parseScale(t, str);
- else if (strncmp(str, "rotate", 6) == 0)
- len = nsvg__parseRotate(t, str);
- else if (strncmp(str, "skewX", 5) == 0)
- len = nsvg__parseSkewX(t, str);
- else if (strncmp(str, "skewY", 5) == 0)
- len = nsvg__parseSkewY(t, str);
- else{
- ++str;
- continue;
- }
- if (len != 0) {
- str += len;
- } else {
- ++str;
- continue;
- }
-
- nsvg__xformPremultiply(xform, t);
- }
-}
-
-static void nsvg__parseUrl(char* id, const char* str)
-{
- int i = 0;
- str += 4; // "url(";
- if (*str == '#')
- str++;
- while (i < 63 && *str != ')') {
- id[i] = *str++;
- i++;
- }
- id[i] = '\0';
-}
-
-static char nsvg__parseLineCap(const char* str)
-{
- if (strcmp(str, "butt") == 0)
- return NSVG_CAP_BUTT;
- else if (strcmp(str, "round") == 0)
- return NSVG_CAP_ROUND;
- else if (strcmp(str, "square") == 0)
- return NSVG_CAP_SQUARE;
- // TODO: handle inherit.
- return NSVG_CAP_BUTT;
-}
-
-static char nsvg__parseLineJoin(const char* str)
-{
- if (strcmp(str, "miter") == 0)
- return NSVG_JOIN_MITER;
- else if (strcmp(str, "round") == 0)
- return NSVG_JOIN_ROUND;
- else if (strcmp(str, "bevel") == 0)
- return NSVG_JOIN_BEVEL;
- // TODO: handle inherit.
- return NSVG_JOIN_MITER;
-}
-
-static char nsvg__parseFillRule(const char* str)
-{
- if (strcmp(str, "nonzero") == 0)
- return NSVG_FILLRULE_NONZERO;
- else if (strcmp(str, "evenodd") == 0)
- return NSVG_FILLRULE_EVENODD;
- // TODO: handle inherit.
- return NSVG_FILLRULE_NONZERO;
-}
-
-static const char* nsvg__getNextDashItem(const char* s, char* it)
-{
- int n = 0;
- it[0] = '\0';
- // Skip white spaces and commas
- while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
- // Advance until whitespace, comma or end.
- while (*s && (!nsvg__isspace(*s) && *s != ',')) {
- if (n < 63)
- it[n++] = *s;
- s++;
- }
- it[n++] = '\0';
- return s;
-}
-
-static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray)
-{
- char item[64];
- int count = 0, i;
- float sum = 0.0f;
-
- // Handle "none"
- if (str[0] == 'n')
- return 0;
-
- // Parse dashes
- while (*str) {
- str = nsvg__getNextDashItem(str, item);
- if (!*item) break;
- if (count < NSVG_MAX_DASHES)
- strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p)));
- }
-
- for (i = 0; i < count; i++)
- sum += strokeDashArray[i];
- if (sum <= 1e-6f)
- count = 0;
-
- return count;
-}
-
-static void nsvg__parseStyle(NSVGparser* p, const char* str);
-
-static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
-{
- float xform[6];
- NSVGattrib* attr = nsvg__getAttr(p);
- if (!attr) return 0;
-
- if (strcmp(name, "style") == 0) {
- nsvg__parseStyle(p, value);
- } else if (strcmp(name, "display") == 0) {
- if (strcmp(value, "none") == 0)
- attr->visible = 0;
- // Don't reset ->visible on display:inline, one display:none hides the whole subtree
-
- } else if (strcmp(name, "fill") == 0) {
- if (strcmp(value, "none") == 0) {
- attr->hasFill = 0;
- } else if (strncmp(value, "url(", 4) == 0) {
- attr->hasFill = 2;
- nsvg__parseUrl(attr->fillGradient, value);
- } else {
- attr->hasFill = 1;
- attr->fillColor = nsvg__parseColor(value);
- }
- } else if (strcmp(name, "opacity") == 0) {
- attr->opacity = nsvg__parseOpacity(value);
- } else if (strcmp(name, "fill-opacity") == 0) {
- attr->fillOpacity = nsvg__parseOpacity(value);
- } else if (strcmp(name, "stroke") == 0) {
- if (strcmp(value, "none") == 0) {
- attr->hasStroke = 0;
- } else if (strncmp(value, "url(", 4) == 0) {
- attr->hasStroke = 2;
- nsvg__parseUrl(attr->strokeGradient, value);
- } else {
- attr->hasStroke = 1;
- attr->strokeColor = nsvg__parseColor(value);
- }
- } else if (strcmp(name, "stroke-width") == 0) {
- attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
- } else if (strcmp(name, "stroke-dasharray") == 0) {
- attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray);
- } else if (strcmp(name, "stroke-dashoffset") == 0) {
- attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
- } else if (strcmp(name, "stroke-opacity") == 0) {
- attr->strokeOpacity = nsvg__parseOpacity(value);
- } else if (strcmp(name, "stroke-linecap") == 0) {
- attr->strokeLineCap = nsvg__parseLineCap(value);
- } else if (strcmp(name, "stroke-linejoin") == 0) {
- attr->strokeLineJoin = nsvg__parseLineJoin(value);
- } else if (strcmp(name, "stroke-miterlimit") == 0) {
- attr->miterLimit = nsvg__parseMiterLimit(value);
- } else if (strcmp(name, "fill-rule") == 0) {
- attr->fillRule = nsvg__parseFillRule(value);
- } else if (strcmp(name, "font-size") == 0) {
- attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
- } else if (strcmp(name, "transform") == 0) {
- nsvg__parseTransform(xform, value);
- nsvg__xformPremultiply(attr->xform, xform);
- } else if (strcmp(name, "stop-color") == 0) {
- attr->stopColor = nsvg__parseColor(value);
- } else if (strcmp(name, "stop-opacity") == 0) {
- attr->stopOpacity = nsvg__parseOpacity(value);
- } else if (strcmp(name, "offset") == 0) {
- attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f);
- } else if (strcmp(name, "id") == 0) {
- strncpy(attr->id, value, 63);
- attr->id[63] = '\0';
- } else {
- return 0;
- }
- return 1;
-}
-
-static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end)
-{
- const char* str;
- const char* val;
- char name[512];
- char value[512];
- int n;
-
- str = start;
- while (str < end && *str != ':') ++str;
-
- val = str;
-
- // Right Trim
- while (str > start && (*str == ':' || nsvg__isspace(*str))) --str;
- ++str;
-
- n = (int)(str - start);
- if (n > 511) n = 511;
- if (n) memcpy(name, start, n);
- name[n] = 0;
-
- while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val;
-
- n = (int)(end - val);
- if (n > 511) n = 511;
- if (n) memcpy(value, val, n);
- value[n] = 0;
-
- return nsvg__parseAttr(p, name, value);
-}
-
-static void nsvg__parseStyle(NSVGparser* p, const char* str)
-{
- const char* start;
- const char* end;
-
- while (*str) {
- // Left Trim
- while(*str && nsvg__isspace(*str)) ++str;
- start = str;
- while(*str && *str != ';') ++str;
- end = str;
-
- // Right Trim
- while (end > start && (*end == ';' || nsvg__isspace(*end))) --end;
- ++end;
-
- nsvg__parseNameValue(p, start, end);
- if (*str) ++str;
- }
-}
-
-static void nsvg__parseAttribs(NSVGparser* p, const char** attr)
-{
- int i;
- for (i = 0; attr[i]; i += 2)
- {
- if (strcmp(attr[i], "style") == 0)
- nsvg__parseStyle(p, attr[i + 1]);
- else
- nsvg__parseAttr(p, attr[i], attr[i + 1]);
- }
-}
-
-static int nsvg__getArgsPerElement(char cmd)
-{
- switch (cmd) {
- case 'v':
- case 'V':
- case 'h':
- case 'H':
- return 1;
- case 'm':
- case 'M':
- case 'l':
- case 'L':
- case 't':
- case 'T':
- return 2;
- case 'q':
- case 'Q':
- case 's':
- case 'S':
- return 4;
- case 'c':
- case 'C':
- return 6;
- case 'a':
- case 'A':
- return 7;
- case 'z':
- case 'Z':
- return 0;
- }
- return -1;
-}
-
-static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
-{
- if (rel) {
- *cpx += args[0];
- *cpy += args[1];
- } else {
- *cpx = args[0];
- *cpy = args[1];
- }
- nsvg__moveTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
-{
- if (rel) {
- *cpx += args[0];
- *cpy += args[1];
- } else {
- *cpx = args[0];
- *cpy = args[1];
- }
- nsvg__lineTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
-{
- if (rel)
- *cpx += args[0];
- else
- *cpx = args[0];
- nsvg__lineTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
-{
- if (rel)
- *cpy += args[0];
- else
- *cpy = args[0];
- nsvg__lineTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy,
- float* cpx2, float* cpy2, float* args, int rel)
-{
- float x2, y2, cx1, cy1, cx2, cy2;
-
- if (rel) {
- cx1 = *cpx + args[0];
- cy1 = *cpy + args[1];
- cx2 = *cpx + args[2];
- cy2 = *cpy + args[3];
- x2 = *cpx + args[4];
- y2 = *cpy + args[5];
- } else {
- cx1 = args[0];
- cy1 = args[1];
- cx2 = args[2];
- cy2 = args[3];
- x2 = args[4];
- y2 = args[5];
- }
-
- nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
-
- *cpx2 = cx2;
- *cpy2 = cy2;
- *cpx = x2;
- *cpy = y2;
-}
-
-static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy,
- float* cpx2, float* cpy2, float* args, int rel)
-{
- float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
-
- x1 = *cpx;
- y1 = *cpy;
- if (rel) {
- cx2 = *cpx + args[0];
- cy2 = *cpy + args[1];
- x2 = *cpx + args[2];
- y2 = *cpy + args[3];
- } else {
- cx2 = args[0];
- cy2 = args[1];
- x2 = args[2];
- y2 = args[3];
- }
-
- cx1 = 2*x1 - *cpx2;
- cy1 = 2*y1 - *cpy2;
-
- nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
-
- *cpx2 = cx2;
- *cpy2 = cy2;
- *cpx = x2;
- *cpy = y2;
-}
-
-static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy,
- float* cpx2, float* cpy2, float* args, int rel)
-{
- float x1, y1, x2, y2, cx, cy;
- float cx1, cy1, cx2, cy2;
-
- x1 = *cpx;
- y1 = *cpy;
- if (rel) {
- cx = *cpx + args[0];
- cy = *cpy + args[1];
- x2 = *cpx + args[2];
- y2 = *cpy + args[3];
- } else {
- cx = args[0];
- cy = args[1];
- x2 = args[2];
- y2 = args[3];
- }
-
- // Convert to cubic bezier
- cx1 = x1 + 2.0f/3.0f*(cx - x1);
- cy1 = y1 + 2.0f/3.0f*(cy - y1);
- cx2 = x2 + 2.0f/3.0f*(cx - x2);
- cy2 = y2 + 2.0f/3.0f*(cy - y2);
-
- nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
-
- *cpx2 = cx;
- *cpy2 = cy;
- *cpx = x2;
- *cpy = y2;
-}
-
-static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy,
- float* cpx2, float* cpy2, float* args, int rel)
-{
- float x1, y1, x2, y2, cx, cy;
- float cx1, cy1, cx2, cy2;
-
- x1 = *cpx;
- y1 = *cpy;
- if (rel) {
- x2 = *cpx + args[0];
- y2 = *cpy + args[1];
- } else {
- x2 = args[0];
- y2 = args[1];
- }
-
- cx = 2*x1 - *cpx2;
- cy = 2*y1 - *cpy2;
-
- // Convert to cubix bezier
- cx1 = x1 + 2.0f/3.0f*(cx - x1);
- cy1 = y1 + 2.0f/3.0f*(cy - y1);
- cx2 = x2 + 2.0f/3.0f*(cx - x2);
- cy2 = y2 + 2.0f/3.0f*(cy - y2);
-
- nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
-
- *cpx2 = cx;
- *cpy2 = cy;
- *cpx = x2;
- *cpy = y2;
-}
-
-static float nsvg__sqr(float x) { return x*x; }
-static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); }
-
-static float nsvg__vecrat(float ux, float uy, float vx, float vy)
-{
- return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy));
-}
-
-static float nsvg__vecang(float ux, float uy, float vx, float vy)
-{
- float r = nsvg__vecrat(ux,uy, vx,vy);
- if (r < -1.0f) r = -1.0f;
- if (r > 1.0f) r = 1.0f;
- return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r);
-}
-
-static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
-{
- // Ported from canvg (https://code.google.com/p/canvg/)
- float rx, ry, rotx;
- float x1, y1, x2, y2, cx, cy, dx, dy, d;
- float x1p, y1p, cxp, cyp, s, sa, sb;
- float ux, uy, vx, vy, a1, da;
- float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6];
- float sinrx, cosrx;
- int fa, fs;
- int i, ndivs;
- float hda, kappa;
-
- rx = fabsf(args[0]); // y radius
- ry = fabsf(args[1]); // x radius
- rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle
- fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc
- fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction
- x1 = *cpx; // start point
- y1 = *cpy;
- if (rel) { // end point
- x2 = *cpx + args[5];
- y2 = *cpy + args[6];
- } else {
- x2 = args[5];
- y2 = args[6];
- }
-
- dx = x1 - x2;
- dy = y1 - y2;
- d = sqrtf(dx*dx + dy*dy);
- if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
- // The arc degenerates to a line
- nsvg__lineTo(p, x2, y2);
- *cpx = x2;
- *cpy = y2;
- return;
- }
-
- sinrx = sinf(rotx);
- cosrx = cosf(rotx);
-
- // Convert to center point parameterization.
- // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
- // 1) Compute x1', y1'
- x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
- y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
- d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry);
- if (d > 1) {
- d = sqrtf(d);
- rx *= d;
- ry *= d;
- }
- // 2) Compute cx', cy'
- s = 0.0f;
- sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p);
- sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p);
- if (sa < 0.0f) sa = 0.0f;
- if (sb > 0.0f)
- s = sqrtf(sa / sb);
- if (fa == fs)
- s = -s;
- cxp = s * rx * y1p / ry;
- cyp = s * -ry * x1p / rx;
-
- // 3) Compute cx,cy from cx',cy'
- cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp;
- cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp;
-
- // 4) Calculate theta1, and delta theta.
- ux = (x1p - cxp) / rx;
- uy = (y1p - cyp) / ry;
- vx = (-x1p - cxp) / rx;
- vy = (-y1p - cyp) / ry;
- a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle
- da = nsvg__vecang(ux,uy, vx,vy); // Delta angle
-
-// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
-// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
-
- if (fs == 0 && da > 0)
- da -= 2 * NSVG_PI;
- else if (fs == 1 && da < 0)
- da += 2 * NSVG_PI;
-
- // Approximate the arc using cubic spline segments.
- t[0] = cosrx; t[1] = sinrx;
- t[2] = -sinrx; t[3] = cosrx;
- t[4] = cx; t[5] = cy;
-
- // Split arc into max 90 degree segments.
- // The loop assumes an iteration per end point (including start and end), this +1.
- ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f);
- hda = (da / (float)ndivs) / 2.0f;
- // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite)
- if ((hda < 1e-3f) && (hda > -1e-3f))
- hda *= 0.5f;
- else
- hda = (1.0f - cosf(hda)) / sinf(hda);
- kappa = fabsf(4.0f / 3.0f * hda);
- if (da < 0.0f)
- kappa = -kappa;
-
- for (i = 0; i <= ndivs; i++) {
- a = a1 + da * ((float)i/(float)ndivs);
- dx = cosf(a);
- dy = sinf(a);
- nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position
- nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent
- if (i > 0)
- nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y);
- px = x;
- py = y;
- ptanx = tanx;
- ptany = tany;
- }
-
- *cpx = x2;
- *cpy = y2;
-}
-
-static void nsvg__parsePath(NSVGparser* p, const char** attr)
-{
- const char* s = NULL;
- char cmd = '\0';
- float args[10];
- int nargs;
- int rargs = 0;
- char initPoint;
- float cpx, cpy, cpx2, cpy2;
- const char* tmp[4];
- char closedFlag;
- int i;
- char item[64];
-
- for (i = 0; attr[i]; i += 2) {
- if (strcmp(attr[i], "d") == 0) {
- s = attr[i + 1];
- } else {
- tmp[0] = attr[i];
- tmp[1] = attr[i + 1];
- tmp[2] = 0;
- tmp[3] = 0;
- nsvg__parseAttribs(p, tmp);
- }
- }
-
- if (s) {
- nsvg__resetPath(p);
- cpx = 0; cpy = 0;
- cpx2 = 0; cpy2 = 0;
- initPoint = 0;
- closedFlag = 0;
- nargs = 0;
-
- while (*s) {
- s = nsvg__getNextPathItem(s, item);
- if (!*item) break;
- if (cmd != '\0' && nsvg__isCoordinate(item)) {
- if (nargs < 10)
- args[nargs++] = (float)nsvg__atof(item);
- if (nargs >= rargs) {
- switch (cmd) {
- case 'm':
- case 'M':
- nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
- // Moveto can be followed by multiple coordinate pairs,
- // which should be treated as linetos.
- cmd = (cmd == 'm') ? 'l' : 'L';
- rargs = nsvg__getArgsPerElement(cmd);
- cpx2 = cpx; cpy2 = cpy;
- initPoint = 1;
- break;
- case 'l':
- case 'L':
- nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
- cpx2 = cpx; cpy2 = cpy;
- break;
- case 'H':
- case 'h':
- nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
- cpx2 = cpx; cpy2 = cpy;
- break;
- case 'V':
- case 'v':
- nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
- cpx2 = cpx; cpy2 = cpy;
- break;
- case 'C':
- case 'c':
- nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
- break;
- case 'S':
- case 's':
- nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
- break;
- case 'Q':
- case 'q':
- nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
- break;
- case 'T':
- case 't':
- nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0);
- break;
- case 'A':
- case 'a':
- nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
- cpx2 = cpx; cpy2 = cpy;
- break;
- default:
- if (nargs >= 2) {
- cpx = args[nargs-2];
- cpy = args[nargs-1];
- cpx2 = cpx; cpy2 = cpy;
- }
- break;
- }
- nargs = 0;
- }
- } else {
- cmd = item[0];
- if (cmd == 'M' || cmd == 'm') {
- // Commit path.
- if (p->npts > 0)
- nsvg__addPath(p, closedFlag);
- // Start new subpath.
- nsvg__resetPath(p);
- closedFlag = 0;
- nargs = 0;
- } else if (initPoint == 0) {
- // Do not allow other commands until initial point has been set (moveTo called once).
- cmd = '\0';
- }
- if (cmd == 'Z' || cmd == 'z') {
- closedFlag = 1;
- // Commit path.
- if (p->npts > 0) {
- // Move current point to first point
- cpx = p->pts[0];
- cpy = p->pts[1];
- cpx2 = cpx; cpy2 = cpy;
- nsvg__addPath(p, closedFlag);
- }
- // Start new subpath.
- nsvg__resetPath(p);
- nsvg__moveTo(p, cpx, cpy);
- closedFlag = 0;
- nargs = 0;
- }
- rargs = nsvg__getArgsPerElement(cmd);
- if (rargs == -1) {
- // Command not recognized
- cmd = '\0';
- rargs = 0;
- }
- }
- }
- // Commit path.
- if (p->npts)
- nsvg__addPath(p, closedFlag);
- }
-
- nsvg__addShape(p);
-}
-
-static void nsvg__parseRect(NSVGparser* p, const char** attr)
-{
- float x = 0.0f;
- float y = 0.0f;
- float w = 0.0f;
- float h = 0.0f;
- float rx = -1.0f; // marks not set
- float ry = -1.0f;
- int i;
-
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
- if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
- if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p));
- if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p));
- if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
- if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
- }
- }
-
- if (rx < 0.0f && ry > 0.0f) rx = ry;
- if (ry < 0.0f && rx > 0.0f) ry = rx;
- if (rx < 0.0f) rx = 0.0f;
- if (ry < 0.0f) ry = 0.0f;
- if (rx > w/2.0f) rx = w/2.0f;
- if (ry > h/2.0f) ry = h/2.0f;
-
- if (w != 0.0f && h != 0.0f) {
- nsvg__resetPath(p);
-
- if (rx < 0.00001f || ry < 0.0001f) {
- nsvg__moveTo(p, x, y);
- nsvg__lineTo(p, x+w, y);
- nsvg__lineTo(p, x+w, y+h);
- nsvg__lineTo(p, x, y+h);
- } else {
- // Rounded rectangle
- nsvg__moveTo(p, x+rx, y);
- nsvg__lineTo(p, x+w-rx, y);
- nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry);
- nsvg__lineTo(p, x+w, y+h-ry);
- nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h);
- nsvg__lineTo(p, x+rx, y+h);
- nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry);
- nsvg__lineTo(p, x, y+ry);
- nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y);
- }
-
- nsvg__addPath(p, 1);
-
- nsvg__addShape(p);
- }
-}
-
-static void nsvg__parseCircle(NSVGparser* p, const char** attr)
-{
- float cx = 0.0f;
- float cy = 0.0f;
- float r = 0.0f;
- int i;
-
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
- if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
- if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p)));
- }
- }
-
- if (r > 0.0f) {
- nsvg__resetPath(p);
-
- nsvg__moveTo(p, cx+r, cy);
- nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r);
- nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy);
- nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r);
- nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy);
-
- nsvg__addPath(p, 1);
-
- nsvg__addShape(p);
- }
-}
-
-static void nsvg__parseEllipse(NSVGparser* p, const char** attr)
-{
- float cx = 0.0f;
- float cy = 0.0f;
- float rx = 0.0f;
- float ry = 0.0f;
- int i;
-
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
- if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
- if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)));
- if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)));
- }
- }
-
- if (rx > 0.0f && ry > 0.0f) {
-
- nsvg__resetPath(p);
-
- nsvg__moveTo(p, cx+rx, cy);
- nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry);
- nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy);
- nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry);
- nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy);
-
- nsvg__addPath(p, 1);
-
- nsvg__addShape(p);
- }
-}
-
-static void nsvg__parseLine(NSVGparser* p, const char** attr)
-{
- float x1 = 0.0;
- float y1 = 0.0;
- float x2 = 0.0;
- float y2 = 0.0;
- int i;
-
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
- if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
- if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
- if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
- }
- }
-
- nsvg__resetPath(p);
-
- nsvg__moveTo(p, x1, y1);
- nsvg__lineTo(p, x2, y2);
-
- nsvg__addPath(p, 0);
-
- nsvg__addShape(p);
-}
-
-static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
-{
- int i;
- const char* s;
- float args[2];
- int nargs, npts = 0;
- char item[64];
-
- nsvg__resetPath(p);
-
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "points") == 0) {
- s = attr[i + 1];
- nargs = 0;
- while (*s) {
- s = nsvg__getNextPathItem(s, item);
- args[nargs++] = (float)nsvg__atof(item);
- if (nargs >= 2) {
- if (npts == 0)
- nsvg__moveTo(p, args[0], args[1]);
- else
- nsvg__lineTo(p, args[0], args[1]);
- nargs = 0;
- npts++;
- }
- }
- }
- }
- }
-
- nsvg__addPath(p, (char)closeFlag);
-
- nsvg__addShape(p);
-}
-
-static void nsvg__parseSVG(NSVGparser* p, const char** attr)
-{
- int i;
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "width") == 0) {
- p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
- } else if (strcmp(attr[i], "height") == 0) {
- p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
- } else if (strcmp(attr[i], "viewBox") == 0) {
- const char *s = attr[i + 1];
- char buf[64];
- s = nsvg__parseNumber(s, buf, 64);
- p->viewMinx = nsvg__atof(buf);
- while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++;
- if (!*s) return;
- s = nsvg__parseNumber(s, buf, 64);
- p->viewMiny = nsvg__atof(buf);
- while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++;
- if (!*s) return;
- s = nsvg__parseNumber(s, buf, 64);
- p->viewWidth = nsvg__atof(buf);
- while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++;
- if (!*s) return;
- s = nsvg__parseNumber(s, buf, 64);
- p->viewHeight = nsvg__atof(buf);
- } else if (strcmp(attr[i], "preserveAspectRatio") == 0) {
- if (strstr(attr[i + 1], "none") != 0) {
- // No uniform scaling
- p->alignType = NSVG_ALIGN_NONE;
- } else {
- // Parse X align
- if (strstr(attr[i + 1], "xMin") != 0)
- p->alignX = NSVG_ALIGN_MIN;
- else if (strstr(attr[i + 1], "xMid") != 0)
- p->alignX = NSVG_ALIGN_MID;
- else if (strstr(attr[i + 1], "xMax") != 0)
- p->alignX = NSVG_ALIGN_MAX;
- // Parse X align
- if (strstr(attr[i + 1], "yMin") != 0)
- p->alignY = NSVG_ALIGN_MIN;
- else if (strstr(attr[i + 1], "yMid") != 0)
- p->alignY = NSVG_ALIGN_MID;
- else if (strstr(attr[i + 1], "yMax") != 0)
- p->alignY = NSVG_ALIGN_MAX;
- // Parse meet/slice
- p->alignType = NSVG_ALIGN_MEET;
- if (strstr(attr[i + 1], "slice") != 0)
- p->alignType = NSVG_ALIGN_SLICE;
- }
- }
- }
- }
-}
-
-static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
-{
- int i;
- NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
- if (grad == NULL) return;
- memset(grad, 0, sizeof(NSVGgradientData));
- grad->units = NSVG_OBJECT_SPACE;
- grad->type = type;
- if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) {
- grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
- grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
- grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT);
- grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
- } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) {
- grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
- grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
- grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
- }
-
- nsvg__xformIdentity(grad->xform);
-
- for (i = 0; attr[i]; i += 2) {
- if (strcmp(attr[i], "id") == 0) {
- strncpy(grad->id, attr[i+1], 63);
- grad->id[63] = '\0';
- } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "gradientUnits") == 0) {
- if (strcmp(attr[i+1], "objectBoundingBox") == 0)
- grad->units = NSVG_OBJECT_SPACE;
- else
- grad->units = NSVG_USER_SPACE;
- } else if (strcmp(attr[i], "gradientTransform") == 0) {
- nsvg__parseTransform(grad->xform, attr[i + 1]);
- } else if (strcmp(attr[i], "cx") == 0) {
- grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]);
- } else if (strcmp(attr[i], "cy") == 0) {
- grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]);
- } else if (strcmp(attr[i], "r") == 0) {
- grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]);
- } else if (strcmp(attr[i], "fx") == 0) {
- grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]);
- } else if (strcmp(attr[i], "fy") == 0) {
- grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]);
- } else if (strcmp(attr[i], "x1") == 0) {
- grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]);
- } else if (strcmp(attr[i], "y1") == 0) {
- grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]);
- } else if (strcmp(attr[i], "x2") == 0) {
- grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]);
- } else if (strcmp(attr[i], "y2") == 0) {
- grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]);
- } else if (strcmp(attr[i], "spreadMethod") == 0) {
- if (strcmp(attr[i+1], "pad") == 0)
- grad->spread = NSVG_SPREAD_PAD;
- else if (strcmp(attr[i+1], "reflect") == 0)
- grad->spread = NSVG_SPREAD_REFLECT;
- else if (strcmp(attr[i+1], "repeat") == 0)
- grad->spread = NSVG_SPREAD_REPEAT;
- } else if (strcmp(attr[i], "xlink:href") == 0) {
- const char *href = attr[i+1];
- strncpy(grad->ref, href+1, 62);
- grad->ref[62] = '\0';
- }
- }
- }
-
- grad->next = p->gradients;
- p->gradients = grad;
-}
-
-static void nsvg__parseGradientStop(NSVGparser* p, const char** attr)
-{
- NSVGattrib* curAttr = nsvg__getAttr(p);
- NSVGgradientData* grad;
- NSVGgradientStop* stop;
- int i, idx;
-
- curAttr->stopOffset = 0;
- curAttr->stopColor = 0;
- curAttr->stopOpacity = 1.0f;
-
- for (i = 0; attr[i]; i += 2) {
- nsvg__parseAttr(p, attr[i], attr[i + 1]);
- }
-
- // Add stop to the last gradient.
- grad = p->gradients;
- if (grad == NULL) return;
-
- grad->nstops++;
- grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops);
- if (grad->stops == NULL) return;
-
- // Insert
- idx = grad->nstops-1;
- for (i = 0; i < grad->nstops-1; i++) {
- if (curAttr->stopOffset < grad->stops[i].offset) {
- idx = i;
- break;
- }
- }
- if (idx != grad->nstops-1) {
- for (i = grad->nstops-1; i > idx; i--)
- grad->stops[i] = grad->stops[i-1];
- }
-
- stop = &grad->stops[idx];
- stop->color = curAttr->stopColor;
- stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24;
- stop->offset = curAttr->stopOffset;
-}
-
-static void nsvg__startElement(void* ud, const char* el, const char** attr)
-{
- NSVGparser* p = (NSVGparser*)ud;
-
- if (p->defsFlag) {
- // Skip everything but gradients in defs
- if (strcmp(el, "linearGradient") == 0) {
- nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
- } else if (strcmp(el, "radialGradient") == 0) {
- nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
- } else if (strcmp(el, "stop") == 0) {
- nsvg__parseGradientStop(p, attr);
- }
- return;
- }
-
- if (strcmp(el, "g") == 0) {
- nsvg__pushAttr(p);
- nsvg__parseAttribs(p, attr);
- } else if (strcmp(el, "path") == 0) {
- if (p->pathFlag) // Do not allow nested paths.
- return;
- nsvg__pushAttr(p);
- nsvg__parsePath(p, attr);
- nsvg__popAttr(p);
- } else if (strcmp(el, "rect") == 0) {
- nsvg__pushAttr(p);
- nsvg__parseRect(p, attr);
- nsvg__popAttr(p);
- } else if (strcmp(el, "circle") == 0) {
- nsvg__pushAttr(p);
- nsvg__parseCircle(p, attr);
- nsvg__popAttr(p);
- } else if (strcmp(el, "ellipse") == 0) {
- nsvg__pushAttr(p);
- nsvg__parseEllipse(p, attr);
- nsvg__popAttr(p);
- } else if (strcmp(el, "line") == 0) {
- nsvg__pushAttr(p);
- nsvg__parseLine(p, attr);
- nsvg__popAttr(p);
- } else if (strcmp(el, "polyline") == 0) {
- nsvg__pushAttr(p);
- nsvg__parsePoly(p, attr, 0);
- nsvg__popAttr(p);
- } else if (strcmp(el, "polygon") == 0) {
- nsvg__pushAttr(p);
- nsvg__parsePoly(p, attr, 1);
- nsvg__popAttr(p);
- } else if (strcmp(el, "linearGradient") == 0) {
- nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
- } else if (strcmp(el, "radialGradient") == 0) {
- nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
- } else if (strcmp(el, "stop") == 0) {
- nsvg__parseGradientStop(p, attr);
- } else if (strcmp(el, "defs") == 0) {
- p->defsFlag = 1;
- } else if (strcmp(el, "svg") == 0) {
- nsvg__parseSVG(p, attr);
- }
-}
-
-static void nsvg__endElement(void* ud, const char* el)
-{
- NSVGparser* p = (NSVGparser*)ud;
-
- if (strcmp(el, "g") == 0) {
- nsvg__popAttr(p);
- } else if (strcmp(el, "path") == 0) {
- p->pathFlag = 0;
- } else if (strcmp(el, "defs") == 0) {
- p->defsFlag = 0;
- }
-}
-
-static void nsvg__content(void* ud, const char* s)
-{
- NSVG_NOTUSED(ud);
- NSVG_NOTUSED(s);
- // empty
-}
-
-static void nsvg__imageBounds(NSVGparser* p, float* bounds)
-{
- NSVGshape* shape;
- shape = p->image->shapes;
- if (shape == NULL) {
- bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0;
- return;
- }
- bounds[0] = shape->bounds[0];
- bounds[1] = shape->bounds[1];
- bounds[2] = shape->bounds[2];
- bounds[3] = shape->bounds[3];
- for (shape = shape->next; shape != NULL; shape = shape->next) {
- bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]);
- bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]);
- bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]);
- bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]);
- }
-}
-
-static float nsvg__viewAlign(float content, float container, int type)
-{
- if (type == NSVG_ALIGN_MIN)
- return 0;
- else if (type == NSVG_ALIGN_MAX)
- return container - content;
- // mid
- return (container - content) * 0.5f;
-}
-
-static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy)
-{
- float t[6];
- nsvg__xformSetTranslation(t, tx, ty);
- nsvg__xformMultiply (grad->xform, t);
-
- nsvg__xformSetScale(t, sx, sy);
- nsvg__xformMultiply (grad->xform, t);
-}
-
-static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
-{
- NSVGshape* shape;
- NSVGpath* path;
- float tx, ty, sx, sy, us, bounds[4], t[6], avgs;
- int i;
- float* pt;
-
- // Guess image size if not set completely.
- nsvg__imageBounds(p, bounds);
-
- if (p->viewWidth == 0) {
- if (p->image->width > 0) {
- p->viewWidth = p->image->width;
- } else {
- p->viewMinx = bounds[0];
- p->viewWidth = bounds[2] - bounds[0];
- }
- }
- if (p->viewHeight == 0) {
- if (p->image->height > 0) {
- p->viewHeight = p->image->height;
- } else {
- p->viewMiny = bounds[1];
- p->viewHeight = bounds[3] - bounds[1];
- }
- }
- if (p->image->width == 0)
- p->image->width = p->viewWidth;
- if (p->image->height == 0)
- p->image->height = p->viewHeight;
-
- tx = -p->viewMinx;
- ty = -p->viewMiny;
- sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0;
- sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0;
- // Unit scaling
- us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f);
-
- // Fix aspect ratio
- if (p->alignType == NSVG_ALIGN_MEET) {
- // fit whole image into viewbox
- sx = sy = nsvg__minf(sx, sy);
- tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
- ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
- } else if (p->alignType == NSVG_ALIGN_SLICE) {
- // fill whole viewbox with image
- sx = sy = nsvg__maxf(sx, sy);
- tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
- ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
- }
-
- // Transform
- sx *= us;
- sy *= us;
- avgs = (sx+sy) / 2.0f;
- for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
- shape->bounds[0] = (shape->bounds[0] + tx) * sx;
- shape->bounds[1] = (shape->bounds[1] + ty) * sy;
- shape->bounds[2] = (shape->bounds[2] + tx) * sx;
- shape->bounds[3] = (shape->bounds[3] + ty) * sy;
- for (path = shape->paths; path != NULL; path = path->next) {
- path->bounds[0] = (path->bounds[0] + tx) * sx;
- path->bounds[1] = (path->bounds[1] + ty) * sy;
- path->bounds[2] = (path->bounds[2] + tx) * sx;
- path->bounds[3] = (path->bounds[3] + ty) * sy;
- for (i =0; i < path->npts; i++) {
- pt = &path->pts[i*2];
- pt[0] = (pt[0] + tx) * sx;
- pt[1] = (pt[1] + ty) * sy;
- }
- }
-
- if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) {
- nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy);
- memcpy(t, shape->fill.gradient->xform, sizeof(float)*6);
- nsvg__xformInverse(shape->fill.gradient->xform, t);
- }
- if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) {
- nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy);
- memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6);
- nsvg__xformInverse(shape->stroke.gradient->xform, t);
- }
-
- shape->strokeWidth *= avgs;
- shape->strokeDashOffset *= avgs;
- for (i = 0; i < shape->strokeDashCount; i++)
- shape->strokeDashArray[i] *= avgs;
- }
-}
-
-NSVGimage* nsvgParse(char* input, const char* units, float dpi)
-{
- NSVGparser* p;
- NSVGimage* ret = 0;
-
- p = nsvg__createParser();
- if (p == NULL) {
- return NULL;
- }
- p->dpi = dpi;
-
- nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p);
-
- // Scale to viewBox
- nsvg__scaleToViewbox(p, units);
-
- ret = p->image;
- p->image = NULL;
-
- nsvg__deleteParser(p);
-
- return ret;
-}
-
-NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
-{
- FILE* fp = NULL;
- size_t size;
- char* data = NULL;
- NSVGimage* image = NULL;
-
- fp = fopen(filename, "rb");
- if (!fp) goto error;
- fseek(fp, 0, SEEK_END);
- size = ftell(fp);
- fseek(fp, 0, SEEK_SET);
- data = (char*)malloc(size+1);
- if (data == NULL) goto error;
- if (fread(data, 1, size, fp) != size) goto error;
- data[size] = '\0'; // Must be null terminated.
- fclose(fp);
- image = nsvgParse(data, units, dpi);
- free(data);
-
- return image;
-
-error:
- if (fp) fclose(fp);
- if (data) free(data);
- if (image) nsvgDelete(image);
- return NULL;
-}
-
-NSVGpath* nsvgDuplicatePath(NSVGpath* p)
-{
- NSVGpath* res = NULL;
-
- if (p == NULL)
- return NULL;
-
- res = (NSVGpath*)malloc(sizeof(NSVGpath));
- if (res == NULL) goto error;
- memset(res, 0, sizeof(NSVGpath));
-
- res->pts = (float*)malloc(p->npts*2*sizeof(float));
- if (res->pts == NULL) goto error;
- memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2);
- res->npts = p->npts;
-
- memcpy(res->bounds, p->bounds, sizeof(p->bounds));
-
- res->closed = p->closed;
-
- return res;
-
-error:
- if (res != NULL) {
- free(res->pts);
- free(res);
- }
- return NULL;
-}
-
-void nsvgDelete(NSVGimage* image)
-{
- NSVGshape *snext, *shape;
- if (image == NULL) return;
- shape = image->shapes;
- while (shape != NULL) {
- snext = shape->next;
- nsvg__deletePaths(shape->paths);
- nsvg__deletePaint(&shape->fill);
- nsvg__deletePaint(&shape->stroke);
- free(shape);
- shape = snext;
- }
- free(image);
-}
-
-#endif
diff --git a/thirdparty/nanosvg/nanosvgrast.h b/thirdparty/nanosvg/nanosvgrast.h
deleted file mode 100644
index b740c316ca..0000000000
--- a/thirdparty/nanosvg/nanosvgrast.h
+++ /dev/null
@@ -1,1452 +0,0 @@
-/*
- * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- *
- * The polygon rasterization is heavily based on stb_truetype rasterizer
- * by Sean Barrett - http://nothings.org/
- *
- */
-
-#ifndef NANOSVGRAST_H
-#define NANOSVGRAST_H
-
-#ifndef NANOSVGRAST_CPLUSPLUS
-#ifdef __cplusplus
-extern "C" {
-#endif
-#endif
-
-typedef struct NSVGrasterizer NSVGrasterizer;
-
-/* Example Usage:
- // Load SVG
- NSVGimage* image;
- image = nsvgParseFromFile("test.svg", "px", 96);
-
- // Create rasterizer (can be used to render multiple images).
- struct NSVGrasterizer* rast = nsvgCreateRasterizer();
- // Allocate memory for image
- unsigned char* img = malloc(w*h*4);
- // Rasterize
- nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4);
-*/
-
-// Allocated rasterizer context.
-NSVGrasterizer* nsvgCreateRasterizer();
-
-// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha)
-// r - pointer to rasterizer context
-// image - pointer to image to rasterize
-// tx,ty - image offset (applied after scaling)
-// scale - image scale
-// dst - pointer to destination image data, 4 bytes per pixel (RGBA)
-// w - width of the image to render
-// h - height of the image to render
-// stride - number of bytes per scaleline in the destination buffer
-void nsvgRasterize(NSVGrasterizer* r,
- NSVGimage* image, float tx, float ty, float scale,
- unsigned char* dst, int w, int h, int stride);
-
-// Deletes rasterizer context.
-void nsvgDeleteRasterizer(NSVGrasterizer*);
-
-
-#ifndef NANOSVGRAST_CPLUSPLUS
-#ifdef __cplusplus
-}
-#endif
-#endif
-
-#endif // NANOSVGRAST_H
-
-#ifdef NANOSVGRAST_IMPLEMENTATION
-
-#include <math.h>
-
-#define NSVG__SUBSAMPLES 5
-#define NSVG__FIXSHIFT 10
-#define NSVG__FIX (1 << NSVG__FIXSHIFT)
-#define NSVG__FIXMASK (NSVG__FIX-1)
-#define NSVG__MEMPAGE_SIZE 1024
-
-typedef struct NSVGedge {
- float x0,y0, x1,y1;
- int dir;
- struct NSVGedge* next;
-} NSVGedge;
-
-typedef struct NSVGpoint {
- float x, y;
- float dx, dy;
- float len;
- float dmx, dmy;
- unsigned char flags;
-} NSVGpoint;
-
-typedef struct NSVGactiveEdge {
- int x,dx;
- float ey;
- int dir;
- struct NSVGactiveEdge *next;
-} NSVGactiveEdge;
-
-typedef struct NSVGmemPage {
- unsigned char mem[NSVG__MEMPAGE_SIZE];
- int size;
- struct NSVGmemPage* next;
-} NSVGmemPage;
-
-typedef struct NSVGcachedPaint {
- char type;
- char spread;
- float xform[6];
- unsigned int colors[256];
-} NSVGcachedPaint;
-
-struct NSVGrasterizer
-{
- float px, py;
-
- float tessTol;
- float distTol;
-
- NSVGedge* edges;
- int nedges;
- int cedges;
-
- NSVGpoint* points;
- int npoints;
- int cpoints;
-
- NSVGpoint* points2;
- int npoints2;
- int cpoints2;
-
- NSVGactiveEdge* freelist;
- NSVGmemPage* pages;
- NSVGmemPage* curpage;
-
- unsigned char* scanline;
- int cscanline;
-
- unsigned char* bitmap;
- int width, height, stride;
-};
-
-NSVGrasterizer* nsvgCreateRasterizer()
-{
- NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer));
- if (r == NULL) goto error;
- memset(r, 0, sizeof(NSVGrasterizer));
-
- r->tessTol = 0.25f;
- r->distTol = 0.01f;
-
- return r;
-
-error:
- nsvgDeleteRasterizer(r);
- return NULL;
-}
-
-void nsvgDeleteRasterizer(NSVGrasterizer* r)
-{
- NSVGmemPage* p;
-
- if (r == NULL) return;
-
- p = r->pages;
- while (p != NULL) {
- NSVGmemPage* next = p->next;
- free(p);
- p = next;
- }
-
- if (r->edges) free(r->edges);
- if (r->points) free(r->points);
- if (r->points2) free(r->points2);
- if (r->scanline) free(r->scanline);
-
- free(r);
-}
-
-static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur)
-{
- NSVGmemPage *newp;
-
- // If using existing chain, return the next page in chain
- if (cur != NULL && cur->next != NULL) {
- return cur->next;
- }
-
- // Alloc new page
- newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage));
- if (newp == NULL) return NULL;
- memset(newp, 0, sizeof(NSVGmemPage));
-
- // Add to linked list
- if (cur != NULL)
- cur->next = newp;
- else
- r->pages = newp;
-
- return newp;
-}
-
-static void nsvg__resetPool(NSVGrasterizer* r)
-{
- NSVGmemPage* p = r->pages;
- while (p != NULL) {
- p->size = 0;
- p = p->next;
- }
- r->curpage = r->pages;
-}
-
-static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size)
-{
- unsigned char* buf;
- if (size > NSVG__MEMPAGE_SIZE) return NULL;
- if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) {
- r->curpage = nsvg__nextPage(r, r->curpage);
- }
- buf = &r->curpage->mem[r->curpage->size];
- r->curpage->size += size;
- return buf;
-}
-
-static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol)
-{
- float dx = x2 - x1;
- float dy = y2 - y1;
- return dx*dx + dy*dy < tol*tol;
-}
-
-static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags)
-{
- NSVGpoint* pt;
-
- if (r->npoints > 0) {
- pt = &r->points[r->npoints-1];
- if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) {
- pt->flags = (unsigned char)(pt->flags | flags);
- return;
- }
- }
-
- if (r->npoints+1 > r->cpoints) {
- r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
- r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
- if (r->points == NULL) return;
- }
-
- pt = &r->points[r->npoints];
- pt->x = x;
- pt->y = y;
- pt->flags = (unsigned char)flags;
- r->npoints++;
-}
-
-static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt)
-{
- if (r->npoints+1 > r->cpoints) {
- r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
- r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
- if (r->points == NULL) return;
- }
- r->points[r->npoints] = pt;
- r->npoints++;
-}
-
-static void nsvg__duplicatePoints(NSVGrasterizer* r)
-{
- if (r->npoints > r->cpoints2) {
- r->cpoints2 = r->npoints;
- r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2);
- if (r->points2 == NULL) return;
- }
-
- memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints);
- r->npoints2 = r->npoints;
-}
-
-static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1)
-{
- NSVGedge* e;
-
- // Skip horizontal edges
- if (y0 == y1)
- return;
-
- if (r->nedges+1 > r->cedges) {
- r->cedges = r->cedges > 0 ? r->cedges * 2 : 64;
- r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges);
- if (r->edges == NULL) return;
- }
-
- e = &r->edges[r->nedges];
- r->nedges++;
-
- if (y0 < y1) {
- e->x0 = x0;
- e->y0 = y0;
- e->x1 = x1;
- e->y1 = y1;
- e->dir = 1;
- } else {
- e->x0 = x1;
- e->y0 = y1;
- e->x1 = x0;
- e->y1 = y0;
- e->dir = -1;
- }
-}
-
-static float nsvg__normalize(float *x, float* y)
-{
- float d = sqrtf((*x)*(*x) + (*y)*(*y));
- if (d > 1e-6f) {
- float id = 1.0f / d;
- *x *= id;
- *y *= id;
- }
- return d;
-}
-
-static float nsvg__absf(float x) { return x < 0 ? -x : x; }
-
-static void nsvg__flattenCubicBez(NSVGrasterizer* r,
- float x1, float y1, float x2, float y2,
- float x3, float y3, float x4, float y4,
- int level, int type)
-{
- float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
- float dx,dy,d2,d3;
-
- if (level > 10) return;
-
- x12 = (x1+x2)*0.5f;
- y12 = (y1+y2)*0.5f;
- x23 = (x2+x3)*0.5f;
- y23 = (y2+y3)*0.5f;
- x34 = (x3+x4)*0.5f;
- y34 = (y3+y4)*0.5f;
- x123 = (x12+x23)*0.5f;
- y123 = (y12+y23)*0.5f;
-
- dx = x4 - x1;
- dy = y4 - y1;
- d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx));
- d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx));
-
- if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) {
- nsvg__addPathPoint(r, x4, y4, type);
- return;
- }
-
- x234 = (x23+x34)*0.5f;
- y234 = (y23+y34)*0.5f;
- x1234 = (x123+x234)*0.5f;
- y1234 = (y123+y234)*0.5f;
-
- nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0);
- nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
-}
-
-static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
-{
- int i, j;
- NSVGpath* path;
-
- for (path = shape->paths; path != NULL; path = path->next) {
- r->npoints = 0;
- // Flatten path
- nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
- for (i = 0; i < path->npts-1; i += 3) {
- float* p = &path->pts[i*2];
- nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0);
- }
- // Close path
- nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0);
- // Build edges
- for (i = 0, j = r->npoints-1; i < r->npoints; j = i++)
- nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y);
- }
-}
-
-enum NSVGpointFlags
-{
- NSVG_PT_CORNER = 0x01,
- NSVG_PT_BEVEL = 0x02,
- NSVG_PT_LEFT = 0x04
-};
-
-static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
-{
- float w = lineWidth * 0.5f;
- float dx = p1->x - p0->x;
- float dy = p1->y - p0->y;
- float len = nsvg__normalize(&dx, &dy);
- float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f;
- float dlx = dy, dly = -dx;
- float lx = px - dlx*w, ly = py - dly*w;
- float rx = px + dlx*w, ry = py + dly*w;
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
-}
-
-static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
-{
- float w = lineWidth * 0.5f;
- float px = p->x, py = p->y;
- float dlx = dy, dly = -dx;
- float lx = px - dlx*w, ly = py - dly*w;
- float rx = px + dlx*w, ry = py + dly*w;
-
- nsvg__addEdge(r, lx, ly, rx, ry);
-
- if (connect) {
- nsvg__addEdge(r, left->x, left->y, lx, ly);
- nsvg__addEdge(r, rx, ry, right->x, right->y);
- }
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
-}
-
-static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
-{
- float w = lineWidth * 0.5f;
- float px = p->x - dx*w, py = p->y - dy*w;
- float dlx = dy, dly = -dx;
- float lx = px - dlx*w, ly = py - dly*w;
- float rx = px + dlx*w, ry = py + dly*w;
-
- nsvg__addEdge(r, lx, ly, rx, ry);
-
- if (connect) {
- nsvg__addEdge(r, left->x, left->y, lx, ly);
- nsvg__addEdge(r, rx, ry, right->x, right->y);
- }
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
-}
-
-#ifndef NSVG_PI
-#define NSVG_PI (3.14159265358979323846264338327f)
-#endif
-
-static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect)
-{
- int i;
- float w = lineWidth * 0.5f;
- float px = p->x, py = p->y;
- float dlx = dy, dly = -dx;
- float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0;
-
- for (i = 0; i < ncap; i++) {
- float a = (float)i/(float)(ncap-1)*NSVG_PI;
- float ax = cosf(a) * w, ay = sinf(a) * w;
- float x = px - dlx*ax - dx*ay;
- float y = py - dly*ax - dy*ay;
-
- if (i > 0)
- nsvg__addEdge(r, prevx, prevy, x, y);
-
- prevx = x;
- prevy = y;
-
- if (i == 0) {
- lx = x; ly = y;
- } else if (i == ncap-1) {
- rx = x; ry = y;
- }
- }
-
- if (connect) {
- nsvg__addEdge(r, left->x, left->y, lx, ly);
- nsvg__addEdge(r, rx, ry, right->x, right->y);
- }
-
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
-}
-
-static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
-{
- float w = lineWidth * 0.5f;
- float dlx0 = p0->dy, dly0 = -p0->dx;
- float dlx1 = p1->dy, dly1 = -p1->dx;
- float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w);
- float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w);
- float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w);
- float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w);
-
- nsvg__addEdge(r, lx0, ly0, left->x, left->y);
- nsvg__addEdge(r, lx1, ly1, lx0, ly0);
-
- nsvg__addEdge(r, right->x, right->y, rx0, ry0);
- nsvg__addEdge(r, rx0, ry0, rx1, ry1);
-
- left->x = lx1; left->y = ly1;
- right->x = rx1; right->y = ry1;
-}
-
-static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
-{
- float w = lineWidth * 0.5f;
- float dlx0 = p0->dy, dly0 = -p0->dx;
- float dlx1 = p1->dy, dly1 = -p1->dx;
- float lx0, rx0, lx1, rx1;
- float ly0, ry0, ly1, ry1;
-
- if (p1->flags & NSVG_PT_LEFT) {
- lx0 = lx1 = p1->x - p1->dmx * w;
- ly0 = ly1 = p1->y - p1->dmy * w;
- nsvg__addEdge(r, lx1, ly1, left->x, left->y);
-
- rx0 = p1->x + (dlx0 * w);
- ry0 = p1->y + (dly0 * w);
- rx1 = p1->x + (dlx1 * w);
- ry1 = p1->y + (dly1 * w);
- nsvg__addEdge(r, right->x, right->y, rx0, ry0);
- nsvg__addEdge(r, rx0, ry0, rx1, ry1);
- } else {
- lx0 = p1->x - (dlx0 * w);
- ly0 = p1->y - (dly0 * w);
- lx1 = p1->x - (dlx1 * w);
- ly1 = p1->y - (dly1 * w);
- nsvg__addEdge(r, lx0, ly0, left->x, left->y);
- nsvg__addEdge(r, lx1, ly1, lx0, ly0);
-
- rx0 = rx1 = p1->x + p1->dmx * w;
- ry0 = ry1 = p1->y + p1->dmy * w;
- nsvg__addEdge(r, right->x, right->y, rx1, ry1);
- }
-
- left->x = lx1; left->y = ly1;
- right->x = rx1; right->y = ry1;
-}
-
-static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap)
-{
- int i, n;
- float w = lineWidth * 0.5f;
- float dlx0 = p0->dy, dly0 = -p0->dx;
- float dlx1 = p1->dy, dly1 = -p1->dx;
- float a0 = atan2f(dly0, dlx0);
- float a1 = atan2f(dly1, dlx1);
- float da = a1 - a0;
- float lx, ly, rx, ry;
-
- if (da < NSVG_PI) da += NSVG_PI*2;
- if (da > NSVG_PI) da -= NSVG_PI*2;
-
- n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap);
- if (n < 2) n = 2;
- if (n > ncap) n = ncap;
-
- lx = left->x;
- ly = left->y;
- rx = right->x;
- ry = right->y;
-
- for (i = 0; i < n; i++) {
- float u = (float)i/(float)(n-1);
- float a = a0 + u*da;
- float ax = cosf(a) * w, ay = sinf(a) * w;
- float lx1 = p1->x - ax, ly1 = p1->y - ay;
- float rx1 = p1->x + ax, ry1 = p1->y + ay;
-
- nsvg__addEdge(r, lx1, ly1, lx, ly);
- nsvg__addEdge(r, rx, ry, rx1, ry1);
-
- lx = lx1; ly = ly1;
- rx = rx1; ry = ry1;
- }
-
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
-}
-
-static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth)
-{
- float w = lineWidth * 0.5f;
- float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w);
- float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w);
-
- nsvg__addEdge(r, lx, ly, left->x, left->y);
- nsvg__addEdge(r, right->x, right->y, rx, ry);
-
- left->x = lx; left->y = ly;
- right->x = rx; right->y = ry;
-}
-
-static int nsvg__curveDivs(float r, float arc, float tol)
-{
- float da = acosf(r / (r + tol)) * 2.0f;
- int divs = (int)ceilf(arc / da);
- if (divs < 2) divs = 2;
- return divs;
-}
-
-static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth)
-{
- int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle.
- NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0};
- NSVGpoint* p0, *p1;
- int j, s, e;
-
- // Build stroke edges
- if (closed) {
- // Looping
- p0 = &points[npoints-1];
- p1 = &points[0];
- s = 0;
- e = npoints;
- } else {
- // Add cap
- p0 = &points[0];
- p1 = &points[1];
- s = 1;
- e = npoints-1;
- }
-
- if (closed) {
- nsvg__initClosed(&left, &right, p0, p1, lineWidth);
- firstLeft = left;
- firstRight = right;
- } else {
- // Add cap
- float dx = p1->x - p0->x;
- float dy = p1->y - p0->y;
- nsvg__normalize(&dx, &dy);
- if (lineCap == NSVG_CAP_BUTT)
- nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0);
- else if (lineCap == NSVG_CAP_SQUARE)
- nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0);
- else if (lineCap == NSVG_CAP_ROUND)
- nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0);
- }
-
- for (j = s; j < e; ++j) {
- if (p1->flags & NSVG_PT_CORNER) {
- if (lineJoin == NSVG_JOIN_ROUND)
- nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap);
- else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL))
- nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth);
- else
- nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth);
- } else {
- nsvg__straightJoin(r, &left, &right, p1, lineWidth);
- }
- p0 = p1++;
- }
-
- if (closed) {
- // Loop it
- nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y);
- nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y);
- } else {
- // Add cap
- float dx = p1->x - p0->x;
- float dy = p1->y - p0->y;
- nsvg__normalize(&dx, &dy);
- if (lineCap == NSVG_CAP_BUTT)
- nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
- else if (lineCap == NSVG_CAP_SQUARE)
- nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1);
- else if (lineCap == NSVG_CAP_ROUND)
- nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1);
- }
-}
-
-static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin)
-{
- int i, j;
- NSVGpoint* p0, *p1;
-
- p0 = &r->points[r->npoints-1];
- p1 = &r->points[0];
- for (i = 0; i < r->npoints; i++) {
- // Calculate segment direction and length
- p0->dx = p1->x - p0->x;
- p0->dy = p1->y - p0->y;
- p0->len = nsvg__normalize(&p0->dx, &p0->dy);
- // Advance
- p0 = p1++;
- }
-
- // calculate joins
- p0 = &r->points[r->npoints-1];
- p1 = &r->points[0];
- for (j = 0; j < r->npoints; j++) {
- float dlx0, dly0, dlx1, dly1, dmr2, cross;
- dlx0 = p0->dy;
- dly0 = -p0->dx;
- dlx1 = p1->dy;
- dly1 = -p1->dx;
- // Calculate extrusions
- p1->dmx = (dlx0 + dlx1) * 0.5f;
- p1->dmy = (dly0 + dly1) * 0.5f;
- dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy;
- if (dmr2 > 0.000001f) {
- float s2 = 1.0f / dmr2;
- if (s2 > 600.0f) {
- s2 = 600.0f;
- }
- p1->dmx *= s2;
- p1->dmy *= s2;
- }
-
- // Clear flags, but keep the corner.
- p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0;
-
- // Keep track of left turns.
- cross = p1->dx * p0->dy - p0->dx * p1->dy;
- if (cross > 0.0f)
- p1->flags |= NSVG_PT_LEFT;
-
- // Check to see if the corner needs to be beveled.
- if (p1->flags & NSVG_PT_CORNER) {
- if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) {
- p1->flags |= NSVG_PT_BEVEL;
- }
- }
-
- p0 = p1++;
- }
-}
-
-static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale)
-{
- int i, j, closed;
- NSVGpath* path;
- NSVGpoint* p0, *p1;
- float miterLimit = shape->miterLimit;
- int lineJoin = shape->strokeLineJoin;
- int lineCap = shape->strokeLineCap;
- float lineWidth = shape->strokeWidth * scale;
-
- for (path = shape->paths; path != NULL; path = path->next) {
- // Flatten path
- r->npoints = 0;
- nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER);
- for (i = 0; i < path->npts-1; i += 3) {
- float* p = &path->pts[i*2];
- nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER);
- }
- if (r->npoints < 2)
- continue;
-
- closed = path->closed;
-
- // If the first and last points are the same, remove the last, mark as closed path.
- p0 = &r->points[r->npoints-1];
- p1 = &r->points[0];
- if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) {
- r->npoints--;
- p0 = &r->points[r->npoints-1];
- closed = 1;
- }
-
- if (shape->strokeDashCount > 0) {
- int idash = 0, dashState = 1;
- float totalDist = 0, dashLen, allDashLen, dashOffset;
- NSVGpoint cur;
-
- if (closed)
- nsvg__appendPathPoint(r, r->points[0]);
-
- // Duplicate points -> points2.
- nsvg__duplicatePoints(r);
-
- r->npoints = 0;
- cur = r->points2[0];
- nsvg__appendPathPoint(r, cur);
-
- // Figure out dash offset.
- allDashLen = 0;
- for (j = 0; j < shape->strokeDashCount; j++)
- allDashLen += shape->strokeDashArray[j];
- if (shape->strokeDashCount & 1)
- allDashLen *= 2.0f;
- // Find location inside pattern
- dashOffset = fmodf(shape->strokeDashOffset, allDashLen);
- if (dashOffset < 0.0f)
- dashOffset += allDashLen;
-
- while (dashOffset > shape->strokeDashArray[idash]) {
- dashOffset -= shape->strokeDashArray[idash];
- idash = (idash + 1) % shape->strokeDashCount;
- }
- dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale;
-
- for (j = 1; j < r->npoints2; ) {
- float dx = r->points2[j].x - cur.x;
- float dy = r->points2[j].y - cur.y;
- float dist = sqrtf(dx*dx + dy*dy);
-
- if ((totalDist + dist) > dashLen) {
- // Calculate intermediate point
- float d = (dashLen - totalDist) / dist;
- float x = cur.x + dx * d;
- float y = cur.y + dy * d;
- nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER);
-
- // Stroke
- if (r->npoints > 1 && dashState) {
- nsvg__prepareStroke(r, miterLimit, lineJoin);
- nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
- }
- // Advance dash pattern
- dashState = !dashState;
- idash = (idash+1) % shape->strokeDashCount;
- dashLen = shape->strokeDashArray[idash] * scale;
- // Restart
- cur.x = x;
- cur.y = y;
- cur.flags = NSVG_PT_CORNER;
- totalDist = 0.0f;
- r->npoints = 0;
- nsvg__appendPathPoint(r, cur);
- } else {
- totalDist += dist;
- cur = r->points2[j];
- nsvg__appendPathPoint(r, cur);
- j++;
- }
- }
- // Stroke any leftover path
- if (r->npoints > 1 && dashState)
- nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
- } else {
- nsvg__prepareStroke(r, miterLimit, lineJoin);
- nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth);
- }
- }
-}
-
-static int nsvg__cmpEdge(const void *p, const void *q)
-{
- const NSVGedge* a = (const NSVGedge*)p;
- const NSVGedge* b = (const NSVGedge*)q;
-
- if (a->y0 < b->y0) return -1;
- if (a->y0 > b->y0) return 1;
- return 0;
-}
-
-
-static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint)
-{
- NSVGactiveEdge* z;
-
- if (r->freelist != NULL) {
- // Restore from freelist.
- z = r->freelist;
- r->freelist = z->next;
- } else {
- // Alloc new edge.
- z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge));
- if (z == NULL) return NULL;
- }
-
- float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
-// STBTT_assert(e->y0 <= start_point);
- // round dx down to avoid going too far
- if (dxdy < 0)
- z->dx = (int)(-floorf(NSVG__FIX * -dxdy));
- else
- z->dx = (int)floorf(NSVG__FIX * dxdy);
- z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0)));
-// z->x -= off_x * FIX;
- z->ey = e->y1;
- z->next = 0;
- z->dir = e->dir;
-
- return z;
-}
-
-static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z)
-{
- z->next = r->freelist;
- r->freelist = z;
-}
-
-static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax)
-{
- int i = x0 >> NSVG__FIXSHIFT;
- int j = x1 >> NSVG__FIXSHIFT;
- if (i < *xmin) *xmin = i;
- if (j > *xmax) *xmax = j;
- if (i < len && j >= 0) {
- if (i == j) {
- // x0,x1 are the same pixel, so compute combined coverage
- scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT));
- } else {
- if (i >= 0) // add antialiasing for x0
- scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT));
- else
- i = -1; // clip
-
- if (j < len) // add antialiasing for x1
- scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT));
- else
- j = len; // clip
-
- for (++i; i < j; ++i) // fill pixels between x0 and x1
- scanline[i] = (unsigned char)(scanline[i] + maxWeight);
- }
- }
-}
-
-// note: this routine clips fills that extend off the edges... ideally this
-// wouldn't happen, but it could happen if the truetype glyph bounding boxes
-// are wrong, or if the user supplies a too-small bitmap
-static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule)
-{
- // non-zero winding fill
- int x0 = 0, w = 0;
-
- if (fillRule == NSVG_FILLRULE_NONZERO) {
- // Non-zero
- while (e != NULL) {
- if (w == 0) {
- // if we're currently at zero, we need to record the edge start point
- x0 = e->x; w += e->dir;
- } else {
- int x1 = e->x; w += e->dir;
- // if we went to zero, we need to draw
- if (w == 0)
- nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
- }
- e = e->next;
- }
- } else if (fillRule == NSVG_FILLRULE_EVENODD) {
- // Even-odd
- while (e != NULL) {
- if (w == 0) {
- // if we're currently at zero, we need to record the edge start point
- x0 = e->x; w = 1;
- } else {
- int x1 = e->x; w = 0;
- nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
- }
- e = e->next;
- }
- }
-}
-
-static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
-
-static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
-{
- return (r) | (g << 8) | (b << 16) | (a << 24);
-}
-
-static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u)
-{
- int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f);
- int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8;
- int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8;
- int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8;
- int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8;
- return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a);
-}
-
-static unsigned int nsvg__applyOpacity(unsigned int c, float u)
-{
- int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f);
- int r = (c) & 0xff;
- int g = (c>>8) & 0xff;
- int b = (c>>16) & 0xff;
- int a = (((c>>24) & 0xff)*iu) >> 8;
- return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a);
-}
-
-static inline int nsvg__div255(int x)
-{
- return ((x+1) * 257) >> 16;
-}
-
-static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y,
- float tx, float ty, float scale, NSVGcachedPaint* cache)
-{
-
- if (cache->type == NSVG_PAINT_COLOR) {
- int i, cr, cg, cb, ca;
- cr = cache->colors[0] & 0xff;
- cg = (cache->colors[0] >> 8) & 0xff;
- cb = (cache->colors[0] >> 16) & 0xff;
- ca = (cache->colors[0] >> 24) & 0xff;
-
- for (i = 0; i < count; i++) {
- int r,g,b;
- int a = nsvg__div255((int)cover[0] * ca);
- int ia = 255 - a;
- // Premultiply
- r = nsvg__div255(cr * a);
- g = nsvg__div255(cg * a);
- b = nsvg__div255(cb * a);
-
- // Blend over
- r += nsvg__div255(ia * (int)dst[0]);
- g += nsvg__div255(ia * (int)dst[1]);
- b += nsvg__div255(ia * (int)dst[2]);
- a += nsvg__div255(ia * (int)dst[3]);
-
- dst[0] = (unsigned char)r;
- dst[1] = (unsigned char)g;
- dst[2] = (unsigned char)b;
- dst[3] = (unsigned char)a;
-
- cover++;
- dst += 4;
- }
- } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) {
- // TODO: spread modes.
- // TODO: plenty of opportunities to optimize.
- float fx, fy, dx, gy;
- float* t = cache->xform;
- int i, cr, cg, cb, ca;
- unsigned int c;
-
- fx = ((float)x - tx) / scale;
- fy = ((float)y - ty) / scale;
- dx = 1.0f / scale;
-
- for (i = 0; i < count; i++) {
- int r,g,b,a,ia;
- gy = fx*t[1] + fy*t[3] + t[5];
- c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)];
- cr = (c) & 0xff;
- cg = (c >> 8) & 0xff;
- cb = (c >> 16) & 0xff;
- ca = (c >> 24) & 0xff;
-
- a = nsvg__div255((int)cover[0] * ca);
- ia = 255 - a;
-
- // Premultiply
- r = nsvg__div255(cr * a);
- g = nsvg__div255(cg * a);
- b = nsvg__div255(cb * a);
-
- // Blend over
- r += nsvg__div255(ia * (int)dst[0]);
- g += nsvg__div255(ia * (int)dst[1]);
- b += nsvg__div255(ia * (int)dst[2]);
- a += nsvg__div255(ia * (int)dst[3]);
-
- dst[0] = (unsigned char)r;
- dst[1] = (unsigned char)g;
- dst[2] = (unsigned char)b;
- dst[3] = (unsigned char)a;
-
- cover++;
- dst += 4;
- fx += dx;
- }
- } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) {
- // TODO: spread modes.
- // TODO: plenty of opportunities to optimize.
- // TODO: focus (fx,fy)
- float fx, fy, dx, gx, gy, gd;
- float* t = cache->xform;
- int i, cr, cg, cb, ca;
- unsigned int c;
-
- fx = ((float)x - tx) / scale;
- fy = ((float)y - ty) / scale;
- dx = 1.0f / scale;
-
- for (i = 0; i < count; i++) {
- int r,g,b,a,ia;
- gx = fx*t[0] + fy*t[2] + t[4];
- gy = fx*t[1] + fy*t[3] + t[5];
- gd = sqrtf(gx*gx + gy*gy);
- c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)];
- cr = (c) & 0xff;
- cg = (c >> 8) & 0xff;
- cb = (c >> 16) & 0xff;
- ca = (c >> 24) & 0xff;
-
- a = nsvg__div255((int)cover[0] * ca);
- ia = 255 - a;
-
- // Premultiply
- r = nsvg__div255(cr * a);
- g = nsvg__div255(cg * a);
- b = nsvg__div255(cb * a);
-
- // Blend over
- r += nsvg__div255(ia * (int)dst[0]);
- g += nsvg__div255(ia * (int)dst[1]);
- b += nsvg__div255(ia * (int)dst[2]);
- a += nsvg__div255(ia * (int)dst[3]);
-
- dst[0] = (unsigned char)r;
- dst[1] = (unsigned char)g;
- dst[2] = (unsigned char)b;
- dst[3] = (unsigned char)a;
-
- cover++;
- dst += 4;
- fx += dx;
- }
- }
-}
-
-static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule)
-{
- NSVGactiveEdge *active = NULL;
- int y, s;
- int e = 0;
- int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline
- int xmin, xmax;
-
- for (y = 0; y < r->height; y++) {
- memset(r->scanline, 0, r->width);
- xmin = r->width;
- xmax = 0;
- for (s = 0; s < NSVG__SUBSAMPLES; ++s) {
- // find center of pixel for this scanline
- float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f;
- NSVGactiveEdge **step = &active;
-
- // update all active edges;
- // remove all active edges that terminate before the center of this scanline
- while (*step) {
- NSVGactiveEdge *z = *step;
- if (z->ey <= scany) {
- *step = z->next; // delete from list
-// NSVG__assert(z->valid);
- nsvg__freeActive(r, z);
- } else {
- z->x += z->dx; // advance to position for current scanline
- step = &((*step)->next); // advance through list
- }
- }
-
- // resort the list if needed
- for (;;) {
- int changed = 0;
- step = &active;
- while (*step && (*step)->next) {
- if ((*step)->x > (*step)->next->x) {
- NSVGactiveEdge* t = *step;
- NSVGactiveEdge* q = t->next;
- t->next = q->next;
- q->next = t;
- *step = q;
- changed = 1;
- }
- step = &(*step)->next;
- }
- if (!changed) break;
- }
-
- // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
- while (e < r->nedges && r->edges[e].y0 <= scany) {
- if (r->edges[e].y1 > scany) {
- NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany);
- if (z == NULL) break;
- // find insertion point
- if (active == NULL) {
- active = z;
- } else if (z->x < active->x) {
- // insert at front
- z->next = active;
- active = z;
- } else {
- // find thing to insert AFTER
- NSVGactiveEdge* p = active;
- while (p->next && p->next->x < z->x)
- p = p->next;
- // at this point, p->next->x is NOT < z->x
- z->next = p->next;
- p->next = z;
- }
- }
- e++;
- }
-
- // now process all active edges in non-zero fashion
- if (active != NULL)
- nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule);
- }
- // Blit
- if (xmin < 0) xmin = 0;
- if (xmax > r->width-1) xmax = r->width-1;
- if (xmin <= xmax) {
- nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache);
- }
- }
-
-}
-
-static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride)
-{
- int x,y;
-
- // Unpremultiply
- for (y = 0; y < h; y++) {
- unsigned char *row = &image[y*stride];
- for (x = 0; x < w; x++) {
- int r = row[0], g = row[1], b = row[2], a = row[3];
- if (a != 0) {
- row[0] = (unsigned char)(r*255/a);
- row[1] = (unsigned char)(g*255/a);
- row[2] = (unsigned char)(b*255/a);
- }
- row += 4;
- }
- }
-
- // Defringe
- for (y = 0; y < h; y++) {
- unsigned char *row = &image[y*stride];
- for (x = 0; x < w; x++) {
- int r = 0, g = 0, b = 0, a = row[3], n = 0;
- if (a == 0) {
- if (x-1 > 0 && row[-1] != 0) {
- r += row[-4];
- g += row[-3];
- b += row[-2];
- n++;
- }
- if (x+1 < w && row[7] != 0) {
- r += row[4];
- g += row[5];
- b += row[6];
- n++;
- }
- if (y-1 > 0 && row[-stride+3] != 0) {
- r += row[-stride];
- g += row[-stride+1];
- b += row[-stride+2];
- n++;
- }
- if (y+1 < h && row[stride+3] != 0) {
- r += row[stride];
- g += row[stride+1];
- b += row[stride+2];
- n++;
- }
- if (n > 0) {
- row[0] = (unsigned char)(r/n);
- row[1] = (unsigned char)(g/n);
- row[2] = (unsigned char)(b/n);
- }
- }
- row += 4;
- }
- }
-}
-
-
-static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity)
-{
- int i, j;
- NSVGgradient* grad;
-
- cache->type = paint->type;
-
- if (paint->type == NSVG_PAINT_COLOR) {
- cache->colors[0] = nsvg__applyOpacity(paint->color, opacity);
- return;
- }
-
- grad = paint->gradient;
-
- cache->spread = grad->spread;
- memcpy(cache->xform, grad->xform, sizeof(float)*6);
-
- if (grad->nstops == 0) {
- for (i = 0; i < 256; i++)
- cache->colors[i] = 0;
- } if (grad->nstops == 1) {
- for (i = 0; i < 256; i++)
- cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity);
- } else {
- unsigned int ca, cb = 0;
- float ua, ub, du, u;
- int ia, ib, count;
-
- ca = nsvg__applyOpacity(grad->stops[0].color, opacity);
- ua = nsvg__clampf(grad->stops[0].offset, 0, 1);
- ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1);
- ia = (int)(ua * 255.0f);
- ib = (int)(ub * 255.0f);
- for (i = 0; i < ia; i++) {
- cache->colors[i] = ca;
- }
-
- for (i = 0; i < grad->nstops-1; i++) {
- ca = nsvg__applyOpacity(grad->stops[i].color, opacity);
- cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity);
- ua = nsvg__clampf(grad->stops[i].offset, 0, 1);
- ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1);
- ia = (int)(ua * 255.0f);
- ib = (int)(ub * 255.0f);
- count = ib - ia;
- if (count <= 0) continue;
- u = 0;
- du = 1.0f / (float)count;
- for (j = 0; j < count; j++) {
- cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u);
- u += du;
- }
- }
-
- for (i = ib; i < 256; i++)
- cache->colors[i] = cb;
- }
-
-}
-
-/*
-static void dumpEdges(NSVGrasterizer* r, const char* name)
-{
- float xmin = 0, xmax = 0, ymin = 0, ymax = 0;
- NSVGedge *e = NULL;
- int i;
- if (r->nedges == 0) return;
- FILE* fp = fopen(name, "w");
- if (fp == NULL) return;
-
- xmin = xmax = r->edges[0].x0;
- ymin = ymax = r->edges[0].y0;
- for (i = 0; i < r->nedges; i++) {
- e = &r->edges[i];
- xmin = nsvg__minf(xmin, e->x0);
- xmin = nsvg__minf(xmin, e->x1);
- xmax = nsvg__maxf(xmax, e->x0);
- xmax = nsvg__maxf(xmax, e->x1);
- ymin = nsvg__minf(ymin, e->y0);
- ymin = nsvg__minf(ymin, e->y1);
- ymax = nsvg__maxf(ymax, e->y0);
- ymax = nsvg__maxf(ymax, e->y1);
- }
-
- fprintf(fp, "<svg viewBox=\"%f %f %f %f\" xmlns=\"http://www.w3.org/2000/svg\">", xmin, ymin, (xmax - xmin), (ymax - ymin));
-
- for (i = 0; i < r->nedges; i++) {
- e = &r->edges[i];
- fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#000;\" />", e->x0,e->y0, e->x1,e->y1);
- }
-
- for (i = 0; i < r->npoints; i++) {
- if (i+1 < r->npoints)
- fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#f00;\" />", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y);
- fprintf(fp ,"<circle cx=\"%f\" cy=\"%f\" r=\"1\" style=\"fill:%s;\" />", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0");
- }
-
- fprintf(fp, "</svg>");
- fclose(fp);
-}
-*/
-
-void nsvgRasterize(NSVGrasterizer* r,
- NSVGimage* image, float tx, float ty, float scale,
- unsigned char* dst, int w, int h, int stride)
-{
- NSVGshape *shape = NULL;
- NSVGedge *e = NULL;
- NSVGcachedPaint cache;
- int i;
-
- r->bitmap = dst;
- r->width = w;
- r->height = h;
- r->stride = stride;
-
- if (w > r->cscanline) {
- r->cscanline = w;
- r->scanline = (unsigned char*)realloc(r->scanline, w);
- if (r->scanline == NULL) return;
- }
-
- for (i = 0; i < h; i++)
- memset(&dst[i*stride], 0, w*4);
-
- for (shape = image->shapes; shape != NULL; shape = shape->next) {
- if (!(shape->flags & NSVG_FLAGS_VISIBLE))
- continue;
-
- if (shape->fill.type != NSVG_PAINT_NONE) {
- nsvg__resetPool(r);
- r->freelist = NULL;
- r->nedges = 0;
-
- nsvg__flattenShape(r, shape, scale);
-
- // Scale and translate edges
- for (i = 0; i < r->nedges; i++) {
- e = &r->edges[i];
- e->x0 = tx + e->x0;
- e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
- e->x1 = tx + e->x1;
- e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
- }
-
- // Rasterize edges
- qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
-
- // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
- nsvg__initPaint(&cache, &shape->fill, shape->opacity);
-
- nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule);
- }
- if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) {
- nsvg__resetPool(r);
- r->freelist = NULL;
- r->nedges = 0;
-
- nsvg__flattenShapeStroke(r, shape, scale);
-
-// dumpEdges(r, "edge.svg");
-
- // Scale and translate edges
- for (i = 0; i < r->nedges; i++) {
- e = &r->edges[i];
- e->x0 = tx + e->x0;
- e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES;
- e->x1 = tx + e->x1;
- e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES;
- }
-
- // Rasterize edges
- qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
-
- // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
- nsvg__initPaint(&cache, &shape->stroke, shape->opacity);
-
- nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO);
- }
- }
-
- nsvg__unpremultiplyAlpha(dst, w, h, stride);
-
- r->bitmap = NULL;
- r->width = 0;
- r->height = 0;
- r->stride = 0;
-}
-
-#endif
diff --git a/thirdparty/pvrtccompressor/AlphaBitmap.h b/thirdparty/pvrtccompressor/AlphaBitmap.h
deleted file mode 100644
index 57c6b026ea..0000000000
--- a/thirdparty/pvrtccompressor/AlphaBitmap.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include "Bitmap.h"
-
-namespace Javelin {
-
-class AlphaBitmap : public Bitmap {
-public:
- AlphaBitmap(int w, int h)
- : Bitmap(w, h, 1) {
- }
-
- const unsigned char *GetData() const { return data; }
-
- unsigned char *GetData() { return data; }
-};
-
-}
diff --git a/thirdparty/pvrtccompressor/BitScale.cpp b/thirdparty/pvrtccompressor/BitScale.cpp
deleted file mode 100644
index 97b3f0aa25..0000000000
--- a/thirdparty/pvrtccompressor/BitScale.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-#include "BitScale.h"
-
-
-const uint8_t Javelin::Data::BITSCALE_5_TO_8[32] = {
- 0, 8, 16, 24, 32, 41, 49, 57, 65, 74,
- 82, 90, 98, 106, 115, 123, 131, 139, 148, 156,
- 164, 172, 180, 189, 197, 205, 213, 222, 230, 238,
- 246, 255};
-
-const uint8_t Javelin::Data::BITSCALE_4_TO_8[16] = {
- 0, 17, 34, 51, 68, 85, 102, 119, 136, 153,
- 170, 187, 204, 221, 238, 255};
-
-const uint8_t Javelin::Data::BITSCALE_3_TO_8[8] = {
- 0, 36, 72, 109, 145, 182, 218, 255};
-
-const uint8_t Javelin::Data::BITSCALE_8_TO_5_FLOOR[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
- 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
- 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
- 3, 3, 3, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
- 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
- 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 9, 9, 9, 9, 9,
- 9, 9, 9, 10, 10, 10, 10, 10, 10, 10,
- 10, 11, 11, 11, 11, 11, 11, 11, 11, 12,
- 12, 12, 12, 12, 12, 12, 12, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 14, 14, 14, 14,
- 14, 14, 14, 14, 15, 15, 15, 15, 15, 15,
- 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
- 17, 17, 17, 17, 17, 17, 17, 17, 17, 18,
- 18, 18, 18, 18, 18, 18, 18, 19, 19, 19,
- 19, 19, 19, 19, 19, 20, 20, 20, 20, 20,
- 20, 20, 20, 21, 21, 21, 21, 21, 21, 21,
- 21, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 23, 23, 23, 23, 23, 23, 23, 23, 24, 24,
- 24, 24, 24, 24, 24, 24, 25, 25, 25, 25,
- 25, 25, 25, 25, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 27, 27, 27, 27, 27, 27, 27,
- 27, 28, 28, 28, 28, 28, 28, 28, 28, 29,
- 29, 29, 29, 29, 29, 29, 29, 30, 30, 30,
- 30, 30, 30, 30, 30, 31};
-
-const uint8_t Javelin::Data::BITSCALE_8_TO_4_FLOOR[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
- 10, 10, 10, 10, 10, 10, 10, 11, 11, 11,
- 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 11, 11, 11, 12, 12, 12, 12, 12, 12,
- 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
- 12, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 15};
-
-const uint8_t Javelin::Data::BITSCALE_8_TO_3_FLOOR[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 7};
-
-const uint8_t Javelin::Data::BITSCALE_8_TO_5_CEIL[256] = {
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 2,
- 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
- 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
- 4, 4, 4, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 8, 8,
- 8, 8, 8, 8, 8, 8, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
- 10, 10, 10, 11, 11, 11, 11, 11, 11, 11,
- 11, 12, 12, 12, 12, 12, 12, 12, 12, 13,
- 13, 13, 13, 13, 13, 13, 13, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 15, 15, 15, 15,
- 15, 15, 15, 15, 16, 16, 16, 16, 16, 16,
- 16, 16, 17, 17, 17, 17, 17, 17, 17, 17,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
- 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
- 20, 20, 20, 20, 20, 21, 21, 21, 21, 21,
- 21, 21, 21, 22, 22, 22, 22, 22, 22, 22,
- 22, 23, 23, 23, 23, 23, 23, 23, 23, 23,
- 24, 24, 24, 24, 24, 24, 24, 24, 25, 25,
- 25, 25, 25, 25, 25, 25, 26, 26, 26, 26,
- 26, 26, 26, 26, 27, 27, 27, 27, 27, 27,
- 27, 27, 27, 28, 28, 28, 28, 28, 28, 28,
- 28, 29, 29, 29, 29, 29, 29, 29, 29, 30,
- 30, 30, 30, 30, 30, 30, 30, 31, 31, 31,
- 31, 31, 31, 31, 31, 31};
-
-const uint8_t Javelin::Data::BITSCALE_8_TO_4_CEIL[256] = {
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 10, 10, 10, 10, 10, 10,
- 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
- 10, 11, 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 11, 11, 11, 11, 11, 11, 11, 12, 12,
- 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
- 12, 12, 12, 12, 12, 13, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
- 13, 13, 14, 14, 14, 14, 14, 14, 14, 14,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 15,
- 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15};
-
-const uint8_t Javelin::Data::BITSCALE_8_TO_3_CEIL[256] = {
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7};
-
diff --git a/thirdparty/pvrtccompressor/BitScale.h b/thirdparty/pvrtccompressor/BitScale.h
deleted file mode 100644
index 3ea7962f55..0000000000
--- a/thirdparty/pvrtccompressor/BitScale.h
+++ /dev/null
@@ -1,28 +0,0 @@
-//============================================================================
-
-#pragma once
-
-#include "core/typedefs.h"
-
-//============================================================================
-
-namespace Javelin
-{
- namespace Data
- {
-//============================================================================
-
- extern const uint8_t BITSCALE_5_TO_8[32];
- extern const uint8_t BITSCALE_4_TO_8[16];
- extern const uint8_t BITSCALE_3_TO_8[8];
- extern const uint8_t BITSCALE_8_TO_5_FLOOR[256];
- extern const uint8_t BITSCALE_8_TO_4_FLOOR[256];
- extern const uint8_t BITSCALE_8_TO_3_FLOOR[256];
- extern const uint8_t BITSCALE_8_TO_5_CEIL[256];
- extern const uint8_t BITSCALE_8_TO_4_CEIL[256];
- extern const uint8_t BITSCALE_8_TO_3_CEIL[256];
-
-//============================================================================
- } // namespace Data
-} // namespace Javelin
-//============================================================================
diff --git a/thirdparty/pvrtccompressor/BitUtility.h b/thirdparty/pvrtccompressor/BitUtility.h
deleted file mode 100644
index 588ff3e892..0000000000
--- a/thirdparty/pvrtccompressor/BitUtility.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-namespace Javelin {
-
-class BitUtility {
-public:
- static bool IsPowerOf2(unsigned int x) {
- return (x & (x - 1)) == 0;
- }
-
- static unsigned int RotateRight(unsigned int value, unsigned int shift) {
- if ((shift &= sizeof(value) * 8 - 1) == 0) {
- return value;
- }
- return (value >> shift) | (value << (sizeof(value) * 8 - shift));
- }
-};
-
-}
diff --git a/thirdparty/pvrtccompressor/Bitmap.h b/thirdparty/pvrtccompressor/Bitmap.h
deleted file mode 100644
index 508ed8cb75..0000000000
--- a/thirdparty/pvrtccompressor/Bitmap.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include "Point2.h"
-
-namespace Javelin {
-
-class Bitmap {
-public:
- int width;
- int height;
- unsigned char *data;
-
- Bitmap(int w, int h, int bytesPerPixel)
- : width(w)
- , height(h)
- , data(new unsigned char[width * height * bytesPerPixel]) {
- }
-
- virtual ~Bitmap() {
- delete [] data;
- }
-
- Point2<int> GetSize() const { return Point2<int>(width, height); }
-
- int GetArea() const { return width * height; }
-
- int GetBitmapWidth() const { return width; }
-
- int GetBitmapHeight() const { return height; }
-
- const unsigned char *GetRawData() const { return data; }
-};
-
-}
diff --git a/thirdparty/pvrtccompressor/ColorRgba.h b/thirdparty/pvrtccompressor/ColorRgba.h
deleted file mode 100644
index 0701420566..0000000000
--- a/thirdparty/pvrtccompressor/ColorRgba.h
+++ /dev/null
@@ -1,152 +0,0 @@
-#pragma once
-
-namespace Javelin {
-
-template<typename T>
-class ColorRgb {
-public:
- T b;
- T g;
- T r;
-
-
- ColorRgb()
- : b(0)
- , g(0)
- , r(0) {
- }
-
- ColorRgb(T red, T green, T blue)
- : b(blue)
- , g(green)
- , r(red) {
- }
-
- ColorRgb(const ColorRgb<T> &x)
- : b(x.b)
- , g(x.g)
- , r(x.r) {
- }
-
- ColorRgb<int> operator *(int x) {
- return ColorRgb<int>(r * x, g * x, b * x);
- }
-
- ColorRgb<int> operator +(const ColorRgb<T> &x) const {
- return ColorRgb<int>(r + (int)x.r, g + (int)x.g, b + (int)x.b);
- }
-
- ColorRgb<int> operator -(const ColorRgb<T> &x) const {
- return ColorRgb<int>(r - (int)x.r, g - (int)x.g, b - (int)x.b);
- }
-
- int operator %(const ColorRgb<T> &x) const {
- return r * (int)x.r + g * (int)x.g + b * (int)x.b;
- }
-
- bool operator ==(const ColorRgb<T> &x) const {
- return r == x.r && g == x.g && b == x.b;
- }
-
- bool operator !=(const ColorRgb<T> &x) const {
- return r != x.r || g != x.g || b != x.b;
- }
-
- void SetMin(const ColorRgb<T> &x) {
- if (x.r < r) {
- r = x.r;
- }
- if (x.g < g) {
- g = x.g;
- }
- if (x.b < b) {
- b = x.b;
- }
- }
-
- void SetMax(const ColorRgb<T> &x) {
- if (x.r > r) {
- r = x.r;
- }
- if (x.g > g) {
- g = x.g;
- }
- if (x.b > b) {
- b = x.b;
- }
- }
-};
-
-template<typename T>
-class ColorRgba : public ColorRgb<T> {
-public:
- T a;
-
- ColorRgba() :
- a(0) {
- }
-
- ColorRgba(T red, T green, T blue, T alpha)
- : ColorRgb<T>(red, green, blue)
- , a(alpha) {
- }
-
- ColorRgba(const ColorRgba<T> &x)
- : ColorRgb<T>(x.r, x.g, x.b)
- , a(x.a) {
- }
-
- ColorRgba<int> operator *(int x) {
- return ColorRgba<T>(ColorRgb<T>::r * x,
- ColorRgb<T>::g * x,
- ColorRgb<T>::b * x,
- a * x);
- }
-
- ColorRgba<int> operator +(const ColorRgba<T> &x) {
- return ColorRgba<T>(ColorRgb<T>::r + (int)x.r,
- ColorRgb<T>::g + (int)x.g,
- ColorRgb<T>::b + (int)x.b,
- a + (int)x.a);
- }
-
- ColorRgba<int> operator -(const ColorRgba<T> &x) {
- return ColorRgba<T>(ColorRgb<T>::r - (int)x.r,
- ColorRgb<T>::g - (int)x.g,
- ColorRgb<T>::b - (int)x.b,
- a - (int)x.a);
- }
-
- int operator %(const ColorRgba<T> &x) {
- return ColorRgb<T>::r * (int)x.r +
- ColorRgb<T>::g * (int)x.g +
- ColorRgb<T>::b * (int)x.b +
- a * (int)x.a;
- }
-
- bool operator ==(const ColorRgba<T> &x) {
- return ColorRgb<T>::r == x.r && ColorRgb<T>::g == x.g &&
- ColorRgb<T>::b == x.b && a == x.a;
- }
-
- bool operator !=(const ColorRgba<T> &x) {
- return ColorRgb<T>::r != x.r || ColorRgb<T>::g != x.g ||
- ColorRgb<T>::b != x.b || a != x.a;
- }
-
- void SetMin(const ColorRgba<T> &x) {
- ColorRgb<T>::SetMin(x);
- if (x.a < a) {
- a = x.a;
- }
- }
-
- void SetMax(const ColorRgba<T> &x) {
- ColorRgb<T>::SetMax(x);
- if (x.a > a) {
- a = x.a;
- }
- }
-};
-
-}
diff --git a/thirdparty/pvrtccompressor/Interval.h b/thirdparty/pvrtccompressor/Interval.h
deleted file mode 100644
index a7252e8375..0000000000
--- a/thirdparty/pvrtccompressor/Interval.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-namespace Javelin {
-
-template<typename T>
-class Interval {
-public:
- T min;
- T max;
-
- Interval() {
- }
-
- Interval<T> &operator|=(const T &x) {
- min.SetMin(x);
- max.SetMax(x);
- return *this;
- }
-};
-
-}
diff --git a/thirdparty/pvrtccompressor/LICENSE.TXT b/thirdparty/pvrtccompressor/LICENSE.TXT
deleted file mode 100644
index 974fc09e25..0000000000
--- a/thirdparty/pvrtccompressor/LICENSE.TXT
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright © 2014, Jeffrey Lim. All Rights Reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
diff --git a/thirdparty/pvrtccompressor/MortonTable.cpp b/thirdparty/pvrtccompressor/MortonTable.cpp
deleted file mode 100644
index 29a5af67f6..0000000000
--- a/thirdparty/pvrtccompressor/MortonTable.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//============================================================================
-
-#include "MortonTable.h"
-
-//============================================================================
-
-const unsigned short Javelin::Data::MORTON_TABLE[256] =
-{
- 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015,
- 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055,
- 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
- 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
- 0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415,
- 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
- 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515,
- 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,
- 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
- 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
- 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
- 0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
- 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
- 0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
- 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
- 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
- 0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
- 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
- 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
- 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
- 0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
- 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
- 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
- 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
- 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
- 0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
- 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
- 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
- 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
- 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
- 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
- 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555
-};
-
-//============================================================================
diff --git a/thirdparty/pvrtccompressor/MortonTable.h b/thirdparty/pvrtccompressor/MortonTable.h
deleted file mode 100644
index 7a27e59544..0000000000
--- a/thirdparty/pvrtccompressor/MortonTable.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//============================================================================
-
-#pragma once
-
-//============================================================================
-
-namespace Javelin
-{
- namespace Data
- {
-//============================================================================
-
- extern const unsigned short MORTON_TABLE[256];
-
-//============================================================================
- } // namespace Data
-} // namespace Javelin
-//============================================================================
diff --git a/thirdparty/pvrtccompressor/Point2.h b/thirdparty/pvrtccompressor/Point2.h
deleted file mode 100644
index 89fa4b6322..0000000000
--- a/thirdparty/pvrtccompressor/Point2.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-namespace Javelin {
-
-template<typename T>
-class Point2 {
-public:
- T x;
- T y;
-
- Point2(int a, int b)
- : x(a)
- , y(b) {
- }
-};
-
-}
diff --git a/thirdparty/pvrtccompressor/PvrTcDecoder.cpp b/thirdparty/pvrtccompressor/PvrTcDecoder.cpp
deleted file mode 100644
index d8a36b342c..0000000000
--- a/thirdparty/pvrtccompressor/PvrTcDecoder.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-//============================================================================
-
-#include "PvrTcDecoder.h"
-#include "PvrTcPacket.h"
-
-#include "MortonTable.h"
-#include <assert.h>
-
-//============================================================================
-
-using namespace Javelin;
-using Data::MORTON_TABLE;
-
-//============================================================================
-
-inline unsigned PvrTcDecoder::GetMortonNumber(int x, int y)
-{
- return MORTON_TABLE[x >> 8] << 17 | MORTON_TABLE[y >> 8] << 16 | MORTON_TABLE[x & 0xFF] << 1 | MORTON_TABLE[y & 0xFF];
-}
-
-//============================================================================
-
-void PvrTcDecoder::DecodeRgb4Bpp(ColorRgb<unsigned char>* result, const Point2<int>& size, const void* data)
-{
- assert(size.x == size.y);
-
- const int blocks = size.x / 4;
- const int blockMask = blocks-1;
- const PvrTcPacket* packets = static_cast<const PvrTcPacket*>(data);
-
- for(int y = 0; y < blocks; ++y)
- {
- for(int x = 0; x < blocks; ++x)
- {
- const PvrTcPacket* packet = packets + GetMortonNumber(x, y);
-
- unsigned mod = packet->modulationData;
- const unsigned char (*weights)[4] = PvrTcPacket::WEIGHTS + 4*packet->usePunchthroughAlpha;
- const unsigned char (*factor)[4] = PvrTcPacket::BILINEAR_FACTORS;
-
- for(int py = 0; py < 4; ++py)
- {
- const int yOffset = (py < 2) ? -1 : 0;
- const int y0 = (y + yOffset) & blockMask;
- const int y1 = (y0+1) & blockMask;
-
- for(int px = 0; px < 4; ++px)
- {
- const int xOffset = (px < 2) ? -1 : 0;
- const int x0 = (x + xOffset) & blockMask;
- const int x1 = (x0+1) & blockMask;
-
- const PvrTcPacket* p0 = packets + GetMortonNumber(x0, y0);
- const PvrTcPacket* p1 = packets + GetMortonNumber(x1, y0);
- const PvrTcPacket* p2 = packets + GetMortonNumber(x0, y1);
- const PvrTcPacket* p3 = packets + GetMortonNumber(x1, y1);
-
- ColorRgb<int> ca = p0->GetColorRgbA() * (*factor)[0] +
- p1->GetColorRgbA() * (*factor)[1] +
- p2->GetColorRgbA() * (*factor)[2] +
- p3->GetColorRgbA() * (*factor)[3];
-
- ColorRgb<int> cb = p0->GetColorRgbB() * (*factor)[0] +
- p1->GetColorRgbB() * (*factor)[1] +
- p2->GetColorRgbB() * (*factor)[2] +
- p3->GetColorRgbB() * (*factor)[3];
-
- const unsigned char* w = weights[mod&3];
- ColorRgb<unsigned char> c;
- c.r = (ca.r * w[0] + cb.r * w[1]) >> 7;
- c.g = (ca.g * w[0] + cb.g * w[1]) >> 7;
- c.b = (ca.b * w[0] + cb.b * w[1]) >> 7;
-
- result[(py+y*4)*size.x + (px+x*4)] = c;
- mod >>= 2;
- factor++;
- }
- }
- }
- }
-}
-
-void PvrTcDecoder::DecodeRgba4Bpp(ColorRgba<unsigned char>* result, const Point2<int>& size, const void* data)
-{
- assert(size.x == size.y);
-
- const int blocks = size.x / 4;
- const int blockMask = blocks-1;
- const PvrTcPacket* packets = static_cast<const PvrTcPacket*>(data);
-
- for(int y = 0; y < blocks; ++y)
- {
- for(int x = 0; x < blocks; ++x)
- {
- const PvrTcPacket* packet = packets + GetMortonNumber(x, y);
-
- unsigned mod = packet->modulationData;
- const unsigned char (*weights)[4] = PvrTcPacket::WEIGHTS + 4*packet->usePunchthroughAlpha;
- const unsigned char (*factor)[4] = PvrTcPacket::BILINEAR_FACTORS;
-
- for(int py = 0; py < 4; ++py)
- {
- const int yOffset = (py < 2) ? -1 : 0;
- const int y0 = (y + yOffset) & blockMask;
- const int y1 = (y0+1) & blockMask;
-
- for(int px = 0; px < 4; ++px)
- {
- const int xOffset = (px < 2) ? -1 : 0;
- const int x0 = (x + xOffset) & blockMask;
- const int x1 = (x0+1) & blockMask;
-
- const PvrTcPacket* p0 = packets + GetMortonNumber(x0, y0);
- const PvrTcPacket* p1 = packets + GetMortonNumber(x1, y0);
- const PvrTcPacket* p2 = packets + GetMortonNumber(x0, y1);
- const PvrTcPacket* p3 = packets + GetMortonNumber(x1, y1);
-
- ColorRgba<int> ca = p0->GetColorRgbaA() * (*factor)[0] +
- p1->GetColorRgbaA() * (*factor)[1] +
- p2->GetColorRgbaA() * (*factor)[2] +
- p3->GetColorRgbaA() * (*factor)[3];
-
- ColorRgba<int> cb = p0->GetColorRgbaB() * (*factor)[0] +
- p1->GetColorRgbaB() * (*factor)[1] +
- p2->GetColorRgbaB() * (*factor)[2] +
- p3->GetColorRgbaB() * (*factor)[3];
-
- const unsigned char* w = weights[mod&3];
- ColorRgba<unsigned char> c;
- c.r = (ca.r * w[0] + cb.r * w[1]) >> 7;
- c.g = (ca.g * w[0] + cb.g * w[1]) >> 7;
- c.b = (ca.b * w[0] + cb.b * w[1]) >> 7;
- c.a = (ca.a * w[2] + cb.a * w[3]) >> 7;
-
- result[(py+y*4)*size.x + (px+x*4)] = c;
- mod >>= 2;
- factor++;
- }
- }
- }
- }
-}
-
-//============================================================================
diff --git a/thirdparty/pvrtccompressor/PvrTcDecoder.h b/thirdparty/pvrtccompressor/PvrTcDecoder.h
deleted file mode 100644
index 1b6fcf964c..0000000000
--- a/thirdparty/pvrtccompressor/PvrTcDecoder.h
+++ /dev/null
@@ -1,25 +0,0 @@
-//============================================================================
-
-#pragma once
-#include "Point2.h"
-#include "ColorRgba.h"
-
-//============================================================================
-
-namespace Javelin
-{
-//============================================================================
-
- class PvrTcDecoder
- {
- public:
- static void DecodeRgb4Bpp(ColorRgb<unsigned char>* result, const Point2<int>& size, const void* data);
- static void DecodeRgba4Bpp(ColorRgba<unsigned char>* result, const Point2<int>& size, const void* data);
-
- private:
- static unsigned GetMortonNumber(int x, int y);
- };
-
-//============================================================================
-}
-//============================================================================
diff --git a/thirdparty/pvrtccompressor/PvrTcEncoder.cpp b/thirdparty/pvrtccompressor/PvrTcEncoder.cpp
deleted file mode 100644
index 587b1320f1..0000000000
--- a/thirdparty/pvrtccompressor/PvrTcEncoder.cpp
+++ /dev/null
@@ -1,474 +0,0 @@
-//============================================================================
-
-#include "PvrTcEncoder.h"
-#include "AlphaBitmap.h"
-#include "PvrTcPacket.h"
-#include "RgbBitmap.h"
-#include "RgbaBitmap.h"
-#include "MortonTable.h"
-#include "BitUtility.h"
-#include "Interval.h"
-#include <assert.h>
-#include <math.h>
-#include <stdint.h>
-
-//============================================================================
-
-using namespace Javelin;
-using Data::MORTON_TABLE;
-
-//============================================================================
-
-static const unsigned char MODULATION_LUT[16] =
-{
- 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3
-};
-
-//============================================================================
-
-inline unsigned PvrTcEncoder::GetMortonNumber(int x, int y)
-{
- return MORTON_TABLE[x >> 8] << 17 | MORTON_TABLE[y >> 8] << 16 | MORTON_TABLE[x & 0xFF] << 1 | MORTON_TABLE[y & 0xFF];
-}
-
-//============================================================================
-
-void PvrTcEncoder::EncodeAlpha2Bpp(void* result, const AlphaBitmap& bitmap)
-{
- int size = bitmap.GetBitmapWidth();
- assert(size == bitmap.GetBitmapHeight());
- assert(BitUtility::IsPowerOf2(size));
-
- // Blocks in each dimension.
- int xBlocks = size/8;
- int yBlocks = size/4;
-
- const unsigned char* bitmapData = bitmap.GetRawData();
-
- PvrTcPacket* packets = static_cast<PvrTcPacket*>(result);
- for(int y = 0; y < yBlocks; ++y)
- {
- for(int x = 0; x < xBlocks; ++x)
- {
- PvrTcPacket* packet = packets + GetMortonNumber(x, y);
- packet->usePunchthroughAlpha = 0;
- packet->colorAIsOpaque = 0;
- packet->colorA = 0x7ff; // White, with 0 alpha
- packet->colorBIsOpaque = 1;
- packet->colorB = 0x7fff; // White with full alpha
-
- const unsigned char* blockBitmapData = &bitmapData[y*4*size + x*8];
-
- uint32_t modulationData = 0;
- for(int py = 0; py < 4; ++py)
- {
- const unsigned char* rowBitmapData = blockBitmapData;
- for(int px = 0; px < 8; ++px)
- {
- unsigned char pixel = *rowBitmapData++;
- modulationData = BitUtility::RotateRight(modulationData | (pixel >> 7), 1);
- }
- blockBitmapData += size;
- }
- packet->modulationData = modulationData;
- }
- }
-}
-
-void PvrTcEncoder::EncodeAlpha4Bpp(void* result, const AlphaBitmap& bitmap)
-{
- int size = bitmap.GetBitmapWidth();
- assert(size == bitmap.GetBitmapHeight());
- assert(BitUtility::IsPowerOf2(size));
-
- // Blocks in each dimension.
- int blocks = size/4;
-
- const unsigned char* bitmapData = bitmap.GetRawData();
-
- PvrTcPacket* packets = static_cast<PvrTcPacket*>(result);
- for(int y = 0; y < blocks; ++y)
- {
- for(int x = 0; x < blocks; ++x)
- {
- PvrTcPacket* packet = packets + GetMortonNumber(x, y);
- packet->usePunchthroughAlpha = 0;
- packet->colorAIsOpaque = 0;
- packet->colorA = 0x7ff; // White, with 0 alpha
- packet->colorBIsOpaque = 1;
- packet->colorB = 0x7fff; // White with full alpha
-
- const unsigned char* blockBitmapData = &bitmapData[(y*size + x)*4];
-
- uint32_t modulationData = 0;
- for(int py = 0; py < 4; ++py)
- {
- const unsigned char* rowBitmapData = blockBitmapData;
- for(int px = 0; px < 4; ++px)
- {
- unsigned char pixel = *rowBitmapData++;
- modulationData = BitUtility::RotateRight(modulationData | MODULATION_LUT[pixel>>4], 2);
- }
- blockBitmapData += size;
- }
- packet->modulationData = modulationData;
- }
- }
-}
-
-//============================================================================
-
-typedef Interval<ColorRgb<unsigned char> > ColorRgbBoundingBox;
-
-static void CalculateBoundingBox(ColorRgbBoundingBox& cbb, const RgbBitmap& bitmap, int blockX, int blockY)
-{
- int size = bitmap.GetBitmapWidth();
- const ColorRgb<unsigned char>* data = bitmap.GetData() + blockY * 4 * size + blockX * 4;
-
- cbb.min = data[0];
- cbb.max = data[0];
- cbb |= data[1];
- cbb |= data[2];
- cbb |= data[3];
-
- cbb |= data[size];
- cbb |= data[size+1];
- cbb |= data[size+2];
- cbb |= data[size+3];
-
- cbb |= data[2*size];
- cbb |= data[2*size+1];
- cbb |= data[2*size+2];
- cbb |= data[2*size+3];
-
- cbb |= data[3*size];
- cbb |= data[3*size+1];
- cbb |= data[3*size+2];
- cbb |= data[3*size+3];
-}
-
-void PvrTcEncoder::EncodeRgb4Bpp(void* result, const RgbBitmap& bitmap)
-{
- assert(bitmap.GetBitmapWidth() == bitmap.GetBitmapHeight());
- assert(BitUtility::IsPowerOf2(bitmap.GetBitmapWidth()));
- const int size = bitmap.GetBitmapWidth();
- const int blocks = size / 4;
- const int blockMask = blocks-1;
-
- PvrTcPacket* packets = static_cast<PvrTcPacket*>(result);
-
- for(int y = 0; y < blocks; ++y)
- {
- for(int x = 0; x < blocks; ++x)
- {
- ColorRgbBoundingBox cbb;
- CalculateBoundingBox(cbb, bitmap, x, y);
- PvrTcPacket* packet = packets + GetMortonNumber(x, y);
- packet->usePunchthroughAlpha = 0;
- packet->SetColorA(cbb.min);
- packet->SetColorB(cbb.max);
- }
- }
-
- for(int y = 0; y < blocks; ++y)
- {
- for(int x = 0; x < blocks; ++x)
- {
- const unsigned char (*factor)[4] = PvrTcPacket::BILINEAR_FACTORS;
- const ColorRgb<unsigned char>* data = bitmap.GetData() + y * 4 * size + x * 4;
-
- uint32_t modulationData = 0;
-
- for(int py = 0; py < 4; ++py)
- {
- const int yOffset = (py < 2) ? -1 : 0;
- const int y0 = (y + yOffset) & blockMask;
- const int y1 = (y0+1) & blockMask;
-
- for(int px = 0; px < 4; ++px)
- {
- const int xOffset = (px < 2) ? -1 : 0;
- const int x0 = (x + xOffset) & blockMask;
- const int x1 = (x0+1) & blockMask;
-
- const PvrTcPacket* p0 = packets + GetMortonNumber(x0, y0);
- const PvrTcPacket* p1 = packets + GetMortonNumber(x1, y0);
- const PvrTcPacket* p2 = packets + GetMortonNumber(x0, y1);
- const PvrTcPacket* p3 = packets + GetMortonNumber(x1, y1);
-
- ColorRgb<int> ca = p0->GetColorRgbA() * (*factor)[0] +
- p1->GetColorRgbA() * (*factor)[1] +
- p2->GetColorRgbA() * (*factor)[2] +
- p3->GetColorRgbA() * (*factor)[3];
-
- ColorRgb<int> cb = p0->GetColorRgbB() * (*factor)[0] +
- p1->GetColorRgbB() * (*factor)[1] +
- p2->GetColorRgbB() * (*factor)[2] +
- p3->GetColorRgbB() * (*factor)[3];
-
- const ColorRgb<unsigned char>& pixel = data[py*size + px];
- ColorRgb<int> d = cb - ca;
- ColorRgb<int> p;
- p.r=pixel.r*16;
- p.g=pixel.g*16;
- p.b=pixel.b*16;
- ColorRgb<int> v = p - ca;
-
- // PVRTC uses weightings of 0, 3/8, 5/8 and 1
- // The boundaries for these are 3/16, 1/2 (=8/16), 13/16
- int projection = (v % d) * 16;
- int lengthSquared = d % d;
- if(projection > 3*lengthSquared) modulationData++;
- if(projection > 8*lengthSquared) modulationData++;
- if(projection > 13*lengthSquared) modulationData++;
-
- modulationData = BitUtility::RotateRight(modulationData, 2);
-
- factor++;
- }
- }
-
- PvrTcPacket* packet = packets + GetMortonNumber(x, y);
- packet->modulationData = modulationData;
- }
- }
-}
-
-//============================================================================
-
-static void CalculateBoundingBox(ColorRgbBoundingBox& cbb, const RgbaBitmap& bitmap, int blockX, int blockY)
-{
- int size = bitmap.GetBitmapWidth();
- const ColorRgba<unsigned char>* data = bitmap.GetData() + blockY * 4 * size + blockX * 4;
-
- cbb.min = data[0];
- cbb.max = data[0];
-
- cbb |= data[1];
- cbb |= data[2];
- cbb |= data[3];
-
- cbb |= data[size];
- cbb |= data[size+1];
- cbb |= data[size+2];
- cbb |= data[size+3];
-
- cbb |= data[2*size];
- cbb |= data[2*size+1];
- cbb |= data[2*size+2];
- cbb |= data[2*size+3];
-
- cbb |= data[3*size];
- cbb |= data[3*size+1];
- cbb |= data[3*size+2];
- cbb |= data[3*size+3];
-}
-
-void PvrTcEncoder::EncodeRgb4Bpp(void* result, const RgbaBitmap& bitmap)
-{
- assert(bitmap.GetBitmapWidth() == bitmap.GetBitmapHeight());
- assert(BitUtility::IsPowerOf2(bitmap.GetBitmapWidth()));
- const int size = bitmap.GetBitmapWidth();
- const int blocks = size / 4;
- const int blockMask = blocks-1;
-
- PvrTcPacket* packets = static_cast<PvrTcPacket*>(result);
-
- for(int y = 0; y < blocks; ++y)
- {
- for(int x = 0; x < blocks; ++x)
- {
- ColorRgbBoundingBox cbb;
- CalculateBoundingBox(cbb, bitmap, x, y);
- PvrTcPacket* packet = packets + GetMortonNumber(x, y);
- packet->usePunchthroughAlpha = 0;
- packet->SetColorA(cbb.min);
- packet->SetColorB(cbb.max);
- }
- }
-
- for(int y = 0; y < blocks; ++y)
- {
- for(int x = 0; x < blocks; ++x)
- {
- const unsigned char (*factor)[4] = PvrTcPacket::BILINEAR_FACTORS;
- const ColorRgba<unsigned char>* data = bitmap.GetData() + y * 4 * size + x * 4;
-
- uint32_t modulationData = 0;
-
- for(int py = 0; py < 4; ++py)
- {
- const int yOffset = (py < 2) ? -1 : 0;
- const int y0 = (y + yOffset) & blockMask;
- const int y1 = (y0+1) & blockMask;
-
- for(int px = 0; px < 4; ++px)
- {
- const int xOffset = (px < 2) ? -1 : 0;
- const int x0 = (x + xOffset) & blockMask;
- const int x1 = (x0+1) & blockMask;
-
- const PvrTcPacket* p0 = packets + GetMortonNumber(x0, y0);
- const PvrTcPacket* p1 = packets + GetMortonNumber(x1, y0);
- const PvrTcPacket* p2 = packets + GetMortonNumber(x0, y1);
- const PvrTcPacket* p3 = packets + GetMortonNumber(x1, y1);
-
- ColorRgb<int> ca = p0->GetColorRgbA() * (*factor)[0] +
- p1->GetColorRgbA() * (*factor)[1] +
- p2->GetColorRgbA() * (*factor)[2] +
- p3->GetColorRgbA() * (*factor)[3];
-
- ColorRgb<int> cb = p0->GetColorRgbB() * (*factor)[0] +
- p1->GetColorRgbB() * (*factor)[1] +
- p2->GetColorRgbB() * (*factor)[2] +
- p3->GetColorRgbB() * (*factor)[3];
-
- const ColorRgb<unsigned char>& pixel = data[py*size + px];
- ColorRgb<int> d = cb - ca;
- ColorRgb<int> p;
- p.r=pixel.r*16;
- p.g=pixel.g*16;
- p.b=pixel.b*16;
- ColorRgb<int> v = p - ca;
-
- // PVRTC uses weightings of 0, 3/8, 5/8 and 1
- // The boundaries for these are 3/16, 1/2 (=8/16), 13/16
- int projection = (v % d) * 16;
- int lengthSquared = d % d;
- if(projection > 3*lengthSquared) modulationData++;
- if(projection > 8*lengthSquared) modulationData++;
- if(projection > 13*lengthSquared) modulationData++;
-
- modulationData = BitUtility::RotateRight(modulationData, 2);
-
- factor++;
- }
- }
-
- PvrTcPacket* packet = packets + GetMortonNumber(x, y);
- packet->modulationData = modulationData;
- }
- }
-}
-
-//============================================================================
-
-typedef Interval<ColorRgba<unsigned char> > ColorRgbaBoundingBox;
-
-static void CalculateBoundingBox(ColorRgbaBoundingBox& cbb, const RgbaBitmap& bitmap, int blockX, int blockY)
-{
- int size = bitmap.GetBitmapWidth();
- const ColorRgba<unsigned char>* data = bitmap.GetData() + blockY * 4 * size + blockX * 4;
-
- cbb.min = data[0];
- cbb.max = data[0];
-
- cbb |= data[1];
- cbb |= data[2];
- cbb |= data[3];
-
- cbb |= data[size];
- cbb |= data[size+1];
- cbb |= data[size+2];
- cbb |= data[size+3];
-
- cbb |= data[2*size];
- cbb |= data[2*size+1];
- cbb |= data[2*size+2];
- cbb |= data[2*size+3];
-
- cbb |= data[3*size];
- cbb |= data[3*size+1];
- cbb |= data[3*size+2];
- cbb |= data[3*size+3];
-}
-
-void PvrTcEncoder::EncodeRgba4Bpp(void* result, const RgbaBitmap& bitmap)
-{
- assert(bitmap.GetBitmapWidth() == bitmap.GetBitmapHeight());
- assert(BitUtility::IsPowerOf2(bitmap.GetBitmapWidth()));
- const int size = bitmap.GetBitmapWidth();
- const int blocks = size / 4;
- const int blockMask = blocks-1;
-
- PvrTcPacket* packets = static_cast<PvrTcPacket*>(result);
-
- for(int y = 0; y < blocks; ++y)
- {
- for(int x = 0; x < blocks; ++x)
- {
- ColorRgbaBoundingBox cbb;
- CalculateBoundingBox(cbb, bitmap, x, y);
- PvrTcPacket* packet = packets + GetMortonNumber(x, y);
- packet->usePunchthroughAlpha = 0;
- packet->SetColorA(cbb.min);
- packet->SetColorB(cbb.max);
- }
- }
-
- for(int y = 0; y < blocks; ++y)
- {
- for(int x = 0; x < blocks; ++x)
- {
- const unsigned char (*factor)[4] = PvrTcPacket::BILINEAR_FACTORS;
- const ColorRgba<unsigned char>* data = bitmap.GetData() + y * 4 * size + x * 4;
-
- uint32_t modulationData = 0;
-
- for(int py = 0; py < 4; ++py)
- {
- const int yOffset = (py < 2) ? -1 : 0;
- const int y0 = (y + yOffset) & blockMask;
- const int y1 = (y0+1) & blockMask;
-
- for(int px = 0; px < 4; ++px)
- {
- const int xOffset = (px < 2) ? -1 : 0;
- const int x0 = (x + xOffset) & blockMask;
- const int x1 = (x0+1) & blockMask;
-
- const PvrTcPacket* p0 = packets + GetMortonNumber(x0, y0);
- const PvrTcPacket* p1 = packets + GetMortonNumber(x1, y0);
- const PvrTcPacket* p2 = packets + GetMortonNumber(x0, y1);
- const PvrTcPacket* p3 = packets + GetMortonNumber(x1, y1);
-
- ColorRgba<int> ca = p0->GetColorRgbaA() * (*factor)[0] +
- p1->GetColorRgbaA() * (*factor)[1] +
- p2->GetColorRgbaA() * (*factor)[2] +
- p3->GetColorRgbaA() * (*factor)[3];
-
- ColorRgba<int> cb = p0->GetColorRgbaB() * (*factor)[0] +
- p1->GetColorRgbaB() * (*factor)[1] +
- p2->GetColorRgbaB() * (*factor)[2] +
- p3->GetColorRgbaB() * (*factor)[3];
-
- const ColorRgba<unsigned char>& pixel = data[py*size + px];
- ColorRgba<int> d = cb - ca;
- ColorRgba<int> p;
- p.r=pixel.r*16;
- p.g=pixel.g*16;
- p.b=pixel.b*16;
- p.a=pixel.a*16;
- ColorRgba<int> v = p - ca;
-
- // PVRTC uses weightings of 0, 3/8, 5/8 and 1
- // The boundaries for these are 3/16, 1/2 (=8/16), 13/16
- int projection = (v % d) * 16;
- int lengthSquared = d % d;
- if(projection > 3*lengthSquared) modulationData++;
- if(projection > 8*lengthSquared) modulationData++;
- if(projection > 13*lengthSquared) modulationData++;
-
- modulationData = BitUtility::RotateRight(modulationData, 2);
-
- factor++;
- }
- }
-
- PvrTcPacket* packet = packets + GetMortonNumber(x, y);
- packet->modulationData = modulationData;
- }
- }
-}
-
-//============================================================================
diff --git a/thirdparty/pvrtccompressor/PvrTcEncoder.h b/thirdparty/pvrtccompressor/PvrTcEncoder.h
deleted file mode 100644
index b9344367d9..0000000000
--- a/thirdparty/pvrtccompressor/PvrTcEncoder.h
+++ /dev/null
@@ -1,40 +0,0 @@
-//============================================================================
-
-#pragma once
-#include "ColorRgba.h"
-
-//============================================================================
-
-namespace Javelin
-{
-//============================================================================
-
- class AlphaBitmap;
- class RgbBitmap;
- class RgbaBitmap;
-
- class PvrTcEncoder
- {
- public:
- // Result must be large enough for bitmap.GetArea()/4 bytes
- static void EncodeAlpha2Bpp(void* result, const AlphaBitmap& bitmap);
-
- // Result must be large enough for bitmap.GetArea()/2 bytes
- static void EncodeAlpha4Bpp(void* result, const AlphaBitmap& bitmap);
-
- // Result must be large enough for bitmap.GetArea()/2 bytes
- static void EncodeRgb4Bpp(void* result, const RgbBitmap& bitmap);
-
- // Result must be large enough for bitmap.GetArea()/2 bytes
- static void EncodeRgb4Bpp(void* result, const RgbaBitmap& bitmap);
-
- // Result must be large enough for bitmap.GetArea()/2 bytes
- static void EncodeRgba4Bpp(void* result, const RgbaBitmap& bitmap);
-
- private:
- static unsigned GetMortonNumber(int x, int y);
- };
-
-//============================================================================
-}
-//============================================================================
diff --git a/thirdparty/pvrtccompressor/PvrTcPacket.cpp b/thirdparty/pvrtccompressor/PvrTcPacket.cpp
deleted file mode 100644
index 2e40d371e8..0000000000
--- a/thirdparty/pvrtccompressor/PvrTcPacket.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-//============================================================================
-
-#include "PvrTcPacket.h"
-#include "BitScale.h"
-
-//============================================================================
-
-using namespace Javelin;
-
-//============================================================================
-
-const unsigned char PvrTcPacket::BILINEAR_FACTORS[16][4] =
-{
- { 4, 4, 4, 4 },
- { 2, 6, 2, 6 },
- { 8, 0, 8, 0 },
- { 6, 2, 6, 2 },
-
- { 2, 2, 6, 6 },
- { 1, 3, 3, 9 },
- { 4, 0, 12, 0 },
- { 3, 1, 9, 3 },
-
- { 8, 8, 0, 0 },
- { 4, 12, 0, 0 },
- { 16, 0, 0, 0 },
- { 12, 4, 0, 0 },
-
- { 6, 6, 2, 2 },
- { 3, 9, 1, 3 },
- { 12, 0, 4, 0 },
- { 9, 3, 3, 1 },
-};
-
-// Weights are { colorA, colorB, alphaA, alphaB }
-const unsigned char PvrTcPacket::WEIGHTS[8][4] =
-{
- // Weights for Mode=0
- { 8, 0, 8, 0 },
- { 5, 3, 5, 3 },
- { 3, 5, 3, 5 },
- { 0, 8, 0, 8 },
-
- // Weights for Mode=1
- { 8, 0, 8, 0 },
- { 4, 4, 4, 4 },
- { 4, 4, 0, 0 },
- { 0, 8, 0, 8 },
-};
-
-//============================================================================
-
-ColorRgb<int> PvrTcPacket::GetColorRgbA() const
-{
- if(colorAIsOpaque)
- {
- unsigned char r = colorA >> 9;
- unsigned char g = colorA >> 4 & 0x1f;
- unsigned char b = colorA & 0xf;
- return ColorRgb<int>(Data::BITSCALE_5_TO_8[r],
- Data::BITSCALE_5_TO_8[g],
- Data::BITSCALE_4_TO_8[b]);
- }
- else
- {
- unsigned char r = (colorA >> 7) & 0xf;
- unsigned char g = (colorA >> 3) & 0xf;
- unsigned char b = colorA & 7;
- return ColorRgb<int>(Data::BITSCALE_4_TO_8[r],
- Data::BITSCALE_4_TO_8[g],
- Data::BITSCALE_3_TO_8[b]);
- }
-}
-
-ColorRgb<int> PvrTcPacket::GetColorRgbB() const
-{
- if(colorBIsOpaque)
- {
- unsigned char r = colorB >> 10;
- unsigned char g = colorB >> 5 & 0x1f;
- unsigned char b = colorB & 0x1f;
- return ColorRgb<int>(Data::BITSCALE_5_TO_8[r],
- Data::BITSCALE_5_TO_8[g],
- Data::BITSCALE_5_TO_8[b]);
- }
- else
- {
- unsigned char r = colorB >> 8 & 0xf;
- unsigned char g = colorB >> 4 & 0xf;
- unsigned char b = colorB & 0xf;
- return ColorRgb<int>(Data::BITSCALE_4_TO_8[r],
- Data::BITSCALE_4_TO_8[g],
- Data::BITSCALE_4_TO_8[b]);
- }
-}
-
-ColorRgba<int> PvrTcPacket::GetColorRgbaA() const
-{
- if(colorAIsOpaque)
- {
- unsigned char r = colorA >> 9;
- unsigned char g = colorA >> 4 & 0x1f;
- unsigned char b = colorA & 0xf;
- return ColorRgba<int>(Data::BITSCALE_5_TO_8[r],
- Data::BITSCALE_5_TO_8[g],
- Data::BITSCALE_4_TO_8[b],
- 255);
- }
- else
- {
- unsigned char a = colorA >> 11 & 7;
- unsigned char r = colorA >> 7 & 0xf;
- unsigned char g = colorA >> 3 & 0xf;
- unsigned char b = colorA & 7;
- return ColorRgba<int>(Data::BITSCALE_4_TO_8[r],
- Data::BITSCALE_4_TO_8[g],
- Data::BITSCALE_3_TO_8[b],
- Data::BITSCALE_3_TO_8[a]);
- }
-}
-
-ColorRgba<int> PvrTcPacket::GetColorRgbaB() const
-{
- if(colorBIsOpaque)
- {
- unsigned char r = colorB >> 10;
- unsigned char g = colorB >> 5 & 0x1f;
- unsigned char b = colorB & 0x1f;
- return ColorRgba<int>(Data::BITSCALE_5_TO_8[r],
- Data::BITSCALE_5_TO_8[g],
- Data::BITSCALE_5_TO_8[b],
- 255);
- }
- else
- {
- unsigned char a = colorB >> 12 & 7;
- unsigned char r = colorB >> 8 & 0xf;
- unsigned char g = colorB >> 4 & 0xf;
- unsigned char b = colorB & 0xf;
- return ColorRgba<int>(Data::BITSCALE_4_TO_8[r],
- Data::BITSCALE_4_TO_8[g],
- Data::BITSCALE_4_TO_8[b],
- Data::BITSCALE_3_TO_8[a]);
- }
-}
-
-//============================================================================
-
-void PvrTcPacket::SetColorA(const ColorRgb<unsigned char>& c)
-{
- int r = Data::BITSCALE_8_TO_5_FLOOR[c.r];
- int g = Data::BITSCALE_8_TO_5_FLOOR[c.g];
- int b = Data::BITSCALE_8_TO_4_FLOOR[c.b];
- colorA = r<<9 | g<<4 | b;
- colorAIsOpaque = true;
-}
-
-void PvrTcPacket::SetColorB(const ColorRgb<unsigned char>& c)
-{
- int r = Data::BITSCALE_8_TO_5_CEIL[c.r];
- int g = Data::BITSCALE_8_TO_5_CEIL[c.g];
- int b = Data::BITSCALE_8_TO_5_CEIL[c.b];
- colorB = r<<10 | g<<5 | b;
- colorBIsOpaque = true;
-}
-
-void PvrTcPacket::SetColorA(const ColorRgba<unsigned char>& c)
-{
- int a = Data::BITSCALE_8_TO_3_FLOOR[c.a];
- if(a == 7)
- {
- int r = Data::BITSCALE_8_TO_5_FLOOR[c.r];
- int g = Data::BITSCALE_8_TO_5_FLOOR[c.g];
- int b = Data::BITSCALE_8_TO_4_FLOOR[c.b];
- colorA = r<<9 | g<<4 | b;
- colorAIsOpaque = true;
- }
- else
- {
- int r = Data::BITSCALE_8_TO_4_FLOOR[c.r];
- int g = Data::BITSCALE_8_TO_4_FLOOR[c.g];
- int b = Data::BITSCALE_8_TO_3_FLOOR[c.b];
- colorA = a<<11 | r<<7 | g<<3 | b;
- colorAIsOpaque = false;
- }
-}
-
-void PvrTcPacket::SetColorB(const ColorRgba<unsigned char>& c)
-{
- int a = Data::BITSCALE_8_TO_3_CEIL[c.a];
- if(a == 7)
- {
- int r = Data::BITSCALE_8_TO_5_CEIL[c.r];
- int g = Data::BITSCALE_8_TO_5_CEIL[c.g];
- int b = Data::BITSCALE_8_TO_5_CEIL[c.b];
- colorB = r<<10 | g<<5 | b;
- colorBIsOpaque = true;
- }
- else
- {
- int r = Data::BITSCALE_8_TO_4_CEIL[c.r];
- int g = Data::BITSCALE_8_TO_4_CEIL[c.g];
- int b = Data::BITSCALE_8_TO_4_CEIL[c.b];
- colorB = a<<12 | r<<8 | g<<4 | b;
- colorBIsOpaque = false;
- }
-}
-
-//============================================================================
diff --git a/thirdparty/pvrtccompressor/PvrTcPacket.h b/thirdparty/pvrtccompressor/PvrTcPacket.h
deleted file mode 100644
index ac3b6a4dd1..0000000000
--- a/thirdparty/pvrtccompressor/PvrTcPacket.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//============================================================================
-//
-// Modulation data specifies weightings of colorA to colorB for each pixel
-//
-// For mode = 0
-// 00: 0/8
-// 01: 3/8
-// 10: 5/8
-// 11: 8/8
-//
-// For mode = 1
-// 00: 0/8
-// 01: 4/8
-// 10: 4/8 with alpha punchthrough
-// 11: 8/8
-//
-// For colorIsOpaque=0
-// 3 bits A
-// 4 bits R
-// 4 bits G
-// 3/4 bits B
-//
-// For colorIsOpaque=1
-// 5 bits R
-// 5 bits G
-// 4/5 bits B
-//
-//============================================================================
-
-#pragma once
-#include "ColorRgba.h"
-
-//============================================================================
-
-namespace Javelin
-{
-//============================================================================
-
- struct PvrTcPacket
- {
- unsigned int modulationData;
- unsigned usePunchthroughAlpha : 1;
- unsigned colorA : 14;
- unsigned colorAIsOpaque : 1;
- unsigned colorB : 15;
- unsigned colorBIsOpaque : 1;
-
- ColorRgb<int> GetColorRgbA() const;
- ColorRgb<int> GetColorRgbB() const;
- ColorRgba<int> GetColorRgbaA() const;
- ColorRgba<int> GetColorRgbaB() const;
-
- void SetColorA(const ColorRgb<unsigned char>& c);
- void SetColorB(const ColorRgb<unsigned char>& c);
-
- void SetColorA(const ColorRgba<unsigned char>& c);
- void SetColorB(const ColorRgba<unsigned char>& c);
-
- static const unsigned char BILINEAR_FACTORS[16][4];
- static const unsigned char WEIGHTS[8][4];
- };
-
-//============================================================================
-} // namespace Javelin
-//============================================================================
diff --git a/thirdparty/pvrtccompressor/RgbBitmap.h b/thirdparty/pvrtccompressor/RgbBitmap.h
deleted file mode 100644
index cf1d78667d..0000000000
--- a/thirdparty/pvrtccompressor/RgbBitmap.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include "Bitmap.h"
-#include "ColorRgba.h"
-
-namespace Javelin {
-
-class RgbBitmap : public Bitmap {
-public:
- RgbBitmap(int w, int h)
- : Bitmap(w, h, 3) {
- }
-
- const ColorRgb<unsigned char> *GetData() const {
- return reinterpret_cast<ColorRgb<unsigned char> *>(data);
- }
-
- ColorRgb<unsigned char> *GetData() {
- return reinterpret_cast<ColorRgb<unsigned char> *>(data);
- }
-};
-
-}
diff --git a/thirdparty/pvrtccompressor/RgbaBitmap.h b/thirdparty/pvrtccompressor/RgbaBitmap.h
deleted file mode 100644
index 66b5542c1a..0000000000
--- a/thirdparty/pvrtccompressor/RgbaBitmap.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include "ColorRgba.h"
-#include "Bitmap.h"
-
-namespace Javelin {
-
-class RgbaBitmap : public Bitmap {
-public:
- RgbaBitmap(int w, int h)
- : Bitmap(w, h, 4) {
- }
-
- const ColorRgba<unsigned char> *GetData() const {
- return reinterpret_cast<ColorRgba<unsigned char> *>(data);
- }
-
- ColorRgba<unsigned char> *GetData() {
- return reinterpret_cast<ColorRgba<unsigned char> *>(data);
- }
-};
-
-}
diff --git a/thirdparty/thorvg/AUTHORS b/thirdparty/thorvg/AUTHORS
new file mode 100644
index 0000000000..ec06c49118
--- /dev/null
+++ b/thirdparty/thorvg/AUTHORS
@@ -0,0 +1,17 @@
+Hermet Park <chuneon.park@samsung.com>
+Prudhvi Raj Vasireddi <prudhvi.raj@samsung.com>
+Junsu Choi <jsuya.choi@samsung.com>
+Pranay Samanta <pranay.ks@samsung.com>
+Mateusz Palkowski <m.palkowski@samsung.com>
+Subhransu Mohanty <sub.mohanty@samsung.com>
+Mira Grudzinska <m.grudzinska@samsung.com>
+Michal Szczecinski <m.szczecinsk@partner.samsung.com>
+Shinwoo Kim <cinoo.kim@samsung.com>
+Piotr Kalota <p.kalota@samsung.com>
+Vincent Torri <vincent.torri@gmail.com>
+Pankaj Kumar <pankaj.m1@samsung.com>
+Patryk Kaczmarek <patryk.k@partner.samsung.com>
+Michal Maciola <m.maciola@samsung.com>
+Peter Vullings <peter@projectitis.com>
+K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com>
+Rémi Verschelde <rverschelde@gmail.com>
diff --git a/thirdparty/thorvg/LICENSE b/thirdparty/thorvg/LICENSE
new file mode 100644
index 0000000000..b096b0888e
--- /dev/null
+++ b/thirdparty/thorvg/LICENSE
@@ -0,0 +1,7 @@
+Copyright (c) 2020 - 2021 notice for the ThorVG Project (see AUTHORS)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
new file mode 100644
index 0000000000..41e8f6dafa
--- /dev/null
+++ b/thirdparty/thorvg/inc/config.h
@@ -0,0 +1,17 @@
+#ifndef THORVG_CONFIG_H
+#define THORVG_CONFIG_H
+
+#define THORVG_SW_RASTER_SUPPORT 1
+
+#define THORVG_SVG_LOADER_SUPPORT 1
+
+#define THORVG_PNG_LOADER_SUPPORT 1
+
+#define THORVG_TVG_LOADER_SUPPORT 1
+
+#define THORVG_TVG_SAVER_SUPPORT 1
+
+#define THORVG_JPG_LOADER_SUPPORT 1
+
+#define THORVG_VERSION_STRING "0.7.1"
+#endif
diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h
new file mode 100644
index 0000000000..e542d36555
--- /dev/null
+++ b/thirdparty/thorvg/inc/thorvg.h
@@ -0,0 +1,1589 @@
+/*!
+ * @file thorvg.h
+ *
+ * The main APIs enabling the TVG initialization, preparation of the canvas and provisioning of its content:
+ * - drawing shapes such as line, curve, arc, rectangle, circle or user-defined
+ * - drawing pictures - SVG, PNG, JPG, RAW
+ * - solid or gradient filling
+ * - continuous and dashed stroking
+ * - clipping and masking
+ * and finally drawing the canvas and TVG termination.
+ */
+
+
+#ifndef _THORVG_H_
+#define _THORVG_H_
+
+#include <memory>
+#include <string>
+
+#ifdef TVG_BUILD
+ #if defined(_MSC_VER) && !defined(__clang__)
+ #define TVG_EXPORT __declspec(dllexport)
+ #define TVG_DEPRECATED __declspec(deprecated)
+ #else
+ #define TVG_EXPORT __attribute__ ((visibility ("default")))
+ #define TVG_DEPRECATED __attribute__ ((__deprecated__))
+ #endif
+#else
+ #define TVG_EXPORT
+ #define TVG_DEPRECATED
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _TVG_DECLARE_PRIVATE(A) \
+protected: \
+ struct Impl; \
+ Impl* pImpl; \
+ A(const A&) = delete; \
+ const A& operator=(const A&) = delete; \
+ A()
+
+#define _TVG_DISABLE_CTOR(A) \
+ A() = delete; \
+ ~A() = delete
+
+#define _TVG_DECLARE_ACCESSOR() \
+ friend Canvas; \
+ friend Scene; \
+ friend Picture; \
+ friend Accessor; \
+ friend IteratorAccessor
+
+
+namespace tvg
+{
+
+class RenderMethod;
+class IteratorAccessor;
+class Scene;
+class Picture;
+class Canvas;
+class Accessor;
+
+/**
+ * @defgroup ThorVG ThorVG
+ * @brief ThorVG classes and enumerations providing C++ APIs.
+ */
+
+/**@{*/
+
+/**
+ * @brief Enumeration specifying the result from the APIs.
+ */
+enum class TVG_EXPORT Result
+{
+ Success = 0, ///< The value returned in case of a correct request execution.
+ InvalidArguments, ///< The value returned in the event of a problem with the arguments given to the API - e.g. empty paths or null pointers.
+ InsufficientCondition, ///< The value returned in case the request cannot be processed - e.g. asking for properties of an object, which does not exist.
+ FailedAllocation, ///< The value returned in case of unsuccessful memory allocation.
+ MemoryCorruption, ///< The value returned in the event of bad memory handling - e.g. failing in pointer releasing or casting
+ NonSupport, ///< The value returned in case of choosing unsupported options.
+ Unknown ///< The value returned in all other cases.
+};
+
+/**
+ * @brief Enumeration specifying the values of the path commands accepted by TVG.
+ *
+ * Not to be confused with the path commands from the svg path element (like M, L, Q, H and many others).
+ * TVG interprets all of them and translates to the ones from the PathCommand values.
+ */
+enum class TVG_EXPORT PathCommand
+{
+ Close = 0, ///< Ends the current sub-path and connects it with its initial point. This command doesn't expect any points.
+ MoveTo, ///< Sets a new initial point of the sub-path and a new current point. This command expects 1 point: the starting position.
+ LineTo, ///< Draws a line from the current point to the given point and sets a new value of the current point. This command expects 1 point: the end-position of the line.
+ CubicTo ///< Draws a cubic Bezier curve from the current point to the given point using two given control points and sets a new value of the current point. This command expects 3 points: the 1st control-point, the 2nd control-point, the end-point of the curve.
+};
+
+/**
+ * @brief Enumeration determining the ending type of a stroke in the open sub-paths.
+ */
+enum class TVG_EXPORT StrokeCap
+{
+ Square = 0, ///< The stroke is extended in both end-points of a sub-path by a rectangle, with the width equal to the stroke width and the length equal to the half of the stroke width. For zero length sub-paths the square is rendered with the size of the stroke width.
+ Round, ///< The stroke is extended in both end-points of a sub-path by a half circle, with a radius equal to the half of a stroke width. For zero length sub-paths a full circle is rendered.
+ Butt ///< The stroke ends exactly at each of the two end-points of a sub-path. For zero length sub-paths no stroke is rendered.
+};
+
+/**
+ * @brief Enumeration determining the style used at the corners of joined stroked path segments.
+ */
+enum class TVG_EXPORT StrokeJoin
+{
+ Bevel = 0, ///< The outer corner of the joined path segments is bevelled at the join point. The triangular region of the corner is enclosed by a straight line between the outer corners of each stroke.
+ Round, ///< The outer corner of the joined path segments is rounded. The circular region is centered at the join point.
+ Miter ///< The outer corner of the joined path segments is spiked. The spike is created by extension beyond the join point of the outer edges of the stroke until they intersect. In case the extension goes beyond the limit, the join style is converted to the Bevel style.
+};
+
+/**
+ * @brief Enumeration specifying how to fill the area outside the gradient bounds.
+ */
+enum class TVG_EXPORT FillSpread
+{
+ Pad = 0, ///< The remaining area is filled with the closest stop color.
+ Reflect, ///< The gradient pattern is reflected outside the gradient area until the expected region is filled.
+ Repeat ///< The gradient pattern is repeated continuously beyond the gradient area until the expected region is filled.
+};
+
+/**
+ * @brief Enumeration specifying the algorithm used to establish which parts of the shape are treated as the inside of the shape.
+ */
+enum class TVG_EXPORT FillRule
+{
+ Winding = 0, ///< A line from the point to a location outside the shape is drawn. The intersections of the line with the path segment of the shape are counted. Starting from zero, if the path segment of the shape crosses the line clockwise, one is added, otherwise one is subtracted. If the resulting sum is non zero, the point is inside the shape.
+ EvenOdd ///< A line from the point to a location outside the shape is drawn and its intersections with the path segments of the shape are counted. If the number of intersections is an odd number, the point is inside the shape.
+};
+
+/**
+ * @brief Enumeration indicating the method used in the composition of two objects - the target and the source.
+ */
+enum class TVG_EXPORT CompositeMethod
+{
+ None = 0, ///< No composition is applied.
+ ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered.
+ AlphaMask, ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which intersects with the target is visible.
+ InvAlphaMask ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which is not covered by the target is visible.
+};
+
+/**
+ * @brief Enumeration specifying the engine type used for the graphics backend. For multiple backends bitwise operation is allowed.
+ */
+enum class TVG_EXPORT CanvasEngine
+{
+ Sw = (1 << 1), ///< CPU rasterizer.
+ Gl = (1 << 2) ///< OpenGL rasterizer.
+};
+
+
+/**
+ * @brief A data structure representing a point in two-dimensional space.
+ */
+struct Point
+{
+ float x, y;
+};
+
+
+/**
+ * @brief A data structure representing a three-dimensional matrix.
+ *
+ * The elements e11, e12, e21 and e22 represent the rotation matrix, including the scaling factor.
+ * The elements e13 and e23 determine the translation of the object along the x and y-axis, respectively.
+ * The elements e31 and e32 are set to 0, e33 is set to 1.
+ */
+struct Matrix
+{
+ float e11, e12, e13;
+ float e21, e22, e23;
+ float e31, e32, e33;
+};
+
+
+/**
+ * @class Paint
+ *
+ * @brief An abstract class for managing graphical elements.
+ *
+ * A graphical element in TVG is any object composed into a Canvas.
+ * Paint represents such a graphical object and its behaviors such as duplication, transformation and composition.
+ * TVG recommends the user to regard a paint as a set of volatile commands. They can prepare a Paint and then request a Canvas to run them.
+ */
+class TVG_EXPORT Paint
+{
+public:
+ virtual ~Paint();
+
+ /**
+ * @brief Sets the angle by which the object is rotated.
+ *
+ * The angle in measured clockwise from the horizontal axis.
+ * The rotational axis passes through the point on the object with zero coordinates.
+ *
+ * @param[in] degree The value of the angle in degrees.
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ */
+ Result rotate(float degree) noexcept;
+
+ /**
+ * @brief Sets the scale value of the object.
+ *
+ * @param[in] factor The value of the scaling factor. The default value is 1.
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ */
+ Result scale(float factor) noexcept;
+
+ /**
+ * @brief Sets the values by which the object is moved in a two-dimensional space.
+ *
+ * The origin of the coordinate system is in the upper left corner of the canvas.
+ * The horizontal and vertical axes point to the right and down, respectively.
+ *
+ * @param[in] x The value of the horizontal shift.
+ * @param[in] y The value of the vertical shift.
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ */
+ Result translate(float x, float y) noexcept;
+
+ /**
+ * @brief Sets the matrix of the affine transformation for the object.
+ *
+ * The augmented matrix of the transformation is expected to be given.
+ *
+ * @param[in] m The 3x3 augmented matrix.
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ */
+ Result transform(const Matrix& m) noexcept;
+
+ /**
+ * @brief Gets the matrix of the affine transformation of the object.
+ *
+ * The values of the matrix can be set by the transform() API, as well by the translate(),
+ * scale() and rotate(). In case no transformation was applied, the identity matrix is returned.
+ *
+ * @retval The augmented transformation matrix.
+ *
+ * @since 0.4
+ */
+ Matrix transform() noexcept;
+
+ /**
+ * @brief Sets the opacity of the object.
+ *
+ * @param[in] o The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque.
+ *
+ * @return Result::Success when succeed.
+ *
+ * @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible.
+ */
+ Result opacity(uint8_t o) noexcept;
+
+ /**
+ * @brief Sets the composition target object and the composition method.
+ *
+ * @param[in] target The paint of the target object.
+ * @param[in] method The method used to composite the source object with the target.
+ *
+ * @return Result::Success when succeed, Result::InvalidArguments otherwise.
+ */
+ Result composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept;
+
+ /**
+ * @brief Gets the bounding box of the paint object before any transformation.
+ *
+ * @param[out] x The x coordinate of the upper left corner of the object.
+ * @param[out] y The y coordinate of the upper left corner of the object.
+ * @param[out] w The width of the object.
+ * @param[out] h The height of the object.
+ *
+ * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
+ *
+ * @note The bounding box doesn't indicate the final rendered region. It's the smallest rectangle that encloses the object.
+ * @see Paint::bounds(float* x, float* y, float* w, float* h, bool transformed);
+ */
+ TVG_DEPRECATED Result bounds(float* x, float* y, float* w, float* h) const noexcept;
+
+ /**
+ * @brief Gets the axis-aligned bounding box of the paint object.
+ *
+ * In case @p transform is @c true, all object's transformations are applied first, and then the bounding box is established. Otherwise, the bounding box is determined before any transformations.
+ *
+ * @param[out] x The x coordinate of the upper left corner of the object.
+ * @param[out] y The y coordinate of the upper left corner of the object.
+ * @param[out] w The width of the object.
+ * @param[out] h The height of the object.
+ * @param[in] transformed If @c true, the paint's transformations are taken into account, otherwise they aren't.
+ *
+ * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
+ *
+ * @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object.
+ */
+ Result bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept;
+
+ /**
+ * @brief Duplicates the object.
+ *
+ * Creates a new object and sets its all properties as in the original object.
+ *
+ * @return The created object when succeed, @c nullptr otherwise.
+ */
+ Paint* duplicate() const noexcept;
+
+ /**
+ * @brief Gets the opacity value of the object.
+ *
+ * @return The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque.
+ */
+ uint8_t opacity() const noexcept;
+
+ /**
+ * @brief Gets the composition target object and the composition method.
+ *
+ * @param[out] target The paint of the target object.
+ *
+ * @return The method used to composite the source object with the target.
+ *
+ * @since 0.5
+ */
+ CompositeMethod composite(const Paint** target) const noexcept;
+
+ /**
+ * @brief Return the unique id value of the paint instance.
+ *
+ * This method can be called for checking the current concrete instance type.
+ *
+ * @return The type id of the Paint instance.
+ *
+ * @BETA_API
+ */
+ uint32_t identifier() const noexcept;
+
+ _TVG_DECLARE_ACCESSOR();
+ _TVG_DECLARE_PRIVATE(Paint);
+};
+
+
+/**
+ * @class Fill
+ *
+ * @brief An abstract class representing the gradient fill of the Shape object.
+ *
+ * It contains the information about the gradient colors and their arrangement
+ * inside the gradient bounds. The gradients bounds are defined in the LinearGradient
+ * or RadialGradient class, depending on the type of the gradient to be used.
+ * It specifies the gradient behavior in case the area defined by the gradient bounds
+ * is smaller than the area to be filled.
+ */
+class TVG_EXPORT Fill
+{
+public:
+ /**
+ * @brief A data structure storing the information about the color and its relative position inside the gradient bounds.
+ */
+ struct ColorStop
+ {
+ float offset; /**< The relative position of the color. */
+ uint8_t r; /**< The red color channel value in the range [0 ~ 255]. */
+ uint8_t g; /**< The green color channel value in the range [0 ~ 255]. */
+ uint8_t b; /**< The blue color channel value in the range [0 ~ 255]. */
+ uint8_t a; /**< The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. */
+ };
+
+ virtual ~Fill();
+
+ /**
+ * @brief Sets the parameters of the colors of the gradient and their position.
+ *
+ * @param[in] colorStops An array of ColorStop data structure.
+ * @param[in] cnt The count of the @p colorStops array equal to the colors number used in the gradient.
+ *
+ * @return Result::Success when succeed.
+ */
+ Result colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept;
+
+ /**
+ * @brief Sets the FillSpread value, which specifies how to fill the area outside the gradient bounds.
+ *
+ * @param[in] s The FillSpread value.
+ *
+ * @return Result::Success when succeed.
+ */
+ Result spread(FillSpread s) noexcept;
+
+ /**
+ * @brief Sets the matrix of the affine transformation for the gradient fill.
+ *
+ * The augmented matrix of the transformation is expected to be given.
+ *
+ * @param[in] m The 3x3 augmented matrix.
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ */
+ Result transform(const Matrix& m) noexcept;
+
+ /**
+ * @brief Gets the parameters of the colors of the gradient, their position and number.
+ *
+ * @param[out] colorStops A pointer to the memory location, where the array of the gradient's ColorStop is stored.
+ *
+ * @return The number of colors used in the gradient. This value corresponds to the length of the @p colorStops array.
+ */
+ uint32_t colorStops(const ColorStop** colorStops) const noexcept;
+
+ /**
+ * @brief Gets the FillSpread value of the fill.
+ *
+ * @return The FillSpread value of this Fill.
+ */
+ FillSpread spread() const noexcept;
+
+ /**
+ * @brief Gets the matrix of the affine transformation of the gradient fill.
+ *
+ * In case no transformation was applied, the identity matrix is returned.
+ *
+ * @retval The augmented transformation matrix.
+ */
+ Matrix transform() const noexcept;
+
+ /**
+ * @brief Creates a copy of the Fill object.
+ *
+ * Return a newly created Fill object with the properties copied from the original.
+ *
+ * @return A copied Fill object when succeed, @c nullptr otherwise.
+ */
+ Fill* duplicate() const noexcept;
+
+ /**
+ * @brief Return the unique id value of the Fill instance.
+ *
+ * This method can be called for checking the current concrete instance type.
+ *
+ * @return The type id of the Fill instance.
+ *
+ * @BETA_API
+ */
+ uint32_t identifier() const noexcept;
+
+ _TVG_DECLARE_PRIVATE(Fill);
+};
+
+
+/**
+ * @class Canvas
+ *
+ * @brief An abstract class for drawing graphical elements.
+ *
+ * A canvas is an entity responsible for drawing the target. It sets up the drawing engine and the buffer, which can be drawn on the screen. It also manages given Paint objects.
+ *
+ * @note A Canvas behavior depends on the raster engine though the final content of the buffer is expected to be identical.
+ * @warning The Paint objects belonging to one Canvas can't be shared among multiple Canvases.
+ */
+class TVG_EXPORT Canvas
+{
+public:
+ Canvas(RenderMethod*);
+ virtual ~Canvas();
+
+ /**
+ * @brief Sets the size of the container, where all the paints pushed into the Canvas are stored.
+ *
+ * If the number of objects pushed into the Canvas is known in advance, calling the function
+ * prevents multiple memory reallocation, thus improving the performance.
+ *
+ * @param[in] n The number of objects for which the memory is to be reserved.
+ *
+ * @return Result::Success when succeed.
+ */
+ Result reserve(uint32_t n) noexcept;
+
+ /**
+ * @brief Passes drawing elements to the Canvas using Paint objects.
+ *
+ * Only pushed paints in the canvas will be drawing targets.
+ * They are retained by the canvas until you call Canvas::clear().
+ * If you know the number of the pushed objects in advance, please call Canvas::reserve().
+ *
+ * @param[in] paint A Paint object to be drawn.
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument.
+ * @retval Result::InsufficientCondition An internal error.
+ *
+ * @note The rendering order of the paints is the same as the order as they were pushed into the canvas. Consider sorting the paints before pushing them if you intend to use layering.
+ * @see Canvas::reserve()
+ * @see Canvas::clear()
+ */
+ virtual Result push(std::unique_ptr<Paint> paint) noexcept;
+
+ /**
+ * @brief Sets the total number of the paints pushed into the canvas to be zero.
+ * Depending on the value of the @p free argument, the paints are freed or not.
+ *
+ * @param[in] free If @c true, the memory occupied by paints is deallocated, otherwise it is not.
+ *
+ * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
+ *
+ * @warning If you don't free the paints they become dangled. They are supposed to be reused, otherwise you are responsible for their lives. Thus please use the @p free argument only when you know how it works, otherwise it's not recommended.
+ */
+ virtual Result clear(bool free = true) noexcept;
+
+ /**
+ * @brief Request the canvas to update the paint objects.
+ *
+ * If a @c nullptr is passed all paint objects retained by the Canvas are updated,
+ * otherwise only the paint to which the given @p paint points.
+ *
+ * @param[in] paint A pointer to the Paint object or @c nullptr.
+ *
+ * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
+ *
+ * @note The Update behavior can be asynchronous if the assigned thread number is greater than zero.
+ */
+ virtual Result update(Paint* paint = nullptr) noexcept;
+
+ /**
+ * @brief Requests the canvas to draw the Paint objects.
+ *
+ * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
+ *
+ * @note Drawing can be asynchronous if the assigned thread number is greater than zero. To guarantee the drawing is done, call sync() afterwards.
+ * @see Canvas::sync()
+ */
+ virtual Result draw() noexcept;
+
+ /**
+ * @brief Guarantees that drawing task is finished.
+ *
+ * The Canvas rendering can be performed asynchronously. To make sure that rendering is finished,
+ * the sync() must be called after the draw() regardless of threading.
+ *
+ * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
+ * @see Canvas::draw()
+ */
+ virtual Result sync() noexcept;
+
+ _TVG_DECLARE_PRIVATE(Canvas);
+};
+
+
+/**
+ * @class LinearGradient
+ *
+ * @brief A class representing the linear gradient fill of the Shape object.
+ *
+ * Besides the APIs inherited from the Fill class, it enables setting and getting the linear gradient bounds.
+ * The behavior outside the gradient bounds depends on the value specified in the spread API.
+ */
+class TVG_EXPORT LinearGradient final : public Fill
+{
+public:
+ ~LinearGradient();
+
+ /**
+ * @brief Sets the linear gradient bounds.
+ *
+ * The bounds of the linear gradient are defined as a surface constrained by two parallel lines crossing
+ * the given points (@p x1, @p y1) and (@p x2, @p y2), respectively. Both lines are perpendicular to the line linking
+ * (@p x1, @p y1) and (@p x2, @p y2).
+ *
+ * @param[in] x1 The horizontal coordinate of the first point used to determine the gradient bounds.
+ * @param[in] y1 The vertical coordinate of the first point used to determine the gradient bounds.
+ * @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds.
+ * @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds.
+ *
+ * @return Result::Success when succeed.
+ *
+ * @note In case the first and the second points are equal, an object filled with such a gradient fill is not rendered.
+ */
+ Result linear(float x1, float y1, float x2, float y2) noexcept;
+
+ /**
+ * @brief Gets the linear gradient bounds.
+ *
+ * The bounds of the linear gradient are defined as a surface constrained by two parallel lines crossing
+ * the given points (@p x1, @p y1) and (@p x2, @p y2), respectively. Both lines are perpendicular to the line linking
+ * (@p x1, @p y1) and (@p x2, @p y2).
+ *
+ * @param[out] x1 The horizontal coordinate of the first point used to determine the gradient bounds.
+ * @param[out] y1 The vertical coordinate of the first point used to determine the gradient bounds.
+ * @param[out] x2 The horizontal coordinate of the second point used to determine the gradient bounds.
+ * @param[out] y2 The vertical coordinate of the second point used to determine the gradient bounds.
+ *
+ * @return Result::Success when succeed.
+ */
+ Result linear(float* x1, float* y1, float* x2, float* y2) const noexcept;
+
+ /**
+ * @brief Creates a new LinearGradient object.
+ *
+ * @return A new LinearGradient object.
+ */
+ static std::unique_ptr<LinearGradient> gen() noexcept;
+
+ /**
+ * @brief Return the unique id value of this class.
+ *
+ * This method can be referred for identifying the LinearGradient class type.
+ *
+ * @return The type id of the LinearGradient class.
+ *
+ * @BETA_API
+ */
+ static uint32_t identifier() noexcept;
+
+ _TVG_DECLARE_PRIVATE(LinearGradient);
+};
+
+
+/**
+ * @class RadialGradient
+ *
+ * @brief A class representing the radial gradient fill of the Shape object.
+ *
+ */
+class TVG_EXPORT RadialGradient final : public Fill
+{
+public:
+ ~RadialGradient();
+
+ /**
+ * @brief Sets the radial gradient bounds.
+ *
+ * The radial gradient bounds are defined as a circle centered in a given point (@p cx, @p cy) of a given radius.
+ *
+ * @param[in] cx The horizontal coordinate of the center of the bounding circle.
+ * @param[in] cy The vertical coordinate of the center of the bounding circle.
+ * @param[in] radius The radius of the bounding circle.
+ *
+ * @return Result::Success when succeed, Result::InvalidArguments in case the @p radius value is zero or less.
+ */
+ Result radial(float cx, float cy, float radius) noexcept;
+
+ /**
+ * @brief Gets the radial gradient bounds.
+ *
+ * The radial gradient bounds are defined as a circle centered in a given point (@p cx, @p cy) of a given radius.
+ *
+ * @param[out] cx The horizontal coordinate of the center of the bounding circle.
+ * @param[out] cy The vertical coordinate of the center of the bounding circle.
+ * @param[out] radius The radius of the bounding circle.
+ *
+ * @return Result::Success when succeed.
+ */
+ Result radial(float* cx, float* cy, float* radius) const noexcept;
+
+ /**
+ * @brief Creates a new RadialGradient object.
+ *
+ * @return A new RadialGradient object.
+ */
+ static std::unique_ptr<RadialGradient> gen() noexcept;
+
+ /**
+ * @brief Return the unique id value of this class.
+ *
+ * This method can be referred for identifying the RadialGradient class type.
+ *
+ * @return The type id of the RadialGradient class.
+ *
+ * @BETA_API
+ */
+ static uint32_t identifier() noexcept;
+
+ _TVG_DECLARE_PRIVATE(RadialGradient);
+};
+
+
+/**
+ * @class Shape
+ *
+ * @brief A class representing two-dimensional figures and their properties.
+ *
+ * A shape has three major properties: shape outline, stroking, filling. The outline in the Shape is retained as the path.
+ * Path can be composed by accumulating primitive commands such as moveTo(), lineTo(), cubicTo(), or complete shape interfaces such as appendRect(), appendCircle(), etc.
+ * Path can consists of sub-paths. One sub-path is determined by a close command.
+ *
+ * The stroke of Shape is an optional property in case the Shape needs to be represented with/without the outline borders.
+ * It's efficient since the shape path and the stroking path can be shared with each other. It's also convenient when controlling both in one context.
+ */
+class TVG_EXPORT Shape final : public Paint
+{
+public:
+ ~Shape();
+
+ /**
+ * @brief Resets the properties of the shape path.
+ *
+ * The color, the fill and the stroke properties are retained.
+ *
+ * @return Result::Success when succeed.
+ *
+ * @note The memory, where the path data is stored, is not deallocated at this stage for caching effect.
+ */
+ Result reset() noexcept;
+
+ /**
+ * @brief Sets the initial point of the sub-path.
+ *
+ * The value of the current point is set to the given point.
+ *
+ * @param[in] x The horizontal coordinate of the initial point of the sub-path.
+ * @param[in] y The vertical coordinate of the initial point of the sub-path.
+ *
+ * @return Result::Success when succeed.
+ */
+ Result moveTo(float x, float y) noexcept;
+
+ /**
+ * @brief Adds a new point to the sub-path, which results in drawing a line from the current point to the given end-point.
+ *
+ * The value of the current point is set to the given end-point.
+ *
+ * @param[in] x The horizontal coordinate of the end-point of the line.
+ * @param[in] y The vertical coordinate of the end-point of the line.
+ *
+ * @return Result::Success when succeed.
+ *
+ * @note In case this is the first command in the path, it corresponds to the moveTo() call.
+ */
+ Result lineTo(float x, float y) noexcept;
+
+ /**
+ * @brief Adds new points to the sub-path, which results in drawing a cubic Bezier curve starting
+ * at the current point and ending at the given end-point (@p x, @p y) using the control points (@p cx1, @p cy1) and (@p cx2, @p cy2).
+ *
+ * The value of the current point is set to the given end-point.
+ *
+ * @param[in] cx1 The horizontal coordinate of the 1st control point.
+ * @param[in] cy1 The vertical coordinate of the 1st control point.
+ * @param[in] cx2 The horizontal coordinate of the 2nd control point.
+ * @param[in] cy2 The vertical coordinate of the 2nd control point.
+ * @param[in] x The horizontal coordinate of the end-point of the curve.
+ * @param[in] y The vertical coordinate of the end-point of the curve.
+ *
+ * @return Result::Success when succeed.
+ *
+ * @note In case this is the first command in the path, no data from the path are rendered.
+ */
+ Result cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept;
+
+ /**
+ * @brief Closes the current sub-path by drawing a line from the current point to the initial point of the sub-path.
+ *
+ * The value of the current point is set to the initial point of the closed sub-path.
+ *
+ * @return Result::Success when succeed.
+ *
+ * @note In case the sub-path does not contain any points, this function has no effect.
+ */
+ Result close() noexcept;
+
+ /**
+ * @brief Appends a rectangle to the path.
+ *
+ * The rectangle with rounded corners can be achieved by setting non-zero values to @p rx and @p ry arguments.
+ * The @p rx and @p ry values specify the radii of the ellipse defining the rounding of the corners.
+ *
+ * The position of the rectangle is specified by the coordinates of its upper left corner - @p x and @p y arguments.
+ *
+ * The rectangle is treated as a new sub-path - it is not connected with the previous sub-path.
+ *
+ * The value of the current point is set to (@p x + @p rx, @p y) - in case @p rx is greater
+ * than @p w/2 the current point is set to (@p x + @p w/2, @p y)
+ *
+ * @param[in] x The horizontal coordinate of the upper left corner of the rectangle.
+ * @param[in] y The vertical coordinate of the upper left corner of the rectangle.
+ * @param[in] w The width of the rectangle.
+ * @param[in] h The height of the rectangle.
+ * @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle.
+ * @param[in] ry The y-axis radius of the ellipse defining the rounded corners of the rectangle.
+ *
+ * @return Result::Success when succeed.
+ *
+ * @note For @p rx and @p ry greater than or equal to the half of @p w and the half of @p h, respectively, the shape become an ellipse.
+ */
+ Result appendRect(float x, float y, float w, float h, float rx, float ry) noexcept;
+
+ /**
+ * @brief Appends an ellipse to the path.
+ *
+ * The position of the ellipse is specified by the coordinates of its center - @p cx and @p cy arguments.
+ *
+ * The ellipse is treated as a new sub-path - it is not connected with the previous sub-path.
+ *
+ * The value of the current point is set to (@p cx, @p cy - @p ry).
+ *
+ * @param[in] cx The horizontal coordinate of the center of the ellipse.
+ * @param[in] cy The vertical coordinate of the center of the ellipse.
+ * @param[in] rx The x-axis radius of the ellipse.
+ * @param[in] ry The y-axis radius of the ellipse.
+ *
+ * @return Result::Success when succeed.
+ */
+ Result appendCircle(float cx, float cy, float rx, float ry) noexcept;
+
+ /**
+ * @brief Appends a circular arc to the path.
+ *
+ * The arc is treated as a new sub-path - it is not connected with the previous sub-path.
+ * The current point value is set to the end-point of the arc in case @p pie is @c false, and to the center of the arc otherwise.
+ *
+ * @param[in] cx The horizontal coordinate of the center of the arc.
+ * @param[in] cy The vertical coordinate of the center of the arc.
+ * @param[in] radius The radius of the arc.
+ * @param[in] startAngle The start angle of the arc given in degrees, measured counter-clockwise from the horizontal line.
+ * @param[in] sweep The central angle of the arc given in degrees, measured counter-clockwise from @p startAngle.
+ * @param[in] pie Specifies whether to draw radii from the arc's center to both of its end-point - drawn if @c true.
+ *
+ * @return Result::Success when succeed.
+ *
+ * @note Setting @p sweep value greater than 360 degrees, is equivalent to calling appendCircle(cx, cy, radius, radius).
+ */
+ Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept;
+
+ /**
+ * @brief Appends a given sub-path to the path.
+ *
+ * The current point value is set to the last point from the sub-path.
+ * For each command from the @p cmds array, an appropriate number of points in @p pts array should be specified.
+ * If the number of points in the @p pts array is different than the number required by the @p cmds array, the shape with this sub-path will not be displayed on the screen.
+ *
+ * @param[in] cmds The array of the commands in the sub-path.
+ * @param[in] cmdCnt The number of the sub-path's commands.
+ * @param[in] pts The array of the two-dimensional points.
+ * @param[in] ptsCnt The number of the points in the @p pts array.
+ *
+ * @return Result::Success when succeed, Result::InvalidArguments otherwise.
+ *
+ * @note The interface is designed for optimal path setting if the caller has a completed path commands already.
+ */
+ Result appendPath(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept;
+
+ /**
+ * @brief Sets the stroke width for all of the figures from the path.
+ *
+ * @param[in] width The width of the stroke. The default value is 0.
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ */
+ Result stroke(float width) noexcept;
+
+ /**
+ * @brief Sets the color of the stroke for all of the figures from the path.
+ *
+ * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0.
+ * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0.
+ * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0.
+ * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0.
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ */
+ Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept;
+
+ /**
+ * @brief Sets the gradient fill of the stroke for all of the figures from the path.
+ *
+ * @param[in] f The gradient fill.
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::FailedAllocation An internal error with a memory allocation for an object to be filled.
+ * @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument.
+ */
+ Result stroke(std::unique_ptr<Fill> f) noexcept;
+
+ /**
+ * @brief Sets the dash pattern of the stroke.
+ *
+ * @param[in] dashPattern The array of consecutive pair values of the dash length and the gap length.
+ * @param[in] cnt The length of the @p dashPattern array.
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::FailedAllocation An internal error with a memory allocation for an object to be dashed.
+ * @retval Result::InvalidArguments In case @p dashPattern is @c nullptr and @p cnt > 0, @p cnt is zero, any of the dash pattern values is zero or less.
+ *
+ * @note To reset the stroke dash pattern, pass @c nullptr to @p dashPattern and zero to @p cnt.
+ * @warning @p cnt must be greater than 1 if the dash pattern is valid.
+ */
+ Result stroke(const float* dashPattern, uint32_t cnt) noexcept;
+
+ /**
+ * @brief Sets the cap style of the stroke in the open sub-paths.
+ *
+ * @param[in] cap The cap style value. The default value is @c StrokeCap::Square.
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ */
+ Result stroke(StrokeCap cap) noexcept;
+
+ /**
+ * @brief Sets the join style for stroked path segments.
+ *
+ * The join style is used for joining the two line segment while stroking the path.
+ *
+ * @param[in] join The join style value. The default value is @c StrokeJoin::Bevel.
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ */
+ Result stroke(StrokeJoin join) noexcept;
+
+ /**
+ * @brief Sets the solid color for all of the figures from the path.
+ *
+ * The parts of the shape defined as inner are colored.
+ *
+ * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0.
+ * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0.
+ * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0.
+ * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0.
+ *
+ * @return Result::Success when succeed.
+ *
+ * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
+ */
+ Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept;
+
+ /**
+ * @brief Sets the gradient fill for all of the figures from the path.
+ *
+ * The parts of the shape defined as inner are filled.
+ *
+ * @param[in] f The unique pointer to the gradient fill.
+ *
+ * @return Result::Success when succeed, Result::MemoryCorruption otherwise.
+ *
+ * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
+ */
+ Result fill(std::unique_ptr<Fill> f) noexcept;
+
+ /**
+ * @brief Sets the fill rule for the Shape object.
+ *
+ * @param[in] r The fill rule value. The default value is @c FillRule::Winding.
+ *
+ * @return Result::Success when succeed.
+ */
+ Result fill(FillRule r) noexcept;
+
+ /**
+ * @brief Gets the commands data of the path.
+ *
+ * @param[out] cmds The pointer to the array of the commands from the path.
+ *
+ * @return The length of the @p cmds array when succeed, zero otherwise.
+ */
+ uint32_t pathCommands(const PathCommand** cmds) const noexcept;
+
+ /**
+ * @brief Gets the points values of the path.
+ *
+ * @param[out] pts The pointer to the array of the two-dimensional points from the path.
+ *
+ * @return The length of the @p pts array when succeed, zero otherwise.
+ */
+ uint32_t pathCoords(const Point** pts) const noexcept;
+
+ /**
+ * @brief Gets the pointer to the gradient fill of the shape.
+ *
+ * @return The pointer to the gradient fill of the stroke when succeed, @c nullptr in case no fill was set.
+ */
+ const Fill* fill() const noexcept;
+
+ /**
+ * @brief Gets the solid color of the shape.
+ *
+ * @param[out] r The red color channel value in the range [0 ~ 255].
+ * @param[out] g The green color channel value in the range [0 ~ 255].
+ * @param[out] b The blue color channel value in the range [0 ~ 255].
+ * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque.
+ *
+ * @return Result::Success when succeed.
+ */
+ Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept;
+
+ /**
+ * @brief Gets the fill rule value.
+ *
+ * @return The fill rule value of the shape.
+ */
+ FillRule fillRule() const noexcept;
+
+ /**
+ * @brief Gets the stroke width.
+ *
+ * @return The stroke width value when succeed, zero if no stroke was set.
+ */
+ float strokeWidth() const noexcept;
+
+ /**
+ * @brief Gets the color of the shape's stroke.
+ *
+ * @param[out] r The red color channel value in the range [0 ~ 255].
+ * @param[out] g The green color channel value in the range [0 ~ 255].
+ * @param[out] b The blue color channel value in the range [0 ~ 255].
+ * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque.
+ *
+ * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
+ */
+ Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept;
+
+ /**
+ * @brief Gets the pointer to the gradient fill of the stroke.
+ *
+ * @return The pointer to the gradient fill of the stroke when succeed, @c nullptr otherwise.
+ */
+ const Fill* strokeFill() const noexcept;
+
+ /**
+ * @brief Gets the dash pattern of the stroke.
+ *
+ * @param[out] dashPattern The pointer to the memory, where the dash pattern array is stored.
+ *
+ * @return The length of the @p dashPattern array.
+ */
+ uint32_t strokeDash(const float** dashPattern) const noexcept;
+
+ /**
+ * @brief Gets the cap style used for stroking the path.
+ *
+ * @return The cap style value of the stroke.
+ */
+ StrokeCap strokeCap() const noexcept;
+
+ /**
+ * @brief Gets the join style value used for stroking the path.
+ *
+ * @return The join style value of the stroke.
+ */
+ StrokeJoin strokeJoin() const noexcept;
+
+ /**
+ * @brief Creates a new Shape object.
+ *
+ * @return A new Shape object.
+ */
+ static std::unique_ptr<Shape> gen() noexcept;
+
+ /**
+ * @brief Return the unique id value of this class.
+ *
+ * This method can be referred for identifying the Shape class type.
+ *
+ * @return The type id of the Shape class.
+ *
+ * @BETA_API
+ */
+ static uint32_t identifier() noexcept;
+
+ _TVG_DECLARE_PRIVATE(Shape);
+};
+
+
+/**
+ * @class Picture
+ *
+ * @brief A class representing an image read in one of the supported formats: raw, svg, png, jpg and etc.
+ * Besides the methods inherited from the Paint, it provides methods to load & draw images on the canvas.
+ *
+ * @note Supported formats are depended on the available TVG loaders.
+ */
+class TVG_EXPORT Picture final : public Paint
+{
+public:
+ ~Picture();
+
+ /**
+ * @brief Loads a picture data directly from a file.
+ *
+ * @param[in] path A path to the picture file.
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::InvalidArguments In case the @p path is invalid.
+ * @retval Result::NonSupport When trying to load a file with an unknown extension.
+ * @retval Result::Unknown If an error occurs at a later stage.
+ *
+ * @note The Load behavior can be asynchronous if the assigned thread number is greater than zero.
+ * @see Initializer::init()
+ */
+ Result load(const std::string& path) noexcept;
+
+ /**
+ * @brief Loads a picture data from a memory block of a given size.
+ *
+ * @param[in] data A pointer to a memory location where the content of the picture file is stored.
+ * @param[in] size The size in bytes of the memory occupied by the @p data.
+ * @param[in] copy Decides whether the data should be copied into the engine local buffer.
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less.
+ * @retval Result::NonSupport When trying to load a file with an unknown extension.
+ * @retval Result::Unknown If an error occurs at a later stage.
+ *
+ * @warning: you have responsibility to release the @p data memory if the @p copy is true
+ * @deprecated Use load(const char* data, uint32_t size, const std::string& mimeType, bool copy) instead.
+ * @see Result load(const char* data, uint32_t size, const std::string& mimeType, bool copy = false) noexcept
+ */
+ TVG_DEPRECATED Result load(const char* data, uint32_t size, bool copy = false) noexcept;
+
+ /**
+ * @brief Loads a picture data from a memory block of a given size.
+ *
+ * @param[in] data A pointer to a memory location where the content of the picture file is stored.
+ * @param[in] size The size in bytes of the memory occupied by the @p data.
+ * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one.
+ * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less.
+ * @retval Result::NonSupport When trying to load a file with an unknown extension.
+ * @retval Result::Unknown If an error occurs at a later stage.
+ *
+ * @warning: It's the user responsibility to release the @p data memory if the @p copy is @c true.
+ *
+ * @since 0.5
+ */
+ Result load(const char* data, uint32_t size, const std::string& mimeType, bool copy = false) noexcept;
+
+ /**
+ * @brief Resizes the picture content to the given width and height.
+ *
+ * The picture content is resized while keeping the default size aspect ratio.
+ * The scaling factor is established for each of dimensions and the smaller value is applied to both of them.
+ *
+ * @param[in] w A new width of the image in pixels.
+ * @param[in] h A new height of the image in pixels.
+ *
+ * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
+ */
+ Result size(float w, float h) noexcept;
+
+ /**
+ * @brief Gets the size of the image.
+ *
+ * @param[out] w The width of the image in pixels.
+ * @param[out] h The height of the image in pixels.
+ *
+ * @return Result::Success when succeed.
+ */
+ Result size(float* w, float* h) const noexcept;
+
+ /**
+ * @brief Gets the pixels information of the picture.
+ *
+ * @note The data must be pre-multiplied by the alpha channels.
+ *
+ * @warning Please do not use it, this API is not official one. It could be modified in the next version.
+ *
+ * @BETA_API
+ */
+ const uint32_t* data(uint32_t* w, uint32_t* h) const noexcept;
+
+ /**
+ * @brief Loads a raw data from a memory block with a given size.
+ *
+ * @warning Please do not use it, this API is not official one. It could be modified in the next version.
+ *
+ * @BETA_API
+ */
+ Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept;
+
+ /**
+ * @brief Gets the position and the size of the loaded SVG picture.
+ *
+ * @warning Please do not use it, this API is not official one. It could be modified in the next version.
+ *
+ * @BETA_API
+ */
+ Result viewbox(float* x, float* y, float* w, float* h) const noexcept;
+
+ /**
+ * @brief Creates a new Picture object.
+ *
+ * @return A new Picture object.
+ */
+ static std::unique_ptr<Picture> gen() noexcept;
+
+ /**
+ * @brief Return the unique id value of this class.
+ *
+ * This method can be referred for identifying the Picture class type.
+ *
+ * @return The type id of the Picture class.
+ *
+ * @BETA_API
+ */
+ static uint32_t identifier() noexcept;
+
+ _TVG_DECLARE_PRIVATE(Picture);
+};
+
+
+/**
+ * @class Scene
+ *
+ * @brief A class to composite children paints.
+ *
+ * As the traditional graphics rendering method, TVG also enables scene-graph mechanism.
+ * This feature supports an array function for managing the multiple paints as one group paint.
+ *
+ * As a group, the scene can be transformed, made translucent and composited with other target paints,
+ * its children will be affected by the scene world.
+ */
+class TVG_EXPORT Scene final : public Paint
+{
+public:
+ ~Scene();
+
+ /**
+ * @brief Passes drawing elements to the Scene using Paint objects.
+ *
+ * Only the paints pushed into the scene will be the drawn targets.
+ * The paints are retained by the scene until Scene::clear() is called.
+ * If you know the number of the pushed objects in advance, please call Scene::reserve().
+ *
+ * @param[in] paint A Paint object to be drawn.
+ *
+ * @return Result::Success when succeed, Result::MemoryCorruption otherwise.
+ *
+ * @note The rendering order of the paints is the same as the order as they were pushed. Consider sorting the paints before pushing them if you intend to use layering.
+ * @see Scene::reserve()
+ */
+ Result push(std::unique_ptr<Paint> paint) noexcept;
+
+ /**
+ * @brief Sets the size of the container, where all the paints pushed into the Scene are stored.
+ *
+ * If the number of objects pushed into the scene is known in advance, calling the function
+ * prevents multiple memory reallocation, thus improving the performance.
+ *
+ * @param[in] size The number of objects for which the memory is to be reserved.
+ *
+ * @return Result::Success when succeed, Result::FailedAllocation otherwise.
+ */
+ Result reserve(uint32_t size) noexcept;
+
+ /**
+ * @brief Sets the total number of the paints pushed into the scene to be zero.
+ * Depending on the value of the @p free argument, the paints are freed or not.
+ *
+ * @param[in] free If @c true, the memory occupied by paints is deallocated, otherwise it is not.
+ *
+ * @return Result::Success when succeed
+ *
+ * @warning If you don't free the paints they become dangled. They are supposed to be reused, otherwise you are responsible for their lives. Thus please use the @p free argument only when you know how it works, otherwise it's not recommended.
+ *
+ * @since 0.2
+ */
+ Result clear(bool free = true) noexcept;
+
+ /**
+ * @brief Creates a new Scene object.
+ *
+ * @return A new Scene object.
+ */
+ static std::unique_ptr<Scene> gen() noexcept;
+
+ /**
+ * @brief Return the unique id value of this class.
+ *
+ * This method can be referred for identifying the Scene class type.
+ *
+ * @return The type id of the Scene class.
+ *
+ * @BETA_API
+ */
+ static uint32_t identifier() noexcept;
+
+ _TVG_DECLARE_PRIVATE(Scene);
+};
+
+
+/**
+ * @class SwCanvas
+ *
+ * @brief A class for the rendering graphical elements with a software raster engine.
+ */
+class TVG_EXPORT SwCanvas final : public Canvas
+{
+public:
+ ~SwCanvas();
+
+ /**
+ * @brief Enumeration specifying the methods of combining the 8-bit color channels into 32-bit color.
+ */
+ enum Colorspace
+ {
+ ABGR8888 = 0, ///< The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied.
+ ARGB8888, ///< The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied.
+ ABGR8888_STRAIGHT, ///< @BETA_API The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied.
+ ARGB8888_STRAIGHT, ///< @BETA_API The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied.
+ };
+
+ /**
+ * @brief Enumeration specifying the methods of Memory Pool behavior policy.
+ * @since 0.4
+ */
+ enum MempoolPolicy
+ {
+ Default = 0, ///< Default behavior that ThorVG is designed to.
+ Shareable, ///< Memory Pool is shared among the SwCanvases.
+ Individual ///< Allocate designated memory pool that is only used by current instance.
+ };
+
+ /**
+ * @brief Sets the target buffer for the rasterization.
+ *
+ * The buffer of a desirable size should be allocated and owned by the caller.
+ *
+ * @param[in] buffer A pointer to a memory block of the size @p stride x @p h, where the raster data are stored.
+ * @param[in] stride The stride of the raster image - greater than or equal to @p w.
+ * @param[in] w The width of the raster image.
+ * @param[in] h The height of the raster image.
+ * @param[in] cs The value specifying the way the 32-bits colors should be read/written.
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::MemoryCorruption When casting in the internal function implementation failed.
+ * @retval Result::InvalidArguments In case no valid pointer is provided or the width, or the height or the stride is zero.
+ * @retval Result::NonSupport In case the software engine is not supported.
+ *
+ * @warning Do not access @p buffer during Canvas::draw() - Canvas::sync(). It should not be accessed while TVG is writing on it.
+ */
+ Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept;
+
+ /**
+ * @brief Set sw engine memory pool behavior policy.
+ *
+ * Basically ThorVG draws a lot of shapes, it allocates/deallocates a few chunk of memory
+ * while processing rendering. It internally uses one shared memory pool
+ * which can be reused among the canvases in order to avoid memory overhead.
+ *
+ * Thus ThorVG suggests using a memory pool policy to satisfy user demands,
+ * if it needs to guarantee the thread-safety of the internal data access.
+ *
+ * @param[in] policy The method specifying the Memory Pool behavior. The default value is @c MempoolPolicy::Default.
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::InsufficientCondition If the canvas contains some paints already.
+ * @retval Result::NonSupport In case the software engine is not supported.
+ *
+ * @note When @c policy is set as @c MempoolPolicy::Individual, the current instance of canvas uses its own individual
+ * memory data, which is not shared with others. This is necessary when the canvas is accessed on a worker-thread.
+ *
+ * @warning It's not allowed after pushing any paints.
+ *
+ * @since 0.4
+ */
+ Result mempool(MempoolPolicy policy) noexcept;
+
+ /**
+ * @brief Creates a new SwCanvas object.
+ * @return A new SwCanvas object.
+ */
+ static std::unique_ptr<SwCanvas> gen() noexcept;
+
+ _TVG_DECLARE_PRIVATE(SwCanvas);
+};
+
+
+/**
+ * @class GlCanvas
+ *
+ * @brief A class for the rendering graphic elements with a GL raster engine.
+ *
+ * @warning Please do not use it. This class is not fully supported yet.
+ *
+ * @BETA_API
+ */
+class TVG_EXPORT GlCanvas final : public Canvas
+{
+public:
+ ~GlCanvas();
+
+ /**
+ * @brief Sets the target buffer for the rasterization.
+ *
+ * @warning Please do not use it, this API is not official one. It could be modified in the next version.
+ *
+ * @BETA_API
+ */
+ Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept;
+
+ /**
+ * @brief Creates a new GlCanvas object.
+ *
+ * @return A new GlCanvas object.
+ *
+ * @BETA_API
+ */
+ static std::unique_ptr<GlCanvas> gen() noexcept;
+
+ _TVG_DECLARE_PRIVATE(GlCanvas);
+};
+
+
+/**
+ * @class Initializer
+ *
+ * @brief A class that enables initialization and termination of the TVG engines.
+ */
+class TVG_EXPORT Initializer final
+{
+public:
+ /**
+ * @brief Initializes TVG engines.
+ *
+ * TVG requires the running-engine environment.
+ * TVG runs its own task-scheduler for parallelizing rendering tasks efficiently.
+ * You can indicate the number of threads, the count of which is designated @p threads.
+ * In the initialization step, TVG will generate/spawn the threads as set by @p threads count.
+ *
+ * @param[in] engine The engine types to initialize. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed.
+ * @param[in] threads The number of additional threads. Zero indicates only the main thread is to be used.
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::FailedAllocation An internal error possibly with memory allocation.
+ * @retval Result::InvalidArguments If unknown engine type chosen.
+ * @retval Result::NonSupport In case the engine type is not supported on the system.
+ * @retval Result::Unknown Others.
+ *
+ * @note The Initializer keeps track of the number of times it was called. Threads count is fixed at the first init() call.
+ * @see Initializer::term()
+ */
+ static Result init(CanvasEngine engine, uint32_t threads) noexcept;
+
+ /**
+ * @brief Terminates TVG engines.
+ *
+ * @param[in] engine The engine types to terminate. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::InsufficientCondition In case there is nothing to be terminated.
+ * @retval Result::InvalidArguments If unknown engine type chosen.
+ * @retval Result::NonSupport In case the engine type is not supported on the system.
+ * @retval Result::Unknown Others.
+ *
+ * @note Initializer does own reference counting for multiple calls.
+ * @see Initializer::init()
+ */
+ static Result term(CanvasEngine engine) noexcept;
+
+ _TVG_DISABLE_CTOR(Initializer);
+};
+
+
+/**
+ * @class Saver
+ *
+ * @brief A class for exporting a paint object into a specified file, from which to recover the paint data later.
+ *
+ * ThorVG provides a feature for exporting & importing paint data. The Saver role is to export the paint data to a file.
+ * It's useful when you need to save the composed scene or image from a paint object and recreate it later.
+ *
+ * The file format is decided by the extension name(i.e. "*.tvg") while the supported formats depend on the TVG packaging environment.
+ * If it doesn't support the file format, the save() method returns the @c Result::NonSuppport result.
+ *
+ * Once you export a paint to the file successfully, you can recreate it using the Picture class.
+ *
+ * @see Picture::load()
+ *
+ * @since 0.5
+ */
+class TVG_EXPORT Saver final
+{
+public:
+ ~Saver();
+
+ /**
+ * @brief Exports the given @p paint data to the given @p path
+ *
+ * If the saver module supports any compression mechanism, it will optimize the data size.
+ * This might affect the encoding/decoding time in some cases. You can turn off the compression
+ * if you wish to optimize for speed.
+ *
+ * @param[in] paint The paint to be saved with all its associated properties.
+ * @param[in] path A path to the file, in which the paint data is to be saved.
+ * @param[in] compress If @c true then compress data if possible.
+ *
+ * @retval Result::Success When succeed.
+ * @retval Result::InsufficientCondition If currently saving other resources.
+ * @retval Result::NonSupport When trying to save a file with an unknown extension or in an unsupported format.
+ * @retval Result::MemoryCorruption An internal error.
+ * @retval Result::Unknown In case an empty paint is to be saved.
+ *
+ * @note Saving can be asynchronous if the assigned thread number is greater than zero. To guarantee the saving is done, call sync() afterwards.
+ * @see Saver::sync()
+ *
+ * @since 0.5
+ */
+ Result save(std::unique_ptr<Paint> paint, const std::string& path, bool compress = true) noexcept;
+
+ /**
+ * @brief Guarantees that the saving task is finished.
+ *
+ * The behavior of the Saver works on a sync/async basis, depending on the threading setting of the Initializer.
+ * Thus, if you wish to have a benefit of it, you must call sync() after the save() in the proper delayed time.
+ * Otherwise, you can call sync() immediately.
+ *
+ * @retval Result::Success when succeed.
+ * @retval Result::InsufficientCondition otherwise.
+ *
+ * @note The asynchronous tasking is dependent on the Saver module implementation.
+ * @see Saver::save()
+ *
+ * @since 0.5
+ */
+ Result sync() noexcept;
+
+ /**
+ * @brief Creates a new Saver object.
+ *
+ * @return A new Saver object.
+ *
+ * @since 0.5
+ */
+ static std::unique_ptr<Saver> gen() noexcept;
+
+ _TVG_DECLARE_PRIVATE(Saver);
+};
+
+
+/**
+ * @class Accessor
+ *
+ * @brief The Accessor is a utility class to debug the Scene structure by traversing the scene-tree.
+ *
+ * The Accessor helps you search specific nodes to read the property information, figure out the structure of the scene tree and its size.
+ *
+ * @warning We strongly warn you not to change the paints of a scene unless you really know the design-structure.
+ *
+ * @BETA_API
+ */
+class TVG_EXPORT Accessor final
+{
+public:
+ ~Accessor();
+
+ /**
+ * @brief Access the Picture scene stree nodes.
+ *
+ * @param[in] picture The picture node to traverse the internal scene-tree.
+ * @param[in] func The callback function calling for every paint nodes of the Picture.
+ *
+ * @return Return the given @p picture instance.
+ *
+ * @note The bitmap based picture might not have the scene-tree.
+ *
+ * @BETA_API
+ */
+ std::unique_ptr<Picture> access(std::unique_ptr<Picture> picture, bool(*func)(const Paint* paint)) noexcept;
+
+ /**
+ * @brief Creates a new Accessor object.
+ *
+ * @return A new Accessor object.
+ *
+ * @BETA_API
+ */
+ static std::unique_ptr<Accessor> gen() noexcept;
+
+ _TVG_DECLARE_PRIVATE(Accessor);
+};
+
+/** @}*/
+
+} //namespace
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_THORVG_H_
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
new file mode 100644
index 0000000000..e0ffc1fb97
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SW_COMMON_H_
+#define _TVG_SW_COMMON_H_
+
+#include "tvgCommon.h"
+#include "tvgRender.h"
+
+#if 0
+#include <sys/time.h>
+static double timeStamp()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec + tv.tv_usec / 1000000.0);
+}
+#endif
+
+#define SW_CURVE_TYPE_POINT 0
+#define SW_CURVE_TYPE_CUBIC 1
+#define SW_ANGLE_PI (180L << 16)
+#define SW_ANGLE_2PI (SW_ANGLE_PI << 1)
+#define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1)
+#define SW_ANGLE_PI4 (SW_ANGLE_PI >> 2)
+
+using SwCoord = signed long;
+using SwFixed = signed long long;
+
+struct SwPoint
+{
+ SwCoord x, y;
+
+ SwPoint& operator+=(const SwPoint& rhs)
+ {
+ x += rhs.x;
+ y += rhs.y;
+ return *this;
+ }
+
+ SwPoint operator+(const SwPoint& rhs) const
+ {
+ return {x + rhs.x, y + rhs.y};
+ }
+
+ SwPoint operator-(const SwPoint& rhs) const
+ {
+ return {x - rhs.x, y - rhs.y};
+ }
+
+ bool operator==(const SwPoint& rhs) const
+ {
+ return (x == rhs.x && y == rhs.y);
+ }
+
+ bool operator!=(const SwPoint& rhs) const
+ {
+ return (x != rhs.x || y != rhs.y);
+ }
+
+ bool zero() const
+ {
+ if (x == 0 && y == 0) return true;
+ else return false;
+ }
+
+ bool small() const
+ {
+ //2 is epsilon...
+ if (abs(x) < 2 && abs(y) < 2) return true;
+ else return false;
+ }
+
+};
+
+struct SwSize
+{
+ SwCoord w, h;
+};
+
+struct SwOutline
+{
+ SwPoint* pts; //the outline's points
+ uint16_t ptsCnt; //number of points in the glyph
+ uint16_t reservedPtsCnt;
+ uint16_t* cntrs; //the contour end points
+ uint16_t cntrsCnt; //number of contours in glyph
+ uint16_t reservedCntrsCnt;
+ uint8_t* types; //curve type
+ bool* closed; //opened or closed path?
+ FillRule fillRule;
+};
+
+struct SwSpan
+{
+ uint16_t x, y;
+ uint16_t len;
+ uint8_t coverage;
+};
+
+struct SwRleData
+{
+ SwSpan *spans;
+ uint32_t alloc;
+ uint32_t size;
+};
+
+struct SwBBox
+{
+ SwPoint min, max;
+
+ void reset()
+ {
+ min.x = min.y = max.x = max.y = 0;
+ }
+};
+
+struct SwFill
+{
+ struct SwLinear {
+ float dx, dy;
+ float len;
+ float offset;
+ };
+
+ struct SwRadial {
+ float a11, a12, shiftX;
+ float a21, a22, shiftY;
+ float detSecDeriv;
+ float a;
+ };
+
+ union {
+ SwLinear linear;
+ SwRadial radial;
+ };
+
+ uint32_t* ctable;
+ FillSpread spread;
+
+ bool translucent;
+};
+
+struct SwStrokeBorder
+{
+ uint32_t ptsCnt;
+ uint32_t maxPts;
+ SwPoint* pts;
+ uint8_t* tags;
+ int32_t start; //index of current sub-path start point
+ bool movable; //true: for ends of lineto borders
+};
+
+struct SwStroke
+{
+ SwFixed angleIn;
+ SwFixed angleOut;
+ SwPoint center;
+ SwFixed lineLength;
+ SwFixed subPathAngle;
+ SwPoint ptStartSubPath;
+ SwFixed subPathLineLength;
+ SwFixed width;
+
+ StrokeCap cap;
+ StrokeJoin join;
+ StrokeJoin joinSaved;
+ SwFill* fill = nullptr;
+
+ SwStrokeBorder borders[2];
+
+ float sx, sy;
+
+ bool firstPt;
+ bool closedSubPath;
+ bool handleWideStrokes;
+};
+
+struct SwDashStroke
+{
+ SwOutline* outline;
+ float curLen;
+ int32_t curIdx;
+ Point ptStart;
+ Point ptCur;
+ float* pattern;
+ uint32_t cnt;
+ bool curOpGap;
+};
+
+struct SwShape
+{
+ SwOutline* outline = nullptr;
+ SwStroke* stroke = nullptr;
+ SwFill* fill = nullptr;
+ SwRleData* rle = nullptr;
+ SwRleData* strokeRle = nullptr;
+ SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling.
+
+ bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips?
+};
+
+struct SwImage
+{
+ SwOutline* outline = nullptr;
+ SwRleData* rle = nullptr;
+ uint32_t* data = nullptr;
+ uint32_t w, h, stride;
+ int32_t ox = 0; //offset x
+ int32_t oy = 0; //offset y
+ float scale;
+
+ bool direct = false; //draw image directly (with offset)
+ bool scaled = false; //draw scaled image
+};
+
+struct SwBlender
+{
+ uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+};
+
+struct SwCompositor;
+
+struct SwSurface : Surface
+{
+ SwBlender blender; //mandatory
+ SwCompositor* compositor = nullptr; //compositor (optional)
+};
+
+struct SwCompositor : Compositor
+{
+ SwSurface* recoverSfc; //Recover surface when composition is started
+ SwCompositor* recoverCmp; //Recover compositor when composition is done
+ SwImage image;
+ SwBBox bbox;
+ bool valid;
+};
+
+struct SwMpool
+{
+ SwOutline* outline;
+ SwOutline* strokeOutline;
+ unsigned allocSize;
+};
+
+static inline SwCoord TO_SWCOORD(float val)
+{
+ return SwCoord(val * 64.0f);
+}
+
+static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
+{
+ return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) +
+ ((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 8) & 0x00ff00ff));
+}
+
+static inline uint32_t INTERPOLATE(uint32_t a, uint32_t c0, uint32_t c1)
+{
+ return (((((((c0 >> 8) & 0xff00ff) - ((c1 >> 8) & 0xff00ff)) * a) + (c1 & 0xff00ff00)) & 0xff00ff00) + ((((((c0 & 0xff00ff) - (c1 & 0xff00ff)) * a) >> 8) + (c1 & 0xff00ff)) & 0xff00ff));
+}
+
+static inline SwCoord HALF_STROKE(float width)
+{
+ return TO_SWCOORD(width * 0.5f);
+}
+
+int64_t mathMultiply(int64_t a, int64_t b);
+int64_t mathDivide(int64_t a, int64_t b);
+int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
+void mathRotate(SwPoint& pt, SwFixed angle);
+SwFixed mathTan(SwFixed angle);
+SwFixed mathAtan(const SwPoint& pt);
+SwFixed mathCos(SwFixed angle);
+SwFixed mathSin(SwFixed angle);
+void mathSplitCubic(SwPoint* base);
+SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
+SwFixed mathLength(const SwPoint& pt);
+bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
+SwFixed mathMean(SwFixed angle1, SwFixed angle2);
+SwPoint mathTransform(const Point* to, const Matrix* transform);
+bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
+bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
+
+void shapeReset(SwShape* shape);
+bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
+bool shapePrepared(const SwShape* shape);
+bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias);
+void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
+void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
+bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
+void shapeFree(SwShape* shape);
+void shapeDelStroke(SwShape* shape);
+bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
+bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
+void shapeResetFill(SwShape* shape);
+void shapeResetStrokeFill(SwShape* shape);
+void shapeDelFill(SwShape* shape);
+void shapeDelStrokeFill(SwShape* shape);
+
+void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
+bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
+SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
+void strokeFree(SwStroke* stroke);
+
+bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
+bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
+void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
+void imageReset(SwImage* image);
+void imageFree(SwImage* image);
+
+bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
+void fillReset(SwFill* fill);
+void fillFree(SwFill* fill);
+void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
+void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
+
+SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
+void rleFree(SwRleData* rle);
+void rleReset(SwRleData* rle);
+void rleClipPath(SwRleData *rle, const SwRleData *clip);
+void rleClipRect(SwRleData *rle, const SwBBox* clip);
+
+SwMpool* mpoolInit(uint32_t threads);
+bool mpoolTerm(SwMpool* mpool);
+bool mpoolClear(SwMpool* mpool);
+SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx);
+void mpoolRetOutline(SwMpool* mpool, unsigned idx);
+SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx);
+void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx);
+
+bool rasterCompositor(SwSurface* surface);
+bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
+bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity);
+bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
+bool rasterClear(SwSurface* surface);
+void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
+void rasterUnpremultiply(SwSurface* surface);
+
+#endif /* _TVG_SW_COMMON_H_ */
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
new file mode 100644
index 0000000000..0bf051c17f
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgSwCommon.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+#define GRADIENT_STOP_SIZE 1024
+#define FIXPT_BITS 8
+#define FIXPT_SIZE (1<<FIXPT_BITS)
+
+
+static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint32_t opacity)
+{
+ if (!fill->ctable) {
+ fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
+ if (!fill->ctable) return false;
+ }
+
+ const Fill::ColorStop* colors;
+ auto cnt = fdata->colorStops(&colors);
+ if (cnt == 0 || !colors) return false;
+
+ auto pColors = colors;
+
+ auto a = (pColors->a * opacity) / 255;
+ if (a < 255) fill->translucent = true;
+
+ auto r = pColors->r;
+ auto g = pColors->g;
+ auto b = pColors->b;
+ auto rgba = surface->blender.join(r, g, b, a);
+
+ auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
+ auto pos = 1.5f * inc;
+ uint32_t i = 0;
+
+ fill->ctable[i++] = ALPHA_BLEND(rgba | 0xff000000, a);
+
+ while (pos <= pColors->offset) {
+ fill->ctable[i] = fill->ctable[i - 1];
+ ++i;
+ pos += inc;
+ }
+
+ for (uint32_t j = 0; j < cnt - 1; ++j) {
+ auto curr = colors + j;
+ auto next = curr + 1;
+ auto delta = 1.0f / (next->offset - curr->offset);
+ auto a2 = (next->a * opacity) / 255;
+ if (!fill->translucent && a2 < 255) fill->translucent = true;
+
+ auto rgba2 = surface->blender.join(next->r, next->g, next->b, a2);
+
+ while (pos < next->offset && i < GRADIENT_STOP_SIZE) {
+ auto t = (pos - curr->offset) * delta;
+ auto dist = static_cast<int32_t>(255 * t);
+ auto dist2 = 255 - dist;
+
+ auto color = INTERPOLATE(dist2, rgba, rgba2);
+ fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
+
+ ++i;
+ pos += inc;
+ }
+ rgba = rgba2;
+ a = a2;
+ }
+ rgba = ALPHA_BLEND((rgba | 0xff000000), a);
+
+ for (; i < GRADIENT_STOP_SIZE; ++i)
+ fill->ctable[i] = rgba;
+
+ //Make sure the last color stop is represented at the end of the table
+ fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba;
+
+ return true;
+}
+
+
+bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* transform)
+{
+ float x1, x2, y1, y2;
+ if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
+
+ fill->linear.dx = x2 - x1;
+ fill->linear.dy = y2 - y1;
+ fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
+
+ if (fill->linear.len < FLT_EPSILON) return true;
+
+ fill->linear.dx /= fill->linear.len;
+ fill->linear.dy /= fill->linear.len;
+ fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
+
+ auto gradTransform = linear->transform();
+ bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
+
+ if (isTransformation) {
+ if (transform) gradTransform = mathMultiply(transform, &gradTransform);
+ } else if (transform) {
+ gradTransform = *transform;
+ isTransformation = true;
+ }
+
+ if (isTransformation) {
+ Matrix invTransform;
+ if (!mathInverse(&gradTransform, &invTransform)) return false;
+
+ fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23;
+
+ auto dx = fill->linear.dx;
+ fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21;
+ fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22;
+
+ fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
+ if (fill->linear.len < FLT_EPSILON) return true;
+ }
+
+ return true;
+}
+
+
+bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
+{
+ float radius, cx, cy;
+ if (radial->radial(&cx, &cy, &radius) != Result::Success) return false;
+ if (radius < FLT_EPSILON) return true;
+
+ float invR = 1.0f / radius;
+ fill->radial.shiftX = -cx;
+ fill->radial.shiftY = -cy;
+ fill->radial.a = radius;
+
+ auto gradTransform = radial->transform();
+ bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
+
+ if (isTransformation) {
+ if (transform) gradTransform = mathMultiply(transform, &gradTransform);
+ } else if (transform) {
+ gradTransform = *transform;
+ isTransformation = true;
+ }
+
+ if (isTransformation) {
+ Matrix invTransform;
+ if (!mathInverse(&gradTransform, &invTransform)) return false;
+
+ fill->radial.a11 = invTransform.e11 * invR;
+ fill->radial.a12 = invTransform.e12 * invR;
+ fill->radial.shiftX += invTransform.e13;
+ fill->radial.a21 = invTransform.e21 * invR;
+ fill->radial.a22 = invTransform.e22 * invR;
+ fill->radial.shiftY += invTransform.e23;
+ fill->radial.detSecDeriv = 2.0f * fill->radial.a11 * fill->radial.a11 + 2 * fill->radial.a21 * fill->radial.a21;
+
+ fill->radial.a *= sqrt(pow(invTransform.e11, 2) + pow(invTransform.e21, 2));
+ } else {
+ fill->radial.a11 = fill->radial.a22 = invR;
+ fill->radial.a12 = fill->radial.a21 = 0.0f;
+ fill->radial.detSecDeriv = 2.0f * invR * invR;
+ }
+ fill->radial.shiftX *= invR;
+ fill->radial.shiftY *= invR;
+
+ return true;
+}
+
+
+static inline uint32_t _clamp(const SwFill* fill, int32_t pos)
+{
+ switch (fill->spread) {
+ case FillSpread::Pad: {
+ if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1;
+ else if (pos < 0) pos = 0;
+ break;
+ }
+ case FillSpread::Repeat: {
+ pos = pos % GRADIENT_STOP_SIZE;
+ if (pos < 0) pos = GRADIENT_STOP_SIZE + pos;
+ break;
+ }
+ case FillSpread::Reflect: {
+ auto limit = GRADIENT_STOP_SIZE * 2;
+ pos = pos % limit;
+ if (pos < 0) pos = limit + pos;
+ if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1);
+ break;
+ }
+ }
+ return pos;
+}
+
+
+static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos)
+{
+ int32_t i = (pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
+ return fill->ctable[_clamp(fill, i)];
+}
+
+
+static inline uint32_t _pixel(const SwFill* fill, float pos)
+{
+ auto i = static_cast<int32_t>(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f);
+ return fill->ctable[_clamp(fill, i)];
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
+{
+ auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
+ auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
+
+ // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
+ auto detSecondDerivative = fill->radial.detSecDeriv;
+ // detFirstDerivative = d(det)/dx
+ auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
+ auto det = rx * rx + ry * ry;
+
+ for (uint32_t i = 0 ; i < len ; ++i) {
+ *dst = _pixel(fill, sqrtf(det));
+ ++dst;
+ det += detFirstDerivative;
+ detFirstDerivative += detSecondDerivative;
+ }
+}
+
+
+void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
+{
+ //Rotation
+ float rx = x + 0.5f;
+ float ry = y + 0.5f;
+ float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
+ float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
+
+ if (mathZero(inc)) {
+ auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
+ rasterRGBA32(dst, color, 0, len);
+ return;
+ }
+
+ auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
+ auto vMin = -vMax;
+ auto v = t + (inc * len);
+
+ //we can use fixed point math
+ if (v < vMax && v > vMin) {
+ auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
+ auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
+ for (uint32_t j = 0; j < len; ++j) {
+ *dst = _fixedPixel(fill, t2);
+ ++dst;
+ t2 += inc2;
+ }
+ //we have to fallback to float math
+ } else {
+ uint32_t counter = 0;
+ while (counter++ < len) {
+ *dst = _pixel(fill, t / GRADIENT_STOP_SIZE);
+ ++dst;
+ t += inc;
+ }
+ }
+}
+
+
+bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
+{
+ if (!fill) return false;
+
+ fill->spread = fdata->spread();
+
+ if (ctable) {
+ if (!_updateColorTable(fill, fdata, surface, opacity)) return false;
+ }
+
+ if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
+ return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform);
+ } else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
+ return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform);
+ }
+
+ //LOG: What type of gradient?!
+
+ return false;
+}
+
+
+void fillReset(SwFill* fill)
+{
+ if (fill->ctable) {
+ free(fill->ctable);
+ fill->ctable = nullptr;
+ }
+ fill->translucent = false;
+}
+
+
+void fillFree(SwFill* fill)
+{
+ if (!fill) return;
+
+ if (fill->ctable) free(fill->ctable);
+
+ free(fill);
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
new file mode 100644
index 0000000000..f9974d9847
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgSwCommon.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static inline bool _onlyShifted(const Matrix* m)
+{
+ if (mathEqual(m->e11, 1.0f) && mathEqual(m->e22, 1.0f) && mathZero(m->e12) && mathZero(m->e21)) return true;
+ return false;
+}
+
+
+static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, unsigned tid)
+{
+ image->outline = mpoolReqOutline(mpool, tid);
+ auto outline = image->outline;
+
+ if (outline->reservedPtsCnt < 5) {
+ outline->reservedPtsCnt = 5;
+ outline->pts = static_cast<SwPoint*>(realloc(outline->pts, outline->reservedPtsCnt * sizeof(SwPoint)));
+ outline->types = static_cast<uint8_t*>(realloc(outline->types, outline->reservedPtsCnt * sizeof(uint8_t)));
+ }
+
+ if (outline->reservedCntrsCnt < 1) {
+ outline->reservedCntrsCnt = 1;
+ outline->cntrs = static_cast<uint16_t*>(realloc(outline->cntrs, outline->reservedCntrsCnt * sizeof(uint16_t)));
+ outline->closed = static_cast<bool*>(realloc(outline->closed, outline->reservedCntrsCnt * sizeof(bool)));
+ outline->closed[0] = true;
+ }
+
+ auto w = static_cast<float>(image->w);
+ auto h = static_cast<float>(image->h);
+
+ Point to[4] = {{0 ,0}, {w, 0}, {w, h}, {0, h}};
+ for (int i = 0; i < 4; i++) {
+ outline->pts[outline->ptsCnt] = mathTransform(&to[i], transform);
+ outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT;
+ ++outline->ptsCnt;
+ }
+
+ outline->pts[outline->ptsCnt] = outline->pts[0];
+ outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT;
+ ++outline->ptsCnt;
+
+ outline->cntrs[outline->cntrsCnt] = outline->ptsCnt - 1;
+ ++outline->cntrsCnt;
+
+ image->outline = outline;
+
+ return true;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
+{
+ image->direct = _onlyShifted(transform);
+
+ //Fast track: Non-transformed image but just shifted.
+ if (image->direct) {
+ image->ox = -static_cast<int32_t>(round(transform->e13));
+ image->oy = -static_cast<int32_t>(round(transform->e23));
+ //Figure out the scale factor by transform matrix
+ } else {
+ auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21));
+ auto scaleY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12));
+ image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX;
+
+ if (mathZero(transform->e12) && mathZero(transform->e21)) image->scaled = true;
+ else image->scaled = false;
+ }
+
+ if (!_genOutline(image, transform, mpool, tid)) return false;
+ return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
+}
+
+
+bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias)
+{
+ if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;
+
+ return false;
+}
+
+
+void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid)
+{
+ mpoolRetOutline(mpool, tid);
+ image->outline = nullptr;
+}
+
+
+void imageReset(SwImage* image)
+{
+ rleReset(image->rle);
+}
+
+
+void imageFree(SwImage* image)
+{
+ rleFree(image->rle);
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp
new file mode 100644
index 0000000000..7a3529bd69
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <math.h>
+#include "tvgSwCommon.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+//clz: count leading zero’s
+#if defined(_MSC_VER) && !defined(__clang__)
+ #include <intrin.h>
+ static uint32_t __inline _clz(uint32_t value)
+ {
+ unsigned long leadingZero = 0;
+ if (_BitScanReverse(&leadingZero, value)) return 31 - leadingZero;
+ else return 32;
+ }
+#else
+ #define _clz(x) __builtin_clz((x))
+#endif
+
+
+constexpr SwFixed CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
+
+//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
+constexpr static auto ATAN_MAX = 23;
+constexpr static SwFixed ATAN_TBL[] = {
+ 1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
+ 14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
+ 57L, 29L, 14L, 7L, 4L, 2L, 1L};
+
+static inline SwCoord SATURATE(const SwCoord x)
+{
+ return (x >> (sizeof(SwCoord) * 8 - 1));
+}
+
+
+static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n)
+{
+ return (((x) + ((n)/2)) & ~((n)-1));
+}
+
+
+static SwCoord _downscale(SwFixed x)
+{
+ //multiply a give value by the CORDIC shrink factor
+ auto s = abs(x);
+ int64_t t = (s * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
+ s = static_cast<SwFixed>(t >> 32);
+ if (x < 0) s = -s;
+ return s;
+}
+
+
+static int32_t _normalize(SwPoint& pt)
+{
+ /* the highest bit in overflow-safe vector components
+ MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
+ constexpr auto SAFE_MSB = 29;
+
+ auto v = pt;
+
+ //High order bit(MSB)
+ int32_t shift = 31 - _clz(abs(v.x) | abs(v.y));
+
+ if (shift <= SAFE_MSB) {
+ shift = SAFE_MSB - shift;
+ pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
+ pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
+ } else {
+ shift -= SAFE_MSB;
+ pt.x = v.x >> shift;
+ pt.y = v.y >> shift;
+ shift = -shift;
+ }
+ return shift;
+}
+
+
+static void _polarize(SwPoint& pt)
+{
+ auto v = pt;
+ SwFixed theta;
+
+ //Get the vector into [-PI/4, PI/4] sector
+ if (v.y > v.x) {
+ if (v.y > -v.x) {
+ auto tmp = v.y;
+ v.y = -v.x;
+ v.x = tmp;
+ theta = SW_ANGLE_PI2;
+ } else {
+ theta = v.y > 0 ? SW_ANGLE_PI : -SW_ANGLE_PI;
+ v.x = -v.x;
+ v.y = -v.y;
+ }
+ } else {
+ if (v.y < -v.x) {
+ theta = -SW_ANGLE_PI2;
+ auto tmp = -v.y;
+ v.y = v.x;
+ v.x = tmp;
+ } else {
+ theta = 0;
+ }
+ }
+
+ auto atan = ATAN_TBL;
+ uint32_t i;
+ SwFixed j;
+
+ //Pseudorotations. with right shifts
+ for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
+ if (v.y > 0) {
+ auto tmp = v.x + ((v.y + j) >> i);
+ v.y = v.y - ((v.x + j) >> i);
+ v.x = tmp;
+ theta += *atan++;
+ } else {
+ auto tmp = v.x - ((v.y + j) >> i);
+ v.y = v.y + ((v.x + j) >> i);
+ v.x = tmp;
+ theta -= *atan++;
+ }
+ }
+
+ //round theta
+ if (theta >= 0) theta = PAD_ROUND(theta, 32);
+ else theta = -PAD_ROUND(-theta, 32);
+
+ pt.x = v.x;
+ pt.y = theta;
+}
+
+
+static void _rotate(SwPoint& pt, SwFixed theta)
+{
+ SwFixed x = pt.x;
+ SwFixed y = pt.y;
+
+ //Rotate inside [-PI/4, PI/4] sector
+ while (theta < -SW_ANGLE_PI4) {
+ auto tmp = y;
+ y = -x;
+ x = tmp;
+ theta += SW_ANGLE_PI2;
+ }
+
+ while (theta > SW_ANGLE_PI4) {
+ auto tmp = -y;
+ y = x;
+ x = tmp;
+ theta -= SW_ANGLE_PI2;
+ }
+
+ auto atan = ATAN_TBL;
+ uint32_t i;
+ SwFixed j;
+
+ for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
+ if (theta < 0) {
+ auto tmp = x + ((y + j) >> i);
+ y = y - ((x + j) >> i);
+ x = tmp;
+ theta += *atan++;
+ } else {
+ auto tmp = x - ((y + j) >> i);
+ y = y + ((x + j) >> i);
+ x = tmp;
+ theta -= *atan++;
+ }
+ }
+
+ pt.x = static_cast<SwCoord>(x);
+ pt.y = static_cast<SwCoord>(y);
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+SwFixed mathMean(SwFixed angle1, SwFixed angle2)
+{
+ return angle1 + mathDiff(angle1, angle2) / 2;
+}
+
+
+bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
+{
+ auto d1 = base[2] - base[3];
+ auto d2 = base[1] - base[2];
+ auto d3 = base[0] - base[1];
+
+ if (d1.small()) {
+ if (d2.small()) {
+ if (d3.small()) {
+ //basically a point.
+ //do nothing to retain original direction
+ } else {
+ angleIn = angleMid = angleOut = mathAtan(d3);
+ }
+ } else {
+ if (d3.small()) {
+ angleIn = angleMid = angleOut = mathAtan(d2);
+ } else {
+ angleIn = angleMid = mathAtan(d2);
+ angleOut = mathAtan(d3);
+ }
+ }
+ } else {
+ if (d2.small()) {
+ if (d3.small()) {
+ angleIn = angleMid = angleOut = mathAtan(d1);
+ } else {
+ angleIn = mathAtan(d1);
+ angleOut = mathAtan(d3);
+ angleMid = mathMean(angleIn, angleOut);
+ }
+ } else {
+ if (d3.small()) {
+ angleIn = mathAtan(d1);
+ angleMid = angleOut = mathAtan(d2);
+ } else {
+ angleIn = mathAtan(d1);
+ angleMid = mathAtan(d2);
+ angleOut = mathAtan(d3);
+ }
+ }
+ }
+
+ auto theta1 = abs(mathDiff(angleIn, angleMid));
+ auto theta2 = abs(mathDiff(angleMid, angleOut));
+
+ if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true;
+ return false;
+}
+
+
+int64_t mathMultiply(int64_t a, int64_t b)
+{
+ int32_t s = 1;
+
+ //move sign
+ if (a < 0) {
+ a = -a;
+ s = -s;
+ }
+ if (b < 0) {
+ b = -b;
+ s = -s;
+ }
+ int64_t c = (a * b + 0x8000L) >> 16;
+ return (s > 0) ? c : -c;
+}
+
+
+int64_t mathDivide(int64_t a, int64_t b)
+{
+ int32_t s = 1;
+
+ //move sign
+ if (a < 0) {
+ a = -a;
+ s = -s;
+ }
+ if (b < 0) {
+ b = -b;
+ s = -s;
+ }
+ int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL;
+ return (s < 0 ? -q : q);
+}
+
+
+int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
+{
+ int32_t s = 1;
+
+ //move sign
+ if (a < 0) {
+ a = -a;
+ s = -s;
+ }
+ if (b < 0) {
+ b = -b;
+ s = -s;
+ }
+ if (c < 0) {
+ c = -c;
+ s = -s;
+ }
+ int64_t d = c > 0 ? (a * b + (c >> 1)) / c : 0x7FFFFFFFL;
+
+ return (s > 0 ? d : -d);
+}
+
+
+void mathRotate(SwPoint& pt, SwFixed angle)
+{
+ if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
+
+ auto v = pt;
+ auto shift = _normalize(v);
+
+ auto theta = angle;
+ _rotate(v, theta);
+
+ v.x = _downscale(v.x);
+ v.y = _downscale(v.y);
+
+ if (shift > 0) {
+ auto half = static_cast<int32_t>(1L << (shift - 1));
+ pt.x = (v.x + half + SATURATE(v.x)) >> shift;
+ pt.y = (v.y + half + SATURATE(v.y)) >> shift;
+ } else {
+ shift = -shift;
+ pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
+ pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
+ }
+}
+
+SwFixed mathTan(SwFixed angle)
+{
+ SwPoint v = {CORDIC_FACTOR >> 8, 0};
+ _rotate(v, angle);
+ return mathDivide(v.y, v.x);
+}
+
+
+SwFixed mathAtan(const SwPoint& pt)
+{
+ if (pt.x == 0 && pt.y == 0) return 0;
+
+ auto v = pt;
+ _normalize(v);
+ _polarize(v);
+
+ return v.y;
+}
+
+
+SwFixed mathSin(SwFixed angle)
+{
+ return mathCos(SW_ANGLE_PI2 - angle);
+}
+
+
+SwFixed mathCos(SwFixed angle)
+{
+ SwPoint v = {CORDIC_FACTOR >> 8, 0};
+ _rotate(v, angle);
+ return (v.x + 0x80L) >> 8;
+}
+
+
+SwFixed mathLength(const SwPoint& pt)
+{
+ auto v = pt;
+
+ //trivial case
+ if (v.x == 0) return abs(v.y);
+ if (v.y == 0) return abs(v.x);
+
+ //general case
+ auto shift = _normalize(v);
+ _polarize(v);
+ v.x = _downscale(v.x);
+
+ if (shift > 0) return (v.x + (static_cast<SwFixed>(1) << (shift -1))) >> shift;
+ return static_cast<SwFixed>((uint32_t)v.x << -shift);
+}
+
+
+void mathSplitCubic(SwPoint* base)
+{
+ SwCoord a, b, c, d;
+
+ base[6].x = base[3].x;
+ c = base[1].x;
+ d = base[2].x;
+ base[1].x = a = (base[0].x + c) / 2;
+ base[5].x = b = (base[3].x + d) / 2;
+ c = (c + d) / 2;
+ base[2].x = a = (a + c) / 2;
+ base[4].x = b = (b + c) / 2;
+ base[3].x = (a + b) / 2;
+
+ base[6].y = base[3].y;
+ c = base[1].y;
+ d = base[2].y;
+ base[1].y = a = (base[0].y + c) / 2;
+ base[5].y = b = (base[3].y + d) / 2;
+ c = (c + d) / 2;
+ base[2].y = a = (a + c) / 2;
+ base[4].y = b = (b + c) / 2;
+ base[3].y = (a + b) / 2;
+}
+
+
+SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
+{
+ auto delta = angle2 - angle1;
+
+ delta %= SW_ANGLE_2PI;
+ if (delta < 0) delta += SW_ANGLE_2PI;
+ if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI;
+
+ return delta;
+}
+
+
+SwPoint mathTransform(const Point* to, const Matrix* transform)
+{
+ if (!transform) return {TO_SWCOORD(to->x), TO_SWCOORD(to->y)};
+
+ auto tx = to->x * transform->e11 + to->y * transform->e12 + transform->e13;
+ auto ty = to->x * transform->e21 + to->y * transform->e22 + transform->e23;
+
+ return {TO_SWCOORD(tx), TO_SWCOORD(ty)};
+}
+
+
+bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee)
+{
+ clipee.max.x = (clipee.max.x < clipper.max.x) ? clipee.max.x : clipper.max.x;
+ clipee.max.y = (clipee.max.y < clipper.max.y) ? clipee.max.y : clipper.max.y;
+ clipee.min.x = (clipee.min.x > clipper.min.x) ? clipee.min.x : clipper.min.x;
+ clipee.min.y = (clipee.min.y > clipper.min.y) ? clipee.min.y : clipper.min.y;
+
+ //Check valid region
+ if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.min.y < 1) return false;
+
+ //Check boundary
+ if (clipee.min.x >= clipper.max.x || clipee.min.y >= clipper.max.y ||
+ clipee.max.x <= clipper.min.x || clipee.max.y <= clipper.min.y) return false;
+
+ return true;
+}
+
+
+bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack)
+{
+ if (!outline) return false;
+
+ auto pt = outline->pts;
+
+ if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) {
+ renderRegion.reset();
+ return false;
+ }
+
+ auto xMin = pt->x;
+ auto xMax = pt->x;
+ auto yMin = pt->y;
+ auto yMax = pt->y;
+
+ ++pt;
+
+ for (uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
+ if (xMin > pt->x) xMin = pt->x;
+ if (xMax < pt->x) xMax = pt->x;
+ if (yMin > pt->y) yMin = pt->y;
+ if (yMax < pt->y) yMax = pt->y;
+ }
+ //Since no antialiasing is applied in the Fast Track case,
+ //the rasterization region has to be rearranged.
+ //https://github.com/Samsung/thorvg/issues/916
+ if (fastTrack) {
+ renderRegion.min.x = static_cast<SwCoord>(round(xMin / 64.0f));
+ renderRegion.max.x = static_cast<SwCoord>(round(xMax / 64.0f));
+ renderRegion.min.y = static_cast<SwCoord>(round(yMin / 64.0f));
+ renderRegion.max.y = static_cast<SwCoord>(round(yMax / 64.0f));
+ } else {
+ renderRegion.min.x = xMin >> 6;
+ renderRegion.max.x = (xMax + 63) >> 6;
+ renderRegion.min.y = yMin >> 6;
+ renderRegion.max.y = (yMax + 63) >> 6;
+ }
+ return mathClipBBox(clipRegion, renderRegion);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp
new file mode 100644
index 0000000000..a44be85bbb
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgSwCommon.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx)
+{
+ return &mpool->outline[idx];
+}
+
+
+void mpoolRetOutline(SwMpool* mpool, unsigned idx)
+{
+ mpool->outline[idx].cntrsCnt = 0;
+ mpool->outline[idx].ptsCnt = 0;
+}
+
+
+SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx)
+{
+ return &mpool->strokeOutline[idx];
+}
+
+
+void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx)
+{
+ mpool->strokeOutline[idx].cntrsCnt = 0;
+ mpool->strokeOutline[idx].ptsCnt = 0;
+}
+
+
+SwMpool* mpoolInit(unsigned threads)
+{
+ if (threads == 0) threads = 1;
+
+ auto mpool = static_cast<SwMpool*>(calloc(sizeof(SwMpool), 1));
+ mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
+ if (!mpool->outline) goto err;
+
+ mpool->strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
+ if (!mpool->strokeOutline) goto err;
+
+ mpool->allocSize = threads;
+
+ return mpool;
+
+err:
+ if (mpool->outline) {
+ free(mpool->outline);
+ mpool->outline = nullptr;
+ }
+
+ if (mpool->strokeOutline) {
+ free(mpool->strokeOutline);
+ mpool->strokeOutline = nullptr;
+ }
+ free(mpool);
+ return nullptr;
+}
+
+
+bool mpoolClear(SwMpool* mpool)
+{
+ SwOutline* p;
+
+ for (unsigned i = 0; i < mpool->allocSize; ++i) {
+
+ //Outline
+ p = &mpool->outline[i];
+
+ free(p->cntrs);
+ p->cntrs = nullptr;
+
+ free(p->pts);
+ p->pts = nullptr;
+
+ free(p->types);
+ p->types = nullptr;
+
+ free(p->closed);
+ p->closed = nullptr;
+
+ p->cntrsCnt = p->reservedCntrsCnt = 0;
+ p->ptsCnt = p->reservedPtsCnt = 0;
+
+ //StrokeOutline
+ p = &mpool->strokeOutline[i];
+
+ free(p->cntrs);
+ p->cntrs = nullptr;
+
+ free(p->pts);
+ p->pts = nullptr;
+
+ free(p->types);
+ p->types = nullptr;
+
+ free(p->closed);
+ p->closed = nullptr;
+
+ p->cntrsCnt = p->reservedCntrsCnt = 0;
+ p->ptsCnt = p->reservedPtsCnt = 0;
+ }
+
+ return true;
+}
+
+
+bool mpoolTerm(SwMpool* mpool)
+{
+ if (!mpool) return false;
+
+ mpoolClear(mpool);
+
+ if (mpool->outline) {
+ free(mpool->outline);
+ mpool->outline = nullptr;
+ }
+
+ if (mpool->strokeOutline) {
+ free(mpool->strokeOutline);
+ mpool->strokeOutline = nullptr;
+ }
+
+ free(mpool);
+
+ return true;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
new file mode 100644
index 0000000000..56bc2f77dc
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
@@ -0,0 +1,1503 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgRender.h"
+#include "tvgSwCommon.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+constexpr auto DOWN_SCALE_TOLERANCE = 0.5f;
+
+
+static inline uint32_t _multiplyAlpha(uint32_t c, uint32_t a)
+{
+ return ((c * a + 0xff) >> 8);
+}
+
+
+static inline uint32_t _alpha(uint32_t c)
+{
+ return (c >> 24);
+}
+
+
+static inline uint32_t _ialpha(uint32_t c)
+{
+ return (~c >> 24);
+}
+
+
+static inline uint32_t _abgrJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+ return (a << 24 | b << 16 | g << 8 | r);
+}
+
+
+static inline uint32_t _argbJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+ return (a << 24 | r << 16 | g << 8 | b);
+}
+
+
+#include "tvgSwRasterTexmap.h"
+#include "tvgSwRasterC.h"
+#include "tvgSwRasterAvx.h"
+#include "tvgSwRasterNeon.h"
+
+
+static inline bool _compositing(const SwSurface* surface)
+{
+ if (!surface->compositor || surface->compositor->method == CompositeMethod::None) return false;
+ return true;
+}
+
+
+static inline uint32_t _halfScale(float scale)
+{
+ auto halfScale = static_cast<uint32_t>(0.5f / scale);
+ if (halfScale == 0) halfScale = 1;
+ return halfScale;
+}
+
+//Bilinear Interpolation
+static uint32_t _interpUpScaler(const uint32_t *img, uint32_t w, uint32_t h, float sx, float sy)
+{
+ auto rx = (uint32_t)(sx);
+ auto ry = (uint32_t)(sy);
+ auto rx2 = rx + 1;
+ if (rx2 >= w) rx2 = w - 1;
+ auto ry2 = ry + 1;
+ if (ry2 >= h) ry2 = h - 1;
+
+ auto dx = static_cast<uint32_t>((sx - rx) * 255.0f);
+ auto dy = static_cast<uint32_t>((sy - ry) * 255.0f);
+
+ auto c1 = img[rx + ry * w];
+ auto c2 = img[rx2 + ry * w];
+ auto c3 = img[rx2 + ry2 * w];
+ auto c4 = img[rx + ry2 * w];
+
+ return INTERPOLATE(dy, INTERPOLATE(dx, c3, c4), INTERPOLATE(dx, c2, c1));
+}
+
+
+//2n x 2n Mean Kernel
+static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, uint32_t rx, uint32_t ry, uint32_t n)
+{
+ uint32_t c[4] = {0, 0, 0, 0};
+ auto n2 = n * n;
+ auto src = img + rx - n + (ry - n) * stride;
+
+ for (auto y = ry - n; y < ry + n; ++y) {
+ if (y >= h) continue;
+ auto p = src;
+ for (auto x = rx - n; x < rx + n; ++x, ++p) {
+ if (x >= w) continue;
+ c[0] += *p >> 24;
+ c[1] += (*p >> 16) & 0xff;
+ c[2] += (*p >> 8) & 0xff;
+ c[3] += *p & 0xff;
+ }
+ src += stride;
+ }
+ for (auto i = 0; i < 4; ++i) {
+ c[i] = (c[i] >> 2) / n2;
+ }
+ return (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
+}
+
+
+/************************************************************************/
+/* Rect */
+/************************************************************************/
+
+static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint32_t color, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Masked Rect");
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+ auto cmp = &cbuffer[y * surface->stride];
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp) {
+ auto tmp = ALPHA_BLEND(color, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+{
+ auto buffer = surface->buffer + (region.min.y * surface->stride);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ rasterRGBA32(buffer + y * surface->stride, color, region.min.x, w);
+ }
+ return true;
+}
+
+
+static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint32_t color, uint8_t opacity)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterMaskedRect(surface, region, color, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterMaskedRect(surface, region, color, _ialpha);
+ }
+ } else {
+ if (opacity == 255) {
+ return _rasterSolidRect(surface, region, color);
+ } else {
+#if defined(THORVG_AVX_VECTOR_SUPPORT)
+ return avxRasterTranslucentRect(surface, region, color);
+#elif defined(THORVG_NEON_VECTOR_SUPPORT)
+ return neonRasterTranslucentRect(surface, region, color);
+#else
+ return cRasterTranslucentRect(surface, region, color);
+#endif
+ }
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* Rle */
+/************************************************************************/
+
+static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Masked Rle");
+
+ auto span = rle->spans;
+ uint32_t src;
+ auto cbuffer = surface->compositor->image.data;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ if (span->coverage == 255) src = color;
+ else src = ALPHA_BLEND(color, span->coverage);
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp) {
+ auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+{
+ auto span = rle->spans;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ if (span->coverage == 255) {
+ rasterRGBA32(surface->buffer + span->y * surface->stride, color, span->x, span->len);
+ } else {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto src = ALPHA_BLEND(color, span->coverage);
+ auto ialpha = 255 - span->coverage;
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = src + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint8_t opacity)
+{
+ if (!rle) return false;
+
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterMaskedRle(surface, rle, color, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterMaskedRle(surface, rle, color, _ialpha);
+ }
+ } else {
+ if (opacity == 255) {
+ return _rasterSolidRle(surface, rle, color);
+ } else {
+#if defined(THORVG_AVX_VECTOR_SUPPORT)
+ return avxRasterTranslucentRle(surface, rle, color);
+#elif defined(THORVG_NEON_VECTOR_SUPPORT)
+ return neonRasterTranslucentRle(surface, rle, color);
+#else
+ return cRasterTranslucentRle(surface, rle, color);
+#endif
+ }
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* RLE Transformed RGBA Image */
+/************************************************************************/
+
+static bool _transformedRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, uint32_t opacity)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _ialpha);
+ }
+ } else {
+ return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, nullptr);
+ }
+ return false;
+}
+
+/************************************************************************/
+/* RLE Scaled RGBA Image */
+/************************************************************************/
+
+static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Scaled Masked Translucent Rle Image");
+
+ auto span = image->rle->spans;
+
+ //Center (Down-Scaled)
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
+ auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ //Center (Up-Scaled)
+ } else {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = span->y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha);
+ auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Scaled Masked Rle Image");
+
+ auto span = image->rle->spans;
+
+ //Center (Down-Scaled)
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
+ if (span->coverage == 255) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto tmp = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage);
+ auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ //Center (Up-Scaled)
+ } else {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = span->y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
+ if (span->coverage == 255) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto tmp = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), span->coverage);
+ auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale)
+{
+ auto span = image->rle->spans;
+
+ //Center (Down-Scaled)
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ //Center (Up-Scaled)
+ } else {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = span->y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale)
+{
+ auto span = image->rle->spans;
+
+ //Center (Down-Scaled)
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ if (span->coverage == 255) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = _interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ } else {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ //Center (Up-Scaled)
+ } else {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = span->y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ if (span->coverage == 255) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = _interpUpScaler(image->data, image->w, image->h, sx, sy);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ } else {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), span->coverage);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
+{
+ Matrix itransform;
+
+ if (transform) {
+ if (!mathInverse(transform, &itransform)) return false;
+ } else mathIdentity(&itransform);
+
+ auto halfScale = _halfScale(image->scale);
+
+ if (_compositing(surface)) {
+ if (opacity == 255) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _ialpha);
+ }
+ } else {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha);
+ }
+ }
+ } else {
+ if (opacity == 255) return _rasterScaledRleRGBAImage(surface, image, &itransform, region, opacity, halfScale);
+ else return _rasterScaledTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* RLE Direct RGBA Image */
+/************************************************************************/
+
+static bool _rasterDirectMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Direct Masked Rle Image");
+
+ auto span = image->rle->spans;
+ auto cbuffer = surface->compositor->image.data;
+
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ if (alpha == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
+ auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
+ auto tmp = ALPHA_BLEND(*img, _multiplyAlpha(alpha, blendMethod(*cmp)));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterDirectMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Direct Masked Rle Image");
+
+ auto span = image->rle->spans;
+ auto cbuffer = surface->compositor->image.data;
+
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ if (span->coverage == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
+ auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
+ auto tmp = ALPHA_BLEND(*img, _multiplyAlpha(span->coverage, blendMethod(*cmp)));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterDirectTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity)
+{
+ auto span = image->rle->spans;
+
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
+ auto src = ALPHA_BLEND(*img, alpha);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterDirectRleRGBAImage(SwSurface* surface, const SwImage* image)
+{
+ auto span = image->rle->spans;
+
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ if (span->coverage == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
+ *dst = *img + ALPHA_BLEND(*dst, _ialpha(*img));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
+ auto src = ALPHA_BLEND(*img, span->coverage);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity)
+{
+ if (_compositing(surface)) {
+ if (opacity == 255) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterDirectMaskedRleRGBAImage(surface, image, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterDirectMaskedRleRGBAImage(surface, image, _ialpha);
+ }
+ } else {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _ialpha);
+ }
+ }
+ } else {
+ if (opacity == 255) return _rasterDirectRleRGBAImage(surface, image);
+ else return _rasterDirectTranslucentRleRGBAImage(surface, image, opacity);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* Transformed RGBA Image */
+/************************************************************************/
+
+static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterTexmapPolygon(surface, image, transform, &region, opacity, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterTexmapPolygon(surface, image, transform, &region, opacity, _ialpha);
+ }
+ } else {
+ return _rasterTexmapPolygon(surface, image, transform, &region, opacity, nullptr);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/*Scaled RGBA Image */
+/************************************************************************/
+
+
+static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Scaled Masked Image");
+
+ auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride + region.min.x);
+
+ // Down-Scaled
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto sy = (uint32_t)(y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = dbuffer;
+ auto cmp = cbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto alpha = _multiplyAlpha(opacity, blendMethod(*cmp));
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ dbuffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ }
+ // Up-Scaled
+ } else {
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto sy = y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = dbuffer;
+ auto cmp = cbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto alpha = _multiplyAlpha(opacity, blendMethod(*cmp));
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ dbuffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Scaled Masked Image");
+
+ auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride + region.min.x);
+
+ // Down-Scaled
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto sy = (uint32_t)(y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = dbuffer;
+ auto cmp = cbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp));
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ dbuffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ }
+ // Up-Scaled
+ } else {
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto sy = y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = dbuffer;
+ auto cmp = cbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), blendMethod(*cmp));
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ dbuffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale)
+{
+ auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
+
+ // Down-Scaled
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
+ auto sy = (uint32_t)(y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = dbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), opacity);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ // Up-Scaled
+ } else {
+ for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
+ auto sy = fabsf(y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = dbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), opacity);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale)
+{
+ auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
+
+ // Down-Scaled
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
+ auto sy = (uint32_t)(y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = dbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = _interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ // Up-Scaled
+ } else {
+ for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
+ auto sy = y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = dbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = _interpUpScaler(image->data, image->w, image->h, sx, sy);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
+{
+ Matrix itransform;
+
+ if (transform) {
+ if (!mathInverse(transform, &itransform)) return false;
+ } else mathIdentity(&itransform);
+
+ auto halfScale = _halfScale(image->scale);
+
+ if (_compositing(surface)) {
+ if (opacity == 255) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _ialpha);
+ }
+ } else {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha);
+ }
+ }
+ } else {
+ if (opacity == 255) return _rasterScaledRGBAImage(surface, image, &itransform, region, halfScale);
+ else return _rasterScaledTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* Direct RGBA Image */
+/************************************************************************/
+
+static bool _rasterDirectMaskedRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Direct Masked Image");
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h2 = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w2 = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer
+
+ for (uint32_t y = 0; y < h2; ++y) {
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, ++cmp) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ buffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ sbuffer += image->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Direct Masked Translucent Image");
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h2 = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w2 = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer
+
+ for (uint32_t y = 0; y < h2; ++y) {
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, ++cmp) {
+ auto tmp = ALPHA_BLEND(*src, _multiplyAlpha(opacity, blendMethod(*cmp)));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ buffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ sbuffer += image->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterDirectTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity)
+{
+ auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
+ auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto dst = dbuffer;
+ auto src = sbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
+ auto tmp = ALPHA_BLEND(*src, opacity);
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ dbuffer += surface->stride;
+ sbuffer += image->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region)
+{
+ auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
+ auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto dst = dbuffer;
+ auto src = sbuffer;
+ for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) {
+ *dst = *src + ALPHA_BLEND(*dst, _ialpha(*src));
+ }
+ dbuffer += surface->stride;
+ sbuffer += image->stride;
+ }
+ return true;
+}
+
+
+//Blenders for the following scenarios: [Composition / Non-Composition] * [Opaque / Translucent]
+static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity)
+{
+ if (_compositing(surface)) {
+ if (opacity == 255) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterDirectMaskedRGBAImage(surface, image, region, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterDirectMaskedRGBAImage(surface, image, region, _ialpha);
+ }
+ } else {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _ialpha);
+ }
+ }
+ } else {
+ if (opacity == 255) return _rasterDirectRGBAImage(surface, image, region);
+ else return _rasterDirectTranslucentRGBAImage(surface, image, region, opacity);
+ }
+ return false;
+}
+
+
+//Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed]
+static bool _rasterRGBAImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
+{
+ //RLE Image
+ if (image->rle) {
+ if (image->direct) return _directRleRGBAImage(surface, image, opacity);
+ else if (image->scaled) return _scaledRleRGBAImage(surface, image, transform, region, opacity);
+ else return _transformedRleRGBAImage(surface, image, transform, opacity);
+ //Whole Image
+ } else {
+ if (image->direct) return _directRGBAImage(surface, image, region, opacity);
+ else if (image->scaled) return _scaledRGBAImage(surface, image, transform, region, opacity);
+ else return _transformedRGBAImage(surface, image, transform, region, opacity);
+ }
+}
+
+
+/************************************************************************/
+/* Rect Linear Gradient */
+/************************************************************************/
+
+static bool _rasterLinearGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x;
+
+ auto sbuffer = static_cast<uint32_t*>(alloca(w * sizeof(uint32_t)));
+ if (!sbuffer) return false;
+
+ for (uint32_t y = 0; y < h; ++y) {
+ fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w);
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ buffer += surface->stride;
+ cbuffer += surface->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterTranslucentLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ auto sbuffer = static_cast<uint32_t*>(alloca(w * sizeof(uint32_t)));
+ if (!sbuffer) return false;
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = buffer;
+ fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w);
+ for (uint32_t x = 0; x < w; ++x, ++dst) {
+ *dst = sbuffer[x] + ALPHA_BLEND(*dst, _ialpha(sbuffer[x]));
+ }
+ buffer += surface->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterSolidLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ fillFetchLinear(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w);
+ }
+ return true;
+}
+
+
+static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterLinearGradientMaskedRect(surface, region, fill, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterLinearGradientMaskedRect(surface, region, fill, _ialpha);
+ }
+ } else {
+ if (fill->translucent) return _rasterTranslucentLinearGradientRect(surface, region, fill);
+ else _rasterSolidLinearGradientRect(surface, region, fill);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* Rle Linear Gradient */
+/************************************************************************/
+
+static bool _rasterLinearGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto span = rle->spans;
+ auto cbuffer = surface->compositor->image.data;
+ auto buffer = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buffer) return false;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ fillFetchLinear(fill, buffer, span->y, span->x, span->len);
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ auto src = buffer;
+ if (span->coverage == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ auto ialpha = 255 - span->coverage;
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ tmp = ALPHA_BLEND(tmp, span->coverage) + ALPHA_BLEND(*dst, ialpha);
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterTranslucentLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto span = rle->spans;
+ auto buffer = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buffer) return false;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ fillFetchLinear(fill, buffer, span->y, span->x, span->len);
+ if (span->coverage == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = buffer[x] + ALPHA_BLEND(*dst, _ialpha(buffer[x]));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ auto tmp = ALPHA_BLEND(buffer[x], span->coverage);
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterSolidLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto buf = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buf) return false;
+
+ auto span = rle->spans;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ if (span->coverage == 255) {
+ fillFetchLinear(fill, surface->buffer + span->y * surface->stride + span->x, span->y, span->x, span->len);
+ } else {
+ fillFetchLinear(fill, buf, span->y, span->x, span->len);
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ for (uint32_t x = 0; x < span->len; ++x) {
+ dst[x] = INTERPOLATE(span->coverage, buf[x], dst[x]);
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (!rle) return false;
+
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterLinearGradientMaskedRle(surface, rle, fill, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterLinearGradientMaskedRle(surface, rle, fill, _ialpha);
+ }
+ } else {
+ if (fill->translucent) return _rasterTranslucentLinearGradientRle(surface, rle, fill);
+ else return _rasterSolidLinearGradientRle(surface, rle, fill);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* Rect Radial Gradient */
+/************************************************************************/
+
+static bool _rasterRadialGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x;
+
+ auto sbuffer = static_cast<uint32_t*>(alloca(w * sizeof(uint32_t)));
+ if (!sbuffer) return false;
+
+ for (uint32_t y = 0; y < h; ++y) {
+ fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w);
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ buffer += surface->stride;
+ cbuffer += surface->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterTranslucentRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ auto sbuffer = static_cast<uint32_t*>(alloca(w * sizeof(uint32_t)));
+ if (!sbuffer) return false;
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = buffer;
+ fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w);
+ for (uint32_t x = 0; x < w; ++x, ++dst) {
+ *dst = sbuffer[x] + ALPHA_BLEND(*dst, _ialpha(sbuffer[x]));
+ }
+ buffer += surface->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterSolidRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+ fillFetchRadial(fill, dst, region.min.y + y, region.min.x, w);
+ }
+ return true;
+}
+
+
+static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterRadialGradientMaskedRect(surface, region, fill, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterRadialGradientMaskedRect(surface, region, fill, _ialpha);
+ }
+ } else {
+ if (fill->translucent) return _rasterTranslucentRadialGradientRect(surface, region, fill);
+ else return _rasterSolidRadialGradientRect(surface, region, fill);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* RLE Radial Gradient */
+/************************************************************************/
+
+static bool _rasterRadialGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto span = rle->spans;
+ auto cbuffer = surface->compositor->image.data;
+ auto buffer = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buffer) return false;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ fillFetchRadial(fill, buffer, span->y, span->x, span->len);
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ auto src = buffer;
+ if (span->coverage == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = INTERPOLATE(span->coverage, ALPHA_BLEND(*src, blendMethod(*cmp)), *dst);
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterTranslucentRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto span = rle->spans;
+ auto buffer = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buffer) return false;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ fillFetchRadial(fill, buffer, span->y, span->x, span->len);
+ if (span->coverage == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = buffer[x] + ALPHA_BLEND(*dst, _ialpha(buffer[x]));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ auto tmp = ALPHA_BLEND(buffer[x], span->coverage);
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterSolidRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto buf = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buf) return false;
+
+ auto span = rle->spans;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ if (span->coverage == 255) {
+ fillFetchRadial(fill, dst, span->y, span->x, span->len);
+ } else {
+ fillFetchRadial(fill, buf, span->y, span->x, span->len);
+ auto ialpha = 255 - span->coverage;
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = ALPHA_BLEND(buf[x], span->coverage) + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (!rle) return false;
+
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterRadialGradientMaskedRle(surface, rle, fill, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterRadialGradientMaskedRle(surface, rle, fill, _ialpha);
+ }
+ } else {
+ if (fill->translucent) _rasterTranslucentRadialGradientRle(surface, rle, fill);
+ else return _rasterSolidRadialGradientRle(surface, rle, fill);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+{
+#if defined(THORVG_AVX_VECTOR_SUPPORT)
+ avxRasterRGBA32(dst, val, offset, len);
+#elif defined(THORVG_NEON_VECTOR_SUPPORT)
+ neonRasterRGBA32(dst, val, offset, len);
+#else
+ cRasterRGBA32(dst, val, offset, len);
+#endif
+}
+
+
+bool rasterCompositor(SwSurface* surface)
+{
+ if (surface->cs == SwCanvas::ABGR8888 || surface->cs == SwCanvas::ABGR8888_STRAIGHT) {
+ surface->blender.join = _abgrJoin;
+ } else if (surface->cs == SwCanvas::ARGB8888 || surface->cs == SwCanvas::ARGB8888_STRAIGHT) {
+ surface->blender.join = _argbJoin;
+ } else {
+ //What Color Space ???
+ return false;
+ }
+ return true;
+}
+
+
+bool rasterClear(SwSurface* surface)
+{
+ if (!surface || !surface->buffer || surface->stride <= 0 || surface->w <= 0 || surface->h <= 0) return false;
+
+ if (surface->w == surface->stride) {
+ rasterRGBA32(surface->buffer, 0x00000000, 0, surface->w * surface->h);
+ } else {
+ for (uint32_t i = 0; i < surface->h; i++) {
+ rasterRGBA32(surface->buffer + surface->stride * i, 0x00000000, 0, surface->w);
+ }
+ }
+ return true;
+}
+
+
+void rasterUnpremultiply(SwSurface* surface)
+{
+ //OPTIMIZE_ME: +SIMD
+ for (uint32_t y = 0; y < surface->h; y++) {
+ auto buffer = surface->buffer + surface->stride * y;
+ for (uint32_t x = 0; x < surface->w; ++x) {
+ uint8_t a = buffer[x] >> 24;
+ if (a == 255) {
+ continue;
+ } else if (a == 0) {
+ buffer[x] = 0x00ffffff;
+ } else {
+ uint16_t r = ((buffer[x] >> 8) & 0xff00) / a;
+ uint16_t g = ((buffer[x]) & 0xff00) / a;
+ uint16_t b = ((buffer[x] << 8) & 0xff00) / a;
+ if (r > 0xff) r = 0xff;
+ if (g > 0xff) g = 0xff;
+ if (b > 0xff) b = 0xff;
+ buffer[x] = (a << 24) | (r << 16) | (g << 8) | (b);
+ }
+ }
+ }
+}
+
+
+bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
+{
+ if (!shape->fill) return false;
+
+ if (shape->fastTrack) {
+ if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill);
+ else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill);
+ } else {
+ if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->rle, shape->fill);
+ else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->rle, shape->fill);
+ }
+ return false;
+}
+
+
+bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id)
+{
+ if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false;
+
+ if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
+ else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
+
+ return false;
+}
+
+
+bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+ if (a < 255) {
+ r = _multiplyAlpha(r, a);
+ g = _multiplyAlpha(g, a);
+ b = _multiplyAlpha(b, a);
+ }
+
+ auto color = surface->blender.join(r, g, b, a);
+
+ if (shape->fastTrack) return _rasterRect(surface, shape->bbox, color, a);
+ else return _rasterRle(surface, shape->rle, color, a);
+}
+
+
+bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+ if (a < 255) {
+ r = _multiplyAlpha(r, a);
+ g = _multiplyAlpha(g, a);
+ b = _multiplyAlpha(b, a);
+ }
+
+ auto color = surface->blender.join(r, g, b, a);
+
+ return _rasterRle(surface, shape->strokeRle, color, a);
+}
+
+
+bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity)
+{
+ //Verify Boundary
+ if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return false;
+
+ //TOOD: switch (image->format)
+ //TODO: case: _rasterRGBImage()
+ //TODO: case: _rasterGrayscaleImage()
+ //TODO: case: _rasterAlphaImage()
+ return _rasterRGBAImage(surface, image, transform, bbox, opacity);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h
new file mode 100644
index 0000000000..1d6552f3e9
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifdef THORVG_AVX_VECTOR_SUPPORT
+
+#include <immintrin.h>
+
+#define N_32BITS_IN_128REG 4
+#define N_32BITS_IN_256REG 8
+
+static inline __m128i ALPHA_BLEND(__m128i c, __m128i a)
+{
+ //1. set the masks for the A/G and R/B channels
+ auto AG = _mm_set1_epi32(0xff00ff00);
+ auto RB = _mm_set1_epi32(0x00ff00ff);
+
+ //2. mask the alpha vector - originally quartet [a, a, a, a]
+ auto aAG = _mm_and_si128(a, AG);
+ auto aRB = _mm_and_si128(a, RB);
+
+ //3. calculate the alpha blending of the 2nd and 4th channel
+ //- mask the color vector
+ //- multiply it by the masked alpha vector
+ //- add the correction to compensate bit shifting used instead of dividing by 255
+ //- shift bits - corresponding to division by 256
+ auto even = _mm_and_si128(c, RB);
+ even = _mm_mullo_epi16(even, aRB);
+ even =_mm_add_epi16(even, RB);
+ even = _mm_srli_epi16(even, 8);
+
+ //4. calculate the alpha blending of the 1st and 3rd channel:
+ //- mask the color vector
+ //- multiply it by the corresponding masked alpha vector and store the high bits of the result
+ //- add the correction to compensate division by 256 instead of by 255 (next step)
+ //- remove the low 8 bits to mimic the division by 256
+ auto odd = _mm_and_si128(c, AG);
+ odd = _mm_mulhi_epu16(odd, aAG);
+ odd = _mm_add_epi16(odd, RB);
+ odd = _mm_and_si128(odd, AG);
+
+ //5. the final result
+ return _mm_or_si128(odd, even);
+}
+
+
+static void avxRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+{
+ //1. calculate how many iterations we need to cover the length
+ uint32_t iterations = len / N_32BITS_IN_256REG;
+ uint32_t avxFilled = iterations * N_32BITS_IN_256REG;
+
+ //2. set the beginning of the array
+ dst += offset;
+
+ //3. fill the octets
+ for (uint32_t i = 0; i < iterations; ++i, dst += N_32BITS_IN_256REG) {
+ _mm256_storeu_si256((__m256i*)dst, _mm256_set1_epi32(val));
+ }
+
+ //4. fill leftovers (in the first step we have to set the pointer to the place where the avx job is done)
+ int32_t leftovers = len - avxFilled;
+ while (leftovers--) *dst++ = val;
+}
+
+
+static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+{
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ auto ialpha = 255 - static_cast<uint8_t>(_alpha(color));
+
+ auto avxColor = _mm_set1_epi32(color);
+ auto avxIalpha = _mm_set1_epi8(ialpha);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+
+ //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
+ auto notAligned = ((uintptr_t)dst & 0xf) / 4;
+ if (notAligned) {
+ notAligned = (N_32BITS_IN_128REG - notAligned > w ? w : N_32BITS_IN_128REG - notAligned);
+ for (uint32_t x = 0; x < notAligned; ++x, ++dst) {
+ *dst = color + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+
+ //2. fill the aligned memory - N_32BITS_IN_128REG pixels processed at once
+ uint32_t iterations = (w - notAligned) / N_32BITS_IN_128REG;
+ uint32_t avxFilled = iterations * N_32BITS_IN_128REG;
+ auto avxDst = (__m128i*)dst;
+ for (uint32_t x = 0; x < iterations; ++x, ++avxDst) {
+ *avxDst = _mm_add_epi32(avxColor, ALPHA_BLEND(*avxDst, avxIalpha));
+ }
+
+ //3. fill the remaining pixels
+ int32_t leftovers = w - notAligned - avxFilled;
+ dst += avxFilled;
+ while (leftovers--) {
+ *dst = color + ALPHA_BLEND(*dst, ialpha);
+ dst++;
+ }
+ }
+ return true;
+}
+
+
+static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+{
+ auto span = rle->spans;
+ uint32_t src;
+
+ for (uint32_t i = 0; i < rle->size; ++i) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+
+ if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
+ else src = color;
+
+ auto ialpha = 255 - static_cast<uint8_t>(_alpha(src));
+
+ //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
+ auto notAligned = ((uintptr_t)dst & 0xf) / 4;
+ if (notAligned) {
+ notAligned = (N_32BITS_IN_128REG - notAligned > span->len ? span->len : N_32BITS_IN_128REG - notAligned);
+ for (uint32_t x = 0; x < notAligned; ++x, ++dst) {
+ *dst = src + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+
+ //2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once
+ //In order to avoid unneccessary avx variables declarations a check is made whether there are any iterations at all
+ uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG;
+ uint32_t avxFilled = 0;
+ if (iterations > 0) {
+ auto avxSrc = _mm_set1_epi32(src);
+ auto avxIalpha = _mm_set1_epi8(ialpha);
+
+ avxFilled = iterations * N_32BITS_IN_128REG;
+ auto avxDst = (__m128i*)dst;
+ for (uint32_t x = 0; x < iterations; ++x, ++avxDst) {
+ *avxDst = _mm_add_epi32(avxSrc, ALPHA_BLEND(*avxDst, avxIalpha));
+ }
+ }
+
+ //3. fill the remaining pixels
+ int32_t leftovers = span->len - notAligned - avxFilled;
+ dst += avxFilled;
+ while (leftovers--) {
+ *dst = src + ALPHA_BLEND(*dst, ialpha);
+ dst++;
+ }
+
+ ++span;
+ }
+ return true;
+}
+
+
+#endif
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h
new file mode 100644
index 0000000000..6d60957eb9
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+static void inline cRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+{
+ dst += offset;
+ while (len--) *dst++ = val;
+}
+
+
+static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+{
+ auto span = rle->spans;
+ uint32_t src;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+
+ if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
+ else src = color;
+
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ return true;
+}
+
+
+static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+{
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto ialpha = _ialpha(color);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+ for (uint32_t x = 0; x < w; ++x, ++dst) {
+ *dst = color + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+ return true;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h
new file mode 100644
index 0000000000..c74a6b309c
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifdef THORVG_NEON_VECTOR_SUPPORT
+
+#include <arm_neon.h>
+
+static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a)
+{
+ uint16x8_t t = vmull_u8(c, a);
+ return vshrn_n_u16(t, 8);
+}
+
+
+static void neonRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+{
+ uint32_t iterations = len / 4;
+ uint32_t neonFilled = iterations * 4;
+
+ dst += offset;
+ uint32x4_t vectorVal = {val, val, val, val};
+
+ for (uint32_t i = 0; i < iterations; ++i) {
+ vst1q_u32(dst, vectorVal);
+ dst += 4;
+ }
+
+ int32_t leftovers = len - neonFilled;
+ while (leftovers--) *dst++ = val;
+}
+
+
+static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+{
+ auto span = rle->spans;
+ uint32_t src;
+ uint8x8_t *vDst = nullptr;
+ uint16_t align;
+
+ for (uint32_t i = 0; i < rle->size; ++i) {
+ if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
+ else src = color;
+
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto ialpha = 255 - _alpha(src);
+
+ if ((((uint32_t) dst) & 0x7) != 0) {
+ //fill not aligned byte
+ *dst = src + ALPHA_BLEND(*dst, ialpha);
+ vDst = (uint8x8_t*)(dst + 1);
+ align = 1;
+ } else {
+ vDst = (uint8x8_t*) dst;
+ align = 0;
+ }
+
+ uint8x8_t vSrc = (uint8x8_t) vdup_n_u32(src);
+ uint8x8_t vIalpha = vdup_n_u8((uint8_t) ialpha);
+
+ for (uint32_t x = 0; x < (span->len - align) / 2; ++x)
+ vDst[x] = vadd_u8(vSrc, ALPHA_BLEND(vDst[x], vIalpha));
+
+ auto leftovers = (span->len - align) % 2;
+ if (leftovers > 0) dst[span->len - 1] = src + ALPHA_BLEND(dst[span->len - 1], ialpha);
+
+ ++span;
+ }
+ return true;
+}
+
+
+static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+{
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto ialpha = 255 - _alpha(color);
+
+ auto vColor = vdup_n_u32(color);
+ auto vIalpha = vdup_n_u8((uint8_t) ialpha);
+
+ uint8x8_t* vDst = nullptr;
+ uint32_t align;
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+
+ if ((((uint32_t) dst) & 0x7) != 0) {
+ //fill not aligned byte
+ *dst = color + ALPHA_BLEND(*dst, ialpha);
+ vDst = (uint8x8_t*) (dst + 1);
+ align = 1;
+ } else {
+ vDst = (uint8x8_t*) dst;
+ align = 0;
+ }
+
+ for (uint32_t x = 0; x < (w - align) / 2; ++x)
+ vDst[x] = vadd_u8((uint8x8_t)vColor, ALPHA_BLEND(vDst[x], vIalpha));
+
+ auto leftovers = (w - align) % 2;
+ if (leftovers > 0) dst[w - 1] = color + ALPHA_BLEND(dst[w - 1], ialpha);
+ }
+ return true;
+}
+
+#endif
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
new file mode 100644
index 0000000000..2cf9fb4e93
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
@@ -0,0 +1,602 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+struct Vertex
+{
+ Point pt;
+ Point uv;
+};
+
+struct Polygon
+{
+ Vertex vertex[3];
+};
+
+struct AALine
+{
+ int32_t x[2];
+ int32_t coverage[2];
+ int32_t length[2];
+};
+
+struct AASpans
+{
+ AALine *lines;
+ int32_t yStart;
+ int32_t yEnd;
+};
+
+static inline void _swap(float& a, float& b, float& tmp)
+{
+ tmp = a;
+ a = b;
+ b = tmp;
+}
+
+//Careful! Shared resource, No support threading
+static float dudx, dvdx;
+static float dxdya, dxdyb, dudya, dvdya;
+static float xa, xb, ua, va;
+
+
+//Y Range exception handling
+static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd)
+{
+ int32_t regionTop, regionBottom;
+
+ if (region) {
+ regionTop = region->min.y;
+ regionBottom = region->max.y;
+ } else {
+ regionTop = image->rle->spans->y;
+ regionBottom = image->rle->spans[image->rle->size - 1].y;
+ }
+
+ if (yStart >= regionBottom) return false;
+
+ if (yStart < regionTop) yStart = regionTop;
+ if (yEnd > regionBottom) yEnd = regionBottom;
+
+ return true;
+}
+
+
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
+{
+#define TEXMAP_TRANSLUCENT
+#define TEXMAP_MASKING
+ #include "tvgSwRasterTexmapInternal.h"
+#undef TEXMAP_MASKING
+#undef TEXMAP_TRANSLUCENT
+}
+
+
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
+{
+#define TEXMAP_MASKING
+ #include "tvgSwRasterTexmapInternal.h"
+#undef TEXMAP_MASKING
+}
+
+
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, AASpans* aaSpans)
+{
+#define TEXMAP_TRANSLUCENT
+ #include "tvgSwRasterTexmapInternal.h"
+#undef TEXMAP_TRANSLUCENT
+}
+
+
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans)
+{
+ #include "tvgSwRasterTexmapInternal.h"
+}
+
+
+/* This mapping algorithm is based on Mikael Kalms's. */
+static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
+{
+ float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
+ float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
+ float u[3] = {polygon.vertex[0].uv.x, polygon.vertex[1].uv.x, polygon.vertex[2].uv.x};
+ float v[3] = {polygon.vertex[0].uv.y, polygon.vertex[1].uv.y, polygon.vertex[2].uv.y};
+
+ float off_y;
+ float dxdy[3] = {0.0f, 0.0f, 0.0f};
+ float tmp;
+
+ auto upper = false;
+
+ //Sort the vertices in ascending Y order
+ if (y[0] > y[1]) {
+ _swap(x[0], x[1], tmp);
+ _swap(y[0], y[1], tmp);
+ _swap(u[0], u[1], tmp);
+ _swap(v[0], v[1], tmp);
+ }
+ if (y[0] > y[2]) {
+ _swap(x[0], x[2], tmp);
+ _swap(y[0], y[2], tmp);
+ _swap(u[0], u[2], tmp);
+ _swap(v[0], v[2], tmp);
+ }
+ if (y[1] > y[2]) {
+ _swap(x[1], x[2], tmp);
+ _swap(y[1], y[2], tmp);
+ _swap(u[1], u[2], tmp);
+ _swap(v[1], v[2], tmp);
+ }
+
+ //Y indexes
+ int yi[3] = {(int)y[0], (int)y[1], (int)y[2]};
+
+ //Skip drawing if it's too thin to cover any pixels at all.
+ if ((yi[0] == yi[1] && yi[0] == yi[2]) || ((int) x[0] == (int) x[1] && (int) x[0] == (int) x[2])) return;
+
+ //Calculate horizontal and vertical increments for UV axes (these calcs are certainly not optimal, although they're stable (handles any dy being 0)
+ auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0]));
+
+ //Skip poly if it's an infinitely thin line
+ if (mathZero(denom)) return;
+
+ denom = 1 / denom; //Reciprocal for speeding up
+ dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom;
+ dvdx = ((v[2] - v[0]) * (y[1] - y[0]) - (v[1] - v[0]) * (y[2] - y[0])) * denom;
+ auto dudy = ((u[1] - u[0]) * (x[2] - x[0]) - (u[2] - u[0]) * (x[1] - x[0])) * denom;
+ auto dvdy = ((v[1] - v[0]) * (x[2] - x[0]) - (v[2] - v[0]) * (x[1] - x[0])) * denom;
+
+ //Calculate X-slopes along the edges
+ if (y[1] > y[0]) dxdy[0] = (x[1] - x[0]) / (y[1] - y[0]);
+ if (y[2] > y[0]) dxdy[1] = (x[2] - x[0]) / (y[2] - y[0]);
+ if (y[2] > y[1]) dxdy[2] = (x[2] - x[1]) / (y[2] - y[1]);
+
+ //Determine which side of the polygon the longer edge is on
+ auto side = (dxdy[1] > dxdy[0]) ? true : false;
+
+ if (mathEqual(y[0], y[1])) side = x[0] > x[1];
+ if (mathEqual(y[1], y[2])) side = x[2] > x[1];
+
+ auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
+
+ //Longer edge is on the left side
+ if (!side) {
+ //Calculate slopes along left edge
+ dxdya = dxdy[1];
+ dudya = dxdya * dudx + dudy;
+ dvdya = dxdya * dvdx + dvdy;
+
+ //Perform subpixel pre-stepping along left edge
+ auto dy = 1.0f - (y[0] - yi[0]);
+ xa = x[0] + dy * dxdya;
+ ua = u[0] + dy * dudya;
+ va = v[0] + dy * dvdya;
+
+ //Draw upper segment if possibly visible
+ if (yi[0] < yi[1]) {
+ off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
+ xa += (off_y * dxdya);
+ ua += (off_y * dudya);
+ va += (off_y * dvdya);
+
+ // Set right edge X-slope and perform subpixel pre-stepping
+ dxdyb = dxdy[0];
+ xb = x[0] + dy * dxdyb + (off_y * dxdyb);
+
+ if (blendMethod) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans);
+ } else {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
+ }
+
+ upper = true;
+ }
+ //Draw lower segment if possibly visible
+ if (yi[1] < yi[2]) {
+ off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
+ if (!upper) {
+ xa += (off_y * dxdya);
+ ua += (off_y * dudya);
+ va += (off_y * dvdya);
+ }
+ // Set right edge X-slope and perform subpixel pre-stepping
+ dxdyb = dxdy[2];
+ xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
+ if (blendMethod) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans);
+ } else {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
+ }
+ }
+ //Longer edge is on the right side
+ } else {
+ //Set right edge X-slope and perform subpixel pre-stepping
+ dxdyb = dxdy[1];
+ auto dy = 1.0f - (y[0] - yi[0]);
+ xb = x[0] + dy * dxdyb;
+
+ //Draw upper segment if possibly visible
+ if (yi[0] < yi[1]) {
+ off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
+ xb += (off_y *dxdyb);
+
+ // Set slopes along left edge and perform subpixel pre-stepping
+ dxdya = dxdy[0];
+ dudya = dxdya * dudx + dudy;
+ dvdya = dxdya * dvdx + dvdy;
+
+ xa = x[0] + dy * dxdya + (off_y * dxdya);
+ ua = u[0] + dy * dudya + (off_y * dudya);
+ va = v[0] + dy * dvdya + (off_y * dvdya);
+
+ if (blendMethod) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans);
+ } else {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
+ }
+
+ upper = true;
+ }
+ //Draw lower segment if possibly visible
+ if (yi[1] < yi[2]) {
+ off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
+ if (!upper) xb += (off_y *dxdyb);
+
+ // Set slopes along left edge and perform subpixel pre-stepping
+ dxdya = dxdy[2];
+ dudya = dxdya * dudx + dudy;
+ dvdya = dxdya * dvdx + dvdy;
+ dy = 1 - (y[1] - yi[1]);
+ xa = x[1] + dy * dxdya + (off_y * dxdya);
+ ua = u[1] + dy * dudya + (off_y * dudya);
+ va = v[1] + dy * dvdya + (off_y * dvdya);
+
+ if (blendMethod) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans);
+ } else {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
+ }
+ }
+ }
+}
+
+
+static AASpans* _AASpans(const Vertex* vertices, const SwImage* image, const SwBBox* region)
+{
+ //Initialize Y range
+ float ys = FLT_MAX, ye = -1.0f;
+
+ for (int i = 0; i < 4; i++) {
+ if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
+ if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
+ }
+
+ auto yStart = static_cast<int32_t>(ys);
+ auto yEnd = static_cast<int32_t>(ye);
+
+ if (!_arrange(image, region, yStart, yEnd)) return nullptr;
+
+ auto aaSpans = static_cast<AASpans*>(malloc(sizeof(AASpans)));
+ aaSpans->yStart = yStart;
+ aaSpans->yEnd = yEnd;
+
+ //Initialize X range
+ auto height = yEnd - yStart;
+
+ aaSpans->lines = static_cast<AALine*>(calloc(height, sizeof(AALine)));
+
+ for (int32_t i = 0; i < height; i++) {
+ aaSpans->lines[i].x[0] = INT32_MAX;
+ aaSpans->lines[i].x[1] = INT32_MIN;
+ }
+ return aaSpans;
+}
+
+
+static void _calcIrregularCoverage(AALine* lines, int32_t eidx, int32_t y, int32_t diagonal, int32_t edgeDist, bool reverse)
+{
+ if (eidx == 1) reverse = !reverse;
+ int32_t coverage = (255 / (diagonal + 2));
+ int32_t tmp;
+ for (int32_t ry = 0; ry < (diagonal + 2); ry++) {
+ tmp = y - ry - edgeDist;
+ if (tmp < 0) return;
+ lines[tmp].length[eidx] = 1;
+ if (reverse) lines[tmp].coverage[eidx] = 255 - (coverage * ry);
+ else lines[tmp].coverage[eidx] = (coverage * ry);
+ }
+}
+
+
+static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t rewind, bool reverse)
+{
+ if (eidx == 1) reverse = !reverse;
+ int32_t coverage = (255 / (rewind + 1));
+ int32_t tmp;
+ for (int ry = 1; ry < (rewind + 1); ry++) {
+ tmp = y - ry;
+ if (tmp < 0) return;
+ lines[tmp].length[eidx] = 1;
+ if (reverse) lines[tmp].coverage[eidx] = (255 - (coverage * ry));
+ else lines[tmp].coverage[eidx] = (coverage * ry);
+ }
+}
+
+
+static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
+{
+ if (lines[y].length[eidx] < abs(x - x2)) {
+ lines[y].length[eidx] = abs(x - x2);
+ lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
+ }
+}
+
+
+/*
+ * This Anti-Aliasing mechanism is originated from Hermet Park's idea.
+ * To understand this AA logic, you can refer this page:
+ * www.hermet.pe.kr/122 (hermetpark@gmail.com)
+*/
+static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
+{
+//Previous edge direction:
+#define DirOutHor 0x0011
+#define DirOutVer 0x0001
+#define DirInHor 0x0010
+#define DirInVer 0x0000
+#define DirNone 0x1000
+
+#define PUSH_VERTEX() \
+ do { \
+ pEdge.x = lines[y].x[eidx]; \
+ pEdge.y = y; \
+ ptx[0] = tx[0]; \
+ ptx[1] = tx[1]; \
+ } while (0)
+
+ int32_t y = 0;
+ SwPoint pEdge = {-1, -1}; //previous edge point
+ SwPoint edgeDiff = {0, 0}; //temporary used for point distance
+
+ /* store bigger to tx[0] between prev and current edge's x positions. */
+ int32_t tx[2] = {0, 0};
+ /* back up prev tx values */
+ int32_t ptx[2] = {0, 0};
+ int32_t diagonal = 0; //straight diagonal pixels count
+
+ auto yStart = aaSpans->yStart;
+ auto yEnd = aaSpans->yEnd;
+ auto lines = aaSpans->lines;
+
+ int32_t prevDir = DirNone;
+ int32_t curDir = DirNone;
+
+ yEnd -= yStart;
+
+ //Start Edge
+ if (y < yEnd) {
+ pEdge.x = lines[y].x[eidx];
+ pEdge.y = y;
+ }
+
+ //Calculates AA Edges
+ for (y++; y < yEnd; y++) {
+ //Ready tx
+ if (eidx == 0) {
+ tx[0] = pEdge.x;
+ tx[1] = lines[y].x[0];
+ } else {
+ tx[0] = lines[y].x[1];
+ tx[1] = pEdge.x;
+ }
+ edgeDiff.x = (tx[0] - tx[1]);
+ edgeDiff.y = (y - pEdge.y);
+
+ //Confirm current edge direction
+ if (edgeDiff.x > 0) {
+ if (edgeDiff.y == 1) curDir = DirOutHor;
+ else curDir = DirOutVer;
+ } else if (edgeDiff.x < 0) {
+ if (edgeDiff.y == 1) curDir = DirInHor;
+ else curDir = DirInVer;
+ } else curDir = DirNone;
+
+ //straight diagonal increase
+ if ((curDir == prevDir) && (y < yEnd)) {
+ if ((abs(edgeDiff.x) == 1) && (edgeDiff.y == 1)) {
+ ++diagonal;
+ PUSH_VERTEX();
+ continue;
+ }
+ }
+
+ switch (curDir) {
+ case DirOutHor: {
+ _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
+ if (diagonal > 0) {
+ _calcIrregularCoverage(lines, eidx, y, diagonal, 0, true);
+ diagonal = 0;
+ }
+ /* Increment direction is changed: Outside Vertical -> Outside Horizontal */
+ if (prevDir == DirOutVer) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
+
+ //Trick, but fine-tunning!
+ if (y == 1) _calcHorizCoverage(lines, eidx, pEdge.y, tx[0], tx[1]);
+ PUSH_VERTEX();
+ }
+ break;
+ case DirOutVer: {
+ _calcVertCoverage(lines, eidx, y, edgeDiff.y, true);
+ if (diagonal > 0) {
+ _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, false);
+ diagonal = 0;
+ }
+ /* Increment direction is changed: Outside Horizontal -> Outside Vertical */
+ if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
+ PUSH_VERTEX();
+ }
+ break;
+ case DirInHor: {
+ _calcHorizCoverage(lines, eidx, (y - 1), tx[0], tx[1]);
+ if (diagonal > 0) {
+ _calcIrregularCoverage(lines, eidx, y, diagonal, 0, false);
+ diagonal = 0;
+ }
+ /* Increment direction is changed: Outside Horizontal -> Inside Horizontal */
+ if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
+ PUSH_VERTEX();
+ }
+ break;
+ case DirInVer: {
+ _calcVertCoverage(lines, eidx, y, edgeDiff.y, false);
+ if (prevDir == DirOutHor) edgeDiff.y -= 1; //Weird, fine tuning?????????????????????
+ if (diagonal > 0) {
+ _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, true);
+ diagonal = 0;
+ }
+ /* Increment direction is changed: Outside Horizontal -> Inside Vertical */
+ if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
+ PUSH_VERTEX();
+ }
+ break;
+ }
+ if (curDir != DirNone) prevDir = curDir;
+ }
+
+ //leftovers...?
+ if ((edgeDiff.y == 1) && (edgeDiff.x != 0)) {
+ if (y >= yEnd) y = (yEnd - 1);
+ _calcHorizCoverage(lines, eidx, y - 1, ptx[0], ptx[1]);
+ _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
+ } else {
+ ++y;
+ if (y > yEnd) y = yEnd;
+ _calcVertCoverage(lines, eidx, y, (edgeDiff.y + 1), (prevDir & 0x00000001));
+ }
+}
+
+
+static bool _apply(SwSurface* surface, AASpans* aaSpans)
+{
+ auto y = aaSpans->yStart;
+ uint32_t pixel;
+ uint32_t* dst;
+ int32_t pos;
+
+ //left side
+ _calcAAEdge(aaSpans, 0);
+ //right side
+ _calcAAEdge(aaSpans, 1);
+
+ while (y < aaSpans->yEnd) {
+ auto line = &aaSpans->lines[y - aaSpans->yStart];
+ auto width = line->x[1] - line->x[0];
+ if (width > 0) {
+ auto offset = y * surface->stride;
+
+ //Left edge
+ dst = surface->buffer + (offset + line->x[0]);
+ if (line->x[0] > 1) pixel = *(dst - 1);
+ else pixel = *dst;
+
+ pos = 1;
+ while (pos <= line->length[0]) {
+ *dst = INTERPOLATE((line->coverage[0] * pos), *dst, pixel);
+ ++dst;
+ ++pos;
+ }
+
+ //Right edge
+ dst = surface->buffer + (offset + line->x[1] - 1);
+ if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
+ else pixel = *dst;
+
+ pos = width;
+ while ((int32_t)(width - line->length[1]) < pos) {
+ *dst = INTERPOLATE(255 - (line->coverage[1] * (line->length[1] - (width - pos))), *dst, pixel);
+ --dst;
+ --pos;
+ }
+ }
+ y++;
+ }
+
+ free(aaSpans->lines);
+ free(aaSpans);
+
+ return true;
+}
+
+
+/*
+ 2 triangles constructs 1 mesh.
+ below figure illustrates vert[4] index info.
+ If you need better quality, please divide a mesh by more number of triangles.
+
+ 0 -- 1
+ | / |
+ | / |
+ 3 -- 2
+*/
+static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+{
+ //Exceptions: No dedicated drawing area?
+ if (!region && image->rle->size == 0) return false;
+
+ /* Prepare vertices.
+ shift XY coordinates to match the sub-pixeling technique. */
+ Vertex vertices[4];
+ vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
+ vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}};
+ vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}};
+ vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}};
+
+ for (int i = 0; i < 4; i++) mathMultiply(&vertices[i].pt, transform);
+
+ auto aaSpans = _AASpans(vertices, image, region);
+ if (!aaSpans) return true;
+
+ Polygon polygon;
+
+ //Draw the first polygon
+ polygon.vertex[0] = vertices[0];
+ polygon.vertex[1] = vertices[1];
+ polygon.vertex[2] = vertices[3];
+
+ _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
+
+ //Draw the second polygon
+ polygon.vertex[0] = vertices[1];
+ polygon.vertex[1] = vertices[2];
+ polygon.vertex[2] = vertices[3];
+
+ _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
+
+ return _apply(surface, aaSpans);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
new file mode 100644
index 0000000000..e96307c874
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+{
+ float _dudx = dudx, _dvdx = dvdx;
+ float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
+ float _xa = xa, _xb = xb, _ua = ua, _va = va;
+ auto sbuf = image->data;
+ auto dbuf = surface->buffer;
+ int32_t sw = static_cast<int32_t>(image->stride);
+ int32_t sh = image->h;
+ int32_t dw = surface->stride;
+ int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
+ int32_t vv = 0, uu = 0;
+ int32_t minx, maxx;
+ float dx, u, v, iptr;
+ uint32_t* buf;
+ SwSpan* span = nullptr; //used only when rle based.
+
+#ifdef TEXMAP_MASKING
+ uint32_t* cmp;
+#endif
+
+ if (!_arrange(image, region, yStart, yEnd)) return;
+
+ //Loop through all lines in the segment
+ uint32_t spanIdx = 0;
+
+ if (region) {
+ minx = region->min.x;
+ maxx = region->max.x;
+ } else {
+ span = image->rle->spans;
+ while (span->y < yStart) {
+ ++span;
+ ++spanIdx;
+ }
+ }
+
+ y = yStart;
+
+ while (y < yEnd) {
+ x1 = (int32_t)_xa;
+ x2 = (int32_t)_xb;
+
+ if (!region) {
+ minx = INT32_MAX;
+ maxx = INT32_MIN;
+ //one single row, could be consisted of multiple spans.
+ while (span->y == y && spanIdx < image->rle->size) {
+ if (minx > span->x) minx = span->x;
+ if (maxx < span->x + span->len) maxx = span->x + span->len;
+ ++span;
+ ++spanIdx;
+ }
+ }
+ if (x1 < minx) x1 = minx;
+ if (x2 > maxx) x2 = maxx;
+
+ //Anti-Aliasing frames
+ ay = y - aaSpans->yStart;
+ if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
+ if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
+
+ //Range exception
+ if ((x2 - x1) < 1 || (x1 >= maxx) || (x2 <= minx)) goto next;
+
+ //Perform subtexel pre-stepping on UV
+ dx = 1 - (_xa - x1);
+ u = _ua + dx * _dudx;
+ v = _va + dx * _dvdx;
+
+ buf = dbuf + ((y * dw) + x1);
+
+ x = x1;
+
+#ifdef TEXMAP_MASKING
+ cmp = &surface->compositor->image.data[y * surface->compositor->image.stride + x1];
+#endif
+ //Draw horizontal line
+ while (x++ < x2) {
+ uu = (int) u;
+ vv = (int) v;
+
+ ar = (int)(255 * (1 - modff(u, &iptr)));
+ ab = (int)(255 * (1 - modff(v, &iptr)));
+ iru = uu + 1;
+ irv = vv + 1;
+ px = *(sbuf + (vv * sw) + uu);
+
+ /* horizontal interpolate */
+ if (iru < sw) {
+ /* right pixel */
+ int px2 = *(sbuf + (vv * sw) + iru);
+ px = INTERPOLATE(ar, px, px2);
+ }
+ /* vertical interpolate */
+ if (irv < sh) {
+ /* bottom pixel */
+ int px2 = *(sbuf + (irv * sw) + uu);
+
+ /* horizontal interpolate */
+ if (iru < sw) {
+ /* bottom right pixel */
+ int px3 = *(sbuf + (irv * sw) + iru);
+ px2 = INTERPOLATE(ar, px2, px3);
+ }
+ px = INTERPOLATE(ab, px, px2);
+ }
+#if defined(TEXMAP_MASKING) && defined(TEXMAP_TRANSLUCENT)
+ auto src = ALPHA_BLEND(px, _multiplyAlpha(opacity, blendMethod(*cmp)));
+#elif defined(TEXMAP_MASKING)
+ auto src = ALPHA_BLEND(px, blendMethod(*cmp));
+#elif defined(TEXMAP_TRANSLUCENT)
+ auto src = ALPHA_BLEND(px, opacity);
+#else
+ auto src = px;
+#endif
+ *buf = src + ALPHA_BLEND(*buf, _ialpha(src));
+ ++buf;
+#ifdef TEXMAP_MASKING
+ ++cmp;
+#endif
+ //Step UV horizontally
+ u += _dudx;
+ v += _dvdx;
+ //range over?
+ if ((uint32_t)v >= image->h) break;
+ }
+next:
+ //Step along both edges
+ _xa += _dxdya;
+ _xb += _dxdyb;
+ _ua += _dudya;
+ _va += _dvdya;
+
+ if (!region && spanIdx >= image->rle->size) break;
+
+ ++y;
+ }
+ xa = _xa;
+ xb = _xb;
+ ua = _ua;
+ va = _va;
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
new file mode 100644
index 0000000000..c75e73760e
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <math.h>
+#include "tvgSwCommon.h"
+#include "tvgTaskScheduler.h"
+#include "tvgSwRenderer.h"
+#include "tvgMath.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+static int32_t initEngineCnt = false;
+static int32_t rendererCnt = 0;
+static SwMpool* globalMpool = nullptr;
+static uint32_t threadsCnt = 0;
+
+struct SwTask : Task
+{
+ Matrix* transform = nullptr;
+ SwSurface* surface = nullptr;
+ SwMpool* mpool = nullptr;
+ RenderUpdateFlag flags = RenderUpdateFlag::None;
+ Array<RenderData> clips;
+ uint32_t opacity;
+ SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region
+ bool pushed = false; //Pushed into task list?
+ bool disposed = false; //Disposed task?
+
+ RenderRegion bounds() const
+ {
+ RenderRegion region;
+
+ //Range over?
+ region.x = bbox.min.x > 0 ? bbox.min.x : 0;
+ region.y = bbox.min.y > 0 ? bbox.min.y : 0;
+ region.w = bbox.max.x - region.x;
+ region.h = bbox.max.y - region.y;
+ if (region.w < 0) region.w = 0;
+ if (region.h < 0) region.h = 0;
+
+ return region;
+ }
+
+ virtual bool dispose() = 0;
+
+ virtual ~SwTask()
+ {
+ free(transform);
+ }
+};
+
+
+struct SwShapeTask : SwTask
+{
+ SwShape shape;
+ const Shape* sdata = nullptr;
+ bool cmpStroking = false;
+
+ void run(unsigned tid) override
+ {
+ if (opacity == 0) return; //Invisible
+
+ uint8_t strokeAlpha = 0;
+ auto visibleStroke = false;
+ bool visibleFill = false;
+ auto clipRegion = bbox;
+
+ if (HALF_STROKE(sdata->strokeWidth()) > 0) {
+ sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
+ visibleStroke = sdata->strokeFill() || (static_cast<uint32_t>(strokeAlpha * opacity / 255) > 0);
+ }
+
+ //This checks also for the case, if the invisible shape turned to visible by alpha.
+ auto prepareShape = false;
+ if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
+
+ //Shape
+ if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
+ uint8_t alpha = 0;
+ sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
+ alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
+ visibleFill = (alpha > 0 || sdata->fill());
+ if (visibleFill || visibleStroke) {
+ shapeReset(&shape);
+ if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
+ }
+ }
+
+ //Decide Stroking Composition
+ if (visibleStroke && visibleFill && opacity < 255) cmpStroking = true;
+ else cmpStroking = false;
+
+ //Fill
+ if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
+ if (visibleFill) {
+ /* We assume that if stroke width is bigger than 2,
+ shape outline below stroke could be full covered by stroke drawing.
+ Thus it turns off antialising in that condition.
+ Also, it shouldn't be dash style. */
+ auto antiAlias = (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) ? false : true;
+
+ if (!shapeGenRle(&shape, sdata, antiAlias)) goto err;
+ }
+ if (auto fill = sdata->fill()) {
+ auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
+ if (ctable) shapeResetFill(&shape);
+ if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
+ } else {
+ shapeDelFill(&shape);
+ }
+ }
+
+ //Stroke
+ if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
+ if (visibleStroke) {
+ shapeResetStroke(&shape, sdata, transform);
+ if (!shapeGenStrokeRle(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err;
+
+ if (auto fill = sdata->strokeFill()) {
+ auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
+ if (ctable) shapeResetStrokeFill(&shape);
+ if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
+ } else {
+ shapeDelStrokeFill(&shape);
+ }
+ } else {
+ shapeDelStroke(&shape);
+ }
+ }
+
+ //Clip Path
+ for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
+ auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
+ //Clip shape rle
+ if (shape.rle) {
+ if (clipper->fastTrack) rleClipRect(shape.rle, &clipper->bbox);
+ else if (clipper->rle) rleClipPath(shape.rle, clipper->rle);
+ else goto err;
+ }
+ //Clip stroke rle
+ if (shape.strokeRle) {
+ if (clipper->fastTrack) rleClipRect(shape.strokeRle, &clipper->bbox);
+ else if (clipper->rle) rleClipPath(shape.strokeRle, clipper->rle);
+ else goto err;
+ }
+ }
+ goto end;
+
+ err:
+ shapeReset(&shape);
+ end:
+ shapeDelOutline(&shape, mpool, tid);
+ }
+
+ bool dispose() override
+ {
+ shapeFree(&shape);
+ return true;
+ }
+};
+
+
+struct SwImageTask : SwTask
+{
+ SwImage image;
+
+ void run(unsigned tid) override
+ {
+ auto clipRegion = bbox;
+
+ //Invisible shape turned to visible by alpha.
+ if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) {
+ imageReset(&image);
+ if (!image.data || image.w == 0 || image.h == 0) goto end;
+
+ if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
+
+ if (clips.count > 0) {
+ if (!imageGenRle(&image, bbox, false)) goto end;
+ if (image.rle) {
+ for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
+ auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
+ if (clipper->fastTrack) rleClipRect(image.rle, &clipper->bbox);
+ else if (clipper->rle) rleClipPath(image.rle, clipper->rle);
+ else goto err;
+ }
+ }
+ }
+ }
+ goto end;
+
+ err:
+ rleReset(image.rle);
+ end:
+ imageDelOutline(&image, mpool, tid);
+ }
+
+ bool dispose() override
+ {
+ imageFree(&image);
+ return true;
+ }
+};
+
+
+static void _termEngine()
+{
+ if (rendererCnt > 0) return;
+
+ mpoolTerm(globalMpool);
+ globalMpool = nullptr;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+SwRenderer::~SwRenderer()
+{
+ clearCompositors();
+
+ if (surface) delete(surface);
+
+ if (!sharedMpool) mpoolTerm(mpool);
+
+ --rendererCnt;
+
+ if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
+}
+
+
+bool SwRenderer::clear()
+{
+ for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) {
+ if ((*task)->disposed) {
+ delete(*task);
+ } else {
+ (*task)->done();
+ (*task)->pushed = false;
+ }
+ }
+ tasks.clear();
+
+ if (!sharedMpool) mpoolClear(mpool);
+
+ if (surface) {
+ vport.x = vport.y = 0;
+ vport.w = surface->w;
+ vport.h = surface->h;
+ }
+
+ return true;
+}
+
+
+bool SwRenderer::sync()
+{
+ return true;
+}
+
+
+RenderRegion SwRenderer::viewport()
+{
+ return vport;
+}
+
+
+bool SwRenderer::viewport(const RenderRegion& vp)
+{
+ vport = vp;
+ return true;
+}
+
+
+bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs)
+{
+ if (!buffer || stride == 0 || w == 0 || h == 0 || w > stride) return false;
+
+ if (!surface) surface = new SwSurface;
+
+ surface->buffer = buffer;
+ surface->stride = stride;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = cs;
+
+ vport.x = vport.y = 0;
+ vport.w = surface->w;
+ vport.h = surface->h;
+
+ return rasterCompositor(surface);
+}
+
+
+bool SwRenderer::preRender()
+{
+ return rasterClear(surface);
+}
+
+void SwRenderer::clearCompositors()
+{
+ //Free Composite Caches
+ for (auto comp = compositors.data; comp < (compositors.data + compositors.count); ++comp) {
+ free((*comp)->compositor->image.data);
+ delete((*comp)->compositor);
+ delete(*comp);
+ }
+ compositors.reset();
+}
+
+
+bool SwRenderer::postRender()
+{
+ //Unmultiply alpha if needed
+ if (surface->cs == SwCanvas::ABGR8888_STRAIGHT || surface->cs == SwCanvas::ARGB8888_STRAIGHT) {
+ rasterUnpremultiply(surface);
+ }
+
+ for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) {
+ (*task)->pushed = false;
+ }
+ tasks.clear();
+
+ clearCompositors();
+ return true;
+}
+
+
+bool SwRenderer::renderImage(RenderData data)
+{
+ auto task = static_cast<SwImageTask*>(data);
+ task->done();
+
+ if (task->opacity == 0) return true;
+
+ return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
+}
+
+
+bool SwRenderer::renderShape(RenderData data)
+{
+ auto task = static_cast<SwShapeTask*>(data);
+ if (!task) return false;
+
+ task->done();
+
+ if (task->opacity == 0) return true;
+
+ uint32_t opacity;
+ Compositor* cmp = nullptr;
+
+ //Do Stroking Composition
+ if (task->cmpStroking) {
+ opacity = 255;
+ cmp = target(task->bounds());
+ beginComposite(cmp, CompositeMethod::None, task->opacity);
+ //No Stroking Composition
+ } else {
+ opacity = task->opacity;
+ }
+
+ //Main raster stage
+ uint8_t r, g, b, a;
+
+ if (auto fill = task->sdata->fill()) {
+ rasterGradientShape(surface, &task->shape, fill->identifier());
+ } else {
+ task->sdata->fillColor(&r, &g, &b, &a);
+ a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
+ if (a > 0) rasterShape(surface, &task->shape, r, g, b, a);
+ }
+
+ if (auto strokeFill = task->sdata->strokeFill()) {
+ rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
+ } else {
+ if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) {
+ a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
+ if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
+ }
+ }
+
+ if (task->cmpStroking) endComposite(cmp);
+
+ return true;
+}
+
+
+RenderRegion SwRenderer::region(RenderData data)
+{
+ return static_cast<SwTask*>(data)->bounds();
+}
+
+
+bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity)
+{
+ if (!cmp) return false;
+ auto p = static_cast<SwCompositor*>(cmp);
+
+ p->method = method;
+ p->opacity = opacity;
+
+ //Current Context?
+ if (p->method != CompositeMethod::None) {
+ surface = p->recoverSfc;
+ surface->compositor = p;
+ }
+
+ return true;
+}
+
+
+bool SwRenderer::mempool(bool shared)
+{
+ if (shared == sharedMpool) return true;
+
+ if (shared) {
+ if (!sharedMpool) {
+ if (!mpoolTerm(mpool)) return false;
+ mpool = globalMpool;
+ }
+ } else {
+ if (sharedMpool) mpool = mpoolInit(threadsCnt);
+ }
+
+ sharedMpool = shared;
+
+ if (mpool) return true;
+ return false;
+}
+
+
+Compositor* SwRenderer::target(const RenderRegion& region)
+{
+ auto x = region.x;
+ auto y = region.y;
+ auto w = region.w;
+ auto h = region.h;
+ auto sw = static_cast<int32_t>(surface->w);
+ auto sh = static_cast<int32_t>(surface->h);
+
+ //Out of boundary
+ if (x > sw || y > sh) return nullptr;
+
+ SwSurface* cmp = nullptr;
+
+ //Use cached data
+ for (auto p = compositors.data; p < (compositors.data + compositors.count); ++p) {
+ if ((*p)->compositor->valid) {
+ cmp = *p;
+ break;
+ }
+ }
+
+ //New Composition
+ if (!cmp) {
+ cmp = new SwSurface;
+ if (!cmp) goto err;
+
+ //Inherits attributes from main surface
+ *cmp = *surface;
+
+ cmp->compositor = new SwCompositor;
+ if (!cmp->compositor) goto err;
+
+ //SwImage, Optimize Me: Surface size from MainSurface(WxH) to Parameter W x H
+ cmp->compositor->image.data = (uint32_t*) malloc(sizeof(uint32_t) * surface->stride * surface->h);
+ if (!cmp->compositor->image.data) goto err;
+ compositors.push(cmp);
+ }
+
+ //Boundary Check
+ if (x + w > sw) w = (sw - x);
+ if (y + h > sh) h = (sh - y);
+
+ TVGLOG("SW_ENGINE", "Using intermediate composition [Region: %d %d %d %d]", x, y, w, h);
+
+ cmp->compositor->recoverSfc = surface;
+ cmp->compositor->recoverCmp = surface->compositor;
+ cmp->compositor->valid = false;
+ cmp->compositor->bbox.min.x = x;
+ cmp->compositor->bbox.min.y = y;
+ cmp->compositor->bbox.max.x = x + w;
+ cmp->compositor->bbox.max.y = y + h;
+ cmp->compositor->image.stride = surface->stride;
+ cmp->compositor->image.w = surface->w;
+ cmp->compositor->image.h = surface->h;
+ cmp->compositor->image.direct = true;
+
+ //We know partial clear region
+ cmp->buffer = cmp->compositor->image.data + (cmp->stride * y + x);
+ cmp->w = w;
+ cmp->h = h;
+
+ rasterClear(cmp);
+
+ //Recover context
+ cmp->buffer = cmp->compositor->image.data;
+ cmp->w = cmp->compositor->image.w;
+ cmp->h = cmp->compositor->image.h;
+
+ //Switch render target
+ surface = cmp;
+
+ return cmp->compositor;
+
+err:
+ if (cmp) {
+ if (cmp->compositor) delete(cmp->compositor);
+ delete(cmp);
+ }
+
+ return nullptr;
+}
+
+
+bool SwRenderer::endComposite(Compositor* cmp)
+{
+ if (!cmp) return false;
+
+ auto p = static_cast<SwCompositor*>(cmp);
+ p->valid = true;
+
+ //Recover Context
+ surface = p->recoverSfc;
+ surface->compositor = p->recoverCmp;
+
+ //Default is alpha blending
+ if (p->method == CompositeMethod::None) {
+ return rasterImage(surface, &p->image, nullptr, p->bbox, p->opacity);
+ }
+
+ return true;
+}
+
+
+bool SwRenderer::dispose(RenderData data)
+{
+ auto task = static_cast<SwTask*>(data);
+ if (!task) return true;
+ task->done();
+ task->dispose();
+
+ if (task->pushed) task->disposed = true;
+ else delete(task);
+
+ return true;
+}
+
+
+void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags)
+{
+ if (!surface) return task;
+ if (flags == RenderUpdateFlag::None) return task;
+
+ //Finish previous task if it has duplicated request.
+ task->done();
+
+ if (clips.count > 0) {
+ //Guarantee composition targets get ready.
+ for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
+ static_cast<SwShapeTask*>(*clip)->done();
+ }
+ task->clips = clips;
+ }
+
+ if (transform) {
+ if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
+ *task->transform = transform->m;
+ } else {
+ if (task->transform) free(task->transform);
+ task->transform = nullptr;
+ }
+
+ task->opacity = opacity;
+ task->surface = surface;
+ task->mpool = mpool;
+ task->flags = flags;
+ task->bbox.min.x = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
+ task->bbox.min.y = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
+ task->bbox.max.x = mathMin(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
+ task->bbox.max.y = mathMin(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
+
+ if (!task->pushed) {
+ task->pushed = true;
+ tasks.push(task);
+ }
+
+ TaskScheduler::request(task);
+
+ return task;
+}
+
+
+RenderData SwRenderer::prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
+{
+ //prepare task
+ auto task = static_cast<SwImageTask*>(data);
+ if (!task) {
+ task = new SwImageTask;
+ if (flags & RenderUpdateFlag::Image) {
+ task->image.data = image->buffer;
+ task->image.w = image->w;
+ task->image.h = image->h;
+ task->image.stride = image->stride;
+ }
+ }
+ return prepareCommon(task, transform, opacity, clips, flags);
+}
+
+
+RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
+{
+ //prepare task
+ auto task = static_cast<SwShapeTask*>(data);
+ if (!task) {
+ task = new SwShapeTask;
+ task->sdata = &sdata;
+ }
+ return prepareCommon(task, transform, opacity, clips, flags);
+}
+
+
+SwRenderer::SwRenderer():mpool(globalMpool)
+{
+}
+
+
+bool SwRenderer::init(uint32_t threads)
+{
+ if ((initEngineCnt++) > 0) return true;
+
+ threadsCnt = threads;
+
+ //Share the memory pool among the renderer
+ globalMpool = mpoolInit(threads);
+ if (!globalMpool) {
+ --initEngineCnt;
+ return false;
+ }
+
+ return true;
+}
+
+
+int32_t SwRenderer::init()
+{
+ return initEngineCnt;
+}
+
+
+bool SwRenderer::term()
+{
+ if ((--initEngineCnt) > 0) return true;
+
+ initEngineCnt = 0;
+
+ _termEngine();
+
+ return true;
+}
+
+SwRenderer* SwRenderer::gen()
+{
+ ++rendererCnt;
+ return new SwRenderer();
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
new file mode 100644
index 0000000000..3f883ac409
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SW_RENDERER_H_
+#define _TVG_SW_RENDERER_H_
+
+#include "tvgRender.h"
+
+struct SwSurface;
+struct SwTask;
+struct SwCompositor;
+struct SwMpool;
+
+namespace tvg
+{
+
+class SwRenderer : public RenderMethod
+{
+public:
+ RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
+ RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
+ bool preRender() override;
+ bool renderShape(RenderData data) override;
+ bool renderImage(RenderData data) override;
+ bool postRender() override;
+ bool dispose(RenderData data) override;
+ RenderRegion region(RenderData data) override;
+ RenderRegion viewport() override;
+ bool viewport(const RenderRegion& vp) override;
+
+ bool clear() override;
+ bool sync() override;
+ bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
+ bool mempool(bool shared);
+
+ Compositor* target(const RenderRegion& region) override;
+ bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) override;
+ bool endComposite(Compositor* cmp) override;
+ void clearCompositors();
+
+ static SwRenderer* gen();
+ static bool init(uint32_t threads);
+ static int32_t init();
+ static bool term();
+
+private:
+ SwSurface* surface = nullptr; //active surface
+ Array<SwTask*> tasks; //async task list
+ Array<SwSurface*> compositors; //render targets cache list
+ SwMpool* mpool; //private memory pool
+ RenderRegion vport; //viewport
+
+ bool sharedMpool = true; //memory-pool behavior policy
+
+ SwRenderer();
+ ~SwRenderer();
+
+ RenderData prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags);
+};
+
+}
+
+#endif /* _TVG_SW_RENDERER_H_ */
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
new file mode 100644
index 0000000000..b41e48b7ca
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
@@ -0,0 +1,1043 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * The FreeType Project LICENSE
+ * ----------------------------
+
+ * 2006-Jan-27
+
+ * Copyright 1996-2002, 2006 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg
+
+
+
+ * Introduction
+ * ============
+
+ * The FreeType Project is distributed in several archive packages;
+ * some of them may contain, in addition to the FreeType font engine,
+ * various tools and contributions which rely on, or relate to, the
+ * FreeType Project.
+
+ * This license applies to all files found in such packages, and
+ * which do not fall under their own explicit license. The license
+ * affects thus the FreeType font engine, the test programs,
+ * documentation and makefiles, at the very least.
+
+ * This license was inspired by the BSD, Artistic, and IJG
+ * (Independent JPEG Group) licenses, which all encourage inclusion
+ * and use of free software in commercial and freeware products
+ * alike. As a consequence, its main points are that:
+
+ * o We don't promise that this software works. However, we will be
+ * interested in any kind of bug reports. (`as is' distribution)
+
+ * o You can use this software for whatever you want, in parts or
+ * full form, without having to pay us. (`royalty-free' usage)
+
+ * o You may not pretend that you wrote this software. If you use
+ * it, or only parts of it, in a program, you must acknowledge
+ * somewhere in your documentation that you have used the
+ * FreeType code. (`credits')
+
+ * We specifically permit and encourage the inclusion of this
+ * software, with or without modifications, in commercial products.
+ * We disclaim all warranties covering The FreeType Project and
+ * assume no liability related to The FreeType Project.
+
+
+ * Finally, many people asked us for a preferred form for a
+ * credit/disclaimer to use in compliance with this license. We thus
+ * encourage you to use the following text:
+
+ * """
+ * Portions of this software are copyright � <year> The FreeType
+ * Project (www.freetype.org). All rights reserved.
+ * """
+
+ * Please replace <year> with the value from the FreeType version you
+ * actually use.
+
+* Legal Terms
+* ===========
+
+* 0. Definitions
+* --------------
+
+* Throughout this license, the terms `package', `FreeType Project',
+* and `FreeType archive' refer to the set of files originally
+* distributed by the authors (David Turner, Robert Wilhelm, and
+* Werner Lemberg) as the `FreeType Project', be they named as alpha,
+* beta or final release.
+
+* `You' refers to the licensee, or person using the project, where
+* `using' is a generic term including compiling the project's source
+* code as well as linking it to form a `program' or `executable'.
+* This program is referred to as `a program using the FreeType
+* engine'.
+
+* This license applies to all files distributed in the original
+* FreeType Project, including all source code, binaries and
+* documentation, unless otherwise stated in the file in its
+* original, unmodified form as distributed in the original archive.
+* If you are unsure whether or not a particular file is covered by
+* this license, you must contact us to verify this.
+
+* The FreeType Project is copyright (C) 1996-2000 by David Turner,
+* Robert Wilhelm, and Werner Lemberg. All rights reserved except as
+* specified below.
+
+* 1. No Warranty
+* --------------
+
+* THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
+* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
+* USE, OF THE FREETYPE PROJECT.
+
+* 2. Redistribution
+* -----------------
+
+* This license grants a worldwide, royalty-free, perpetual and
+* irrevocable right and license to use, execute, perform, compile,
+* display, copy, create derivative works of, distribute and
+* sublicense the FreeType Project (in both source and object code
+* forms) and derivative works thereof for any purpose; and to
+* authorize others to exercise some or all of the rights granted
+* herein, subject to the following conditions:
+
+* o Redistribution of source code must retain this license file
+* (`FTL.TXT') unaltered; any additions, deletions or changes to
+* the original files must be clearly indicated in accompanying
+* documentation. The copyright notices of the unaltered,
+* original files must be preserved in all copies of source
+* files.
+
+* o Redistribution in binary form must provide a disclaimer that
+* states that the software is based in part of the work of the
+* FreeType Team, in the distribution documentation. We also
+* encourage you to put an URL to the FreeType web page in your
+* documentation, though this isn't mandatory.
+
+* These conditions apply to any software derived from or based on
+* the FreeType Project, not just the unmodified files. If you use
+* our work, you must acknowledge us. However, no fee need be paid
+* to us.
+
+* 3. Advertising
+* --------------
+
+* Neither the FreeType authors and contributors nor you shall use
+* the name of the other for commercial, advertising, or promotional
+* purposes without specific prior written permission.
+
+* We suggest, but do not require, that you use one or more of the
+* following phrases to refer to this software in your documentation
+* or advertising materials: `FreeType Project', `FreeType Engine',
+* `FreeType library', or `FreeType Distribution'.
+
+* As you have not signed this license, you are not required to
+* accept it. However, as the FreeType Project is copyrighted
+* material, only this license, or another one contracted with the
+* authors, grants you the right to use, distribute, and modify it.
+* Therefore, by using, distributing, or modifying the FreeType
+* Project, you indicate that you understand and accept all the terms
+* of this license.
+
+* 4. Contacts
+* -----------
+
+* There are two mailing lists related to FreeType:
+
+* o freetype@nongnu.org
+
+* Discusses general use and applications of FreeType, as well as
+* future and wanted additions to the library and distribution.
+* If you are looking for support, start in this list if you
+* haven't found anything to help you in the documentation.
+
+* o freetype-devel@nongnu.org
+
+* Discusses bugs, as well as engine internals, design issues,
+* specific licenses, porting, etc.
+
+* Our home page can be found at
+
+* http://www.freetype.org
+*/
+
+#include <setjmp.h>
+#include <limits.h>
+#include <memory.h>
+#include "tvgSwCommon.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+constexpr auto MAX_SPANS = 256;
+constexpr auto PIXEL_BITS = 8; //must be at least 6 bits!
+constexpr auto ONE_PIXEL = (1L << PIXEL_BITS);
+
+using Area = long;
+
+struct Band
+{
+ SwCoord min, max;
+};
+
+struct Cell
+{
+ SwCoord x;
+ SwCoord cover;
+ Area area;
+ Cell *next;
+};
+
+struct RleWorker
+{
+ SwRleData* rle;
+
+ SwPoint cellPos;
+ SwPoint cellMin;
+ SwPoint cellMax;
+ SwCoord cellXCnt;
+ SwCoord cellYCnt;
+
+ Area area;
+ SwCoord cover;
+
+ Cell* cells;
+ ptrdiff_t maxCells;
+ ptrdiff_t cellsCnt;
+
+ SwPoint pos;
+
+ SwPoint bezStack[32 * 3 + 1];
+ int levStack[32];
+
+ SwOutline* outline;
+
+ SwSpan spans[MAX_SPANS];
+ int spansCnt;
+ int ySpan;
+
+ int bandSize;
+ int bandShoot;
+
+ jmp_buf jmpBuf;
+
+ void* buffer;
+ long bufferSize;
+
+ Cell** yCells;
+ SwCoord yCnt;
+
+ bool invalid;
+ bool antiAlias;
+};
+
+
+static inline SwPoint UPSCALE(const SwPoint& pt)
+{
+ return {SwCoord(((unsigned long) pt.x) << (PIXEL_BITS - 6)), SwCoord(((unsigned long) pt.y) << (PIXEL_BITS - 6))};
+}
+
+
+static inline SwPoint TRUNC(const SwPoint& pt)
+{
+ return {pt.x >> PIXEL_BITS, pt.y >> PIXEL_BITS};
+}
+
+
+static inline SwCoord TRUNC(const SwCoord x)
+{
+ return x >> PIXEL_BITS;
+}
+
+
+static inline SwPoint SUBPIXELS(const SwPoint& pt)
+{
+ return {SwCoord(((unsigned long) pt.x) << PIXEL_BITS), SwCoord(((unsigned long) pt.y) << PIXEL_BITS)};
+}
+
+
+static inline SwCoord SUBPIXELS(const SwCoord x)
+{
+ return SwCoord(((unsigned long) x) << PIXEL_BITS);
+}
+
+/*
+ * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min'
+ * algorithm. We use alpha = 1, beta = 3/8, giving us results with a
+ * largest error less than 7% compared to the exact value.
+ */
+static inline SwCoord HYPOT(SwPoint pt)
+{
+ if (pt.x < 0) pt.x = -pt.x;
+ if (pt.y < 0) pt.y = -pt.y;
+ return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3)));
+}
+
+static void _genSpan(SwRleData* rle, const SwSpan* spans, uint32_t count)
+{
+ auto newSize = rle->size + count;
+
+ /* allocate enough memory for new spans */
+ /* alloc is required to prevent free and reallocation */
+ /* when the rle needs to be regenerated because of attribute change. */
+ if (rle->alloc < newSize) {
+ rle->alloc = (newSize * 2);
+ //OPTIMIZE: use mempool!
+ rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
+ }
+
+ //copy the new spans to the allocated memory
+ SwSpan* lastSpan = rle->spans + rle->size;
+ memcpy(lastSpan, spans, count * sizeof(SwSpan));
+
+ rle->size = newSize;
+}
+
+
+static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount)
+{
+ x += rw.cellMin.x;
+ y += rw.cellMin.y;
+
+ //Clip Y range
+ if (y < rw.cellMin.y || y >= rw.cellMax.y) return;
+
+ /* compute the coverage line's coverage, depending on the outline fill rule */
+ /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
+ auto coverage = static_cast<int>(area >> (PIXEL_BITS * 2 + 1 - 8)); //range 0 - 255
+
+ if (coverage < 0) coverage = -coverage;
+
+ if (rw.outline->fillRule == FillRule::EvenOdd) {
+ coverage &= 511;
+ if (coverage > 255) coverage = 511 - coverage;
+ } else {
+ //normal non-zero winding rule
+ if (coverage > 255) coverage = 255;
+ }
+
+ //span has ushort coordinates. check limit overflow
+ if (x >= SHRT_MAX) {
+ TVGERR("SW_ENGINE", "X-coordiante overflow!");
+ x = SHRT_MAX;
+ }
+ if (y >= SHRT_MAX) {
+ TVGERR("SW_ENGINE", "Y Coordiante overflow!");
+ y = SHRT_MAX;
+ }
+
+ if (coverage > 0) {
+ if (!rw.antiAlias) coverage = 255;
+ auto count = rw.spansCnt;
+ auto span = rw.spans + count - 1;
+
+ //see whether we can add this span to the current list
+ if ((count > 0) && (rw.ySpan == y) &&
+ (span->x + span->len == x) && (span->coverage == coverage)) {
+
+ //Clip x range
+ SwCoord xOver = 0;
+ if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
+ if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x);
+
+ //span->len += (acount + xOver) - 1;
+ span->len += (acount + xOver);
+ return;
+ }
+
+ if (count >= MAX_SPANS) {
+ _genSpan(rw.rle, rw.spans, count);
+ rw.spansCnt = 0;
+ rw.ySpan = 0;
+ span = rw.spans;
+ } else {
+ ++span;
+ }
+
+ //Clip x range
+ SwCoord xOver = 0;
+ if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
+ if (x < rw.cellMin.x) {
+ xOver -= (rw.cellMin.x - x);
+ x = rw.cellMin.x;
+ }
+
+ //Nothing to draw
+ if (acount + xOver <= 0) return;
+
+ //add a span to the current list
+ span->x = x;
+ span->y = y;
+ span->len = (acount + xOver);
+ span->coverage = coverage;
+ ++rw.spansCnt;
+ rw.ySpan = y;
+ }
+}
+
+
+static void _sweep(RleWorker& rw)
+{
+ if (rw.cellsCnt == 0) return;
+
+ rw.spansCnt = 0;
+ rw.ySpan = 0;
+
+ for (int y = 0; y < rw.yCnt; ++y) {
+ auto cover = 0;
+ auto x = 0;
+ auto cell = rw.yCells[y];
+
+ while (cell) {
+ if (cell->x > x && cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), cell->x - x);
+ cover += cell->cover;
+ auto area = cover * (ONE_PIXEL * 2) - cell->area;
+ if (area != 0 && cell->x >= 0) _horizLine(rw, cell->x, y, area, 1);
+ x = cell->x + 1;
+ cell = cell->next;
+ }
+
+ if (cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), rw.cellXCnt - x);
+ }
+
+ if (rw.spansCnt > 0) _genSpan(rw.rle, rw.spans, rw.spansCnt);
+}
+
+
+static Cell* _findCell(RleWorker& rw)
+{
+ auto x = rw.cellPos.x;
+ if (x > rw.cellXCnt) x = rw.cellXCnt;
+
+ auto pcell = &rw.yCells[rw.cellPos.y];
+
+ while(true) {
+ Cell* cell = *pcell;
+ if (!cell || cell->x > x) break;
+ if (cell->x == x) return cell;
+ pcell = &cell->next;
+ }
+
+ if (rw.cellsCnt >= rw.maxCells) longjmp(rw.jmpBuf, 1);
+
+ auto cell = rw.cells + rw.cellsCnt++;
+ cell->x = x;
+ cell->area = 0;
+ cell->cover = 0;
+ cell->next = *pcell;
+ *pcell = cell;
+
+ return cell;
+}
+
+
+static void _recordCell(RleWorker& rw)
+{
+ if (rw.area | rw.cover) {
+ auto cell = _findCell(rw);
+ cell->area += rw.area;
+ cell->cover += rw.cover;
+ }
+}
+
+
+static void _setCell(RleWorker& rw, SwPoint pos)
+{
+ /* Move the cell pointer to a new position. We set the `invalid' */
+ /* flag to indicate that the cell isn't part of those we're interested */
+ /* in during the render phase. This means that: */
+ /* */
+ /* . the new vertical position must be within min_ey..max_ey-1. */
+ /* . the new horizontal position must be strictly less than max_ex */
+ /* */
+ /* Note that if a cell is to the left of the clipping region, it is */
+ /* actually set to the (min_ex-1) horizontal position. */
+
+ /* All cells that are on the left of the clipping region go to the
+ min_ex - 1 horizontal position. */
+ pos.x -= rw.cellMin.x;
+ pos.y -= rw.cellMin.y;
+
+ if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
+
+ //Are we moving to a different cell?
+ if (pos != rw.cellPos) {
+ //Record the current one if it is valid
+ if (!rw.invalid) _recordCell(rw);
+ }
+
+ rw.area = 0;
+ rw.cover = 0;
+ rw.cellPos = pos;
+ rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt);
+}
+
+
+static void _startCell(RleWorker& rw, SwPoint pos)
+{
+ if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
+ if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x;
+
+ rw.area = 0;
+ rw.cover = 0;
+ rw.cellPos = pos - rw.cellMin;
+ rw.invalid = false;
+
+ _setCell(rw, pos);
+}
+
+
+static void _moveTo(RleWorker& rw, const SwPoint& to)
+{
+ //record current cell, if any */
+ if (!rw.invalid) _recordCell(rw);
+
+ //start to a new position
+ _startCell(rw, TRUNC(to));
+
+ rw.pos = to;
+}
+
+
+static void _lineTo(RleWorker& rw, const SwPoint& to)
+{
+#define SW_UDIV(a, b) \
+ static_cast<SwCoord>(((unsigned long)(a) * (unsigned long)(b)) >> \
+ (sizeof(long) * CHAR_BIT - PIXEL_BITS))
+
+ auto e1 = TRUNC(rw.pos);
+ auto e2 = TRUNC(to);
+
+ //vertical clipping
+ if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) {
+ rw.pos = to;
+ return;
+ }
+
+ auto diff = to - rw.pos;
+ auto f1 = rw.pos - SUBPIXELS(e1);
+ SwPoint f2;
+
+ //inside one cell
+ if (e1 == e2) {
+ ;
+ //any horizontal line
+ } else if (diff.y == 0) {
+ e1.x = e2.x;
+ _setCell(rw, e1);
+ } else if (diff.x == 0) {
+ //vertical line up
+ if (diff.y > 0) {
+ do {
+ f2.y = ONE_PIXEL;
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * f1.x * 2;
+ f1.y = 0;
+ ++e1.y;
+ _setCell(rw, e1);
+ } while(e1.y != e2.y);
+ //vertical line down
+ } else {
+ do {
+ f2.y = 0;
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * f1.x * 2;
+ f1.y = ONE_PIXEL;
+ --e1.y;
+ _setCell(rw, e1);
+ } while(e1.y != e2.y);
+ }
+ //any other line
+ } else {
+ Area prod = diff.x * f1.y - diff.y * f1.x;
+
+ /* These macros speed up repetitive divisions by replacing them
+ with multiplications and right shifts. */
+ auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
+ auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
+
+ /* The fundamental value `prod' determines which side and the */
+ /* exact coordinate where the line exits current cell. It is */
+ /* also easily updated when moving from one cell to the next. */
+ do {
+ auto px = diff.x * ONE_PIXEL;
+ auto py = diff.y * ONE_PIXEL;
+
+ //left
+ if (prod <= 0 && prod - px > 0) {
+ f2 = {0, SW_UDIV(-prod, -dx_r)};
+ prod -= py;
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+ f1 = {ONE_PIXEL, f2.y};
+ --e1.x;
+ //up
+ } else if (prod - px <= 0 && prod - px + py > 0) {
+ prod -= px;
+ f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+ f1 = {f2.x, 0};
+ ++e1.y;
+ //right
+ } else if (prod - px + py <= 0 && prod + py >= 0) {
+ prod += py;
+ f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+ f1 = {0, f2.y};
+ ++e1.x;
+ //down
+ } else {
+ f2 = {SW_UDIV(prod, -dy_r), 0};
+ prod += px;
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+ f1 = {f2.x, ONE_PIXEL};
+ --e1.y;
+ }
+
+ _setCell(rw, e1);
+
+ } while(e1 != e2);
+ }
+
+ f2 = {to.x - SUBPIXELS(e2.x), to.y - SUBPIXELS(e2.y)};
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+ rw.pos = to;
+}
+
+
+static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
+{
+ auto arc = rw.bezStack;
+ arc[0] = to;
+ arc[1] = ctrl2;
+ arc[2] = ctrl1;
+ arc[3] = rw.pos;
+
+ //Short-cut the arc that crosses the current band
+ auto min = arc[0].y;
+ auto max = arc[0].y;
+
+ SwCoord y;
+ for (auto i = 1; i < 4; ++i) {
+ y = arc[i].y;
+ if (y < min) min = y;
+ if (y > max) max = y;
+ }
+
+ if (TRUNC(min) >= rw.cellMax.y || TRUNC(max) < rw.cellMin.y) goto draw;
+
+ /* Decide whether to split or draw. See `Rapid Termination */
+ /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */
+ /* F. Hain, at */
+ /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */
+ while (true) {
+ {
+ //diff is the P0 - P3 chord vector
+ auto diff = arc[3] - arc[0];
+ auto L = HYPOT(diff);
+
+ //avoid possible arithmetic overflow below by splitting
+ if (L > SHRT_MAX) goto split;
+
+ //max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1)
+ auto sLimit = L * (ONE_PIXEL / 6);
+
+ auto diff1 = arc[1] - arc[0];
+ auto s = diff.y * diff1.x - diff.x * diff1.y;
+ if (s < 0) s = -s;
+ if (s > sLimit) goto split;
+
+ //s is L * the perpendicular distance from P2 to the line P0 - P3
+ auto diff2 = arc[2] - arc[0];
+ s = diff.y * diff2.x - diff.x * diff2.y;
+ if (s < 0) s = -s;
+ if (s > sLimit) goto split;
+
+ /* Split super curvy segments where the off points are so far
+ from the chord that the angles P0-P1-P3 or P0-P2-P3 become
+ acute as detected by appropriate dot products */
+ if (diff1.x * (diff1.x - diff.x) + diff1.y * (diff1.y - diff.y) > 0 ||
+ diff2.x * (diff2.x - diff.x) + diff2.y * (diff2.y - diff.y) > 0)
+ goto split;
+
+ //no reason to split
+ goto draw;
+ }
+ split:
+ mathSplitCubic(arc);
+ arc += 3;
+ continue;
+
+ draw:
+ _lineTo(rw, arc[0]);
+ if (arc == rw.bezStack) return;
+ arc -= 3;
+ }
+}
+
+
+static bool _decomposeOutline(RleWorker& rw)
+{
+ auto outline = rw.outline;
+ auto first = 0; //index of first point in contour
+
+ for (uint32_t n = 0; n < outline->cntrsCnt; ++n) {
+ auto last = outline->cntrs[n];
+ auto limit = outline->pts + last;
+ auto start = UPSCALE(outline->pts[first]);
+ auto pt = outline->pts + first;
+ auto types = outline->types + first;
+
+ /* A contour cannot start with a cubic control point! */
+ if (types[0] == SW_CURVE_TYPE_CUBIC) goto invalid_outline;
+
+ _moveTo(rw, UPSCALE(outline->pts[first]));
+
+ while (pt < limit) {
+ ++pt;
+ ++types;
+
+ //emit a single line_to
+ if (types[0] == SW_CURVE_TYPE_POINT) {
+ _lineTo(rw, UPSCALE(*pt));
+ //types cubic
+ } else {
+ if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC)
+ goto invalid_outline;
+
+ pt += 2;
+ types += 2;
+
+ if (pt <= limit) {
+ _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
+ continue;
+ }
+ _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
+ goto close;
+ }
+ }
+ _lineTo(rw, start);
+ close:
+ first = last + 1;
+ }
+
+ return true;
+
+invalid_outline:
+ TVGERR("SW_ENGINE", "Invalid Outline!");
+ return false;
+}
+
+
+static int _genRle(RleWorker& rw)
+{
+ if (setjmp(rw.jmpBuf) == 0) {
+ auto ret = _decomposeOutline(rw);
+ if (!rw.invalid) _recordCell(rw);
+ if (ret) return 0; //success
+ else return 1; //fail
+ }
+ return -1; //lack of cell memory
+}
+
+
+SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
+{
+ auto out = outSpans;
+ auto spans = targetRle->spans;
+ auto end = targetRle->spans + targetRle->size;
+ auto clipSpans = clip->spans;
+ auto clipEnd = clip->spans + clip->size;
+
+ while (spanCnt > 0 && spans < end) {
+ if (clipSpans == clipEnd) {
+ spans = end;
+ break;
+ }
+ if (clipSpans->y > spans->y) {
+ ++spans;
+ continue;
+ }
+ if (spans->y != clipSpans->y) {
+ ++clipSpans;
+ continue;
+ }
+ auto sx1 = spans->x;
+ auto sx2 = sx1 + spans->len;
+ auto cx1 = clipSpans->x;
+ auto cx2 = cx1 + clipSpans->len;
+
+ if (cx1 < sx1 && cx2 < sx1) {
+ ++clipSpans;
+ continue;
+ }
+ else if (sx1 < cx1 && sx2 < cx1) {
+ ++spans;
+ continue;
+ }
+ auto x = sx1 > cx1 ? sx1 : cx1;
+ auto len = (sx2 < cx2 ? sx2 : cx2) - x;
+ if (len) {
+ auto spansCorverage = spans->coverage;
+ auto clipSpansCoverage = clipSpans->coverage;
+ out->x = sx1 > cx1 ? sx1 : cx1;
+ out->len = (sx2 < cx2 ? sx2 : cx2) - out->x;
+ out->y = spans->y;
+ out->coverage = (uint8_t)(((spansCorverage * clipSpansCoverage) + 0xff) >> 8);
+ ++out;
+ --spanCnt;
+ }
+ if (sx2 < cx2) ++spans;
+ else ++clipSpans;
+ }
+ return out;
+}
+
+
+SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
+{
+ auto out = outSpans;
+ auto spans = targetRle->spans;
+ auto end = targetRle->spans + targetRle->size;
+ auto minx = static_cast<int16_t>(bbox->min.x);
+ auto miny = static_cast<int16_t>(bbox->min.y);
+ auto maxx = minx + static_cast<int16_t>(bbox->max.x - bbox->min.x) - 1;
+ auto maxy = miny + static_cast<int16_t>(bbox->max.y - bbox->min.y) - 1;
+
+ while (spanCnt && spans < end) {
+ if (spans->y > maxy) {
+ spans = end;
+ break;
+ }
+ if (spans->y < miny || spans->x > maxx || spans->x + spans->len <= minx) {
+ ++spans;
+ continue;
+ }
+ if (spans->x < minx) {
+ out->len = (spans->len - (minx - spans->x)) < (maxx - minx + 1) ? (spans->len - (minx - spans->x)) : (maxx - minx + 1);
+ out->x = minx;
+ }
+ else {
+ out->x = spans->x;
+ out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1);
+ }
+ if (out->len != 0) {
+ out->y = spans->y;
+ out->coverage = spans->coverage;
+ ++out;
+ }
+ ++spans;
+ --spanCnt;
+ }
+ return out;
+}
+
+
+void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
+{
+ free(rle->spans);
+ rle->spans = clippedSpans;
+ rle->size = rle->alloc = size;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
+{
+ constexpr auto RENDER_POOL_SIZE = 16384L;
+ constexpr auto BAND_SIZE = 40;
+
+ //TODO: We can preserve several static workers in advance
+ RleWorker rw;
+ Cell buffer[RENDER_POOL_SIZE / sizeof(Cell)];
+
+ //Init Cells
+ rw.buffer = buffer;
+ rw.bufferSize = sizeof(buffer);
+ rw.yCells = reinterpret_cast<Cell**>(buffer);
+ rw.cells = nullptr;
+ rw.maxCells = 0;
+ rw.cellsCnt = 0;
+ rw.area = 0;
+ rw.cover = 0;
+ rw.invalid = true;
+ rw.cellMin = renderRegion.min;
+ rw.cellMax = renderRegion.max;
+ rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
+ rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
+ rw.ySpan = 0;
+ rw.outline = const_cast<SwOutline*>(outline);
+ rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64
+ rw.bandShoot = 0;
+ rw.antiAlias = antiAlias;
+
+ if (!rle) rw.rle = reinterpret_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
+ else rw.rle = rle;
+
+ //Generate RLE
+ Band bands[BAND_SIZE];
+ Band* band;
+
+ /* set up vertical bands */
+ auto bandCnt = static_cast<int>((rw.cellMax.y - rw.cellMin.y) / rw.bandSize);
+ if (bandCnt == 0) bandCnt = 1;
+ else if (bandCnt >= BAND_SIZE) bandCnt = (BAND_SIZE - 1);
+
+ auto min = rw.cellMin.y;
+ auto yMax = rw.cellMax.y;
+ SwCoord max;
+ int ret;
+
+ for (int n = 0; n < bandCnt; ++n, min = max) {
+ max = min + rw.bandSize;
+ if (n == bandCnt -1 || max > yMax) max = yMax;
+
+ bands[0].min = min;
+ bands[0].max = max;
+ band = bands;
+
+ while (band >= bands) {
+ rw.yCells = static_cast<Cell**>(rw.buffer);
+ rw.yCnt = band->max - band->min;
+
+ int cellStart = sizeof(Cell*) * (int)rw.yCnt;
+ int cellMod = cellStart % sizeof(Cell);
+
+ if (cellMod > 0) cellStart += sizeof(Cell) - cellMod;
+
+ auto cellEnd = rw.bufferSize;
+ cellEnd -= cellEnd % sizeof(Cell);
+
+ auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + cellEnd);
+ rw.cells = reinterpret_cast<Cell*>((char*)rw.buffer + cellStart);
+
+ if (rw.cells >= cellsMax) goto reduce_bands;
+
+ rw.maxCells = cellsMax - rw.cells;
+ if (rw.maxCells < 2) goto reduce_bands;
+
+ for (int y = 0; y < rw.yCnt; ++y)
+ rw.yCells[y] = nullptr;
+
+ rw.cellsCnt = 0;
+ rw.invalid = true;
+ rw.cellMin.y = band->min;
+ rw.cellMax.y = band->max;
+ rw.cellYCnt = band->max - band->min;
+
+ ret = _genRle(rw);
+ if (ret == 0) {
+ _sweep(rw);
+ --band;
+ continue;
+ } else if (ret == 1) {
+ goto error;
+ }
+
+ reduce_bands:
+ /* render pool overflow: we will reduce the render band by half */
+ auto bottom = band->min;
+ auto top = band->max;
+ auto middle = bottom + ((top - bottom) >> 1);
+
+ /* This is too complex for a single scanline; there must
+ be some problems */
+ if (middle == bottom) goto error;
+
+ if (bottom - top >= rw.bandSize) ++rw.bandShoot;
+
+ band[1].min = bottom;
+ band[1].max = middle;
+ band[0].min = middle;
+ band[0].max = top;
+ ++band;
+ }
+ }
+
+ if (rw.bandShoot > 8 && rw.bandSize > 16)
+ rw.bandSize = (rw.bandSize >> 1);
+
+ return rw.rle;
+
+error:
+ free(rw.rle);
+ rw.rle = nullptr;
+ return nullptr;
+}
+
+
+void rleReset(SwRleData* rle)
+{
+ if (!rle) return;
+ rle->size = 0;
+}
+
+
+void rleFree(SwRleData* rle)
+{
+ if (!rle) return;
+ if (rle->spans) free(rle->spans);
+ free(rle);
+}
+
+
+void rleClipPath(SwRleData *rle, const SwRleData *clip)
+{
+ if (rle->size == 0 || clip->size == 0) return;
+ auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
+ auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
+ if (!spans) return;
+ auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
+
+ _replaceClipSpan(rle, spans, spansEnd - spans);
+
+ TVGLOG("SW_ENGINE", "Using ClipPath!");
+}
+
+
+void rleClipRect(SwRleData *rle, const SwBBox* clip)
+{
+ if (rle->size == 0) return;
+ auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
+ if (!spans) return;
+ auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
+
+ _replaceClipSpan(rle, spans, spansEnd - spans);
+
+ TVGLOG("SW_ENGINE", "Using ClipRect!");
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
new file mode 100644
index 0000000000..7b49c232b1
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
@@ -0,0 +1,664 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgSwCommon.h"
+#include "tvgBezier.h"
+#include <float.h>
+#include <math.h>
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct Line
+{
+ Point pt1;
+ Point pt2;
+};
+
+
+static float _lineLength(const Point& pt1, const Point& pt2)
+{
+ /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
+ With alpha = 1, beta = 3/8, giving results with the largest error less
+ than 7% compared to the exact value. */
+ Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
+ if (diff.x < 0) diff.x = -diff.x;
+ if (diff.y < 0) diff.y = -diff.y;
+ return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
+}
+
+
+static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right)
+{
+ auto len = _lineLength(cur.pt1, cur.pt2);
+ auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
+ auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
+ left.pt1 = cur.pt1;
+ left.pt2.x = left.pt1.x + dx;
+ left.pt2.y = left.pt1.y + dy;
+ right.pt1 = left.pt2;
+ right.pt2 = cur.pt2;
+}
+
+
+static bool _growOutlineContour(SwOutline& outline, uint32_t n)
+{
+ if (outline.reservedCntrsCnt >= outline.cntrsCnt + n) return false;
+ outline.reservedCntrsCnt = outline.cntrsCnt + n;
+ outline.cntrs = static_cast<uint16_t*>(realloc(outline.cntrs, outline.reservedCntrsCnt * sizeof(uint16_t)));
+ return true;
+}
+
+
+static void _reserveOutlineClose(SwOutline& outline)
+{
+ //Dash outlines are always opened.
+ //Only normal outlines use this information, it sholud be same to their contour counts.
+ if (outline.closed) free(outline.closed);
+ outline.closed = static_cast<bool*>(calloc(outline.reservedCntrsCnt, sizeof(bool)));
+}
+
+
+static void _resetOutlineClose(SwOutline& outline)
+{
+ memset(outline.closed, 0x0, outline.reservedCntrsCnt * sizeof(bool));
+}
+
+
+static void _growOutlinePoint(SwOutline& outline, uint32_t n)
+{
+ if (outline.reservedPtsCnt >= outline.ptsCnt + n) return;
+ outline.reservedPtsCnt = outline.ptsCnt + n;
+ outline.pts = static_cast<SwPoint*>(realloc(outline.pts, outline.reservedPtsCnt * sizeof(SwPoint)));
+ outline.types = static_cast<uint8_t*>(realloc(outline.types, outline.reservedPtsCnt * sizeof(uint8_t)));
+}
+
+
+static void _outlineEnd(SwOutline& outline)
+{
+ if (outline.ptsCnt == 0) return;
+
+ _growOutlineContour(outline, 1);
+ outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
+ ++outline.cntrsCnt;
+}
+
+
+static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform)
+{
+ _growOutlinePoint(outline, 1);
+
+ outline.pts[outline.ptsCnt] = mathTransform(to, transform);
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
+
+ if (outline.ptsCnt > 0) {
+ _growOutlineContour(outline, 1);
+ outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
+ ++outline.cntrsCnt;
+ }
+
+ ++outline.ptsCnt;
+}
+
+
+static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix* transform)
+{
+ _growOutlinePoint(outline, 1);
+
+ outline.pts[outline.ptsCnt] = mathTransform(to, transform);
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
+ ++outline.ptsCnt;
+}
+
+
+static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
+{
+ _growOutlinePoint(outline, 3);
+
+ outline.pts[outline.ptsCnt] = mathTransform(ctrl1, transform);
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
+ ++outline.ptsCnt;
+
+ outline.pts[outline.ptsCnt] = mathTransform(ctrl2, transform);
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
+ ++outline.ptsCnt;
+
+ outline.pts[outline.ptsCnt] = mathTransform(to, transform);
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
+ ++outline.ptsCnt;
+}
+
+
+static void _outlineClose(SwOutline& outline)
+{
+ uint32_t i = 0;
+
+ if (outline.cntrsCnt > 0) {
+ i = outline.cntrs[outline.cntrsCnt - 1] + 1;
+ } else {
+ i = 0; //First Path
+ }
+
+ //Make sure there is at least one point in the current path
+ if (outline.ptsCnt == i) return;
+
+ //Close the path
+ _growOutlinePoint(outline, 1);
+
+ outline.pts[outline.ptsCnt] = outline.pts[i];
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
+ ++outline.ptsCnt;
+ outline.closed[outline.cntrsCnt] = true;
+}
+
+
+static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
+{
+ _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
+ _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
+
+ Line cur = {dash.ptCur, *to};
+ auto len = _lineLength(cur.pt1, cur.pt2);
+
+ if (len < dash.curLen) {
+ dash.curLen -= len;
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
+ _outlineLineTo(*dash.outline, to, transform);
+ }
+ } else {
+ while (len > dash.curLen) {
+ len -= dash.curLen;
+ Line left, right;
+ _lineSplitAt(cur, dash.curLen, left, right);;
+ dash.curIdx = (dash.curIdx + 1) % dash.cnt;
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &left.pt1, transform);
+ _outlineLineTo(*dash.outline, &left.pt2, transform);
+ }
+ dash.curLen = dash.pattern[dash.curIdx];
+ dash.curOpGap = !dash.curOpGap;
+ cur = right;
+ dash.ptCur = cur.pt1;
+ }
+ //leftovers
+ dash.curLen -= len;
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &cur.pt1, transform);
+ _outlineLineTo(*dash.outline, &cur.pt2, transform);
+ }
+ if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
+ //move to next dash
+ dash.curIdx = (dash.curIdx + 1) % dash.cnt;
+ dash.curLen = dash.pattern[dash.curIdx];
+ dash.curOpGap = !dash.curOpGap;
+ }
+ }
+ dash.ptCur = *to;
+}
+
+
+static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
+{
+ _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
+ _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
+
+ Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
+ auto len = bezLength(cur);
+
+ if (len < dash.curLen) {
+ dash.curLen -= len;
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
+ _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
+ }
+ } else {
+ while (len > dash.curLen) {
+ Bezier left, right;
+ len -= dash.curLen;
+ bezSplitAt(cur, dash.curLen, left, right);
+ if (!dash.curOpGap) {
+ // leftovers from a previous command don't require moveTo
+ if (dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
+ _outlineMoveTo(*dash.outline, &left.start, transform);
+ }
+ _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
+ }
+ dash.curIdx = (dash.curIdx + 1) % dash.cnt;
+ dash.curLen = dash.pattern[dash.curIdx];
+ dash.curOpGap = !dash.curOpGap;
+ cur = right;
+ dash.ptCur = right.start;
+ }
+ //leftovers
+ dash.curLen -= len;
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &cur.start, transform);
+ _outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform);
+ }
+ if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
+ //move to next dash
+ dash.curIdx = (dash.curIdx + 1) % dash.cnt;
+ dash.curLen = dash.pattern[dash.curIdx];
+ dash.curOpGap = !dash.curOpGap;
+ }
+ }
+ dash.ptCur = *to;
+}
+
+
+static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
+{
+ const PathCommand* cmds = nullptr;
+ auto cmdCnt = sdata->pathCommands(&cmds);
+
+ const Point* pts = nullptr;
+ auto ptsCnt = sdata->pathCoords(&pts);
+
+ //No actual shape data
+ if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
+
+ SwDashStroke dash;
+ dash.curIdx = 0;
+ dash.curLen = 0;
+ dash.ptStart = {0, 0};
+ dash.ptCur = {0, 0};
+ dash.curOpGap = false;
+
+ const float* pattern;
+ dash.cnt = sdata->strokeDash(&pattern);
+ if (dash.cnt == 0) return nullptr;
+
+ //OPTMIZE ME: Use mempool???
+ dash.pattern = const_cast<float*>(pattern);
+ dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
+
+ //smart reservation
+ auto outlinePtsCnt = 0;
+ auto outlineCntrsCnt = 0;
+
+ for (uint32_t i = 0; i < cmdCnt; ++i) {
+ switch (*(cmds + i)) {
+ case PathCommand::Close: {
+ ++outlinePtsCnt;
+ break;
+ }
+ case PathCommand::MoveTo: {
+ ++outlineCntrsCnt;
+ ++outlinePtsCnt;
+ break;
+ }
+ case PathCommand::LineTo: {
+ ++outlinePtsCnt;
+ break;
+ }
+ case PathCommand::CubicTo: {
+ outlinePtsCnt += 3;
+ break;
+ }
+ }
+ }
+
+ ++outlinePtsCnt; //for close
+ ++outlineCntrsCnt; //for end
+
+ //No idea exact count.... Reserve Approximitely 20x...
+ _growOutlinePoint(*dash.outline, outlinePtsCnt * 20);
+ _growOutlineContour(*dash.outline, outlineCntrsCnt * 20);
+
+ while (cmdCnt-- > 0) {
+ switch (*cmds) {
+ case PathCommand::Close: {
+ _dashLineTo(dash, &dash.ptStart, transform);
+ break;
+ }
+ case PathCommand::MoveTo: {
+ //reset the dash
+ dash.curIdx = 0;
+ dash.curLen = *dash.pattern;
+ dash.curOpGap = false;
+ dash.ptStart = dash.ptCur = *pts;
+ ++pts;
+ break;
+ }
+ case PathCommand::LineTo: {
+ _dashLineTo(dash, pts, transform);
+ ++pts;
+ break;
+ }
+ case PathCommand::CubicTo: {
+ _dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
+ pts += 3;
+ break;
+ }
+ }
+ ++cmds;
+ }
+
+ _outlineEnd(*dash.outline);
+
+ return dash.outline;
+}
+
+
+static bool _axisAlignedRect(const SwOutline* outline)
+{
+ //Fast Track: axis-aligned rectangle?
+ if (outline->ptsCnt != 5) return false;
+
+ auto pt1 = outline->pts + 0;
+ auto pt2 = outline->pts + 1;
+ auto pt3 = outline->pts + 2;
+ auto pt4 = outline->pts + 3;
+
+ auto a = SwPoint{pt1->x, pt3->y};
+ auto b = SwPoint{pt3->x, pt1->y};
+
+ if ((*pt2 == a && *pt4 == b) || (*pt2 == b && *pt4 == a)) return true;
+
+ return false;
+}
+
+
+
+static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
+{
+ const PathCommand* cmds = nullptr;
+ auto cmdCnt = sdata->pathCommands(&cmds);
+
+ const Point* pts = nullptr;
+ auto ptsCnt = sdata->pathCoords(&pts);
+
+ //No actual shape data
+ if (cmdCnt == 0 || ptsCnt == 0) return false;
+
+ //smart reservation
+ auto outlinePtsCnt = 0;
+ auto outlineCntrsCnt = 0;
+ auto closeCnt = 0;
+
+ for (uint32_t i = 0; i < cmdCnt; ++i) {
+ switch (*(cmds + i)) {
+ case PathCommand::Close: {
+ ++outlinePtsCnt;
+ ++closeCnt;
+ break;
+ }
+ case PathCommand::MoveTo: {
+ ++outlineCntrsCnt;
+ ++outlinePtsCnt;
+ break;
+ }
+ case PathCommand::LineTo: {
+ ++outlinePtsCnt;
+ break;
+ }
+ case PathCommand::CubicTo: {
+ outlinePtsCnt += 3;
+ break;
+ }
+ }
+ }
+
+ if (static_cast<uint32_t>(outlinePtsCnt - closeCnt) > ptsCnt) {
+ TVGERR("SW_ENGINE", "Wrong a pair of the commands & points - required(%d), current(%d)", outlinePtsCnt - closeCnt, ptsCnt);
+ return false;
+ }
+
+ ++outlinePtsCnt; //for close
+ ++outlineCntrsCnt; //for end
+
+ shape->outline = mpoolReqOutline(mpool, tid);
+ auto outline = shape->outline;
+
+ _growOutlinePoint(*outline, outlinePtsCnt);
+
+ if (_growOutlineContour(*outline, outlineCntrsCnt)) {
+ _reserveOutlineClose(*outline);
+ } else {
+ _resetOutlineClose(*outline);
+ }
+
+ //Generate Outlines
+ while (cmdCnt-- > 0) {
+ switch (*cmds) {
+ case PathCommand::Close: {
+ _outlineClose(*outline);
+ break;
+ }
+ case PathCommand::MoveTo: {
+ _outlineMoveTo(*outline, pts, transform);
+ ++pts;
+ break;
+ }
+ case PathCommand::LineTo: {
+ _outlineLineTo(*outline, pts, transform);
+ ++pts;
+ break;
+ }
+ case PathCommand::CubicTo: {
+ _outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform);
+ pts += 3;
+ break;
+ }
+ }
+ ++cmds;
+ }
+
+ _outlineEnd(*outline);
+
+ outline->fillRule = sdata->fillRule();
+ shape->outline = outline;
+
+ shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline));
+ return true;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
+{
+ if (!_genOutline(shape, sdata, transform, mpool, tid, hasComposite)) return false;
+ if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
+
+ //Keep it for Rasterization Region
+ shape->bbox = renderRegion;
+
+ //Check valid region
+ if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false;
+
+ //Check boundary
+ if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y ||
+ renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false;
+
+ return true;
+}
+
+
+bool shapePrepared(const SwShape* shape)
+{
+ return shape->rle ? true : false;
+}
+
+
+bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias)
+{
+ //FIXME: Should we draw it?
+ //Case: Stroke Line
+ //if (shape.outline->opened) return true;
+
+ //Case A: Fast Track Rectangle Drawing
+ if (shape->fastTrack) return true;
+
+ //Case B: Normal Shape RLE Drawing
+ if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true;
+
+ return false;
+}
+
+
+void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid)
+{
+ mpoolRetOutline(mpool, tid);
+ shape->outline = nullptr;
+}
+
+
+void shapeReset(SwShape* shape)
+{
+ rleReset(shape->rle);
+ rleReset(shape->strokeRle);
+ shape->fastTrack = false;
+ shape->bbox.reset();
+}
+
+
+void shapeFree(SwShape* shape)
+{
+ rleFree(shape->rle);
+ shapeDelFill(shape);
+
+ if (shape->stroke) {
+ rleFree(shape->strokeRle);
+ strokeFree(shape->stroke);
+ }
+}
+
+
+void shapeDelStroke(SwShape* shape)
+{
+ if (!shape->stroke) return;
+ rleFree(shape->strokeRle);
+ shape->strokeRle = nullptr;
+ strokeFree(shape->stroke);
+ shape->stroke = nullptr;
+}
+
+
+void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform)
+{
+ if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
+ auto stroke = shape->stroke;
+ if (!stroke) return;
+
+ strokeReset(stroke, sdata, transform);
+ rleReset(shape->strokeRle);
+}
+
+
+bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
+{
+ SwOutline* shapeOutline = nullptr;
+ SwOutline* strokeOutline = nullptr;
+ bool freeOutline = false;
+ bool ret = true;
+
+ //Dash Style Stroke
+ if (sdata->strokeDash(nullptr) > 0) {
+ shapeOutline = _genDashOutline(sdata, transform);
+ if (!shapeOutline) return false;
+ freeOutline = true;
+ //Normal Style stroke
+ } else {
+ if (!shape->outline) {
+ if (!_genOutline(shape, sdata, transform, mpool, tid, false)) return false;
+ }
+ shapeOutline = shape->outline;
+ }
+
+ if (!strokeParseOutline(shape->stroke, *shapeOutline)) {
+ ret = false;
+ goto fail;
+ }
+
+ strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
+
+ if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) {
+ ret = false;
+ goto fail;
+ }
+
+ shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true);
+
+fail:
+ if (freeOutline) {
+ if (shapeOutline->cntrs) free(shapeOutline->cntrs);
+ if (shapeOutline->pts) free(shapeOutline->pts);
+ if (shapeOutline->types) free(shapeOutline->types);
+ if (shapeOutline->closed) free(shapeOutline->closed);
+ free(shapeOutline);
+ }
+ mpoolRetStrokeOutline(mpool, tid);
+
+ return ret;
+}
+
+
+bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
+{
+ return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable);
+}
+
+
+bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
+{
+ return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
+}
+
+
+void shapeResetFill(SwShape* shape)
+{
+ if (!shape->fill) {
+ shape->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
+ if (!shape->fill) return;
+ }
+ fillReset(shape->fill);
+}
+
+
+void shapeResetStrokeFill(SwShape* shape)
+{
+ if (!shape->stroke->fill) {
+ shape->stroke->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
+ if (!shape->stroke->fill) return;
+ }
+ fillReset(shape->stroke->fill);
+}
+
+
+void shapeDelFill(SwShape* shape)
+{
+ if (!shape->fill) return;
+ fillFree(shape->fill);
+ shape->fill = nullptr;
+}
+
+
+void shapeDelStrokeFill(SwShape* shape)
+{
+ if (!shape->stroke->fill) return;
+ fillFree(shape->stroke->fill);
+ shape->stroke->fill = nullptr;
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
new file mode 100644
index 0000000000..c0cfc1be26
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
@@ -0,0 +1,932 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <string.h>
+#include <math.h>
+#include "tvgSwCommon.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static constexpr auto SW_STROKE_TAG_POINT = 1;
+static constexpr auto SW_STROKE_TAG_CUBIC = 2;
+static constexpr auto SW_STROKE_TAG_BEGIN = 4;
+static constexpr auto SW_STROKE_TAG_END = 8;
+
+static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
+{
+ return (SW_ANGLE_PI2 - static_cast<SwFixed>(s) * SW_ANGLE_PI);
+}
+
+
+static inline void SCALE(const SwStroke& stroke, SwPoint& pt)
+{
+ pt.x = static_cast<SwCoord>(pt.x * stroke.sx);
+ pt.y = static_cast<SwCoord>(pt.y * stroke.sy);
+}
+
+
+static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
+{
+ auto maxOld = border->maxPts;
+ auto maxNew = border->ptsCnt + newPts;
+
+ if (maxNew <= maxOld) return;
+
+ auto maxCur = maxOld;
+
+ while (maxCur < maxNew)
+ maxCur += (maxCur >> 1) + 16;
+ //OPTIMIZE: use mempool!
+ border->pts = static_cast<SwPoint*>(realloc(border->pts, maxCur * sizeof(SwPoint)));
+ border->tags = static_cast<uint8_t*>(realloc(border->tags, maxCur * sizeof(uint8_t)));
+ border->maxPts = maxCur;
+}
+
+
+static void _borderClose(SwStrokeBorder* border, bool reverse)
+{
+ auto start = border->start;
+ auto count = border->ptsCnt;
+
+ //Don't record empty paths!
+ if (count <= start + 1U) {
+ border->ptsCnt = start;
+ } else {
+ /* Copy the last point to the start of this sub-path,
+ since it contains the adjusted starting coordinates */
+ border->ptsCnt = --count;
+ border->pts[start] = border->pts[count];
+
+ if (reverse) {
+ //reverse the points
+ auto pt1 = border->pts + start + 1;
+ auto pt2 = border->pts + count - 1;
+
+ while (pt1 < pt2) {
+ auto tmp = *pt1;
+ *pt1 = *pt2;
+ *pt2 = tmp;
+ ++pt1;
+ --pt2;
+ }
+
+ //reverse the tags
+ auto tag1 = border->tags + start + 1;
+ auto tag2 = border->tags + count - 1;
+
+ while (tag1 < tag2) {
+ auto tmp = *tag1;
+ *tag1 = *tag2;
+ *tag2 = tmp;
+ ++tag1;
+ --tag2;
+ }
+ }
+
+ border->tags[start] |= SW_STROKE_TAG_BEGIN;
+ border->tags[count - 1] |= SW_STROKE_TAG_END;
+ }
+
+ border->start = -1;
+ border->movable = false;
+}
+
+
+static void _borderCubicTo(SwStrokeBorder* border, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
+{
+ _growBorder(border, 3);
+
+ auto pt = border->pts + border->ptsCnt;
+ auto tag = border->tags + border->ptsCnt;
+
+ pt[0] = ctrl1;
+ pt[1] = ctrl2;
+ pt[2] = to;
+
+ tag[0] = SW_STROKE_TAG_CUBIC;
+ tag[1] = SW_STROKE_TAG_CUBIC;
+ tag[2] = SW_STROKE_TAG_POINT;
+
+ border->ptsCnt += 3;
+
+ border->movable = false;
+}
+
+
+static void _borderArcTo(SwStrokeBorder* border, const SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff, SwStroke& stroke)
+{
+ constexpr SwFixed ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2;
+ SwPoint a = {static_cast<SwCoord>(radius), 0};
+ mathRotate(a, angleStart);
+ SCALE(stroke, a);
+ a += center;
+
+ auto total = angleDiff;
+ auto angle = angleStart;
+ auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2;
+
+ while (total != 0) {
+ auto step = total;
+ if (step > ARC_CUBIC_ANGLE) step = ARC_CUBIC_ANGLE;
+ else if (step < -ARC_CUBIC_ANGLE) step = -ARC_CUBIC_ANGLE;
+
+ auto next = angle + step;
+ auto theta = step;
+ if (theta < 0) theta = -theta;
+
+ theta >>= 1;
+
+ //compute end point
+ SwPoint b = {static_cast<SwCoord>(radius), 0};
+ mathRotate(b, next);
+ SCALE(stroke, b);
+ b += center;
+
+ //compute first and second control points
+ auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3);
+
+ SwPoint a2 = {static_cast<SwCoord>(length), 0};
+ mathRotate(a2, angle + rotate);
+ SCALE(stroke, a2);
+ a2 += a;
+
+ SwPoint b2 = {static_cast<SwCoord>(length), 0};
+ mathRotate(b2, next - rotate);
+ SCALE(stroke, b2);
+ b2 += b;
+
+ //add cubic arc
+ _borderCubicTo(border, a2, b2, b);
+
+ //process the rest of the arc?
+ a = b;
+ total -= step;
+ angle = next;
+ }
+}
+
+
+static void _borderLineTo(SwStrokeBorder* border, const SwPoint& to, bool movable)
+{
+ if (border->movable) {
+ //move last point
+ border->pts[border->ptsCnt - 1] = to;
+ } else {
+
+ //don't add zero-length line_to
+ if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return;
+
+ _growBorder(border, 1);
+ border->pts[border->ptsCnt] = to;
+ border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT;
+ border->ptsCnt += 1;
+ }
+
+ border->movable = movable;
+}
+
+
+static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to)
+{
+ //close current open path if any?
+ if (border->start >= 0) _borderClose(border, false);
+
+ border->start = border->ptsCnt;
+ border->movable = false;
+
+ _borderLineTo(border, to, false);
+}
+
+
+static void _arcTo(SwStroke& stroke, int32_t side)
+{
+ auto border = stroke.borders + side;
+ auto rotate = SIDE_TO_ROTATE(side);
+ auto total = mathDiff(stroke.angleIn, stroke.angleOut);
+ if (total == SW_ANGLE_PI) total = -rotate * 2;
+
+ _borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total, stroke);
+ border->movable = false;
+}
+
+
+static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
+{
+ constexpr SwFixed MITER_LIMIT = 4 * (1 << 16);
+
+ auto border = stroke.borders + side;
+
+ if (stroke.join == StrokeJoin::Round) {
+ _arcTo(stroke, side);
+ } else {
+ //this is a mitered (pointed) or beveled (truncated) corner
+ auto rotate = SIDE_TO_ROTATE(side);
+ auto bevel = (stroke.join == StrokeJoin::Bevel) ? true : false;
+ SwFixed phi = 0;
+ SwFixed thcos = 0;
+
+ if (!bevel) {
+ auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
+ if (theta == SW_ANGLE_PI) {
+ theta = rotate;
+ phi = stroke.angleIn;
+ } else {
+ theta /= 2;
+ phi = stroke.angleIn + theta + rotate;
+ }
+
+ thcos = mathCos(theta);
+ auto sigma = mathMultiply(MITER_LIMIT, thcos);
+
+ //is miter limit exceeded?
+ if (sigma < 0x10000L) bevel = true;
+ }
+
+ //this is a bevel (broken angle)
+ if (bevel) {
+ SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, stroke.angleOut + rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+ border->movable = false;
+ _borderLineTo(border, delta, false);
+ //this is a miter (intersection)
+ } else {
+ auto length = mathDivide(stroke.width, thcos);
+ SwPoint delta = {static_cast<SwCoord>(length), 0};
+ mathRotate(delta, phi);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+ _borderLineTo(border, delta, false);
+
+ /* Now add and end point
+ Only needed if not lineto (lineLength is zero for curves) */
+ if (lineLength == 0) {
+ delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, stroke.angleOut + rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+ _borderLineTo(border, delta, false);
+ }
+ }
+ }
+}
+
+
+static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
+{
+ auto border = stroke.borders + side;
+ auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2;
+ SwPoint delta;
+ bool intersect = false;
+
+ /* Only intersect borders if between two line_to's and both
+ lines are long enough (line length is zero fur curves). */
+ if (border->movable && lineLength > 0) {
+ //compute minimum required length of lines
+ SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta)));
+ if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true;
+ }
+
+ auto rotate = SIDE_TO_ROTATE(side);
+
+ if (!intersect) {
+ delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, stroke.angleOut + rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+ border->movable = false;
+ } else {
+ //compute median angle
+ auto phi = stroke.angleIn + theta;
+ auto thcos = mathCos(theta);
+ delta = {static_cast<SwCoord>(mathDivide(stroke.width, thcos)), 0};
+ mathRotate(delta, phi + rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+ }
+
+ _borderLineTo(border, delta, false);
+}
+
+
+void _processCorner(SwStroke& stroke, SwFixed lineLength)
+{
+ auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
+
+ //no specific corner processing is required if the turn is 0
+ if (turn == 0) return;
+
+ //when we turn to the right, the inside side is 0
+ int32_t inside = 0;
+
+ //otherwise, the inside is 1
+ if (turn < 0) inside = 1;
+
+ //process the inside
+ _inside(stroke, inside, lineLength);
+
+ //process the outside
+ _outside(stroke, 1 - inside, lineLength);
+}
+
+
+void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
+{
+ SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, startAngle + SW_ANGLE_PI2);
+ SCALE(stroke, delta);
+
+ auto pt = stroke.center + delta;
+ auto border = stroke.borders;
+ _borderMoveTo(border, pt);
+
+ pt = stroke.center - delta;
+ ++border;
+ _borderMoveTo(border, pt);
+
+ /* Save angle, position and line length for last join
+ lineLength is zero for curves */
+ stroke.subPathAngle = startAngle;
+ stroke.firstPt = false;
+ stroke.subPathLineLength = lineLength;
+}
+
+
+static void _lineTo(SwStroke& stroke, const SwPoint& to)
+{
+ auto delta = to - stroke.center;
+
+ //a zero-length lineto is a no-op; avoid creating a spurious corner
+ if (delta.zero()) return;
+
+ //compute length of line
+ auto lineLength = mathLength(delta);
+ auto angle = mathAtan(delta);
+
+ delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, angle + SW_ANGLE_PI2);
+ SCALE(stroke, delta);
+
+ //process corner if necessary
+ if (stroke.firstPt) {
+ /* This is the first segment of a subpath. We need to add a point to each border
+ at their respective starting point locations. */
+ _firstSubPath(stroke, angle, lineLength);
+ } else {
+ //process the current corner
+ stroke.angleOut = angle;
+ _processCorner(stroke, lineLength);
+ }
+
+ //now add a line segment to both the inside and outside paths
+ auto border = stroke.borders;
+ auto side = 1;
+
+ while (side >= 0) {
+ auto pt = to + delta;
+
+ //the ends of lineto borders are movable
+ _borderLineTo(border, pt, true);
+
+ delta.x = -delta.x;
+ delta.y = -delta.y;
+
+ --side;
+ ++border;
+ }
+
+ stroke.angleIn = angle;
+ stroke.center = to;
+ stroke.lineLength = lineLength;
+}
+
+
+static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
+{
+ /* if all control points are coincident, this is a no-op;
+ avoid creating a spurious corner */
+ if ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) {
+ stroke.center = to;
+ return;
+ }
+
+ SwPoint bezStack[37]; //TODO: static?
+ auto limit = bezStack + 32;
+ auto arc = bezStack;
+ auto firstArc = true;
+ arc[0] = to;
+ arc[1] = ctrl2;
+ arc[2] = ctrl1;
+ arc[3] = stroke.center;
+
+ while (arc >= bezStack) {
+ SwFixed angleIn, angleOut, angleMid;
+
+ //initialize with current direction
+ angleIn = angleOut = angleMid = stroke.angleIn;
+
+ if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
+ if (stroke.firstPt) stroke.angleIn = angleIn;
+ mathSplitCubic(arc);
+ arc += 3;
+ continue;
+ }
+
+ if (firstArc) {
+ firstArc = false;
+ //process corner if necessary
+ if (stroke.firstPt) {
+ _firstSubPath(stroke, angleIn, 0);
+ } else {
+ stroke.angleOut = angleIn;
+ _processCorner(stroke, 0);
+ }
+ } else if (abs(mathDiff(stroke.angleIn, angleIn)) > (SW_ANGLE_PI / 8) / 4) {
+ //if the deviation from one arc to the next is too great add a round corner
+ stroke.center = arc[3];
+ stroke.angleOut = angleIn;
+ stroke.join = StrokeJoin::Round;
+
+ _processCorner(stroke, 0);
+
+ //reinstate line join style
+ stroke.join = stroke.joinSaved;
+ }
+
+ //the arc's angle is small enough; we can add it directly to each border
+ auto theta1 = mathDiff(angleIn, angleMid) / 2;
+ auto theta2 = mathDiff(angleMid, angleOut) / 2;
+ auto phi1 = mathMean(angleIn, angleMid);
+ auto phi2 = mathMean(angleMid, angleOut);
+ auto length1 = mathDivide(stroke.width, mathCos(theta1));
+ auto length2 = mathDivide(stroke.width, mathCos(theta2));
+ SwFixed alpha0 = 0;
+
+ //compute direction of original arc
+ if (stroke.handleWideStrokes) {
+ alpha0 = mathAtan(arc[0] - arc[3]);
+ }
+
+ auto border = stroke.borders;
+ int32_t side = 0;
+
+ while (side <= 1)
+ {
+ auto rotate = SIDE_TO_ROTATE(side);
+
+ //compute control points
+ SwPoint _ctrl1 = {static_cast<SwCoord>(length1), 0};
+ mathRotate(_ctrl1, phi1 + rotate);
+ SCALE(stroke, _ctrl1);
+ _ctrl1 += arc[2];
+
+ SwPoint _ctrl2 = {static_cast<SwCoord>(length2), 0};
+ mathRotate(_ctrl2, phi2 + rotate);
+ SCALE(stroke, _ctrl2);
+ _ctrl2 += arc[1];
+
+ //compute end point
+ SwPoint _end = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(_end, angleOut + rotate);
+ SCALE(stroke, _end);
+ _end += arc[0];
+
+ if (stroke.handleWideStrokes) {
+
+ /* determine whether the border radius is greater than the radius of
+ curvature of the original arc */
+ auto _start = border->pts[border->ptsCnt - 1];
+ auto alpha1 = mathAtan(_end - _start);
+
+ //is the direction of the border arc opposite to that of the original arc?
+ if (abs(mathDiff(alpha0, alpha1)) > SW_ANGLE_PI / 2) {
+
+ //use the sine rule to find the intersection point
+ auto beta = mathAtan(arc[3] - _start);
+ auto gamma = mathAtan(arc[0] - _end);
+ auto bvec = _end - _start;
+ auto blen = mathLength(bvec);
+ auto sinA = abs(mathSin(alpha1 - gamma));
+ auto sinB = abs(mathSin(beta - gamma));
+ auto alen = mathMulDiv(blen, sinA, sinB);
+
+ SwPoint delta = {static_cast<SwCoord>(alen), 0};
+ mathRotate(delta, beta);
+ delta += _start;
+
+ //circumnavigate the negative sector backwards
+ border->movable = false;
+ _borderLineTo(border, delta, false);
+ _borderLineTo(border, _end, false);
+ _borderCubicTo(border, _ctrl2, _ctrl1, _start);
+
+ //and then move to the endpoint
+ _borderLineTo(border, _end, false);
+
+ ++side;
+ ++border;
+ continue;
+ }
+
+ //else fall through
+ }
+ _borderCubicTo(border, _ctrl1, _ctrl2, _end);
+ ++side;
+ ++border;
+ }
+ arc -= 3;
+ stroke.angleIn = angleOut;
+ }
+ stroke.center = to;
+}
+
+
+static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
+{
+ if (stroke.cap == StrokeCap::Square) {
+ auto rotate = SIDE_TO_ROTATE(side);
+ auto border = stroke.borders + side;
+
+ SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, angle);
+ SCALE(stroke, delta);
+
+ SwPoint delta2 = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta2, angle + rotate);
+ SCALE(stroke, delta2);
+ delta += stroke.center + delta2;
+
+ _borderLineTo(border, delta, false);
+
+ delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, angle);
+ SCALE(stroke, delta);
+
+ delta2 = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta2, angle - rotate);
+ SCALE(stroke, delta2);
+ delta += delta2 + stroke.center;
+
+ _borderLineTo(border, delta, false);
+
+ } else if (stroke.cap == StrokeCap::Round) {
+
+ stroke.angleIn = angle;
+ stroke.angleOut = angle + SW_ANGLE_PI;
+ _arcTo(stroke, side);
+ return;
+
+ } else { //Butt
+ auto rotate = SIDE_TO_ROTATE(side);
+ auto border = stroke.borders + side;
+
+ SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, angle + rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+
+ _borderLineTo(border, delta, false);
+
+ delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, angle - rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+
+ _borderLineTo(border, delta, false);
+ }
+}
+
+
+static void _addReverseLeft(SwStroke& stroke, bool opened)
+{
+ auto right = stroke.borders + 0;
+ auto left = stroke.borders + 1;
+ auto newPts = left->ptsCnt - left->start;
+
+ if (newPts <= 0) return;
+
+ _growBorder(right, newPts);
+
+ auto dstPt = right->pts + right->ptsCnt;
+ auto dstTag = right->tags + right->ptsCnt;
+ auto srcPt = left->pts + left->ptsCnt - 1;
+ auto srcTag = left->tags + left->ptsCnt - 1;
+
+ while (srcPt >= left->pts + left->start) {
+ *dstPt = *srcPt;
+ *dstTag = *srcTag;
+
+ if (opened) {
+ dstTag[0] &= ~(SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
+ } else {
+ //switch begin/end tags if necessary
+ auto ttag = dstTag[0] & (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
+ if (ttag == SW_STROKE_TAG_BEGIN || ttag == SW_STROKE_TAG_END)
+ dstTag[0] ^= (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
+ }
+
+ --srcPt;
+ --srcTag;
+ ++dstPt;
+ ++dstTag;
+ }
+
+ left->ptsCnt = left->start;
+ right->ptsCnt += newPts;
+ right->movable = false;
+ left->movable = false;
+}
+
+
+static void _beginSubPath(SwStroke& stroke, const SwPoint& to, bool closed)
+{
+ /* We cannot process the first point because there is not enough
+ information regarding its corner/cap. Later, it will be processed
+ in the _endSubPath() */
+
+ stroke.firstPt = true;
+ stroke.center = to;
+ stroke.closedSubPath = closed;
+
+ /* Determine if we need to check whether the border radius is greater
+ than the radius of curvature of a curve, to handle this case specially.
+ This is only required if bevel joins or butt caps may be created because
+ round & miter joins and round & square caps cover the nagative sector
+ created with wide strokes. */
+ if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt))
+ stroke.handleWideStrokes = true;
+ else
+ stroke.handleWideStrokes = false;
+
+ stroke.ptStartSubPath = to;
+ stroke.angleIn = 0;
+}
+
+
+static void _endSubPath(SwStroke& stroke)
+{
+ if (stroke.closedSubPath) {
+ //close the path if needed
+ if (stroke.center != stroke.ptStartSubPath)
+ _lineTo(stroke, stroke.ptStartSubPath);
+
+ //process the corner
+ stroke.angleOut = stroke.subPathAngle;
+ auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
+
+ //No specific corner processing is required if the turn is 0
+ if (turn != 0) {
+
+ //when we turn to the right, the inside is 0
+ int32_t inside = 0;
+
+ //otherwise, the inside is 1
+ if (turn < 0) inside = 1;
+
+ _inside(stroke, inside, stroke.subPathLineLength); //inside
+ _outside(stroke, 1 - inside, stroke.subPathLineLength); //outside
+ }
+
+ _borderClose(stroke.borders + 0, false);
+ _borderClose(stroke.borders + 1, true);
+ } else {
+ auto right = stroke.borders;
+
+ /* all right, this is an opened path, we need to add a cap between
+ right & left, add the reverse of left, then add a final cap
+ between left & right */
+ _addCap(stroke, stroke.angleIn, 0);
+
+ //add reversed points from 'left' to 'right'
+ _addReverseLeft(stroke, true);
+
+ //now add the final cap
+ stroke.center = stroke.ptStartSubPath;
+ _addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
+
+ /* now end the right subpath accordingly. The left one is rewind
+ and deosn't need further processing */
+ _borderClose(right, false);
+ }
+}
+
+
+static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt)
+{
+ auto count = border->ptsCnt;
+ auto tags = border->tags;
+ uint32_t _ptsCnt = 0;
+ uint32_t _cntrsCnt = 0;
+ bool inCntr = false;
+
+ while (count > 0) {
+ if (tags[0] & SW_STROKE_TAG_BEGIN) {
+ if (inCntr) goto fail;
+ inCntr = true;
+ } else if (!inCntr) goto fail;
+
+ if (tags[0] & SW_STROKE_TAG_END) {
+ inCntr = false;
+ ++_cntrsCnt;
+ }
+ --count;
+ ++_ptsCnt;
+ ++tags;
+ }
+
+ if (inCntr) goto fail;
+
+ ptsCnt = _ptsCnt;
+ cntrsCnt = _cntrsCnt;
+
+ return;
+
+fail:
+ ptsCnt = 0;
+ cntrsCnt = 0;
+}
+
+
+static void _exportBorderOutline(const SwStroke& stroke, SwOutline* outline, uint32_t side)
+{
+ auto border = stroke.borders + side;
+
+ if (border->ptsCnt == 0) return; //invalid border
+
+ memcpy(outline->pts + outline->ptsCnt, border->pts, border->ptsCnt * sizeof(SwPoint));
+
+ auto cnt = border->ptsCnt;
+ auto src = border->tags;
+ auto tags = outline->types + outline->ptsCnt;
+ auto cntrs = outline->cntrs + outline->cntrsCnt;
+ uint16_t idx = outline->ptsCnt;
+
+ while (cnt > 0) {
+
+ if (*src & SW_STROKE_TAG_POINT) *tags = SW_CURVE_TYPE_POINT;
+ else if (*src & SW_STROKE_TAG_CUBIC) *tags = SW_CURVE_TYPE_CUBIC;
+ else {
+ //LOG: What type of stroke outline??
+ }
+
+ if (*src & SW_STROKE_TAG_END) {
+ *cntrs = idx;
+ ++cntrs;
+ ++outline->cntrsCnt;
+ }
+ ++src;
+ ++tags;
+ ++idx;
+ --cnt;
+ }
+ outline->ptsCnt = outline->ptsCnt + border->ptsCnt;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void strokeFree(SwStroke* stroke)
+{
+ if (!stroke) return;
+
+ //free borders
+ if (stroke->borders[0].pts) free(stroke->borders[0].pts);
+ if (stroke->borders[0].tags) free(stroke->borders[0].tags);
+ if (stroke->borders[1].pts) free(stroke->borders[1].pts);
+ if (stroke->borders[1].tags) free(stroke->borders[1].tags);
+
+ fillFree(stroke->fill);
+ stroke->fill = nullptr;
+
+ free(stroke);
+}
+
+
+void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform)
+{
+ if (transform) {
+ stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f));
+ stroke->sy = sqrtf(powf(transform->e12, 2.0f) + powf(transform->e22, 2.0f));
+ } else {
+ stroke->sx = stroke->sy = 1.0f;
+ }
+
+ stroke->width = HALF_STROKE(sdata->strokeWidth());
+ stroke->cap = sdata->strokeCap();
+
+ //Save line join: it can be temporarily changed when stroking curves...
+ stroke->joinSaved = stroke->join = sdata->strokeJoin();
+
+ stroke->borders[0].ptsCnt = 0;
+ stroke->borders[0].start = -1;
+ stroke->borders[1].ptsCnt = 0;
+ stroke->borders[1].start = -1;
+}
+
+
+bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
+{
+ uint32_t first = 0;
+
+ for (uint32_t i = 0; i < outline.cntrsCnt; ++i) {
+ auto last = outline.cntrs[i]; //index of last point in contour
+ auto limit = outline.pts + last;
+
+ //Skip empty points
+ if (last <= first) {
+ first = last + 1;
+ continue;
+ }
+
+ auto start = outline.pts[first];
+ auto pt = outline.pts + first;
+ auto types = outline.types + first;
+ auto type = types[0];
+
+ //A contour cannot start with a cubic control point
+ if (type == SW_CURVE_TYPE_CUBIC) return false;
+
+ auto closed = outline.closed ? outline.closed[i]: false;
+
+ _beginSubPath(*stroke, start, closed);
+
+ while (pt < limit) {
+ ++pt;
+ ++types;
+
+ //emit a signel line_to
+ if (types[0] == SW_CURVE_TYPE_POINT) {
+ _lineTo(*stroke, *pt);
+ //types cubic
+ } else {
+ if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
+
+ pt += 2;
+ types += 2;
+
+ if (pt <= limit) {
+ _cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
+ continue;
+ }
+ _cubicTo(*stroke, pt[-2], pt[-1], start);
+ goto close;
+ }
+ }
+
+ close:
+ if (!stroke->firstPt) _endSubPath(*stroke);
+ first = last + 1;
+ }
+ return true;
+}
+
+
+SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid)
+{
+ uint32_t count1, count2, count3, count4;
+
+ _getCounts(stroke->borders + 0, count1, count2);
+ _getCounts(stroke->borders + 1, count3, count4);
+
+ auto ptsCnt = count1 + count3;
+ auto cntrsCnt = count2 + count4;
+
+ auto outline = mpoolReqStrokeOutline(mpool, tid);
+ if (outline->reservedPtsCnt < ptsCnt) {
+ outline->pts = static_cast<SwPoint*>(realloc(outline->pts, sizeof(SwPoint) * ptsCnt));
+ outline->types = static_cast<uint8_t*>(realloc(outline->types, sizeof(uint8_t) * ptsCnt));
+ outline->reservedPtsCnt = ptsCnt;
+ }
+ if (outline->reservedCntrsCnt < cntrsCnt) {
+ outline->cntrs = static_cast<uint16_t*>(realloc(outline->cntrs, sizeof(uint16_t) * cntrsCnt));
+ outline->reservedCntrsCnt = cntrsCnt;
+ }
+
+ _exportBorderOutline(*stroke, outline, 0); //left
+ _exportBorderOutline(*stroke, outline, 1); //right
+
+ return outline;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgAccessor.cpp b/thirdparty/thorvg/src/lib/tvgAccessor.cpp
new file mode 100644
index 0000000000..085c8a3cbc
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgAccessor.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "tvgIteratorAccessor.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), IteratorAccessor& itrAccessor)
+{
+ while (auto child = it->next()) {
+ //Access the child
+ if (!func(child)) return false;
+
+ //Access the children of the child
+ if (auto it2 = itrAccessor.iterator(child)) {
+ if (!accessChildren(it2, func, itrAccessor)) {
+ delete(it2);
+ return false;
+ }
+ delete(it2);
+ }
+ }
+ return true;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+unique_ptr<Picture> Accessor::access(unique_ptr<Picture> picture, bool(*func)(const Paint* paint)) noexcept
+{
+ auto p = picture.get();
+ if (!p || !func) return picture;
+
+ //Use the Preorder Tree-Search
+
+ //Root
+ if (!func(p)) return picture;
+
+ //Children
+ IteratorAccessor itrAccessor;
+ if (auto it = itrAccessor.iterator(p)) {
+ accessChildren(it, func, itrAccessor);
+ delete(it);
+ }
+ return picture;
+}
+
+
+Accessor::~Accessor()
+{
+
+}
+
+
+Accessor::Accessor()
+{
+
+}
+
+
+unique_ptr<Accessor> Accessor::gen() noexcept
+{
+ return unique_ptr<Accessor>(new Accessor);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgArray.h b/thirdparty/thorvg/src/lib/tvgArray.h
new file mode 100644
index 0000000000..d04e68e73c
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgArray.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_ARRAY_H_
+#define _TVG_ARRAY_H_
+
+#include <memory.h>
+
+namespace tvg
+{
+
+template<class T>
+struct Array
+{
+ T* data = nullptr;
+ uint32_t count = 0;
+ uint32_t reserved = 0;
+
+ void push(T element)
+ {
+ if (count + 1 > reserved) {
+ reserved = (count + 1) * 2;
+ auto p = data;
+ data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
+ if (!data) {
+ data = p;
+ return;
+ }
+ }
+ data[count++] = element;
+ }
+
+ bool reserve(uint32_t size)
+ {
+ if (size > reserved) {
+ reserved = size;
+ auto p = data;
+ data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
+ if (!data) {
+ data = p;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool grow(uint32_t size)
+ {
+ return reserve(count + size);
+ }
+
+ T* ptr()
+ {
+ return data + count;
+ }
+
+ void pop()
+ {
+ if (count > 0) --count;
+ }
+
+ void reset()
+ {
+ if (data) {
+ free(data);
+ data = nullptr;
+ }
+ count = reserved = 0;
+ }
+
+ void clear()
+ {
+ count = 0;
+ }
+
+ void operator=(const Array& rhs)
+ {
+ reserve(rhs.count);
+ if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * reserved);
+ count = rhs.count;
+ }
+
+ ~Array()
+ {
+ if (data) free(data);
+ }
+};
+
+}
+
+#endif //_TVG_ARRAY_H_
diff --git a/thirdparty/thorvg/src/lib/tvgBezier.cpp b/thirdparty/thorvg/src/lib/tvgBezier.cpp
new file mode 100644
index 0000000000..2290afa728
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgBezier.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <float.h>
+#include <math.h>
+#include "tvgBezier.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static float _lineLength(const Point& pt1, const Point& pt2)
+{
+ /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
+ With alpha = 1, beta = 3/8, giving results with the largest error less
+ than 7% compared to the exact value. */
+ Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
+ if (diff.x < 0) diff.x = -diff.x;
+ if (diff.y < 0) diff.y = -diff.y;
+ return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+namespace tvg
+{
+
+void bezSplit(const Bezier&cur, Bezier& left, Bezier& right)
+{
+ auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
+ left.ctrl1.x = (cur.start.x + cur.ctrl1.x) * 0.5f;
+ right.ctrl2.x = (cur.ctrl2.x + cur.end.x) * 0.5f;
+ left.start.x = cur.start.x;
+ right.end.x = cur.end.x;
+ left.ctrl2.x = (left.ctrl1.x + c) * 0.5f;
+ right.ctrl1.x = (right.ctrl2.x + c) * 0.5f;
+ left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f;
+
+ c = (cur.ctrl1.y + cur.ctrl2.y) * 0.5f;
+ left.ctrl1.y = (cur.start.y + cur.ctrl1.y) * 0.5f;
+ right.ctrl2.y = (cur.ctrl2.y + cur.end.y) * 0.5f;
+ left.start.y = cur.start.y;
+ right.end.y = cur.end.y;
+ left.ctrl2.y = (left.ctrl1.y + c) * 0.5f;
+ right.ctrl1.y = (right.ctrl2.y + c) * 0.5f;
+ left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f;
+}
+
+
+float bezLength(const Bezier& cur)
+{
+ Bezier left, right;
+ auto len = _lineLength(cur.start, cur.ctrl1) + _lineLength(cur.ctrl1, cur.ctrl2) + _lineLength(cur.ctrl2, cur.end);
+ auto chord = _lineLength(cur.start, cur.end);
+
+ if (fabsf(len - chord) > BEZIER_EPSILON) {
+ bezSplit(cur, left, right);
+ return bezLength(left) + bezLength(right);
+ }
+ return len;
+}
+
+
+void bezSplitLeft(Bezier& cur, float at, Bezier& left)
+{
+ left.start = cur.start;
+
+ left.ctrl1.x = cur.start.x + at * (cur.ctrl1.x - cur.start.x);
+ left.ctrl1.y = cur.start.y + at * (cur.ctrl1.y - cur.start.y);
+
+ left.ctrl2.x = cur.ctrl1.x + at * (cur.ctrl2.x - cur.ctrl1.x); //temporary holding spot
+ left.ctrl2.y = cur.ctrl1.y + at * (cur.ctrl2.y - cur.ctrl1.y); //temporary holding spot
+
+ cur.ctrl2.x = cur.ctrl2.x + at * (cur.end.x - cur.ctrl2.x);
+ cur.ctrl2.y = cur.ctrl2.y + at * (cur.end.y - cur.ctrl2.y);
+
+ cur.ctrl1.x = left.ctrl2.x + at * (cur.ctrl2.x - left.ctrl2.x);
+ cur.ctrl1.y = left.ctrl2.y + at * (cur.ctrl2.y - left.ctrl2.y);
+
+ left.ctrl2.x = left.ctrl1.x + at * (left.ctrl2.x - left.ctrl1.x);
+ left.ctrl2.y = left.ctrl1.y + at * (left.ctrl2.y - left.ctrl1.y);
+
+ left.end.x = cur.start.x = left.ctrl2.x + at * (cur.ctrl1.x - left.ctrl2.x);
+ left.end.y = cur.start.y = left.ctrl2.y + at * (cur.ctrl1.y - left.ctrl2.y);
+}
+
+
+float bezAt(const Bezier& bz, float at)
+{
+ auto len = bezLength(bz);
+ auto biggest = 1.0f;
+ auto smallest = 0.0f;
+ auto t = 0.5f;
+
+ //just in case to prevent an infinite loop
+ if (at <= 0) return 0.0f;
+
+ if (at >= len) return 1.0f;
+
+ while (true) {
+ auto right = bz;
+ Bezier left;
+ bezSplitLeft(right, t, left);
+ len = bezLength(left);
+
+ if (fabsf(len - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
+ break;
+ }
+
+ if (len < at) {
+ smallest = t;
+ t = (t + biggest) * 0.5f;
+ } else {
+ biggest = t;
+ t = (smallest + t) * 0.5f;
+ }
+ }
+ return t;
+}
+
+
+void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right)
+{
+ right = cur;
+ auto t = bezAt(right, at);
+ bezSplitLeft(right, t, left);
+}
+
+}
diff --git a/thirdparty/thorvg/src/lib/tvgBezier.h b/thirdparty/thorvg/src/lib/tvgBezier.h
new file mode 100644
index 0000000000..866e39148e
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgBezier.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_BEZIER_H_
+#define _TVG_BEZIER_H_
+
+#include "tvgCommon.h"
+
+namespace tvg
+{
+
+#define BEZIER_EPSILON 1e-4f
+
+struct Bezier
+{
+ Point start;
+ Point ctrl1;
+ Point ctrl2;
+ Point end;
+};
+
+void bezSplit(const Bezier&cur, Bezier& left, Bezier& right);
+float bezLength(const Bezier& cur);
+void bezSplitLeft(Bezier& cur, float at, Bezier& left);
+float bezAt(const Bezier& bz, float at);
+void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right);
+
+}
+
+#endif //_TVG_BEZIER_H_
diff --git a/thirdparty/thorvg/src/lib/tvgBinaryDesc.h b/thirdparty/thorvg/src/lib/tvgBinaryDesc.h
new file mode 100644
index 0000000000..30ba2d5a29
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgBinaryDesc.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_BINARY_DESC_H_
+#define _TVG_BINARY_DESC_H_
+
+/* TODO: Need to consider whether uin8_t is enough size for extension...
+ Rather than optimal data, we can use enough size and data compress? */
+
+/* Data types, do not change data types once Tvg Format is officially released,
+ That would occur the abi break. */
+
+using TvgBinByte = uint8_t;
+using TvgBinCounter = uint32_t;
+using TvgBinTag = TvgBinByte;
+using TvgBinFlag = TvgBinByte;
+
+
+//Header
+#define TVG_HEADER_SIZE 33 //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE
+#define TVG_HEADER_SIGNATURE "ThorVG"
+#define TVG_HEADER_SIGNATURE_LENGTH 6
+#define TVG_HEADER_VERSION "000400" //Major 00, Minor 04, Micro 00
+#define TVG_HEADER_VERSION_LENGTH 6
+#define TVG_HEADER_RESERVED_LENGTH 1 //Storing flags for extensions
+#define TVG_HEADER_COMPRESS_SIZE 12 //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS
+//Compress Size
+#define TVG_HEADER_UNCOMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
+#define TVG_HEADER_COMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
+#define TVG_HEADER_COMPRESSED_SIZE_BITS 4 //SIZE (TvgBinCounter)
+//Reserved Flag
+#define TVG_HEAD_FLAG_COMPRESSED 0x01
+
+//Paint Type
+#define TVG_TAG_CLASS_PICTURE (TvgBinTag)0xfc
+#define TVG_TAG_CLASS_SHAPE (TvgBinTag)0xfd
+#define TVG_TAG_CLASS_SCENE (TvgBinTag)0xfe
+
+
+//Paint
+#define TVG_TAG_PAINT_OPACITY (TvgBinTag)0x10
+#define TVG_TAG_PAINT_TRANSFORM (TvgBinTag)0x11
+#define TVG_TAG_PAINT_CMP_TARGET (TvgBinTag)0x01
+#define TVG_TAG_PAINT_CMP_METHOD (TvgBinTag)0x20
+
+
+//Scene
+#define TVG_TAG_SCENE_RESERVEDCNT (TvgBinTag)0x30
+
+
+//Shape
+#define TVG_TAG_SHAPE_PATH (TvgBinTag)0x40
+#define TVG_TAG_SHAPE_STROKE (TvgBinTag)0x41
+#define TVG_TAG_SHAPE_FILL (TvgBinTag)0x42
+#define TVG_TAG_SHAPE_COLOR (TvgBinTag)0x43
+#define TVG_TAG_SHAPE_FILLRULE (TvgBinTag)0x44
+#define TVG_TAG_SHAPE_STROKE_CAP (TvgBinTag)0x50
+#define TVG_TAG_SHAPE_STROKE_JOIN (TvgBinTag)0x51
+
+//Stroke
+#define TVG_TAG_SHAPE_STROKE_WIDTH (TvgBinTag)0x52
+#define TVG_TAG_SHAPE_STROKE_COLOR (TvgBinTag)0x53
+#define TVG_TAG_SHAPE_STROKE_FILL (TvgBinTag)0x54
+#define TVG_TAG_SHAPE_STROKE_DASHPTRN (TvgBinTag)0x55
+
+
+//Fill
+#define TVG_TAG_FILL_LINEAR_GRADIENT (TvgBinTag)0x60
+#define TVG_TAG_FILL_RADIAL_GRADIENT (TvgBinTag)0x61
+#define TVG_TAG_FILL_COLORSTOPS (TvgBinTag)0x62
+#define TVG_TAG_FILL_FILLSPREAD (TvgBinTag)0x63
+#define TVG_TAG_FILL_TRANSFORM (TvgBinTag)0x64
+
+
+//Picture
+#define TVG_TAG_PICTURE_RAW_IMAGE (TvgBinTag)0x70
+
+#endif //_TVG_BINARY_DESC_H_
diff --git a/thirdparty/thorvg/src/lib/tvgCanvas.cpp b/thirdparty/thorvg/src/lib/tvgCanvas.cpp
new file mode 100644
index 0000000000..bb42441c62
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgCanvas.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgCanvasImpl.h"
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Canvas::Canvas(RenderMethod *pRenderer):pImpl(new Impl(pRenderer))
+{
+}
+
+
+Canvas::~Canvas()
+{
+ delete(pImpl);
+}
+
+
+Result Canvas::reserve(uint32_t n) noexcept
+{
+ if (!pImpl->paints.reserve(n)) return Result::FailedAllocation;
+ return Result::Success;
+}
+
+
+Result Canvas::push(unique_ptr<Paint> paint) noexcept
+{
+ return pImpl->push(move(paint));
+}
+
+
+Result Canvas::clear(bool free) noexcept
+{
+ return pImpl->clear(free);
+}
+
+
+Result Canvas::draw() noexcept
+{
+ return pImpl->draw();
+}
+
+
+Result Canvas::update(Paint* paint) noexcept
+{
+ return pImpl->update(paint, false);
+}
+
+
+Result Canvas::sync() noexcept
+{
+ return pImpl->sync();
+}
diff --git a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
new file mode 100644
index 0000000000..848c03aaed
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_CANVAS_IMPL_H_
+#define _TVG_CANVAS_IMPL_H_
+
+#include "tvgPaint.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct Canvas::Impl
+{
+ Array<Paint*> paints;
+ RenderMethod* renderer;
+ bool refresh = false; //if all paints should be updated by force.
+ bool drawing = false; //on drawing condition?
+
+ Impl(RenderMethod* pRenderer):renderer(pRenderer)
+ {
+ }
+
+ ~Impl()
+ {
+ clear(true);
+ delete(renderer);
+ }
+
+ Result push(unique_ptr<Paint> paint)
+ {
+ //You can not push paints during rendering.
+ if (drawing) return Result::InsufficientCondition;
+
+ auto p = paint.release();
+ if (!p) return Result::MemoryCorruption;
+ paints.push(p);
+
+ return update(p, true);
+ }
+
+ Result clear(bool free)
+ {
+ //Clear render target before drawing
+ if (!renderer || !renderer->clear()) return Result::InsufficientCondition;
+
+ //Free paints
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ (*paint)->pImpl->dispose(*renderer);
+ if (free) delete(*paint);
+ }
+
+ paints.clear();
+
+ drawing = false;
+
+ return Result::Success;
+ }
+
+ void needRefresh()
+ {
+ refresh = true;
+ }
+
+ Result update(Paint* paint, bool force)
+ {
+ if (paints.count == 0 || drawing || !renderer) return Result::InsufficientCondition;
+
+ Array<RenderData> clips;
+ auto flag = RenderUpdateFlag::None;
+ if (refresh || force) flag = RenderUpdateFlag::All;
+
+ //Update single paint node
+ if (paint) {
+ //Optimize Me: Can we skip the searching?
+ for (auto paint2 = paints.data; paint2 < (paints.data + paints.count); ++paint2) {
+ if ((*paint2) == paint) {
+ paint->pImpl->update(*renderer, nullptr, 255, clips, flag);
+ return Result::Success;
+ }
+ }
+ return Result::InvalidArguments;
+ //Update all retained paint nodes
+ } else {
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ (*paint)->pImpl->update(*renderer, nullptr, 255, clips, flag);
+ }
+ }
+
+ refresh = false;
+
+ return Result::Success;
+ }
+
+ Result draw()
+ {
+ if (drawing || paints.count == 0 || !renderer || !renderer->preRender()) return Result::InsufficientCondition;
+
+ bool rendered = false;
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ if ((*paint)->pImpl->render(*renderer)) rendered = true;
+ }
+
+ if (!rendered || !renderer->postRender()) return Result::InsufficientCondition;
+
+ drawing = true;
+
+ return Result::Success;
+ }
+
+ Result sync()
+ {
+ if (!drawing) return Result::InsufficientCondition;
+
+ if (renderer->sync()) {
+ drawing = false;
+ return Result::Success;
+ }
+
+ return Result::InsufficientCondition;
+ }
+};
+
+#endif /* _TVG_CANVAS_IMPL_H_ */
diff --git a/thirdparty/thorvg/src/lib/tvgCommon.h b/thirdparty/thorvg/src/lib/tvgCommon.h
new file mode 100644
index 0000000000..b1c586188c
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgCommon.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_COMMON_H_
+#define _TVG_COMMON_H_
+
+#include "config.h"
+#include "thorvg.h"
+
+using namespace std;
+using namespace tvg;
+
+//for MSVC Compat
+#ifdef _MSC_VER
+ #define TVG_UNUSED
+ #define strncasecmp _strnicmp
+ #define strcasecmp _stricmp
+#else
+ #define TVG_UNUSED __attribute__ ((__unused__))
+#endif
+
+// Portable 'fallthrough' attribute
+#if __has_cpp_attribute(fallthrough)
+ #ifdef _MSC_VER
+ #define TVG_FALLTHROUGH [[fallthrough]];
+ #else
+ #define TVG_FALLTHROUGH __attribute__ ((fallthrough));
+ #endif
+#else
+ #define TVG_FALLTHROUGH
+#endif
+
+#if defined(_MSC_VER) && defined(__clang__)
+ #define strncpy strncpy_s
+ #define strdup _strdup
+#endif
+
+//TVG class identifier values
+#define TVG_CLASS_ID_UNDEFINED 0
+#define TVG_CLASS_ID_SHAPE 1
+#define TVG_CLASS_ID_SCENE 2
+#define TVG_CLASS_ID_PICTURE 3
+#define TVG_CLASS_ID_LINEAR 4
+#define TVG_CLASS_ID_RADIAL 5
+
+enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown };
+
+#ifdef THORVG_LOG_ENABLED
+ #define TVGLOG(tag, fmt, ...) fprintf(stdout, tag ": " fmt "\n", ##__VA_ARGS__) //Log Message for notifying user some useful info
+ #define TVGERR(tag, fmt, ...) fprintf(stderr, tag ": " fmt "\n", ##__VA_ARGS__) //Error Message for us to fix it
+#else
+ #define TVGERR(...)
+ #define TVGLOG(...)
+#endif
+
+uint16_t THORVG_VERSION_NUMBER();
+
+#endif //_TVG_COMMON_H_
diff --git a/thirdparty/thorvg/src/lib/tvgFill.cpp b/thirdparty/thorvg/src/lib/tvgFill.cpp
new file mode 100644
index 0000000000..4bfb93c102
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgFill.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgFill.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Fill::Fill():pImpl(new Impl())
+{
+}
+
+
+Fill::~Fill()
+{
+ delete(pImpl);
+}
+
+
+Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept
+{
+ if ((!colorStops && cnt > 0) || (colorStops && cnt == 0)) return Result::InvalidArguments;
+
+ if (cnt == 0) {
+ if (pImpl->colorStops) {
+ free(pImpl->colorStops);
+ pImpl->colorStops = nullptr;
+ pImpl->cnt = 0;
+ }
+ return Result::Success;
+ }
+
+ if (pImpl->cnt != cnt) {
+ pImpl->colorStops = static_cast<ColorStop*>(realloc(pImpl->colorStops, cnt * sizeof(ColorStop)));
+ }
+
+ pImpl->cnt = cnt;
+ memcpy(pImpl->colorStops, colorStops, cnt * sizeof(ColorStop));
+
+ return Result::Success;
+}
+
+
+uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
+{
+ if (colorStops) *colorStops = pImpl->colorStops;
+
+ return pImpl->cnt;
+}
+
+
+Result Fill::spread(FillSpread s) noexcept
+{
+ pImpl->spread = s;
+
+ return Result::Success;
+}
+
+
+FillSpread Fill::spread() const noexcept
+{
+ return pImpl->spread;
+}
+
+
+Result Fill::transform(const Matrix& m) noexcept
+{
+ if (!pImpl->transform) {
+ pImpl->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
+ }
+ *pImpl->transform = m;
+ return Result::Success;
+}
+
+
+Matrix Fill::transform() const noexcept
+{
+ if (pImpl->transform) return *pImpl->transform;
+ return {1, 0, 0, 0, 1, 0, 0, 0, 1};
+}
+
+
+Fill* Fill::duplicate() const noexcept
+{
+ return pImpl->duplicate();
+}
+
+uint32_t Fill::identifier() const noexcept
+{
+ return pImpl->id;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgFill.h b/thirdparty/thorvg/src/lib/tvgFill.h
new file mode 100644
index 0000000000..912091f8cb
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgFill.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_FILL_H_
+#define _TVG_FILL_H_
+
+#include <cstdlib>
+#include <cstring>
+#include "tvgCommon.h"
+
+template<typename T>
+struct DuplicateMethod
+{
+ virtual ~DuplicateMethod() {}
+ virtual T* duplicate() = 0;
+};
+
+template<class T>
+struct FillDup : DuplicateMethod<Fill>
+{
+ T* inst = nullptr;
+
+ FillDup(T* _inst) : inst(_inst) {}
+ ~FillDup() {}
+
+ Fill* duplicate() override
+ {
+ return inst->duplicate();
+ }
+};
+
+struct Fill::Impl
+{
+ ColorStop* colorStops = nullptr;
+ Matrix* transform = nullptr;
+ uint32_t cnt = 0;
+ FillSpread spread;
+ DuplicateMethod<Fill>* dup = nullptr;
+ uint32_t id;
+
+ ~Impl()
+ {
+ if (dup) delete(dup);
+ free(colorStops);
+ free(transform);
+ }
+
+ void method(DuplicateMethod<Fill>* dup)
+ {
+ this->dup = dup;
+ }
+
+ Fill* duplicate()
+ {
+ auto ret = dup->duplicate();
+ if (!ret) return nullptr;
+
+ ret->pImpl->cnt = cnt;
+ ret->pImpl->spread = spread;
+ ret->pImpl->colorStops = static_cast<ColorStop*>(malloc(sizeof(ColorStop) * cnt));
+ memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt);
+ if (transform) {
+ ret->pImpl->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
+ *ret->pImpl->transform = *transform;
+ }
+ return ret;
+ }
+};
+
+#endif //_TVG_FILL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp b/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp
new file mode 100644
index 0000000000..3a88b6d799
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgCanvasImpl.h"
+
+#ifdef THORVG_GL_RASTER_SUPPORT
+ #include "tvgGlRenderer.h"
+#else
+ class GlRenderer : public RenderMethod
+ {
+ //Non Supported. Dummy Class */
+ };
+#endif
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct GlCanvas::Impl
+{
+};
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+#ifdef THORVG_GL_RASTER_SUPPORT
+GlCanvas::GlCanvas() : Canvas(GlRenderer::gen()), pImpl(new Impl)
+#else
+GlCanvas::GlCanvas() : Canvas(nullptr), pImpl(new Impl)
+#endif
+{
+}
+
+
+
+GlCanvas::~GlCanvas()
+{
+ delete(pImpl);
+}
+
+
+Result GlCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept
+{
+#ifdef THORVG_GL_RASTER_SUPPORT
+ //We know renderer type, avoid dynamic_cast for performance.
+ auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer);
+ if (!renderer) return Result::MemoryCorruption;
+
+ if (!renderer->target(buffer, stride, w, h)) return Result::Unknown;
+
+ //Paints must be updated again with this new target.
+ Canvas::pImpl->needRefresh();
+
+ return Result::Success;
+#endif
+ return Result::NonSupport;
+}
+
+
+unique_ptr<GlCanvas> GlCanvas::gen() noexcept
+{
+#ifdef THORVG_GL_RASTER_SUPPORT
+ if (GlRenderer::init() <= 0) return nullptr;
+ return unique_ptr<GlCanvas>(new GlCanvas);
+#endif
+ return nullptr;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgInitializer.cpp b/thirdparty/thorvg/src/lib/tvgInitializer.cpp
new file mode 100644
index 0000000000..83ec50be95
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgInitializer.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgCommon.h"
+#include "tvgTaskScheduler.h"
+#include "tvgLoader.h"
+
+#ifdef _WIN32
+ #include <cstring>
+#endif
+
+#ifdef THORVG_SW_RASTER_SUPPORT
+ #include "tvgSwRenderer.h"
+#endif
+
+#ifdef THORVG_GL_RASTER_SUPPORT
+ #include "tvgGlRenderer.h"
+#endif
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static int _initCnt = 0;
+static uint16_t _version = 0;
+
+
+static bool _buildVersionInfo()
+{
+ auto SRC = THORVG_VERSION_STRING; //ex) 0.3.99
+ auto p = SRC;
+ const char* x;
+
+ char major[3];
+ x = strchr(p, '.');
+ if (!x) return false;
+ memcpy(major, p, x - p);
+ major[x - p] = '\0';
+ p = x + 1;
+
+ char minor[3];
+ x = strchr(p, '.');
+ if (!x) return false;
+ memcpy(minor, p, x - p);
+ minor[x - p] = '\0';
+ p = x + 1;
+
+ char micro[3];
+ x = SRC + strlen(THORVG_VERSION_STRING);
+ memcpy(micro, p, x - p);
+ micro[x - p] = '\0';
+
+ char sum[7];
+ snprintf(sum, sizeof(sum), "%s%s%s", major, minor, micro);
+
+ _version = atoi(sum);
+
+ return true;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept
+{
+ auto nonSupport = true;
+
+ if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Sw)) {
+ #ifdef THORVG_SW_RASTER_SUPPORT
+ if (!SwRenderer::init(threads)) return Result::FailedAllocation;
+ nonSupport = false;
+ #endif
+ } else if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Gl)) {
+ #ifdef THORVG_GL_RASTER_SUPPORT
+ if (!GlRenderer::init(threads)) return Result::FailedAllocation;
+ nonSupport = false;
+ #endif
+ } else {
+ return Result::InvalidArguments;
+ }
+
+ if (nonSupport) return Result::NonSupport;
+
+ if (_initCnt++ > 0) return Result::Success;
+
+ if (!_buildVersionInfo()) return Result::Unknown;
+
+ if (!LoaderMgr::init()) return Result::Unknown;
+
+ TaskScheduler::init(threads);
+
+ return Result::Success;
+}
+
+
+Result Initializer::term(CanvasEngine engine) noexcept
+{
+ if (_initCnt == 0) return Result::InsufficientCondition;
+
+ auto nonSupport = true;
+
+ if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Sw)) {
+ #ifdef THORVG_SW_RASTER_SUPPORT
+ if (!SwRenderer::term()) return Result::InsufficientCondition;
+ nonSupport = false;
+ #endif
+ } else if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Gl)) {
+ #ifdef THORVG_GL_RASTER_SUPPORT
+ if (!GlRenderer::term()) return Result::InsufficientCondition;
+ nonSupport = false;
+ #endif
+ } else {
+ return Result::InvalidArguments;
+ }
+
+ if (nonSupport) return Result::NonSupport;
+
+ if (--_initCnt > 0) return Result::Success;
+
+ TaskScheduler::term();
+
+ if (!LoaderMgr::term()) return Result::Unknown;
+
+ return Result::Success;
+}
+
+
+uint16_t THORVG_VERSION_NUMBER()
+{
+ return _version;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h b/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h
new file mode 100644
index 0000000000..10b55a536d
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_ITERATOR_ACCESSOR_H_
+#define _TVG_ITERATOR_ACCESSOR_H_
+
+#include "tvgPaint.h"
+
+namespace tvg
+{
+
+class IteratorAccessor
+{
+public:
+ //Utility Method: Iterator Accessor
+ Iterator* iterator(const Paint* paint)
+ {
+ return paint->pImpl->iterator();
+ }
+};
+
+}
+
+#endif //_TVG_ITERATOR_ACCESSOR_H_
diff --git a/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp b/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
new file mode 100644
index 0000000000..6ec7ddab20
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <float.h>
+#include <math.h>
+#include "tvgFill.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct LinearGradient::Impl
+{
+ float x1 = 0;
+ float y1 = 0;
+ float x2 = 0;
+ float y2 = 0;
+
+ Fill* duplicate()
+ {
+ auto ret = LinearGradient::gen();
+ if (!ret) return nullptr;
+
+ ret->pImpl->x1 = x1;
+ ret->pImpl->y1 = y1;
+ ret->pImpl->x2 = x2;
+ ret->pImpl->y2 = y2;
+
+ return ret.release();
+ }
+};
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+LinearGradient::LinearGradient():pImpl(new Impl())
+{
+ Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
+ Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
+}
+
+
+LinearGradient::~LinearGradient()
+{
+ delete(pImpl);
+}
+
+
+Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
+{
+ pImpl->x1 = x1;
+ pImpl->y1 = y1;
+ pImpl->x2 = x2;
+ pImpl->y2 = y2;
+
+ return Result::Success;
+}
+
+
+Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
+{
+ if (x1) *x1 = pImpl->x1;
+ if (x2) *x2 = pImpl->x2;
+ if (y1) *y1 = pImpl->y1;
+ if (y2) *y2 = pImpl->y2;
+
+ return Result::Success;
+}
+
+
+unique_ptr<LinearGradient> LinearGradient::gen() noexcept
+{
+ return unique_ptr<LinearGradient>(new LinearGradient);
+}
+
+
+uint32_t LinearGradient::identifier() noexcept
+{
+ return TVG_CLASS_ID_LINEAR;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgLoadModule.h b/thirdparty/thorvg/src/lib/tvgLoadModule.h
new file mode 100644
index 0000000000..0c34ecbf6a
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLoadModule.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_LOAD_MODULE_H_
+#define _TVG_LOAD_MODULE_H_
+
+#include "tvgRender.h"
+
+namespace tvg
+{
+
+class LoadModule
+{
+public:
+ //default view box, if any.
+ float vx = 0;
+ float vy = 0;
+ float vw = 0;
+ float vh = 0;
+ float w = 0, h = 0; //default image size
+ bool preserveAspect = true; //keep aspect ratio by default.
+
+ virtual ~LoadModule() {}
+
+ virtual bool open(const string& path) { return false; }
+ virtual bool open(const char* data, uint32_t size, bool copy) { return false; }
+ virtual bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) { return false; }
+
+ //Override this if the vector-format has own resizing policy.
+ virtual bool resize(Paint* paint, float w, float h) { return false; }
+
+ virtual bool read() = 0;
+ virtual bool close() = 0;
+ virtual unique_ptr<Surface> bitmap() { return nullptr; }
+ virtual unique_ptr<Paint> paint() { return nullptr; }
+};
+
+}
+
+#endif //_TVG_LOAD_MODULE_H_
diff --git a/thirdparty/thorvg/src/lib/tvgLoader.cpp b/thirdparty/thorvg/src/lib/tvgLoader.cpp
new file mode 100644
index 0000000000..fb93e0faec
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLoader.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgLoader.h"
+
+#ifdef THORVG_SVG_LOADER_SUPPORT
+ #include "tvgSvgLoader.h"
+#endif
+
+#ifdef THORVG_PNG_LOADER_SUPPORT
+ #include "tvgPngLoader.h"
+#endif
+
+#ifdef THORVG_TVG_LOADER_SUPPORT
+ #include "tvgTvgLoader.h"
+#endif
+
+#ifdef THORVG_JPG_LOADER_SUPPORT
+ #include "tvgJpgLoader.h"
+#endif
+
+#include "tvgRawLoader.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static LoadModule* _find(FileType type)
+{
+ switch(type) {
+ case FileType::Tvg: {
+#ifdef THORVG_TVG_LOADER_SUPPORT
+ return new TvgLoader;
+#endif
+ break;
+ }
+ case FileType::Svg: {
+#ifdef THORVG_SVG_LOADER_SUPPORT
+ return new SvgLoader;
+#endif
+ break;
+ }
+ case FileType::Raw: {
+ return new RawLoader;
+ break;
+ }
+ case FileType::Png: {
+#ifdef THORVG_PNG_LOADER_SUPPORT
+ return new PngLoader;
+#endif
+ break;
+ }
+ case FileType::Jpg: {
+#ifdef THORVG_JPG_LOADER_SUPPORT
+ return new JpgLoader;
+#endif
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+#ifdef THORVG_LOG_ENABLED
+ const char *format;
+ switch(type) {
+ case FileType::Tvg: {
+ format = "TVG";
+ break;
+ }
+ case FileType::Svg: {
+ format = "SVG";
+ break;
+ }
+ case FileType::Raw: {
+ format = "RAW";
+ break;
+ }
+ case FileType::Png: {
+ format = "PNG";
+ break;
+ }
+ case FileType::Jpg: {
+ format = "JPG";
+ break;
+ }
+ default: {
+ format = "???";
+ break;
+ }
+ }
+ TVGLOG("LOADER", "%s format is not supported", format);
+#endif
+ return nullptr;
+}
+
+
+static LoadModule* _findByPath(const string& path)
+{
+ auto ext = path.substr(path.find_last_of(".") + 1);
+ if (!ext.compare("tvg")) return _find(FileType::Tvg);
+ if (!ext.compare("svg")) return _find(FileType::Svg);
+ if (!ext.compare("png")) return _find(FileType::Png);
+ if (!ext.compare("jpg")) return _find(FileType::Jpg);
+ return nullptr;
+}
+
+
+static LoadModule* _findByType(const string& mimeType)
+{
+ if (mimeType.empty()) return nullptr;
+
+ auto type = FileType::Unknown;
+
+ if (mimeType == "tvg") type = FileType::Tvg;
+ else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
+ else if (mimeType == "raw") type = FileType::Raw;
+ else if (mimeType == "png") type = FileType::Png;
+ else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
+ else {
+ TVGLOG("LOADER", "Given mimetype is unknown = \"%s\".", mimeType.c_str());
+ return nullptr;
+ }
+
+ return _find(type);
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+
+bool LoaderMgr::init()
+{
+ //TODO:
+
+ return true;
+}
+
+
+bool LoaderMgr::term()
+{
+ //TODO:
+
+ return true;
+}
+
+
+shared_ptr<LoadModule> LoaderMgr::loader(const string& path, bool* invalid)
+{
+ *invalid = false;
+
+ if (auto loader = _findByPath(path)) {
+ if (loader->open(path)) return shared_ptr<LoadModule>(loader);
+ else delete(loader);
+ *invalid = true;
+ }
+ return nullptr;
+}
+
+
+shared_ptr<LoadModule> LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
+{
+ //Try first with the given MimeType
+ if (auto loader = _findByType(mimeType)) {
+ if (loader->open(data, size, copy)) {
+ return shared_ptr<LoadModule>(loader);
+ } else {
+ TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
+ delete(loader);
+ }
+ }
+
+ //Abnormal MimeType. Try with the candidates in the order
+ for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
+ auto loader = _find(static_cast<FileType>(i));
+ if (loader) {
+ if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
+ else delete(loader);
+ }
+ }
+ return nullptr;
+}
+
+
+shared_ptr<LoadModule> LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool copy)
+{
+ //function is dedicated for raw images only
+ auto loader = new RawLoader;
+ if (loader->open(data, w, h, copy)) return shared_ptr<LoadModule>(loader);
+ else delete(loader);
+
+ return nullptr;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgLoader.h b/thirdparty/thorvg/src/lib/tvgLoader.h
new file mode 100644
index 0000000000..8ba3c139fa
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLoader.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_LOADER_H_
+#define _TVG_LOADER_H_
+
+#include "tvgLoadModule.h"
+
+struct LoaderMgr
+{
+ static bool init();
+ static bool term();
+ static shared_ptr<LoadModule> loader(const string& path, bool* invalid);
+ static shared_ptr<LoadModule> loader(const char* data, uint32_t size, const string& mimeType, bool copy);
+ static shared_ptr<LoadModule> loader(const uint32_t* data, uint32_t w, uint32_t h, bool copy);
+};
+
+#endif //_TVG_LOADER_H_
diff --git a/thirdparty/thorvg/src/lib/tvgLzw.cpp b/thirdparty/thorvg/src/lib/tvgLzw.cpp
new file mode 100644
index 0000000000..0049c89962
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLzw.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Lempel–Ziv–Welch (LZW) encoder/decoder by Guilherme R. Lampert(guilherme.ronaldo.lampert@gmail.com)
+
+ * This is the compression scheme used by the GIF image format and the Unix 'compress' tool.
+ * Main differences from this implementation is that End Of Input (EOI) and Clear Codes (CC)
+ * are not stored in the output and the max code length in bits is 12, vs 16 in compress.
+ *
+ * EOI is simply detected by the end of the data stream, while CC happens if the
+ * dictionary gets filled. Data is written/read from bit streams, which handle
+ * byte-alignment for us in a transparent way.
+
+ * The decoder relies on the hardcoded data layout produced by the encoder, since
+ * no additional reconstruction data is added to the output, so they must match.
+ * The nice thing about LZW is that we can reconstruct the dictionary directly from
+ * the stream of codes generated by the encoder, so this avoids storing additional
+ * headers in the bit stream.
+
+ * The output code length is variable. It starts with the minimum number of bits
+ * required to store the base byte-sized dictionary and automatically increases
+ * as the dictionary gets larger (it starts at 9-bits and grows to 10-bits when
+ * code 512 is added, then 11-bits when 1024 is added, and so on). If the dictionary
+ * is filled (4096 items for a 12-bits dictionary), the whole thing is cleared and
+ * the process starts over. This is the main reason why the encoder and the decoder
+ * must match perfectly, since the lengths of the codes will not be specified with
+ * the data itself.
+
+ * USEFUL LINKS:
+ * https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
+ * http://rosettacode.org/wiki/LZW_compression
+ * http://www.cs.duke.edu/csed/curious/compression/lzw.html
+ * http://www.cs.cf.ac.uk/Dave/Multimedia/node214.html
+ * http://marknelson.us/1989/10/01/lzw-data-compression/
+ */
+#include "config.h"
+
+#if defined(THORVG_TVG_SAVER_SUPPORT) || defined(THORVG_TVG_LOADER_SUPPORT)
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+#include <string>
+#include <memory.h>
+#include "tvgLzw.h"
+
+namespace {
+//LZW Dictionary helper:
+constexpr int Nil = -1;
+constexpr int MaxDictBits = 12;
+constexpr int StartBits = 9;
+constexpr int FirstCode = (1 << (StartBits - 1)); // 256
+constexpr int MaxDictEntries = (1 << MaxDictBits); // 4096
+
+
+//Round up to the next power-of-two number, e.g. 37 => 64
+static int nextPowerOfTwo(int num)
+{
+ --num;
+ for (size_t i = 1; i < sizeof(num) * 8; i <<= 1) {
+ num = num | num >> i;
+ }
+ return ++num;
+}
+
+
+struct BitStreamWriter
+{
+ uint8_t* stream; //Growable buffer to store our bits. Heap allocated & owned by the class instance.
+ int bytesAllocated; //Current size of heap-allocated stream buffer *in bytes*.
+ int granularity; //Amount bytesAllocated multiplies by when auto-resizing in appendBit().
+ int currBytePos; //Current byte being written to, from 0 to bytesAllocated-1.
+ int nextBitPos; //Bit position within the current byte to access next. 0 to 7.
+ int numBitsWritten; //Number of bits in use from the stream buffer, not including byte-rounding padding.
+
+ void internalInit()
+ {
+ stream = nullptr;
+ bytesAllocated = 0;
+ granularity = 2;
+ currBytePos = 0;
+ nextBitPos = 0;
+ numBitsWritten = 0;
+ }
+
+ uint8_t* allocBytes(const int bytesWanted, uint8_t * oldPtr, const int oldSize)
+ {
+ auto newMemory = static_cast<uint8_t *>(malloc(bytesWanted));
+ memset(newMemory, 0, bytesWanted);
+
+ if (oldPtr) {
+ memcpy(newMemory, oldPtr, oldSize);
+ free(oldPtr);
+ }
+ return newMemory;
+ }
+
+ BitStreamWriter()
+ {
+ /* 8192 bits for a start (1024 bytes). It will resize if needed.
+ Default granularity is 2. */
+ internalInit();
+ allocate(8192);
+ }
+
+ BitStreamWriter(const int initialSizeInBits, const int growthGranularity = 2)
+ {
+ internalInit();
+ setGranularity(growthGranularity);
+ allocate(initialSizeInBits);
+ }
+
+ ~BitStreamWriter()
+ {
+ free(stream);
+ }
+
+ void allocate(int bitsWanted)
+ {
+ //Require at least a byte.
+ if (bitsWanted <= 0) bitsWanted = 8;
+
+ //Round upwards if needed:
+ if ((bitsWanted % 8) != 0) bitsWanted = nextPowerOfTwo(bitsWanted);
+
+ //We might already have the required count.
+ const int sizeInBytes = bitsWanted / 8;
+ if (sizeInBytes <= bytesAllocated) return;
+
+ stream = allocBytes(sizeInBytes, stream, bytesAllocated);
+ bytesAllocated = sizeInBytes;
+ }
+
+ void appendBit(const int bit)
+ {
+ const uint32_t mask = uint32_t(1) << nextBitPos;
+ stream[currBytePos] = (stream[currBytePos] & ~mask) | (-bit & mask);
+ ++numBitsWritten;
+
+ if (++nextBitPos == 8) {
+ nextBitPos = 0;
+ if (++currBytePos == bytesAllocated) allocate(bytesAllocated * granularity * 8);
+ }
+ }
+
+ void appendBitsU64(const uint64_t num, const int bitCount)
+ {
+ for (int b = 0; b < bitCount; ++b) {
+ const uint64_t mask = uint64_t(1) << b;
+ const int bit = !!(num & mask);
+ appendBit(bit);
+ }
+ }
+
+ uint8_t* release()
+ {
+ auto oldPtr = stream;
+ internalInit();
+ return oldPtr;
+ }
+
+ void setGranularity(const int growthGranularity)
+ {
+ granularity = (growthGranularity >= 2) ? growthGranularity : 2;
+ }
+
+ int getByteCount() const
+ {
+ int usedBytes = numBitsWritten / 8;
+ int leftovers = numBitsWritten % 8;
+ if (leftovers != 0) ++usedBytes;
+ return usedBytes;
+ }
+};
+
+
+struct BitStreamReader
+{
+ const uint8_t* stream; // Pointer to the external bit stream. Not owned by the reader.
+ const int sizeInBytes; // Size of the stream *in bytes*. Might include padding.
+ const int sizeInBits; // Size of the stream *in bits*, padding *not* include.
+ int currBytePos = 0; // Current byte being read in the stream.
+ int nextBitPos = 0; // Bit position within the current byte to access next. 0 to 7.
+ int numBitsRead = 0; // Total bits read from the stream so far. Never includes byte-rounding padding.
+
+ BitStreamReader(const uint8_t* bitStream, const int byteCount, const int bitCount) : stream(bitStream), sizeInBytes(byteCount), sizeInBits(bitCount)
+ {
+ }
+
+ bool readNextBit(int& bitOut)
+ {
+ if (numBitsRead >= sizeInBits) return false; //We are done.
+
+ const uint32_t mask = uint32_t(1) << nextBitPos;
+ bitOut = !!(stream[currBytePos] & mask);
+ ++numBitsRead;
+
+ if (++nextBitPos == 8) {
+ nextBitPos = 0;
+ ++currBytePos;
+ }
+ return true;
+ }
+
+ uint64_t readBitsU64(const int bitCount)
+ {
+ uint64_t num = 0;
+ for (int b = 0; b < bitCount; ++b) {
+ int bit;
+ if (!readNextBit(bit)) break;
+ /* Based on a "Stanford bit-hack":
+ http://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching */
+ const uint64_t mask = uint64_t(1) << b;
+ num = (num & ~mask) | (-bit & mask);
+ }
+ return num;
+ }
+
+ bool isEndOfStream() const
+ {
+ return numBitsRead >= sizeInBits;
+ }
+};
+
+
+struct Dictionary
+{
+ struct Entry
+ {
+ int code;
+ int value;
+ };
+
+ //Dictionary entries 0-255 are always reserved to the byte/ASCII range.
+ int size;
+ Entry entries[MaxDictEntries];
+
+ Dictionary()
+ {
+ /* First 256 dictionary entries are reserved to the byte/ASCII range.
+ Additional entries follow for the character sequences found in the input.
+ Up to 4096 - 256 (MaxDictEntries - FirstCode). */
+ size = FirstCode;
+
+ for (int i = 0; i < size; ++i) {
+ entries[i].code = Nil;
+ entries[i].value = i;
+ }
+ }
+
+ int findIndex(const int code, const int value) const
+ {
+ if (code == Nil) return value;
+
+ //Linear search for now.
+ //TODO: Worth optimizing with a proper hash-table?
+ for (int i = 0; i < size; ++i) {
+ if (entries[i].code == code && entries[i].value == value) return i;
+ }
+ return Nil;
+ }
+
+ bool add(const int code, const int value)
+ {
+ if (size == MaxDictEntries) return false;
+ entries[size].code = code;
+ entries[size].value = value;
+ ++size;
+ return true;
+ }
+
+ bool flush(int & codeBitsWidth)
+ {
+ if (size == (1 << codeBitsWidth)) {
+ ++codeBitsWidth;
+ if (codeBitsWidth > MaxDictBits) {
+ //Clear the dictionary (except the first 256 byte entries).
+ codeBitsWidth = StartBits;
+ size = FirstCode;
+ return true;
+ }
+ }
+ return false;
+ }
+};
+
+
+static bool outputByte(int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar)
+{
+ if (bytesDecodedSoFar >= outputSizeBytes) return false;
+ *output++ = static_cast<uint8_t>(code);
+ ++bytesDecodedSoFar;
+ return true;
+}
+
+
+static bool outputSequence(const Dictionary& dict, int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar, int& firstByte)
+{
+ /* A sequence is stored backwards, so we have to write
+ it to a temp then output the buffer in reverse. */
+ int i = 0;
+ uint8_t sequence[MaxDictEntries];
+
+ do {
+ sequence[i++] = dict.entries[code].value;
+ code = dict.entries[code].code;
+ } while (code >= 0);
+
+ firstByte = sequence[--i];
+
+ for (; i >= 0; --i) {
+ if (!outputByte(sequence[i], output, outputSizeBytes, bytesDecodedSoFar)) return false;
+ }
+ return true;
+}
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+namespace tvg {
+
+uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes)
+{
+ int code = Nil;
+ int prevCode = Nil;
+ int firstByte = 0;
+ int bytesDecoded = 0;
+ int codeBitsWidth = StartBits;
+ auto uncompressed = (uint8_t*) malloc(sizeof(uint8_t) * uncompressedSizeBytes);
+ auto ptr = uncompressed;
+
+ /* We'll reconstruct the dictionary based on the bit stream codes.
+ Unlike Huffman encoding, we don't store the dictionary as a prefix to the data. */
+ Dictionary dictionary;
+ BitStreamReader bitStream(compressed, compressedSizeBytes, compressedSizeBits);
+
+ /* We check to avoid an overflow of the user buffer.
+ If the buffer is smaller than the decompressed size, we break the loop and return the current decompression count. */
+ while (!bitStream.isEndOfStream()) {
+ code = static_cast<int>(bitStream.readBitsU64(codeBitsWidth));
+
+ if (prevCode == Nil) {
+ if (!outputByte(code, ptr, uncompressedSizeBytes, bytesDecoded)) break;
+ firstByte = code;
+ prevCode = code;
+ continue;
+ }
+ if (code >= dictionary.size) {
+ if (!outputSequence(dictionary, prevCode, ptr, uncompressedSizeBytes, bytesDecoded, firstByte)) break;
+ if (!outputByte(firstByte, ptr, uncompressedSizeBytes, bytesDecoded)) break;
+ } else if (!outputSequence(dictionary, code, ptr, uncompressedSizeBytes, bytesDecoded, firstByte)) break;
+
+ dictionary.add(prevCode, firstByte);
+ if (dictionary.flush(codeBitsWidth)) prevCode = Nil;
+ else prevCode = code;
+ }
+
+ return uncompressed;
+}
+
+
+uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits)
+{
+ //LZW encoding context:
+ int code = Nil;
+ int codeBitsWidth = StartBits;
+ Dictionary dictionary;
+
+ //Output bit stream we write to. This will allocate memory as needed to accommodate the encoded data.
+ BitStreamWriter bitStream;
+
+ for (; uncompressedSizeBytes > 0; --uncompressedSizeBytes, ++uncompressed) {
+ const int value = *uncompressed;
+ const int index = dictionary.findIndex(code, value);
+
+ if (index != Nil) {
+ code = index;
+ continue;
+ }
+
+ //Write the dictionary code using the minimum bit-with:
+ bitStream.appendBitsU64(code, codeBitsWidth);
+
+ //Flush it when full so we can restart the sequences.
+ if (!dictionary.flush(codeBitsWidth)) {
+ //There's still space for this sequence.
+ dictionary.add(code, value);
+ }
+ code = value;
+ }
+
+ //Residual code at the end:
+ if (code != Nil) bitStream.appendBitsU64(code, codeBitsWidth);
+
+ //Pass ownership of the compressed data buffer to the user pointer:
+ *compressedSizeBytes = bitStream.getByteCount();
+ *compressedSizeBits = bitStream.numBitsWritten;
+
+ return bitStream.release();
+}
+
+}
+
+#endif
diff --git a/thirdparty/thorvg/src/lib/tvgLzw.h b/thirdparty/thorvg/src/lib/tvgLzw.h
new file mode 100644
index 0000000000..3fdb439a02
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLzw.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_LZW_H_
+#define _TVG_LZW_H_
+
+namespace tvg
+{
+ uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
+ uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
+}
+
+#endif //_TVG_LZW_H \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/lib/tvgMath.h
new file mode 100644
index 0000000000..423fb6eb1b
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgMath.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_MATH_H_
+#define _TVG_MATH_H_
+
+ #define _USE_MATH_DEFINES
+
+#include <float.h>
+#include <math.h>
+#include "tvgCommon.h"
+
+
+#define mathMin(x, y) (((x) < (y)) ? (x) : (y))
+#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
+
+
+static inline bool mathZero(float a)
+{
+ return (fabsf(a) < FLT_EPSILON) ? true : false;
+}
+
+
+static inline bool mathEqual(float a, float b)
+{
+ return (fabsf(a - b) < FLT_EPSILON);
+}
+
+
+static inline bool mathRightAngle(const Matrix* m)
+{
+ auto radian = fabsf(atan2f(m->e21, m->e11));
+ if (radian < FLT_EPSILON || mathEqual(radian, float(M_PI_2)) || mathEqual(radian, float(M_PI))) return true;
+ return false;
+}
+
+
+static inline bool mathIdentity(const Matrix* m)
+{
+ if (!mathEqual(m->e11, 1.0f) || !mathZero(m->e12) || !mathZero(m->e13) ||
+ !mathZero(m->e21) || !mathEqual(m->e22, 1.0f) || !mathZero(m->e23) ||
+ !mathZero(m->e31) || !mathZero(m->e32) || !mathEqual(m->e33, 1.0f)) {
+ return false;
+ }
+ return true;
+}
+
+
+static inline bool mathInverse(const Matrix* m, Matrix* out)
+{
+ auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) -
+ m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) +
+ m->e13 * (m->e21 * m->e32 - m->e22 * m->e31);
+
+ if (mathZero(det)) return false;
+
+ auto invDet = 1 / det;
+
+ out->e11 = (m->e22 * m->e33 - m->e32 * m->e23) * invDet;
+ out->e12 = (m->e13 * m->e32 - m->e12 * m->e33) * invDet;
+ out->e13 = (m->e12 * m->e23 - m->e13 * m->e22) * invDet;
+ out->e21 = (m->e23 * m->e31 - m->e21 * m->e33) * invDet;
+ out->e22 = (m->e11 * m->e33 - m->e13 * m->e31) * invDet;
+ out->e23 = (m->e21 * m->e13 - m->e11 * m->e23) * invDet;
+ out->e31 = (m->e21 * m->e32 - m->e31 * m->e22) * invDet;
+ out->e32 = (m->e31 * m->e12 - m->e11 * m->e32) * invDet;
+ out->e33 = (m->e11 * m->e22 - m->e21 * m->e12) * invDet;
+
+ return true;
+}
+
+
+static inline void mathIdentity(Matrix* m)
+{
+ m->e11 = 1.0f;
+ m->e12 = 0.0f;
+ m->e13 = 0.0f;
+ m->e21 = 0.0f;
+ m->e22 = 1.0f;
+ m->e23 = 0.0f;
+ m->e31 = 0.0f;
+ m->e32 = 0.0f;
+ m->e33 = 1.0f;
+}
+
+
+static inline void mathScale(Matrix* m, float scale)
+{
+ m->e11 = scale;
+ m->e22 = scale;
+}
+
+
+static inline void mathTranslate(Matrix* m, float x, float y)
+{
+ m->e13 = x;
+ m->e23 = y;
+}
+
+
+static inline void mathRotate(Matrix* m, float degree)
+{
+ auto radian = degree / 180.0f * M_PI;
+ auto cosVal = cosf(radian);
+ auto sinVal = sinf(radian);
+
+ m->e12 = m->e11 * -sinVal;
+ m->e11 *= cosVal;
+ m->e21 = m->e22 * sinVal;
+ m->e22 *= cosVal;
+}
+
+
+static inline void mathMultiply(Point* pt, const Matrix* transform)
+{
+ auto tx = pt->x * transform->e11 + pt->y * transform->e12 + transform->e13;
+ auto ty = pt->x * transform->e21 + pt->y * transform->e22 + transform->e23;
+ pt->x = tx;
+ pt->y = ty;
+}
+
+
+static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
+{
+ Matrix m;
+
+ m.e11 = lhs->e11 * rhs->e11 + lhs->e12 * rhs->e21 + lhs->e13 * rhs->e31;
+ m.e12 = lhs->e11 * rhs->e12 + lhs->e12 * rhs->e22 + lhs->e13 * rhs->e32;
+ m.e13 = lhs->e11 * rhs->e13 + lhs->e12 * rhs->e23 + lhs->e13 * rhs->e33;
+
+ m.e21 = lhs->e21 * rhs->e11 + lhs->e22 * rhs->e21 + lhs->e23 * rhs->e31;
+ m.e22 = lhs->e21 * rhs->e12 + lhs->e22 * rhs->e22 + lhs->e23 * rhs->e32;
+ m.e23 = lhs->e21 * rhs->e13 + lhs->e22 * rhs->e23 + lhs->e23 * rhs->e33;
+
+ m.e31 = lhs->e31 * rhs->e11 + lhs->e32 * rhs->e21 + lhs->e33 * rhs->e31;
+ m.e32 = lhs->e31 * rhs->e12 + lhs->e32 * rhs->e22 + lhs->e33 * rhs->e32;
+ m.e33 = lhs->e31 * rhs->e13 + lhs->e32 * rhs->e23 + lhs->e33 * rhs->e33;
+
+ return m;
+}
+
+
+#endif //_TVG_MATH_H_
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.cpp b/thirdparty/thorvg/src/lib/tvgPaint.cpp
new file mode 100644
index 0000000000..30e82fbc60
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgPaint.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgPaint.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& viewport)
+{
+ /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */
+ auto shape = static_cast<Shape*>(cmpTarget);
+
+ //Rectangle Candidates?
+ const Point* pts;
+ if (shape->pathCoords(&pts) != 4) return false;
+
+ if (rTransform) rTransform->update();
+
+ //No rotational.
+ if (pTransform && !mathRightAngle(&pTransform->m)) return false;
+ if (rTransform && !mathRightAngle(&rTransform->m)) return false;
+
+ //Perpendicular Rectangle?
+ auto pt1 = pts + 0;
+ auto pt2 = pts + 1;
+ auto pt3 = pts + 2;
+ auto pt4 = pts + 3;
+
+ if ((mathEqual(pt1->x, pt2->x) && mathEqual(pt2->y, pt3->y) && mathEqual(pt3->x, pt4->x) && mathEqual(pt1->y, pt4->y)) ||
+ (mathEqual(pt2->x, pt3->x) && mathEqual(pt1->y, pt2->y) && mathEqual(pt1->x, pt4->x) && mathEqual(pt3->y, pt4->y))) {
+
+ auto v1 = *pt1;
+ auto v2 = *pt3;
+
+ if (rTransform) {
+ mathMultiply(&v1, &rTransform->m);
+ mathMultiply(&v2, &rTransform->m);
+ }
+
+ if (pTransform) {
+ mathMultiply(&v1, &pTransform->m);
+ mathMultiply(&v2, &pTransform->m);
+ }
+
+ //sorting
+ if (v1.x > v2.x) {
+ auto tmp = v2.x;
+ v2.x = v1.x;
+ v1.x = tmp;
+ }
+
+ if (v1.y > v2.y) {
+ auto tmp = v2.y;
+ v2.y = v1.y;
+ v1.y = tmp;
+ }
+
+ viewport.x = static_cast<int32_t>(v1.x);
+ viewport.y = static_cast<int32_t>(v1.y);
+ viewport.w = static_cast<int32_t>(v2.x - v1.x + 0.5f);
+ viewport.h = static_cast<int32_t>(v2.y - v1.y + 0.5f);
+
+ if (viewport.w < 0) viewport.w = 0;
+ if (viewport.h < 0) viewport.h = 0;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+Paint* Paint::Impl::duplicate()
+{
+ auto ret = smethod->duplicate();
+
+ //duplicate Transform
+ if (rTransform) {
+ ret->pImpl->rTransform = new RenderTransform();
+ *ret->pImpl->rTransform = *rTransform;
+ ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
+ }
+
+ ret->pImpl->opacity = opacity;
+
+ if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method);
+
+ return ret;
+}
+
+
+bool Paint::Impl::rotate(float degree)
+{
+ if (rTransform) {
+ if (mathEqual(degree, rTransform->degree)) return true;
+ } else {
+ if (mathZero(degree)) return true;
+ rTransform = new RenderTransform();
+ }
+ rTransform->degree = degree;
+ if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
+
+ return true;
+}
+
+
+bool Paint::Impl::scale(float factor)
+{
+ if (rTransform) {
+ if (mathEqual(factor, rTransform->scale)) return true;
+ } else {
+ if (mathZero(factor)) return true;
+ rTransform = new RenderTransform();
+ }
+ rTransform->scale = factor;
+ if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
+
+ return true;
+}
+
+
+bool Paint::Impl::translate(float x, float y)
+{
+ if (rTransform) {
+ if (mathEqual(x, rTransform->x) && mathEqual(y, rTransform->y)) return true;
+ } else {
+ if (mathZero(x) && mathZero(y)) return true;
+ rTransform = new RenderTransform();
+ }
+ rTransform->x = x;
+ rTransform->y = y;
+ if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
+
+ return true;
+}
+
+
+bool Paint::Impl::render(RenderMethod& renderer)
+{
+ Compositor* cmp = nullptr;
+
+ //OPTIMIZE_ME: Can we replace the simple AlphaMasking with ClipPath?
+
+ /* Note: only ClipPath is processed in update() step.
+ Create a composition image. */
+ if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
+ auto region = smethod->bounds(renderer);
+ if (region.w == 0 || region.h == 0) return true;
+ cmp = renderer.target(region);
+ renderer.beginComposite(cmp, CompositeMethod::None, 255);
+ compData->target->pImpl->render(renderer);
+ }
+
+ if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
+
+ auto ret = smethod->render(renderer);
+
+ if (cmp) renderer.endComposite(cmp);
+
+ return ret;
+}
+
+
+void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag)
+{
+ if (renderFlag & RenderUpdateFlag::Transform) {
+ if (!rTransform) return nullptr;
+ if (!rTransform->update()) {
+ delete(rTransform);
+ rTransform = nullptr;
+ }
+ }
+
+ /* 1. Composition Pre Processing */
+ void *tdata = nullptr;
+ RenderRegion viewport;
+ bool compFastTrack = false;
+
+ if (compData) {
+ auto target = compData->target;
+ auto method = compData->method;
+ target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset
+
+ /* If transform has no rotation factors && ClipPath / AlphaMasking is a simple rectangle,
+ we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */
+ auto tryFastTrack = false;
+ if (method == CompositeMethod::ClipPath) tryFastTrack = true;
+ else if (method == CompositeMethod::AlphaMask && target->identifier() == TVG_CLASS_ID_SHAPE) {
+ auto shape = static_cast<Shape*>(target);
+ uint8_t a;
+ shape->fillColor(nullptr, nullptr, nullptr, &a);
+ if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true;
+ }
+ if (tryFastTrack) {
+ RenderRegion viewport2;
+ if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2))) {
+ viewport = renderer.viewport();
+ viewport2.intersect(viewport);
+ renderer.viewport(viewport2);
+ target->pImpl->ctxFlag |= ContextFlag::FastTrack;
+ }
+ }
+ if (!compFastTrack) {
+ tdata = target->pImpl->update(renderer, pTransform, 255, clips, pFlag);
+ if (method == CompositeMethod::ClipPath) clips.push(tdata);
+ }
+ }
+
+ /* 2. Main Update */
+ void *edata = nullptr;
+ auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
+ renderFlag = RenderUpdateFlag::None;
+ opacity = (opacity * this->opacity) / 255;
+
+ if (rTransform && pTransform) {
+ RenderTransform outTransform(pTransform, rTransform);
+ edata = smethod->update(renderer, &outTransform, opacity, clips, newFlag);
+ } else {
+ auto outTransform = pTransform ? pTransform : rTransform;
+ edata = smethod->update(renderer, outTransform, opacity, clips, newFlag);
+ }
+
+ /* 3. Composition Post Processing */
+ if (compFastTrack) renderer.viewport(viewport);
+ else if (tdata && compData->method == CompositeMethod::ClipPath) clips.pop();
+
+ return edata;
+}
+
+
+bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed)
+{
+ Matrix* m = nullptr;
+
+ //Case: No transformed, quick return!
+ if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h);
+
+ //Case: Transformed
+ auto tx = 0.0f;
+ auto ty = 0.0f;
+ auto tw = 0.0f;
+ auto th = 0.0f;
+
+ auto ret = smethod->bounds(&tx, &ty, &tw, &th);
+
+ //Get vertices
+ Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}};
+
+ //New bounding box
+ auto x1 = FLT_MAX;
+ auto y1 = FLT_MAX;
+ auto x2 = -FLT_MAX;
+ auto y2 = -FLT_MAX;
+
+ //Compute the AABB after transformation
+ for (int i = 0; i < 4; i++) {
+ mathMultiply(&pt[i], m);
+
+ if (pt[i].x < x1) x1 = pt[i].x;
+ if (pt[i].x > x2) x2 = pt[i].x;
+ if (pt[i].y < y1) y1 = pt[i].y;
+ if (pt[i].y > y2) y2 = pt[i].y;
+ }
+
+ if (x) *x = x1;
+ if (y) *y = y1;
+ if (w) *w = x2 - x1;
+ if (h) *h = y2 - y1;
+
+ return ret;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Paint :: Paint() : pImpl(new Impl())
+{
+}
+
+
+Paint :: ~Paint()
+{
+ delete(pImpl);
+}
+
+
+Result Paint::rotate(float degree) noexcept
+{
+ if (pImpl->rotate(degree)) return Result::Success;
+ return Result::FailedAllocation;
+}
+
+
+Result Paint::scale(float factor) noexcept
+{
+ if (pImpl->scale(factor)) return Result::Success;
+ return Result::FailedAllocation;
+}
+
+
+Result Paint::translate(float x, float y) noexcept
+{
+ if (pImpl->translate(x, y)) return Result::Success;
+ return Result::FailedAllocation;
+}
+
+
+Result Paint::transform(const Matrix& m) noexcept
+{
+ if (pImpl->transform(m)) return Result::Success;
+ return Result::FailedAllocation;
+}
+
+
+Matrix Paint::transform() noexcept
+{
+ auto pTransform = pImpl->transform();
+ if (pTransform) return *pTransform;
+ return {1, 0, 0, 0, 1, 0, 0, 0, 1};
+}
+
+
+TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
+{
+ return this->bounds(x, y, w, h, false);
+}
+
+
+Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept
+{
+ if (pImpl->bounds(x, y, w, h, transform)) return Result::Success;
+ return Result::InsufficientCondition;
+}
+
+
+Paint* Paint::duplicate() const noexcept
+{
+ return pImpl->duplicate();
+}
+
+
+Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
+{
+ auto p = target.release();
+ if (pImpl->composite(this, p, method)) return Result::Success;
+ if (p) delete(p);
+ return Result::InvalidArguments;
+}
+
+
+CompositeMethod Paint::composite(const Paint** target) const noexcept
+{
+ if (pImpl->compData) {
+ if (target) *target = pImpl->compData->target;
+ return pImpl->compData->method;
+ } else {
+ if (target) *target = nullptr;
+ return CompositeMethod::None;
+ }
+}
+
+
+Result Paint::opacity(uint8_t o) noexcept
+{
+ if (pImpl->opacity == o) return Result::Success;
+
+ pImpl->opacity = o;
+ pImpl->renderFlag |= RenderUpdateFlag::Color;
+
+ return Result::Success;
+}
+
+
+uint8_t Paint::opacity() const noexcept
+{
+ return pImpl->opacity;
+}
+
+
+uint32_t Paint::identifier() const noexcept
+{
+ return pImpl->id;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.h b/thirdparty/thorvg/src/lib/tvgPaint.h
new file mode 100644
index 0000000000..4003bdbeac
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgPaint.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_PAINT_H_
+#define _TVG_PAINT_H_
+
+#include "tvgRender.h"
+
+
+namespace tvg
+{
+ enum ContextFlag {Invalid = 0, FastTrack = 1};
+
+ struct Iterator
+ {
+ virtual ~Iterator() {}
+ virtual const Paint* next() = 0;
+ virtual uint32_t count() = 0;
+ virtual void begin() = 0;
+ };
+
+ struct StrategyMethod
+ {
+ virtual ~StrategyMethod() {}
+
+ virtual bool dispose(RenderMethod& renderer) = 0;
+ virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag) = 0; //Return engine data if it has.
+ virtual bool render(RenderMethod& renderer) = 0;
+ virtual bool bounds(float* x, float* y, float* w, float* h) = 0;
+ virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
+ virtual Paint* duplicate() = 0;
+ virtual Iterator* iterator() = 0;
+ };
+
+ struct Composite
+ {
+ Paint* target;
+ Paint* source;
+ CompositeMethod method;
+ };
+
+ struct Paint::Impl
+ {
+ StrategyMethod* smethod = nullptr;
+ RenderTransform* rTransform = nullptr;
+ Composite* compData = nullptr;
+ uint32_t renderFlag = RenderUpdateFlag::None;
+ uint32_t ctxFlag = ContextFlag::Invalid;
+ uint32_t id;
+ uint8_t opacity = 255;
+
+ ~Impl()
+ {
+ if (compData) {
+ delete(compData->target);
+ free(compData);
+ }
+ if (smethod) delete(smethod);
+ if (rTransform) delete(rTransform);
+ }
+
+ void method(StrategyMethod* method)
+ {
+ smethod = method;
+ }
+
+ bool transform(const Matrix& m)
+ {
+ if (!rTransform) {
+ rTransform = new RenderTransform();
+ if (!rTransform) return false;
+ }
+ rTransform->override(m);
+ renderFlag |= RenderUpdateFlag::Transform;
+
+ return true;
+ }
+
+ Matrix* transform()
+ {
+ if (rTransform) {
+ rTransform->update();
+ return &rTransform->m;
+ }
+ return nullptr;
+ }
+
+ RenderRegion bounds(RenderMethod& renderer) const
+ {
+ return smethod->bounds(renderer);
+ }
+
+ bool dispose(RenderMethod& renderer)
+ {
+ if (compData) compData->target->pImpl->dispose(renderer);
+ return smethod->dispose(renderer);
+ }
+
+ Iterator* iterator()
+ {
+ return smethod->iterator();
+ }
+
+ bool composite(Paint* source, Paint* target, CompositeMethod method)
+ {
+ //Invalid case
+ if ((!target && method != CompositeMethod::None) || (target && method == CompositeMethod::None)) return false;
+
+ if (compData) {
+ delete(compData->target);
+ //Reset scenario
+ if (!target && method == CompositeMethod::None) {
+ free(compData);
+ compData = nullptr;
+ return true;
+ }
+ } else {
+ if (!target && method == CompositeMethod::None) return true;
+ compData = static_cast<Composite*>(calloc(1, sizeof(Composite)));
+ }
+ compData->target = target;
+ compData->source = source;
+ compData->method = method;
+ return true;
+ }
+
+ bool rotate(float degree);
+ bool scale(float factor);
+ bool translate(float x, float y);
+ bool bounds(float* x, float* y, float* w, float* h, bool transformed);
+ void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag);
+ bool render(RenderMethod& renderer);
+ Paint* duplicate();
+ };
+
+
+ template<class T>
+ struct PaintMethod : StrategyMethod
+ {
+ T* inst = nullptr;
+
+ PaintMethod(T* _inst) : inst(_inst) {}
+ ~PaintMethod() {}
+
+ bool bounds(float* x, float* y, float* w, float* h) override
+ {
+ return inst->bounds(x, y, w, h);
+ }
+
+ RenderRegion bounds(RenderMethod& renderer) const override
+ {
+ return inst->bounds(renderer);
+ }
+
+ bool dispose(RenderMethod& renderer) override
+ {
+ return inst->dispose(renderer);
+ }
+
+ void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag renderFlag) override
+ {
+ return inst->update(renderer, transform, opacity, clips, renderFlag);
+ }
+
+ bool render(RenderMethod& renderer) override
+ {
+ return inst->render(renderer);
+ }
+
+ Paint* duplicate() override
+ {
+ return inst->duplicate();
+ }
+
+ Iterator* iterator() override
+ {
+ return inst->iterator();
+ }
+ };
+}
+
+#endif //_TVG_PAINT_H_
diff --git a/thirdparty/thorvg/src/lib/tvgPicture.cpp b/thirdparty/thorvg/src/lib/tvgPicture.cpp
new file mode 100644
index 0000000000..1d9776363c
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgPicture.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "tvgPictureImpl.h"
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Picture::Picture() : pImpl(new Impl)
+{
+ Paint::pImpl->id = TVG_CLASS_ID_PICTURE;
+ Paint::pImpl->method(new PaintMethod<Picture::Impl>(pImpl));
+}
+
+
+Picture::~Picture()
+{
+ delete(pImpl);
+}
+
+
+unique_ptr<Picture> Picture::gen() noexcept
+{
+ return unique_ptr<Picture>(new Picture);
+}
+
+
+uint32_t Picture::identifier() noexcept
+{
+ return TVG_CLASS_ID_PICTURE;
+}
+
+
+Result Picture::load(const std::string& path) noexcept
+{
+ if (path.empty()) return Result::InvalidArguments;
+
+ return pImpl->load(path);
+}
+
+
+Result Picture::load(const char* data, uint32_t size, const string& mimeType, bool copy) noexcept
+{
+ if (!data || size <= 0) return Result::InvalidArguments;
+
+ return pImpl->load(data, size, mimeType, copy);
+}
+
+
+TVG_DEPRECATED Result Picture::load(const char* data, uint32_t size, bool copy) noexcept
+{
+ return load(data, size, "", copy);
+}
+
+
+Result Picture::load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept
+{
+ if (!data || w <= 0 || h <= 0) return Result::InvalidArguments;
+
+ return pImpl->load(data, w, h, copy);
+}
+
+
+Result Picture::viewbox(float* x, float* y, float* w, float* h) const noexcept
+{
+ if (pImpl->viewbox(x, y, w, h)) return Result::Success;
+ return Result::InsufficientCondition;
+}
+
+
+Result Picture::size(float w, float h) noexcept
+{
+ if (pImpl->size(w, h)) return Result::Success;
+ return Result::InsufficientCondition;
+}
+
+
+Result Picture::size(float* w, float* h) const noexcept
+{
+ if (!pImpl->loader) return Result::InsufficientCondition;
+ if (w) *w = pImpl->w;
+ if (h) *h = pImpl->h;
+ return Result::Success;
+}
+
+
+const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept
+{
+ //Try it, If not loaded yet.
+ pImpl->reload();
+
+ if (pImpl->loader) {
+ if (w) *w = static_cast<uint32_t>(pImpl->loader->w);
+ if (h) *h = static_cast<uint32_t>(pImpl->loader->h);
+ } else {
+ if (w) *w = 0;
+ if (h) *h = 0;
+ }
+ if (pImpl->surface) return pImpl->surface->buffer;
+ else return nullptr;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgPictureImpl.h b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
new file mode 100644
index 0000000000..00e8aa6dd9
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_PICTURE_IMPL_H_
+#define _TVG_PICTURE_IMPL_H_
+
+#include <string>
+#include "tvgPaint.h"
+#include "tvgLoader.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct PictureIterator : Iterator
+{
+ Paint* paint = nullptr;
+ Paint* ptr = nullptr;
+
+ PictureIterator(Paint* p) : paint(p) {}
+
+ const Paint* next() override
+ {
+ if (!ptr) ptr = paint;
+ else ptr = nullptr;
+ return ptr;
+ }
+
+ uint32_t count() override
+ {
+ if (paint) return 1;
+ else return 0;
+ }
+
+ void begin() override
+ {
+ ptr = nullptr;
+ }
+};
+
+
+struct Picture::Impl
+{
+ shared_ptr<LoadModule> loader = nullptr;
+
+ Paint* paint = nullptr; //vector picture uses
+ Surface* surface = nullptr; //bitmap picture uses
+ void* rdata = nullptr; //engine data
+ float w = 0, h = 0;
+ bool resizing = false;
+
+ ~Impl()
+ {
+ if (paint) delete(paint);
+ free(surface);
+ }
+
+ bool dispose(RenderMethod& renderer)
+ {
+ bool ret = true;
+ if (paint) {
+ ret = paint->pImpl->dispose(renderer);
+ } else if (surface) {
+ ret = renderer.dispose(rdata);
+ rdata = nullptr;
+ }
+ return ret;
+ }
+
+ uint32_t reload()
+ {
+ if (loader) {
+ if (!paint) {
+ if (auto p = loader->paint()) {
+ paint = p.release();
+ loader->close();
+ if (w != loader->w || h != loader->h) {
+ loader->resize(paint, w, h);
+ resizing = false;
+ }
+ if (paint) return RenderUpdateFlag::None;
+ }
+ }
+ free(surface);
+ if ((surface = loader->bitmap().release())) {
+ loader->close();
+ return RenderUpdateFlag::Image;
+ }
+ }
+ return RenderUpdateFlag::None;
+ }
+
+ RenderTransform resizeTransform(const RenderTransform* pTransform)
+ {
+ //Overriding Transformation by the desired image size
+ auto sx = w / loader->w;
+ auto sy = h / loader->h;
+ auto scale = sx < sy ? sx : sy;
+
+ RenderTransform tmp;
+ tmp.m = {scale, 0, 0, 0, scale, 0, 0, 0, 1};
+
+ if (!pTransform) return tmp;
+ else return RenderTransform(pTransform, &tmp);
+ }
+
+ void* update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag)
+ {
+ auto flag = reload();
+
+ if (surface) {
+ auto transform = resizeTransform(pTransform);
+ rdata = renderer.prepare(surface, rdata, &transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
+ } else if (paint) {
+ if (resizing) {
+ loader->resize(paint, w, h);
+ resizing = false;
+ }
+ rdata = paint->pImpl->update(renderer, pTransform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
+ }
+ return rdata;
+ }
+
+ bool render(RenderMethod &renderer)
+ {
+ if (surface) return renderer.renderImage(rdata);
+ else if (paint) return paint->pImpl->render(renderer);
+ return false;
+ }
+
+ bool viewbox(float* x, float* y, float* w, float* h) const
+ {
+ if (!loader) return false;
+ if (x) *x = loader->vx;
+ if (y) *y = loader->vy;
+ if (w) *w = loader->vw;
+ if (h) *h = loader->vh;
+ return true;
+ }
+
+ bool size(float w, float h)
+ {
+ this->w = w;
+ this->h = h;
+ resizing = true;
+ return true;
+ }
+
+ bool bounds(float* x, float* y, float* w, float* h)
+ {
+ if (x) *x = 0;
+ if (y) *y = 0;
+ if (w) *w = this->w;
+ if (h) *h = this->h;
+
+ return true;
+ }
+
+ RenderRegion bounds(RenderMethod& renderer)
+ {
+ if (rdata) return renderer.region(rdata);
+ if (paint) return paint->pImpl->bounds(renderer);
+ return {0, 0, 0, 0};
+ }
+
+ Result load(const string& path)
+ {
+ if (paint || surface) return Result::InsufficientCondition;
+ if (loader) loader->close();
+ bool invalid; //Invalid Path
+ loader = LoaderMgr::loader(path, &invalid);
+ if (!loader) {
+ if (invalid) return Result::InvalidArguments;
+ return Result::NonSupport;
+ }
+ if (!loader->read()) return Result::Unknown;
+ w = loader->w;
+ h = loader->h;
+ return Result::Success;
+ }
+
+ Result load(const char* data, uint32_t size, const string& mimeType, bool copy)
+ {
+ if (paint || surface) return Result::InsufficientCondition;
+ if (loader) loader->close();
+ loader = LoaderMgr::loader(data, size, mimeType, copy);
+ if (!loader) return Result::NonSupport;
+ if (!loader->read()) return Result::Unknown;
+ w = loader->w;
+ h = loader->h;
+ return Result::Success;
+ }
+
+ Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy)
+ {
+ if (paint || surface) return Result::InsufficientCondition;
+ if (loader) loader->close();
+ loader = LoaderMgr::loader(data, w, h, copy);
+ if (!loader) return Result::NonSupport;
+ this->w = loader->w;
+ this->h = loader->h;
+ return Result::Success;
+ }
+
+ Paint* duplicate()
+ {
+ reload();
+
+ auto ret = Picture::gen();
+
+ auto dup = ret.get()->pImpl;
+ if (paint) dup->paint = paint->duplicate();
+
+ dup->loader = loader;
+ if (surface) {
+ dup->surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ *dup->surface = *surface;
+ }
+ dup->w = w;
+ dup->h = h;
+ dup->resizing = resizing;
+
+ return ret.release();
+ }
+
+ Iterator* iterator()
+ {
+ reload();
+ return new PictureIterator(paint);
+ }
+};
+
+#endif //_TVG_PICTURE_IMPL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp b/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
new file mode 100644
index 0000000000..42a83461e3
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <float.h>
+#include "tvgFill.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct RadialGradient::Impl
+{
+ float cx = 0;
+ float cy = 0;
+ float radius = 0;
+
+ Fill* duplicate()
+ {
+ auto ret = RadialGradient::gen();
+ if (!ret) return nullptr;
+
+ ret->pImpl->cx = cx;
+ ret->pImpl->cy = cy;
+ ret->pImpl->radius = radius;
+
+ return ret.release();
+ }
+};
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+RadialGradient::RadialGradient():pImpl(new Impl())
+{
+ Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
+ Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
+}
+
+
+RadialGradient::~RadialGradient()
+{
+ delete(pImpl);
+}
+
+
+Result RadialGradient::radial(float cx, float cy, float radius) noexcept
+{
+ if (radius < 0) return Result::InvalidArguments;
+
+ pImpl->cx = cx;
+ pImpl->cy = cy;
+ pImpl->radius = radius;
+
+ return Result::Success;
+}
+
+
+Result RadialGradient::radial(float* cx, float* cy, float* radius) const noexcept
+{
+ if (cx) *cx = pImpl->cx;
+ if (cy) *cy = pImpl->cy;
+ if (radius) *radius = pImpl->radius;
+
+ return Result::Success;
+}
+
+
+unique_ptr<RadialGradient> RadialGradient::gen() noexcept
+{
+ return unique_ptr<RadialGradient>(new RadialGradient);
+}
+
+
+uint32_t RadialGradient::identifier() noexcept
+{
+ return TVG_CLASS_ID_RADIAL;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgRender.cpp b/thirdparty/thorvg/src/lib/tvgRender.cpp
new file mode 100644
index 0000000000..a48075dd04
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgRender.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgRender.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void RenderTransform::override(const Matrix& m)
+{
+ this->m = m;
+
+ if (m.e11 == 0.0f && m.e12 == 0.0f && m.e13 == 0.0f &&
+ m.e21 == 0.0f && m.e22 == 0.0f && m.e23 == 0.0f &&
+ m.e31 == 0.0f && m.e32 == 0.0f && m.e33 == 0.0f) {
+ overriding = false;
+ } else overriding = true;
+}
+
+
+bool RenderTransform::update()
+{
+ if (overriding) return true;
+
+ //Init Status
+ if (mathZero(x) && mathZero(y) && mathZero(degree) && mathEqual(scale, 1)) return false;
+
+ mathIdentity(&m);
+
+ mathScale(&m, scale);
+
+ if (!mathZero(degree)) mathRotate(&m, degree);
+
+ mathTranslate(&m, x, y);
+
+ return true;
+}
+
+
+RenderTransform::RenderTransform()
+{
+}
+
+
+RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
+{
+ m = mathMultiply(&lhs->m, &rhs->m);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgRender.h b/thirdparty/thorvg/src/lib/tvgRender.h
new file mode 100644
index 0000000000..f927947aa6
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgRender.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_RENDER_H_
+#define _TVG_RENDER_H_
+
+#include "tvgCommon.h"
+#include "tvgArray.h"
+
+namespace tvg
+{
+
+enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255};
+
+struct Surface
+{
+ //TODO: Union for multiple types
+ uint32_t* buffer;
+ uint32_t stride;
+ uint32_t w, h;
+ uint32_t cs;
+};
+
+using RenderData = void*;
+
+struct Compositor
+{
+ CompositeMethod method;
+ uint32_t opacity;
+};
+
+struct RenderRegion
+{
+ int32_t x, y, w, h;
+
+ void intersect(const RenderRegion& rhs)
+ {
+ auto x1 = x + w;
+ auto y1 = y + h;
+ auto x2 = rhs.x + rhs.w;
+ auto y2 = rhs.y + rhs.h;
+
+ x = (x > rhs.x) ? x : rhs.x;
+ y = (y > rhs.y) ? y : rhs.y;
+ w = ((x1 < x2) ? x1 : x2) - x;
+ h = ((y1 < y2) ? y1 : y2) - y;
+
+ if (w < 0) w = 0;
+ if (h < 0) h = 0;
+ }
+};
+
+struct RenderTransform
+{
+ Matrix m; //3x3 Matrix Elements
+ float x = 0.0f;
+ float y = 0.0f;
+ float degree = 0.0f; //rotation degree
+ float scale = 1.0f; //scale factor
+ bool overriding = false; //user transform?
+
+ bool update();
+ void override(const Matrix& m);
+
+ RenderTransform();
+ RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
+};
+
+
+class RenderMethod
+{
+public:
+ virtual ~RenderMethod() {}
+ virtual RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
+ virtual RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
+ virtual bool preRender() = 0;
+ virtual bool renderShape(RenderData data) = 0;
+ virtual bool renderImage(RenderData data) = 0;
+ virtual bool postRender() = 0;
+ virtual bool dispose(RenderData data) = 0;
+ virtual RenderRegion region(RenderData data) = 0;
+ virtual RenderRegion viewport() = 0;
+ virtual bool viewport(const RenderRegion& vp) = 0;
+
+ virtual bool clear() = 0;
+ virtual bool sync() = 0;
+
+ virtual Compositor* target(const RenderRegion& region) = 0;
+ virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) = 0;
+ virtual bool endComposite(Compositor* cmp) = 0;
+};
+
+}
+
+#endif //_TVG_RENDER_H_
diff --git a/thirdparty/thorvg/src/lib/tvgSaveModule.h b/thirdparty/thorvg/src/lib/tvgSaveModule.h
new file mode 100644
index 0000000000..2a0f427f11
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgSaveModule.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SAVE_MODULE_H_
+#define _TVG_SAVE_MODULE_H_
+
+#include "tvgIteratorAccessor.h"
+
+namespace tvg
+{
+
+class SaveModule : public IteratorAccessor
+{
+public:
+ virtual ~SaveModule() {}
+
+ virtual bool save(Paint* paint, const string& path, bool compress) = 0;
+ virtual bool close() = 0;
+};
+
+}
+
+#endif //_TVG_SAVE_MODULE_H_
diff --git a/thirdparty/thorvg/src/lib/tvgSaver.cpp b/thirdparty/thorvg/src/lib/tvgSaver.cpp
new file mode 100644
index 0000000000..1a3e8614a6
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgSaver.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgCommon.h"
+#include "tvgSaveModule.h"
+
+#ifdef THORVG_TVG_SAVER_SUPPORT
+ #include "tvgTvgSaver.h"
+#endif
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct Saver::Impl
+{
+ SaveModule* saveModule = nullptr;
+ ~Impl()
+ {
+ if (saveModule) delete(saveModule);
+ }
+};
+
+
+static SaveModule* _find(FileType type)
+{
+ switch(type) {
+ case FileType::Tvg: {
+#ifdef THORVG_TVG_SAVER_SUPPORT
+ return new TvgSaver;
+#endif
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+#ifdef THORVG_LOG_ENABLED
+ const char *format;
+ switch(type) {
+ case FileType::Tvg: {
+ format = "TVG";
+ break;
+ }
+ default: {
+ format = "???";
+ break;
+ }
+ }
+ TVGLOG("SAVER", "%s format is not supported", format);
+#endif
+ return nullptr;
+}
+
+
+static SaveModule* _find(const string& path)
+{
+ auto ext = path.substr(path.find_last_of(".") + 1);
+ if (!ext.compare("tvg")) {
+ return _find(FileType::Tvg);
+ }
+ return nullptr;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Saver::Saver() : pImpl(new Impl())
+{
+}
+
+
+Saver::~Saver()
+{
+ delete(pImpl);
+}
+
+
+Result Saver::save(std::unique_ptr<Paint> paint, const string& path, bool compress) noexcept
+{
+ auto p = paint.release();
+ if (!p) return Result::MemoryCorruption;
+
+ //Already on saving an other resource.
+ if (pImpl->saveModule) {
+ delete(p);
+ return Result::InsufficientCondition;
+ }
+
+ if (auto saveModule = _find(path)) {
+ if (saveModule->save(p, path, compress)) {
+ pImpl->saveModule = saveModule;
+ return Result::Success;
+ } else {
+ delete(p);
+ delete(saveModule);
+ return Result::Unknown;
+ }
+ }
+ delete(p);
+ return Result::NonSupport;
+}
+
+
+Result Saver::sync() noexcept
+{
+ if (!pImpl->saveModule) return Result::InsufficientCondition;
+ pImpl->saveModule->close();
+ delete(pImpl->saveModule);
+ pImpl->saveModule = nullptr;
+
+ return Result::Success;
+}
+
+
+unique_ptr<Saver> Saver::gen() noexcept
+{
+ return unique_ptr<Saver>(new Saver);
+}
diff --git a/thirdparty/thorvg/src/lib/tvgScene.cpp b/thirdparty/thorvg/src/lib/tvgScene.cpp
new file mode 100644
index 0000000000..4b2f77dde9
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgScene.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgSceneImpl.h"
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Scene::Scene() : pImpl(new Impl())
+{
+ Paint::pImpl->id = TVG_CLASS_ID_SCENE;
+ Paint::pImpl->method(new PaintMethod<Scene::Impl>(pImpl));
+}
+
+
+Scene::~Scene()
+{
+ delete(pImpl);
+}
+
+
+unique_ptr<Scene> Scene::gen() noexcept
+{
+ return unique_ptr<Scene>(new Scene);
+}
+
+
+uint32_t Scene::identifier() noexcept
+{
+ return TVG_CLASS_ID_SCENE;
+}
+
+
+Result Scene::push(unique_ptr<Paint> paint) noexcept
+{
+ auto p = paint.release();
+ if (!p) return Result::MemoryCorruption;
+ pImpl->paints.push(p);
+
+ return Result::Success;
+}
+
+
+Result Scene::reserve(uint32_t size) noexcept
+{
+ if (!pImpl->paints.reserve(size)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+Result Scene::clear(bool free) noexcept
+{
+ pImpl->clear(free);
+
+ return Result::Success;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgSceneImpl.h b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
new file mode 100644
index 0000000000..de94a66e2f
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SCENE_IMPL_H_
+#define _TVG_SCENE_IMPL_H_
+
+#include <float.h>
+#include "tvgPaint.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct SceneIterator : Iterator
+{
+ Array<Paint*>* paints;
+ uint32_t idx = 0;
+
+ SceneIterator(Array<Paint*>* p) : paints(p)
+ {
+ }
+
+ const Paint* next() override
+ {
+ if (idx >= paints->count) return nullptr;
+ return paints->data[idx++];
+ }
+
+ uint32_t count() override
+ {
+ return paints->count;
+ }
+
+ void begin() override
+ {
+ idx = 0;
+ }
+};
+
+struct Scene::Impl
+{
+ Array<Paint*> paints;
+ uint8_t opacity; //for composition
+ RenderMethod* renderer = nullptr; //keep it for explicit clear
+
+ ~Impl()
+ {
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ delete(*paint);
+ }
+ }
+
+ bool dispose(RenderMethod& renderer)
+ {
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ (*paint)->pImpl->dispose(renderer);
+ }
+
+ this->renderer = nullptr;
+
+ return true;
+ }
+
+ bool needComposition(uint32_t opacity)
+ {
+ //Half translucent requires intermediate composition.
+ if (opacity == 255 || opacity == 0) return false;
+
+ //If scene has several children or only scene, it may require composition.
+ if (paints.count > 1) return true;
+ if (paints.count == 1 && (*paints.data)->identifier() == TVG_CLASS_ID_SCENE) return true;
+ return false;
+ }
+
+ void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flag)
+ {
+ /* Overriding opacity value. If this scene is half-translucent,
+ It must do intermeidate composition with that opacity value. */
+ this->opacity = static_cast<uint8_t>(opacity);
+ if (needComposition(opacity)) opacity = 255;
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ (*paint)->pImpl->update(renderer, transform, opacity, clips, static_cast<uint32_t>(flag));
+ }
+
+ /* FXIME: it requires to return list of children engine data
+ This is necessary for scene composition */
+
+ this->renderer = &renderer;
+
+ return nullptr;
+ }
+
+ bool render(RenderMethod& renderer)
+ {
+ Compositor* cmp = nullptr;
+
+ if (needComposition(opacity)) {
+ cmp = renderer.target(bounds(renderer));
+ renderer.beginComposite(cmp, CompositeMethod::None, opacity);
+ }
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ if (!(*paint)->pImpl->render(renderer)) return false;
+ }
+
+ if (cmp) renderer.endComposite(cmp);
+
+ return true;
+ }
+
+ RenderRegion bounds(RenderMethod& renderer) const
+ {
+ if (paints.count == 0) return {0, 0, 0, 0};
+
+ int32_t x1 = INT32_MAX;
+ int32_t y1 = INT32_MAX;
+ int32_t x2 = 0;
+ int32_t y2 = 0;
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ auto region = (*paint)->pImpl->bounds(renderer);
+
+ //Merge regions
+ if (region.x < x1) x1 = region.x;
+ if (x2 < region.x + region.w) x2 = (region.x + region.w);
+ if (region.y < y1) y1 = region.y;
+ if (y2 < region.y + region.h) y2 = (region.y + region.h);
+ }
+
+ return {x1, y1, (x2 - x1), (y2 - y1)};
+ }
+
+ bool bounds(float* px, float* py, float* pw, float* ph)
+ {
+ if (paints.count == 0) return false;
+
+ auto x1 = FLT_MAX;
+ auto y1 = FLT_MAX;
+ auto x2 = -FLT_MAX;
+ auto y2 = -FLT_MAX;
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ auto x = FLT_MAX;
+ auto y = FLT_MAX;
+ auto w = 0.0f;
+ auto h = 0.0f;
+
+ if ((*paint)->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue;
+
+ //Merge regions
+ if (x < x1) x1 = x;
+ if (x2 < x + w) x2 = (x + w);
+ if (y < y1) y1 = y;
+ if (y2 < y + h) y2 = (y + h);
+ }
+
+ if (px) *px = x1;
+ if (py) *py = y1;
+ if (pw) *pw = (x2 - x1);
+ if (ph) *ph = (y2 - y1);
+
+ return true;
+ }
+
+ Paint* duplicate()
+ {
+ auto ret = Scene::gen();
+
+ auto dup = ret.get()->pImpl;
+
+ dup->paints.reserve(paints.count);
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ dup->paints.push((*paint)->duplicate());
+ }
+
+ return ret.release();
+ }
+
+ void clear(bool free)
+ {
+ auto dispose = renderer ? true : false;
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ if (dispose) (*paint)->pImpl->dispose(*renderer);
+ if (free) delete(*paint);
+ }
+ paints.clear();
+ renderer = nullptr;
+ }
+
+ Iterator* iterator()
+ {
+ return new SceneIterator(&paints);
+ }
+};
+
+#endif //_TVG_SCENE_IMPL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgShape.cpp b/thirdparty/thorvg/src/lib/tvgShape.cpp
new file mode 100644
index 0000000000..8db5635932
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgShape.cpp
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "tvgMath.h"
+#include "tvgShapeImpl.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+constexpr auto PATH_KAPPA = 0.552284f;
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Shape :: Shape() : pImpl(new Impl(this))
+{
+ Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
+ Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl));
+}
+
+
+Shape :: ~Shape()
+{
+ delete(pImpl);
+}
+
+
+unique_ptr<Shape> Shape::gen() noexcept
+{
+ return unique_ptr<Shape>(new Shape);
+}
+
+
+uint32_t Shape::identifier() noexcept
+{
+ return TVG_CLASS_ID_SHAPE;
+}
+
+
+Result Shape::reset() noexcept
+{
+ pImpl->path.reset();
+ pImpl->flag = RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
+{
+ if (!cmds) return 0;
+
+ *cmds = pImpl->path.cmds;
+
+ return pImpl->path.cmdCnt;
+}
+
+
+uint32_t Shape::pathCoords(const Point** pts) const noexcept
+{
+ if (!pts) return 0;
+
+ *pts = pImpl->path.pts;
+
+ return pImpl->path.ptsCnt;
+}
+
+
+Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept
+{
+ if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments;
+
+ pImpl->path.grow(cmdCnt, ptsCnt);
+ pImpl->path.append(cmds, cmdCnt, pts, ptsCnt);
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::moveTo(float x, float y) noexcept
+{
+ pImpl->path.moveTo(x, y);
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::lineTo(float x, float y) noexcept
+{
+ pImpl->path.lineTo(x, y);
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept
+{
+ pImpl->path.cubicTo(cx1, cy1, cx2, cy2, x, y);
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::close() noexcept
+{
+ pImpl->path.close();
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
+{
+ auto rxKappa = rx * PATH_KAPPA;
+ auto ryKappa = ry * PATH_KAPPA;
+
+ pImpl->path.grow(6, 13);
+ pImpl->path.moveTo(cx, cy - ry);
+ pImpl->path.cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
+ pImpl->path.cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
+ pImpl->path.cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
+ pImpl->path.cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
+ pImpl->path.close();
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept
+{
+ //just circle
+ if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
+
+ startAngle = (startAngle * M_PI) / 180.0f;
+ sweep = sweep * M_PI / 180.0f;
+
+ auto nCurves = ceil(fabsf(sweep / float(M_PI_2)));
+ auto sweepSign = (sweep < 0 ? -1 : 1);
+ auto fract = fmodf(sweep, float(M_PI_2));
+ fract = (mathZero(fract)) ? float(M_PI_2) * sweepSign : fract;
+
+ //Start from here
+ Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
+
+ if (pie) {
+ pImpl->path.moveTo(cx, cy);
+ pImpl->path.lineTo(start.x + cx, start.y + cy);
+ } else {
+ pImpl->path.moveTo(start.x + cx, start.y + cy);
+ }
+
+ for (int i = 0; i < nCurves; ++i) {
+ auto endAngle = startAngle + ((i != nCurves - 1) ? float(M_PI_2) * sweepSign : fract);
+ Point end = {radius * cosf(endAngle), radius * sinf(endAngle)};
+
+ //variables needed to calculate bezier control points
+
+ //get bezier control points using article:
+ //(http://itc.ktu.lt/index.php/ITC/article/view/11812/6479)
+ auto ax = start.x;
+ auto ay = start.y;
+ auto bx = end.x;
+ auto by = end.y;
+ auto q1 = ax * ax + ay * ay;
+ auto q2 = ax * bx + ay * by + q1;
+ auto k2 = (4.0f/3.0f) * ((sqrtf(2 * q1 * q2) - q2) / (ax * by - ay * bx));
+
+ start = end; //Next start point is the current end point
+
+ end.x += cx;
+ end.y += cy;
+
+ Point ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy};
+ Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy};
+
+ pImpl->path.cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y);
+
+ startAngle = endAngle;
+ }
+
+ if (pie) pImpl->path.close();
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept
+{
+ auto halfW = w * 0.5f;
+ auto halfH = h * 0.5f;
+
+ //clamping cornerRadius by minimum size
+ if (rx > halfW) rx = halfW;
+ if (ry > halfH) ry = halfH;
+
+ //rectangle
+ if (rx == 0 && ry == 0) {
+ pImpl->path.grow(5, 4);
+ pImpl->path.moveTo(x, y);
+ pImpl->path.lineTo(x + w, y);
+ pImpl->path.lineTo(x + w, y + h);
+ pImpl->path.lineTo(x, y + h);
+ pImpl->path.close();
+ //circle
+ } else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
+ return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
+ } else {
+ auto hrx = rx * 0.5f;
+ auto hry = ry * 0.5f;
+ pImpl->path.grow(10, 17);
+ pImpl->path.moveTo(x + rx, y);
+ pImpl->path.lineTo(x + w - rx, y);
+ pImpl->path.cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
+ pImpl->path.lineTo(x + w, y + h - ry);
+ pImpl->path.cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
+ pImpl->path.lineTo(x + rx, y + h);
+ pImpl->path.cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
+ pImpl->path.lineTo(x, y + ry);
+ pImpl->path.cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
+ pImpl->path.close();
+ }
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
+{
+ pImpl->color[0] = r;
+ pImpl->color[1] = g;
+ pImpl->color[2] = b;
+ pImpl->color[3] = a;
+ pImpl->flag |= RenderUpdateFlag::Color;
+
+ if (pImpl->fill) {
+ delete(pImpl->fill);
+ pImpl->fill = nullptr;
+ pImpl->flag |= RenderUpdateFlag::Gradient;
+ }
+
+ return Result::Success;
+}
+
+
+Result Shape::fill(unique_ptr<Fill> f) noexcept
+{
+ auto p = f.release();
+ if (!p) return Result::MemoryCorruption;
+
+ if (pImpl->fill && pImpl->fill != p) delete(pImpl->fill);
+ pImpl->fill = p;
+ pImpl->flag |= RenderUpdateFlag::Gradient;
+
+ return Result::Success;
+}
+
+
+Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
+{
+ if (r) *r = pImpl->color[0];
+ if (g) *g = pImpl->color[1];
+ if (b) *b = pImpl->color[2];
+ if (a) *a = pImpl->color[3];
+
+ return Result::Success;
+}
+
+const Fill* Shape::fill() const noexcept
+{
+ return pImpl->fill;
+}
+
+
+Result Shape::stroke(float width) noexcept
+{
+ if (!pImpl->strokeWidth(width)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+float Shape::strokeWidth() const noexcept
+{
+ if (!pImpl->stroke) return 0;
+ return pImpl->stroke->width;
+}
+
+
+Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
+{
+ if (!pImpl->strokeColor(r, g, b, a)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
+{
+ if (!pImpl->stroke) return Result::InsufficientCondition;
+
+ if (r) *r = pImpl->stroke->color[0];
+ if (g) *g = pImpl->stroke->color[1];
+ if (b) *b = pImpl->stroke->color[2];
+ if (a) *a = pImpl->stroke->color[3];
+
+ return Result::Success;
+}
+
+
+Result Shape::stroke(unique_ptr<Fill> f) noexcept
+{
+ return pImpl->strokeFill(move(f));
+}
+
+
+const Fill* Shape::strokeFill() const noexcept
+{
+ if (!pImpl->stroke) return nullptr;
+
+ return pImpl->stroke->fill;
+}
+
+
+Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
+{
+ if ((cnt == 1) || (!dashPattern && cnt > 0) || (dashPattern && cnt == 0)) {
+ return Result::InvalidArguments;
+ }
+
+ for (uint32_t i = 0; i < cnt; i++)
+ if (dashPattern[i] < FLT_EPSILON) return Result::InvalidArguments;
+
+ if (!pImpl->strokeDash(dashPattern, cnt)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
+{
+ if (!pImpl->stroke) return 0;
+
+ if (dashPattern) *dashPattern = pImpl->stroke->dashPattern;
+
+ return pImpl->stroke->dashCnt;
+}
+
+
+Result Shape::stroke(StrokeCap cap) noexcept
+{
+ if (!pImpl->strokeCap(cap)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+Result Shape::stroke(StrokeJoin join) noexcept
+{
+ if (!pImpl->strokeJoin(join)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+StrokeCap Shape::strokeCap() const noexcept
+{
+ if (!pImpl->stroke) return StrokeCap::Square;
+
+ return pImpl->stroke->cap;
+}
+
+
+StrokeJoin Shape::strokeJoin() const noexcept
+{
+ if (!pImpl->stroke) return StrokeJoin::Bevel;
+
+ return pImpl->stroke->join;
+}
+
+
+Result Shape::fill(FillRule r) noexcept
+{
+ pImpl->rule = r;
+
+ return Result::Success;
+}
+
+
+FillRule Shape::fillRule() const noexcept
+{
+ return pImpl->rule;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgShapeImpl.h b/thirdparty/thorvg/src/lib/tvgShapeImpl.h
new file mode 100644
index 0000000000..dcf4e6e954
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgShapeImpl.h
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SHAPE_IMPL_H_
+#define _TVG_SHAPE_IMPL_H_
+
+#include <memory.h>
+#include "tvgPaint.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct ShapeStroke
+{
+ float width;
+ uint8_t color[4];
+ Fill *fill;
+ float* dashPattern;
+ uint32_t dashCnt;
+ StrokeCap cap;
+ StrokeJoin join;
+
+ void copy(const ShapeStroke* src)
+ {
+ width = src->width;
+ dashCnt = src->dashCnt;
+ cap = src->cap;
+ join = src->join;
+
+ memcpy(color, src->color, sizeof(color));
+ if (dashCnt > 0) {
+ dashPattern = static_cast<float*>(malloc(sizeof(float) * dashCnt));
+ memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt);
+ }
+ if (src->fill) fill = src->fill->duplicate();
+ }
+
+ void clear()
+ {
+ if (dashPattern) free(dashPattern);
+ if (fill) delete(fill);
+ }
+};
+
+
+struct ShapePath
+{
+ PathCommand* cmds = nullptr;
+ uint32_t cmdCnt = 0;
+ uint32_t reservedCmdCnt = 0;
+
+ Point *pts = nullptr;
+ uint32_t ptsCnt = 0;
+ uint32_t reservedPtsCnt = 0;
+
+ ~ShapePath()
+ {
+ if (cmds) free(cmds);
+ if (pts) free(pts);
+ }
+
+ ShapePath()
+ {
+ }
+
+ void duplicate(const ShapePath* src)
+ {
+ if (src->cmdCnt == 0 || src->ptsCnt == 0) return;
+
+ cmdCnt = src->cmdCnt;
+ reservedCmdCnt = src->reservedCmdCnt;
+ ptsCnt = src->ptsCnt;
+ reservedPtsCnt = src->reservedPtsCnt;
+
+ cmds = static_cast<PathCommand*>(malloc(sizeof(PathCommand) * reservedCmdCnt));
+ if (!cmds) return;
+ memcpy(cmds, src->cmds, sizeof(PathCommand) * cmdCnt);
+
+ pts = static_cast<Point*>(malloc(sizeof(Point) * reservedPtsCnt));
+ if (!pts) {
+ free(cmds);
+ return;
+ }
+ memcpy(pts, src->pts, sizeof(Point) * ptsCnt);
+ }
+
+ void reserveCmd(uint32_t cmdCnt)
+ {
+ if (cmdCnt <= reservedCmdCnt) return;
+ reservedCmdCnt = cmdCnt;
+ cmds = static_cast<PathCommand*>(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt));
+ }
+
+ void reservePts(uint32_t ptsCnt)
+ {
+ if (ptsCnt <= reservedPtsCnt) return;
+ reservedPtsCnt = ptsCnt;
+ pts = static_cast<Point*>(realloc(pts, sizeof(Point) * reservedPtsCnt));
+ }
+
+ void grow(uint32_t cmdCnt, uint32_t ptsCnt)
+ {
+ reserveCmd(this->cmdCnt + cmdCnt);
+ reservePts(this->ptsCnt + ptsCnt);
+ }
+
+ void reset()
+ {
+ cmdCnt = 0;
+ ptsCnt = 0;
+ }
+
+ void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
+ {
+ memcpy(this->cmds + this->cmdCnt, cmds, sizeof(PathCommand) * cmdCnt);
+ memcpy(this->pts + this->ptsCnt, pts, sizeof(Point) * ptsCnt);
+ this->cmdCnt += cmdCnt;
+ this->ptsCnt += ptsCnt;
+ }
+
+ void moveTo(float x, float y)
+ {
+ if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
+ if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
+
+ cmds[cmdCnt++] = PathCommand::MoveTo;
+ pts[ptsCnt++] = {x, y};
+ }
+
+ void lineTo(float x, float y)
+ {
+ if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
+ if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
+
+ cmds[cmdCnt++] = PathCommand::LineTo;
+ pts[ptsCnt++] = {x, y};
+ }
+
+ void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
+ {
+ if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
+ if (ptsCnt + 3 > reservedPtsCnt) reservePts((ptsCnt + 3) * 2);
+
+ cmds[cmdCnt++] = PathCommand::CubicTo;
+ pts[ptsCnt++] = {cx1, cy1};
+ pts[ptsCnt++] = {cx2, cy2};
+ pts[ptsCnt++] = {x, y};
+ }
+
+ void close()
+ {
+ if (cmdCnt > 0 && cmds[cmdCnt - 1] == PathCommand::Close) return;
+
+ if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
+ cmds[cmdCnt++] = PathCommand::Close;
+ }
+
+ bool bounds(float* x, float* y, float* w, float* h) const
+ {
+ if (ptsCnt == 0) return false;
+
+ Point min = { pts[0].x, pts[0].y };
+ Point max = { pts[0].x, pts[0].y };
+
+ for (uint32_t i = 1; i < ptsCnt; ++i) {
+ if (pts[i].x < min.x) min.x = pts[i].x;
+ if (pts[i].y < min.y) min.y = pts[i].y;
+ if (pts[i].x > max.x) max.x = pts[i].x;
+ if (pts[i].y > max.y) max.y = pts[i].y;
+ }
+
+ if (x) *x = min.x;
+ if (y) *y = min.y;
+ if (w) *w = max.x - min.x;
+ if (h) *h = max.y - min.y;
+
+ return true;
+ }
+};
+
+
+struct Shape::Impl
+{
+ ShapePath path;
+ Fill *fill = nullptr;
+ ShapeStroke *stroke = nullptr;
+ uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
+ FillRule rule = FillRule::Winding;
+ RenderData rdata = nullptr; //engine data
+ Shape *shape = nullptr;
+ uint32_t flag = RenderUpdateFlag::None;
+
+ Impl(Shape* s) : shape(s)
+ {
+ }
+
+ ~Impl()
+ {
+ if (fill) delete(fill);
+ if (stroke) {
+ stroke->clear();
+ free (stroke);
+ }
+ }
+
+ bool dispose(RenderMethod& renderer)
+ {
+ auto ret = renderer.dispose(rdata);
+ rdata = nullptr;
+ return ret;
+ }
+
+ bool render(RenderMethod& renderer)
+ {
+ return renderer.renderShape(rdata);
+ }
+
+ void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag)
+ {
+ this->rdata = renderer.prepare(*shape, this->rdata, transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
+ flag = RenderUpdateFlag::None;
+ return this->rdata;
+ }
+
+ RenderRegion bounds(RenderMethod& renderer)
+ {
+ return renderer.region(rdata);
+ }
+
+ bool bounds(float* x, float* y, float* w, float* h)
+ {
+ auto ret = path.bounds(x, y, w, h);
+
+ //Stroke feathering
+ if (stroke) {
+ if (x) *x -= stroke->width * 0.5f;
+ if (y) *y -= stroke->width * 0.5f;
+ if (w) *w += stroke->width;
+ if (h) *h += stroke->width;
+ }
+ return ret;
+ }
+
+ bool strokeWidth(float width)
+ {
+ //TODO: Size Exception?
+
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ stroke->width = width;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ bool strokeCap(StrokeCap cap)
+ {
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ stroke->cap = cap;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ bool strokeJoin(StrokeJoin join)
+ {
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ stroke->join = join;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+ {
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ if (stroke->fill) {
+ delete(stroke->fill);
+ stroke->fill = nullptr;
+ flag |= RenderUpdateFlag::GradientStroke;
+ }
+
+ stroke->color[0] = r;
+ stroke->color[1] = g;
+ stroke->color[2] = b;
+ stroke->color[3] = a;
+
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ Result strokeFill(unique_ptr<Fill> f)
+ {
+ auto p = f.release();
+ if (!p) return Result::MemoryCorruption;
+
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ if (stroke->fill && stroke->fill != p) delete(stroke->fill);
+ stroke->fill = p;
+
+ flag |= RenderUpdateFlag::Stroke;
+ flag |= RenderUpdateFlag::GradientStroke;
+
+ return Result::Success;
+ }
+
+ bool strokeDash(const float* pattern, uint32_t cnt)
+ {
+ //Reset dash
+ if (!pattern && cnt == 0) {
+ free(stroke->dashPattern);
+ stroke->dashPattern = nullptr;
+ } else {
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ if (stroke->dashCnt != cnt) {
+ free(stroke->dashPattern);
+ stroke->dashPattern = nullptr;
+ }
+ if (!stroke->dashPattern) {
+ stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
+ if (!stroke->dashPattern) return false;
+ }
+ for (uint32_t i = 0; i < cnt; ++i) {
+ stroke->dashPattern[i] = pattern[i];
+ }
+ }
+ stroke->dashCnt = cnt;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ Paint* duplicate()
+ {
+ auto ret = Shape::gen();
+
+ auto dup = ret.get()->pImpl;
+ dup->rule = rule;
+
+ //Color
+ memcpy(dup->color, color, sizeof(color));
+ dup->flag = RenderUpdateFlag::Color;
+
+ //Path
+ dup->path.duplicate(&path);
+ dup->flag |= RenderUpdateFlag::Path;
+
+ //Stroke
+ if (stroke) {
+ dup->stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ dup->stroke->copy(stroke);
+ dup->flag |= RenderUpdateFlag::Stroke;
+
+ if (stroke->fill)
+ dup->flag |= RenderUpdateFlag::GradientStroke;
+ }
+
+ //Fill
+ if (fill) {
+ dup->fill = fill->duplicate();
+ dup->flag |= RenderUpdateFlag::Gradient;
+ }
+
+ return ret.release();
+ }
+
+ Iterator* iterator()
+ {
+ return nullptr;
+ }
+};
+
+#endif //_TVG_SHAPE_IMPL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp b/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp
new file mode 100644
index 0000000000..f7a03b819c
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgCanvasImpl.h"
+
+#ifdef THORVG_SW_RASTER_SUPPORT
+ #include "tvgSwRenderer.h"
+#else
+ class SwRenderer : public RenderMethod
+ {
+ //Non Supported. Dummy Class */
+ };
+#endif
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct SwCanvas::Impl
+{
+};
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+#ifdef THORVG_SW_RASTER_SUPPORT
+SwCanvas::SwCanvas() : Canvas(SwRenderer::gen()), pImpl(new Impl)
+#else
+SwCanvas::SwCanvas() : Canvas(nullptr), pImpl(new Impl)
+#endif
+{
+}
+
+
+SwCanvas::~SwCanvas()
+{
+ delete(pImpl);
+}
+
+
+Result SwCanvas::mempool(MempoolPolicy policy) noexcept
+{
+#ifdef THORVG_SW_RASTER_SUPPORT
+ //We know renderer type, avoid dynamic_cast for performance.
+ auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
+ if (!renderer) return Result::MemoryCorruption;
+
+ //It can't change the policy during the running.
+ if (Canvas::pImpl->paints.count > 0) return Result::InsufficientCondition;
+
+ if (policy == MempoolPolicy::Individual) renderer->mempool(false);
+ else renderer->mempool(true);
+
+ return Result::Success;
+#endif
+ return Result::NonSupport;
+}
+
+
+Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
+{
+#ifdef THORVG_SW_RASTER_SUPPORT
+ //We know renderer type, avoid dynamic_cast for performance.
+ auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
+ if (!renderer) return Result::MemoryCorruption;
+
+ if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments;
+
+ //Paints must be updated again with this new target.
+ Canvas::pImpl->needRefresh();
+
+ return Result::Success;
+#endif
+ return Result::NonSupport;
+}
+
+
+unique_ptr<SwCanvas> SwCanvas::gen() noexcept
+{
+#ifdef THORVG_SW_RASTER_SUPPORT
+ if (SwRenderer::init() <= 0) return nullptr;
+ return unique_ptr<SwCanvas>(new SwCanvas);
+#endif
+ return nullptr;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
new file mode 100644
index 0000000000..780127b87b
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <deque>
+#include <thread>
+#include <vector>
+#include <atomic>
+#include <condition_variable>
+#include "tvgTaskScheduler.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+namespace tvg {
+
+struct TaskQueue {
+ deque<Task*> taskDeque;
+ mutex mtx;
+ condition_variable ready;
+ bool done = false;
+
+ bool tryPop(Task** task)
+ {
+ unique_lock<mutex> lock{mtx, try_to_lock};
+ if (!lock || taskDeque.empty()) return false;
+ *task = taskDeque.front();
+ taskDeque.pop_front();
+
+ return true;
+ }
+
+ bool tryPush(Task* task)
+ {
+ {
+ unique_lock<mutex> lock{mtx, try_to_lock};
+ if (!lock) return false;
+ taskDeque.push_back(task);
+ }
+
+ ready.notify_one();
+
+ return true;
+ }
+
+ void complete()
+ {
+ {
+ unique_lock<mutex> lock{mtx};
+ done = true;
+ }
+ ready.notify_all();
+ }
+
+ bool pop(Task** task)
+ {
+ unique_lock<mutex> lock{mtx};
+
+ while (taskDeque.empty() && !done) {
+ ready.wait(lock);
+ }
+
+ if (taskDeque.empty()) return false;
+
+ *task = taskDeque.front();
+ taskDeque.pop_front();
+
+ return true;
+ }
+
+ void push(Task* task)
+ {
+ {
+ unique_lock<mutex> lock{mtx};
+ taskDeque.push_back(task);
+ }
+
+ ready.notify_one();
+ }
+
+};
+
+
+class TaskSchedulerImpl
+{
+public:
+ unsigned threadCnt;
+ vector<thread> threads;
+ vector<TaskQueue> taskQueues;
+ atomic<unsigned> idx{0};
+
+ TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt)
+ {
+ for (unsigned i = 0; i < threadCnt; ++i) {
+ threads.emplace_back([&, i] { run(i); });
+ }
+ }
+
+ ~TaskSchedulerImpl()
+ {
+ for (auto& queue : taskQueues) queue.complete();
+ for (auto& thread : threads) thread.join();
+ }
+
+ void run(unsigned i)
+ {
+ Task* task;
+
+ //Thread Loop
+ while (true) {
+ auto success = false;
+ for (unsigned x = 0; x < threadCnt * 2; ++x) {
+ if (taskQueues[(i + x) % threadCnt].tryPop(&task)) {
+ success = true;
+ break;
+ }
+ }
+
+ if (!success && !taskQueues[i].pop(&task)) break;
+ (*task)(i);
+ }
+ }
+
+ void request(Task* task)
+ {
+ //Async
+ if (threadCnt > 0) {
+ task->prepare();
+ auto i = idx++;
+ for (unsigned n = 0; n < threadCnt; ++n) {
+ if (taskQueues[(i + n) % threadCnt].tryPush(task)) return;
+ }
+ taskQueues[i % threadCnt].push(task);
+ //Sync
+ } else {
+ task->run(0);
+ }
+ }
+};
+
+}
+
+static TaskSchedulerImpl* inst = nullptr;
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void TaskScheduler::init(unsigned threads)
+{
+ if (inst) return;
+ inst = new TaskSchedulerImpl(threads);
+}
+
+
+void TaskScheduler::term()
+{
+ if (!inst) return;
+ delete(inst);
+ inst = nullptr;
+}
+
+
+void TaskScheduler::request(Task* task)
+{
+ if (inst) inst->request(task);
+}
+
+
+unsigned TaskScheduler::threads()
+{
+ if (inst) return inst->threadCnt;
+ return 0;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
new file mode 100644
index 0000000000..f30bdf9a8f
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_TASK_SCHEDULER_H_
+#define _TVG_TASK_SCHEDULER_H_
+
+#include <mutex>
+#include <condition_variable>
+#include "tvgCommon.h"
+
+namespace tvg
+{
+
+struct Task;
+
+struct TaskScheduler
+{
+ static unsigned threads();
+ static void init(unsigned threads);
+ static void term();
+ static void request(Task* task);
+};
+
+struct Task
+{
+private:
+ mutex mtx;
+ condition_variable cv;
+ bool ready{true};
+ bool pending{false};
+
+public:
+ virtual ~Task() = default;
+
+ void done()
+ {
+ if (!pending) return;
+
+ unique_lock<mutex> lock(mtx);
+ while (!ready) cv.wait(lock);
+ pending = false;
+ }
+
+protected:
+ virtual void run(unsigned tid) = 0;
+
+private:
+ void operator()(unsigned tid)
+ {
+ run(tid);
+
+ lock_guard<mutex> lock(mtx);
+ ready = true;
+ cv.notify_one();
+ }
+
+ void prepare()
+ {
+ ready = false;
+ pending = true;
+ }
+
+ friend class TaskSchedulerImpl;
+};
+
+
+
+}
+
+#endif //_TVG_TASK_SCHEDULER_H_
diff --git a/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp
new file mode 100644
index 0000000000..6f9416b69c
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <memory.h>
+#include <turbojpeg.h>
+#include "tvgLoader.h"
+#include "tvgJpgLoader.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+void JpgLoader::clear()
+{
+ if (freeData) free(data);
+ data = nullptr;
+ size = 0;
+ freeData = false;
+}
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+JpgLoader::JpgLoader()
+{
+ jpegDecompressor = tjInitDecompress();
+}
+
+
+JpgLoader::~JpgLoader()
+{
+ if (freeData) free(data);
+ tjDestroy(jpegDecompressor);
+
+ //This image is shared with raster engine.
+ tjFree(image);
+}
+
+
+bool JpgLoader::open(const string& path)
+{
+ clear();
+
+ auto jpegFile = fopen(path.c_str(), "rb");
+ if (!jpegFile) return false;
+
+ auto ret = false;
+
+ //determine size
+ if (fseek(jpegFile, 0, SEEK_END) < 0) goto finalize;
+ if (((size = ftell(jpegFile)) < 1)) goto finalize;
+ if (fseek(jpegFile, 0, SEEK_SET)) goto finalize;
+
+ data = (unsigned char *) malloc(size);
+ if (!data) goto finalize;
+
+ freeData = true;
+
+ if (fread(data, size, 1, jpegFile) < 1) goto failure;
+
+ int width, height, subSample, colorSpace;
+ if (tjDecompressHeader3(jpegDecompressor, data, size, &width, &height, &subSample, &colorSpace) < 0) {
+ TVGERR("JPG LOADER", "%s", tjGetErrorStr());
+ goto failure;
+ }
+
+ w = static_cast<float>(width);
+ h = static_cast<float>(height);
+ ret = true;
+
+ goto finalize;
+
+failure:
+ clear();
+
+finalize:
+ fclose(jpegFile);
+ return ret;
+}
+
+
+bool JpgLoader::open(const char* data, uint32_t size, bool copy)
+{
+ clear();
+
+ int width, height, subSample, colorSpace;
+ if (tjDecompressHeader3(jpegDecompressor, (unsigned char *) data, size, &width, &height, &subSample, &colorSpace) < 0) return false;
+
+ if (copy) {
+ this->data = (unsigned char *) malloc(size);
+ if (!this->data) return false;
+ memcpy((unsigned char *)this->data, data, size);
+ freeData = true;
+ } else {
+ this->data = (unsigned char *) data;
+ freeData = false;
+ }
+
+ w = static_cast<float>(width);
+ h = static_cast<float>(height);
+ this->size = size;
+
+ return true;
+}
+
+
+bool JpgLoader::read()
+{
+ if (image) tjFree(image);
+ image = (unsigned char *)tjAlloc(static_cast<int>(w) * static_cast<int>(h) * tjPixelSize[TJPF_BGRX]);
+ if (!image) return false;
+
+ //decompress jpg image
+ if (tjDecompress2(jpegDecompressor, data, size, image, static_cast<int>(w), 0, static_cast<int>(h), TJPF_BGRX, 0) < 0) {
+ TVGERR("JPG LOADER", "%s", tjGetErrorStr());
+ tjFree(image);
+ image = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+
+bool JpgLoader::close()
+{
+ clear();
+ return true;
+}
+
+
+unique_ptr<Surface> JpgLoader::bitmap()
+{
+ if (!image) return nullptr;
+
+ auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ surface->buffer = (uint32_t*)(image);
+ surface->stride = w;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = SwCanvas::ARGB8888;
+
+ return unique_ptr<Surface>(surface);
+}
diff --git a/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h
new file mode 100644
index 0000000000..7d35e57d72
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_JPG_LOADER_H_
+#define _TVG_JPG_LOADER_H_
+
+using tjhandle = void*;
+
+//TODO: Use Task?
+class JpgLoader : public LoadModule
+{
+public:
+ JpgLoader();
+ ~JpgLoader();
+
+ using LoadModule::open;
+ bool open(const string& path) override;
+ bool open(const char* data, uint32_t size, bool copy) override;
+ bool read() override;
+ bool close() override;
+
+ unique_ptr<Surface> bitmap() override;
+
+private:
+ void clear();
+
+ tjhandle jpegDecompressor;
+ unsigned char* data = nullptr;
+ unsigned char *image = nullptr;
+ unsigned long size = 0;
+ bool freeData = false;
+};
+
+#endif //_TVG_JPG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
new file mode 100644
index 0000000000..c3d281482a
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "tvgLoader.h"
+#include "tvgPngLoader.h"
+
+static inline uint32_t PREMULTIPLY(uint32_t c)
+{
+ auto a = (c >> 24);
+ return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff);
+}
+
+
+static void _premultiply(uint32_t* data, uint32_t w, uint32_t h)
+{
+ auto buffer = data;
+ for (uint32_t y = 0; y < h; ++y, buffer += w) {
+ auto src = buffer;
+ for (uint32_t x = 0; x < w; ++x, ++src) {
+ *src = PREMULTIPLY(*src);
+ }
+ }
+}
+
+
+PngLoader::PngLoader()
+{
+ image = static_cast<png_imagep>(calloc(1, sizeof(png_image)));
+ image->version = PNG_IMAGE_VERSION;
+ image->opaque = NULL;
+}
+
+PngLoader::~PngLoader()
+{
+ if (content) {
+ free((void*)content);
+ content = nullptr;
+ }
+ free(image);
+}
+
+bool PngLoader::open(const string& path)
+{
+ image->opaque = NULL;
+
+ if (!png_image_begin_read_from_file(image, path.c_str())) return false;
+
+ w = (float)image->width;
+ h = (float)image->height;
+
+ return true;
+}
+
+bool PngLoader::open(const char* data, uint32_t size, bool copy)
+{
+ image->opaque = NULL;
+
+ if (!png_image_begin_read_from_memory(image, data, size)) return false;
+
+ w = (float)image->width;
+ h = (float)image->height;
+
+ return true;
+}
+
+
+bool PngLoader::read()
+{
+ png_bytep buffer;
+ image->format = PNG_FORMAT_BGRA;
+ buffer = static_cast<png_bytep>(malloc(PNG_IMAGE_SIZE((*image))));
+ if (!buffer) {
+ //out of memory, only time when libpng doesnt free its data
+ png_image_free(image);
+ return false;
+ }
+ if (!png_image_finish_read(image, NULL, buffer, 0, NULL)) return false;
+ content = reinterpret_cast<uint32_t*>(buffer);
+
+ _premultiply(reinterpret_cast<uint32_t*>(buffer), image->width, image->height);
+
+ return true;
+}
+
+bool PngLoader::close()
+{
+ png_image_free(image);
+ return true;
+}
+
+unique_ptr<Surface> PngLoader::bitmap()
+{
+ if (!content) return nullptr;
+
+ auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ surface->buffer = (uint32_t*)(content);
+ surface->stride = w;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = SwCanvas::ARGB8888;
+
+ return unique_ptr<Surface>(surface);
+}
diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
new file mode 100644
index 0000000000..b42537c73f
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_PNG_LOADER_H_
+#define _TVG_PNG_LOADER_H_
+
+#include <png.h>
+
+class PngLoader : public LoadModule
+{
+public:
+ PngLoader();
+ ~PngLoader();
+
+ using LoadModule::open;
+ bool open(const string& path) override;
+ bool open(const char* data, uint32_t size, bool copy) override;
+ bool read() override;
+ bool close() override;
+
+ unique_ptr<Surface> bitmap() override;
+
+private:
+ png_imagep image = nullptr;
+ const uint32_t* content = nullptr;
+};
+
+#endif //_TVG_PNG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
new file mode 100644
index 0000000000..f27881da42
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <memory.h>
+#include "tvgLoader.h"
+#include "tvgJpgLoader.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+void JpgLoader::clear()
+{
+ jpgdDelete(decoder);
+ if (freeData) free(data);
+ decoder = nullptr;
+ data = nullptr;
+ freeData = false;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+
+JpgLoader::~JpgLoader()
+{
+ jpgdDelete(decoder);
+ if (freeData) free(data);
+ free(image);
+}
+
+
+bool JpgLoader::open(const string& path)
+{
+ clear();
+
+ int width, height;
+ decoder = jpgdHeader(path.c_str(), &width, &height);
+ if (!decoder) return false;
+
+ w = static_cast<float>(width);
+ h = static_cast<float>(height);
+
+ return true;
+}
+
+
+bool JpgLoader::open(const char* data, uint32_t size, bool copy)
+{
+ clear();
+
+ if (copy) {
+ this->data = (char *) malloc(size);
+ if (!this->data) return false;
+ memcpy((char *)this->data, data, size);
+ freeData = true;
+ } else {
+ this->data = (char *) data;
+ freeData = false;
+ }
+
+ int width, height;
+ decoder = jpgdHeader(this->data, size, &width, &height);
+ if (!decoder) return false;
+
+ w = static_cast<float>(width);
+ h = static_cast<float>(height);
+
+ return true;
+}
+
+
+
+bool JpgLoader::read()
+{
+ if (!decoder || w <= 0 || h <= 0) return false;
+
+ TaskScheduler::request(this);
+
+ return true;
+}
+
+
+bool JpgLoader::close()
+{
+ this->done();
+ clear();
+ return true;
+}
+
+
+unique_ptr<Surface> JpgLoader::bitmap()
+{
+ this->done();
+
+ if (!image) return nullptr;
+
+ auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ surface->buffer = (uint32_t*)(image);
+ surface->stride = w;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = SwCanvas::ARGB8888;
+
+ return unique_ptr<Surface>(surface);
+}
+
+
+void JpgLoader::run(unsigned tid)
+{
+ if (image) {
+ free(image);
+ image = nullptr;
+ }
+ image = jpgdDecompress(decoder);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
new file mode 100644
index 0000000000..39732dbc3a
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_JPG_LOADER_H_
+#define _TVG_JPG_LOADER_H_
+
+#include "tvgTaskScheduler.h"
+#include "tvgJpgd.h"
+
+class JpgLoader : public LoadModule, public Task
+{
+private:
+ jpeg_decoder* decoder = nullptr;
+ char* data = nullptr;
+ unsigned char *image = nullptr;
+ bool freeData = false;
+
+ void clear();
+
+public:
+ ~JpgLoader();
+
+ using LoadModule::open;
+ bool open(const string& path) override;
+ bool open(const char* data, uint32_t size, bool copy) override;
+ bool read() override;
+ bool close() override;
+
+ unique_ptr<Surface> bitmap() override;
+ void run(unsigned tid) override;
+};
+
+#endif //_TVG_JPG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
new file mode 100644
index 0000000000..4ccc5788d5
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
@@ -0,0 +1,3029 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+// jpgd.cpp - C++ class for JPEG decompression.
+// Public domain, Rich Geldreich <richgel99@gmail.com>
+// Alex Evans: Linear memory allocator (taken from jpge.h).
+// v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings (all looked harmless)
+//
+// Supports progressive and baseline sequential JPEG image files, and the most common chroma subsampling factors: Y, H1V1, H2V1, H1V2, and H2V2.
+//
+// Chroma upsampling quality: H2V2 is upsampled in the frequency domain, H2V1 and H1V2 are upsampled using point sampling.
+// Chroma upsampling reference: "Fast Scheme for Image Size Change in the Compressed Domain"
+// http://vision.ai.uiuc.edu/~dugad/research/dct/index.html
+
+#include <memory.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <stdint.h>
+#include "tvgJpgd.h"
+
+#ifdef _MSC_VER
+ #pragma warning (disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
+ #define JPGD_NORETURN __declspec(noreturn)
+#elif defined(__GNUC__)
+ #define JPGD_NORETURN __attribute__ ((noreturn))
+#else
+ #define JPGD_NORETURN
+#endif
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+// Set to 1 to enable freq. domain chroma upsampling on images using H2V2 subsampling (0=faster nearest neighbor sampling).
+// This is slower, but results in higher quality on images with highly saturated colors.
+#define JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING 1
+
+#define JPGD_ASSERT(x)
+#define JPGD_MAX(a,b) (((a)>(b)) ? (a) : (b))
+#define JPGD_MIN(a,b) (((a)<(b)) ? (a) : (b))
+
+typedef int16_t jpgd_quant_t;
+typedef int16_t jpgd_block_t;
+
+// Success/failure error codes.
+enum jpgd_status
+{
+ JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1,
+ JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
+ JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
+ JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
+ JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
+ JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
+ JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
+ JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR,
+ JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM
+};
+
+enum
+{
+ JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4,
+ JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384
+};
+
+// Input stream interface.
+// Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available.
+// The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set.
+// It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer.
+// Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding.
+struct jpeg_decoder_stream
+{
+ jpeg_decoder_stream() { }
+ virtual ~jpeg_decoder_stream() { }
+
+ // The read() method is called when the internal input buffer is empty.
+ // Parameters:
+ // pBuf - input buffer
+ // max_bytes_to_read - maximum bytes that can be written to pBuf
+ // pEOF_flag - set this to true if at end of stream (no more bytes remaining)
+ // Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0).
+ // Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full.
+ virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag) = 0;
+};
+
+
+// stdio FILE stream class.
+class jpeg_decoder_file_stream : public jpeg_decoder_stream
+{
+ jpeg_decoder_file_stream(const jpeg_decoder_file_stream &);
+ jpeg_decoder_file_stream &operator =(const jpeg_decoder_file_stream &);
+
+ FILE *m_pFile = nullptr;
+ bool m_eof_flag = false;
+ bool m_error_flag = false;
+
+public:
+ jpeg_decoder_file_stream() {}
+ virtual ~jpeg_decoder_file_stream();
+ bool open(const char *Pfilename);
+ void close();
+ virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag);
+ };
+
+
+// Memory stream class.
+class jpeg_decoder_mem_stream : public jpeg_decoder_stream
+{
+ const uint8_t *m_pSrc_data;
+ uint32_t m_ofs, m_size;
+
+public:
+ jpeg_decoder_mem_stream() : m_pSrc_data(nullptr), m_ofs(0), m_size(0) {}
+ jpeg_decoder_mem_stream(const uint8_t *pSrc_data, uint32_t size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) {}
+ virtual ~jpeg_decoder_mem_stream() {}
+ bool open(const uint8_t *pSrc_data, uint32_t size);
+ void close() { m_pSrc_data = nullptr; m_ofs = 0; m_size = 0; }
+ virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag);
+};
+
+
+class jpeg_decoder
+{
+public:
+ // Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc.
+ // methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline.
+ jpeg_decoder(jpeg_decoder_stream *pStream);
+ ~jpeg_decoder();
+
+ // Call this method after constructing the object to begin decompression.
+ // If JPGD_SUCCESS is returned you may then call decode() on each scanline.
+ int begin_decoding();
+ // Returns the next scan line.
+ // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1).
+ // Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4).
+ // Returns JPGD_SUCCESS if a scan line has been returned.
+ // Returns JPGD_DONE if all scan lines have been returned.
+ // Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info.
+ int decode(const void** pScan_line, uint32_t* pScan_line_len);
+ inline jpgd_status get_error_code() const { return m_error_code; }
+ inline int get_width() const { return m_image_x_size; }
+ inline int get_height() const { return m_image_y_size; }
+ inline int get_num_components() const { return m_comps_in_frame; }
+ inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; }
+ inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); }
+ // Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file).
+ inline int get_total_bytes_read() const { return m_total_bytes_read; }
+
+private:
+ jpeg_decoder(const jpeg_decoder &);
+ jpeg_decoder &operator =(const jpeg_decoder &);
+
+ typedef void (*pDecode_block_func)(jpeg_decoder *, int, int, int);
+
+ struct huff_tables
+ {
+ bool ac_table;
+ uint32_t look_up[256];
+ uint32_t look_up2[256];
+ uint8_t code_size[256];
+ uint32_t tree[512];
+ };
+
+ struct coeff_buf
+ {
+ uint8_t *pData;
+ int block_num_x, block_num_y;
+ int block_len_x, block_len_y;
+ int block_size;
+ };
+
+ struct mem_block
+ {
+ mem_block *m_pNext;
+ size_t m_used_count;
+ size_t m_size;
+ char m_data[1];
+ };
+
+ jmp_buf m_jmp_state;
+ mem_block *m_pMem_blocks;
+ int m_image_x_size;
+ int m_image_y_size;
+ jpeg_decoder_stream *m_pStream;
+ int m_progressive_flag;
+ uint8_t m_huff_ac[JPGD_MAX_HUFF_TABLES];
+ uint8_t* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size
+ uint8_t* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size
+ jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables
+ int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported)
+ int m_comps_in_frame; // # of components in frame
+ int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor
+ int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor
+ int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector
+ int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID
+ int m_comp_h_blocks[JPGD_MAX_COMPONENTS];
+ int m_comp_v_blocks[JPGD_MAX_COMPONENTS];
+ int m_comps_in_scan; // # of components in scan
+ int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan
+ int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector
+ int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector
+ int m_spectral_start; // spectral selection start
+ int m_spectral_end; // spectral selection end
+ int m_successive_low; // successive approximation low
+ int m_successive_high; // successive approximation high
+ int m_max_mcu_x_size; // MCU's max. X size in pixels
+ int m_max_mcu_y_size; // MCU's max. Y size in pixels
+ int m_blocks_per_mcu;
+ int m_max_blocks_per_row;
+ int m_mcus_per_row, m_mcus_per_col;
+ int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU];
+ int m_total_lines_left; // total # lines left in image
+ int m_mcu_lines_left; // total # lines left in this MCU
+ int m_real_dest_bytes_per_scan_line;
+ int m_dest_bytes_per_scan_line; // rounded up
+ int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y)
+ huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES];
+ coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS];
+ coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS];
+ int m_eob_run;
+ int m_block_y_mcu[JPGD_MAX_COMPONENTS];
+ uint8_t* m_pIn_buf_ofs;
+ int m_in_buf_left;
+ int m_tem_flag;
+ bool m_eof_flag;
+ uint8_t m_in_buf_pad_start[128];
+ uint8_t m_in_buf[JPGD_IN_BUF_SIZE + 128];
+ uint8_t m_in_buf_pad_end[128];
+ int m_bits_left;
+ uint32_t m_bit_buf;
+ int m_restart_interval;
+ int m_restarts_left;
+ int m_next_restart_num;
+ int m_max_mcus_per_row;
+ int m_max_blocks_per_mcu;
+ int m_expanded_blocks_per_mcu;
+ int m_expanded_blocks_per_row;
+ int m_expanded_blocks_per_component;
+ bool m_freq_domain_chroma_upsample;
+ int m_max_mcus_per_col;
+ uint32_t m_last_dc_val[JPGD_MAX_COMPONENTS];
+ jpgd_block_t* m_pMCU_coefficients;
+ int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU];
+ uint8_t* m_pSample_buf;
+ int m_crr[256];
+ int m_cbb[256];
+ int m_crg[256];
+ int m_cbg[256];
+ uint8_t* m_pScan_line_0;
+ uint8_t* m_pScan_line_1;
+ jpgd_status m_error_code;
+ bool m_ready_flag;
+ int m_total_bytes_read;
+
+ void free_all_blocks();
+ JPGD_NORETURN void stop_decoding(jpgd_status status);
+ void *alloc(size_t n, bool zero = false);
+ void word_clear(void *p, uint16_t c, uint32_t n);
+ void prep_in_buffer();
+ void read_dht_marker();
+ void read_dqt_marker();
+ void read_sof_marker();
+ void skip_variable_marker();
+ void read_dri_marker();
+ void read_sos_marker();
+ int next_marker();
+ int process_markers();
+ void locate_soi_marker();
+ void locate_sof_marker();
+ int locate_sos_marker();
+ void init(jpeg_decoder_stream * pStream);
+ void create_look_ups();
+ void fix_in_buffer();
+ void transform_mcu(int mcu_row);
+ void transform_mcu_expand(int mcu_row);
+ coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y);
+ inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y);
+ void load_next_row();
+ void decode_next_row();
+ void make_huff_table(int index, huff_tables *pH);
+ void check_quant_tables();
+ void check_huff_tables();
+ void calc_mcu_block_order();
+ int init_scan();
+ void init_frame();
+ void process_restart();
+ void decode_scan(pDecode_block_func decode_block_func);
+ void init_progressive();
+ void init_sequential();
+ void decode_start();
+ void decode_init(jpeg_decoder_stream * pStream);
+ void H2V2Convert();
+ void H2V1Convert();
+ void H1V2Convert();
+ void H1V1Convert();
+ void gray_convert();
+ void expanded_convert();
+ void find_eoi();
+ inline uint32_t get_char();
+ inline uint32_t get_char(bool *pPadding_flag);
+ inline void stuff_char(uint8_t q);
+ inline uint8_t get_octet();
+ inline uint32_t get_bits(int num_bits);
+ inline uint32_t get_bits_no_markers(int numbits);
+ inline int huff_decode(huff_tables *pH);
+ inline int huff_decode(huff_tables *pH, int& extrabits);
+ static inline uint8_t clamp(int i);
+ static void decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
+ static void decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
+ static void decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
+ static void decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
+};
+
+
+// DCT coefficients are stored in this sequence.
+static int g_ZAG[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
+
+enum JPEG_MARKER
+{
+ M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8,
+ M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC,
+ M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7,
+ M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF,
+ M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0
+};
+
+enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 };
+
+#define CONST_BITS 13
+#define PASS1_BITS 2
+#define SCALEDONE ((int32_t)1)
+#define DESCALE(x,n) (((x) + (SCALEDONE << ((n)-1))) >> (n))
+#define DESCALE_ZEROSHIFT(x,n) (((x) + (128 << (n)) + (SCALEDONE << ((n)-1))) >> (n))
+#define MULTIPLY(var, cnst) ((var) * (cnst))
+#define CLAMP(i) ((static_cast<uint32_t>(i) > 255) ? (((~i) >> 31) & 0xFF) : (i))
+
+#define FIX_0_298631336 ((int32_t)2446) /* FIX(0.298631336) */
+#define FIX_0_390180644 ((int32_t)3196) /* FIX(0.390180644) */
+#define FIX_0_541196100 ((int32_t)4433) /* FIX(0.541196100) */
+#define FIX_0_765366865 ((int32_t)6270) /* FIX(0.765366865) */
+#define FIX_0_899976223 ((int32_t)7373) /* FIX(0.899976223) */
+#define FIX_1_175875602 ((int32_t)9633) /* FIX(1.175875602) */
+#define FIX_1_501321110 ((int32_t)12299) /* FIX(1.501321110) */
+#define FIX_1_847759065 ((int32_t)15137) /* FIX(1.847759065) */
+#define FIX_1_961570560 ((int32_t)16069) /* FIX(1.961570560) */
+#define FIX_2_053119869 ((int32_t)16819) /* FIX(2.053119869) */
+#define FIX_2_562915447 ((int32_t)20995) /* FIX(2.562915447) */
+#define FIX_3_072711026 ((int32_t)25172) /* FIX(3.072711026) */
+
+
+// Compiler creates a fast path 1D IDCT for X non-zero columns
+template <int NONZERO_COLS>
+struct Row
+{
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
+ {
+ // ACCESS_COL() will be optimized at compile time to either an array access, or 0.
+ #define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0)
+
+ const int z2 = ACCESS_COL(2), z3 = ACCESS_COL(6);
+ const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
+ const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
+ const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
+
+ const int tmp0 = (ACCESS_COL(0) + ACCESS_COL(4)) << CONST_BITS;
+ const int tmp1 = (ACCESS_COL(0) - ACCESS_COL(4)) << CONST_BITS;
+
+ const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
+
+ const int atmp0 = ACCESS_COL(7), atmp1 = ACCESS_COL(5), atmp2 = ACCESS_COL(3), atmp3 = ACCESS_COL(1);
+
+ const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
+ const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
+
+ const int az1 = MULTIPLY(bz1, - FIX_0_899976223);
+ const int az2 = MULTIPLY(bz2, - FIX_2_562915447);
+ const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5;
+ const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5;
+
+ const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
+ const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
+ const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
+ const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
+
+ pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS-PASS1_BITS);
+ pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS-PASS1_BITS);
+ pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS-PASS1_BITS);
+ pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS-PASS1_BITS);
+ pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS-PASS1_BITS);
+ pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS-PASS1_BITS);
+ pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS-PASS1_BITS);
+ pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS-PASS1_BITS);
+ }
+};
+
+
+template <>
+struct Row<0>
+{
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
+ {
+#ifdef _MSC_VER
+ pTemp; pSrc;
+#endif
+ }
+};
+
+
+template <>
+struct Row<1>
+{
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
+ {
+ const int dcval = (pSrc[0] << PASS1_BITS);
+
+ pTemp[0] = dcval;
+ pTemp[1] = dcval;
+ pTemp[2] = dcval;
+ pTemp[3] = dcval;
+ pTemp[4] = dcval;
+ pTemp[5] = dcval;
+ pTemp[6] = dcval;
+ pTemp[7] = dcval;
+ }
+};
+
+
+// Compiler creates a fast path 1D IDCT for X non-zero rows
+template <int NONZERO_ROWS>
+struct Col
+{
+ static void idct(uint8_t* pDst_ptr, const int* pTemp)
+ {
+ // ACCESS_ROW() will be optimized at compile time to either an array access, or 0.
+ #define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0)
+
+ const int z2 = ACCESS_ROW(2);
+ const int z3 = ACCESS_ROW(6);
+
+ const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
+ const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
+ const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
+
+ const int tmp0 = (ACCESS_ROW(0) + ACCESS_ROW(4)) << CONST_BITS;
+ const int tmp1 = (ACCESS_ROW(0) - ACCESS_ROW(4)) << CONST_BITS;
+
+ const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
+
+ const int atmp0 = ACCESS_ROW(7), atmp1 = ACCESS_ROW(5), atmp2 = ACCESS_ROW(3), atmp3 = ACCESS_ROW(1);
+
+ const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
+ const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
+
+ const int az1 = MULTIPLY(bz1, - FIX_0_899976223);
+ const int az2 = MULTIPLY(bz2, - FIX_2_562915447);
+ const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5;
+ const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5;
+
+ const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
+ const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
+ const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
+ const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
+
+ int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*0] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*7] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*1] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*6] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*2] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*5] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*3] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*4] = (uint8_t)CLAMP(i);
+ }
+};
+
+
+template <>
+struct Col<1>
+{
+ static void idct(uint8_t* pDst_ptr, const int* pTemp)
+ {
+ int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS+3);
+ const uint8_t dcval_clamped = (uint8_t)CLAMP(dcval);
+ pDst_ptr[0*8] = dcval_clamped;
+ pDst_ptr[1*8] = dcval_clamped;
+ pDst_ptr[2*8] = dcval_clamped;
+ pDst_ptr[3*8] = dcval_clamped;
+ pDst_ptr[4*8] = dcval_clamped;
+ pDst_ptr[5*8] = dcval_clamped;
+ pDst_ptr[6*8] = dcval_clamped;
+ pDst_ptr[7*8] = dcval_clamped;
+ }
+};
+
+
+static const uint8_t s_idct_row_table[] = {
+ 1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0,
+ 4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0,
+ 6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0,
+ 6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0,
+ 8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2,
+ 8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2,
+ 8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4,
+ 8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8,
+};
+
+
+static const uint8_t s_idct_col_table[] = { 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 };
+
+
+void idct(const jpgd_block_t* pSrc_ptr, uint8_t* pDst_ptr, int block_max_zag)
+{
+ JPGD_ASSERT(block_max_zag >= 1);
+ JPGD_ASSERT(block_max_zag <= 64);
+
+ if (block_max_zag <= 1) {
+ int k = ((pSrc_ptr[0] + 4) >> 3) + 128;
+ k = CLAMP(k);
+ k = k | (k<<8);
+ k = k | (k<<16);
+ for (int i = 8; i > 0; i--) {
+ *(int*)&pDst_ptr[0] = k;
+ *(int*)&pDst_ptr[4] = k;
+ pDst_ptr += 8;
+ }
+ return;
+ }
+
+ int temp[64];
+ const jpgd_block_t* pSrc = pSrc_ptr;
+ int* pTemp = temp;
+ const uint8_t* pRow_tab = &s_idct_row_table[(block_max_zag - 1) * 8];
+ int i;
+ for (i = 8; i > 0; i--, pRow_tab++) {
+ switch (*pRow_tab) {
+ case 0: Row<0>::idct(pTemp, pSrc); break;
+ case 1: Row<1>::idct(pTemp, pSrc); break;
+ case 2: Row<2>::idct(pTemp, pSrc); break;
+ case 3: Row<3>::idct(pTemp, pSrc); break;
+ case 4: Row<4>::idct(pTemp, pSrc); break;
+ case 5: Row<5>::idct(pTemp, pSrc); break;
+ case 6: Row<6>::idct(pTemp, pSrc); break;
+ case 7: Row<7>::idct(pTemp, pSrc); break;
+ case 8: Row<8>::idct(pTemp, pSrc); break;
+ }
+ pSrc += 8;
+ pTemp += 8;
+ }
+
+ pTemp = temp;
+
+ const int nonzero_rows = s_idct_col_table[block_max_zag - 1];
+ for (i = 8; i > 0; i--) {
+ switch (nonzero_rows) {
+ case 1: Col<1>::idct(pDst_ptr, pTemp); break;
+ case 2: Col<2>::idct(pDst_ptr, pTemp); break;
+ case 3: Col<3>::idct(pDst_ptr, pTemp); break;
+ case 4: Col<4>::idct(pDst_ptr, pTemp); break;
+ case 5: Col<5>::idct(pDst_ptr, pTemp); break;
+ case 6: Col<6>::idct(pDst_ptr, pTemp); break;
+ case 7: Col<7>::idct(pDst_ptr, pTemp); break;
+ case 8: Col<8>::idct(pDst_ptr, pTemp); break;
+ }
+ pTemp++;
+ pDst_ptr++;
+ }
+}
+
+
+void idct_4x4(const jpgd_block_t* pSrc_ptr, uint8_t* pDst_ptr)
+{
+ int temp[64];
+ int* pTemp = temp;
+ const jpgd_block_t* pSrc = pSrc_ptr;
+
+ for (int i = 4; i > 0; i--) {
+ Row<4>::idct(pTemp, pSrc);
+ pSrc += 8;
+ pTemp += 8;
+ }
+
+ pTemp = temp;
+
+ for (int i = 8; i > 0; i--) {
+ Col<4>::idct(pDst_ptr, pTemp);
+ pTemp++;
+ pDst_ptr++;
+ }
+}
+
+
+// Retrieve one character from the input stream.
+inline uint32_t jpeg_decoder::get_char()
+{
+ // Any bytes remaining in buffer?
+ if (!m_in_buf_left) {
+ // Try to get more bytes.
+ prep_in_buffer();
+ // Still nothing to get?
+ if (!m_in_buf_left) {
+ // Pad the end of the stream with 0xFF 0xD9 (EOI marker)
+ int t = m_tem_flag;
+ m_tem_flag ^= 1;
+ if (t) return 0xD9;
+ else return 0xFF;
+ }
+ }
+ uint32_t c = *m_pIn_buf_ofs++;
+ m_in_buf_left--;
+ return c;
+}
+
+
+// Same as previous method, except can indicate if the character is a pad character or not.
+inline uint32_t jpeg_decoder::get_char(bool *pPadding_flag)
+{
+ if (!m_in_buf_left) {
+ prep_in_buffer();
+ if (!m_in_buf_left) {
+ *pPadding_flag = true;
+ int t = m_tem_flag;
+ m_tem_flag ^= 1;
+ if (t) return 0xD9;
+ else return 0xFF;
+ }
+ }
+ *pPadding_flag = false;
+ uint32_t c = *m_pIn_buf_ofs++;
+ m_in_buf_left--;
+
+ return c;
+}
+
+
+// Inserts a previously retrieved character back into the input buffer.
+inline void jpeg_decoder::stuff_char(uint8_t q)
+{
+ *(--m_pIn_buf_ofs) = q;
+ m_in_buf_left++;
+}
+
+
+// Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered.
+inline uint8_t jpeg_decoder::get_octet()
+{
+ bool padding_flag;
+ int c = get_char(&padding_flag);
+
+ if (c == 0xFF) {
+ if (padding_flag) return 0xFF;
+
+ c = get_char(&padding_flag);
+ if (padding_flag) {
+ stuff_char(0xFF);
+ return 0xFF;
+ }
+ if (c == 0x00) return 0xFF;
+ else {
+ stuff_char(static_cast<uint8_t>(c));
+ stuff_char(0xFF);
+ return 0xFF;
+ }
+ }
+ return static_cast<uint8_t>(c);
+}
+
+
+// Retrieves a variable number of bits from the input stream. Does not recognize markers.
+inline uint32_t jpeg_decoder::get_bits(int num_bits)
+{
+ if (!num_bits) return 0;
+
+ uint32_t i = m_bit_buf >> (32 - num_bits);
+
+ if ((m_bits_left -= num_bits) <= 0) {
+ m_bit_buf <<= (num_bits += m_bits_left);
+ uint32_t c1 = get_char();
+ uint32_t c2 = get_char();
+ m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2;
+ m_bit_buf <<= -m_bits_left;
+ m_bits_left += 16;
+ JPGD_ASSERT(m_bits_left >= 0);
+ }
+ else m_bit_buf <<= num_bits;
+
+ return i;
+}
+
+
+// Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered.
+inline uint32_t jpeg_decoder::get_bits_no_markers(int num_bits)
+{
+ if (!num_bits)return 0;
+
+ uint32_t i = m_bit_buf >> (32 - num_bits);
+
+ if ((m_bits_left -= num_bits) <= 0) {
+ m_bit_buf <<= (num_bits += m_bits_left);
+ if ((m_in_buf_left < 2) || (m_pIn_buf_ofs[0] == 0xFF) || (m_pIn_buf_ofs[1] == 0xFF)) {
+ uint32_t c1 = get_octet();
+ uint32_t c2 = get_octet();
+ m_bit_buf |= (c1 << 8) | c2;
+ } else {
+ m_bit_buf |= ((uint32_t)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1];
+ m_in_buf_left -= 2;
+ m_pIn_buf_ofs += 2;
+ }
+ m_bit_buf <<= -m_bits_left;
+ m_bits_left += 16;
+ JPGD_ASSERT(m_bits_left >= 0);
+ } else m_bit_buf <<= num_bits;
+
+ return i;
+}
+
+
+// Decodes a Huffman encoded symbol.
+inline int jpeg_decoder::huff_decode(huff_tables *pH)
+{
+ int symbol;
+
+ // Check first 8-bits: do we have a complete symbol?
+ if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0) {
+ // Decode more bits, use a tree traversal to find symbol.
+ int ofs = 23;
+ do {
+ symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
+ ofs--;
+ } while (symbol < 0);
+ get_bits_no_markers(8 + (23 - ofs));
+ } else get_bits_no_markers(pH->code_size[symbol]);
+
+ return symbol;
+}
+
+
+// Decodes a Huffman encoded symbol.
+inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits)
+{
+ int symbol;
+
+ // Check first 8-bits: do we have a complete symbol?
+ if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0) {
+ // Use a tree traversal to find symbol.
+ int ofs = 23;
+ do {
+ symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
+ ofs--;
+ } while (symbol < 0);
+
+ get_bits_no_markers(8 + (23 - ofs));
+ extra_bits = get_bits_no_markers(symbol & 0xF);
+ } else {
+ JPGD_ASSERT(((symbol >> 8) & 31) == pH->code_size[symbol & 255] + ((symbol & 0x8000) ? (symbol & 15) : 0));
+
+ if (symbol & 0x8000) {
+ get_bits_no_markers((symbol >> 8) & 31);
+ extra_bits = symbol >> 16;
+ } else {
+ int code_size = (symbol >> 8) & 31;
+ int num_extra_bits = symbol & 0xF;
+ int bits = code_size + num_extra_bits;
+ if (bits <= (m_bits_left + 16)) extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1);
+ else {
+ get_bits_no_markers(code_size);
+ extra_bits = get_bits_no_markers(num_extra_bits);
+ }
+ }
+ symbol &= 0xFF;
+ }
+ return symbol;
+}
+
+
+// Tables and macro used to fully decode the DPCM differences.
+static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
+static const unsigned int s_extend_offset[16] = { 0, ((-1u)<<1) + 1, ((-1u)<<2) + 1, ((-1u)<<3) + 1, ((-1u)<<4) + 1, ((-1u)<<5) + 1, ((-1u)<<6) + 1, ((-1u)<<7) + 1, ((-1u)<<8) + 1, ((-1u)<<9) + 1, ((-1u)<<10) + 1, ((-1u)<<11) + 1, ((-1u)<<12) + 1, ((-1u)<<13) + 1, ((-1u)<<14) + 1, ((-1u)<<15) + 1 };
+
+// The logical AND's in this macro are to shut up static code analysis (aren't really necessary - couldn't find another way to do this)
+#define JPGD_HUFF_EXTEND(x, s) (((x) < s_extend_test[s & 15]) ? ((x) + s_extend_offset[s & 15]) : (x))
+
+
+// Clamps a value between 0-255.
+inline uint8_t jpeg_decoder::clamp(int i)
+{
+ if (static_cast<uint32_t>(i) > 255) i = (((~i) >> 31) & 0xFF);
+ return static_cast<uint8_t>(i);
+}
+
+
+namespace DCT_Upsample
+{
+ struct Matrix44
+ {
+ typedef int Element_Type;
+ enum { NUM_ROWS = 4, NUM_COLS = 4 };
+
+ Element_Type v[NUM_ROWS][NUM_COLS];
+
+ inline int rows() const { return NUM_ROWS; }
+ inline int cols() const { return NUM_COLS; }
+ inline const Element_Type & at(int r, int c) const { return v[r][c]; }
+ inline Element_Type & at(int r, int c) { return v[r][c]; }
+
+ inline Matrix44() {}
+
+ inline Matrix44& operator += (const Matrix44& a)
+ {
+ for (int r = 0; r < NUM_ROWS; r++) {
+ at(r, 0) += a.at(r, 0);
+ at(r, 1) += a.at(r, 1);
+ at(r, 2) += a.at(r, 2);
+ at(r, 3) += a.at(r, 3);
+ }
+ return *this;
+ }
+
+ inline Matrix44& operator -= (const Matrix44& a)
+ {
+ for (int r = 0; r < NUM_ROWS; r++) {
+ at(r, 0) -= a.at(r, 0);
+ at(r, 1) -= a.at(r, 1);
+ at(r, 2) -= a.at(r, 2);
+ at(r, 3) -= a.at(r, 3);
+ }
+ return *this;
+ }
+
+ friend inline Matrix44 operator + (const Matrix44& a, const Matrix44& b)
+ {
+ Matrix44 ret;
+ for (int r = 0; r < NUM_ROWS; r++) {
+ ret.at(r, 0) = a.at(r, 0) + b.at(r, 0);
+ ret.at(r, 1) = a.at(r, 1) + b.at(r, 1);
+ ret.at(r, 2) = a.at(r, 2) + b.at(r, 2);
+ ret.at(r, 3) = a.at(r, 3) + b.at(r, 3);
+ }
+ return ret;
+ }
+
+ friend inline Matrix44 operator - (const Matrix44& a, const Matrix44& b)
+ {
+ Matrix44 ret;
+ for (int r = 0; r < NUM_ROWS; r++) {
+ ret.at(r, 0) = a.at(r, 0) - b.at(r, 0);
+ ret.at(r, 1) = a.at(r, 1) - b.at(r, 1);
+ ret.at(r, 2) = a.at(r, 2) - b.at(r, 2);
+ ret.at(r, 3) = a.at(r, 3) - b.at(r, 3);
+ }
+ return ret;
+ }
+
+ static inline void add_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b)
+ {
+ for (int r = 0; r < 4; r++) {
+ pDst[0*8 + r] = static_cast<jpgd_block_t>(a.at(r, 0) + b.at(r, 0));
+ pDst[1*8 + r] = static_cast<jpgd_block_t>(a.at(r, 1) + b.at(r, 1));
+ pDst[2*8 + r] = static_cast<jpgd_block_t>(a.at(r, 2) + b.at(r, 2));
+ pDst[3*8 + r] = static_cast<jpgd_block_t>(a.at(r, 3) + b.at(r, 3));
+ }
+ }
+
+ static inline void sub_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b)
+ {
+ for (int r = 0; r < 4; r++) {
+ pDst[0*8 + r] = static_cast<jpgd_block_t>(a.at(r, 0) - b.at(r, 0));
+ pDst[1*8 + r] = static_cast<jpgd_block_t>(a.at(r, 1) - b.at(r, 1));
+ pDst[2*8 + r] = static_cast<jpgd_block_t>(a.at(r, 2) - b.at(r, 2));
+ pDst[3*8 + r] = static_cast<jpgd_block_t>(a.at(r, 3) - b.at(r, 3));
+ }
+ }
+ };
+
+ const int FRACT_BITS = 10;
+ const int SCALE = 1 << FRACT_BITS;
+
+ typedef int Temp_Type;
+ #define D(i) (((i) + (SCALE >> 1)) >> FRACT_BITS)
+ #define F(i) ((int)((i) * SCALE + .5f))
+
+ // Any decent C++ compiler will optimize this at compile time to a 0, or an array access.
+ #define AT(c, r) ((((c)>=NUM_COLS)||((r)>=NUM_ROWS)) ? 0 : pSrc[(c)+(r)*8])
+
+ // NUM_ROWS/NUM_COLS = # of non-zero rows/cols in input matrix
+ template<int NUM_ROWS, int NUM_COLS>
+ struct P_Q
+ {
+ static void calc(Matrix44& P, Matrix44& Q, const jpgd_block_t* pSrc)
+ {
+ // 4x8 = 4x8 times 8x8, matrix 0 is constant
+ const Temp_Type X000 = AT(0, 0);
+ const Temp_Type X001 = AT(0, 1);
+ const Temp_Type X002 = AT(0, 2);
+ const Temp_Type X003 = AT(0, 3);
+ const Temp_Type X004 = AT(0, 4);
+ const Temp_Type X005 = AT(0, 5);
+ const Temp_Type X006 = AT(0, 6);
+ const Temp_Type X007 = AT(0, 7);
+ const Temp_Type X010 = D(F(0.415735f) * AT(1, 0) + F(0.791065f) * AT(3, 0) + F(-0.352443f) * AT(5, 0) + F(0.277785f) * AT(7, 0));
+ const Temp_Type X011 = D(F(0.415735f) * AT(1, 1) + F(0.791065f) * AT(3, 1) + F(-0.352443f) * AT(5, 1) + F(0.277785f) * AT(7, 1));
+ const Temp_Type X012 = D(F(0.415735f) * AT(1, 2) + F(0.791065f) * AT(3, 2) + F(-0.352443f) * AT(5, 2) + F(0.277785f) * AT(7, 2));
+ const Temp_Type X013 = D(F(0.415735f) * AT(1, 3) + F(0.791065f) * AT(3, 3) + F(-0.352443f) * AT(5, 3) + F(0.277785f) * AT(7, 3));
+ const Temp_Type X014 = D(F(0.415735f) * AT(1, 4) + F(0.791065f) * AT(3, 4) + F(-0.352443f) * AT(5, 4) + F(0.277785f) * AT(7, 4));
+ const Temp_Type X015 = D(F(0.415735f) * AT(1, 5) + F(0.791065f) * AT(3, 5) + F(-0.352443f) * AT(5, 5) + F(0.277785f) * AT(7, 5));
+ const Temp_Type X016 = D(F(0.415735f) * AT(1, 6) + F(0.791065f) * AT(3, 6) + F(-0.352443f) * AT(5, 6) + F(0.277785f) * AT(7, 6));
+ const Temp_Type X017 = D(F(0.415735f) * AT(1, 7) + F(0.791065f) * AT(3, 7) + F(-0.352443f) * AT(5, 7) + F(0.277785f) * AT(7, 7));
+ const Temp_Type X020 = AT(4, 0);
+ const Temp_Type X021 = AT(4, 1);
+ const Temp_Type X022 = AT(4, 2);
+ const Temp_Type X023 = AT(4, 3);
+ const Temp_Type X024 = AT(4, 4);
+ const Temp_Type X025 = AT(4, 5);
+ const Temp_Type X026 = AT(4, 6);
+ const Temp_Type X027 = AT(4, 7);
+ const Temp_Type X030 = D(F(0.022887f) * AT(1, 0) + F(-0.097545f) * AT(3, 0) + F(0.490393f) * AT(5, 0) + F(0.865723f) * AT(7, 0));
+ const Temp_Type X031 = D(F(0.022887f) * AT(1, 1) + F(-0.097545f) * AT(3, 1) + F(0.490393f) * AT(5, 1) + F(0.865723f) * AT(7, 1));
+ const Temp_Type X032 = D(F(0.022887f) * AT(1, 2) + F(-0.097545f) * AT(3, 2) + F(0.490393f) * AT(5, 2) + F(0.865723f) * AT(7, 2));
+ const Temp_Type X033 = D(F(0.022887f) * AT(1, 3) + F(-0.097545f) * AT(3, 3) + F(0.490393f) * AT(5, 3) + F(0.865723f) * AT(7, 3));
+ const Temp_Type X034 = D(F(0.022887f) * AT(1, 4) + F(-0.097545f) * AT(3, 4) + F(0.490393f) * AT(5, 4) + F(0.865723f) * AT(7, 4));
+ const Temp_Type X035 = D(F(0.022887f) * AT(1, 5) + F(-0.097545f) * AT(3, 5) + F(0.490393f) * AT(5, 5) + F(0.865723f) * AT(7, 5));
+ const Temp_Type X036 = D(F(0.022887f) * AT(1, 6) + F(-0.097545f) * AT(3, 6) + F(0.490393f) * AT(5, 6) + F(0.865723f) * AT(7, 6));
+ const Temp_Type X037 = D(F(0.022887f) * AT(1, 7) + F(-0.097545f) * AT(3, 7) + F(0.490393f) * AT(5, 7) + F(0.865723f) * AT(7, 7));
+
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
+ P.at(0, 0) = X000;
+ P.at(0, 1) = D(X001 * F(0.415735f) + X003 * F(0.791065f) + X005 * F(-0.352443f) + X007 * F(0.277785f));
+ P.at(0, 2) = X004;
+ P.at(0, 3) = D(X001 * F(0.022887f) + X003 * F(-0.097545f) + X005 * F(0.490393f) + X007 * F(0.865723f));
+ P.at(1, 0) = X010;
+ P.at(1, 1) = D(X011 * F(0.415735f) + X013 * F(0.791065f) + X015 * F(-0.352443f) + X017 * F(0.277785f));
+ P.at(1, 2) = X014;
+ P.at(1, 3) = D(X011 * F(0.022887f) + X013 * F(-0.097545f) + X015 * F(0.490393f) + X017 * F(0.865723f));
+ P.at(2, 0) = X020;
+ P.at(2, 1) = D(X021 * F(0.415735f) + X023 * F(0.791065f) + X025 * F(-0.352443f) + X027 * F(0.277785f));
+ P.at(2, 2) = X024;
+ P.at(2, 3) = D(X021 * F(0.022887f) + X023 * F(-0.097545f) + X025 * F(0.490393f) + X027 * F(0.865723f));
+ P.at(3, 0) = X030;
+ P.at(3, 1) = D(X031 * F(0.415735f) + X033 * F(0.791065f) + X035 * F(-0.352443f) + X037 * F(0.277785f));
+ P.at(3, 2) = X034;
+ P.at(3, 3) = D(X031 * F(0.022887f) + X033 * F(-0.097545f) + X035 * F(0.490393f) + X037 * F(0.865723f));
+ // 40 muls 24 adds
+
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
+ Q.at(0, 0) = D(X001 * F(0.906127f) + X003 * F(-0.318190f) + X005 * F(0.212608f) + X007 * F(-0.180240f));
+ Q.at(0, 1) = X002;
+ Q.at(0, 2) = D(X001 * F(-0.074658f) + X003 * F(0.513280f) + X005 * F(0.768178f) + X007 * F(-0.375330f));
+ Q.at(0, 3) = X006;
+ Q.at(1, 0) = D(X011 * F(0.906127f) + X013 * F(-0.318190f) + X015 * F(0.212608f) + X017 * F(-0.180240f));
+ Q.at(1, 1) = X012;
+ Q.at(1, 2) = D(X011 * F(-0.074658f) + X013 * F(0.513280f) + X015 * F(0.768178f) + X017 * F(-0.375330f));
+ Q.at(1, 3) = X016;
+ Q.at(2, 0) = D(X021 * F(0.906127f) + X023 * F(-0.318190f) + X025 * F(0.212608f) + X027 * F(-0.180240f));
+ Q.at(2, 1) = X022;
+ Q.at(2, 2) = D(X021 * F(-0.074658f) + X023 * F(0.513280f) + X025 * F(0.768178f) + X027 * F(-0.375330f));
+ Q.at(2, 3) = X026;
+ Q.at(3, 0) = D(X031 * F(0.906127f) + X033 * F(-0.318190f) + X035 * F(0.212608f) + X037 * F(-0.180240f));
+ Q.at(3, 1) = X032;
+ Q.at(3, 2) = D(X031 * F(-0.074658f) + X033 * F(0.513280f) + X035 * F(0.768178f) + X037 * F(-0.375330f));
+ Q.at(3, 3) = X036;
+ // 40 muls 24 adds
+ }
+ };
+
+
+ template<int NUM_ROWS, int NUM_COLS>
+ struct R_S
+ {
+ static void calc(Matrix44& R, Matrix44& S, const jpgd_block_t* pSrc)
+ {
+ // 4x8 = 4x8 times 8x8, matrix 0 is constant
+ const Temp_Type X100 = D(F(0.906127f) * AT(1, 0) + F(-0.318190f) * AT(3, 0) + F(0.212608f) * AT(5, 0) + F(-0.180240f) * AT(7, 0));
+ const Temp_Type X101 = D(F(0.906127f) * AT(1, 1) + F(-0.318190f) * AT(3, 1) + F(0.212608f) * AT(5, 1) + F(-0.180240f) * AT(7, 1));
+ const Temp_Type X102 = D(F(0.906127f) * AT(1, 2) + F(-0.318190f) * AT(3, 2) + F(0.212608f) * AT(5, 2) + F(-0.180240f) * AT(7, 2));
+ const Temp_Type X103 = D(F(0.906127f) * AT(1, 3) + F(-0.318190f) * AT(3, 3) + F(0.212608f) * AT(5, 3) + F(-0.180240f) * AT(7, 3));
+ const Temp_Type X104 = D(F(0.906127f) * AT(1, 4) + F(-0.318190f) * AT(3, 4) + F(0.212608f) * AT(5, 4) + F(-0.180240f) * AT(7, 4));
+ const Temp_Type X105 = D(F(0.906127f) * AT(1, 5) + F(-0.318190f) * AT(3, 5) + F(0.212608f) * AT(5, 5) + F(-0.180240f) * AT(7, 5));
+ const Temp_Type X106 = D(F(0.906127f) * AT(1, 6) + F(-0.318190f) * AT(3, 6) + F(0.212608f) * AT(5, 6) + F(-0.180240f) * AT(7, 6));
+ const Temp_Type X107 = D(F(0.906127f) * AT(1, 7) + F(-0.318190f) * AT(3, 7) + F(0.212608f) * AT(5, 7) + F(-0.180240f) * AT(7, 7));
+ const Temp_Type X110 = AT(2, 0);
+ const Temp_Type X111 = AT(2, 1);
+ const Temp_Type X112 = AT(2, 2);
+ const Temp_Type X113 = AT(2, 3);
+ const Temp_Type X114 = AT(2, 4);
+ const Temp_Type X115 = AT(2, 5);
+ const Temp_Type X116 = AT(2, 6);
+ const Temp_Type X117 = AT(2, 7);
+ const Temp_Type X120 = D(F(-0.074658f) * AT(1, 0) + F(0.513280f) * AT(3, 0) + F(0.768178f) * AT(5, 0) + F(-0.375330f) * AT(7, 0));
+ const Temp_Type X121 = D(F(-0.074658f) * AT(1, 1) + F(0.513280f) * AT(3, 1) + F(0.768178f) * AT(5, 1) + F(-0.375330f) * AT(7, 1));
+ const Temp_Type X122 = D(F(-0.074658f) * AT(1, 2) + F(0.513280f) * AT(3, 2) + F(0.768178f) * AT(5, 2) + F(-0.375330f) * AT(7, 2));
+ const Temp_Type X123 = D(F(-0.074658f) * AT(1, 3) + F(0.513280f) * AT(3, 3) + F(0.768178f) * AT(5, 3) + F(-0.375330f) * AT(7, 3));
+ const Temp_Type X124 = D(F(-0.074658f) * AT(1, 4) + F(0.513280f) * AT(3, 4) + F(0.768178f) * AT(5, 4) + F(-0.375330f) * AT(7, 4));
+ const Temp_Type X125 = D(F(-0.074658f) * AT(1, 5) + F(0.513280f) * AT(3, 5) + F(0.768178f) * AT(5, 5) + F(-0.375330f) * AT(7, 5));
+ const Temp_Type X126 = D(F(-0.074658f) * AT(1, 6) + F(0.513280f) * AT(3, 6) + F(0.768178f) * AT(5, 6) + F(-0.375330f) * AT(7, 6));
+ const Temp_Type X127 = D(F(-0.074658f) * AT(1, 7) + F(0.513280f) * AT(3, 7) + F(0.768178f) * AT(5, 7) + F(-0.375330f) * AT(7, 7));
+ const Temp_Type X130 = AT(6, 0);
+ const Temp_Type X131 = AT(6, 1);
+ const Temp_Type X132 = AT(6, 2);
+ const Temp_Type X133 = AT(6, 3);
+ const Temp_Type X134 = AT(6, 4);
+ const Temp_Type X135 = AT(6, 5);
+ const Temp_Type X136 = AT(6, 6);
+ const Temp_Type X137 = AT(6, 7);
+ // 80 muls 48 adds
+
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
+ R.at(0, 0) = X100;
+ R.at(0, 1) = D(X101 * F(0.415735f) + X103 * F(0.791065f) + X105 * F(-0.352443f) + X107 * F(0.277785f));
+ R.at(0, 2) = X104;
+ R.at(0, 3) = D(X101 * F(0.022887f) + X103 * F(-0.097545f) + X105 * F(0.490393f) + X107 * F(0.865723f));
+ R.at(1, 0) = X110;
+ R.at(1, 1) = D(X111 * F(0.415735f) + X113 * F(0.791065f) + X115 * F(-0.352443f) + X117 * F(0.277785f));
+ R.at(1, 2) = X114;
+ R.at(1, 3) = D(X111 * F(0.022887f) + X113 * F(-0.097545f) + X115 * F(0.490393f) + X117 * F(0.865723f));
+ R.at(2, 0) = X120;
+ R.at(2, 1) = D(X121 * F(0.415735f) + X123 * F(0.791065f) + X125 * F(-0.352443f) + X127 * F(0.277785f));
+ R.at(2, 2) = X124;
+ R.at(2, 3) = D(X121 * F(0.022887f) + X123 * F(-0.097545f) + X125 * F(0.490393f) + X127 * F(0.865723f));
+ R.at(3, 0) = X130;
+ R.at(3, 1) = D(X131 * F(0.415735f) + X133 * F(0.791065f) + X135 * F(-0.352443f) + X137 * F(0.277785f));
+ R.at(3, 2) = X134;
+ R.at(3, 3) = D(X131 * F(0.022887f) + X133 * F(-0.097545f) + X135 * F(0.490393f) + X137 * F(0.865723f));
+ // 40 muls 24 adds
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
+ S.at(0, 0) = D(X101 * F(0.906127f) + X103 * F(-0.318190f) + X105 * F(0.212608f) + X107 * F(-0.180240f));
+ S.at(0, 1) = X102;
+ S.at(0, 2) = D(X101 * F(-0.074658f) + X103 * F(0.513280f) + X105 * F(0.768178f) + X107 * F(-0.375330f));
+ S.at(0, 3) = X106;
+ S.at(1, 0) = D(X111 * F(0.906127f) + X113 * F(-0.318190f) + X115 * F(0.212608f) + X117 * F(-0.180240f));
+ S.at(1, 1) = X112;
+ S.at(1, 2) = D(X111 * F(-0.074658f) + X113 * F(0.513280f) + X115 * F(0.768178f) + X117 * F(-0.375330f));
+ S.at(1, 3) = X116;
+ S.at(2, 0) = D(X121 * F(0.906127f) + X123 * F(-0.318190f) + X125 * F(0.212608f) + X127 * F(-0.180240f));
+ S.at(2, 1) = X122;
+ S.at(2, 2) = D(X121 * F(-0.074658f) + X123 * F(0.513280f) + X125 * F(0.768178f) + X127 * F(-0.375330f));
+ S.at(2, 3) = X126;
+ S.at(3, 0) = D(X131 * F(0.906127f) + X133 * F(-0.318190f) + X135 * F(0.212608f) + X137 * F(-0.180240f));
+ S.at(3, 1) = X132;
+ S.at(3, 2) = D(X131 * F(-0.074658f) + X133 * F(0.513280f) + X135 * F(0.768178f) + X137 * F(-0.375330f));
+ S.at(3, 3) = X136;
+ // 40 muls 24 adds
+ }
+ };
+} // end namespace DCT_Upsample
+
+
+// Unconditionally frees all allocated m_blocks.
+void jpeg_decoder::free_all_blocks()
+{
+ delete(m_pStream);
+ m_pStream = nullptr;
+
+ for (mem_block *b = m_pMem_blocks; b; ) {
+ mem_block *n = b->m_pNext;
+ free(b);
+ b = n;
+ }
+ m_pMem_blocks = nullptr;
+}
+
+
+// This method handles all errors. It will never return.
+// It could easily be changed to use C++ exceptions.
+JPGD_NORETURN void jpeg_decoder::stop_decoding(jpgd_status status)
+{
+ m_error_code = status;
+ free_all_blocks();
+ longjmp(m_jmp_state, status);
+}
+
+
+void *jpeg_decoder::alloc(size_t nSize, bool zero)
+{
+ nSize = (JPGD_MAX(nSize, 1) + 3) & ~3;
+ char *rv = nullptr;
+ for (mem_block *b = m_pMem_blocks; b; b = b->m_pNext) {
+ if ((b->m_used_count + nSize) <= b->m_size) {
+ rv = b->m_data + b->m_used_count;
+ b->m_used_count += nSize;
+ break;
+ }
+ }
+ if (!rv) {
+ int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047);
+ mem_block *b = (mem_block*)malloc(sizeof(mem_block) + capacity);
+ if (!b) stop_decoding(JPGD_NOTENOUGHMEM);
+ b->m_pNext = m_pMem_blocks; m_pMem_blocks = b;
+ b->m_used_count = nSize;
+ b->m_size = capacity;
+ rv = b->m_data;
+ }
+ if (zero) memset(rv, 0, nSize);
+ return rv;
+}
+
+
+void jpeg_decoder::word_clear(void *p, uint16_t c, uint32_t n)
+{
+ uint8_t *pD = (uint8_t*)p;
+ const uint8_t l = c & 0xFF, h = (c >> 8) & 0xFF;
+ while (n) {
+ pD[0] = l; pD[1] = h; pD += 2;
+ n--;
+ }
+}
+
+
+// Refill the input buffer.
+// This method will sit in a loop until (A) the buffer is full or (B)
+// the stream's read() method reports and end of file condition.
+void jpeg_decoder::prep_in_buffer()
+{
+ m_in_buf_left = 0;
+ m_pIn_buf_ofs = m_in_buf;
+
+ if (m_eof_flag) return;
+
+ do {
+ int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag);
+ if (bytes_read == -1) stop_decoding(JPGD_STREAM_READ);
+ m_in_buf_left += bytes_read;
+ } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag));
+
+ m_total_bytes_read += m_in_buf_left;
+
+ // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid).
+ // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.)
+ word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64);
+}
+
+
+// Read a Huffman code table.
+void jpeg_decoder::read_dht_marker()
+{
+ int i, index, count;
+ uint8_t huff_num[17];
+ uint8_t huff_val[256];
+ uint32_t num_left = get_bits(16);
+
+ if (num_left < 2) stop_decoding(JPGD_BAD_DHT_MARKER);
+ num_left -= 2;
+
+ while (num_left) {
+ index = get_bits(8);
+ huff_num[0] = 0;
+ count = 0;
+
+ for (i = 1; i <= 16; i++) {
+ huff_num[i] = static_cast<uint8_t>(get_bits(8));
+ count += huff_num[i];
+ }
+
+ if (count > 255) stop_decoding(JPGD_BAD_DHT_COUNTS);
+
+ for (i = 0; i < count; i++)
+ huff_val[i] = static_cast<uint8_t>(get_bits(8));
+
+ i = 1 + 16 + count;
+
+ if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DHT_MARKER);
+ num_left -= i;
+
+ if ((index & 0x10) > 0x10) stop_decoding(JPGD_BAD_DHT_INDEX);
+ index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1);
+ if (index >= JPGD_MAX_HUFF_TABLES) stop_decoding(JPGD_BAD_DHT_INDEX);
+
+ if (!m_huff_num[index]) m_huff_num[index] = (uint8_t *)alloc(17);
+ if (!m_huff_val[index]) m_huff_val[index] = (uint8_t *)alloc(256);
+
+ m_huff_ac[index] = (index & 0x10) != 0;
+ memcpy(m_huff_num[index], huff_num, 17);
+ memcpy(m_huff_val[index], huff_val, 256);
+ }
+}
+
+
+// Read a quantization table.
+void jpeg_decoder::read_dqt_marker()
+{
+ int n, i, prec;
+ uint32_t temp;
+ uint32_t num_left = get_bits(16);
+ if (num_left < 2) stop_decoding(JPGD_BAD_DQT_MARKER);
+ num_left -= 2;
+
+ while (num_left) {
+ n = get_bits(8);
+ prec = n >> 4;
+ n &= 0x0F;
+
+ if (n >= JPGD_MAX_QUANT_TABLES) stop_decoding(JPGD_BAD_DQT_TABLE);
+
+ if (!m_quant[n]) m_quant[n] = (jpgd_quant_t *)alloc(64 * sizeof(jpgd_quant_t));
+
+ // read quantization entries, in zag order
+ for (i = 0; i < 64; i++) {
+ temp = get_bits(8);
+ if (prec) temp = (temp << 8) + get_bits(8);
+ m_quant[n][i] = static_cast<jpgd_quant_t>(temp);
+ }
+ i = 64 + 1;
+ if (prec) i += 64;
+ if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DQT_LENGTH);
+ num_left -= i;
+ }
+}
+
+
+// Read the start of frame (SOF) marker.
+void jpeg_decoder::read_sof_marker()
+{
+ int i;
+ uint32_t num_left = get_bits(16);
+
+ if (get_bits(8) != 8) stop_decoding(JPGD_BAD_PRECISION); /* precision: sorry, only 8-bit precision is supported right now */
+
+ m_image_y_size = get_bits(16);
+ if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) stop_decoding(JPGD_BAD_HEIGHT);
+
+ m_image_x_size = get_bits(16);
+ if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) stop_decoding(JPGD_BAD_WIDTH);
+
+ m_comps_in_frame = get_bits(8);
+ if (m_comps_in_frame > JPGD_MAX_COMPONENTS) stop_decoding(JPGD_TOO_MANY_COMPONENTS);
+
+ if (num_left != (uint32_t)(m_comps_in_frame * 3 + 8)) stop_decoding(JPGD_BAD_SOF_LENGTH);
+
+ for (i = 0; i < m_comps_in_frame; i++) {
+ m_comp_ident[i] = get_bits(8);
+ m_comp_h_samp[i] = get_bits(4);
+ m_comp_v_samp[i] = get_bits(4);
+ m_comp_quant[i] = get_bits(8);
+ }
+}
+
+
+// Used to skip unrecognized markers.
+void jpeg_decoder::skip_variable_marker()
+{
+ uint32_t num_left = get_bits(16);
+ if (num_left < 2) stop_decoding(JPGD_BAD_VARIABLE_MARKER);
+ num_left -= 2;
+
+ while (num_left) {
+ get_bits(8);
+ num_left--;
+ }
+}
+
+
+// Read a define restart interval (DRI) marker.
+void jpeg_decoder::read_dri_marker()
+{
+ if (get_bits(16) != 4) stop_decoding(JPGD_BAD_DRI_LENGTH);
+ m_restart_interval = get_bits(16);
+}
+
+
+// Read a start of scan (SOS) marker.
+void jpeg_decoder::read_sos_marker()
+{
+ int i, ci, c, cc;
+ uint32_t num_left = get_bits(16);
+ int n = get_bits(8);
+
+ m_comps_in_scan = n;
+ num_left -= 3;
+
+ if ( (num_left != (uint32_t)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) stop_decoding(JPGD_BAD_SOS_LENGTH);
+
+ for (i = 0; i < n; i++) {
+ cc = get_bits(8);
+ c = get_bits(8);
+ num_left -= 2;
+
+ for (ci = 0; ci < m_comps_in_frame; ci++)
+ if (cc == m_comp_ident[ci]) break;
+
+ if (ci >= m_comps_in_frame) stop_decoding(JPGD_BAD_SOS_COMP_ID);
+
+ m_comp_list[i] = ci;
+ m_comp_dc_tab[ci] = (c >> 4) & 15;
+ m_comp_ac_tab[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1);
+ }
+ m_spectral_start = get_bits(8);
+ m_spectral_end = get_bits(8);
+ m_successive_high = get_bits(4);
+ m_successive_low = get_bits(4);
+
+ if (!m_progressive_flag) {
+ m_spectral_start = 0;
+ m_spectral_end = 63;
+ }
+ num_left -= 3;
+
+ while (num_left) { /* read past whatever is num_left */
+ get_bits(8);
+ num_left--;
+ }
+}
+
+
+// Finds the next marker.
+int jpeg_decoder::next_marker()
+{
+ uint32_t c, bytes = 0;
+
+ do {
+ do {
+ bytes++;
+ c = get_bits(8);
+ } while (c != 0xFF);
+
+ do {
+ c = get_bits(8);
+ } while (c == 0xFF);
+ } while (c == 0);
+
+ // If bytes > 0 here, there where extra bytes before the marker (not good).
+ return c;
+}
+
+
+// Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is
+// encountered.
+int jpeg_decoder::process_markers()
+{
+ int c;
+
+ for ( ; ; ) {
+ c = next_marker();
+ switch (c) {
+ case M_SOF0:
+ case M_SOF1:
+ case M_SOF2:
+ case M_SOF3:
+ case M_SOF5:
+ case M_SOF6:
+ case M_SOF7:
+ // case M_JPG:
+ case M_SOF9:
+ case M_SOF10:
+ case M_SOF11:
+ case M_SOF13:
+ case M_SOF14:
+ case M_SOF15:
+ case M_SOI:
+ case M_EOI:
+ case M_SOS: return c;
+ case M_DHT: {
+ read_dht_marker();
+ break;
+ }
+ // No arithmitic support - dumb patents!
+ case M_DAC: {
+ stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
+ break;
+ }
+ case M_DQT: {
+ read_dqt_marker();
+ break;
+ }
+ case M_DRI: {
+ read_dri_marker();
+ break;
+ }
+ //case M_APP0: /* no need to read the JFIF marker */
+ case M_JPG:
+ case M_RST0: /* no parameters */
+ case M_RST1:
+ case M_RST2:
+ case M_RST3:
+ case M_RST4:
+ case M_RST5:
+ case M_RST6:
+ case M_RST7:
+ case M_TEM: {
+ stop_decoding(JPGD_UNEXPECTED_MARKER);
+ break;
+ }
+ default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
+ skip_variable_marker();
+ break;
+ }
+ }
+ }
+}
+
+
+// Finds the start of image (SOI) marker.
+// This code is rather defensive: it only checks the first 512 bytes to avoid
+// false positives.
+void jpeg_decoder::locate_soi_marker()
+{
+ uint32_t lastchar = get_bits(8);
+ uint32_t thischar = get_bits(8);
+
+ /* ok if it's a normal JPEG file without a special header */
+ if ((lastchar == 0xFF) && (thischar == M_SOI)) return;
+
+ uint32_t bytesleft = 4096; //512;
+
+ while (true) {
+ if (--bytesleft == 0) stop_decoding(JPGD_NOT_JPEG);
+
+ lastchar = thischar;
+ thischar = get_bits(8);
+
+ if (lastchar == 0xFF) {
+ if (thischar == M_SOI) break;
+ else if (thischar == M_EOI) stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end
+ }
+ }
+
+ // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad.
+ thischar = (m_bit_buf >> 24) & 0xFF;
+ if (thischar != 0xFF) stop_decoding(JPGD_NOT_JPEG);
+}
+
+
+// Find a start of frame (SOF) marker.
+void jpeg_decoder::locate_sof_marker()
+{
+ locate_soi_marker();
+ int c = process_markers();
+
+ switch (c) {
+ case M_SOF2: m_progressive_flag = true;
+ case M_SOF0: /* baseline DCT */
+ case M_SOF1: { /* extended sequential DCT */
+ read_sof_marker();
+ break;
+ }
+ case M_SOF9: { /* Arithmitic coding */
+ stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
+ break;
+ }
+ default: {
+ stop_decoding(JPGD_UNSUPPORTED_MARKER);
+ break;
+ }
+ }
+}
+
+
+// Find a start of scan (SOS) marker.
+int jpeg_decoder::locate_sos_marker()
+{
+ int c = process_markers();
+ if (c == M_EOI) return false;
+ else if (c != M_SOS) stop_decoding(JPGD_UNEXPECTED_MARKER);
+ read_sos_marker();
+ return true;
+}
+
+
+// Reset everything to default/uninitialized state.
+void jpeg_decoder::init(jpeg_decoder_stream *pStream)
+{
+ m_pMem_blocks = nullptr;
+ m_error_code = JPGD_SUCCESS;
+ m_ready_flag = false;
+ m_image_x_size = m_image_y_size = 0;
+ m_pStream = pStream;
+ m_progressive_flag = false;
+
+ memset(m_huff_ac, 0, sizeof(m_huff_ac));
+ memset(m_huff_num, 0, sizeof(m_huff_num));
+ memset(m_huff_val, 0, sizeof(m_huff_val));
+ memset(m_quant, 0, sizeof(m_quant));
+
+ m_scan_type = 0;
+ m_comps_in_frame = 0;
+
+ memset(m_comp_h_samp, 0, sizeof(m_comp_h_samp));
+ memset(m_comp_v_samp, 0, sizeof(m_comp_v_samp));
+ memset(m_comp_quant, 0, sizeof(m_comp_quant));
+ memset(m_comp_ident, 0, sizeof(m_comp_ident));
+ memset(m_comp_h_blocks, 0, sizeof(m_comp_h_blocks));
+ memset(m_comp_v_blocks, 0, sizeof(m_comp_v_blocks));
+
+ m_comps_in_scan = 0;
+ memset(m_comp_list, 0, sizeof(m_comp_list));
+ memset(m_comp_dc_tab, 0, sizeof(m_comp_dc_tab));
+ memset(m_comp_ac_tab, 0, sizeof(m_comp_ac_tab));
+
+ m_spectral_start = 0;
+ m_spectral_end = 0;
+ m_successive_low = 0;
+ m_successive_high = 0;
+ m_max_mcu_x_size = 0;
+ m_max_mcu_y_size = 0;
+ m_blocks_per_mcu = 0;
+ m_max_blocks_per_row = 0;
+ m_mcus_per_row = 0;
+ m_mcus_per_col = 0;
+ m_expanded_blocks_per_component = 0;
+ m_expanded_blocks_per_mcu = 0;
+ m_expanded_blocks_per_row = 0;
+ m_freq_domain_chroma_upsample = false;
+
+ memset(m_mcu_org, 0, sizeof(m_mcu_org));
+
+ m_total_lines_left = 0;
+ m_mcu_lines_left = 0;
+ m_real_dest_bytes_per_scan_line = 0;
+ m_dest_bytes_per_scan_line = 0;
+ m_dest_bytes_per_pixel = 0;
+
+ memset(m_pHuff_tabs, 0, sizeof(m_pHuff_tabs));
+
+ memset(m_dc_coeffs, 0, sizeof(m_dc_coeffs));
+ memset(m_ac_coeffs, 0, sizeof(m_ac_coeffs));
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
+
+ m_eob_run = 0;
+
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
+
+ m_pIn_buf_ofs = m_in_buf;
+ m_in_buf_left = 0;
+ m_eof_flag = false;
+ m_tem_flag = 0;
+
+ memset(m_in_buf_pad_start, 0, sizeof(m_in_buf_pad_start));
+ memset(m_in_buf, 0, sizeof(m_in_buf));
+ memset(m_in_buf_pad_end, 0, sizeof(m_in_buf_pad_end));
+
+ m_restart_interval = 0;
+ m_restarts_left = 0;
+ m_next_restart_num = 0;
+
+ m_max_mcus_per_row = 0;
+ m_max_blocks_per_mcu = 0;
+ m_max_mcus_per_col = 0;
+
+ memset(m_last_dc_val, 0, sizeof(m_last_dc_val));
+ m_pMCU_coefficients = nullptr;
+ m_pSample_buf = nullptr;
+
+ m_total_bytes_read = 0;
+
+ m_pScan_line_0 = nullptr;
+ m_pScan_line_1 = nullptr;
+
+ // Ready the input buffer.
+ prep_in_buffer();
+
+ // Prime the bit buffer.
+ m_bits_left = 16;
+ m_bit_buf = 0;
+
+ get_bits(16);
+ get_bits(16);
+
+ for (int i = 0; i < JPGD_MAX_BLOCKS_PER_MCU; i++) {
+ m_mcu_block_max_zag[i] = 64;
+ }
+}
+
+#define SCALEBITS 16
+#define ONE_HALF ((int) 1 << (SCALEBITS-1))
+#define FIX(x) ((int) ((x) * (1L<<SCALEBITS) + 0.5f))
+
+
+// Create a few tables that allow us to quickly convert YCbCr to RGB.
+void jpeg_decoder::create_look_ups()
+{
+ for (int i = 0; i <= 255; i++) {
+ int k = i - 128;
+ m_crr[i] = ( FIX(1.40200f) * k + ONE_HALF) >> SCALEBITS;
+ m_cbb[i] = ( FIX(1.77200f) * k + ONE_HALF) >> SCALEBITS;
+ m_crg[i] = (-FIX(0.71414f)) * k;
+ m_cbg[i] = (-FIX(0.34414f)) * k + ONE_HALF;
+ }
+}
+
+
+// This method throws back into the stream any bytes that where read
+// into the bit buffer during initial marker scanning.
+void jpeg_decoder::fix_in_buffer()
+{
+ // In case any 0xFF's where pulled into the buffer during marker scanning.
+ JPGD_ASSERT((m_bits_left & 7) == 0);
+
+ if (m_bits_left == 16) stuff_char( (uint8_t)(m_bit_buf & 0xFF));
+ if (m_bits_left >= 8) stuff_char( (uint8_t)((m_bit_buf >> 8) & 0xFF));
+
+ stuff_char((uint8_t)((m_bit_buf >> 16) & 0xFF));
+ stuff_char((uint8_t)((m_bit_buf >> 24) & 0xFF));
+
+ m_bits_left = 16;
+ get_bits_no_markers(16);
+ get_bits_no_markers(16);
+}
+
+
+void jpeg_decoder::transform_mcu(int mcu_row)
+{
+ jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
+ uint8_t* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64;
+
+ for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
+ idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
+ pSrc_ptr += 64;
+ pDst_ptr += 64;
+ }
+}
+
+
+static const uint8_t s_max_rc[64] =
+{
+ 17, 18, 34, 50, 50, 51, 52, 52, 52, 68, 84, 84, 84, 84, 85, 86, 86, 86, 86, 86,
+ 102, 118, 118, 118, 118, 118, 118, 119, 120, 120, 120, 120, 120, 120, 120, 136,
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136
+};
+
+
+void jpeg_decoder::transform_mcu_expand(int mcu_row)
+{
+ jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
+ uint8_t* pDst_ptr = m_pSample_buf + mcu_row * m_expanded_blocks_per_mcu * 64;
+
+ // Y IDCT
+ int mcu_block;
+ for (mcu_block = 0; mcu_block < m_expanded_blocks_per_component; mcu_block++) {
+ idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
+ pSrc_ptr += 64;
+ pDst_ptr += 64;
+ }
+
+ // Chroma IDCT, with upsampling
+ jpgd_block_t temp_block[64];
+
+ for (int i = 0; i < 2; i++) {
+ DCT_Upsample::Matrix44 P, Q, R, S;
+ JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] >= 1);
+ JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] <= 64);
+
+ int max_zag = m_mcu_block_max_zag[mcu_block++] - 1;
+ if (max_zag <= 0) max_zag = 0; // should never happen, only here to shut up static analysis
+
+ switch (s_max_rc[max_zag]) {
+ case 1*16+1:
+ DCT_Upsample::P_Q<1, 1>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<1, 1>::calc(R, S, pSrc_ptr);
+ break;
+ case 1*16+2:
+ DCT_Upsample::P_Q<1, 2>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<1, 2>::calc(R, S, pSrc_ptr);
+ break;
+ case 2*16+2:
+ DCT_Upsample::P_Q<2, 2>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<2, 2>::calc(R, S, pSrc_ptr);
+ break;
+ case 3*16+2:
+ DCT_Upsample::P_Q<3, 2>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<3, 2>::calc(R, S, pSrc_ptr);
+ break;
+ case 3*16+3:
+ DCT_Upsample::P_Q<3, 3>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<3, 3>::calc(R, S, pSrc_ptr);
+ break;
+ case 3*16+4:
+ DCT_Upsample::P_Q<3, 4>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<3, 4>::calc(R, S, pSrc_ptr);
+ break;
+ case 4*16+4:
+ DCT_Upsample::P_Q<4, 4>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<4, 4>::calc(R, S, pSrc_ptr);
+ break;
+ case 5*16+4:
+ DCT_Upsample::P_Q<5, 4>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<5, 4>::calc(R, S, pSrc_ptr);
+ break;
+ case 5*16+5:
+ DCT_Upsample::P_Q<5, 5>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<5, 5>::calc(R, S, pSrc_ptr);
+ break;
+ case 5*16+6:
+ DCT_Upsample::P_Q<5, 6>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<5, 6>::calc(R, S, pSrc_ptr);
+ break;
+ case 6*16+6:
+ DCT_Upsample::P_Q<6, 6>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<6, 6>::calc(R, S, pSrc_ptr);
+ break;
+ case 7*16+6:
+ DCT_Upsample::P_Q<7, 6>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<7, 6>::calc(R, S, pSrc_ptr);
+ break;
+ case 7*16+7:
+ DCT_Upsample::P_Q<7, 7>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<7, 7>::calc(R, S, pSrc_ptr);
+ break;
+ case 7*16+8:
+ DCT_Upsample::P_Q<7, 8>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<7, 8>::calc(R, S, pSrc_ptr);
+ break;
+ case 8*16+8:
+ DCT_Upsample::P_Q<8, 8>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr);
+ break;
+ default:
+ JPGD_ASSERT(false);
+ }
+ DCT_Upsample::Matrix44 a(P + Q); P -= Q;
+ DCT_Upsample::Matrix44& b = P;
+ DCT_Upsample::Matrix44 c(R + S); R -= S;
+ DCT_Upsample::Matrix44& d = R;
+
+ DCT_Upsample::Matrix44::add_and_store(temp_block, a, c);
+ idct_4x4(temp_block, pDst_ptr);
+ pDst_ptr += 64;
+
+ DCT_Upsample::Matrix44::sub_and_store(temp_block, a, c);
+ idct_4x4(temp_block, pDst_ptr);
+ pDst_ptr += 64;
+
+ DCT_Upsample::Matrix44::add_and_store(temp_block, b, d);
+ idct_4x4(temp_block, pDst_ptr);
+ pDst_ptr += 64;
+
+ DCT_Upsample::Matrix44::sub_and_store(temp_block, b, d);
+ idct_4x4(temp_block, pDst_ptr);
+ pDst_ptr += 64;
+ pSrc_ptr += 64;
+ }
+}
+
+
+// Loads and dequantizes the next row of (already decoded) coefficients.
+// Progressive images only.
+void jpeg_decoder::load_next_row()
+{
+ int i;
+ jpgd_block_t *p;
+ jpgd_quant_t *q;
+ int mcu_row, mcu_block, row_block = 0;
+ int component_num, component_id;
+ int block_x_mcu[JPGD_MAX_COMPONENTS];
+
+ memset(block_x_mcu, 0, JPGD_MAX_COMPONENTS * sizeof(int));
+
+ for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
+ int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
+
+ for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
+ component_id = m_mcu_org[mcu_block];
+ q = m_quant[m_comp_quant[component_id]];
+ p = m_pMCU_coefficients + 64 * mcu_block;
+
+ jpgd_block_t* pAC = coeff_buf_getp(m_ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
+ jpgd_block_t* pDC = coeff_buf_getp(m_dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
+ p[0] = pDC[0];
+ memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t));
+
+ for (i = 63; i > 0; i--) {
+ if (p[g_ZAG[i]]) break;
+ }
+
+ m_mcu_block_max_zag[mcu_block] = i + 1;
+
+ for ( ; i >= 0; i--) {
+ if (p[g_ZAG[i]]) {
+ p[g_ZAG[i]] = static_cast<jpgd_block_t>(p[g_ZAG[i]] * q[i]);
+ }
+ }
+
+ row_block++;
+
+ if (m_comps_in_scan == 1) block_x_mcu[component_id]++;
+ else {
+ if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) block_x_mcu_ofs = 0;
+ if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) {
+ block_y_mcu_ofs = 0;
+ block_x_mcu[component_id] += m_comp_h_samp[component_id];
+ }
+ }
+ }
+ if (m_freq_domain_chroma_upsample) transform_mcu_expand(mcu_row);
+ else transform_mcu(mcu_row);
+ }
+ if (m_comps_in_scan == 1) m_block_y_mcu[m_comp_list[0]]++;
+ else {
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++) {
+ component_id = m_comp_list[component_num];
+ m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
+ }
+ }
+}
+
+
+// Restart interval processing.
+void jpeg_decoder::process_restart()
+{
+ int i;
+ int c = 0;
+
+ // Align to a byte boundry
+ // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers!
+ //get_bits_no_markers(m_bits_left & 7);
+
+ // Let's scan a little bit to find the marker, but not _too_ far.
+ // 1536 is a "fudge factor" that determines how much to scan.
+ for (i = 1536; i > 0; i--) {
+ if (get_char() == 0xFF) break;
+ }
+ if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER);
+
+ for ( ; i > 0; i--) {
+ if ((c = get_char()) != 0xFF) break;
+ }
+ if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER);
+
+ // Is it the expected marker? If not, something bad happened.
+ if (c != (m_next_restart_num + M_RST0)) stop_decoding(JPGD_BAD_RESTART_MARKER);
+
+ // Reset each component's DC prediction values.
+ memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t));
+
+ m_eob_run = 0;
+ m_restarts_left = m_restart_interval;
+ m_next_restart_num = (m_next_restart_num + 1) & 7;
+
+ // Get the bit buffer going again...
+ m_bits_left = 16;
+ get_bits_no_markers(16);
+ get_bits_no_markers(16);
+}
+
+
+static inline int dequantize_ac(int c, int q)
+{
+ c *= q;
+ return c;
+}
+
+// Decodes and dequantizes the next row of coefficients.
+void jpeg_decoder::decode_next_row()
+{
+ int row_block = 0;
+
+ for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
+ if ((m_restart_interval) && (m_restarts_left == 0)) process_restart();
+
+ jpgd_block_t* p = m_pMCU_coefficients;
+
+ for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64) {
+ int component_id = m_mcu_org[mcu_block];
+ jpgd_quant_t* q = m_quant[m_comp_quant[component_id]];
+
+ int r, s;
+ s = huff_decode(m_pHuff_tabs[m_comp_dc_tab[component_id]], r);
+ s = JPGD_HUFF_EXTEND(r, s);
+
+ m_last_dc_val[component_id] = (s += m_last_dc_val[component_id]);
+
+ p[0] = static_cast<jpgd_block_t>(s * q[0]);
+
+ int prev_num_set = m_mcu_block_max_zag[mcu_block];
+ huff_tables *pH = m_pHuff_tabs[m_comp_ac_tab[component_id]];
+ int k;
+ for (k = 1; k < 64; k++) {
+ int extra_bits;
+ s = huff_decode(pH, extra_bits);
+ r = s >> 4;
+ s &= 15;
+
+ if (s) {
+ if (r) {
+ if ((k + r) > 63) stop_decoding(JPGD_DECODE_ERROR);
+ if (k < prev_num_set) {
+ int n = JPGD_MIN(r, prev_num_set - k);
+ int kt = k;
+ while (n--) p[g_ZAG[kt++]] = 0;
+ }
+ k += r;
+ }
+ s = JPGD_HUFF_EXTEND(extra_bits, s);
+ JPGD_ASSERT(k < 64);
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(dequantize_ac(s, q[k])); //s * q[k];
+ } else {
+ if (r == 15) {
+ if ((k + 16) > 64) stop_decoding(JPGD_DECODE_ERROR);
+ if (k < prev_num_set) {
+ int n = JPGD_MIN(16, prev_num_set - k);
+ int kt = k;
+ while (n--) {
+ JPGD_ASSERT(kt <= 63);
+ p[g_ZAG[kt++]] = 0;
+ }
+ }
+ k += 16 - 1; // - 1 because the loop counter is k
+ JPGD_ASSERT(p[g_ZAG[k]] == 0);
+ } else break;
+ }
+ }
+
+ if (k < prev_num_set) {
+ int kt = k;
+ while (kt < prev_num_set) p[g_ZAG[kt++]] = 0;
+ }
+
+ m_mcu_block_max_zag[mcu_block] = k;
+ row_block++;
+ }
+ if (m_freq_domain_chroma_upsample) transform_mcu_expand(mcu_row);
+ else transform_mcu(mcu_row);
+ m_restarts_left--;
+ }
+}
+
+
+// YCbCr H1V1 (1x1:1:1, 3 m_blocks per MCU) to RGB
+void jpeg_decoder::H1V1Convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t *d = m_pScan_line_0;
+ uint8_t *s = m_pSample_buf + row * 8;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ for (int j = 0; j < 8; j++) {
+ int y = s[j];
+ int cb = s[64+j];
+ int cr = s[128+j];
+
+ d[0] = clamp(y + m_crr[cr]);
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
+ d[2] = clamp(y + m_cbb[cb]);
+ d[3] = 255;
+ d += 4;
+ }
+ s += 64*3;
+ }
+}
+
+
+// YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB
+void jpeg_decoder::H2V1Convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t *d0 = m_pScan_line_0;
+ uint8_t *y = m_pSample_buf + row * 8;
+ uint8_t *c = m_pSample_buf + 2*64 + row * 8;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ for (int l = 0; l < 2; l++) {
+ for (int j = 0; j < 4; j++) {
+ int cb = c[0];
+ int cr = c[64];
+
+ int rc = m_crr[cr];
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
+ int bc = m_cbb[cb];
+
+ int yy = y[j<<1];
+ d0[0] = clamp(yy+rc);
+ d0[1] = clamp(yy+gc);
+ d0[2] = clamp(yy+bc);
+ d0[3] = 255;
+
+ yy = y[(j<<1)+1];
+ d0[4] = clamp(yy+rc);
+ d0[5] = clamp(yy+gc);
+ d0[6] = clamp(yy+bc);
+ d0[7] = 255;
+ d0 += 8;
+ c++;
+ }
+ y += 64;
+ }
+ y += 64*4 - 64*2;
+ c += 64*4 - 8;
+ }
+}
+
+
+// YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB
+void jpeg_decoder::H1V2Convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t *d0 = m_pScan_line_0;
+ uint8_t *d1 = m_pScan_line_1;
+ uint8_t *y;
+ uint8_t *c;
+
+ if (row < 8) y = m_pSample_buf + row * 8;
+ else y = m_pSample_buf + 64*1 + (row & 7) * 8;
+
+ c = m_pSample_buf + 64*2 + (row >> 1) * 8;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ for (int j = 0; j < 8; j++) {
+ int cb = c[0+j];
+ int cr = c[64+j];
+
+ int rc = m_crr[cr];
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
+ int bc = m_cbb[cb];
+
+ int yy = y[j];
+ d0[0] = clamp(yy+rc);
+ d0[1] = clamp(yy+gc);
+ d0[2] = clamp(yy+bc);
+ d0[3] = 255;
+
+ yy = y[8+j];
+ d1[0] = clamp(yy+rc);
+ d1[1] = clamp(yy+gc);
+ d1[2] = clamp(yy+bc);
+ d1[3] = 255;
+
+ d0 += 4;
+ d1 += 4;
+ }
+ y += 64*4;
+ c += 64*4;
+ }
+}
+
+
+// YCbCr H2V2 (2x2:1:1, 6 m_blocks per MCU) to RGB
+void jpeg_decoder::H2V2Convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t *d0 = m_pScan_line_0;
+ uint8_t *d1 = m_pScan_line_1;
+ uint8_t *y;
+ uint8_t *c;
+
+ if (row < 8) y = m_pSample_buf + row * 8;
+ else y = m_pSample_buf + 64*2 + (row & 7) * 8;
+
+ c = m_pSample_buf + 64*4 + (row >> 1) * 8;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ for (int l = 0; l < 2; l++) {
+ for (int j = 0; j < 8; j += 2) {
+ int cb = c[0];
+ int cr = c[64];
+
+ int rc = m_crr[cr];
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
+ int bc = m_cbb[cb];
+
+ int yy = y[j];
+ d0[0] = clamp(yy+rc);
+ d0[1] = clamp(yy+gc);
+ d0[2] = clamp(yy+bc);
+ d0[3] = 255;
+
+ yy = y[j+1];
+ d0[4] = clamp(yy+rc);
+ d0[5] = clamp(yy+gc);
+ d0[6] = clamp(yy+bc);
+ d0[7] = 255;
+
+ yy = y[j+8];
+ d1[0] = clamp(yy+rc);
+ d1[1] = clamp(yy+gc);
+ d1[2] = clamp(yy+bc);
+ d1[3] = 255;
+
+ yy = y[j+8+1];
+ d1[4] = clamp(yy+rc);
+ d1[5] = clamp(yy+gc);
+ d1[6] = clamp(yy+bc);
+ d1[7] = 255;
+
+ d0 += 8;
+ d1 += 8;
+
+ c++;
+ }
+ y += 64;
+ }
+ y += 64*6 - 64*2;
+ c += 64*6 - 8;
+ }
+}
+
+
+// Y (1 block per MCU) to 8-bit grayscale
+void jpeg_decoder::gray_convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t *d = m_pScan_line_0;
+ uint8_t *s = m_pSample_buf + row * 8;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ *(uint32_t *)d = *(uint32_t *)s;
+ *(uint32_t *)(&d[4]) = *(uint32_t *)(&s[4]);
+ s += 64;
+ d += 8;
+ }
+}
+
+
+void jpeg_decoder::expanded_convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t* Py = m_pSample_buf + (row / 8) * 64 * m_comp_h_samp[0] + (row & 7) * 8;
+ uint8_t* d = m_pScan_line_0;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ for (int k = 0; k < m_max_mcu_x_size; k += 8) {
+ const int Y_ofs = k * 8;
+ const int Cb_ofs = Y_ofs + 64 * m_expanded_blocks_per_component;
+ const int Cr_ofs = Y_ofs + 64 * m_expanded_blocks_per_component * 2;
+ for (int j = 0; j < 8; j++) {
+ int y = Py[Y_ofs + j];
+ int cb = Py[Cb_ofs + j];
+ int cr = Py[Cr_ofs + j];
+
+ d[0] = clamp(y + m_crr[cr]);
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
+ d[2] = clamp(y + m_cbb[cb]);
+ d[3] = 255;
+
+ d += 4;
+ }
+ }
+ Py += 64 * m_expanded_blocks_per_mcu;
+ }
+}
+
+
+// Find end of image (EOI) marker, so we can return to the user the exact size of the input stream.
+void jpeg_decoder::find_eoi()
+{
+ if (!m_progressive_flag) {
+ // Attempt to read the EOI marker.
+ //get_bits_no_markers(m_bits_left & 7);
+
+ // Prime the bit buffer
+ m_bits_left = 16;
+ get_bits(16);
+ get_bits(16);
+
+ // The next marker _should_ be EOI
+ process_markers();
+ }
+ m_total_bytes_read -= m_in_buf_left;
+}
+
+
+int jpeg_decoder::decode(const void** pScan_line, uint32_t* pScan_line_len)
+{
+ if ((m_error_code) || (!m_ready_flag)) return JPGD_FAILED;
+ if (m_total_lines_left == 0) return JPGD_DONE;
+ if (m_mcu_lines_left == 0) {
+ if (setjmp(m_jmp_state)) return JPGD_FAILED;
+ if (m_progressive_flag) load_next_row();
+ else decode_next_row();
+ // Find the EOI marker if that was the last row.
+ if (m_total_lines_left <= m_max_mcu_y_size) find_eoi();
+ m_mcu_lines_left = m_max_mcu_y_size;
+ }
+
+ if (m_freq_domain_chroma_upsample) {
+ expanded_convert();
+ *pScan_line = m_pScan_line_0;
+ } else {
+ switch (m_scan_type) {
+ case JPGD_YH2V2: {
+ if ((m_mcu_lines_left & 1) == 0) {
+ H2V2Convert();
+ *pScan_line = m_pScan_line_0;
+ }
+ else *pScan_line = m_pScan_line_1;
+ break;
+ }
+ case JPGD_YH2V1: {
+ H2V1Convert();
+ *pScan_line = m_pScan_line_0;
+ break;
+ }
+ case JPGD_YH1V2: {
+ if ((m_mcu_lines_left & 1) == 0) {
+ H1V2Convert();
+ *pScan_line = m_pScan_line_0;
+ } else *pScan_line = m_pScan_line_1;
+ break;
+ }
+ case JPGD_YH1V1: {
+ H1V1Convert();
+ *pScan_line = m_pScan_line_0;
+ break;
+ }
+ case JPGD_GRAYSCALE: {
+ gray_convert();
+ *pScan_line = m_pScan_line_0;
+ break;
+ }
+ }
+ }
+
+ *pScan_line_len = m_real_dest_bytes_per_scan_line;
+ m_mcu_lines_left--;
+ m_total_lines_left--;
+
+ return JPGD_SUCCESS;
+}
+
+
+// Creates the tables needed for efficient Huffman decoding.
+void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
+{
+ int p, i, l, si;
+ uint8_t huffsize[257];
+ uint32_t huffcode[257];
+ uint32_t code;
+ uint32_t subtree;
+ int code_size;
+ int lastp;
+ int nextfreeentry;
+ int currententry;
+
+ pH->ac_table = m_huff_ac[index] != 0;
+ p = 0;
+
+ for (l = 1; l <= 16; l++) {
+ for (i = 1; i <= m_huff_num[index][l]; i++) {
+ huffsize[p++] = static_cast<uint8_t>(l);
+ }
+ }
+
+ huffsize[p] = 0;
+ lastp = p;
+ code = 0;
+ si = huffsize[0];
+ p = 0;
+
+ while (huffsize[p]) {
+ while (huffsize[p] == si) {
+ huffcode[p++] = code;
+ code++;
+ }
+ code <<= 1;
+ si++;
+ }
+
+ memset(pH->look_up, 0, sizeof(pH->look_up));
+ memset(pH->look_up2, 0, sizeof(pH->look_up2));
+ memset(pH->tree, 0, sizeof(pH->tree));
+ memset(pH->code_size, 0, sizeof(pH->code_size));
+
+ nextfreeentry = -1;
+ p = 0;
+
+ while (p < lastp) {
+ i = m_huff_val[index][p];
+ code = huffcode[p];
+ code_size = huffsize[p];
+ pH->code_size[i] = static_cast<uint8_t>(code_size);
+
+ if (code_size <= 8) {
+ code <<= (8 - code_size);
+ for (l = 1 << (8 - code_size); l > 0; l--) {
+ JPGD_ASSERT(i < 256);
+ pH->look_up[code] = i;
+ bool has_extrabits = false;
+ int extra_bits = 0;
+ int num_extra_bits = i & 15;
+ int bits_to_fetch = code_size;
+
+ if (num_extra_bits) {
+ int total_codesize = code_size + num_extra_bits;
+ if (total_codesize <= 8) {
+ has_extrabits = true;
+ extra_bits = ((1 << num_extra_bits) - 1) & (code >> (8 - total_codesize));
+ JPGD_ASSERT(extra_bits <= 0x7FFF);
+ bits_to_fetch += num_extra_bits;
+ }
+ }
+ if (!has_extrabits) pH->look_up2[code] = i | (bits_to_fetch << 8);
+ else pH->look_up2[code] = i | 0x8000 | (extra_bits << 16) | (bits_to_fetch << 8);
+ code++;
+ }
+ } else {
+ subtree = (code >> (code_size - 8)) & 0xFF;
+ currententry = pH->look_up[subtree];
+
+ if (currententry == 0) {
+ pH->look_up[subtree] = currententry = nextfreeentry;
+ pH->look_up2[subtree] = currententry = nextfreeentry;
+ nextfreeentry -= 2;
+ }
+
+ code <<= (16 - (code_size - 8));
+
+ for (l = code_size; l > 9; l--) {
+ if ((code & 0x8000) == 0) currententry--;
+ if (pH->tree[-currententry - 1] == 0) {
+ pH->tree[-currententry - 1] = nextfreeentry;
+ currententry = nextfreeentry;
+ nextfreeentry -= 2;
+ } else currententry = pH->tree[-currententry - 1];
+ code <<= 1;
+ }
+ if ((code & 0x8000) == 0) currententry--;
+ pH->tree[-currententry - 1] = i;
+ }
+ p++;
+ }
+}
+
+
+// Verifies the quantization tables needed for this scan are available.
+void jpeg_decoder::check_quant_tables()
+{
+ for (int i = 0; i < m_comps_in_scan; i++) {
+ if (m_quant[m_comp_quant[m_comp_list[i]]] == nullptr) stop_decoding(JPGD_UNDEFINED_QUANT_TABLE);
+ }
+}
+
+
+// Verifies that all the Huffman tables needed for this scan are available.
+void jpeg_decoder::check_huff_tables()
+{
+ for (int i = 0; i < m_comps_in_scan; i++) {
+ if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
+ if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
+ }
+
+ for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++) {
+ if (m_huff_num[i]) {
+ if (!m_pHuff_tabs[i]) m_pHuff_tabs[i] = (huff_tables *)alloc(sizeof(huff_tables));
+ make_huff_table(i, m_pHuff_tabs[i]);
+ }
+ }
+}
+
+
+// Determines the component order inside each MCU.
+// Also calcs how many MCU's are on each row, etc.
+void jpeg_decoder::calc_mcu_block_order()
+{
+ int component_num, component_id;
+ int max_h_samp = 0, max_v_samp = 0;
+
+ for (component_id = 0; component_id < m_comps_in_frame; component_id++) {
+ if (m_comp_h_samp[component_id] > max_h_samp) {
+ max_h_samp = m_comp_h_samp[component_id];
+ }
+ if (m_comp_v_samp[component_id] > max_v_samp) {
+ max_v_samp = m_comp_v_samp[component_id];
+ }
+ }
+
+ for (component_id = 0; component_id < m_comps_in_frame; component_id++) {
+ m_comp_h_blocks[component_id] = ((((m_image_x_size * m_comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8;
+ m_comp_v_blocks[component_id] = ((((m_image_y_size * m_comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8;
+ }
+
+ if (m_comps_in_scan == 1) {
+ m_mcus_per_row = m_comp_h_blocks[m_comp_list[0]];
+ m_mcus_per_col = m_comp_v_blocks[m_comp_list[0]];
+ } else {
+ m_mcus_per_row = (((m_image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp;
+ m_mcus_per_col = (((m_image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp;
+ }
+
+ if (m_comps_in_scan == 1) {
+ m_mcu_org[0] = m_comp_list[0];
+ m_blocks_per_mcu = 1;
+ } else {
+ m_blocks_per_mcu = 0;
+
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++) {
+ int num_blocks;
+ component_id = m_comp_list[component_num];
+ num_blocks = m_comp_h_samp[component_id] * m_comp_v_samp[component_id];
+ while (num_blocks--) m_mcu_org[m_blocks_per_mcu++] = component_id;
+ }
+ }
+}
+
+
+// Starts a new scan.
+int jpeg_decoder::init_scan()
+{
+ if (!locate_sos_marker()) return false;
+
+ calc_mcu_block_order();
+ check_huff_tables();
+ check_quant_tables();
+
+ memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t));
+
+ m_eob_run = 0;
+
+ if (m_restart_interval) {
+ m_restarts_left = m_restart_interval;
+ m_next_restart_num = 0;
+ }
+ fix_in_buffer();
+ return true;
+}
+
+
+// Starts a frame. Determines if the number of components or sampling factors
+// are supported.
+void jpeg_decoder::init_frame()
+{
+ int i;
+
+ if (m_comps_in_frame == 1) {
+ if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
+ m_scan_type = JPGD_GRAYSCALE;
+ m_max_blocks_per_mcu = 1;
+ m_max_mcu_x_size = 8;
+ m_max_mcu_y_size = 8;
+ } else if (m_comps_in_frame == 3) {
+ if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1)))
+ stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
+
+ if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) {
+ m_scan_type = JPGD_YH1V1;
+ m_max_blocks_per_mcu = 3;
+ m_max_mcu_x_size = 8;
+ m_max_mcu_y_size = 8;
+ } else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1)) {
+ m_scan_type = JPGD_YH2V1;
+ m_max_blocks_per_mcu = 4;
+ m_max_mcu_x_size = 16;
+ m_max_mcu_y_size = 8;
+ } else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 2)) {
+ m_scan_type = JPGD_YH1V2;
+ m_max_blocks_per_mcu = 4;
+ m_max_mcu_x_size = 8;
+ m_max_mcu_y_size = 16;
+ } else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2)) {
+ m_scan_type = JPGD_YH2V2;
+ m_max_blocks_per_mcu = 6;
+ m_max_mcu_x_size = 16;
+ m_max_mcu_y_size = 16;
+ } else stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
+ } else stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
+
+ m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size;
+ m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size;
+
+ // These values are for the *destination* pixels: after conversion.
+ if (m_scan_type == JPGD_GRAYSCALE) m_dest_bytes_per_pixel = 1;
+ else m_dest_bytes_per_pixel = 4;
+
+ m_dest_bytes_per_scan_line = ((m_image_x_size + 15) & 0xFFF0) * m_dest_bytes_per_pixel;
+ m_real_dest_bytes_per_scan_line = (m_image_x_size * m_dest_bytes_per_pixel);
+
+ // Initialize two scan line buffers.
+ m_pScan_line_0 = (uint8_t *)alloc(m_dest_bytes_per_scan_line, true);
+ if ((m_scan_type == JPGD_YH1V2) || (m_scan_type == JPGD_YH2V2)) {
+ m_pScan_line_1 = (uint8_t *)alloc(m_dest_bytes_per_scan_line, true);
+ }
+
+ m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu;
+
+ // Should never happen
+ if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) stop_decoding(JPGD_ASSERTION_ERROR);
+
+ // Allocate the coefficient buffer, enough for one MCU
+ m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t));
+
+ for (i = 0; i < m_max_blocks_per_mcu; i++) {
+ m_mcu_block_max_zag[i] = 64;
+ }
+
+ m_expanded_blocks_per_component = m_comp_h_samp[0] * m_comp_v_samp[0];
+ m_expanded_blocks_per_mcu = m_expanded_blocks_per_component * m_comps_in_frame;
+ m_expanded_blocks_per_row = m_max_mcus_per_row * m_expanded_blocks_per_mcu;
+ // Freq. domain chroma upsampling is only supported for H2V2 subsampling factor (the most common one I've seen).
+ m_freq_domain_chroma_upsample = false;
+#if JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING
+ m_freq_domain_chroma_upsample = (m_expanded_blocks_per_mcu == 4*3);
+#endif
+
+ if (m_freq_domain_chroma_upsample)
+ m_pSample_buf = (uint8_t *)alloc(m_expanded_blocks_per_row * 64);
+ else
+ m_pSample_buf = (uint8_t *)alloc(m_max_blocks_per_row * 64);
+
+ m_total_lines_left = m_image_y_size;
+ m_mcu_lines_left = 0;
+ create_look_ups();
+}
+
+
+// The coeff_buf series of methods originally stored the coefficients
+// into a "virtual" file which was located in EMS, XMS, or a disk file. A cache
+// was used to make this process more efficient. Now, we can store the entire
+// thing in RAM.
+jpeg_decoder::coeff_buf* jpeg_decoder::coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y)
+{
+ coeff_buf* cb = (coeff_buf*)alloc(sizeof(coeff_buf));
+ cb->block_num_x = block_num_x;
+ cb->block_num_y = block_num_y;
+ cb->block_len_x = block_len_x;
+ cb->block_len_y = block_len_y;
+ cb->block_size = (block_len_x * block_len_y) * sizeof(jpgd_block_t);
+ cb->pData = (uint8_t *)alloc(cb->block_size * block_num_x * block_num_y, true);
+ return cb;
+}
+
+
+inline jpgd_block_t *jpeg_decoder::coeff_buf_getp(coeff_buf *cb, int block_x, int block_y)
+{
+ JPGD_ASSERT((block_x < cb->block_num_x) && (block_y < cb->block_num_y));
+ return (jpgd_block_t *)(cb->pData + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x));
+}
+
+
+// The following methods decode the various types of m_blocks encountered
+// in progressively encoded images.
+void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
+{
+ int s, r;
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
+
+ if ((s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_dc_tab[component_id]])) != 0) {
+ r = pD->get_bits_no_markers(s);
+ s = JPGD_HUFF_EXTEND(r, s);
+ }
+ pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]);
+ p[0] = static_cast<jpgd_block_t>(s << pD->m_successive_low);
+}
+
+
+void jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
+{
+ if (pD->get_bits_no_markers(1)) {
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
+ p[0] |= (1 << pD->m_successive_low);
+ }
+}
+
+
+void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
+{
+ int k, s, r;
+
+ if (pD->m_eob_run) {
+ pD->m_eob_run--;
+ return;
+ }
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
+
+ for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++) {
+ s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
+ r = s >> 4;
+ s &= 15;
+ if (s) {
+ if ((k += r) > 63) pD->stop_decoding(JPGD_DECODE_ERROR);
+ r = pD->get_bits_no_markers(s);
+ s = JPGD_HUFF_EXTEND(r, s);
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(s << pD->m_successive_low);
+ } else {
+ if (r == 15) {
+ if ((k += 15) > 63) pD->stop_decoding(JPGD_DECODE_ERROR);
+ } else {
+ pD->m_eob_run = 1 << r;
+ if (r) pD->m_eob_run += pD->get_bits_no_markers(r);
+ pD->m_eob_run--;
+ break;
+ }
+ }
+ }
+}
+
+
+void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
+{
+ int s, k, r;
+ int p1 = 1 << pD->m_successive_low;
+ int m1 = (-1) << pD->m_successive_low;
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
+
+ JPGD_ASSERT(pD->m_spectral_end <= 63);
+
+ k = pD->m_spectral_start;
+
+ if (pD->m_eob_run == 0) {
+ for ( ; k <= pD->m_spectral_end; k++) {
+ s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
+ r = s >> 4;
+ s &= 15;
+ if (s) {
+ if (s != 1) pD->stop_decoding(JPGD_DECODE_ERROR);
+ if (pD->get_bits_no_markers(1)) s = p1;
+ else s = m1;
+ } else {
+ if (r != 15) {
+ pD->m_eob_run = 1 << r;
+ if (r) pD->m_eob_run += pD->get_bits_no_markers(r);
+ break;
+ }
+ }
+
+ do {
+ jpgd_block_t *this_coef = p + g_ZAG[k & 63];
+
+ if (*this_coef != 0) {
+ if (pD->get_bits_no_markers(1)) {
+ if ((*this_coef & p1) == 0) {
+ if (*this_coef >= 0) *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
+ else *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
+ }
+ }
+ } else {
+ if (--r < 0) break;
+ }
+ k++;
+ } while (k <= pD->m_spectral_end);
+
+ if ((s) && (k < 64)) {
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(s);
+ }
+ }
+ }
+
+ if (pD->m_eob_run > 0) {
+ for ( ; k <= pD->m_spectral_end; k++) {
+ jpgd_block_t *this_coef = p + g_ZAG[k & 63]; // logical AND to shut up static code analysis
+
+ if (*this_coef != 0) {
+ if (pD->get_bits_no_markers(1)) {
+ if ((*this_coef & p1) == 0) {
+ if (*this_coef >= 0) *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
+ else *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
+ }
+ }
+ }
+ }
+ pD->m_eob_run--;
+ }
+}
+
+
+// Decode a scan in a progressively encoded image.
+void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
+{
+ int mcu_row, mcu_col, mcu_block;
+ int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS];
+
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
+
+ for (mcu_col = 0; mcu_col < m_mcus_per_col; mcu_col++) {
+ int component_num, component_id;
+ memset(block_x_mcu, 0, sizeof(block_x_mcu));
+
+ for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
+ int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
+
+ if ((m_restart_interval) && (m_restarts_left == 0)) process_restart();
+
+ for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
+ component_id = m_mcu_org[mcu_block];
+ decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
+
+ if (m_comps_in_scan == 1) block_x_mcu[component_id]++;
+ else {
+ if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) {
+ block_x_mcu_ofs = 0;
+
+ if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) {
+ block_y_mcu_ofs = 0;
+ block_x_mcu[component_id] += m_comp_h_samp[component_id];
+ }
+ }
+ }
+ }
+ m_restarts_left--;
+ }
+
+ if (m_comps_in_scan == 1) m_block_y_mcu[m_comp_list[0]]++;
+ else {
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++) {
+ component_id = m_comp_list[component_num];
+ m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
+ }
+ }
+ }
+}
+
+
+// Decode a progressively encoded image.
+void jpeg_decoder::init_progressive()
+{
+ int i;
+
+ if (m_comps_in_frame == 4) stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
+
+ // Allocate the coefficient buffers.
+ for (i = 0; i < m_comps_in_frame; i++) {
+ m_dc_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 1, 1);
+ m_ac_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 8, 8);
+ }
+
+ while (true) {
+ int dc_only_scan, refinement_scan;
+ pDecode_block_func decode_block_func;
+
+ if (!init_scan()) break;
+
+ dc_only_scan = (m_spectral_start == 0);
+ refinement_scan = (m_successive_high != 0);
+
+ if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) stop_decoding(JPGD_BAD_SOS_SPECTRAL);
+
+ if (dc_only_scan) {
+ if (m_spectral_end) stop_decoding(JPGD_BAD_SOS_SPECTRAL);
+ } else if (m_comps_in_scan != 1) { /* AC scans can only contain one component */
+ stop_decoding(JPGD_BAD_SOS_SPECTRAL);
+ }
+
+ if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) stop_decoding(JPGD_BAD_SOS_SUCCESSIVE);
+
+ if (dc_only_scan) {
+ if (refinement_scan) decode_block_func = decode_block_dc_refine;
+ else decode_block_func = decode_block_dc_first;
+ } else {
+ if (refinement_scan) decode_block_func = decode_block_ac_refine;
+ else decode_block_func = decode_block_ac_first;
+ }
+ decode_scan(decode_block_func);
+ m_bits_left = 16;
+ get_bits(16);
+ get_bits(16);
+ }
+
+ m_comps_in_scan = m_comps_in_frame;
+
+ for (i = 0; i < m_comps_in_frame; i++) {
+ m_comp_list[i] = i;
+ }
+
+ calc_mcu_block_order();
+}
+
+
+void jpeg_decoder::init_sequential()
+{
+ if (!init_scan()) stop_decoding(JPGD_UNEXPECTED_MARKER);
+}
+
+
+void jpeg_decoder::decode_start()
+{
+ init_frame();
+ if (m_progressive_flag) init_progressive();
+ else init_sequential();
+}
+
+
+void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream)
+{
+ init(pStream);
+ locate_sof_marker();
+}
+
+
+jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream)
+{
+ if (setjmp(m_jmp_state)) return;
+ decode_init(pStream);
+}
+
+
+int jpeg_decoder::begin_decoding()
+{
+ if (m_ready_flag) return JPGD_SUCCESS;
+ if (m_error_code) return JPGD_FAILED;
+ if (setjmp(m_jmp_state)) return JPGD_FAILED;
+
+ decode_start();
+ m_ready_flag = true;
+
+ return JPGD_SUCCESS;
+}
+
+
+jpeg_decoder::~jpeg_decoder()
+{
+ free_all_blocks();
+}
+
+
+void jpeg_decoder_file_stream::close()
+{
+ if (m_pFile) {
+ fclose(m_pFile);
+ m_pFile = nullptr;
+ }
+ m_eof_flag = false;
+ m_error_flag = false;
+}
+
+
+jpeg_decoder_file_stream::~jpeg_decoder_file_stream()
+{
+ close();
+}
+
+
+bool jpeg_decoder_file_stream::open(const char *Pfilename)
+{
+ close();
+
+ m_eof_flag = false;
+ m_error_flag = false;
+
+#if defined(_MSC_VER)
+ m_pFile = nullptr;
+ fopen_s(&m_pFile, Pfilename, "rb");
+#else
+ m_pFile = fopen(Pfilename, "rb");
+#endif
+ return m_pFile != nullptr;
+}
+
+
+int jpeg_decoder_file_stream::read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag)
+{
+ if (!m_pFile) return -1;
+
+ if (m_eof_flag) {
+ *pEOF_flag = true;
+ return 0;
+ }
+
+ if (m_error_flag) return -1;
+
+ int bytes_read = static_cast<int>(fread(pBuf, 1, max_bytes_to_read, m_pFile));
+ if (bytes_read < max_bytes_to_read) {
+ if (ferror(m_pFile)) {
+ m_error_flag = true;
+ return -1;
+ }
+ m_eof_flag = true;
+ *pEOF_flag = true;
+ }
+ return bytes_read;
+}
+
+
+bool jpeg_decoder_mem_stream::open(const uint8_t *pSrc_data, uint32_t size)
+{
+ close();
+ m_pSrc_data = pSrc_data;
+ m_ofs = 0;
+ m_size = size;
+ return true;
+}
+
+
+int jpeg_decoder_mem_stream::read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag)
+{
+ *pEOF_flag = false;
+ if (!m_pSrc_data) return -1;
+
+ uint32_t bytes_remaining = m_size - m_ofs;
+ if ((uint32_t)max_bytes_to_read > bytes_remaining) {
+ max_bytes_to_read = bytes_remaining;
+ *pEOF_flag = true;
+ }
+ memcpy(pBuf, m_pSrc_data + m_ofs, max_bytes_to_read);
+ m_ofs += max_bytes_to_read;
+
+ return max_bytes_to_read;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+
+jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height)
+{
+ auto decoder = new jpeg_decoder(new jpeg_decoder_mem_stream((const uint8_t*)data, size));
+ if (decoder->get_error_code() != JPGD_SUCCESS) {
+ delete(decoder);
+ return nullptr;
+ }
+
+ if (width) *width = decoder->get_width();
+ if (height) *height = decoder->get_height();
+
+ return decoder;
+}
+
+
+jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height)
+{
+ auto fileStream = new jpeg_decoder_file_stream();
+ if (!fileStream->open(filename)) return nullptr;
+
+ auto decoder = new jpeg_decoder(fileStream);
+ if (decoder->get_error_code() != JPGD_SUCCESS) {
+ delete(decoder);
+ return nullptr;
+ }
+
+ if (width) *width = decoder->get_width();
+ if (height) *height = decoder->get_height();
+
+ return decoder;
+}
+
+
+void jpgdDelete(jpeg_decoder* decoder)
+{
+ delete(decoder);
+}
+
+
+unsigned char* jpgdDecompress(jpeg_decoder* decoder)
+{
+ if (!decoder) return nullptr;
+
+ int req_comps = 4; //TODO: fixed 4 channel components now?
+ if ((req_comps != 1) && (req_comps != 3) && (req_comps != 4)) return nullptr;
+
+ auto image_width = decoder->get_width();
+ auto image_height = decoder->get_height();
+ //auto actual_comps = decoder->get_num_components();
+
+ if (decoder->begin_decoding() != JPGD_SUCCESS) return nullptr;
+
+ const int dst_bpl = image_width * req_comps;
+ uint8_t *pImage_data = (uint8_t*)malloc(dst_bpl * image_height);
+ if (!pImage_data) return nullptr;
+
+ for (int y = 0; y < image_height; y++) {
+ const uint8_t* pScan_line;
+ uint32_t scan_line_len;
+ if (decoder->decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS) {
+ free(pImage_data);
+ return nullptr;
+ }
+
+ uint8_t *pDst = pImage_data + y * dst_bpl;
+
+ //Return as BGRA
+ if ((req_comps == 4) && (decoder->get_num_components() == 3)) {
+ for (int x = 0; x < image_width; x++) {
+ pDst[0] = pScan_line[x*4+2];
+ pDst[1] = pScan_line[x*4+1];
+ pDst[2] = pScan_line[x*4+0];
+ pDst[3] = 255;
+ pDst += 4;
+ }
+ } else if (((req_comps == 1) && (decoder->get_num_components() == 1)) || ((req_comps == 4) && (decoder->get_num_components() == 3))) {
+ memcpy(pDst, pScan_line, dst_bpl);
+ } else if (decoder->get_num_components() == 1) {
+ if (req_comps == 3) {
+ for (int x = 0; x < image_width; x++) {
+ uint8_t luma = pScan_line[x];
+ pDst[0] = luma;
+ pDst[1] = luma;
+ pDst[2] = luma;
+ pDst += 3;
+ }
+ } else {
+ for (int x = 0; x < image_width; x++) {
+ uint8_t luma = pScan_line[x];
+ pDst[0] = luma;
+ pDst[1] = luma;
+ pDst[2] = luma;
+ pDst[3] = 255;
+ pDst += 4;
+ }
+ }
+ } else if (decoder->get_num_components() == 3) {
+ if (req_comps == 1) {
+ const int YR = 19595, YG = 38470, YB = 7471;
+ for (int x = 0; x < image_width; x++) {
+ int r = pScan_line[x*4+0];
+ int g = pScan_line[x*4+1];
+ int b = pScan_line[x*4+2];
+ *pDst++ = static_cast<uint8_t>((r * YR + g * YG + b * YB + 32768) >> 16);
+ }
+ } else {
+ for (int x = 0; x < image_width; x++) {
+ pDst[0] = pScan_line[x*4+0];
+ pDst[1] = pScan_line[x*4+1];
+ pDst[2] = pScan_line[x*4+2];
+ pDst += 3;
+ }
+ }
+ }
+ }
+ return pImage_data;
+}
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
new file mode 100644
index 0000000000..ca9cb35c32
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+// jpgd.h - C++ class for JPEG decompression.
+// Public domain, Rich Geldreich <richgel99@gmail.com>
+#ifndef _TVG_JPGD_H_
+#define _TVG_JPGD_H_
+
+class jpeg_decoder;
+
+jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height);
+jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height);
+unsigned char* jpgdDecompress(jpeg_decoder* decoder);
+void jpgdDelete(jpeg_decoder* decoder);
+
+#endif //_TVG_JPGD_H_
diff --git a/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp b/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp
new file mode 100644
index 0000000000..f2dfa02875
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp
@@ -0,0 +1,2647 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ LodePNG version 20200306
+
+ Copyright (c) 2005-2020 Lode Vandevenne
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any sourcedistribution.
+*/
+
+#include <cstdlib>
+#include "tvgLodePng.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/
+ #pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/
+ #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
+#endif /*_MSC_VER */
+
+
+/* convince the compiler to inline a function, for use when this measurably improves performance */
+/* inline is not available in C90, but use it when supported by the compiler */
+#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L))
+ #define LODEPNG_INLINE inline
+#else
+ #define LODEPNG_INLINE /* not available */
+#endif
+
+/* restrict is not available in C90, but use it when supported by the compiler */
+#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\
+ (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \
+ (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus))
+ #define LODEPNG_RESTRICT __restrict
+#else
+ #define LODEPNG_RESTRICT /* not available */
+#endif
+
+#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define LODEPNG_ABS(x) ((x) < 0 ? -(x) : (x))
+
+
+/* Replacements for C library functions such as memcpy and strlen, to support platforms
+where a full C library is not available. The compiler can recognize them and compile
+to something as fast. */
+
+static void lodepng_memcpy(void* LODEPNG_RESTRICT dst, const void* LODEPNG_RESTRICT src, size_t size)
+{
+ size_t i;
+ for (i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i];
+}
+
+
+static void lodepng_memset(void* LODEPNG_RESTRICT dst, int value, size_t num)
+{
+ size_t i;
+ for (i = 0; i < num; i++) ((char*)dst)[i] = (char)value;
+}
+
+
+/* does not check memory out of bounds, do not use on untrusted data */
+static size_t lodepng_strlen(const char* a)
+{
+ const char* orig = a;
+ /* avoid warning about unused function in case of disabled COMPILE... macros */
+ (void)(&lodepng_strlen);
+ while (*a) a++;
+ return (size_t)(a - orig);
+}
+
+
+/* Safely check if adding two integers will overflow (no undefined
+behavior, compiler removing the code, etc...) and output result. */
+static int lodepng_addofl(size_t a, size_t b, size_t* result)
+{
+ *result = a + b; /* Unsigned addition is well defined and safe in C90 */
+ return *result < a;
+}
+
+
+/* Safely check if multiplying two integers will overflow (no undefined
+behavior, compiler removing the code, etc...) and output result. */
+static int lodepng_mulofl(size_t a, size_t b, size_t* result)
+{
+ *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */
+ return (a != 0 && *result / a != b);
+}
+
+
+/* Safely check if a + b > c, even if overflow could happen. */
+static int lodepng_gtofl(size_t a, size_t b, size_t c)
+{
+ size_t d;
+ if (lodepng_addofl(a, b, &d)) return 1;
+ return d > c;
+}
+
+
+/*
+ Often in case of an error a value is assigned to a variable and then it breaks
+ out of a loop (to go to the cleanup phase of a function). This macro does that.
+ It makes the error handling code shorter and more readable.
+
+ Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83);
+*/
+#define CERROR_BREAK(errorvar, code){\
+ errorvar = code;\
+ break;\
+}
+
+/* version of CERROR_BREAK that assumes the common case where the error variable is named "error" */
+#define ERROR_BREAK(code) CERROR_BREAK(error, code)
+
+/* Set error var to the error code, and return it.*/
+#define CERROR_RETURN_ERROR(errorvar, code){\
+ errorvar = code;\
+ return code;\
+}
+
+/* Try the code, if it returns error, also return the error. */
+#define CERROR_TRY_RETURN(call){\
+ unsigned error = call;\
+ if(error) return error;\
+}
+
+/* Set error var to the error code, and return from the void function. */
+#define CERROR_RETURN(errorvar, code){\
+ errorvar = code;\
+ return;\
+}
+
+
+/* dynamic vector of unsigned chars */
+struct ucvector
+{
+ unsigned char* data;
+ size_t size; /*used size*/
+ size_t allocsize; /*allocated size*/
+};
+
+
+/* returns 1 if success, 0 if failure ==> nothing done */
+static unsigned ucvector_resize(ucvector* p, size_t size)
+{
+ if (size > p->allocsize) {
+ size_t newsize = size + (p->allocsize >> 1u);
+ void* data = realloc(p->data, newsize);
+ if(data) {
+ p->allocsize = newsize;
+ p->data = (unsigned char*)data;
+ }
+ else return 0; /*error: not enough memory*/
+ }
+ p->size = size;
+ return 1; /*success*/
+}
+
+
+static ucvector ucvector_init(unsigned char* buffer, size_t size)
+{
+ ucvector v;
+ v.data = buffer;
+ v.allocsize = v.size = size;
+ return v;
+}
+
+
+static unsigned lodepng_read32bitInt(const unsigned char* buffer)
+{
+ return (((unsigned)buffer[0] << 24u) | ((unsigned)buffer[1] << 16u) | ((unsigned)buffer[2] << 8u) | (unsigned)buffer[3]);
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* // End of common code and tools. Begin of Zlib related code. // */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+struct LodePNGBitReader
+{
+ const unsigned char* data;
+ size_t size; /*size of data in bytes*/
+ size_t bitsize; /*size of data in bits, end of valid bp values, should be 8*size*/
+ size_t bp;
+ unsigned buffer; /*buffer for reading bits. NOTE: 'unsigned' must support at least 32 bits*/
+};
+
+
+/* data size argument is in bytes. Returns error if size too large causing overflow */
+static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned char* data, size_t size)
+{
+ size_t temp;
+ reader->data = data;
+ reader->size = size;
+ /* size in bits, return error if overflow (if size_t is 32 bit this supports up to 500MB) */
+ if (lodepng_mulofl(size, 8u, &reader->bitsize)) return 105;
+ /*ensure incremented bp can be compared to bitsize without overflow even when it would be incremented 32 too much and
+ trying to ensure 32 more bits*/
+ if (lodepng_addofl(reader->bitsize, 64u, &temp)) return 105;
+ reader->bp = 0;
+ reader->buffer = 0;
+ return 0; /*ok*/
+ }
+
+/*
+ ensureBits functions:
+ Ensures the reader can at least read nbits bits in one or more readBits calls,
+ safely even if not enough bits are available.
+ Returns 1 if there are enough bits available, 0 if not.
+*/
+
+/*See ensureBits documentation above. This one ensures exactly 1 bit */
+/*static unsigned ensureBits1(LodePNGBitReader* reader) {
+ if(reader->bp >= reader->bitsize) return 0;
+ reader->buffer = (unsigned)reader->data[reader->bp >> 3u] >> (reader->bp & 7u);
+ return 1;
+}*/
+
+/*See ensureBits documentation above. This one ensures up to 9 bits */
+static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits)
+{
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if (start + 1u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u);
+ reader->buffer >>= (reader->bp & 7u);
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if (start + 0u < size) reader->buffer |= reader->data[start + 0];
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+
+/*See ensureBits documentation above. This one ensures up to 17 bits */
+static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits)
+{
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if (start + 2u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u);
+ reader->buffer >>= (reader->bp & 7u);
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if (start + 0u < size) reader->buffer |= reader->data[start + 0];
+ if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+
+/*See ensureBits documentation above. This one ensures up to 25 bits */
+static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbits)
+{
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if (start + 3u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
+ reader->buffer >>= (reader->bp & 7u);
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if (start + 0u < size) reader->buffer |= reader->data[start + 0];
+ if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
+ if (start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+
+/*See ensureBits documentation above. This one ensures up to 32 bits */
+static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbits)
+{
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if(start + 4u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
+ reader->buffer >>= (reader->bp & 7u);
+ reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u)));
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if (start + 0u < size) reader->buffer |= reader->data[start + 0];
+ if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
+ if (start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
+ if (start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u);
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+
+/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */
+static unsigned peekBits(LodePNGBitReader* reader, size_t nbits)
+{
+ /* The shift allows nbits to be only up to 31. */
+ return reader->buffer & ((1u << nbits) - 1u);
+}
+
+
+/* Must have enough bits available with ensureBits */
+static void advanceBits(LodePNGBitReader* reader, size_t nbits)
+{
+ reader->buffer >>= nbits;
+ reader->bp += nbits;
+}
+
+
+/* Must have enough bits available with ensureBits */
+static unsigned readBits(LodePNGBitReader* reader, size_t nbits)
+{
+ unsigned result = peekBits(reader, nbits);
+ advanceBits(reader, nbits);
+ return result;
+}
+
+
+/* Public for testing only. steps and result must have numsteps values. */
+unsigned lode_png_test_bitreader(const unsigned char* data, size_t size, size_t numsteps, const size_t* steps, unsigned* result)
+{
+ size_t i;
+ LodePNGBitReader reader;
+ unsigned error = LodePNGBitReader_init(&reader, data, size);
+ if (error) return 0;
+ for (i = 0; i < numsteps; i++) {
+ size_t step = steps[i];
+ unsigned ok;
+ if (step > 25) ok = ensureBits32(&reader, step);
+ else if (step > 17) ok = ensureBits25(&reader, step);
+ else if (step > 9) ok = ensureBits17(&reader, step);
+ else ok = ensureBits9(&reader, step);
+ if (!ok) return 0;
+ result[i] = readBits(&reader, step);
+ }
+ return 1;
+}
+
+
+static unsigned reverseBits(unsigned bits, unsigned num)
+{
+ /*TODO: implement faster lookup table based version when needed*/
+ unsigned i, result = 0;
+ for (i = 0; i < num; i++) result |= ((bits >> (num - i - 1u)) & 1u) << i;
+ return result;
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Deflate - Huffman / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#define FIRST_LENGTH_CODE_INDEX 257
+#define LAST_LENGTH_CODE_INDEX 285
+/*256 literals, the end code, some length codes, and 2 unused codes*/
+#define NUM_DEFLATE_CODE_SYMBOLS 288
+/*the distance codes have their own symbols, 30 used, 2 unused*/
+#define NUM_DISTANCE_SYMBOLS 32
+/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/
+#define NUM_CODE_LENGTH_CODES 19
+
+/*the base lengths represented by codes 257-285*/
+static const unsigned LENGTHBASE[29]
+ = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+ 67, 83, 99, 115, 131, 163, 195, 227, 258};
+
+/*the extra bits used by codes 257-285 (added to base length)*/
+static const unsigned LENGTHEXTRA[29]
+ = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
+ 4, 4, 4, 4, 5, 5, 5, 5, 0};
+
+/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/
+static const unsigned DISTANCEBASE[30]
+ = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+ 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
+
+/*the extra bits of backwards distances (added to base)*/
+static const unsigned DISTANCEEXTRA[30]
+ = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
+ 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
+
+/*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman
+tree of the dynamic huffman tree lengths is generated*/
+static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES]
+ = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*
+Huffman tree struct, containing multiple representations of the tree
+*/
+struct HuffmanTree
+{
+ unsigned* codes; /*the huffman codes (bit patterns representing the symbols)*/
+ unsigned* lengths; /*the lengths of the huffman codes*/
+ unsigned maxbitlen; /*maximum number of bits a single code can get*/
+ unsigned numcodes; /*number of symbols in the alphabet = number of codes*/
+ /* for reading only */
+ unsigned char* table_len; /*length of symbol from lookup table, or max length if secondary lookup needed*/
+ unsigned short* table_value; /*value of symbol from lookup table, or pointer to secondary table if needed*/
+};
+
+
+static void HuffmanTree_init(HuffmanTree* tree)
+{
+ tree->codes = 0;
+ tree->lengths = 0;
+ tree->table_len = 0;
+ tree->table_value = 0;
+}
+
+
+static void HuffmanTree_cleanup(HuffmanTree* tree)
+{
+ free(tree->codes);
+ free(tree->lengths);
+ free(tree->table_len);
+ free(tree->table_value);
+}
+
+
+/* amount of bits for first huffman table lookup (aka root bits), see HuffmanTree_makeTable and huffmanDecodeSymbol.*/
+/* values 8u and 9u work the fastest */
+#define FIRSTBITS 9u
+
+/* a symbol value too big to represent any valid symbol, to indicate reading disallowed huffman bits combination,
+which is possible in case of only 0 or 1 present symbols. */
+#define INVALIDSYMBOL 65535u
+
+/* make table for huffman decoding */
+static unsigned HuffmanTree_makeTable(HuffmanTree* tree)
+{
+ static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/
+ static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u;
+ size_t i, numpresent, pointer, size; /*total table size*/
+ unsigned* maxlens = (unsigned*)malloc(headsize * sizeof(unsigned));
+ if (!maxlens) return 83; /*alloc fail*/
+
+ /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/
+ lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens));
+ for (i = 0; i < tree->numcodes; i++) {
+ unsigned symbol = tree->codes[i];
+ unsigned l = tree->lengths[i];
+ unsigned index;
+ if(l <= FIRSTBITS) continue; /*symbols that fit in first table don't increase secondary table size*/
+ /*get the FIRSTBITS MSBs, the MSBs of the symbol are encoded first. See later comment about the reversing*/
+ index = reverseBits(symbol >> (l - FIRSTBITS), FIRSTBITS);
+ maxlens[index] = LODEPNG_MAX(maxlens[index], l);
+ }
+ /* compute total table size: size of first table plus all secondary tables for symbols longer than FIRSTBITS */
+ size = headsize;
+ for (i = 0; i < headsize; ++i) {
+ unsigned l = maxlens[i];
+ if (l > FIRSTBITS) size += (1u << (l - FIRSTBITS));
+ }
+ tree->table_len = (unsigned char*)malloc(size * sizeof(*tree->table_len));
+ tree->table_value = (unsigned short*)malloc(size * sizeof(*tree->table_value));
+ if (!tree->table_len || !tree->table_value) {
+ free(maxlens);
+ /* freeing tree->table values is done at a higher scope */
+ return 83; /*alloc fail*/
+ }
+ /*initialize with an invalid length to indicate unused entries*/
+ for (i = 0; i < size; ++i) tree->table_len[i] = 16;
+
+ /*fill in the first table for long symbols: max prefix size and pointer to secondary tables*/
+ pointer = headsize;
+ for (i = 0; i < headsize; ++i) {
+ unsigned l = maxlens[i];
+ if(l <= FIRSTBITS) continue;
+ tree->table_len[i] = l;
+ tree->table_value[i] = pointer;
+ pointer += (1u << (l - FIRSTBITS));
+ }
+ free(maxlens);
+
+ /*fill in the first table for short symbols, or secondary table for long symbols*/
+ numpresent = 0;
+ for (i = 0; i < tree->numcodes; ++i) {
+ unsigned l = tree->lengths[i];
+ unsigned symbol = tree->codes[i]; /*the huffman bit pattern. i itself is the value.*/
+ /*reverse bits, because the huffman bits are given in MSB first order but the bit reader reads LSB first*/
+ unsigned reverse = reverseBits(symbol, l);
+ if (l == 0) continue;
+ numpresent++;
+
+ if (l <= FIRSTBITS) {
+ /*short symbol, fully in first table, replicated num times if l < FIRSTBITS*/
+ unsigned num = 1u << (FIRSTBITS - l);
+ unsigned j;
+ for (j = 0; j < num; ++j) {
+ /*bit reader will read the l bits of symbol first, the remaining FIRSTBITS - l bits go to the MSB's*/
+ unsigned index = reverse | (j << l);
+ if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
+ tree->table_len[index] = l;
+ tree->table_value[index] = i;
+ }
+ } else {
+ /*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/
+ /*the FIRSTBITS MSBs of the symbol are the first table index*/
+ unsigned index = reverse & mask;
+ unsigned maxlen = tree->table_len[index];
+ /*log2 of secondary table length, should be >= l - FIRSTBITS*/
+ unsigned tablelen = maxlen - FIRSTBITS;
+ unsigned start = tree->table_value[index]; /*starting index in secondary table*/
+ unsigned num = 1u << (tablelen - (l - FIRSTBITS)); /*amount of entries of this symbol in secondary table*/
+ unsigned j;
+ if (maxlen < l) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
+ for (j = 0; j < num; ++j) {
+ unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */
+ unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS)));
+ tree->table_len[index2] = l;
+ tree->table_value[index2] = i;
+ }
+ }
+ }
+
+ if (numpresent < 2) {
+ /* In case of exactly 1 symbol, in theory the huffman symbol needs 0 bits,
+ but deflate uses 1 bit instead. In case of 0 symbols, no symbols can
+ appear at all, but such huffman tree could still exist (e.g. if distance
+ codes are never used). In both cases, not all symbols of the table will be
+ filled in. Fill them in with an invalid symbol value so returning them from
+ huffmanDecodeSymbol will cause error. */
+ for (i = 0; i < size; ++i) {
+ if (tree->table_len[i] == 16) {
+ /* As length, use a value smaller than FIRSTBITS for the head table,
+ and a value larger than FIRSTBITS for the secondary table, to ensure
+ valid behavior for advanceBits when reading this symbol. */
+ tree->table_len[i] = (i < headsize) ? 1 : (FIRSTBITS + 1);
+ tree->table_value[i] = INVALIDSYMBOL;
+ }
+ }
+ } else {
+ /* A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes.
+ If that is not the case (due to too long length codes), the table will not
+ have been fully used, and this is an error (not all bit combinations can be
+ decoded): an oversubscribed huffman tree, indicated by error 55. */
+ for (i = 0; i < size; ++i) {
+ if (tree->table_len[i] == 16) return 55;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ Second step for the ...makeFromLengths and ...makeFromFrequencies functions.
+ numcodes, lengths and maxbitlen must already be filled in correctly. return
+ value is error.
+*/
+static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree)
+{
+ unsigned* blcount;
+ unsigned* nextcode;
+ unsigned error = 0;
+ unsigned bits, n;
+
+ tree->codes = (unsigned*)malloc(tree->numcodes * sizeof(unsigned));
+ blcount = (unsigned*)malloc((tree->maxbitlen + 1) * sizeof(unsigned));
+ nextcode = (unsigned*)malloc((tree->maxbitlen + 1) * sizeof(unsigned));
+ if (!tree->codes || !blcount || !nextcode) error = 83; /*alloc fail*/
+
+ if (!error) {
+ for (n = 0; n != tree->maxbitlen + 1; n++) blcount[n] = nextcode[n] = 0;
+ /*step 1: count number of instances of each code length*/
+ for (bits = 0; bits != tree->numcodes; ++bits) ++blcount[tree->lengths[bits]];
+ /*step 2: generate the nextcode values*/
+ for(bits = 1; bits <= tree->maxbitlen; ++bits) {
+ nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1u;
+ }
+ /*step 3: generate all the codes*/
+ for (n = 0; n != tree->numcodes; ++n) {
+ if (tree->lengths[n] != 0) {
+ tree->codes[n] = nextcode[tree->lengths[n]]++;
+ /*remove superfluous bits from the code*/
+ tree->codes[n] &= ((1u << tree->lengths[n]) - 1u);
+ }
+ }
+ }
+
+ free(blcount);
+ free(nextcode);
+
+ if (!error) error = HuffmanTree_makeTable(tree);
+ return error;
+}
+
+
+/*
+ given the code lengths (as stored in the PNG file), generate the tree as defined
+ by Deflate. maxbitlen is the maximum bits that a code in the tree can have.
+ return value is error.
+*/
+static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen)
+{
+ unsigned i;
+ tree->lengths = (unsigned*)malloc(numcodes * sizeof(unsigned));
+ if (!tree->lengths) return 83; /*alloc fail*/
+ for (i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i];
+ tree->numcodes = (unsigned)numcodes; /*number of symbols*/
+ tree->maxbitlen = maxbitlen;
+ return HuffmanTree_makeFromLengths2(tree);
+}
+
+
+/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/
+static unsigned generateFixedLitLenTree(HuffmanTree* tree)
+{
+ unsigned i, error = 0;
+ unsigned* bitlen = (unsigned*)malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
+ if (!bitlen) return 83; /*alloc fail*/
+
+ /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/
+ for (i = 0; i <= 143; ++i) bitlen[i] = 8;
+ for (i = 144; i <= 255; ++i) bitlen[i] = 9;
+ for (i = 256; i <= 279; ++i) bitlen[i] = 7;
+ for (i = 280; i <= 287; ++i) bitlen[i] = 8;
+
+ error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15);
+
+ free(bitlen);
+ return error;
+}
+
+
+/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/
+static unsigned generateFixedDistanceTree(HuffmanTree* tree)
+{
+ unsigned i, error = 0;
+ unsigned* bitlen = (unsigned*)malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
+ if (!bitlen) return 83; /*alloc fail*/
+
+ /*there are 32 distance codes, but 30-31 are unused*/
+ for (i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5;
+ error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15);
+
+ free(bitlen);
+ return error;
+}
+
+
+/*
+ returns the code. The bit reader must already have been ensured at least 15 bits
+*/
+static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree* codetree)
+{
+ unsigned short code = peekBits(reader, FIRSTBITS);
+ unsigned short l = codetree->table_len[code];
+ unsigned short value = codetree->table_value[code];
+ if (l <= FIRSTBITS) {
+ advanceBits(reader, l);
+ return value;
+ } else {
+ unsigned index2;
+ advanceBits(reader, FIRSTBITS);
+ index2 = value + peekBits(reader, l - FIRSTBITS);
+ advanceBits(reader, codetree->table_len[index2] - FIRSTBITS);
+ return codetree->table_value[index2];
+ }
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Inflator (Decompressor) / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*get the tree of a deflated block with fixed tree, as specified in the deflate specification
+Returns error code.*/
+static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d)
+{
+ unsigned error = generateFixedLitLenTree(tree_ll);
+ if (error) return error;
+ return generateFixedDistanceTree(tree_d);
+}
+
+
+/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/
+static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, LodePNGBitReader* reader)
+{
+ /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/
+ unsigned error = 0;
+ unsigned n, HLIT, HDIST, HCLEN, i;
+
+ /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/
+ unsigned* bitlen_ll = 0; /*lit,len code lengths*/
+ unsigned* bitlen_d = 0; /*dist code lengths*/
+ /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/
+ unsigned* bitlen_cl = 0;
+ HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/
+
+ if (!ensureBits17(reader, 14)) return 49; /*error: the bit pointer is or will go past the memory*/
+
+ /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/
+ HLIT = readBits(reader, 5) + 257;
+ /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/
+ HDIST = readBits(reader, 5) + 1;
+ /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/
+ HCLEN = readBits(reader, 4) + 4;
+
+ bitlen_cl = (unsigned*)malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned));
+ if(!bitlen_cl) return 83 /*alloc fail*/;
+
+ HuffmanTree_init(&tree_cl);
+
+ while (!error) {
+ /*read the code length codes out of 3 * (amount of code length codes) bits*/
+ if (lodepng_gtofl(reader->bp, HCLEN * 3, reader->bitsize)) {
+ ERROR_BREAK(50); /*error: the bit pointer is or will go past the memory*/
+ }
+ for (i = 0; i != HCLEN; ++i) {
+ ensureBits9(reader, 3); /*out of bounds already checked above */
+ bitlen_cl[CLCL_ORDER[i]] = readBits(reader, 3);
+ }
+ for (i = HCLEN; i != NUM_CODE_LENGTH_CODES; ++i) {
+ bitlen_cl[CLCL_ORDER[i]] = 0;
+ }
+
+ error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7);
+ if(error) break;
+
+ /*now we can use this tree to read the lengths for the tree that this function will return*/
+ bitlen_ll = (unsigned*)malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
+ bitlen_d = (unsigned*)malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
+ if (!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/);
+ lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll));
+ lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d));
+
+ /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/
+ i = 0;
+ while (i < HLIT + HDIST) {
+ unsigned code;
+ ensureBits25(reader, 22); /* up to 15 bits for huffman code, up to 7 extra bits below*/
+ code = huffmanDecodeSymbol(reader, &tree_cl);
+ if (code <= 15) /*a length code*/ {
+ if (i < HLIT) bitlen_ll[i] = code;
+ else bitlen_d[i - HLIT] = code;
+ ++i;
+ } else if (code == 16) /*repeat previous*/ {
+ unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/
+ unsigned value; /*set value to the previous code*/
+
+ if (i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/
+
+ replength += readBits(reader, 2);
+
+ if (i < HLIT + 1) value = bitlen_ll[i - 1];
+ else value = bitlen_d[i - HLIT - 1];
+ /*repeat this value in the next lengths*/
+ for (n = 0; n < replength; ++n) {
+ if (i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/
+ if (i < HLIT) bitlen_ll[i] = value;
+ else bitlen_d[i - HLIT] = value;
+ ++i;
+ }
+ } else if(code == 17) /*repeat "0" 3-10 times*/ {
+ unsigned replength = 3; /*read in the bits that indicate repeat length*/
+ replength += readBits(reader, 3);
+
+ /*repeat this value in the next lengths*/
+ for (n = 0; n < replength; ++n) {
+ if (i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/
+
+ if (i < HLIT) bitlen_ll[i] = 0;
+ else bitlen_d[i - HLIT] = 0;
+ ++i;
+ }
+ } else if(code == 18) /*repeat "0" 11-138 times*/ {
+ unsigned replength = 11; /*read in the bits that indicate repeat length*/
+ replength += readBits(reader, 7);
+
+ /*repeat this value in the next lengths*/
+ for (n = 0; n < replength; ++n) {
+ if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/
+
+ if(i < HLIT) bitlen_ll[i] = 0;
+ else bitlen_d[i - HLIT] = 0;
+ ++i;
+ }
+ } else /*if(code == INVALIDSYMBOL)*/ {
+ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
+ }
+ /*check if any of the ensureBits above went out of bounds*/
+ if (reader->bp > reader->bitsize) {
+ /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
+ (10=no endcode, 11=wrong jump outside of tree)*/
+ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
+ ERROR_BREAK(50); /*error, bit pointer jumps past memory*/
+ }
+ }
+ if (error) break;
+
+ if (bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/
+
+ /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/
+ error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15);
+ if (error) break;
+ error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15);
+
+ break; /*end of error-while*/
+ }
+
+ free(bitlen_cl);
+ free(bitlen_ll);
+ free(bitlen_d);
+ HuffmanTree_cleanup(&tree_cl);
+
+ return error;
+}
+
+
+/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/
+static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, unsigned btype)
+{
+ unsigned error = 0;
+ HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/
+ HuffmanTree tree_d; /*the huffman tree for distance codes*/
+
+ HuffmanTree_init(&tree_ll);
+ HuffmanTree_init(&tree_d);
+
+ if (btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d);
+ else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader);
+
+ while (!error) /*decode all symbols until end reached, breaks at end code*/ {
+ /*code_ll is literal, length or end code*/
+ unsigned code_ll;
+ ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */
+ code_ll = huffmanDecodeSymbol(reader, &tree_ll);
+ if (code_ll <= 255) /*literal symbol*/ {
+ if (!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/);
+ out->data[out->size - 1] = (unsigned char)code_ll;
+ } else if (code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ {
+ unsigned code_d, distance;
+ unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/
+ size_t start, backward, length;
+
+ /*part 1: get length base*/
+ length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX];
+
+ /*part 2: get extra bits and add the value of that to length*/
+ numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX];
+ if (numextrabits_l != 0) {
+ /* bits already ensured above */
+ length += readBits(reader, numextrabits_l);
+ }
+
+ /*part 3: get distance code*/
+ ensureBits32(reader, 28); /* up to 15 for the huffman symbol, up to 13 for the extra bits */
+ code_d = huffmanDecodeSymbol(reader, &tree_d);
+ if (code_d > 29) {
+ if (code_d <= 31) {
+ ERROR_BREAK(18); /*error: invalid distance code (30-31 are never used)*/
+ } else /* if(code_d == INVALIDSYMBOL) */{
+ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
+ }
+ }
+ distance = DISTANCEBASE[code_d];
+
+ /*part 4: get extra bits from distance*/
+ numextrabits_d = DISTANCEEXTRA[code_d];
+ if (numextrabits_d != 0) {
+ /* bits already ensured above */
+ distance += readBits(reader, numextrabits_d);
+ }
+
+ /*part 5: fill in all the out[n] values based on the length and dist*/
+ start = out->size;
+ if (distance > start) ERROR_BREAK(52); /*too long backward distance*/
+ backward = start - distance;
+
+ if (!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/);
+ if (distance < length) {
+ size_t forward;
+ lodepng_memcpy(out->data + start, out->data + backward, distance);
+ start += distance;
+ for (forward = distance; forward < length; ++forward) {
+ out->data[start++] = out->data[backward++];
+ }
+ } else {
+ lodepng_memcpy(out->data + start, out->data + backward, length);
+ }
+ } else if (code_ll == 256) {
+ break; /*end code, break the loop*/
+ } else /*if(code_ll == INVALIDSYMBOL)*/ {
+ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
+ }
+ /*check if any of the ensureBits above went out of bounds*/
+ if (reader->bp > reader->bitsize) {
+ /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
+ (10=no endcode, 11=wrong jump outside of tree)*/
+ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
+ ERROR_BREAK(51); /*error, bit pointer jumps past memory*/
+ }
+ }
+
+ HuffmanTree_cleanup(&tree_ll);
+ HuffmanTree_cleanup(&tree_d);
+
+ return error;
+}
+
+
+static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader, const LodePNGDecompressSettings* settings)
+{
+ size_t bytepos;
+ size_t size = reader->size;
+ unsigned LEN, NLEN, error = 0;
+
+ /*go to first boundary of byte*/
+ bytepos = (reader->bp + 7u) >> 3u;
+
+ /*read LEN (2 bytes) and NLEN (2 bytes)*/
+ if (bytepos + 4 >= size) return 52; /*error, bit pointer will jump past memory*/
+ LEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2;
+ NLEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2;
+
+ /*check if 16-bit NLEN is really the one's complement of LEN*/
+ if (!settings->ignore_nlen && LEN + NLEN != 65535) {
+ return 21; /*error: NLEN is not one's complement of LEN*/
+ }
+
+ if (!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/
+
+ /*read the literal data: LEN bytes are now stored in the out buffer*/
+ if (bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/
+
+ lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN);
+ bytepos += LEN;
+
+ reader->bp = bytepos << 3u;
+
+ return error;
+}
+
+
+static unsigned lodepng_inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
+{
+ unsigned BFINAL = 0;
+ LodePNGBitReader reader;
+ unsigned error = LodePNGBitReader_init(&reader, in, insize);
+
+ if (error) return error;
+
+ while (!BFINAL) {
+ unsigned BTYPE;
+ if (!ensureBits9(&reader, 3)) return 52; /*error, bit pointer will jump past memory*/
+ BFINAL = readBits(&reader, 1);
+ BTYPE = readBits(&reader, 2);
+
+ if (BTYPE == 3) return 20; /*error: invalid BTYPE*/
+ else if (BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/
+ else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/
+
+ if (error) return error;
+ }
+
+ return error;
+}
+
+
+static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
+{
+ if (settings->custom_inflate) {
+ unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings);
+ out->allocsize = out->size;
+ return error;
+ } else {
+ return lodepng_inflatev(out, in, insize, settings);
+ }
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Adler32 / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len)
+{
+ unsigned s1 = adler & 0xffffu;
+ unsigned s2 = (adler >> 16u) & 0xffffu;
+
+ while (len != 0u) {
+ unsigned i;
+ /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/
+ unsigned amount = len > 5552u ? 5552u : len;
+ len -= amount;
+ for (i = 0; i != amount; ++i) {
+ s1 += (*data++);
+ s2 += s1;
+ }
+ s1 %= 65521u;
+ s2 %= 65521u;
+ }
+
+ return (s2 << 16u) | s1;
+}
+
+/*Return the adler32 of the bytes data[0..len-1]*/
+static unsigned adler32(const unsigned char* data, unsigned len)
+{
+ return update_adler32(1u, data, len);
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Zlib / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+static unsigned lodepng_zlib_decompressv(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
+{
+ unsigned error = 0;
+ unsigned CM, CINFO, FDICT;
+
+ if (insize < 2) return 53; /*error, size of zlib data too small*/
+ /*read information from zlib header*/
+ if ((in[0] * 256 + in[1]) % 31 != 0) {
+ /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/
+ return 24;
+ }
+
+ CM = in[0] & 15;
+ CINFO = (in[0] >> 4) & 15;
+ /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/
+ FDICT = (in[1] >> 5) & 1;
+ /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/
+
+ if (CM != 8 || CINFO > 7) {
+ /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/
+ return 25;
+ }
+ if (FDICT != 0) {
+ /*error: the specification of PNG says about the zlib stream:
+ "The additional flags shall not specify a preset dictionary."*/
+ return 26;
+ }
+
+ error = inflatev(out, in + 2, insize - 2, settings);
+ if (error) return error;
+
+ if (!settings->ignore_adler32) {
+ unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]);
+ unsigned checksum = adler32(out->data, (unsigned)(out->size));
+ if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/
+ }
+
+ return 0; /*no error*/
+}
+
+
+/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */
+static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
+{
+ if(settings->custom_zlib) {
+ return settings->custom_zlib(out, outsize, in, insize, settings);
+ } else {
+ unsigned error;
+ ucvector v = ucvector_init(*out, *outsize);
+ if (expected_size) {
+ /*reserve the memory to avoid intermediate reallocations*/
+ ucvector_resize(&v, *outsize + expected_size);
+ v.size = *outsize;
+ }
+ error = lodepng_zlib_decompressv(&v, in, insize, settings);
+ *out = v.data;
+ *outsize = v.size;
+ return error;
+ }
+}
+
+
+static void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings)
+{
+ settings->ignore_adler32 = 0;
+ settings->ignore_nlen = 0;
+ settings->custom_zlib = 0;
+ settings->custom_inflate = 0;
+ settings->custom_context = 0;
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* // End of Zlib related code. Begin of PNG related code. // */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+
+#if 0 //thorvg don't use crc
+/* CRC polynomial: 0xedb88320 */
+static unsigned lodepng_crc32_table[256] = {
+ 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u,
+ 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u,
+ 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u,
+ 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u,
+ 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u,
+ 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u,
+ 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u,
+ 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u,
+ 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u,
+ 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u,
+ 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u,
+ 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u,
+ 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u,
+ 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u,
+ 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u,
+ 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u,
+ 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u,
+ 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u,
+ 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u,
+ 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u,
+ 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u,
+ 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u,
+ 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u,
+ 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u,
+ 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u,
+ 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u,
+ 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u,
+ 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u,
+ 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u,
+ 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u,
+ 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u,
+ 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u
+};
+
+
+/* Calculate CRC32 of buffer
+ Return the CRC of the bytes buf[0..len-1]. */
+static unsigned lodepng_crc32(const unsigned char* data, size_t length)
+{
+ unsigned r = 0xffffffffu;
+ size_t i;
+ for (i = 0; i < length; ++i) {
+ r = lodepng_crc32_table[(r ^ data[i]) & 0xffu] ^ (r >> 8u);
+ }
+ return r ^ 0xffffffffu;
+}
+#endif
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Reading and writing PNG color channel bits / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/* The color channel bits of less-than-8-bit pixels are read with the MSB of bytes first,
+so LodePNGBitWriter and LodePNGBitReader can't be used for those. */
+
+static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream)
+{
+ unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
+ ++(*bitpointer);
+ return result;
+}
+
+
+/* TODO: make this faster */
+static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits)
+{
+ unsigned result = 0;
+ size_t i;
+ for (i = 0 ; i < nbits; ++i) {
+ result <<= 1u;
+ result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream);
+ }
+ return result;
+}
+
+
+static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
+{
+ /*the current bit in bitstream may be 0 or 1 for this to work*/
+ if (bit == 0) bitstream[(*bitpointer) >> 3u] &= (unsigned char)(~(1u << (7u - ((*bitpointer) & 7u))));
+ else bitstream[(*bitpointer) >> 3u] |= (1u << (7u - ((*bitpointer) & 7u)));
+ ++(*bitpointer);
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / PNG chunks / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*
+ The lodepng_chunk functions are normally not needed, except to traverse the
+ unknown chunks stored in the LodePNGInfo struct, or add new ones to it.
+ It also allows traversing the chunks of an encoded PNG file yourself.
+
+ The chunk pointer always points to the beginning of the chunk itself, that is
+ the first byte of the 4 length bytes.
+
+ In the PNG file format, chunks have the following format:
+ -4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer)
+ -4 bytes chunk type (ASCII a-z,A-Z only, see below)
+ -length bytes of data (may be 0 bytes if length was 0)
+ -4 bytes of CRC, computed on chunk name + data
+
+ The first chunk starts at the 8th byte of the PNG file, the entire rest of the file
+ exists out of concatenated chunks with the above format.
+
+ PNG standard chunk ASCII naming conventions:
+ -First byte: uppercase = critical, lowercase = ancillary
+ -Second byte: uppercase = public, lowercase = private
+ -Third byte: must be uppercase
+ -Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy
+*/
+
+
+/*
+ Gets the length of the data of the chunk. Total chunk length has 12 bytes more.
+ There must be at least 4 bytes to read from. If the result value is too large,
+ it may be corrupt data.
+*/
+static unsigned lodepng_chunk_length(const unsigned char* chunk)
+{
+ return lodepng_read32bitInt(&chunk[0]);
+}
+
+
+/* check if the type is the given type */
+static unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type)
+{
+ if (lodepng_strlen(type) != 4) return 0;
+ return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]);
+}
+
+
+/* 0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard) */
+static unsigned char lodepng_chunk_ancillary(const unsigned char* chunk)
+{
+ return ((chunk[4] & 32) != 0);
+}
+
+
+static const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk)
+{
+ return &chunk[8];
+}
+
+#if 0 //thorvg don't use crc
+/* returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!) */
+static unsigned lodepng_chunk_check_crc(const unsigned char* chunk)
+{
+ unsigned length = lodepng_chunk_length(chunk);
+ unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]);
+ /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
+ unsigned checksum = lodepng_crc32(&chunk[4], length + 4);
+ if (CRC != checksum) return 1;
+ else return 0;
+}
+#endif
+
+static const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end)
+{
+ if (chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/
+ if (chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
+ && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
+ /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
+ return chunk + 8;
+ } else {
+ size_t total_chunk_length;
+ const unsigned char* result;
+ if (lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
+ result = chunk + total_chunk_length;
+ if (result < chunk) return end; /*pointer overflow*/
+ return result;
+ }
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Color types, channels, bits / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*checks if the colortype is valid and the bitdepth bd is allowed for this colortype.
+Return value is a LodePNG error code.*/
+static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd)
+{
+ switch(colortype) {
+ case LCT_GREY: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break;
+ case LCT_RGB: if(!( bd == 8 || bd == 16)) return 37; break;
+ case LCT_PALETTE: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break;
+ case LCT_GREY_ALPHA: if(!( bd == 8 || bd == 16)) return 37; break;
+ case LCT_RGBA: if(!( bd == 8 || bd == 16)) return 37; break;
+ case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */
+ default: return 31; /* invalid color type */
+ }
+ return 0; /*allowed color type / bits combination*/
+}
+
+
+static unsigned getNumColorChannels(LodePNGColorType colortype)
+{
+ switch(colortype) {
+ case LCT_GREY: return 1;
+ case LCT_RGB: return 3;
+ case LCT_PALETTE: return 1;
+ case LCT_GREY_ALPHA: return 2;
+ case LCT_RGBA: return 4;
+ case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */
+ default: return 0; /*invalid color type*/
+ }
+}
+
+
+static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth)
+{
+ /*bits per pixel is amount of channels * bits per channel*/
+ return getNumColorChannels(colortype) * bitdepth;
+}
+
+
+static void lodepng_color_mode_init(LodePNGColorMode* info)
+{
+ info->key_defined = 0;
+ info->key_r = info->key_g = info->key_b = 0;
+ info->colortype = LCT_RGBA;
+ info->bitdepth = 8;
+ info->palette = 0;
+ info->palettesize = 0;
+}
+
+
+/*allocates palette memory if needed, and initializes all colors to black*/
+static void lodepng_color_mode_alloc_palette(LodePNGColorMode* info)
+{
+ size_t i;
+ /*if the palette is already allocated, it will have size 1024 so no reallocation needed in that case*/
+ /*the palette must have room for up to 256 colors with 4 bytes each.*/
+ if (!info->palette) info->palette = (unsigned char*)malloc(1024);
+ if (!info->palette) return; /*alloc fail*/
+ for (i = 0; i != 256; ++i) {
+ /*Initialize all unused colors with black, the value used for invalid palette indices.
+ This is an error according to the PNG spec, but common PNG decoders make it black instead.
+ That makes color conversion slightly faster due to no error handling needed.*/
+ info->palette[i * 4 + 0] = 0;
+ info->palette[i * 4 + 1] = 0;
+ info->palette[i * 4 + 2] = 0;
+ info->palette[i * 4 + 3] = 255;
+ }
+}
+
+static void lodepng_palette_clear(LodePNGColorMode* info)
+{
+ if (info->palette) free(info->palette);
+ info->palette = 0;
+ info->palettesize = 0;
+}
+
+
+static void lodepng_color_mode_cleanup(LodePNGColorMode* info)
+{
+ lodepng_palette_clear(info);
+}
+
+
+/*return value is error code (0 means no error)*/
+static unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source)
+{
+ lodepng_color_mode_cleanup(dest);
+ lodepng_memcpy(dest, source, sizeof(LodePNGColorMode));
+ if (source->palette) {
+ dest->palette = (unsigned char*)malloc(1024);
+ if (!dest->palette && source->palettesize) return 83; /*alloc fail*/
+ lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4);
+ }
+ return 0;
+}
+
+
+static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b)
+{
+ size_t i;
+ if (a->colortype != b->colortype) return 0;
+ if (a->bitdepth != b->bitdepth) return 0;
+ if (a->key_defined != b->key_defined) return 0;
+ if (a->key_defined) {
+ if(a->key_r != b->key_r) return 0;
+ if(a->key_g != b->key_g) return 0;
+ if(a->key_b != b->key_b) return 0;
+ }
+ if (a->palettesize != b->palettesize) return 0;
+ for (i = 0; i != a->palettesize * 4; ++i) {
+ if (a->palette[i] != b->palette[i]) return 0;
+ }
+ return 1;
+}
+
+
+static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth)
+{
+ size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth);
+ size_t n = (size_t)w * (size_t)h;
+ return ((n / 8u) * bpp) + ((n & 7u) * bpp + 7u) / 8u;
+}
+
+
+/* Returns the byte size of a raw image buffer with given width, height and color mode */
+static size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color)
+{
+ return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth);
+}
+
+
+/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer,
+and in addition has one extra byte per line: the filter byte. So this gives a larger
+result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */
+static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp)
+{
+ /* + 1 for the filter byte, and possibly plus padding bits per line. */
+ /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */
+ size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u;
+ return (size_t)h * line;
+}
+
+
+/* Safely checks whether size_t overflow can be caused due to amount of pixels.
+ This check is overcautious rather than precise. If this check indicates no overflow,
+ you can safely compute in a size_t (but not an unsigned):
+ -(size_t)w * (size_t)h * 8
+ -amount of bytes in IDAT (including filter, padding and Adam7 bytes)
+ -amount of bytes in raw color model
+ Returns 1 if overflow possible, 0 if not. */
+static int lodepng_pixel_overflow(unsigned w, unsigned h, const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor)
+{
+ size_t bpp = LODEPNG_MAX(lodepng_get_bpp_lct(pngcolor->colortype, pngcolor->bitdepth), lodepng_get_bpp_lct(rawcolor->colortype, rawcolor->bitdepth));
+ size_t numpixels, total;
+ size_t line; /* bytes per line in worst case */
+
+ if (lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1;
+ if (lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */
+
+ /* Bytes per scanline with the expression "(w / 8u) * bpp) + ((w & 7u) * bpp + 7u) / 8u" */
+ if (lodepng_mulofl((size_t)(w / 8u), bpp, &line)) return 1;
+ if (lodepng_addofl(line, ((w & 7u) * bpp + 7u) / 8u, &line)) return 1;
+
+ if (lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */
+ if (lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */
+
+ return 0; /* no overflow */
+}
+
+
+static void lodepng_info_init(LodePNGInfo* info)
+{
+ lodepng_color_mode_init(&info->color);
+ info->interlace_method = 0;
+ info->compression_method = 0;
+ info->filter_method = 0;
+}
+
+
+static void lodepng_info_cleanup(LodePNGInfo* info)
+{
+ lodepng_color_mode_cleanup(&info->color);
+}
+
+
+/* index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to */
+static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in)
+{
+ unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/
+ /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/
+ unsigned p = index & m;
+ in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/
+ in = in << (bits * (m - p));
+ if(p == 0) out[index * bits / 8u] = in;
+ else out[index * bits / 8u] |= in;
+}
+
+/*
+ One node of a color tree
+ This is the data structure used to count the number of unique colors and to get a palette
+ index for a color. It's like an octree, but because the alpha channel is used too, each
+ node has 16 instead of 8 children.
+*/
+struct ColorTree
+{
+ ColorTree* children[16]; /* up to 16 pointers to ColorTree of next level */
+ int index; /* the payload. Only has a meaningful value if this is in the last level */
+};
+
+static void color_tree_init(ColorTree* tree)
+{
+ lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children));
+ tree->index = -1;
+}
+
+static void color_tree_cleanup(ColorTree* tree)
+{
+ int i;
+ for (i = 0; i != 16; ++i) {
+ if(tree->children[i]) {
+ color_tree_cleanup(tree->children[i]);
+ free(tree->children[i]);
+ }
+ }
+}
+
+
+/* returns -1 if color not present, its index otherwise */
+static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+ int bit = 0;
+ for (bit = 0; bit < 8; ++bit) {
+ int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
+ if (!tree->children[i]) return -1;
+ else tree = tree->children[i];
+ }
+ return tree ? tree->index : -1;
+}
+
+
+/* color is not allowed to already exist.
+ Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")
+ Returns error code, or 0 if ok */
+static unsigned color_tree_add(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index)
+{
+ int bit;
+ for (bit = 0; bit < 8; ++bit) {
+ int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
+ if (!tree->children[i]) {
+ tree->children[i] = (ColorTree*)malloc(sizeof(ColorTree));
+ if (!tree->children[i]) return 83; /*alloc fail*/
+ color_tree_init(tree->children[i]);
+ }
+ tree = tree->children[i];
+ }
+ tree->index = (int)index;
+ return 0;
+}
+
+/* put a pixel, given its RGBA color, into image of any color type */
+static unsigned rgba8ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+ if (mode->colortype == LCT_GREY) {
+ unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/
+ if (mode->bitdepth == 8) out[i] = gray;
+ else if (mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray;
+ else {
+ /*take the most significant bits of gray*/
+ gray = ((unsigned)gray >> (8u - mode->bitdepth)) & ((1u << mode->bitdepth) - 1u);
+ addColorBits(out, i, mode->bitdepth, gray);
+ }
+ } else if (mode->colortype == LCT_RGB) {
+ if (mode->bitdepth == 8) {
+ out[i * 3 + 0] = r;
+ out[i * 3 + 1] = g;
+ out[i * 3 + 2] = b;
+ } else {
+ out[i * 6 + 0] = out[i * 6 + 1] = r;
+ out[i * 6 + 2] = out[i * 6 + 3] = g;
+ out[i * 6 + 4] = out[i * 6 + 5] = b;
+ }
+ } else if(mode->colortype == LCT_PALETTE) {
+ int index = color_tree_get(tree, r, g, b, a);
+ if (index < 0) return 82; /*color not in palette*/
+ if (mode->bitdepth == 8) out[i] = index;
+ else addColorBits(out, i, mode->bitdepth, (unsigned)index);
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/
+ if (mode->bitdepth == 8) {
+ out[i * 2 + 0] = gray;
+ out[i * 2 + 1] = a;
+ } else if (mode->bitdepth == 16) {
+ out[i * 4 + 0] = out[i * 4 + 1] = gray;
+ out[i * 4 + 2] = out[i * 4 + 3] = a;
+ }
+ } else if (mode->colortype == LCT_RGBA) {
+ if (mode->bitdepth == 8) {
+ out[i * 4 + 0] = r;
+ out[i * 4 + 1] = g;
+ out[i * 4 + 2] = b;
+ out[i * 4 + 3] = a;
+ } else {
+ out[i * 8 + 0] = out[i * 8 + 1] = r;
+ out[i * 8 + 2] = out[i * 8 + 3] = g;
+ out[i * 8 + 4] = out[i * 8 + 5] = b;
+ out[i * 8 + 6] = out[i * 8 + 7] = a;
+ }
+ }
+ return 0; /*no error*/
+}
+
+
+/* put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type */
+static void rgba16ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, unsigned short r, unsigned short g, unsigned short b, unsigned short a)
+{
+ if (mode->colortype == LCT_GREY) {
+ unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/
+ out[i * 2 + 0] = (gray >> 8) & 255;
+ out[i * 2 + 1] = gray & 255;
+ } else if (mode->colortype == LCT_RGB) {
+ out[i * 6 + 0] = (r >> 8) & 255;
+ out[i * 6 + 1] = r & 255;
+ out[i * 6 + 2] = (g >> 8) & 255;
+ out[i * 6 + 3] = g & 255;
+ out[i * 6 + 4] = (b >> 8) & 255;
+ out[i * 6 + 5] = b & 255;
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/
+ out[i * 4 + 0] = (gray >> 8) & 255;
+ out[i * 4 + 1] = gray & 255;
+ out[i * 4 + 2] = (a >> 8) & 255;
+ out[i * 4 + 3] = a & 255;
+ } else if (mode->colortype == LCT_RGBA) {
+ out[i * 8 + 0] = (r >> 8) & 255;
+ out[i * 8 + 1] = r & 255;
+ out[i * 8 + 2] = (g >> 8) & 255;
+ out[i * 8 + 3] = g & 255;
+ out[i * 8 + 4] = (b >> 8) & 255;
+ out[i * 8 + 5] = b & 255;
+ out[i * 8 + 6] = (a >> 8) & 255;
+ out[i * 8 + 7] = a & 255;
+ }
+}
+
+
+/* Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type. */
+static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode)
+{
+ if (mode->colortype == LCT_GREY) {
+ if (mode->bitdepth == 8) {
+ *r = *g = *b = in[i];
+ if (mode->key_defined && *r == mode->key_r) *a = 0;
+ else *a = 255;
+ } else if (mode->bitdepth == 16) {
+ *r = *g = *b = in[i * 2 + 0];
+ if (mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
+ else *a = 255;
+ } else {
+ unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */
+ size_t j = i * mode->bitdepth;
+ unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ *r = *g = *b = (value * 255) / highest;
+ if (mode->key_defined && value == mode->key_r) *a = 0;
+ else *a = 255;
+ }
+ } else if (mode->colortype == LCT_RGB) {
+ if (mode->bitdepth == 8) {
+ *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2];
+ if (mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0;
+ else *a = 255;
+ } else {
+ *r = in[i * 6 + 0];
+ *g = in[i * 6 + 2];
+ *b = in[i * 6 + 4];
+ if (mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
+ && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
+ && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
+ else *a = 255;
+ }
+ } else if (mode->colortype == LCT_PALETTE) {
+ unsigned index;
+ if (mode->bitdepth == 8) index = in[i];
+ else {
+ size_t j = i * mode->bitdepth;
+ index = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ }
+ /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
+ *r = mode->palette[index * 4 + 0];
+ *g = mode->palette[index * 4 + 1];
+ *b = mode->palette[index * 4 + 2];
+ *a = mode->palette[index * 4 + 3];
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ if (mode->bitdepth == 8) {
+ *r = *g = *b = in[i * 2 + 0];
+ *a = in[i * 2 + 1];
+ } else {
+ *r = *g = *b = in[i * 4 + 0];
+ *a = in[i * 4 + 2];
+ }
+ } else if (mode->colortype == LCT_RGBA) {
+ if (mode->bitdepth == 8) {
+ *r = in[i * 4 + 0];
+ *g = in[i * 4 + 1];
+ *b = in[i * 4 + 2];
+ *a = in[i * 4 + 3];
+ } else {
+ *r = in[i * 8 + 0];
+ *g = in[i * 8 + 2];
+ *b = in[i * 8 + 4];
+ *a = in[i * 8 + 6];
+ }
+ }
+}
+
+
+/* Similar to getPixelColorRGBA8, but with all the for loops inside of the color
+ mode test cases, optimized to convert the colors much faster, when converting
+ to the common case of RGBA with 8 bit per channel. buffer must be RGBA with
+ enough memory.*/
+static void getPixelColorsRGBA8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode)
+{
+ unsigned num_channels = 4;
+ size_t i;
+ if (mode->colortype == LCT_GREY) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i];
+ buffer[3] = 255;
+ }
+ if (mode->key_defined) {
+ buffer -= numpixels * num_channels;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ if(buffer[0] == mode->key_r) buffer[3] = 0;
+ }
+ }
+ } else if (mode->bitdepth == 16) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2];
+ buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255;
+ }
+ } else {
+ unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */
+ size_t j = 0;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
+ buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255;
+ }
+ }
+ } else if (mode->colortype == LCT_RGB) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ //lodepng_memcpy(buffer, &in[i * 3], 3);
+ //Convert colortype to LCT_BGR?
+ buffer[0] = in[i * 3 + 2];
+ buffer[1] = in[i * 3 + 1];
+ buffer[2] = in[i * 3 + 0];
+ buffer[3] = 255;
+ }
+ if (mode->key_defined) {
+ buffer -= numpixels * num_channels;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ if (buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b) buffer[3] = 0;
+ }
+ }
+ } else {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 6 + 0];
+ buffer[1] = in[i * 6 + 2];
+ buffer[2] = in[i * 6 + 4];
+ buffer[3] = mode->key_defined
+ && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
+ && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
+ && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255;
+ }
+ }
+ } else if (mode->colortype == LCT_PALETTE) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = in[i];
+ /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 4);
+ }
+ } else {
+ size_t j = 0;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 4);
+ }
+ }
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
+ buffer[3] = in[i * 2 + 1];
+ }
+ } else {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
+ buffer[3] = in[i * 4 + 2];
+ }
+ }
+ } else if (mode->colortype == LCT_RGBA) {
+ if (mode->bitdepth == 8) {
+ lodepng_memcpy(buffer, in, numpixels * 4);
+ } else {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 8 + 0];
+ buffer[1] = in[i * 8 + 2];
+ buffer[2] = in[i * 8 + 4];
+ buffer[3] = in[i * 8 + 6];
+ }
+ }
+ }
+}
+
+
+/* Similar to getPixelColorsRGBA8, but with 3-channel RGB output. */
+static void getPixelColorsRGB8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode)
+{
+ const unsigned num_channels = 3;
+ size_t i;
+ if (mode->colortype == LCT_GREY) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i];
+ }
+ } else if (mode->bitdepth == 16) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2];
+ }
+ } else {
+ unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */
+ size_t j = 0;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
+ }
+ }
+ } else if (mode->colortype == LCT_RGB) {
+ if (mode->bitdepth == 8) {
+ lodepng_memcpy(buffer, in, numpixels * 3);
+ } else {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 6 + 0];
+ buffer[1] = in[i * 6 + 2];
+ buffer[2] = in[i * 6 + 4];
+ }
+ }
+ } else if (mode->colortype == LCT_PALETTE) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = in[i];
+ /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 3);
+ }
+ } else {
+ size_t j = 0;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 3);
+ }
+ }
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
+ }
+ } else {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
+ }
+ }
+ } else if (mode->colortype == LCT_RGBA) {
+ if (mode->bitdepth == 8) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ lodepng_memcpy(buffer, &in[i * 4], 3);
+ }
+ } else {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 8 + 0];
+ buffer[1] = in[i * 8 + 2];
+ buffer[2] = in[i * 8 + 4];
+ }
+ }
+ }
+}
+
+
+/* Get RGBA16 color of pixel with index i (y * width + x) from the raw image with
+ given color type, but the given color type must be 16-bit itself. */
+static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode)
+{
+ if (mode->colortype == LCT_GREY) {
+ *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1];
+ if (mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
+ else *a = 65535;
+ } else if (mode->colortype == LCT_RGB) {
+ *r = 256u * in[i * 6 + 0] + in[i * 6 + 1];
+ *g = 256u * in[i * 6 + 2] + in[i * 6 + 3];
+ *b = 256u * in[i * 6 + 4] + in[i * 6 + 5];
+ if (mode->key_defined
+ && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
+ && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
+ && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
+ else *a = 65535;
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1];
+ *a = 256u * in[i * 4 + 2] + in[i * 4 + 3];
+ } else if (mode->colortype == LCT_RGBA) {
+ *r = 256u * in[i * 8 + 0] + in[i * 8 + 1];
+ *g = 256u * in[i * 8 + 2] + in[i * 8 + 3];
+ *b = 256u * in[i * 8 + 4] + in[i * 8 + 5];
+ *a = 256u * in[i * 8 + 6] + in[i * 8 + 7];
+ }
+}
+
+/*
+ Converts raw buffer from one color type to another color type, based on
+ LodePNGColorMode structs to describe the input and output color type.
+ See the reference manual at the end of this header file to see which color conversions are supported.
+ return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported)
+ The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel
+ of the output color type (lodepng_get_bpp).
+ For < 8 bpp images, there should not be padding bits at the end of scanlines.
+ For 16-bit per channel colors, uses big endian format like PNG does.
+ Return value is LodePNG error code
+*/
+static unsigned lodepng_convert(unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h)
+{
+ size_t i;
+ ColorTree tree;
+ size_t numpixels = (size_t)w * (size_t)h;
+ unsigned error = 0;
+
+ if (mode_in->colortype == LCT_PALETTE && !mode_in->palette) {
+ return 107; /* error: must provide palette if input mode is palette */
+ }
+
+ if (lodepng_color_mode_equal(mode_out, mode_in)) {
+ size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
+ lodepng_memcpy(out, in, numbytes);
+ return 0;
+ }
+
+ if (mode_out->colortype == LCT_PALETTE) {
+ size_t palettesize = mode_out->palettesize;
+ const unsigned char* palette = mode_out->palette;
+ size_t palsize = (size_t)1u << mode_out->bitdepth;
+ /* if the user specified output palette but did not give the values, assume
+ they want the values of the input color type (assuming that one is palette).
+ Note that we never create a new palette ourselves.*/
+ if (palettesize == 0) {
+ palettesize = mode_in->palettesize;
+ palette = mode_in->palette;
+ /* if the input was also palette with same bitdepth, then the color types are also
+ equal, so copy literally. This to preserve the exact indices that were in the PNG
+ even in case there are duplicate colors in the palette.*/
+ if (mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) {
+ size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
+ lodepng_memcpy(out, in, numbytes);
+ return 0;
+ }
+ }
+ if (palettesize < palsize) palsize = palettesize;
+ color_tree_init(&tree);
+ for (i = 0; i != palsize; ++i) {
+ const unsigned char* p = &palette[i * 4];
+ error = color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i);
+ if (error) break;
+ }
+ }
+
+ if (!error) {
+ if (mode_in->bitdepth == 16 && mode_out->bitdepth == 16) {
+ for (i = 0; i != numpixels; ++i) {
+ unsigned short r = 0, g = 0, b = 0, a = 0;
+ getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
+ rgba16ToPixel(out, i, mode_out, r, g, b, a);
+ }
+ } else if (mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) {
+ getPixelColorsRGBA8(out, numpixels, in, mode_in);
+ } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) {
+ getPixelColorsRGB8(out, numpixels, in, mode_in);
+ } else {
+ unsigned char r = 0, g = 0, b = 0, a = 0;
+ for (i = 0; i != numpixels; ++i) {
+ getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
+ error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a);
+ if (error) break;
+ }
+ }
+ }
+
+ if (mode_out->colortype == LCT_PALETTE) {
+ color_tree_cleanup(&tree);
+ }
+
+ return error;
+}
+
+
+/* Paeth predictor, used by PNG filter type 4
+ The parameters are of type short, but should come from unsigned chars, the shorts
+ are only needed to make the paeth calculation correct.
+*/
+static unsigned char paethPredictor(short a, short b, short c)
+{
+ short pa = LODEPNG_ABS(b - c);
+ short pb = LODEPNG_ABS(a - c);
+ short pc = LODEPNG_ABS(a + b - c - c);
+ /* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */
+ if (pb < pa) { a = b; pa = pb; }
+ return (pc < pa) ? c : a;
+}
+
+
+/*shared values used by multiple Adam7 related functions*/
+static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
+static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
+static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
+static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
+
+/* Outputs various dimensions and positions in the image related to the Adam7 reduced images.
+ passw: output containing the width of the 7 passes
+ passh: output containing the height of the 7 passes
+ filter_passstart: output containing the index of the start and end of each
+ reduced image with filter bytes
+ padded_passstart output containing the index of the start and end of each
+ reduced image when without filter bytes but with padded scanlines
+ passstart: output containing the index of the start and end of each reduced
+ image without padding between scanlines, but still padding between the images
+ w, h: width and height of non-interlaced image
+ bpp: bits per pixel
+ "padded" is only relevant if bpp is less than 8 and a scanline or image does not
+ end at a full byte */
+static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp)
+{
+ /* the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass */
+ unsigned i;
+
+ /* calculate width and height in pixels of each pass */
+ for (i = 0; i != 7; ++i) {
+ passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
+ passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
+ if(passw[i] == 0) passh[i] = 0;
+ if(passh[i] == 0) passw[i] = 0;
+ }
+
+ filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
+ for (i = 0; i != 7; ++i) {
+ /* if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte) */
+ filter_passstart[i + 1] = filter_passstart[i]
+ + ((passw[i] && passh[i]) ? passh[i] * (1u + (passw[i] * bpp + 7u) / 8u) : 0);
+ /* bits padded if needed to fill full byte at end of each scanline */
+ padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7u) / 8u);
+ /* only padded at end of reduced image */
+ passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7u) / 8u;
+ }
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / PNG Decoder / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length)
+{
+ /* For PNG filter method 0
+ unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte,
+ the filter works byte per byte (bytewidth = 1)
+ precon is the previous unfiltered scanline, recon the result, scanline the current one
+ the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
+ recon and scanline MAY be the same memory address! precon must be disjoint. */
+
+ size_t i;
+ switch (filterType) {
+ case 0: {
+ if (bytewidth == 4) {
+ for (i = 0; i < length; i += 4) {
+ //RGBA -> BGRA
+ recon[i + 0] = scanline[i + 2];
+ recon[i + 1] = scanline[i + 1];
+ recon[i + 2] = scanline[i + 0];
+ recon[i + 3] = scanline[i + 3];
+ }
+ } else {
+ for (i = 0; i != length; ++i) recon[i] = scanline[i];
+ }
+ break;
+ }
+ case 1: {
+ for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
+ for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth];
+ break;
+ }
+ case 2: {
+ if (precon) {
+ for (i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i];
+ } else {
+ for (i = 0; i != length; ++i) recon[i] = scanline[i];
+ }
+ break;
+ }
+ case 3: {
+ if (precon) {
+ for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u);
+ for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u);
+ } else {
+ for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
+ for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u);
+ }
+ break;
+ }
+ case 4: {
+ if (precon) {
+ for (i = 0; i != bytewidth; ++i) {
+ recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/
+ }
+
+ /* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that
+ adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */
+ if (bytewidth >= 4) {
+ for (; i + 3 < length; i += 4) {
+ size_t j = i - bytewidth;
+ unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3];
+ unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3];
+ unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3];
+ unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2], q3 = precon[j + 3];
+ recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
+ recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
+ recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
+ recon[i + 3] = s3 + paethPredictor(r3, p3, q3);
+ }
+ } else if (bytewidth >= 3) {
+ for (; i + 2 < length; i += 3) {
+ size_t j = i - bytewidth;
+ unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2];
+ unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2];
+ unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2];
+ unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2];
+ recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
+ recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
+ recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
+ }
+ } else if (bytewidth >= 2) {
+ for (; i + 1 < length; i += 2) {
+ size_t j = i - bytewidth;
+ unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1];
+ unsigned char r0 = recon[j + 0], r1 = recon[j + 1];
+ unsigned char p0 = precon[i + 0], p1 = precon[i + 1];
+ unsigned char q0 = precon[j + 0], q1 = precon[j + 1];
+ recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
+ recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
+ }
+ }
+
+ for (; i != length; ++i) {
+ recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
+ }
+ } else {
+ for (i = 0; i != bytewidth; ++i) {
+ recon[i] = scanline[i];
+ }
+ for (i = bytewidth; i < length; ++i) {
+ /* paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth] */
+ recon[i] = (scanline[i] + recon[i - bytewidth]);
+ }
+ }
+ break;
+ }
+ default: return 36; /* error: invalid filter type given */
+ }
+ return 0;
+}
+
+
+static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
+{
+ /* For PNG filter method 0
+ this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times)
+ out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
+ w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
+ in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) */
+
+ unsigned y;
+ unsigned char* prevline = 0;
+
+ /* bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise */
+ size_t bytewidth = (bpp + 7u) / 8u;
+ /* the width of a scanline in bytes, not including the filter type */
+ size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u;
+
+ for (y = 0; y < h; ++y) {
+ size_t outindex = linebytes * y;
+ size_t inindex = (1 + linebytes) * y; /* the extra filterbyte added to each row */
+ unsigned char filterType = in[inindex];
+ CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes));
+ prevline = &out[outindex];
+ }
+
+ return 0;
+}
+
+/* in: Adam7 interlaced image, with no padding bits between scanlines, but between
+ reduced images so that each reduced image starts at a byte.
+ out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h
+ bpp: bits per pixel
+ out has the following size in bits: w * h * bpp.
+ in is possibly bigger due to padding bits between reduced images.
+ out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation
+ (because that's likely a little bit faster)
+ NOTE: comments about padding bits are only relevant if bpp < 8 */
+static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
+{
+ unsigned passw[7], passh[7];
+ size_t filter_passstart[8], padded_passstart[8], passstart[8];
+ unsigned i;
+
+ Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
+
+ if (bpp >= 8) {
+ for(i = 0; i != 7; ++i) {
+ unsigned x, y, b;
+ size_t bytewidth = bpp / 8u;
+ for (y = 0; y < passh[i]; ++y)
+ for (x = 0; x < passw[i]; ++x) {
+ size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth;
+ size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth;
+ for (b = 0; b < bytewidth; ++b) {
+ out[pixeloutstart + b] = in[pixelinstart + b];
+ }
+ }
+ }
+ } else /* bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers */ {
+ for (i = 0; i != 7; ++i) {
+ unsigned x, y, b;
+ unsigned ilinebits = bpp * passw[i];
+ unsigned olinebits = bpp * w;
+ size_t obp, ibp; /* bit pointers (for out and in buffer) */
+ for (y = 0; y < passh[i]; ++y)
+ for (x = 0; x < passw[i]; ++x) {
+ ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
+ obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp;
+ for (b = 0; b < bpp; ++b) {
+ unsigned char bit = readBitFromReversedStream(&ibp, in);
+ setBitOfReversedStream(&obp, out, bit);
+ }
+ }
+ }
+ }
+}
+
+
+static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h)
+{
+ /* After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need
+ to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers
+ for the Adam7 code, the color convert code and the output to the user.
+ in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must
+ have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits
+ also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
+ only useful if (ilinebits - olinebits) is a value in the range 1..7 */
+ unsigned y;
+ size_t diff = ilinebits - olinebits;
+ size_t ibp = 0, obp = 0; /*input and output bit pointers*/
+ for (y = 0; y < h; ++y) {
+ size_t x;
+ for (x = 0; x < olinebits; ++x) {
+ unsigned char bit = readBitFromReversedStream(&ibp, in);
+ setBitOfReversedStream(&obp, out, bit);
+ }
+ ibp += diff;
+ }
+}
+
+
+/* out must be buffer big enough to contain full image, and in must contain the full decompressed data from
+ the IDAT chunks (with filter index bytes and possible padding bits)
+ return value is error */
+static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png)
+{
+ /* This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype.
+ Steps:
+ *) if no Adam7: 1) unfilter 2) remove padding bits (= possible extra bits per scanline if bpp < 8)
+ *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
+ NOTE: the in buffer will be overwritten with intermediate data! */
+ unsigned bpp = lodepng_get_bpp_lct(info_png->color.colortype, info_png->color.bitdepth);
+ if (bpp == 0) return 31; /* error: invalid colortype */
+
+ if (info_png->interlace_method == 0) {
+ if (bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) {
+ CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp));
+ removePaddingBits(out, in, w * bpp, ((w * bpp + 7u) / 8u) * 8u, h);
+ }
+ /* we can immediately filter into the out buffer, no other steps needed */
+ else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp));
+ } else /* interlace_method is 1 (Adam7) */ {
+ unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
+ unsigned i;
+
+ Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
+
+ for (i = 0; i != 7; ++i) {
+ CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp));
+ /* TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline,
+ move bytes instead of bits or move not at all */
+ if (bpp < 8) {
+ /* remove padding bits in scanlines; after this there still may be padding
+ bits between the different reduced images: each reduced image still starts nicely at a byte */
+ removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7u) / 8u) * 8u, passh[i]);
+ }
+ }
+ Adam7_deinterlace(out, in, w, h, bpp);
+ }
+ return 0;
+}
+
+
+static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength)
+{
+ unsigned pos = 0, i;
+ color->palettesize = chunkLength / 3u;
+ if (color->palettesize == 0 || color->palettesize > 256) return 38; /* error: palette too small or big */
+ lodepng_color_mode_alloc_palette(color);
+ if (!color->palette && color->palettesize) {
+ color->palettesize = 0;
+ return 83; /* alloc fail */
+ }
+
+ for (i = 0; i != color->palettesize; ++i) {
+ color->palette[4 * i + 0] = data[pos++]; /*R*/
+ color->palette[4 * i + 1] = data[pos++]; /*G*/
+ color->palette[4 * i + 2] = data[pos++]; /*B*/
+ color->palette[4 * i + 3] = 255; /*alpha*/
+ }
+
+ return 0; /* OK */
+}
+
+
+static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength)
+{
+ unsigned i;
+ if (color->colortype == LCT_PALETTE) {
+ /* error: more alpha values given than there are palette entries */
+ if (chunkLength > color->palettesize) return 39;
+
+ for (i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i];
+ } else if (color->colortype == LCT_GREY) {
+ /* error: this chunk must be 2 bytes for grayscale image */
+ if (chunkLength != 2) return 30;
+
+ color->key_defined = 1;
+ color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1];
+ } else if (color->colortype == LCT_RGB) {
+ /* error: this chunk must be 6 bytes for RGB image */
+ if (chunkLength != 6) return 41;
+
+ color->key_defined = 1;
+ color->key_r = 256u * data[0] + data[1];
+ color->key_g = 256u * data[2] + data[3];
+ color->key_b = 256u * data[4] + data[5];
+ }
+ else return 42; /* error: tRNS chunk not allowed for other color models */
+
+ return 0; /* OK */
+}
+
+
+/* read a PNG, the result will be in the same color type as the PNG (hence "generic") */
+static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize)
+{
+ unsigned char IEND = 0;
+ const unsigned char* chunk;
+ unsigned char* idat; /*the data from idat chunks, zlib compressed*/
+ size_t idatsize = 0;
+ unsigned char* scanlines = 0;
+ size_t scanlines_size = 0, expected_size = 0;
+ size_t outsize = 0;
+
+ /* safe output values in case error happens */
+ *out = 0;
+ *w = *h = 0;
+
+ state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/
+ if (state->error) return;
+
+ if (lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) {
+ CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/
+ }
+
+ /*the input filesize is a safe upper bound for the sum of idat chunks size*/
+ idat = (unsigned char*)malloc(insize);
+ if (!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/
+
+ chunk = &in[33]; /*first byte of the first chunk after the header*/
+
+ /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk.
+ IDAT data is put at the start of the in buffer*/
+ while (!IEND && !state->error) {
+ unsigned chunkLength;
+ const unsigned char* data; /*the data in the chunk*/
+
+ /*error: size of the in buffer too small to contain next chunk*/
+ if ((size_t)((chunk - in) + 12) > insize || chunk < in) {
+ if (state->decoder.ignore_end) break; /*other errors may still happen though*/
+ CERROR_BREAK(state->error, 30);
+ }
+
+ /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
+ chunkLength = lodepng_chunk_length(chunk);
+ /*error: chunk length larger than the max PNG chunk size*/
+ if (chunkLength > 2147483647) {
+ if (state->decoder.ignore_end) break; /*other errors may still happen though*/
+ CERROR_BREAK(state->error, 63);
+ }
+
+ if ((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) {
+ CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/
+ }
+
+ data = lodepng_chunk_data_const(chunk);
+
+ /*for unknown chunk order*/
+ //unsigned unknown = 0;
+
+ /*IDAT chunk, containing compressed image data*/
+ if (lodepng_chunk_type_equals(chunk, "IDAT")) {
+ size_t newsize;
+ if (lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95);
+ if (newsize > insize) CERROR_BREAK(state->error, 95);
+ lodepng_memcpy(idat + idatsize, data, chunkLength);
+ idatsize += chunkLength;
+ } else if (lodepng_chunk_type_equals(chunk, "IEND")) {
+ /*IEND chunk*/
+ IEND = 1;
+ } else if (lodepng_chunk_type_equals(chunk, "PLTE")) {
+ /*palette chunk (PLTE)*/
+ state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength);
+ if (state->error) break;
+ } else if (lodepng_chunk_type_equals(chunk, "tRNS")) {
+ /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled
+ in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that
+ affects the alpha channel of pixels. */
+ state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength);
+ if (state->error) break;
+ } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ {
+ /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
+ if (!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) {
+ CERROR_BREAK(state->error, 69);
+ }
+ //unknown = 1;
+ }
+
+#if 0 //We don't use CRC
+ if (!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ {
+ if (lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/
+ }
+#endif
+ if (!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
+ }
+
+ if (state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
+ state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */
+ }
+
+ if (!state->error) {
+ /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation.
+ If the decompressed size does not match the prediction, the image must be corrupt.*/
+ if (state->info_png.interlace_method == 0) {
+ size_t bpp = lodepng_get_bpp_lct(state->info_png.color.colortype, state->info_png.color.bitdepth);
+ expected_size = lodepng_get_raw_size_idat(*w, *h, bpp);
+ } else {
+ size_t bpp = lodepng_get_bpp_lct(state->info_png.color.colortype, state->info_png.color.bitdepth);
+ /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/
+ expected_size = 0;
+ expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp);
+ if (*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp);
+ expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp);
+ if (*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp);
+ expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp);
+ if (*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp);
+ expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp);
+ }
+ state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings);
+ }
+
+ if (!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/
+ free(idat);
+
+ if (!state->error) {
+ outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color);
+ *out = (unsigned char*)malloc(outsize);
+ if (!*out) state->error = 83; /*alloc fail*/
+ }
+ if (!state->error) {
+ lodepng_memset(*out, 0, outsize);
+ state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png);
+ }
+ free(scanlines);
+}
+
+
+static void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings)
+{
+ settings->color_convert = 1;
+ settings->ignore_crc = 0;
+ settings->ignore_critical = 0;
+ settings->ignore_end = 0;
+ lodepng_decompress_settings_init(&settings->zlibsettings);
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+/*read the information from the header and store it in the LodePNGInfo. return value is error*/
+unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize)
+{
+ unsigned width, height;
+ LodePNGInfo* info = &state->info_png;
+ if (insize == 0 || in == 0) {
+ CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/
+ }
+ if (insize < 33) {
+ CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/
+ }
+
+ /* when decoding a new PNG image, make sure all parameters created after previous decoding are reset */
+ /* TODO: remove this. One should use a new LodePNGState for new sessions */
+ lodepng_info_cleanup(info);
+ lodepng_info_init(info);
+
+ if (in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) {
+ CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/
+ }
+ if (lodepng_chunk_length(in + 8) != 13) {
+ CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/
+ }
+ if (!lodepng_chunk_type_equals(in + 8, "IHDR")) {
+ CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/
+ }
+
+ /*read the values given in the header*/
+ width = lodepng_read32bitInt(&in[16]);
+ height = lodepng_read32bitInt(&in[20]);
+ /*TODO: remove the undocumented feature that allows to give null pointers to width or height*/
+ if (w) *w = width;
+ if (h) *h = height;
+ info->color.bitdepth = in[24];
+ info->color.colortype = (LodePNGColorType)in[25];
+ info->compression_method = in[26];
+ info->filter_method = in[27];
+ info->interlace_method = in[28];
+
+ /*errors returned only after the parsing so other values are still output*/
+
+ /*error: invalid image size*/
+ if (width == 0 || height == 0) CERROR_RETURN_ERROR(state->error, 93);
+ /*error: invalid colortype or bitdepth combination*/
+ state->error = checkColorValidity(info->color.colortype, info->color.bitdepth);
+ if (state->error) return state->error;
+ /*error: only compression method 0 is allowed in the specification*/
+ if (info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32);
+ /*error: only filter method 0 is allowed in the specification*/
+ if (info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33);
+ /*error: only interlace methods 0 and 1 exist in the specification*/
+ if (info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34);
+
+#if 0 //thorvg don't use crc
+ if (!state->decoder.ignore_crc) {
+ unsigned CRC = lodepng_read32bitInt(&in[29]);
+ unsigned checksum = lodepng_crc32(&in[12], 17);
+ if (CRC != checksum) {
+ CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/
+ }
+ }
+#endif
+ return state->error;
+}
+
+
+unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize)
+{
+ *out = 0;
+ decodeGeneric(out, w, h, state, in, insize);
+ if (state->error) return state->error;
+ if (!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) {
+ /*same color type, no copying or converting of data needed*/
+ /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype
+ the raw image has to the end user*/
+ if (!state->decoder.color_convert) {
+ state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color);
+ if (state->error) return state->error;
+ }
+ } else { /*color conversion needed*/
+ unsigned char* data = *out;
+ size_t outsize;
+
+ /*TODO: check if this works according to the statement in the documentation: "The converter can convert
+ from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/
+ if (!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) && !(state->info_raw.bitdepth == 8)) {
+ return 56; /*unsupported color mode conversion*/
+ }
+
+ outsize = lodepng_get_raw_size(*w, *h, &state->info_raw);
+ *out = (unsigned char*)malloc(outsize);
+ if (!(*out)) {
+ state->error = 83; /*alloc fail*/
+ }
+ else state->error = lodepng_convert(*out, data, &state->info_raw, &state->info_png.color, *w, *h);
+ free(data);
+ }
+ return state->error;
+}
+
+
+void lodepng_state_init(LodePNGState* state)
+{
+ lodepng_decoder_settings_init(&state->decoder);
+ lodepng_color_mode_init(&state->info_raw);
+ lodepng_info_init(&state->info_png);
+ state->error = 1;
+}
+
+
+void lodepng_state_cleanup(LodePNGState* state)
+{
+ lodepng_color_mode_cleanup(&state->info_raw);
+ lodepng_info_cleanup(&state->info_png);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/loaders/png/tvgLodePng.h b/thirdparty/thorvg/src/loaders/png/tvgLodePng.h
new file mode 100644
index 0000000000..02e1feeec5
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/png/tvgLodePng.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ LodePNG version 20200306
+
+ Copyright (c) 2005-2020 Lode Vandevenne
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef _TVG_LODEPNG_H_
+#define _TVG_LODEPNG_H_
+
+#include <stddef.h>
+
+/*The PNG color types (also used for raw image).*/
+enum LodePNGColorType
+{
+ LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/
+ LCT_RGB = 2, /*RGB: 8,16 bit*/
+ LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/
+ LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/
+ LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/
+ /*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid
+ byte value from 0 to 255 that could be present in an invalid PNG file header. Do
+ not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use
+ the valid color type names above, or numeric values like 1 or 7 when checking for
+ particular disallowed color type byte values, or cast to integer to print it.*/
+ LCT_MAX_OCTET_VALUE = 255
+};
+
+/*Settings for zlib decompression*/
+struct LodePNGDecompressSettings
+{
+ /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */
+ unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
+ unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
+
+ /*use custom zlib decoder instead of built in one (default: null)*/
+ unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*);
+ /*use custom deflate decoder instead of built in one (default: null) if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/
+ unsigned (*custom_inflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*);
+
+ const void* custom_context; /*optional custom settings for custom functions*/
+};
+
+/*
+ Color mode of an image. Contains all information required to decode the pixel
+ bits to RGBA colors. This information is the same as used in the PNG file
+ format, and is used both for PNG and raw image data in LodePNG.
+*/
+struct LodePNGColorMode
+{
+ /*header (IHDR)*/
+ LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/
+ unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/
+
+ /*
+ palette (PLTE and tRNS)
+
+ Dynamically allocated with the colors of the palette, including alpha.
+ This field may not be allocated directly, use lodepng_color_mode_init first,
+ then lodepng_palette_add per color to correctly initialize it (to ensure size
+ of exactly 1024 bytes).
+
+ The alpha channels must be set as well, set them to 255 for opaque images.
+
+ When decoding, by default you can ignore this palette, since LodePNG already
+ fills the palette colors in the pixels of the raw RGBA output.
+
+ The palette is only supported for color type 3.
+ */
+ unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/
+ size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/
+
+ /*
+ transparent color key (tRNS)
+
+ This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
+ For grayscale PNGs, r, g and b will all 3 be set to the same.
+
+ When decoding, by default you can ignore this information, since LodePNG sets
+ pixels with this key to transparent already in the raw RGBA output.
+
+ The color key is only supported for color types 0 and 2.
+ */
+ unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/
+ unsigned key_r; /*red/grayscale component of color key*/
+ unsigned key_g; /*green component of color key*/
+ unsigned key_b; /*blue component of color key*/
+};
+
+/*Information about the PNG image, except pixels, width and height.*/
+struct LodePNGInfo
+{
+ /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
+ unsigned compression_method;/*compression method of the original file. Always 0.*/
+ unsigned filter_method; /*filter method of the original file*/
+ unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/
+ LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/
+};
+
+/*
+ Settings for the decoder. This contains settings for the PNG and the Zlib
+ decoder, but not the Info settings from the Info structs.
+*/
+struct LodePNGDecoderSettings
+{
+ LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/
+
+ /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */
+ unsigned ignore_crc; /*ignore CRC checksums*/
+ unsigned ignore_critical; /*ignore unknown critical chunks*/
+ unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/
+ /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable
+ errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some
+ strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters
+ in string keys, etc... */
+
+ unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
+};
+
+/*The settings, state and information for extended encoding and decoding.*/
+struct LodePNGState
+{
+ LodePNGDecoderSettings decoder; /*the decoding settings*/
+ LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/
+ LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/
+ unsigned error;
+};
+
+void lodepng_state_init(LodePNGState* state);
+void lodepng_state_cleanup(LodePNGState* state);
+unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize);
+unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize);
+
+#endif //_TVG_LODEPNG_H_ \ No newline at end of file
diff --git a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp
new file mode 100644
index 0000000000..3cc08e902b
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <memory.h>
+#include "tvgLoader.h"
+#include "tvgPngLoader.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+static inline uint32_t PREMULTIPLY(uint32_t c)
+{
+ auto a = (c >> 24);
+ return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff);
+}
+
+
+static void _premultiply(uint32_t* data, uint32_t w, uint32_t h)
+{
+ auto buffer = data;
+ for (uint32_t y = 0; y < h; ++y, buffer += w) {
+ auto src = buffer;
+ for (uint32_t x = 0; x < w; ++x, ++src) {
+ *src = PREMULTIPLY(*src);
+ }
+ }
+}
+
+
+void PngLoader::clear()
+{
+ lodepng_state_cleanup(&state);
+
+ if (freeData) free(data);
+ data = nullptr;
+ size = 0;
+ freeData = false;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+PngLoader::PngLoader()
+{
+ lodepng_state_init(&state);
+}
+
+
+PngLoader::~PngLoader()
+{
+ if (freeData) free(data);
+ free(image);
+}
+
+
+bool PngLoader::open(const string& path)
+{
+ clear();
+
+ auto pngFile = fopen(path.c_str(), "rb");
+ if (!pngFile) return false;
+
+ auto ret = false;
+
+ //determine size
+ if (fseek(pngFile, 0, SEEK_END) < 0) goto finalize;
+ if (((size = ftell(pngFile)) < 1)) goto finalize;
+ if (fseek(pngFile, 0, SEEK_SET)) goto finalize;
+
+ data = (unsigned char *) malloc(size);
+ if (!data) goto finalize;
+
+ freeData = true;
+
+ if (fread(data, size, 1, pngFile) < 1) goto failure;
+
+ lodepng_state_init(&state);
+
+ unsigned int width, height;
+ if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto failure;
+
+ w = static_cast<float>(width);
+ h = static_cast<float>(height);
+ ret = true;
+
+ goto finalize;
+
+failure:
+ clear();
+
+finalize:
+ fclose(pngFile);
+ return ret;
+}
+
+
+bool PngLoader::open(const char* data, uint32_t size, bool copy)
+{
+ clear();
+
+ lodepng_state_init(&state);
+
+ unsigned int width, height;
+ if (lodepng_inspect(&width, &height, &state, (unsigned char*)(data), size) > 0) return false;
+
+ if (copy) {
+ this->data = (unsigned char *) malloc(size);
+ if (!this->data) return false;
+ memcpy((unsigned char *)this->data, data, size);
+ freeData = true;
+ } else {
+ this->data = (unsigned char *) data;
+ freeData = false;
+ }
+
+ w = static_cast<float>(width);
+ h = static_cast<float>(height);
+ this->size = size;
+
+ return true;
+}
+
+
+bool PngLoader::read()
+{
+ if (!data || w <= 0 || h <= 0) return false;
+
+ TaskScheduler::request(this);
+
+ return true;
+}
+
+
+bool PngLoader::close()
+{
+ this->done();
+ clear();
+ return true;
+}
+
+
+unique_ptr<Surface> PngLoader::bitmap()
+{
+ this->done();
+
+ if (!image) return nullptr;
+
+ auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ surface->buffer = (uint32_t*)(image);
+ surface->stride = w;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = SwCanvas::ARGB8888;
+
+ return unique_ptr<Surface>(surface);
+}
+
+
+void PngLoader::run(unsigned tid)
+{
+ if (image) {
+ free(image);
+ image = nullptr;
+ }
+ auto width = static_cast<unsigned>(w);
+ auto height = static_cast<unsigned>(h);
+
+ lodepng_decode(&image, &width, &height, &state, data, size);
+
+ _premultiply((uint32_t*)(image), width, height);
+}
diff --git a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h
new file mode 100644
index 0000000000..34dbeed012
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_PNG_LOADER_H_
+#define _TVG_PNG_LOADER_H_
+
+#include "tvgLodePng.h"
+#include "tvgTaskScheduler.h"
+
+
+class PngLoader : public LoadModule, public Task
+{
+private:
+ LodePNGState state;
+ unsigned char* data = nullptr;
+ unsigned char *image = nullptr;
+ unsigned long size = 0;
+ bool freeData = false;
+
+ void clear();
+
+public:
+ PngLoader();
+ ~PngLoader();
+
+ using LoadModule::open;
+ bool open(const string& path) override;
+ bool open(const char* data, uint32_t size, bool copy) override;
+ bool read() override;
+ bool close() override;
+
+ unique_ptr<Surface> bitmap() override;
+ void run(unsigned tid) override;
+};
+
+#endif //_TVG_PNG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
new file mode 100644
index 0000000000..d7d425b119
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <fstream>
+#include <string.h>
+#include "tvgLoader.h"
+#include "tvgRawLoader.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+RawLoader::~RawLoader()
+{
+ if (copy && content) {
+ free((void*)content);
+ content = nullptr;
+ }
+}
+
+
+bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy)
+{
+ if (!data || w == 0 || h == 0) return false;
+
+ this->w = (float)w;
+ this->h = (float)h;
+ this->copy = copy;
+
+ if (copy) {
+ content = (uint32_t*)malloc(sizeof(uint32_t) * w * h);
+ if (!content) return false;
+ memcpy((void*)content, data, sizeof(uint32_t) * w * h);
+ }
+ else content = data;
+
+ return true;
+}
+
+
+bool RawLoader::read()
+{
+ return true;
+}
+
+
+bool RawLoader::close()
+{
+ return true;
+}
+
+
+unique_ptr<Surface> RawLoader::bitmap()
+{
+ if (!content) return nullptr;
+
+ auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ surface->buffer = (uint32_t*)(content);
+ surface->stride = w;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = SwCanvas::ARGB8888;
+
+ return unique_ptr<Surface>(surface);
+}
diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
new file mode 100644
index 0000000000..20fa332981
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_RAW_LOADER_H_
+#define _TVG_RAW_LOADER_H_
+
+class RawLoader : public LoadModule
+{
+public:
+ const uint32_t* content = nullptr;
+ bool copy = false;
+
+ ~RawLoader();
+
+ using LoadModule::open;
+ bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) override;
+ bool read() override;
+ bool close() override;
+
+ unique_ptr<Surface> bitmap() override;
+};
+
+
+#endif //_TVG_RAW_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
new file mode 100644
index 0000000000..08b3308165
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -0,0 +1,3078 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright notice for the EFL:
+
+ * Copyright (C) EFL developers (see AUTHORS)
+
+ * All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++.
+
+#include <cstring>
+#include <fstream>
+#include <float.h>
+#include <math.h>
+#include "tvgLoader.h"
+#include "tvgXmlParser.h"
+#include "tvgSvgLoader.h"
+#include "tvgSvgSceneBuilder.h"
+#include "tvgSvgUtil.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+/*
+ * According to: https://www.w3.org/TR/SVG2/coords.html#Units
+ * and: https://www.w3.org/TR/css-values-4/#absolute-lengths
+ */
+#define PX_PER_IN 96 //1 in = 96 px
+#define PX_PER_PC 16 //1 pc = 1/6 in -> PX_PER_IN/6
+#define PX_PER_PT 1.333333f //1 pt = 1/72 in -> PX_PER_IN/72
+#define PX_PER_MM 3.779528f //1 in = 25.4 mm -> PX_PER_IN/25.4
+#define PX_PER_CM 37.79528f //1 in = 2.54 cm -> PX_PER_IN/2.54
+
+
+typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength);
+typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
+
+
+static char* _skipSpace(const char* str, const char* end)
+{
+ while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
+ ++str;
+ }
+ return (char*) str;
+}
+
+
+static char* _copyId(const char* str)
+{
+ if (!str) return nullptr;
+
+ return strdup(str);
+}
+
+
+static const char* _skipComma(const char* content)
+{
+ content = _skipSpace(content, nullptr);
+ if (*content == ',') return content + 1;
+ return content;
+}
+
+
+static bool _parseNumber(const char** content, float* number)
+{
+ char* end = nullptr;
+
+ *number = svgUtilStrtof(*content, &end);
+ //If the start of string is not number
+ if ((*content) == end) return false;
+ //Skip comma if any
+ *content = _skipComma(end);
+ return true;
+}
+
+/**
+ * According to https://www.w3.org/TR/SVG/coords.html#Units
+ */
+static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
+{
+ float parsedValue = svgUtilStrtof(str, nullptr);
+
+ if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
+ else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
+ else if (strstr(str, "pt")) parsedValue *= PX_PER_PT;
+ else if (strstr(str, "pc")) parsedValue *= PX_PER_PC;
+ else if (strstr(str, "in")) parsedValue *= PX_PER_IN;
+ else if (strstr(str, "%")) {
+ if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0) * svgParse->global.h;
+ else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0) * svgParse->global.w;
+ else //if other then it's radius
+ {
+ float max = (float)svgParse->global.w;
+ if (max < svgParse->global.h)
+ max = (float)svgParse->global.h;
+ parsedValue = (parsedValue / 100.0) * max;
+ }
+ }
+ //TODO: Implement 'em', 'ex' attributes
+
+ return parsedValue;
+}
+
+
+static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool& isPercentage)
+{
+ char* end = nullptr;
+
+ float parsedValue = svgUtilStrtof(str, &end);
+ isPercentage = false;
+
+ if (strstr(str, "%")) {
+ parsedValue = parsedValue / 100.0;
+ isPercentage = true;
+ }
+ else if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
+ else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
+ else if (strstr(str, "pt")) parsedValue *= PX_PER_PT;
+ else if (strstr(str, "pc")) parsedValue *= PX_PER_PC;
+ else if (strstr(str, "in")) parsedValue *= PX_PER_IN;
+ //TODO: Implement 'em', 'ex' attributes
+
+ return parsedValue;
+}
+
+
+static float _toOffset(const char* str)
+{
+ char* end = nullptr;
+ auto strEnd = str + strlen(str);
+
+ float parsedValue = svgUtilStrtof(str, &end);
+
+ end = _skipSpace(end, nullptr);
+ auto ptr = strstr(str, "%");
+
+ if (ptr) {
+ parsedValue = parsedValue / 100.0;
+ if (end != ptr || (end + 1) != strEnd) return 0;
+ } else if (end != strEnd) return 0;
+
+ return parsedValue;
+}
+
+
+static int _toOpacity(const char* str)
+{
+ char* end = nullptr;
+ float opacity = svgUtilStrtof(str, &end);
+
+ if (end) {
+ if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f);
+ else if (*end == '\0') return lrint(opacity * 255);
+ }
+ return 255;
+}
+
+
+#define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
+ static Type _to##Name1(const char* str) \
+ { \
+ unsigned int i; \
+ \
+ for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
+ if (!strcmp(str, Tags_Array[i].tag)) return Tags_Array[i].Name; \
+ } \
+ return Default; \
+ }
+
+
+/* parse the line cap used during stroking a path.
+ * Value: butt | round | square | inherit
+ * Initial: butt
+ * https://www.w3.org/TR/SVG/painting.html
+ */
+static constexpr struct
+{
+ StrokeCap lineCap;
+ const char* tag;
+} lineCapTags[] = {
+ { StrokeCap::Butt, "butt" },
+ { StrokeCap::Round, "round" },
+ { StrokeCap::Square, "square" }
+};
+
+
+_PARSE_TAG(StrokeCap, lineCap, LineCap, lineCapTags, StrokeCap::Butt)
+
+
+/* parse the line join used during stroking a path.
+ * Value: miter | round | bevel | inherit
+ * Initial: miter
+ * https://www.w3.org/TR/SVG/painting.html
+ */
+static constexpr struct
+{
+ StrokeJoin lineJoin;
+ const char* tag;
+} lineJoinTags[] = {
+ { StrokeJoin::Miter, "miter" },
+ { StrokeJoin::Round, "round" },
+ { StrokeJoin::Bevel, "bevel" }
+};
+
+
+_PARSE_TAG(StrokeJoin, lineJoin, LineJoin, lineJoinTags, StrokeJoin::Miter)
+
+
+/* parse the fill rule used during filling a path.
+ * Value: nonzero | evenodd | inherit
+ * Initial: nonzero
+ * https://www.w3.org/TR/SVG/painting.html
+ */
+static constexpr struct
+{
+ FillRule fillRule;
+ const char* tag;
+} fillRuleTags[] = {
+ { FillRule::EvenOdd, "evenodd" }
+};
+
+
+_PARSE_TAG(FillRule, fillRule, FillRule, fillRuleTags, FillRule::Winding)
+
+
+/* parse the dash pattern used during stroking a path.
+ * Value: none | <dasharray> | inherit
+ * Initial: none
+ * https://www.w3.org/TR/SVG/painting.html
+ */
+static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* dash)
+{
+ if (!strncmp(str, "none", 4)) return;
+
+ char *end = nullptr;
+
+ while (*str) {
+ str = _skipComma(str);
+ float parsedValue = svgUtilStrtof(str, &end);
+ if (str == end) break;
+ if (parsedValue <= 0.0f) break;
+ if (*end == '%') {
+ ++end;
+ //Refers to the diagonal length of the viewport.
+ //https://www.w3.org/TR/SVG2/coords.html#Units
+ parsedValue = (sqrtf(pow(loader->svgParse->global.w, 2) + pow(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
+ }
+ (*dash).array.push(parsedValue);
+ str = end;
+ }
+ //If dash array size is 1, it means that dash and gap size are the same.
+ if ((*dash).array.count == 1) (*dash).array.push((*dash).array.data[0]);
+}
+
+
+static char* _idFromUrl(const char* url)
+{
+ url = _skipSpace(url, nullptr);
+ if ((*url) == '(') {
+ ++url;
+ url = _skipSpace(url, nullptr);
+ }
+
+ if ((*url) == '\'') ++url;
+ if ((*url) == '#') ++url;
+
+ int i = 0;
+ while (url[i] > ' ' && url[i] != ')' && url[i] != '\'') ++i;
+
+ //custom strndup() for portability
+ int len = strlen(url);
+ if (i < len) len = i;
+
+ auto ret = (char*) malloc(len + 1);
+ if (!ret) return 0;
+ ret[len] = '\0';
+ return (char*) memcpy(ret, url, len);
+}
+
+
+static unsigned char _parserColor(const char* value, char** end)
+{
+ float r;
+
+ r = svgUtilStrtof(value, end);
+ *end = _skipSpace(*end, nullptr);
+ if (**end == '%') r = 255 * r / 100;
+ *end = _skipSpace(*end, nullptr);
+
+ if (r < 0 || r > 255) {
+ *end = nullptr;
+ return 0;
+ }
+
+ return lrint(r);
+}
+
+
+static constexpr struct
+{
+ const char* name;
+ unsigned int value;
+} colors[] = {
+ { "aliceblue", 0xfff0f8ff },
+ { "antiquewhite", 0xfffaebd7 },
+ { "aqua", 0xff00ffff },
+ { "aquamarine", 0xff7fffd4 },
+ { "azure", 0xfff0ffff },
+ { "beige", 0xfff5f5dc },
+ { "bisque", 0xffffe4c4 },
+ { "black", 0xff000000 },
+ { "blanchedalmond", 0xffffebcd },
+ { "blue", 0xff0000ff },
+ { "blueviolet", 0xff8a2be2 },
+ { "brown", 0xffa52a2a },
+ { "burlywood", 0xffdeb887 },
+ { "cadetblue", 0xff5f9ea0 },
+ { "chartreuse", 0xff7fff00 },
+ { "chocolate", 0xffd2691e },
+ { "coral", 0xffff7f50 },
+ { "cornflowerblue", 0xff6495ed },
+ { "cornsilk", 0xfffff8dc },
+ { "crimson", 0xffdc143c },
+ { "cyan", 0xff00ffff },
+ { "darkblue", 0xff00008b },
+ { "darkcyan", 0xff008b8b },
+ { "darkgoldenrod", 0xffb8860b },
+ { "darkgray", 0xffa9a9a9 },
+ { "darkgrey", 0xffa9a9a9 },
+ { "darkgreen", 0xff006400 },
+ { "darkkhaki", 0xffbdb76b },
+ { "darkmagenta", 0xff8b008b },
+ { "darkolivegreen", 0xff556b2f },
+ { "darkorange", 0xffff8c00 },
+ { "darkorchid", 0xff9932cc },
+ { "darkred", 0xff8b0000 },
+ { "darksalmon", 0xffe9967a },
+ { "darkseagreen", 0xff8fbc8f },
+ { "darkslateblue", 0xff483d8b },
+ { "darkslategray", 0xff2f4f4f },
+ { "darkslategrey", 0xff2f4f4f },
+ { "darkturquoise", 0xff00ced1 },
+ { "darkviolet", 0xff9400d3 },
+ { "deeppink", 0xffff1493 },
+ { "deepskyblue", 0xff00bfff },
+ { "dimgray", 0xff696969 },
+ { "dimgrey", 0xff696969 },
+ { "dodgerblue", 0xff1e90ff },
+ { "firebrick", 0xffb22222 },
+ { "floralwhite", 0xfffffaf0 },
+ { "forestgreen", 0xff228b22 },
+ { "fuchsia", 0xffff00ff },
+ { "gainsboro", 0xffdcdcdc },
+ { "ghostwhite", 0xfff8f8ff },
+ { "gold", 0xffffd700 },
+ { "goldenrod", 0xffdaa520 },
+ { "gray", 0xff808080 },
+ { "grey", 0xff808080 },
+ { "green", 0xff008000 },
+ { "greenyellow", 0xffadff2f },
+ { "honeydew", 0xfff0fff0 },
+ { "hotpink", 0xffff69b4 },
+ { "indianred", 0xffcd5c5c },
+ { "indigo", 0xff4b0082 },
+ { "ivory", 0xfffffff0 },
+ { "khaki", 0xfff0e68c },
+ { "lavender", 0xffe6e6fa },
+ { "lavenderblush", 0xfffff0f5 },
+ { "lawngreen", 0xff7cfc00 },
+ { "lemonchiffon", 0xfffffacd },
+ { "lightblue", 0xffadd8e6 },
+ { "lightcoral", 0xfff08080 },
+ { "lightcyan", 0xffe0ffff },
+ { "lightgoldenrodyellow", 0xfffafad2 },
+ { "lightgray", 0xffd3d3d3 },
+ { "lightgrey", 0xffd3d3d3 },
+ { "lightgreen", 0xff90ee90 },
+ { "lightpink", 0xffffb6c1 },
+ { "lightsalmon", 0xffffa07a },
+ { "lightseagreen", 0xff20b2aa },
+ { "lightskyblue", 0xff87cefa },
+ { "lightslategray", 0xff778899 },
+ { "lightslategrey", 0xff778899 },
+ { "lightsteelblue", 0xffb0c4de },
+ { "lightyellow", 0xffffffe0 },
+ { "lime", 0xff00ff00 },
+ { "limegreen", 0xff32cd32 },
+ { "linen", 0xfffaf0e6 },
+ { "magenta", 0xffff00ff },
+ { "maroon", 0xff800000 },
+ { "mediumaquamarine", 0xff66cdaa },
+ { "mediumblue", 0xff0000cd },
+ { "mediumorchid", 0xffba55d3 },
+ { "mediumpurple", 0xff9370d8 },
+ { "mediumseagreen", 0xff3cb371 },
+ { "mediumslateblue", 0xff7b68ee },
+ { "mediumspringgreen", 0xff00fa9a },
+ { "mediumturquoise", 0xff48d1cc },
+ { "mediumvioletred", 0xffc71585 },
+ { "midnightblue", 0xff191970 },
+ { "mintcream", 0xfff5fffa },
+ { "mistyrose", 0xffffe4e1 },
+ { "moccasin", 0xffffe4b5 },
+ { "navajowhite", 0xffffdead },
+ { "navy", 0xff000080 },
+ { "oldlace", 0xfffdf5e6 },
+ { "olive", 0xff808000 },
+ { "olivedrab", 0xff6b8e23 },
+ { "orange", 0xffffa500 },
+ { "orangered", 0xffff4500 },
+ { "orchid", 0xffda70d6 },
+ { "palegoldenrod", 0xffeee8aa },
+ { "palegreen", 0xff98fb98 },
+ { "paleturquoise", 0xffafeeee },
+ { "palevioletred", 0xffd87093 },
+ { "papayawhip", 0xffffefd5 },
+ { "peachpuff", 0xffffdab9 },
+ { "peru", 0xffcd853f },
+ { "pink", 0xffffc0cb },
+ { "plum", 0xffdda0dd },
+ { "powderblue", 0xffb0e0e6 },
+ { "purple", 0xff800080 },
+ { "red", 0xffff0000 },
+ { "rosybrown", 0xffbc8f8f },
+ { "royalblue", 0xff4169e1 },
+ { "saddlebrown", 0xff8b4513 },
+ { "salmon", 0xfffa8072 },
+ { "sandybrown", 0xfff4a460 },
+ { "seagreen", 0xff2e8b57 },
+ { "seashell", 0xfffff5ee },
+ { "sienna", 0xffa0522d },
+ { "silver", 0xffc0c0c0 },
+ { "skyblue", 0xff87ceeb },
+ { "slateblue", 0xff6a5acd },
+ { "slategray", 0xff708090 },
+ { "slategrey", 0xff708090 },
+ { "snow", 0xfffffafa },
+ { "springgreen", 0xff00ff7f },
+ { "steelblue", 0xff4682b4 },
+ { "tan", 0xffd2b48c },
+ { "teal", 0xff008080 },
+ { "thistle", 0xffd8bfd8 },
+ { "tomato", 0xffff6347 },
+ { "turquoise", 0xff40e0d0 },
+ { "violet", 0xffee82ee },
+ { "wheat", 0xfff5deb3 },
+ { "white", 0xffffffff },
+ { "whitesmoke", 0xfff5f5f5 },
+ { "yellow", 0xffffff00 },
+ { "yellowgreen", 0xff9acd32 }
+};
+
+
+static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref)
+{
+ unsigned int len = strlen(str);
+ char *red, *green, *blue;
+ unsigned char tr, tg, tb;
+
+ if (len == 4 && str[0] == '#') {
+ //Case for "#456" should be interprete as "#445566"
+ if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) {
+ char tmp[3] = { '\0', '\0', '\0' };
+ tmp[0] = str[1];
+ tmp[1] = str[1];
+ *r = strtol(tmp, nullptr, 16);
+ tmp[0] = str[2];
+ tmp[1] = str[2];
+ *g = strtol(tmp, nullptr, 16);
+ tmp[0] = str[3];
+ tmp[1] = str[3];
+ *b = strtol(tmp, nullptr, 16);
+ }
+ } else if (len == 7 && str[0] == '#') {
+ if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
+ char tmp[3] = { '\0', '\0', '\0' };
+ tmp[0] = str[1];
+ tmp[1] = str[2];
+ *r = strtol(tmp, nullptr, 16);
+ tmp[0] = str[3];
+ tmp[1] = str[4];
+ *g = strtol(tmp, nullptr, 16);
+ tmp[0] = str[5];
+ tmp[1] = str[6];
+ *b = strtol(tmp, nullptr, 16);
+ }
+ } else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') {
+ tr = _parserColor(str + 4, &red);
+ if (red && *red == ',') {
+ tg = _parserColor(red + 1, &green);
+ if (green && *green == ',') {
+ tb = _parserColor(green + 1, &blue);
+ if (blue && blue[0] == ')' && blue[1] == '\0') {
+ *r = tr;
+ *g = tg;
+ *b = tb;
+ }
+ }
+ }
+ } else if (ref && len >= 3 && !strncmp(str, "url", 3)) {
+ *ref = _idFromUrl((const char*)(str + 3));
+ } else {
+ //Handle named color
+ for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
+ if (!strcasecmp(colors[i].name, str)) {
+ *r = (((uint8_t*)(&(colors[i].value)))[2]);
+ *g = (((uint8_t*)(&(colors[i].value)))[1]);
+ *b = (((uint8_t*)(&(colors[i].value)))[0]);
+ return;
+ }
+ }
+ }
+}
+
+
+static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
+{
+ int count = 0;
+ char* end = nullptr;
+
+ str = _skipSpace(str, nullptr);
+ while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
+ points[count++] = svgUtilStrtof(str, &end);
+ str = end;
+ str = _skipSpace(str, nullptr);
+ if (*str == ',') ++str;
+ //Eat the rest of space
+ str = _skipSpace(str, nullptr);
+ }
+ *ptCount = count;
+ return str;
+}
+
+
+enum class MatrixState {
+ Unknown,
+ Matrix,
+ Translate,
+ Rotate,
+ Scale,
+ SkewX,
+ SkewY
+};
+
+
+#define MATRIX_DEF(Name, Value) \
+ { \
+#Name, sizeof(#Name), Value \
+ }
+
+
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ MatrixState state;
+} matrixTags[] = {
+ MATRIX_DEF(matrix, MatrixState::Matrix),
+ MATRIX_DEF(translate, MatrixState::Translate),
+ MATRIX_DEF(rotate, MatrixState::Rotate),
+ MATRIX_DEF(scale, MatrixState::Scale),
+ MATRIX_DEF(skewX, MatrixState::SkewX),
+ MATRIX_DEF(skewY, MatrixState::SkewY)
+};
+
+
+static void _matrixCompose(const Matrix* m1, const Matrix* m2, Matrix* dst)
+{
+ auto a11 = (m1->e11 * m2->e11) + (m1->e12 * m2->e21) + (m1->e13 * m2->e31);
+ auto a12 = (m1->e11 * m2->e12) + (m1->e12 * m2->e22) + (m1->e13 * m2->e32);
+ auto a13 = (m1->e11 * m2->e13) + (m1->e12 * m2->e23) + (m1->e13 * m2->e33);
+
+ auto a21 = (m1->e21 * m2->e11) + (m1->e22 * m2->e21) + (m1->e23 * m2->e31);
+ auto a22 = (m1->e21 * m2->e12) + (m1->e22 * m2->e22) + (m1->e23 * m2->e32);
+ auto a23 = (m1->e21 * m2->e13) + (m1->e22 * m2->e23) + (m1->e23 * m2->e33);
+
+ auto a31 = (m1->e31 * m2->e11) + (m1->e32 * m2->e21) + (m1->e33 * m2->e31);
+ auto a32 = (m1->e31 * m2->e12) + (m1->e32 * m2->e22) + (m1->e33 * m2->e32);
+ auto a33 = (m1->e31 * m2->e13) + (m1->e32 * m2->e23) + (m1->e33 * m2->e33);
+
+ dst->e11 = a11;
+ dst->e12 = a12;
+ dst->e13 = a13;
+ dst->e21 = a21;
+ dst->e22 = a22;
+ dst->e23 = a23;
+ dst->e31 = a31;
+ dst->e32 = a32;
+ dst->e33 = a33;
+}
+
+
+/* parse transform attribute
+ * https://www.w3.org/TR/SVG/coords.html#TransformAttribute
+ */
+static Matrix* _parseTransformationMatrix(const char* value)
+{
+ const int POINT_CNT = 8;
+
+ auto matrix = (Matrix*)malloc(sizeof(Matrix));
+ if (!matrix) return nullptr;
+ *matrix = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+
+ float points[POINT_CNT];
+ int ptCount = 0;
+ char* str = (char*)value;
+ char* end = str + strlen(str);
+
+ while (str < end) {
+ auto state = MatrixState::Unknown;
+
+ if (isspace(*str) || (*str == ',')) {
+ ++str;
+ continue;
+ }
+ for (unsigned int i = 0; i < sizeof(matrixTags) / sizeof(matrixTags[0]); i++) {
+ if (!strncmp(matrixTags[i].tag, str, matrixTags[i].sz - 1)) {
+ state = matrixTags[i].state;
+ str += (matrixTags[i].sz - 1);
+ break;
+ }
+ }
+ if (state == MatrixState::Unknown) goto error;
+
+ str = _skipSpace(str, end);
+ if (*str != '(') goto error;
+ ++str;
+ str = _parseNumbersArray(str, points, &ptCount, POINT_CNT);
+ if (*str != ')') goto error;
+ ++str;
+
+ if (state == MatrixState::Matrix) {
+ if (ptCount != 6) goto error;
+ Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
+ _matrixCompose(matrix, &tmp, matrix);
+ } else if (state == MatrixState::Translate) {
+ if (ptCount == 1) {
+ Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
+ _matrixCompose(matrix, &tmp, matrix);
+ } else if (ptCount == 2) {
+ Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
+ _matrixCompose(matrix, &tmp, matrix);
+ } else goto error;
+ } else if (state == MatrixState::Rotate) {
+ //Transform to signed.
+ points[0] = fmod(points[0], 360);
+ if (points[0] < 0) points[0] += 360;
+ auto c = cosf(points[0] * (M_PI / 180.0));
+ auto s = sinf(points[0] * (M_PI / 180.0));
+ if (ptCount == 1) {
+ Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
+ _matrixCompose(matrix, &tmp, matrix);
+ } else if (ptCount == 3) {
+ Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
+ _matrixCompose(matrix, &tmp, matrix);
+ tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
+ _matrixCompose(matrix, &tmp, matrix);
+ tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
+ _matrixCompose(matrix, &tmp, matrix);
+ } else {
+ goto error;
+ }
+ } else if (state == MatrixState::Scale) {
+ if (ptCount < 1 || ptCount > 2) goto error;
+ auto sx = points[0];
+ auto sy = sx;
+ if (ptCount == 2) sy = points[1];
+ Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
+ _matrixCompose(matrix, &tmp, matrix);
+ }
+ }
+ return matrix;
+error:
+ if (matrix) free(matrix);
+ return nullptr;
+}
+
+
+#define LENGTH_DEF(Name, Value) \
+ { \
+#Name, sizeof(#Name), Value \
+ }
+
+
+/*
+// TODO - remove?
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ SvgLengthType type;
+} lengthTags[] = {
+ LENGTH_DEF(%, SvgLengthType::Percent),
+ LENGTH_DEF(px, SvgLengthType::Px),
+ LENGTH_DEF(pc, SvgLengthType::Pc),
+ LENGTH_DEF(pt, SvgLengthType::Pt),
+ LENGTH_DEF(mm, SvgLengthType::Mm),
+ LENGTH_DEF(cm, SvgLengthType::Cm),
+ LENGTH_DEF(in, SvgLengthType::In)
+};
+
+static float _parseLength(const char* str, SvgLengthType* type)
+{
+ float value;
+ int sz = strlen(str);
+
+ *type = SvgLengthType::Px;
+ for (unsigned int i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) {
+ if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type;
+ }
+ value = svgUtilStrtof(str, nullptr);
+ return value;
+}
+*/
+
+static bool _parseStyleAttr(void* data, const char* key, const char* value);
+static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style);
+
+
+static bool _attrParseSvgNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgDocNode* doc = &(node->node.doc);
+
+ if (!strcmp(key, "width")) {
+ doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+ } else if (!strcmp(key, "height")) {
+ doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
+ } else if (!strcmp(key, "viewBox")) {
+ if (_parseNumber(&value, &doc->vx)) {
+ if (_parseNumber(&value, &doc->vy)) {
+ if (_parseNumber(&value, &doc->vw)) {
+ _parseNumber(&value, &doc->vh);
+ loader->svgParse->global.h = (uint32_t)doc->vh;
+ }
+ loader->svgParse->global.w = (uint32_t)doc->vw;
+ }
+ loader->svgParse->global.y = (int)doc->vy;
+ }
+ loader->svgParse->global.x = (int)doc->vx;
+ } else if (!strcmp(key, "preserveAspectRatio")) {
+ if (!strcmp(value, "none")) doc->preserveAspect = false;
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ }
+#ifdef THORVG_LOG_ENABLED
+ else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) {
+ TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
+ }
+#endif
+ else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+//https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
+static void _handlePaintAttr(SvgPaint* paint, const char* value)
+{
+ if (!strcmp(value, "none")) {
+ //No paint property
+ paint->none = true;
+ return;
+ }
+ paint->none = false;
+ if (!strcmp(value, "currentColor")) {
+ paint->curColor = true;
+ return;
+ }
+ _toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url);
+}
+
+
+static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ SvgStyleProperty* style = node->style;
+ style->curColorSet = true;
+ _toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr);
+}
+
+
+static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ SvgStyleProperty* style = node->style;
+ style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint);
+ _handlePaintAttr(&style->fill.paint, value);
+}
+
+
+static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ SvgStyleProperty* style = node->style;
+ style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint);
+ _handlePaintAttr(&style->stroke.paint, value);
+}
+
+
+static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity);
+ node->style->stroke.opacity = _toOpacity(value);
+}
+
+static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Dash);
+ _parseDashArray(loader, value, &node->style->stroke.dash);
+}
+
+static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width);
+ node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+}
+
+
+static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap);
+ node->style->stroke.cap = _toLineCap(value);
+}
+
+
+static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join);
+ node->style->stroke.join = _toLineJoin(value);
+}
+
+
+static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule);
+ node->style->fill.fillRule = _toFillRule(value);
+}
+
+
+static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->opacity = _toOpacity(value);
+}
+
+
+static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity);
+ node->style->fill.opacity = _toOpacity(value);
+}
+
+
+static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->transform = _parseTransformationMatrix(value);
+}
+
+
+static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ SvgStyleProperty* style = node->style;
+ int len = strlen(value);
+ if (len >= 3 && !strncmp(value, "url", 3)) {
+ if (style->clipPath.url) free(style->clipPath.url);
+ style->clipPath.url = _idFromUrl((const char*)(value + 3));
+ }
+}
+
+
+static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ SvgStyleProperty* style = node->style;
+ int len = strlen(value);
+ if (len >= 3 && !strncmp(value, "url", 3)) {
+ if (style->mask.url) free(style->mask.url);
+ style->mask.url = _idFromUrl((const char*)(value + 3));
+ }
+}
+
+
+static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ //TODO : The display attribute can have various values as well as "none".
+ // The default is "inline" which means visible and "none" means invisible.
+ // Depending on the type of node, additional functionality may be required.
+ // refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
+ if (!strcmp(value, "none")) node->display = false;
+ else node->display = true;
+}
+
+
+typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value);
+
+#define STYLE_DEF(Name, Name1, Flag) { #Name, sizeof(#Name), _handle##Name1##Attr, Flag }
+
+
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ styleMethod tagHandler;
+ SvgStyleFlags flag;
+} styleTags[] = {
+ STYLE_DEF(color, Color, SvgStyleFlags::Color),
+ STYLE_DEF(fill, Fill, SvgStyleFlags::Fill),
+ STYLE_DEF(fill-rule, FillRule, SvgStyleFlags::FillRule),
+ STYLE_DEF(fill-opacity, FillOpacity, SvgStyleFlags::FillOpacity),
+ STYLE_DEF(opacity, Opacity, SvgStyleFlags::Opacity),
+ STYLE_DEF(stroke, Stroke, SvgStyleFlags::Stroke),
+ STYLE_DEF(stroke-width, StrokeWidth, SvgStyleFlags::StrokeWidth),
+ STYLE_DEF(stroke-linejoin, StrokeLineJoin, SvgStyleFlags::StrokeLineJoin),
+ STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
+ STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
+ STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
+ STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
+ STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
+ STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
+ STYLE_DEF(display, Display, SvgStyleFlags::Display)
+};
+
+
+static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ int sz;
+ if (!key || !value) return false;
+
+ //Trim the white space
+ key = _skipSpace(key, nullptr);
+ value = _skipSpace(value, nullptr);
+
+ sz = strlen(key);
+ for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) {
+ if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
+ if (style) {
+ styleTags[i].tagHandler(loader, node, value);
+ node->style->flags = (SvgStyleFlags)((int)node->style->flags | (int)styleTags[i].flag);
+ } else if (!((int)node->style->flags & (int)styleTags[i].flag)) {
+ styleTags[i].tagHandler(loader, node, value);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+static bool _parseStyleAttr(void* data, const char* key, const char* value)
+{
+ return _parseStyleAttr(data, key, value, true);
+}
+
+
+/* parse g node
+ * https://www.w3.org/TR/SVG/struct.html#Groups
+ */
+static bool _attrParseGNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+
+ if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ } else if (!strcmp(key, "transform")) {
+ node->transform = _parseTransformationMatrix(value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+/* parse clipPath node
+ * https://www.w3.org/TR/SVG/struct.html#Groups
+ */
+static bool _attrParseClipPathNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgCompositeNode* comp = &(node->node.comp);
+
+ if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ } else if (!strcmp(key, "transform")) {
+ node->transform = _parseTransformationMatrix(value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "clipPathUnits")) {
+ if (!strcmp(value, "objectBoundingBox")) comp->userSpace = false;
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static bool _attrParseMaskNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgCompositeNode* comp = &(node->node.comp);
+
+ if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ } else if (!strcmp(key, "transform")) {
+ node->transform = _parseTransformationMatrix(value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "maskContentUnits")) {
+ if (!strcmp(value, "objectBoundingBox")) comp->userSpace = false;
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
+{
+ SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
+
+ if (!node) return nullptr;
+
+ //Default fill property
+ node->style = (SvgStyleProperty*)calloc(1, sizeof(SvgStyleProperty));
+
+ if (!node->style) {
+ free(node);
+ return nullptr;
+ }
+
+ //Update the default value of stroke and fill
+ //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
+ node->style->fill.paint.none = false;
+ //Default fill opacity is 1
+ node->style->fill.opacity = 255;
+ node->style->opacity = 255;
+ //Default current color is not set
+ node->style->fill.paint.curColor = false;
+ node->style->curColorSet = false;
+ //Default fill rule is nonzero
+ node->style->fill.fillRule = FillRule::Winding;
+
+ //Default stroke is none
+ node->style->stroke.paint.none = true;
+ //Default stroke opacity is 1
+ node->style->stroke.opacity = 255;
+ //Default stroke current color is not set
+ node->style->stroke.paint.curColor = false;
+ //Default stroke width is 1
+ node->style->stroke.width = 1;
+ //Default line cap is butt
+ node->style->stroke.cap = StrokeCap::Butt;
+ //Default line join is miter
+ node->style->stroke.join = StrokeJoin::Miter;
+ node->style->stroke.scale = 1.0;
+
+ //Default display is true("inline").
+ node->display = true;
+
+ node->parent = parent;
+ node->type = type;
+
+ if (parent) parent->child.push(node);
+ return node;
+}
+
+
+static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ if (loader->def && loader->doc->node.doc.defs) return loader->def;
+ SvgNode* node = _createNode(nullptr, SvgNodeType::Defs);
+
+ loader->def = node;
+ loader->doc->node.doc.defs = node;
+ return node;
+}
+
+
+static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::G);
+ if (!loader->svgParse->node) return nullptr;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParseGNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Doc);
+ if (!loader->svgParse->node) return nullptr;
+ SvgDocNode* doc = &(loader->svgParse->node->node.doc);
+
+ loader->svgParse->global.w = 0;
+ loader->svgParse->global.h = 0;
+
+ doc->preserveAspect = true;
+ simpleXmlParseAttributes(buf, bufLength, _attrParseSvgNode, loader);
+
+ if (loader->svgParse->global.w == 0) {
+ if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1;
+ else loader->svgParse->global.w = (uint32_t)doc->w;
+ }
+ if (loader->svgParse->global.h == 0) {
+ if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1;
+ else loader->svgParse->global.h = (uint32_t)doc->h;
+ }
+
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Mask);
+ if (!loader->svgParse->node) return nullptr;
+
+ loader->svgParse->node->node.comp.userSpace = true;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParseMaskNode, loader);
+
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
+ if (!loader->svgParse->node) return nullptr;
+
+ loader->svgParse->node->display = false;
+ loader->svgParse->node->node.comp.userSpace = true;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParseClipPathNode, loader);
+
+ return loader->svgParse->node;
+}
+
+static bool _attrParsePathNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgPathNode* path = &(node->node.path);
+
+ if (!strcmp(key, "d")) {
+ //Temporary: need to copy
+ path->path = _copyId(value);
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Path);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParsePathNode, loader);
+
+ return loader->svgParse->node;
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} circleTags[] = {
+ {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
+ {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)},
+ {"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)}
+};
+
+
+/* parse the attributes for a circle element.
+ * https://www.w3.org/TR/SVG/shapes.html#CircleElement
+ */
+static bool _attrParseCircleNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgCircleNode* circle = &(node->node.circle);
+ unsigned char* array;
+ int sz = strlen(key);
+
+ array = (unsigned char*)circle;
+ for (unsigned int i = 0; i < sizeof(circleTags) / sizeof(circleTags[0]); i++) {
+ if (circleTags[i].sz - 1 == sz && !strncmp(circleTags[i].tag, key, sz)) {
+ *((float*)(array + circleTags[i].offset)) = _toFloat(loader->svgParse, value, circleTags[i].type);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Circle);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParseCircleNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} ellipseTags[] = {
+ {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgEllipseNode, cx)},
+ {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgEllipseNode, cy)},
+ {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgEllipseNode, rx)},
+ {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgEllipseNode, ry)}
+};
+
+
+/* parse the attributes for an ellipse element.
+ * https://www.w3.org/TR/SVG/shapes.html#EllipseElement
+ */
+static bool _attrParseEllipseNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgEllipseNode* ellipse = &(node->node.ellipse);
+ unsigned char* array;
+ int sz = strlen(key);
+
+ array = (unsigned char*)ellipse;
+ for (unsigned int i = 0; i < sizeof(ellipseTags) / sizeof(ellipseTags[0]); i++) {
+ if (ellipseTags[i].sz - 1 == sz && !strncmp(ellipseTags[i].tag, key, sz)) {
+ *((float*)(array + ellipseTags[i].offset)) = _toFloat(loader->svgParse, value, ellipseTags[i].type);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParseEllipseNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static bool _attrParsePolygonPoints(const char* str, float** points, int* ptCount)
+{
+ float tmp[50];
+ int tmpCount = 0;
+ int count = 0;
+ float num;
+ float *pointArray = nullptr, *tmpArray;
+
+ while (_parseNumber(&str, &num)) {
+ tmp[tmpCount++] = num;
+ if (tmpCount == 50) {
+ tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
+ if (!tmpArray) goto error_alloc;
+ pointArray = tmpArray;
+ memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
+ count += tmpCount;
+ tmpCount = 0;
+ }
+ }
+
+ if (tmpCount > 0) {
+ tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
+ if (!tmpArray) goto error_alloc;
+ pointArray = tmpArray;
+ memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
+ count += tmpCount;
+ }
+ *ptCount = count;
+ *points = pointArray;
+ return true;
+
+error_alloc:
+ return false;
+}
+
+
+/* parse the attributes for a polygon element.
+ * https://www.w3.org/TR/SVG/shapes.html#PolylineElement
+ */
+static bool _attrParsePolygonNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgPolygonNode* polygon = nullptr;
+
+ if (node->type == SvgNodeType::Polygon) polygon = &(node->node.polygon);
+ else polygon = &(node->node.polyline);
+
+ if (!strcmp(key, "points")) {
+ return _attrParsePolygonPoints(value, &polygon->points, &polygon->pointsCount);
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
+ return loader->svgParse->node;
+}
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} rectTags[] = {
+ {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
+ {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
+ {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
+ {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)},
+ {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgRectNode, rx)},
+ {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgRectNode, ry)}
+};
+
+
+/* parse the attributes for a rect element.
+ * https://www.w3.org/TR/SVG/shapes.html#RectElement
+ */
+static bool _attrParseRectNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgRectNode* rect = &(node->node.rect);
+ unsigned char* array;
+ bool ret = true;
+ int sz = strlen(key);
+
+ array = (unsigned char*)rect;
+ for (unsigned int i = 0; i < sizeof(rectTags) / sizeof(rectTags[0]); i++) {
+ if (rectTags[i].sz - 1 == sz && !strncmp(rectTags[i].tag, key, sz)) {
+ *((float*)(array + rectTags[i].offset)) = _toFloat(loader->svgParse, value, rectTags[i].type);
+
+ //Case if only rx or ry is declared
+ if (!strncmp(rectTags[i].tag, "rx", sz)) rect->hasRx = true;
+ if (!strncmp(rectTags[i].tag, "ry", sz)) rect->hasRy = true;
+
+ if ((rect->rx >= FLT_EPSILON) && (rect->ry < FLT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx;
+ if ((rect->ry >= FLT_EPSILON) && (rect->rx < FLT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry;
+ return ret;
+ }
+ }
+
+ if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "style")) {
+ ret = simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else {
+ ret = _parseStyleAttr(loader, key, value, false);
+ }
+
+ return ret;
+}
+
+
+static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Rect);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ loader->svgParse->node->node.rect.hasRx = loader->svgParse->node->node.rect.hasRy = false;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParseRectNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} lineTags[] = {
+ {"x1", SvgParserLengthType::Horizontal, sizeof("x1"), offsetof(SvgLineNode, x1)},
+ {"y1", SvgParserLengthType::Vertical, sizeof("y1"), offsetof(SvgLineNode, y1)},
+ {"x2", SvgParserLengthType::Horizontal, sizeof("x2"), offsetof(SvgLineNode, x2)},
+ {"y2", SvgParserLengthType::Vertical, sizeof("y2"), offsetof(SvgLineNode, y2)}
+};
+
+
+/* parse the attributes for a line element.
+ * https://www.w3.org/TR/SVG/shapes.html#LineElement
+ */
+static bool _attrParseLineNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgLineNode* line = &(node->node.line);
+ unsigned char* array;
+ int sz = strlen(key);
+
+ array = (unsigned char*)line;
+ for (unsigned int i = 0; i < sizeof(lineTags) / sizeof(lineTags[0]); i++) {
+ if (lineTags[i].sz - 1 == sz && !strncmp(lineTags[i].tag, key, sz)) {
+ *((float*)(array + lineTags[i].offset)) = _toFloat(loader->svgParse, value, lineTags[i].type);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Line);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParseLineNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static char* _idFromHref(const char* href)
+{
+ href = _skipSpace(href, nullptr);
+ if ((*href) == '#') href++;
+ return strdup(href);
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} imageTags[] = {
+ {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
+ {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
+ {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
+ {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)},
+};
+
+
+/* parse the attributes for a image element.
+ * https://www.w3.org/TR/SVG/embedded.html#ImageElement
+ */
+static bool _attrParseImageNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgImageNode* image = &(node->node.image);
+ unsigned char* array;
+ int sz = strlen(key);
+
+ array = (unsigned char*)image;
+ for (unsigned int i = 0; i < sizeof(imageTags) / sizeof(imageTags[0]); i++) {
+ if (imageTags[i].sz - 1 == sz && !strncmp(imageTags[i].tag, key, sz)) {
+ *((float*)(array + imageTags[i].offset)) = _toFloat(loader->svgParse, value, imageTags[i].type);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ if (image->href && value) free(image->href);
+ image->href = _idFromHref(value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else {
+ return _parseStyleAttr(loader, key, value);
+ }
+ return true;
+}
+
+
+static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Image);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParseImageNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _getDefsNode(SvgNode* node)
+{
+ if (!node) return nullptr;
+
+ while (node->parent != nullptr) {
+ node = node->parent;
+ }
+
+ if (node->type == SvgNodeType::Doc) return node->node.doc.defs;
+
+ return nullptr;
+}
+
+
+static SvgNode* _findChildById(const SvgNode* node, const char* id)
+{
+ if (!node) return nullptr;
+
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ if (((*child)->id) && !strcmp((*child)->id, id)) return (*child);
+ }
+ return nullptr;
+}
+
+static SvgNode* _findNodeById(SvgNode *node, const char* id)
+{
+ SvgNode* result = nullptr;
+ if (node->id && !strcmp(node->id, id)) return node;
+
+ if (node->child.count > 0) {
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ result = _findNodeById(*child, id);
+ if (result) break;
+ }
+ }
+ return result;
+}
+
+static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
+{
+ for (uint32_t i = 0; i < src.count; ++i) {
+ dst.push(src.data[i]);
+ }
+}
+
+
+static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
+{
+ if (!from) return nullptr;
+
+ auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
+ if (!grad) return nullptr;
+
+ grad->type = from->type;
+ grad->id = from->id ? _copyId(from->id) : nullptr;
+ grad->ref = from->ref ? _copyId(from->ref) : nullptr;
+ grad->spread = from->spread;
+ grad->userSpace = from->userSpace;
+
+ if (from->transform) {
+ grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
+ if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
+ }
+
+ if (grad->type == SvgGradientType::Linear) {
+ grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
+ if (!grad->linear) goto error_grad_alloc;
+ memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
+ } else if (grad->type == SvgGradientType::Radial) {
+ grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
+ if (!grad->radial) goto error_grad_alloc;
+ memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
+ }
+
+ _cloneGradStops(grad->stops, from->stops);
+
+ return grad;
+
+error_grad_alloc:
+ if (grad) {
+ grad->clear();
+ free(grad);
+ }
+ return nullptr;
+}
+
+
+static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
+{
+ if (parent == nullptr) return;
+ //Inherit the property of parent if not present in child.
+ if (!child->curColorSet) {
+ child->color = parent->color;
+ child->curColorSet = parent->curColorSet;
+ }
+ //Fill
+ if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
+ child->fill.paint.color = parent->fill.paint.color;
+ child->fill.paint.none = parent->fill.paint.none;
+ child->fill.paint.curColor = parent->fill.paint.curColor;
+ if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url);
+ }
+ if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
+ child->fill.opacity = parent->fill.opacity;
+ }
+ if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
+ child->fill.fillRule = parent->fill.fillRule;
+ }
+ //Stroke
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
+ child->stroke.paint.color = parent->stroke.paint.color;
+ child->stroke.paint.none = parent->stroke.paint.none;
+ child->stroke.paint.curColor = parent->stroke.paint.curColor;
+ child->stroke.paint.url = parent->stroke.paint.url ? _copyId(parent->stroke.paint.url) : nullptr;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
+ child->stroke.opacity = parent->stroke.opacity;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
+ child->stroke.width = parent->stroke.width;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) {
+ if (parent->stroke.dash.array.count > 0) {
+ child->stroke.dash.array.clear();
+ child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
+ for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
+ child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
+ child->stroke.cap = parent->stroke.cap;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
+ child->stroke.join = parent->stroke.join;
+ }
+}
+
+
+static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
+{
+ if (from == nullptr) return;
+ //Copy the properties of 'from' only if they were explicitly set (not the default ones).
+ if (from->curColorSet) {
+ to->color = from->color;
+ to->curColorSet = true;
+ }
+ //Fill
+ to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)from->fill.flags);
+ if (((int)from->fill.flags & (int)SvgFillFlags::Paint)) {
+ to->fill.paint.color = from->fill.paint.color;
+ to->fill.paint.none = from->fill.paint.none;
+ to->fill.paint.curColor = from->fill.paint.curColor;
+ if (from->fill.paint.url) to->fill.paint.url = _copyId(from->fill.paint.url);
+ }
+ if (((int)from->fill.flags & (int)SvgFillFlags::Opacity)) {
+ to->fill.opacity = from->fill.opacity;
+ }
+ if (((int)from->fill.flags & (int)SvgFillFlags::FillRule)) {
+ to->fill.fillRule = from->fill.fillRule;
+ }
+ //Stroke
+ to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)from->stroke.flags);
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint)) {
+ to->stroke.paint.color = from->stroke.paint.color;
+ to->stroke.paint.none = from->stroke.paint.none;
+ to->stroke.paint.curColor = from->stroke.paint.curColor;
+ to->stroke.paint.url = from->stroke.paint.url ? _copyId(from->stroke.paint.url) : nullptr;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
+ to->stroke.opacity = from->stroke.opacity;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width)) {
+ to->stroke.width = from->stroke.width;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash)) {
+ if (from->stroke.dash.array.count > 0) {
+ to->stroke.dash.array.clear();
+ to->stroke.dash.array.reserve(from->stroke.dash.array.count);
+ for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
+ to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap)) {
+ to->stroke.cap = from->stroke.cap;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join)) {
+ to->stroke.join = from->stroke.join;
+ }
+}
+
+
+static void _copyAttr(SvgNode* to, const SvgNode* from)
+{
+ //Copy matrix attribute
+ if (from->transform) {
+ to->transform = (Matrix*)malloc(sizeof(Matrix));
+ if (to->transform) *to->transform = *from->transform;
+ }
+ //Copy style attribute
+ _styleCopy(to->style, from->style);
+ to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags);
+ if (from->style->fill.paint.url) to->style->fill.paint.url = strdup(from->style->fill.paint.url);
+ if (from->style->stroke.paint.url) to->style->stroke.paint.url = strdup(from->style->stroke.paint.url);
+ if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
+ if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url);
+
+ //Copy node attribute
+ switch (from->type) {
+ case SvgNodeType::Circle: {
+ to->node.circle.cx = from->node.circle.cx;
+ to->node.circle.cy = from->node.circle.cy;
+ to->node.circle.r = from->node.circle.r;
+ break;
+ }
+ case SvgNodeType::Ellipse: {
+ to->node.ellipse.cx = from->node.ellipse.cx;
+ to->node.ellipse.cy = from->node.ellipse.cy;
+ to->node.ellipse.rx = from->node.ellipse.rx;
+ to->node.ellipse.ry = from->node.ellipse.ry;
+ break;
+ }
+ case SvgNodeType::Rect: {
+ to->node.rect.x = from->node.rect.x;
+ to->node.rect.y = from->node.rect.y;
+ to->node.rect.w = from->node.rect.w;
+ to->node.rect.h = from->node.rect.h;
+ to->node.rect.rx = from->node.rect.rx;
+ to->node.rect.ry = from->node.rect.ry;
+ to->node.rect.hasRx = from->node.rect.hasRx;
+ to->node.rect.hasRy = from->node.rect.hasRy;
+ break;
+ }
+ case SvgNodeType::Line: {
+ to->node.line.x1 = from->node.line.x1;
+ to->node.line.y1 = from->node.line.y1;
+ to->node.line.x2 = from->node.line.x2;
+ to->node.line.y2 = from->node.line.y2;
+ break;
+ }
+ case SvgNodeType::Path: {
+ if (from->node.path.path) to->node.path.path = strdup(from->node.path.path);
+ break;
+ }
+ case SvgNodeType::Polygon: {
+ if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) {
+ to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
+ memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
+ }
+ break;
+ }
+ case SvgNodeType::Polyline: {
+ if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) {
+ to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
+ memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
+ }
+ break;
+ }
+ case SvgNodeType::Image: {
+ to->node.image.x = from->node.image.x;
+ to->node.image.y = from->node.image.y;
+ to->node.image.w = from->node.image.w;
+ to->node.image.h = from->node.image.h;
+ if (from->node.image.href) to->node.image.href = strdup(from->node.image.href);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+
+static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
+{
+ /* Exception handling: Prevent invalid SVG data input.
+ The size is the arbitrary value, we need an experimental size. */
+ if (depth == 8192) {
+ TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
+ return;
+ }
+
+ SvgNode* newNode;
+ if (!from || !parent || from == parent) return;
+
+ newNode = _createNode(parent, from->type);
+ if (!newNode) return;
+
+ _styleInherit(newNode->style, parent->style);
+ _copyAttr(newNode, from);
+
+ auto child = from->child.data;
+ for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
+ _cloneNode(*child, newNode, depth + 1);
+ }
+}
+
+
+static void _postponeCloneNode(SvgLoaderData* loader, SvgNode *node, char* id)
+{
+ loader->cloneNodes.push({node, id});
+}
+
+
+static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
+{
+ for (uint32_t i = 0; i < cloneNodes->count; ++i) {
+ auto nodeIdPair = cloneNodes->data[i];
+ auto defs = _getDefsNode(nodeIdPair.node);
+ auto nodeFrom = _findChildById(defs, nodeIdPair.id);
+ if (!nodeFrom) nodeFrom = _findChildById(doc, nodeIdPair.id);
+ _cloneNode(nodeFrom, nodeIdPair.node, 0);
+ free(nodeIdPair.id);
+ }
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} useTags[] = {
+ {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
+ {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
+ {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
+ {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)}
+};
+
+
+static bool _attrParseUseNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode *defs, *nodeFrom, *node = loader->svgParse->node;
+ char* id;
+
+ SvgUseNode* use = &(node->node.use);
+ int sz = strlen(key);
+ unsigned char* array = (unsigned char*)use;
+ for (unsigned int i = 0; i < sizeof(useTags) / sizeof(useTags[0]); i++) {
+ if (useTags[i].sz - 1 == sz && !strncmp(useTags[i].tag, key, sz)) {
+ *((float*)(array + useTags[i].offset)) = _toFloat(loader->svgParse, value, useTags[i].type);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ id = _idFromHref(value);
+ defs = _getDefsNode(node);
+ nodeFrom = _findChildById(defs, id);
+ if (nodeFrom) {
+ _cloneNode(nodeFrom, node, 0);
+ free(id);
+ } else {
+ //some svg export software include <defs> element at the end of the file
+ //if so the 'from' element won't be found now and we have to repeat finding
+ //after the whole file is parsed
+ _postponeCloneNode(loader, node, id);
+ }
+ } else {
+ return _attrParseGNode(data, key, value);
+ }
+ return true;
+}
+
+
+static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Use);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParseUseNode, loader);
+ return loader->svgParse->node;
+}
+
+//TODO: Implement 'text' primitive
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ FactoryMethod tagHandler;
+} graphicsTags[] = {
+ {"use", sizeof("use"), _createUseNode},
+ {"circle", sizeof("circle"), _createCircleNode},
+ {"ellipse", sizeof("ellipse"), _createEllipseNode},
+ {"path", sizeof("path"), _createPathNode},
+ {"polygon", sizeof("polygon"), _createPolygonNode},
+ {"rect", sizeof("rect"), _createRectNode},
+ {"polyline", sizeof("polyline"), _createPolylineNode},
+ {"line", sizeof("line"), _createLineNode},
+ {"image", sizeof("image"), _createImageNode}
+};
+
+
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ FactoryMethod tagHandler;
+} groupTags[] = {
+ {"defs", sizeof("defs"), _createDefsNode},
+ {"g", sizeof("g"), _createGNode},
+ {"svg", sizeof("svg"), _createSvgNode},
+ {"mask", sizeof("mask"), _createMaskNode},
+ {"clipPath", sizeof("clipPath"), _createClipPathNode}
+};
+
+
+#define FIND_FACTORY(Short_Name, Tags_Array) \
+ static FactoryMethod \
+ _find##Short_Name##Factory(const char* name) \
+ { \
+ unsigned int i; \
+ int sz = strlen(name); \
+ \
+ for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
+ if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \
+ return Tags_Array[i].tagHandler; \
+ } \
+ } \
+ return nullptr; \
+ }
+
+FIND_FACTORY(Group, groupTags)
+FIND_FACTORY(Graphics, graphicsTags)
+
+
+FillSpread _parseSpreadValue(const char* value)
+{
+ auto spread = FillSpread::Pad;
+
+ if (!strcmp(value, "reflect")) {
+ spread = FillSpread::Reflect;
+ } else if (!strcmp(value, "repeat")) {
+ spread = FillSpread::Repeat;
+ }
+
+ return spread;
+}
+
+
+static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
+{
+ radial->cx = _gradientToFloat(loader->svgParse, value, radial->isCxPercentage);
+ if (!loader->svgParse->gradient.parsedFx) {
+ radial->fx = radial->cx;
+ radial->isFxPercentage = radial->isCxPercentage;
+ }
+}
+
+
+static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
+{
+ radial->cy = _gradientToFloat(loader->svgParse, value, radial->isCyPercentage);
+ if (!loader->svgParse->gradient.parsedFy) {
+ radial->fy = radial->cy;
+ radial->isFyPercentage = radial->isCyPercentage;
+ }
+}
+
+
+static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
+{
+ radial->fx = _gradientToFloat(loader->svgParse, value, radial->isFxPercentage);
+ loader->svgParse->gradient.parsedFx = true;
+}
+
+
+static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
+{
+ radial->fy = _gradientToFloat(loader->svgParse, value, radial->isFyPercentage);
+ loader->svgParse->gradient.parsedFy = true;
+}
+
+
+static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
+{
+ radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage);
+}
+
+
+static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (userSpace && !radial->isCxPercentage) radial->cx = radial->cx / loader->svgParse->global.w;
+}
+
+
+static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (userSpace && !radial->isCyPercentage) radial->cy = radial->cy / loader->svgParse->global.h;
+}
+
+
+static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (userSpace && !radial->isFxPercentage) radial->fx = radial->fx / loader->svgParse->global.w;
+}
+
+
+static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (userSpace && !radial->isFyPercentage) radial->fy = radial->fy / loader->svgParse->global.h;
+}
+
+
+static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
+ if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrtf(2.0));
+}
+
+
+typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
+typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
+
+
+#define RADIAL_DEF(Name, Name1) \
+ { \
+#Name, sizeof(#Name), _handleRadial##Name1##Attr, _recalcRadial##Name1##Attr \
+ }
+
+
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ radialMethod tagHandler;
+ radialMethodRecalc tagRecalc;
+} radialTags[] = {
+ RADIAL_DEF(cx, Cx),
+ RADIAL_DEF(cy, Cy),
+ RADIAL_DEF(fx, Fx),
+ RADIAL_DEF(fy, Fy),
+ RADIAL_DEF(r, R)
+};
+
+
+static bool _attrParseRadialGradientNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgStyleGradient* grad = loader->svgParse->styleGrad;
+ SvgRadialGradient* radial = grad->radial;
+ int sz = strlen(key);
+
+ for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
+ if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
+ radialTags[i].tagHandler(loader, radial, value);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "id")) {
+ if (grad->id && value) free(grad->id);
+ grad->id = _copyId(value);
+ } else if (!strcmp(key, "spreadMethod")) {
+ grad->spread = _parseSpreadValue(value);
+ } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ if (grad->ref && value) free(grad->ref);
+ grad->ref = _idFromHref(value);
+ } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
+ grad->userSpace = true;
+ } else if (!strcmp(key, "gradientTransform")) {
+ grad->transform = _parseTransformationMatrix(value);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+
+static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
+{
+ auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
+ loader->svgParse->styleGrad = grad;
+
+ grad->type = SvgGradientType::Radial;
+ grad->userSpace = false;
+ grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
+ if (!grad->radial) {
+ grad->clear();
+ free(grad);
+ return nullptr;
+ }
+ /**
+ * Default values of gradient transformed into global percentage
+ */
+ grad->radial->cx = 0.5f;
+ grad->radial->cy = 0.5f;
+ grad->radial->fx = 0.5f;
+ grad->radial->fy = 0.5f;
+ grad->radial->r = 0.5f;
+ grad->radial->isCxPercentage = true;
+ grad->radial->isCyPercentage = true;
+ grad->radial->isFxPercentage = true;
+ grad->radial->isFyPercentage = true;
+ grad->radial->isRPercentage = true;
+
+ loader->svgParse->gradient.parsedFx = false;
+ loader->svgParse->gradient.parsedFy = false;
+ simpleXmlParseAttributes(buf, bufLength,
+ _attrParseRadialGradientNode, loader);
+
+ for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
+ radialTags[i].tagRecalc(loader, grad->radial, grad->userSpace);
+ }
+
+ return loader->svgParse->styleGrad;
+}
+
+
+static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ auto stop = &loader->svgParse->gradStop;
+
+ if (!strcmp(key, "stop-opacity")) {
+ stop->a = _toOpacity(value);
+ loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopOpacity);
+ } else if (!strcmp(key, "stop-color")) {
+ _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
+ loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopColor);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool _attrParseStops(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ auto stop = &loader->svgParse->gradStop;
+
+ if (!strcmp(key, "offset")) {
+ stop->offset = _toOffset(value);
+ } else if (!strcmp(key, "stop-opacity")) {
+ if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopOpacity)) {
+ stop->a = _toOpacity(value);
+ }
+ } else if (!strcmp(key, "stop-color")) {
+ if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopColor)) {
+ _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
+ }
+ } else if (!strcmp(key, "style")) {
+ simpleXmlParseW3CAttribute(value, _attrParseStopsStyle, data);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+
+static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
+{
+ linear->x1 = _gradientToFloat(loader->svgParse, value, linear->isX1Percentage);
+}
+
+
+static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
+{
+ linear->y1 = _gradientToFloat(loader->svgParse, value, linear->isY1Percentage);
+}
+
+
+static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
+{
+ linear->x2 = _gradientToFloat(loader->svgParse, value, linear->isX2Percentage);
+}
+
+
+static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
+{
+ linear->y2 = _gradientToFloat(loader->svgParse, value, linear->isY2Percentage);
+}
+
+
+static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (userSpace && !linear->isX1Percentage) linear->x1 = linear->x1 / loader->svgParse->global.w;
+}
+
+
+static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (userSpace && !linear->isY1Percentage) linear->y1 = linear->y1 / loader->svgParse->global.h;
+}
+
+
+static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (userSpace && !linear->isX2Percentage) linear->x2 = linear->x2 / loader->svgParse->global.w;
+}
+
+
+static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (userSpace && !linear->isY2Percentage) linear->y2 = linear->y2 / loader->svgParse->global.h;
+}
+
+
+typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
+typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
+
+
+#define LINEAR_DEF(Name, Name1) \
+ { \
+#Name, sizeof(#Name), _handleLinear##Name1##Attr, _recalcLinear##Name1##Attr \
+ }
+
+
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ Linear_Method tagHandler;
+ Linear_Method_Recalc tagRecalc;
+} linear_tags[] = {
+ LINEAR_DEF(x1, X1),
+ LINEAR_DEF(y1, Y1),
+ LINEAR_DEF(x2, X2),
+ LINEAR_DEF(y2, Y2)
+};
+
+
+static bool _attrParseLinearGradientNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgStyleGradient* grad = loader->svgParse->styleGrad;
+ SvgLinearGradient* linear = grad->linear;
+ int sz = strlen(key);
+
+ for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
+ if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
+ linear_tags[i].tagHandler(loader, linear, value);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "id")) {
+ if (grad->id && value) free(grad->id);
+ grad->id = _copyId(value);
+ } else if (!strcmp(key, "spreadMethod")) {
+ grad->spread = _parseSpreadValue(value);
+ } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ if (grad->ref && value) free(grad->ref);
+ grad->ref = _idFromHref(value);
+ } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
+ grad->userSpace = true;
+ } else if (!strcmp(key, "gradientTransform")) {
+ grad->transform = _parseTransformationMatrix(value);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+
+static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
+{
+ auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
+ loader->svgParse->styleGrad = grad;
+
+ grad->type = SvgGradientType::Linear;
+ grad->userSpace = false;
+ grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
+ if (!grad->linear) {
+ grad->clear();
+ free(grad);
+ return nullptr;
+ }
+ /**
+ * Default value of x2 is 100% - transformed to the global percentage
+ */
+ grad->linear->x2 = 1.0f;
+ grad->linear->isX2Percentage = true;
+
+ simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader);
+
+ for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
+ linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace);
+ }
+
+ return loader->svgParse->styleGrad;
+}
+
+
+#define GRADIENT_DEF(Name, Name1) \
+ { \
+#Name, sizeof(#Name), _create##Name1 \
+ }
+
+
+/**
+ * In the case when the gradients lengths are given as numbers (not percentages)
+ * in the current user coordinate system, they are recalculated into percentages
+ * related to the canvas width and height.
+ */
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ GradientFactoryMethod tagHandler;
+} gradientTags[] = {
+ GRADIENT_DEF(linearGradient, LinearGradient),
+ GRADIENT_DEF(radialGradient, RadialGradient)
+};
+
+
+static GradientFactoryMethod _findGradientFactory(const char* name)
+{
+ int sz = strlen(name);
+
+ for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
+ if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) {
+ return gradientTags[i].tagHandler;
+ }
+ }
+ return nullptr;
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ size_t sz;
+} popArray[] = {
+ {"g", sizeof("g")},
+ {"svg", sizeof("svg")},
+ {"defs", sizeof("defs")},
+ {"mask", sizeof("mask")},
+ {"clipPath", sizeof("clipPath")}
+};
+
+
+static void _svgLoaderParerXmlClose(SvgLoaderData* loader, const char* content)
+{
+ content = _skipSpace(content, nullptr);
+
+ for (unsigned int i = 0; i < sizeof(popArray) / sizeof(popArray[0]); i++) {
+ if (!strncmp(content, popArray[i].tag, popArray[i].sz - 1)) {
+ loader->stack.pop();
+ break;
+ }
+ }
+
+ loader->level--;
+}
+
+
+static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length, bool empty)
+{
+ const char* attrs = nullptr;
+ int attrsLength = 0;
+ int sz = length;
+ char tagName[20] = "";
+ FactoryMethod method;
+ GradientFactoryMethod gradientMethod;
+ SvgNode *node = nullptr, *parent = nullptr;
+ loader->level++;
+ attrs = simpleXmlFindAttributesTag(content, length);
+
+ if (!attrs) {
+ //Parse the empty tag
+ attrs = content;
+ while ((attrs != nullptr) && *attrs != '>') attrs++;
+ }
+
+ if (attrs) {
+ //Find out the tag name starting from content till sz length
+ sz = attrs - content;
+ while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
+ if ((unsigned)sz >= sizeof(tagName)) return;
+ strncpy(tagName, content, sz);
+ tagName[sz] = '\0';
+ attrsLength = length - sz;
+ }
+
+ if ((method = _findGroupFactory(tagName))) {
+ //Group
+ if (empty) return;
+ if (!loader->doc) {
+ if (strcmp(tagName, "svg")) return; //Not a valid svg document
+ node = method(loader, nullptr, attrs, attrsLength);
+ loader->doc = node;
+ } else {
+ if (!strcmp(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
+ if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
+ else parent = loader->doc;
+ node = method(loader, parent, attrs, attrsLength);
+ }
+
+ if (!node) return;
+ if (node->type != SvgNodeType::Defs || !empty) {
+ loader->stack.push(node);
+ }
+ } else if ((method = _findGraphicsFactory(tagName))) {
+ if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
+ else parent = loader->doc;
+ node = method(loader, parent, attrs, attrsLength);
+ } else if ((gradientMethod = _findGradientFactory(tagName))) {
+ SvgStyleGradient* gradient;
+ gradient = gradientMethod(loader, attrs, attrsLength);
+ //FIXME: The current parsing structure does not distinguish end tags.
+ // There is no way to know if the currently parsed gradient is in defs.
+ // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
+ // But finally, the loader has a gradient style list regardless of defs.
+ // This is only to support this when multiple gradients are declared, even if no defs are declared.
+ // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
+ if (loader->def && loader->doc->node.doc.defs) {
+ loader->def->node.defs.gradients.push(gradient);
+ } else {
+ loader->gradients.push(gradient);
+ }
+ loader->latestGradient = gradient;
+ } else if (!strcmp(tagName, "stop")) {
+ if (!loader->latestGradient) {
+ TVGLOG("SVG", "Stop element is used outside of the Gradient element");
+ return;
+ }
+ /* default value for opacity */
+ loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255};
+ simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
+ loader->latestGradient->stops.push(loader->svgParse->gradStop);
+ } else if (!isIgnoreUnsupportedLogElements(tagName)) {
+ TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName);
+ }
+}
+
+
+static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int length)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+
+ switch (type) {
+ case SimpleXMLType::Open: {
+ _svgLoaderParserXmlOpen(loader, content, length, false);
+ break;
+ }
+ case SimpleXMLType::OpenEmpty: {
+ _svgLoaderParserXmlOpen(loader, content, length, true);
+ break;
+ }
+ case SimpleXMLType::Close: {
+ _svgLoaderParerXmlClose(loader, content);
+ break;
+ }
+ case SimpleXMLType::Data:
+ case SimpleXMLType::CData:
+ case SimpleXMLType::DoctypeChild: {
+ break;
+ }
+ case SimpleXMLType::Ignored:
+ case SimpleXMLType::Comment:
+ case SimpleXMLType::Doctype: {
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
+{
+#ifdef THORVG_LOG_ENABLED
+ auto type = simpleXmlNodeTypeToString(node->type);
+
+ if (!node->display && node->type != SvgNodeType::ClipPath) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
+ if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type);
+ if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type);
+
+ switch (node->type) {
+ case SvgNodeType::Path: {
+ if (!node->node.path.path) TVGLOG("SVG", "Inefficient elements used [Empty path][Node Type : %s]", type);
+ break;
+ }
+ case SvgNodeType::Ellipse: {
+ if (node->node.ellipse.rx == 0 && node->node.ellipse.ry == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
+ break;
+ }
+ case SvgNodeType::Polygon:
+ case SvgNodeType::Polyline: {
+ if (node->node.polygon.pointsCount < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type);
+ break;
+ }
+ case SvgNodeType::Circle: {
+ if (node->node.circle.r == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
+ break;
+ }
+ case SvgNodeType::Rect: {
+ if (node->node.rect.w == 0 && node->node.rect.h) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
+ break;
+ }
+ case SvgNodeType::Line: {
+ if (node->node.line.x1 == node->node.line.x2 && node->node.line.y1 == node->node.line.y2) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
+ break;
+ }
+ default: break;
+ }
+#endif
+}
+
+
+static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
+{
+ _styleInherit(node->style, parentStyle);
+ _inefficientNodeCheck(node);
+
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ _updateStyle(*child, node->style);
+ }
+}
+
+
+static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const char* id)
+{
+ SvgStyleGradient* result = nullptr;
+
+ auto gradList = gradients->data;
+
+ for (uint32_t i = 0; i < gradients->count; ++i) {
+ if (!strcmp((*gradList)->id, id)) {
+ result = _cloneGradient(*gradList);
+ break;
+ }
+ ++gradList;
+ }
+
+ if (result && result->ref) {
+ gradList = gradients->data;
+ for (uint32_t i = 0; i < gradients->count; ++i) {
+ if (!strcmp((*gradList)->id, result->ref)) {
+ if (result->stops.count == 0) _cloneGradStops(result->stops, (*gradList)->stops);
+ //TODO: Properly inherit other property
+ break;
+ }
+ ++gradList;
+ }
+ }
+
+ return result;
+}
+
+
+static void _updateGradient(SvgNode* node, Array<SvgStyleGradient*>* gradients)
+{
+ if (node->child.count > 0) {
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ _updateGradient(*child, gradients);
+ }
+ } else {
+ if (node->style->fill.paint.url) {
+ if (node->style->fill.paint.gradient) {
+ node->style->fill.paint.gradient->clear();
+ free(node->style->fill.paint.gradient);
+ }
+ node->style->fill.paint.gradient = _gradientDup(gradients, node->style->fill.paint.url);
+ }
+ if (node->style->stroke.paint.url) {
+ if (node->style->stroke.paint.gradient) {
+ node->style->stroke.paint.gradient->clear();
+ free(node->style->stroke.paint.gradient);
+ }
+ node->style->stroke.paint.gradient = _gradientDup(gradients, node->style->stroke.paint.url);
+ }
+ }
+}
+
+
+static void _updateComposite(SvgNode* node, SvgNode* root)
+{
+ if (node->style->clipPath.url && !node->style->clipPath.node) {
+ SvgNode* findResult = _findNodeById(root, node->style->clipPath.url);
+ if (findResult) node->style->clipPath.node = findResult;
+ }
+ if (node->style->mask.url && !node->style->mask.node) {
+ SvgNode* findResult = _findNodeById(root, node->style->mask.url);
+ if (findResult) node->style->mask.node = findResult;
+ }
+ if (node->child.count > 0) {
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ _updateComposite(*child, root);
+ }
+ }
+}
+
+
+static void _freeNodeStyle(SvgStyleProperty* style)
+{
+ if (!style) return;
+
+ //style->clipPath.node and style->mask.node has only the addresses of node. Therefore, node is released from _freeNode.
+ free(style->clipPath.url);
+ free(style->mask.url);
+
+ if (style->fill.paint.gradient) {
+ style->fill.paint.gradient->clear();
+ free(style->fill.paint.gradient);
+ }
+ if (style->stroke.paint.gradient) {
+ style->stroke.paint.gradient->clear();
+ free(style->stroke.paint.gradient);
+ }
+ free(style->fill.paint.url);
+ free(style->stroke.paint.url);
+ style->stroke.dash.array.reset();
+ free(style);
+}
+
+
+static void _freeNode(SvgNode* node)
+{
+ if (!node) return;
+
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ _freeNode(*child);
+ }
+ node->child.reset();
+
+ free(node->id);
+ free(node->transform);
+ _freeNodeStyle(node->style);
+ switch (node->type) {
+ case SvgNodeType::Path: {
+ free(node->node.path.path);
+ break;
+ }
+ case SvgNodeType::Polygon: {
+ free(node->node.polygon.points);
+ break;
+ }
+ case SvgNodeType::Polyline: {
+ free(node->node.polyline.points);
+ break;
+ }
+ case SvgNodeType::Doc: {
+ _freeNode(node->node.doc.defs);
+ break;
+ }
+ case SvgNodeType::Defs: {
+ auto gradients = node->node.defs.gradients.data;
+ for (size_t i = 0; i < node->node.defs.gradients.count; ++i) {
+ (*gradients)->clear();
+ free(*gradients);
+ ++gradients;
+ }
+ node->node.defs.gradients.reset();
+ break;
+ }
+ case SvgNodeType::Image: {
+ free(node->node.image.href);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ free(node);
+}
+
+
+static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length)
+{
+ const char* attrs = nullptr;
+ int sz = length;
+ char tagName[20] = "";
+ FactoryMethod method;
+ SvgNode *node = nullptr;
+ int attrsLength = 0;
+ loader->level++;
+ attrs = simpleXmlFindAttributesTag(content, length);
+
+ if (!attrs) {
+ //Parse the empty tag
+ attrs = content;
+ while ((attrs != nullptr) && *attrs != '>') attrs++;
+ }
+
+ if (attrs) {
+ sz = attrs - content;
+ while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
+ if ((unsigned)sz >= sizeof(tagName)) return false;
+ strncpy(tagName, content, sz);
+ tagName[sz] = '\0';
+ attrsLength = length - sz;
+ }
+
+ if ((method = _findGroupFactory(tagName))) {
+ if (!loader->doc) {
+ if (strcmp(tagName, "svg")) return true; //Not a valid svg document
+ node = method(loader, nullptr, attrs, attrsLength);
+ loader->doc = node;
+ loader->stack.push(node);
+ return false;
+ }
+ }
+ return true;
+}
+
+
+static bool _svgLoaderParserForValidCheck(void* data, SimpleXMLType type, const char* content, unsigned int length)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ bool res = true;;
+
+ switch (type) {
+ case SimpleXMLType::Open:
+ case SimpleXMLType::OpenEmpty: {
+ //If 'res' is false, it means <svg> tag is found.
+ res = _svgLoaderParserForValidCheckXmlOpen(loader, content, length);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ return res;
+}
+
+
+void SvgLoader::clear()
+{
+ if (copy) free((char*)content);
+ size = 0;
+ content = nullptr;
+ copy = false;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+SvgLoader::SvgLoader()
+{
+}
+
+
+SvgLoader::~SvgLoader()
+{
+ close();
+}
+
+
+void SvgLoader::run(unsigned tid)
+{
+ if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return;
+
+ if (loaderData.doc) {
+ _updateStyle(loaderData.doc, nullptr);
+ auto defs = loaderData.doc->node.doc.defs;
+
+ _updateComposite(loaderData.doc, loaderData.doc);
+ if (defs) _updateComposite(loaderData.doc, defs);
+
+ if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc);
+
+ if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
+ if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
+ }
+ root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath);
+}
+
+
+bool SvgLoader::header()
+{
+ //For valid check, only <svg> tag is parsed first.
+ //If the <svg> tag is found, the loaded file is valid and stores viewbox information.
+ //After that, the remaining content data is parsed in order with async.
+ loaderData.svgParse = (SvgParser*)malloc(sizeof(SvgParser));
+ if (!loaderData.svgParse) return false;
+
+ loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault;
+
+ simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
+
+ if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
+ //Return the brief resource info such as viewbox:
+ vx = loaderData.doc->node.doc.vx;
+ vy = loaderData.doc->node.doc.vy;
+ w = vw = loaderData.doc->node.doc.vw;
+ h = vh = loaderData.doc->node.doc.vh;
+
+ //Override size
+ if (loaderData.doc->node.doc.w > 0) {
+ w = loaderData.doc->node.doc.w;
+ if (vw < FLT_EPSILON) vw = w;
+ }
+ if (loaderData.doc->node.doc.h > 0) {
+ h = loaderData.doc->node.doc.h;
+ if (vh < FLT_EPSILON) vh = h;
+ }
+
+ preserveAspect = loaderData.doc->node.doc.preserveAspect;
+ } else {
+ TVGLOG("SVG", "No SVG File. There is no <svg/>");
+ return false;
+ }
+
+ return true;
+}
+
+
+bool SvgLoader::open(const char* data, uint32_t size, bool copy)
+{
+ clear();
+
+ if (copy) {
+ content = (char*)malloc(size);
+ if (!content) return false;
+ memcpy((char*)content, data, size);
+ } else content = data;
+
+ this->size = size;
+ this->copy = copy;
+
+ return header();
+}
+
+
+bool SvgLoader::open(const string& path)
+{
+ clear();
+
+ ifstream f;
+ f.open(path);
+
+ if (!f.is_open()) return false;
+
+ svgPath = path;
+ getline(f, filePath, '\0');
+ f.close();
+
+ if (filePath.empty()) return false;
+
+ content = filePath.c_str();
+ size = filePath.size();
+
+ return header();
+}
+
+
+bool SvgLoader::resize(Paint* paint, float w, float h)
+{
+ if (!paint) return false;
+
+ auto sx = w / this->w;
+ auto sy = h / this->h;
+
+ if (preserveAspect) {
+ //Scale
+ auto scale = sx < sy ? sx : sy;
+ paint->scale(scale);
+ //Align
+ auto tx = 0.0f;
+ auto ty = 0.0f;
+ auto tw = this->w * scale;
+ auto th = this->h * scale;
+ if (tw > th) ty -= (h - th) * 0.5f;
+ else tx -= (w - tw) * 0.5f;
+ paint->translate(-tx, -ty);
+ } else {
+ //Align
+ auto tx = 0.0f;
+ auto ty = 0.0f;
+ auto tw = this->w * sx;
+ auto th = this->h * sy;
+ if (tw > th) ty -= (h - th) * 0.5f;
+ else tx -= (w - tw) * 0.5f;
+
+ Matrix m = {sx, 0, -tx, 0, sy, -ty, 0, 0, 1};
+ paint->transform(m);
+ }
+ return true;
+}
+
+
+bool SvgLoader::read()
+{
+ if (!content || size == 0) return false;
+
+ TaskScheduler::request(this);
+
+ return true;
+}
+
+
+bool SvgLoader::close()
+{
+ this->done();
+
+ if (loaderData.svgParse) {
+ free(loaderData.svgParse);
+ loaderData.svgParse = nullptr;
+ }
+ auto gradients = loaderData.gradients.data;
+ for (size_t i = 0; i < loaderData.gradients.count; ++i) {
+ (*gradients)->clear();
+ free(*gradients);
+ ++gradients;
+ }
+ loaderData.gradients.reset();
+
+ _freeNode(loaderData.doc);
+ loaderData.doc = nullptr;
+ loaderData.stack.reset();
+
+ clear();
+
+ return true;
+}
+
+
+unique_ptr<Paint> SvgLoader::paint()
+{
+ this->done();
+ if (root) return move(root);
+ else return nullptr;
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
new file mode 100644
index 0000000000..468f05801d
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SVG_LOADER_H_
+#define _TVG_SVG_LOADER_H_
+
+#include "tvgTaskScheduler.h"
+#include "tvgSvgLoaderCommon.h"
+
+class SvgLoader : public LoadModule, public Task
+{
+public:
+ string filePath;
+ string svgPath = "";
+ const char* content = nullptr;
+ uint32_t size = 0;
+
+ SvgLoaderData loaderData;
+ unique_ptr<Scene> root;
+
+ bool copy = false;
+
+ SvgLoader();
+ ~SvgLoader();
+
+ using LoadModule::open;
+ bool open(const string& path) override;
+ bool open(const char* data, uint32_t size, bool copy) override;
+ bool resize(Paint* paint, float w, float h) override;
+ bool read() override;
+ bool close() override;
+ unique_ptr<Paint> paint() override;
+
+private:
+ bool header();
+ void clear();
+ void run(unsigned tid) override;
+};
+
+
+#endif //_TVG_SVG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
new file mode 100644
index 0000000000..cceef915f0
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SVG_LOADER_COMMON_H_
+#define _TVG_SVG_LOADER_COMMON_H_
+
+#include "tvgCommon.h"
+#include "tvgArray.h"
+
+struct SvgNode;
+struct SvgStyleGradient;
+
+//NOTE: Please update simpleXmlNodeTypeToString() as well.
+enum class SvgNodeType
+{
+ Doc,
+ G,
+ Defs,
+ Animation,
+ Arc,
+ Circle,
+ Ellipse,
+ Image,
+ Line,
+ Path,
+ Polygon,
+ Polyline,
+ Rect,
+ Text,
+ TextArea,
+ Tspan,
+ Use,
+ Video,
+ ClipPath,
+ Mask,
+ Unknown
+};
+
+/*
+// TODO - remove?
+enum class SvgLengthType
+{
+ Percent,
+ Px,
+ Pc,
+ Pt,
+ Mm,
+ Cm,
+ In,
+};
+*/
+
+enum class SvgFillFlags
+{
+ Paint = 0x01,
+ Opacity = 0x02,
+ Gradient = 0x04,
+ FillRule = 0x08,
+ ClipPath = 0x16
+};
+
+enum class SvgStrokeFlags
+{
+ Paint = 0x1,
+ Opacity = 0x2,
+ Gradient = 0x4,
+ Scale = 0x8,
+ Width = 0x10,
+ Cap = 0x20,
+ Join = 0x40,
+ Dash = 0x80,
+};
+
+enum class SvgGradientType
+{
+ Linear,
+ Radial
+};
+
+enum class SvgStyleFlags
+{
+ Color = 0x01,
+ Fill = 0x02,
+ FillRule = 0x04,
+ FillOpacity = 0x08,
+ Opacity = 0x010,
+ Stroke = 0x20,
+ StrokeWidth = 0x40,
+ StrokeLineJoin = 0x80,
+ StrokeLineCap = 0x100,
+ StrokeOpacity = 0x200,
+ StrokeDashArray = 0x400,
+ Transform = 0x800,
+ ClipPath = 0x1000,
+ Mask = 0x2000,
+ Display = 0x4000
+};
+
+enum class SvgStopStyleFlags
+{
+ StopDefault = 0x0,
+ StopOpacity = 0x01,
+ StopColor = 0x02
+};
+
+enum class SvgFillRule
+{
+ Winding = 0,
+ OddEven = 1
+};
+
+//Length type to recalculate %, pt, pc, mm, cm etc
+enum class SvgParserLengthType
+{
+ Vertical,
+ Horizontal,
+ //In case of, for example, radius of radial gradient
+ Other
+};
+
+struct SvgDocNode
+{
+ float w;
+ float h;
+ float vx;
+ float vy;
+ float vw;
+ float vh;
+ SvgNode* defs;
+ bool preserveAspect;
+};
+
+struct SvgGNode
+{
+};
+
+struct SvgDefsNode
+{
+ Array<SvgStyleGradient*> gradients;
+};
+
+struct SvgUseNode
+{
+ float x, y, w, h;
+};
+
+
+struct SvgEllipseNode
+{
+ float cx;
+ float cy;
+ float rx;
+ float ry;
+};
+
+struct SvgCircleNode
+{
+ float cx;
+ float cy;
+ float r;
+};
+
+struct SvgRectNode
+{
+ float x;
+ float y;
+ float w;
+ float h;
+ float rx;
+ float ry;
+ bool hasRx;
+ bool hasRy;
+};
+
+struct SvgLineNode
+{
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+};
+
+struct SvgImageNode
+{
+ float x, y, w, h;
+ char* href;
+};
+
+struct SvgPathNode
+{
+ char* path;
+};
+
+struct SvgPolygonNode
+{
+ int pointsCount;
+ float* points;
+};
+
+struct SvgCompositeNode
+{
+ bool userSpace;
+};
+
+struct SvgLinearGradient
+{
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+ bool isX1Percentage;
+ bool isY1Percentage;
+ bool isX2Percentage;
+ bool isY2Percentage;
+};
+
+struct SvgRadialGradient
+{
+ float cx;
+ float cy;
+ float fx;
+ float fy;
+ float r;
+ bool isCxPercentage;
+ bool isCyPercentage;
+ bool isFxPercentage;
+ bool isFyPercentage;
+ bool isRPercentage;
+};
+
+struct SvgComposite
+{
+ char *url;
+ SvgNode* node;
+ bool applying; //flag for checking circular dependency.
+};
+
+struct SvgColor
+{
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+struct SvgPaint
+{
+ SvgStyleGradient* gradient;
+ char *url;
+ SvgColor color;
+ bool none;
+ bool curColor;
+};
+
+struct SvgDash
+{
+ Array<float> array;
+};
+
+struct SvgStyleGradient
+{
+ SvgGradientType type;
+ char* id;
+ char* ref;
+ FillSpread spread;
+ SvgRadialGradient* radial;
+ SvgLinearGradient* linear;
+ Matrix* transform;
+ Array<Fill::ColorStop> stops;
+ bool userSpace;
+
+ void clear()
+ {
+ stops.reset();
+ free(transform);
+ free(radial);
+ free(linear);
+ free(ref);
+ free(id);
+ }
+};
+
+struct SvgStyleFill
+{
+ SvgFillFlags flags;
+ SvgPaint paint;
+ int opacity;
+ FillRule fillRule;
+};
+
+struct SvgStyleStroke
+{
+ SvgStrokeFlags flags;
+ SvgPaint paint;
+ int opacity;
+ float scale;
+ float width;
+ float centered;
+ StrokeCap cap;
+ StrokeJoin join;
+ SvgDash dash;
+ int dashCount;
+};
+
+struct SvgStyleProperty
+{
+ SvgStyleFill fill;
+ SvgStyleStroke stroke;
+ SvgComposite clipPath;
+ SvgComposite mask;
+ int opacity;
+ SvgColor color;
+ bool curColorSet;
+ SvgStyleFlags flags;
+};
+
+struct SvgNode
+{
+ SvgNodeType type;
+ SvgNode* parent;
+ Array<SvgNode*> child;
+ char *id;
+ SvgStyleProperty *style;
+ Matrix* transform;
+ union {
+ SvgGNode g;
+ SvgDocNode doc;
+ SvgDefsNode defs;
+ SvgUseNode use;
+ SvgCircleNode circle;
+ SvgEllipseNode ellipse;
+ SvgPolygonNode polygon;
+ SvgPolygonNode polyline;
+ SvgRectNode rect;
+ SvgPathNode path;
+ SvgLineNode line;
+ SvgImageNode image;
+ SvgCompositeNode comp;
+ } node;
+ bool display;
+ ~SvgNode();
+};
+
+struct SvgParser
+{
+ SvgNode* node;
+ SvgStyleGradient* styleGrad;
+ Fill::ColorStop gradStop;
+ SvgStopStyleFlags flags;
+ struct
+ {
+ int x, y;
+ uint32_t w, h;
+ } global;
+ struct
+ {
+ bool parsedFx;
+ bool parsedFy;
+ } gradient;
+};
+
+struct SvgNodeIdPair
+{
+ SvgNode* node;
+ char *id;
+};
+
+struct SvgLoaderData
+{
+ Array<SvgNode *> stack = {nullptr, 0, 0};
+ SvgNode* doc = nullptr;
+ SvgNode* def = nullptr;
+ Array<SvgStyleGradient*> gradients;
+ SvgStyleGradient* latestGradient = nullptr; //For stops
+ SvgParser* svgParse = nullptr;
+ Array<SvgNodeIdPair> cloneNodes;
+ int level = 0;
+ bool result = false;
+};
+
+/*
+ * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017
+ *
+ * src should be one of the following form :
+ *
+ * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
+ * [whitespace] [sign] {INF | INFINITY}
+ * [whitespace] [sign] NAN [sequence]
+ *
+ * No hexadecimal form supported
+ * no sequence supported after NAN
+ */
+float customStrtof(const char *nptr, char **endptr);
+
+#endif
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
new file mode 100644
index 0000000000..32685ee620
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright notice for the EFL:
+
+ * Copyright (C) EFL developers (see AUTHORS)
+
+ * All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++.
+
+#include <cstring>
+#include <math.h>
+#include <clocale>
+#include <ctype.h>
+#include "tvgSvgLoaderCommon.h"
+#include "tvgSvgPath.h"
+#include "tvgSvgUtil.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static char* _skipComma(const char* content)
+{
+ while (*content && isspace(*content)) {
+ content++;
+ }
+ if (*content == ',') return (char*)content + 1;
+ return (char*)content;
+}
+
+
+static bool _parseNumber(char** content, float* number)
+{
+ char* end = NULL;
+ *number = svgUtilStrtof(*content, &end);
+ //If the start of string is not number
+ if ((*content) == end) return false;
+ //Skip comma if any
+ *content = _skipComma(end);
+ return true;
+}
+
+
+static bool _parseFlag(char** content, int* number)
+{
+ char* end = NULL;
+ if (*(*content) != '0' && *(*content) != '1') return false;
+ *number = *(*content) - '0';
+ *content += 1;
+ end = *content;
+ *content = _skipComma(end);
+
+ return true;
+}
+
+
+void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, Point* curCtl, float x, float y, float rx, float ry, float angle, bool largeArc, bool sweep)
+{
+ float cxp, cyp, cx, cy;
+ float sx, sy;
+ float cosPhi, sinPhi;
+ float dx2, dy2;
+ float x1p, y1p;
+ float x1p2, y1p2;
+ float rx2, ry2;
+ float lambda;
+ float c;
+ float at;
+ float theta1, deltaTheta;
+ float nat;
+ float delta, bcp;
+ float cosPhiRx, cosPhiRy;
+ float sinPhiRx, sinPhiRy;
+ float cosTheta1, sinTheta1;
+ int segments;
+
+ //Some helpful stuff is available here:
+ //http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
+ sx = cur->x;
+ sy = cur->y;
+
+ //If start and end points are identical, then no arc is drawn
+ if ((fabsf(x - sx) < (1.0f / 256.0f)) && (fabsf(y - sy) < (1.0f / 256.0f))) return;
+
+ //Correction of out-of-range radii, see F6.6.1 (step 2)
+ rx = fabsf(rx);
+ ry = fabsf(ry);
+
+ angle = angle * M_PI / 180.0f;
+ cosPhi = cosf(angle);
+ sinPhi = sinf(angle);
+ dx2 = (sx - x) / 2.0f;
+ dy2 = (sy - y) / 2.0f;
+ x1p = cosPhi * dx2 + sinPhi * dy2;
+ y1p = cosPhi * dy2 - sinPhi * dx2;
+ x1p2 = x1p * x1p;
+ y1p2 = y1p * y1p;
+ rx2 = rx * rx;
+ ry2 = ry * ry;
+ lambda = (x1p2 / rx2) + (y1p2 / ry2);
+
+ //Correction of out-of-range radii, see F6.6.2 (step 4)
+ if (lambda > 1.0f) {
+ //See F6.6.3
+ float lambdaRoot = sqrtf(lambda);
+
+ rx *= lambdaRoot;
+ ry *= lambdaRoot;
+ //Update rx2 and ry2
+ rx2 = rx * rx;
+ ry2 = ry * ry;
+ }
+
+ c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2);
+
+ //Check if there is no possible solution
+ //(i.e. we can't do a square root of a negative value)
+ if (c < 0.0f) {
+ //Scale uniformly until we have a single solution
+ //(see F6.2) i.e. when c == 0.0
+ float scale = sqrtf(1.0f - c / (rx2 * ry2));
+ rx *= scale;
+ ry *= scale;
+ //Update rx2 and ry2
+ rx2 = rx * rx;
+ ry2 = ry * ry;
+
+ //Step 2 (F6.5.2) - simplified since c == 0.0
+ cxp = 0.0f;
+ cyp = 0.0f;
+ //Step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0
+ cx = 0.0f;
+ cy = 0.0f;
+ } else {
+ //Complete c calculation
+ c = sqrtf(c / ((rx2 * y1p2) + (ry2 * x1p2)));
+ //Inverse sign if Fa == Fs
+ if (largeArc == sweep) c = -c;
+
+ //Step 2 (F6.5.2)
+ cxp = c * (rx * y1p / ry);
+ cyp = c * (-ry * x1p / rx);
+
+ //Step 3 (F6.5.3 first part)
+ cx = cosPhi * cxp - sinPhi * cyp;
+ cy = sinPhi * cxp + cosPhi * cyp;
+ }
+
+ //Step 3 (F6.5.3 second part) we now have the center point of the ellipse
+ cx += (sx + x) / 2.0f;
+ cy += (sy + y) / 2.0f;
+
+ //Sstep 4 (F6.5.4)
+ //We dont' use arccos (as per w3c doc), see
+ //http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
+ //Note: atan2 (0.0, 1.0) == 0.0
+ at = atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
+ theta1 = (at < 0.0f) ? 2.0f * M_PI + at : at;
+
+ nat = atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
+ deltaTheta = (nat < at) ? 2.0f * M_PI - at + nat : nat - at;
+
+ if (sweep) {
+ //Ensure delta theta < 0 or else add 360 degrees
+ if (deltaTheta < 0.0f) deltaTheta += (float)(2.0f * M_PI);
+ } else {
+ //Ensure delta theta > 0 or else substract 360 degrees
+ if (deltaTheta > 0.0f) deltaTheta -= (float)(2.0f * M_PI);
+ }
+
+ //Add several cubic bezier to approximate the arc
+ //(smaller than 90 degrees)
+ //We add one extra segment because we want something
+ //Smaller than 90deg (i.e. not 90 itself)
+ segments = static_cast<int>(fabsf(deltaTheta / float(M_PI_2)) + 1.0f);
+ delta = deltaTheta / segments;
+
+ //http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
+ bcp = 4.0f / 3.0f * (1.0f - cosf(delta / 2.0f)) / sinf(delta / 2.0f);
+
+ cosPhiRx = cosPhi * rx;
+ cosPhiRy = cosPhi * ry;
+ sinPhiRx = sinPhi * rx;
+ sinPhiRy = sinPhi * ry;
+
+ cosTheta1 = cosf(theta1);
+ sinTheta1 = sinf(theta1);
+
+ for (int i = 0; i < segments; ++i) {
+ //End angle (for this segment) = current + delta
+ float c1x, c1y, ex, ey, c2x, c2y;
+ float theta2 = theta1 + delta;
+ float cosTheta2 = cosf(theta2);
+ float sinTheta2 = sinf(theta2);
+ Point p[3];
+
+ //First control point (based on start point sx,sy)
+ c1x = sx - bcp * (cosPhiRx * sinTheta1 + sinPhiRy * cosTheta1);
+ c1y = sy + bcp * (cosPhiRy * cosTheta1 - sinPhiRx * sinTheta1);
+
+ //End point (for this segment)
+ ex = cx + (cosPhiRx * cosTheta2 - sinPhiRy * sinTheta2);
+ ey = cy + (sinPhiRx * cosTheta2 + cosPhiRy * sinTheta2);
+
+ //Second control point (based on end point ex,ey)
+ c2x = ex + bcp * (cosPhiRx * sinTheta2 + sinPhiRy * cosTheta2);
+ c2y = ey + bcp * (sinPhiRx * sinTheta2 - cosPhiRy * cosTheta2);
+ cmds->push(PathCommand::CubicTo);
+ p[0] = {c1x, c1y};
+ p[1] = {c2x, c2y};
+ p[2] = {ex, ey};
+ pts->push(p[0]);
+ pts->push(p[1]);
+ pts->push(p[2]);
+ *curCtl = p[1];
+ *cur = p[2];
+
+ //Next start point is the current end point (same for angle)
+ sx = ex;
+ sy = ey;
+ theta1 = theta2;
+ //Avoid recomputations
+ cosTheta1 = cosTheta2;
+ sinTheta1 = sinTheta2;
+ }
+}
+
+static int _numberCount(char cmd)
+{
+ int count = 0;
+ switch (cmd) {
+ case 'M':
+ case 'm':
+ case 'L':
+ case 'l':
+ case 'T':
+ case 't': {
+ count = 2;
+ break;
+ }
+ case 'C':
+ case 'c':
+ case 'E':
+ case 'e': {
+ count = 6;
+ break;
+ }
+ case 'H':
+ case 'h':
+ case 'V':
+ case 'v': {
+ count = 1;
+ break;
+ }
+ case 'S':
+ case 's':
+ case 'Q':
+ case 'q': {
+ count = 4;
+ break;
+ }
+ case 'A':
+ case 'a': {
+ count = 7;
+ break;
+ }
+ default:
+ break;
+ }
+ return count;
+}
+
+
+static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl, Point* startPoint, bool *isQuadratic)
+{
+ switch (cmd) {
+ case 'm':
+ case 'l':
+ case 'c':
+ case 's':
+ case 'q':
+ case 't': {
+ for (int i = 0; i < count - 1; i += 2) {
+ arr[i] = arr[i] + cur->x;
+ arr[i + 1] = arr[i + 1] + cur->y;
+ }
+ break;
+ }
+ case 'h': {
+ arr[0] = arr[0] + cur->x;
+ break;
+ }
+ case 'v': {
+ arr[0] = arr[0] + cur->y;
+ break;
+ }
+ case 'a': {
+ arr[5] = arr[5] + cur->x;
+ arr[6] = arr[6] + cur->y;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ switch (cmd) {
+ case 'm':
+ case 'M': {
+ Point p = {arr[0], arr[1]};
+ cmds->push(PathCommand::MoveTo);
+ pts->push(p);
+ *cur = {arr[0], arr[1]};
+ *startPoint = {arr[0], arr[1]};
+ break;
+ }
+ case 'l':
+ case 'L': {
+ Point p = {arr[0], arr[1]};
+ cmds->push(PathCommand::LineTo);
+ pts->push(p);
+ *cur = {arr[0], arr[1]};
+ break;
+ }
+ case 'c':
+ case 'C': {
+ Point p[3];
+ cmds->push(PathCommand::CubicTo);
+ p[0] = {arr[0], arr[1]};
+ p[1] = {arr[2], arr[3]};
+ p[2] = {arr[4], arr[5]};
+ pts->push(p[0]);
+ pts->push(p[1]);
+ pts->push(p[2]);
+ *curCtl = p[1];
+ *cur = p[2];
+ *isQuadratic = false;
+ break;
+ }
+ case 's':
+ case 'S': {
+ Point p[3], ctrl;
+ if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
+ !(*isQuadratic)) {
+ ctrl.x = 2 * cur->x - curCtl->x;
+ ctrl.y = 2 * cur->y - curCtl->y;
+ } else {
+ ctrl = *cur;
+ }
+ cmds->push(PathCommand::CubicTo);
+ p[0] = ctrl;
+ p[1] = {arr[0], arr[1]};
+ p[2] = {arr[2], arr[3]};
+ pts->push(p[0]);
+ pts->push(p[1]);
+ pts->push(p[2]);
+ *curCtl = p[1];
+ *cur = p[2];
+ *isQuadratic = false;
+ break;
+ }
+ case 'q':
+ case 'Q': {
+ Point p[3];
+ float ctrl_x0 = (cur->x + 2 * arr[0]) * (1.0 / 3.0);
+ float ctrl_y0 = (cur->y + 2 * arr[1]) * (1.0 / 3.0);
+ float ctrl_x1 = (arr[2] + 2 * arr[0]) * (1.0 / 3.0);
+ float ctrl_y1 = (arr[3] + 2 * arr[1]) * (1.0 / 3.0);
+ cmds->push(PathCommand::CubicTo);
+ p[0] = {ctrl_x0, ctrl_y0};
+ p[1] = {ctrl_x1, ctrl_y1};
+ p[2] = {arr[2], arr[3]};
+ pts->push(p[0]);
+ pts->push(p[1]);
+ pts->push(p[2]);
+ *curCtl = {arr[0], arr[1]};
+ *cur = p[2];
+ *isQuadratic = true;
+ break;
+ }
+ case 't':
+ case 'T': {
+ Point p[3], ctrl;
+ if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
+ *isQuadratic) {
+ ctrl.x = 2 * cur->x - curCtl->x;
+ ctrl.y = 2 * cur->y - curCtl->y;
+ } else {
+ ctrl = *cur;
+ }
+ float ctrl_x0 = (cur->x + 2 * ctrl.x) * (1.0 / 3.0);
+ float ctrl_y0 = (cur->y + 2 * ctrl.y) * (1.0 / 3.0);
+ float ctrl_x1 = (arr[0] + 2 * ctrl.x) * (1.0 / 3.0);
+ float ctrl_y1 = (arr[1] + 2 * ctrl.y) * (1.0 / 3.0);
+ cmds->push(PathCommand::CubicTo);
+ p[0] = {ctrl_x0, ctrl_y0};
+ p[1] = {ctrl_x1, ctrl_y1};
+ p[2] = {arr[0], arr[1]};
+ pts->push(p[0]);
+ pts->push(p[1]);
+ pts->push(p[2]);
+ *curCtl = {ctrl.x, ctrl.y};
+ *cur = p[2];
+ *isQuadratic = true;
+ break;
+ }
+ case 'h':
+ case 'H': {
+ Point p = {arr[0], cur->y};
+ cmds->push(PathCommand::LineTo);
+ pts->push(p);
+ cur->x = arr[0];
+ break;
+ }
+ case 'v':
+ case 'V': {
+ Point p = {cur->x, arr[0]};
+ cmds->push(PathCommand::LineTo);
+ pts->push(p);
+ cur->y = arr[0];
+ break;
+ }
+ case 'z':
+ case 'Z': {
+ cmds->push(PathCommand::Close);
+ *cur = *startPoint;
+ break;
+ }
+ case 'a':
+ case 'A': {
+ _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], arr[0], arr[1], arr[2], arr[3], arr[4]);
+ *cur = *curCtl = {arr[5], arr[6]};
+ *isQuadratic = false;
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+static char* _nextCommand(char* path, char* cmd, float* arr, int* count)
+{
+ int large, sweep;
+
+ path = _skipComma(path);
+ if (isalpha(*path)) {
+ *cmd = *path;
+ path++;
+ *count = _numberCount(*cmd);
+ } else {
+ if (*cmd == 'm') *cmd = 'l';
+ else if (*cmd == 'M') *cmd = 'L';
+ }
+ if (*count == 7) {
+ //Special case for arc command
+ if (_parseNumber(&path, &arr[0])) {
+ if (_parseNumber(&path, &arr[1])) {
+ if (_parseNumber(&path, &arr[2])) {
+ if (_parseFlag(&path, &large)) {
+ if (_parseFlag(&path, &sweep)) {
+ if (_parseNumber(&path, &arr[5])) {
+ if (_parseNumber(&path, &arr[6])) {
+ arr[3] = (float)large;
+ arr[4] = (float)sweep;
+ return path;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ *count = 0;
+ return NULL;
+ }
+ for (int i = 0; i < *count; i++) {
+ if (!_parseNumber(&path, &arr[i])) {
+ *count = 0;
+ return NULL;
+ }
+ path = _skipComma(path);
+ }
+ return path;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+
+bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point>& pts)
+{
+ float numberArray[7];
+ int numberCount = 0;
+ Point cur = { 0, 0 };
+ Point curCtl = { 0, 0 };
+ Point startPoint = { 0, 0 };
+ char cmd = 0;
+ bool isQuadratic = false;
+ char* path = (char*)svgPath;
+ char* curLocale;
+
+ curLocale = setlocale(LC_NUMERIC, NULL);
+ if (curLocale) curLocale = strdup(curLocale);
+ setlocale(LC_NUMERIC, "POSIX");
+
+ while ((path[0] != '\0')) {
+ path = _nextCommand(path, &cmd, numberArray, &numberCount);
+ if (!path) break;
+ if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic)) break;
+ }
+
+ setlocale(LC_NUMERIC, curLocale);
+ if (curLocale) free(curLocale);
+
+ return true;
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
new file mode 100644
index 0000000000..7f26c4a213
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _TVG_SVG_PATH_H_
+#define _TVG_SVG_PATH_H_
+
+#include <tvgCommon.h>
+
+bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point>& pts);
+
+#endif //_TVG_SVG_PATH_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
new file mode 100644
index 0000000000..ae17634f31
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -0,0 +1,653 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright notice for the EFL:
+
+ * Copyright (C) EFL developers (see AUTHORS)
+
+ * All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <cstring>
+#include <string>
+#include "tvgMath.h"
+#include "tvgSvgLoaderCommon.h"
+#include "tvgSvgSceneBuilder.h"
+#include "tvgSvgPath.h"
+#include "tvgSvgUtil.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct Box
+{
+ float x, y, w, h;
+};
+
+
+static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
+static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask);
+
+
+static inline bool _isGroupType(SvgNodeType type)
+{
+ if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath) return true;
+ return false;
+}
+
+
+//According to: https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits (the last paragraph)
+//a stroke width should be ignored for bounding box calculations
+static Box _boundingBox(const Shape* shape)
+{
+ float x, y, w, h;
+ shape->bounds(&x, &y, &w, &h, false);
+
+ if (auto strokeW = shape->strokeWidth()) {
+ x += 0.5f * strokeW;
+ y += 0.5f * strokeW;
+ w -= strokeW;
+ h -= strokeW;
+ }
+
+ return {x, y, w, h};
+}
+
+
+static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf)
+{
+ gradTransf->e13 = gradTransf->e13 * mBBox->e11 + mBBox->e13;
+ gradTransf->e12 *= mBBox->e11;
+ gradTransf->e11 *= mBBox->e11;
+
+ gradTransf->e23 = gradTransf->e23 * mBBox->e22 + mBBox->e23;
+ gradTransf->e22 *= mBBox->e22;
+ gradTransf->e21 *= mBBox->e22;
+}
+
+
+static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, const Shape* vg, const Box& vBox, int opacity)
+{
+ Fill::ColorStop* stops;
+ int stopCount = 0;
+ auto fillGrad = LinearGradient::gen();
+
+ bool isTransform = (g->transform ? true : false);
+ Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ if (isTransform) finalTransform = *g->transform;
+
+ if (g->userSpace) {
+ g->linear->x1 = g->linear->x1 * vBox.w;
+ g->linear->y1 = g->linear->y1 * vBox.h;
+ g->linear->x2 = g->linear->x2 * vBox.w;
+ g->linear->y2 = g->linear->y2 * vBox.h;
+ } else {
+ Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1};
+ if (isTransform) _transformMultiply(&m, &finalTransform);
+ else {
+ finalTransform = m;
+ isTransform = true;
+ }
+ }
+
+ if (isTransform) fillGrad->transform(finalTransform);
+
+ fillGrad->linear(g->linear->x1, g->linear->y1, g->linear->x2, g->linear->y2);
+ fillGrad->spread(g->spread);
+
+ //Update the stops
+ stopCount = g->stops.count;
+ if (stopCount > 0) {
+ stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop));
+ if (!stops) return fillGrad;
+ auto prevOffset = 0.0f;
+ for (uint32_t i = 0; i < g->stops.count; ++i) {
+ auto colorStop = &g->stops.data[i];
+ //Use premultiplied color
+ stops[i].r = colorStop->r;
+ stops[i].g = colorStop->g;
+ stops[i].b = colorStop->b;
+ stops[i].a = static_cast<uint8_t>((colorStop->a * opacity) / 255);
+ stops[i].offset = colorStop->offset;
+ //check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes
+ if (colorStop->offset < prevOffset) stops[i].offset = prevOffset;
+ else if (colorStop->offset > 1) stops[i].offset = 1;
+ prevOffset = stops[i].offset;
+ }
+ fillGrad->colorStops(stops, stopCount);
+ free(stops);
+ }
+ return fillGrad;
+}
+
+
+static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient* g, const Shape* vg, const Box& vBox, int opacity)
+{
+ Fill::ColorStop *stops;
+ int stopCount = 0;
+ auto fillGrad = RadialGradient::gen();
+
+ bool isTransform = (g->transform ? true : false);
+ Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ if (isTransform) finalTransform = *g->transform;
+
+ if (g->userSpace) {
+ //The radius scalling is done according to the Units section:
+ //https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
+ g->radial->cx = g->radial->cx * vBox.w;
+ g->radial->cy = g->radial->cy * vBox.h;
+ g->radial->r = g->radial->r * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
+ g->radial->fx = g->radial->fx * vBox.w;
+ g->radial->fy = g->radial->fy * vBox.h;
+ } else {
+ Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1};
+ if (isTransform) _transformMultiply(&m, &finalTransform);
+ else {
+ finalTransform = m;
+ isTransform = true;
+ }
+ }
+
+ if (isTransform) fillGrad->transform(finalTransform);
+
+ //TODO: Tvg is not support to focal
+ //if (g->radial->fx != 0 && g->radial->fy != 0) {
+ // fillGrad->radial(g->radial->fx, g->radial->fy, g->radial->r);
+ //}
+ fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r);
+ fillGrad->spread(g->spread);
+
+ //Update the stops
+ stopCount = g->stops.count;
+ if (stopCount > 0) {
+ stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop));
+ if (!stops) return fillGrad;
+ auto prevOffset = 0.0f;
+ for (uint32_t i = 0; i < g->stops.count; ++i) {
+ auto colorStop = &g->stops.data[i];
+ //Use premultiplied color
+ stops[i].r = colorStop->r;
+ stops[i].g = colorStop->g;
+ stops[i].b = colorStop->b;
+ stops[i].a = static_cast<uint8_t>((colorStop->a * opacity) / 255);
+ stops[i].offset = colorStop->offset;
+ //check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes
+ if (colorStop->offset < prevOffset) stops[i].offset = prevOffset;
+ else if (colorStop->offset > 1) stops[i].offset = 1;
+ prevOffset = stops[i].offset;
+ }
+ fillGrad->colorStops(stops, stopCount);
+ free(stops);
+ }
+ return fillGrad;
+}
+
+
+static bool _appendChildShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+{
+ auto valid = false;
+
+ if (_appendShape(node, shape, vBox, svgPath)) valid = true;
+
+ if (node->child.count > 0) {
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ if (_appendChildShape(*child, shape, vBox, svgPath)) valid = true;
+ }
+ }
+
+ return valid;
+}
+
+
+static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox, const string& svgPath)
+{
+ /* ClipPath */
+ /* Do not drop in Circular Dependency for ClipPath.
+ Composition can be applied recursively if its children nodes have composition target to this one. */
+ if (node->style->clipPath.applying) {
+ TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?");
+ } else {
+ auto compNode = node->style->clipPath.node;
+ if (compNode && compNode->child.count > 0) {
+ node->style->clipPath.applying = true;
+
+ auto comp = Shape::gen();
+ comp->fill(255, 255, 255, 255);
+ if (node->transform) comp->transform(*node->transform);
+
+ auto child = compNode->child.data;
+ auto valid = false; //Composite only when valid shapes are existed
+
+ for (uint32_t i = 0; i < compNode->child.count; ++i, ++child) {
+ if (_appendChildShape(*child, comp.get(), vBox, svgPath)) valid = true;
+ }
+
+ if (valid) paint->composite(move(comp), CompositeMethod::ClipPath);
+
+ node->style->clipPath.applying = false;
+ }
+ }
+
+ /* Mask */
+ /* Do not drop in Circular Dependency for Mask.
+ Composition can be applied recursively if its children nodes have composition target to this one. */
+ if (node->style->mask.applying) {
+ TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?");
+ } else {
+ auto compNode = node->style->mask.node;
+ if (compNode && compNode->child.count > 0) {
+ node->style->mask.applying = true;
+
+ auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true);
+ if (comp) {
+ if (node->transform) comp->transform(*node->transform);
+ paint->composite(move(comp), CompositeMethod::AlphaMask);
+ }
+
+ node->style->mask.applying = false;
+ }
+ }
+}
+
+
+static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath)
+{
+ SvgStyleProperty* style = node->style;
+
+ if (node->transform) vg->transform(*node->transform);
+ if (node->type == SvgNodeType::Doc || !node->display) return;
+
+ //If fill property is nullptr then do nothing
+ if (style->fill.paint.none) {
+ //Do nothing
+ } else if (style->fill.paint.gradient) {
+ Box bBox = vBox;
+ if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(vg);
+
+ if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
+ auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity);
+ vg->fill(move(linear));
+ } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) {
+ auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity);
+ vg->fill(move(radial));
+ }
+ } else if (style->fill.paint.url) {
+ //TODO: Apply the color pointed by url
+ } else if (style->fill.paint.curColor) {
+ //Apply the current style color
+ vg->fill(style->color.r, style->color.g, style->color.b, style->fill.opacity);
+ } else {
+ //Apply the fill color
+ vg->fill(style->fill.paint.color.r, style->fill.paint.color.g, style->fill.paint.color.b, style->fill.opacity);
+ }
+
+ //Apply the fill rule
+ vg->fill((tvg::FillRule)style->fill.fillRule);
+
+ //Apply node opacity
+ if (style->opacity < 255) vg->opacity(style->opacity);
+
+ if (node->type == SvgNodeType::G || node->type == SvgNodeType::Use) return;
+
+ //Apply the stroke style property
+ vg->stroke(style->stroke.width);
+ vg->stroke(style->stroke.cap);
+ vg->stroke(style->stroke.join);
+ if (style->stroke.dash.array.count > 0) {
+ vg->stroke(style->stroke.dash.array.data, style->stroke.dash.array.count);
+ }
+
+ //If stroke property is nullptr then do nothing
+ if (style->stroke.paint.none) {
+ //Do nothing
+ } else if (style->stroke.paint.gradient) {
+ Box bBox = vBox;
+ if (!style->stroke.paint.gradient->userSpace) bBox = _boundingBox(vg);
+
+ if (style->stroke.paint.gradient->type == SvgGradientType::Linear) {
+ auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity);
+ vg->stroke(move(linear));
+ } else if (style->stroke.paint.gradient->type == SvgGradientType::Radial) {
+ auto radial = _applyRadialGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity);
+ vg->stroke(move(radial));
+ }
+ } else if (style->stroke.paint.url) {
+ //TODO: Apply the color pointed by url
+ } else if (style->stroke.paint.curColor) {
+ //Apply the current style color
+ vg->stroke(style->color.r, style->color.g, style->color.b, style->stroke.opacity);
+ } else {
+ //Apply the stroke color
+ vg->stroke(style->stroke.paint.color.r, style->stroke.paint.color.g, style->stroke.paint.color.b, style->stroke.opacity);
+ }
+
+ _applyComposition(vg, node, vBox, svgPath);
+}
+
+
+static unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, const Box& vBox, const string& svgPath)
+{
+ auto shape = Shape::gen();
+ if (_appendShape(node, shape.get(), vBox, svgPath)) return shape;
+ else return nullptr;
+}
+
+
+static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+{
+ Array<PathCommand> cmds;
+ Array<Point> pts;
+
+ switch (node->type) {
+ case SvgNodeType::Path: {
+ if (node->node.path.path) {
+ if (svgPathToTvgPath(node->node.path.path, cmds, pts)) {
+ shape->appendPath(cmds.data, cmds.count, pts.data, pts.count);
+ }
+ }
+ break;
+ }
+ case SvgNodeType::Ellipse: {
+ shape->appendCircle(node->node.ellipse.cx, node->node.ellipse.cy, node->node.ellipse.rx, node->node.ellipse.ry);
+ break;
+ }
+ case SvgNodeType::Polygon: {
+ if (node->node.polygon.pointsCount < 2) break;
+ shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]);
+ for (int i = 2; i < node->node.polygon.pointsCount - 1; i += 2) {
+ shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]);
+ }
+ shape->close();
+ break;
+ }
+ case SvgNodeType::Polyline: {
+ if (node->node.polygon.pointsCount < 2) break;
+ shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]);
+ for (int i = 2; i < node->node.polygon.pointsCount - 1; i += 2) {
+ shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]);
+ }
+ break;
+ }
+ case SvgNodeType::Circle: {
+ shape->appendCircle(node->node.circle.cx, node->node.circle.cy, node->node.circle.r, node->node.circle.r);
+ break;
+ }
+ case SvgNodeType::Rect: {
+ shape->appendRect(node->node.rect.x, node->node.rect.y, node->node.rect.w, node->node.rect.h, node->node.rect.rx, node->node.rect.ry);
+ break;
+ }
+ case SvgNodeType::Line: {
+ shape->moveTo(node->node.line.x1, node->node.line.y1);
+ shape->lineTo(node->node.line.x2, node->node.line.y2);
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+
+ _applyProperty(node, shape, vBox, svgPath);
+ return true;
+}
+
+
+enum class imageMimeTypeEncoding
+{
+ base64 = 0x1,
+ utf8 = 0x2
+};
+constexpr imageMimeTypeEncoding operator|(imageMimeTypeEncoding a, imageMimeTypeEncoding b) {
+ return static_cast<imageMimeTypeEncoding>(static_cast<int>(a) | static_cast<int>(b));
+}
+constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) {
+ return (static_cast<int>(a) & static_cast<int>(b));
+}
+
+
+static constexpr struct
+{
+ const char* name;
+ int sz;
+ imageMimeTypeEncoding encoding;
+} imageMimeTypes[] = {
+ {"jpeg", sizeof("jpeg"), imageMimeTypeEncoding::base64},
+ {"png", sizeof("png"), imageMimeTypeEncoding::base64},
+ {"svg+xml", sizeof("svg+xml"), imageMimeTypeEncoding::base64 | imageMimeTypeEncoding::utf8},
+};
+
+
+static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mimetype, imageMimeTypeEncoding* encoding) {
+ if (strncmp(*href, "image/", sizeof("image/") - 1)) return false; //not allowed mime type
+ *href += sizeof("image/") - 1;
+
+ //RFC2397 data:[<mediatype>][;base64],<data>
+ //mediatype := [ type "/" subtype ] *( ";" parameter )
+ //parameter := attribute "=" value
+ for (unsigned int i = 0; i < sizeof(imageMimeTypes) / sizeof(imageMimeTypes[0]); i++) {
+ if (!strncmp(*href, imageMimeTypes[i].name, imageMimeTypes[i].sz - 1)) {
+ *href += imageMimeTypes[i].sz - 1;
+ *mimetype = imageMimeTypes[i].name;
+
+ while (**href && **href != ',') {
+ while (**href && **href != ';') ++(*href);
+ if (!**href) return false;
+ ++(*href);
+
+ if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::base64) {
+ if (!strncmp(*href, "base64,", sizeof("base64,") - 1)) {
+ *href += sizeof("base64,") - 1;
+ *encoding = imageMimeTypeEncoding::base64;
+ return true; //valid base64
+ }
+ }
+ if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8) {
+ if (!strncmp(*href, "utf8,", sizeof("utf8,") - 1)) {
+ *href += sizeof("utf8,") - 1;
+ *encoding = imageMimeTypeEncoding::utf8;
+ return true; //valid utf8
+ }
+ }
+ }
+ //no encoding defined
+ if (**href == ',' && (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8)) {
+ ++(*href);
+ *encoding = imageMimeTypeEncoding::utf8;
+ return true; //allow no encoding defined if utf8 expected
+ }
+ return false;
+ }
+ }
+ return false;
+}
+
+
+static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, const string& svgPath)
+{
+ if (!node->node.image.href) return nullptr;
+ auto picture = Picture::gen();
+
+ const char* href = node->node.image.href;
+ if (!strncmp(href, "data:", sizeof("data:") - 1)) {
+ href += sizeof("data:") - 1;
+ const char* mimetype;
+ imageMimeTypeEncoding encoding;
+ if (!_isValidImageMimeTypeAndEncoding(&href, &mimetype, &encoding)) return nullptr; //not allowed mime type or encoding
+ if (encoding == imageMimeTypeEncoding::base64) {
+ string decoded = svgUtilBase64Decode(href);
+ if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
+ } else {
+ string decoded = svgUtilURLDecode(href);
+ if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
+ }
+ } else {
+ if (!strncmp(href, "file://", sizeof("file://") - 1)) href += sizeof("file://") - 1;
+ //TODO: protect against recursive svg image loading
+ //Temporarily disable embedded svg:
+ const char *dot = strrchr(href, '.');
+ if (dot && !strcmp(dot, ".svg")) {
+ TVGLOG("SVG", "Embedded svg file is disabled.");
+ return nullptr;
+ }
+ string imagePath = href;
+ if (strncmp(href, "/", 1)) {
+ auto last = svgPath.find_last_of("/");
+ imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1 )) + imagePath;
+ }
+ if (picture->load(imagePath) != Result::Success) return nullptr;
+ }
+
+ float w, h;
+ if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) {
+ auto sx = node->node.image.w / w;
+ auto sy = node->node.image.h / h;
+ Matrix m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1};
+ picture->transform(m);
+ }
+
+ _applyComposition(picture.get(), node, vBox, svgPath);
+ return picture;
+}
+
+
+static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath)
+{
+ auto scene = _sceneBuildHelper(node, vBox, svgPath, false);
+ if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
+ scene->translate(node->node.use.x, node->node.use.y);
+ }
+ if (node->node.use.w > 0.0f && node->node.use.h > 0.0f) {
+ //TODO: handle width/height properties
+ }
+ return scene;
+}
+
+
+static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask)
+{
+ if (_isGroupType(node->type) || mask) {
+ auto scene = Scene::gen();
+ if (!mask && node->transform) scene->transform(*node->transform);
+
+ if (node->display && node->style->opacity != 0) {
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ if (_isGroupType((*child)->type)) {
+ if ((*child)->type == SvgNodeType::Use)
+ scene->push(_useBuildHelper(*child, vBox, svgPath));
+ else
+ scene->push(_sceneBuildHelper(*child, vBox, svgPath, false));
+ } else if ((*child)->type == SvgNodeType::Image) {
+ auto image = _imageBuildHelper(*child, vBox, svgPath);
+ if (image) scene->push(move(image));
+ } else if ((*child)->type != SvgNodeType::Mask) {
+ auto shape = _shapeBuildHelper(*child, vBox, svgPath);
+ if (shape) scene->push(move(shape));
+ }
+ }
+ _applyComposition(scene.get(), node, vBox, svgPath);
+ scene->opacity(node->style->opacity);
+ }
+ return scene;
+ }
+ return nullptr;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath)
+{
+ if (!node || (node->type != SvgNodeType::Doc)) return nullptr;
+
+ Box vBox = {vx, vy, vw, vh};
+ auto docNode = _sceneBuildHelper(node, vBox, svgPath, false);
+
+ if (!mathEqual(w, vw) || !mathEqual(h, vh)) {
+ auto sx = w / vw;
+ auto sy = h / vh;
+
+ if (preserveAspect) {
+ //Scale
+ auto scale = sx < sy ? sx : sy;
+ docNode->scale(scale);
+ //Align
+ auto tvx = vx * scale;
+ auto tvy = vy * scale;
+ auto tvw = vw * scale;
+ auto tvh = vh * scale;
+ if (vw > vh) tvy -= (h - tvh) * 0.5f;
+ else tvx -= (w - tvw) * 0.5f;
+ docNode->translate(-tvx, -tvy);
+ } else {
+ //Align
+ auto tvx = vx * sx;
+ auto tvy = vy * sy;
+ auto tvw = vw * sx;
+ auto tvh = vh * sy;
+ if (tvw > tvh) tvy -= (h - tvh) * 0.5f;
+ else tvx -= (w - tvw) * 0.5f;
+ Matrix m = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
+ docNode->transform(m);
+ }
+ } else if (!mathZero(vx) || !mathZero(vy)) {
+ docNode->translate(-vx, -vy);
+ }
+
+ auto viewBoxClip = Shape::gen();
+ viewBoxClip->appendRect(0, 0, w, h, 0, 0);
+ viewBoxClip->fill(0, 0, 0, 255);
+
+ auto compositeLayer = Scene::gen();
+ compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath);
+ compositeLayer->push(move(docNode));
+
+ auto root = Scene::gen();
+ root->push(move(compositeLayer));
+
+ return root;
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
new file mode 100644
index 0000000000..4232aca612
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _TVG_SVG_SCENE_BUILDER_H_
+#define _TVG_SVG_SCENE_BUILDER_H_
+
+#include "tvgCommon.h"
+
+unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath);
+
+#endif //_TVG_SVG_SCENE_BUILDER_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
new file mode 100644
index 0000000000..9f269b29a2
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <cstring>
+#include <math.h>
+#include <memory.h>
+#include "tvgSvgUtil.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static inline bool _floatExact(float a, float b)
+{
+ return memcmp(&a, &b, sizeof(float)) == 0;
+}
+
+static uint8_t _hexCharToDec(const char c)
+{
+ if (c >= 'a') return c - 'a' + 10;
+ else if (c >= 'A') return c - 'A' + 10;
+ else return c - '0';
+}
+
+static uint8_t _base64Value(const char chr)
+{
+ if (chr >= 'A' && chr <= 'Z') return chr - 'A';
+ else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
+ else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
+ else if (chr == '+' || chr == '-') return 62;
+ else return 63;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+
+/*
+ * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
+ *
+ * src should be one of the following form :
+ *
+ * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
+ * [whitespace] [sign] {INF | INFINITY}
+ * [whitespace] [sign] NAN [sequence]
+ *
+ * No hexadecimal form supported
+ * no sequence supported after NAN
+ */
+float svgUtilStrtof(const char *nPtr, char **endPtr)
+{
+ if (endPtr) *endPtr = (char*)(nPtr);
+ if (!nPtr) return 0.0f;
+
+ auto a = nPtr;
+ auto iter = nPtr;
+ auto val = 0.0f;
+ unsigned long long integerPart = 0;
+ int minus = 1;
+
+ //ignore leading whitespaces
+ while (isspace(*iter)) iter++;
+
+ //signed or not
+ if (*iter == '-') {
+ minus = -1;
+ iter++;
+ } else if (*iter == '+') {
+ iter++;
+ }
+
+ if (tolower(*iter) == 'i') {
+ if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
+ else goto error;
+
+ if (tolower(*(iter + 3)) == 'i') {
+ if ((tolower(*(iter + 4)) == 'n') && (tolower(*(iter + 5)) == 'i') && (tolower(*(iter + 6)) == 't') && (tolower(*(iter + 7)) == 'y')) iter += 5;
+ else goto error;
+ }
+ if (endPtr) *endPtr = (char *)(iter);
+ return (minus == -1) ? -INFINITY : INFINITY;
+ }
+
+ if (tolower(*iter) == 'n') {
+ if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
+ else goto error;
+
+ if (endPtr) *endPtr = (char *)(iter);
+ return (minus == -1) ? -NAN : NAN;
+ }
+
+ //Optional: integer part before dot
+ if (isdigit(*iter)) {
+ for (; isdigit(*iter); iter++) {
+ integerPart = integerPart * 10ULL + (unsigned long long)(*iter - '0');
+ }
+ a = iter;
+ } else if (*iter != '.') {
+ goto success;
+ }
+
+ val = static_cast<float>(integerPart);
+
+ //Optional: decimal part after dot
+ if (*iter == '.') {
+ unsigned long long decimalPart = 0;
+ unsigned long long pow10 = 1;
+ int count = 0;
+
+ iter++;
+
+ if (isdigit(*iter)) {
+ for (; isdigit(*iter); iter++, count++) {
+ if (count < 19) {
+ decimalPart = decimalPart * 10ULL + + static_cast<unsigned long long>(*iter - '0');
+ pow10 *= 10ULL;
+ }
+ }
+ }
+ val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
+ a = iter;
+ }
+
+ //Optional: exponent
+ if (*iter == 'e' || *iter == 'E') {
+ ++iter;
+
+ //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
+ if ((*iter == 'm') || (*iter == 'M')) {
+ //TODO: We don't support font em unit now, but has to multiply val * font size later...
+ a = iter + 1;
+ goto success;
+ }
+
+ //signed or not
+ int minus_e = 1;
+
+ if (*iter == '-') {
+ minus_e = -1;
+ ++iter;
+ } else if (*iter == '+') {
+ iter++;
+ }
+
+ unsigned int exponentPart = 0;
+
+ if (isdigit(*iter)) {
+ while (*iter == '0') iter++;
+ for (; isdigit(*iter); iter++) {
+ exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
+ }
+ } else if (!isdigit(*(a - 1))) {
+ a = nPtr;
+ goto success;
+ } else if (*iter == 0) {
+ goto success;
+ }
+
+ //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
+ if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
+ //val *= 1.0e-308f;
+ val *= 1.0e-38f;
+ a = iter;
+ goto success;
+ }
+
+ a = iter;
+ auto scale = 1.0f;
+
+ while (exponentPart >= 8U) {
+ scale *= 1E8;
+ exponentPart -= 8U;
+ }
+ while (exponentPart > 0U) {
+ scale *= 10.0f;
+ exponentPart--;
+ }
+ val = (minus_e == -1) ? (val / scale) : (val * scale);
+ } else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
+ a = nPtr;
+ goto success;
+ }
+
+success:
+ if (endPtr) *endPtr = (char*)(a);
+ return minus * val;
+
+error:
+ if (endPtr) *endPtr = (char*)(nPtr);
+ return 0.0f;
+}
+
+
+string svgUtilURLDecode(const char *src)
+{
+ if (!src) return nullptr;
+
+ auto length = strlen(src);
+ if (length == 0) return nullptr;
+
+ string decoded;
+ decoded.reserve(length);
+
+ char a, b;
+ while (*src) {
+ if (*src == '%' &&
+ ((a = src[1]) && (b = src[2])) &&
+ (isxdigit(a) && isxdigit(b))) {
+ decoded += (_hexCharToDec(a) << 4) + _hexCharToDec(b);
+ src+=3;
+ } else if (*src == '+') {
+ decoded += ' ';
+ src++;
+ } else {
+ decoded += *src++;
+ }
+ }
+ return decoded;
+}
+
+
+string svgUtilBase64Decode(const char *src)
+{
+ if (!src) return nullptr;
+
+ auto length = strlen(src);
+ if (length == 0) return nullptr;
+
+ string decoded;
+ decoded.reserve(3*(1+(length >> 2)));
+
+ while (*src && *(src+1)) {
+ if (*src <= 0x20) {
+ ++src;
+ continue;
+ }
+
+ auto value1 = _base64Value(src[0]);
+ auto value2 = _base64Value(src[1]);
+ decoded += (value1 << 2) + ((value2 & 0x30) >> 4);
+
+ if (!src[2] || src[2] == '=' || src[2] == '.') break;
+ auto value3 = _base64Value(src[2]);
+ decoded += ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
+
+ if (!src[3] || src[3] == '=' || src[3] == '.') break;
+ auto value4 = _base64Value(src[3]);
+ decoded += ((value3 & 0x03) << 6) + value4;
+ src += 4;
+ }
+ return decoded;
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
new file mode 100644
index 0000000000..4320cfed4e
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _TVG_SVG_UTIL_H_
+#define _TVG_SVG_UTIL_H_
+
+#include "tvgCommon.h"
+
+float svgUtilStrtof(const char *nPtr, char **endPtr);
+
+string svgUtilURLDecode(const char *src);
+string svgUtilBase64Decode(const char *src);
+
+#endif //_TVG_SVG_UTIL_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
new file mode 100644
index 0000000000..ee199da231
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <cstring>
+#include <ctype.h>
+#include <string>
+
+#ifdef _WIN32
+ #include <malloc.h>
+#else
+ #include <alloca.h>
+#endif
+
+#include "tvgXmlParser.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+bool _isIgnoreUnsupportedLogAttributes(TVG_UNUSED const char* tagAttribute, TVG_UNUSED const char* tagValue)
+{
+#ifdef THORVG_LOG_ENABLED
+ const auto attributesNum = 6;
+ const struct
+ {
+ const char* tag;
+ bool tagWildcard; //If true, it is assumed that a wildcard is used after the tag. (ex: tagName*)
+ const char* value;
+ } attributes[] = {
+ {"id", false, nullptr},
+ {"data-name", false, nullptr},
+ {"overflow", false, "visible"},
+ {"version", false, nullptr},
+ {"xmlns", true, nullptr},
+ {"xml:space", false, nullptr},
+ };
+
+ for (unsigned int i = 0; i < attributesNum; ++i) {
+ if (!strncmp(tagAttribute, attributes[i].tag, attributes[i].tagWildcard ? strlen(attributes[i].tag) : strlen(tagAttribute))) {
+ if (attributes[i].value && tagValue) {
+ if (!strncmp(tagValue, attributes[i].value, strlen(tagValue))) {
+ return true;
+ } else continue;
+ }
+ return true;
+ }
+ }
+ return false;
+#endif
+ return true;
+}
+
+
+static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
+{
+ for (; itr < itrEnd; itr++) {
+ if (isspace((unsigned char)*itr)) break;
+ }
+ return itr;
+}
+
+
+static const char* _simpleXmlSkipWhiteSpace(const char* itr, const char* itrEnd)
+{
+ for (; itr < itrEnd; itr++) {
+ if (!isspace((unsigned char)*itr)) break;
+ }
+ return itr;
+}
+
+
+static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrStart)
+{
+ for (itr--; itr > itrStart; itr--) {
+ if (!isspace((unsigned char)*itr)) break;
+ }
+ return itr + 1;
+}
+
+
+static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd)
+{
+ auto p = itr;
+ while (itr < itrEnd && *itr == '&') {
+ for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
+ if (strncmp(itr, xmlEntity[i], xmlEntityLength[i]) == 0) {
+ itr += xmlEntityLength[i];
+ break;
+ }
+ }
+ if (itr == p) break;
+ p = itr;
+ }
+ return itr;
+}
+
+
+static const char* _simpleXmlUnskipXmlEntities(const char* itr, const char* itrStart)
+{
+ auto p = itr;
+ while (itr > itrStart && *(itr - 1) == ';') {
+ for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
+ if (itr - xmlEntityLength[i] > itrStart &&
+ strncmp(itr - xmlEntityLength[i], xmlEntity[i], xmlEntityLength[i]) == 0) {
+ itr -= xmlEntityLength[i];
+ break;
+ }
+ }
+ if (itr == p) break;
+ p = itr;
+ }
+ return itr;
+}
+
+
+static const char* _skipWhiteSpacesAndXmlEntities(const char* itr, const char* itrEnd)
+{
+ itr = _simpleXmlSkipWhiteSpace(itr, itrEnd);
+ auto p = itr;
+ while (true) {
+ if (p != (itr = _simpleXmlSkipXmlEntities(itr, itrEnd))) p = itr;
+ else break;
+ if (p != (itr = _simpleXmlSkipWhiteSpace(itr, itrEnd))) p = itr;
+ else break;
+ }
+ return itr;
+}
+
+
+static const char* _unskipWhiteSpacesAndXmlEntities(const char* itr, const char* itrStart)
+{
+ itr = _simpleXmlUnskipWhiteSpace(itr, itrStart);
+ auto p = itr;
+ while (true) {
+ if (p != (itr = _simpleXmlUnskipXmlEntities(itr, itrStart))) p = itr;
+ else break;
+ if (p != (itr = _simpleXmlUnskipWhiteSpace(itr, itrStart))) p = itr;
+ else break;
+ }
+ return itr;
+}
+
+
+static const char* _simpleXmlFindStartTag(const char* itr, const char* itrEnd)
+{
+ return (const char*)memchr(itr, '<', itrEnd - itr);
+}
+
+
+static const char* _simpleXmlFindEndTag(const char* itr, const char* itrEnd)
+{
+ bool insideQuote = false;
+ for (; itr < itrEnd; itr++) {
+ if (*itr == '"') insideQuote = !insideQuote;
+ if (!insideQuote) {
+ if ((*itr == '>') || (*itr == '<'))
+ return itr;
+ }
+ }
+ return nullptr;
+}
+
+
+static const char* _simpleXmlFindEndCommentTag(const char* itr, const char* itrEnd)
+{
+ for (; itr < itrEnd; itr++) {
+ if ((*itr == '-') && ((itr + 1 < itrEnd) && (*(itr + 1) == '-')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
+ }
+ return nullptr;
+}
+
+
+static const char* _simpleXmlFindEndCdataTag(const char* itr, const char* itrEnd)
+{
+ for (; itr < itrEnd; itr++) {
+ if ((*itr == ']') && ((itr + 1 < itrEnd) && (*(itr + 1) == ']')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
+ }
+ return nullptr;
+}
+
+
+static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd)
+{
+ for (; itr < itrEnd; itr++) {
+ if (*itr == '>') return itr;
+ }
+ return nullptr;
+}
+
+
+static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &toff)
+{
+ toff = 0;
+ if (itr[1] == '/') {
+ toff = 1;
+ return SimpleXMLType::Close;
+ } else if (itr[1] == '?') {
+ toff = 1;
+ return SimpleXMLType::Processing;
+ } else if (itr[1] == '!') {
+ if ((itr + sizeof("<!DOCTYPE>") - 1 < itrEnd) && (!memcmp(itr + 2, "DOCTYPE", sizeof("DOCTYPE") - 1)) && ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1])))) {
+ toff = sizeof("!DOCTYPE") - 1;
+ return SimpleXMLType::Doctype;
+ } else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
+ toff = sizeof("![CDATA[") - 1;
+ return SimpleXMLType::CData;
+ } else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
+ toff = sizeof("!--") - 1;
+ return SimpleXMLType::Comment;
+ } else if (itr + sizeof("<!>") - 1 < itrEnd) {
+ toff = sizeof("!") - 1;
+ return SimpleXMLType::DoctypeChild;
+ }
+ return SimpleXMLType::Open;
+ }
+ return SimpleXMLType::Open;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+const char* simpleXmlNodeTypeToString(TVG_UNUSED SvgNodeType type)
+{
+#ifdef THORVG_LOG_ENABLED
+ static const char* TYPE_NAMES[] = {
+ "Svg",
+ "G",
+ "Defs",
+ "Animation",
+ "Arc",
+ "Circle",
+ "Ellipse",
+ "Image",
+ "Line",
+ "Path",
+ "Polygon",
+ "Polyline",
+ "Rect",
+ "Text",
+ "TextArea",
+ "Tspan",
+ "Use",
+ "Video",
+ "ClipPath",
+ "Mask",
+ "Unknown",
+ };
+ return TYPE_NAMES[(int) type];
+#endif
+ return nullptr;
+}
+
+
+bool isIgnoreUnsupportedLogElements(TVG_UNUSED const char* tagName)
+{
+#ifdef THORVG_LOG_ENABLED
+ const auto elementsNum = 1;
+ const char* const elements[] = { "title" };
+
+ for (unsigned int i = 0; i < elementsNum; ++i) {
+ if (!strncmp(tagName, elements[i], strlen(tagName))) {
+ return true;
+ }
+ }
+ return false;
+#else
+ return true;
+#endif
+}
+
+
+bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
+{
+ const char *itr = buf, *itrEnd = buf + bufLength;
+ char* tmpBuf = (char*)alloca(bufLength + 1);
+
+ if (!buf || !func) return false;
+
+ while (itr < itrEnd) {
+ const char* p = _skipWhiteSpacesAndXmlEntities(itr, itrEnd);
+ const char *key, *keyEnd, *value, *valueEnd;
+ char* tval;
+
+ if (p == itrEnd) return true;
+
+ key = p;
+ for (keyEnd = key; keyEnd < itrEnd; keyEnd++) {
+ if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
+ }
+ if (keyEnd == itrEnd) return false;
+ if (keyEnd == key) continue;
+
+ if (*keyEnd == '=') value = keyEnd + 1;
+ else {
+ value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd);
+ if (!value) return false;
+ value++;
+ }
+ keyEnd = _simpleXmlUnskipXmlEntities(keyEnd, key);
+
+ value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
+ if (value == itrEnd) return false;
+
+ if ((*value == '"') || (*value == '\'')) {
+ valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value);
+ if (!valueEnd) return false;
+ value++;
+ } else {
+ valueEnd = _simpleXmlFindWhiteSpace(value, itrEnd);
+ }
+
+ itr = valueEnd + 1;
+
+ value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
+ valueEnd = _unskipWhiteSpacesAndXmlEntities(valueEnd, value);
+
+ memcpy(tmpBuf, key, keyEnd - key);
+ tmpBuf[keyEnd - key] = '\0';
+
+ tval = tmpBuf + (keyEnd - key) + 1;
+ int i = 0;
+ while (value < valueEnd) {
+ value = _simpleXmlSkipXmlEntities(value, valueEnd);
+ tval[i++] = *value;
+ value++;
+ }
+ tval[i] = '\0';
+
+ if (!func((void*)data, tmpBuf, tval)) {
+ if (!_isIgnoreUnsupportedLogAttributes(tmpBuf, tval)) {
+ TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", tmpBuf, tval ? tval : "NONE");
+ }
+ }
+ }
+ return true;
+}
+
+
+bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data)
+{
+ const char *itr = buf, *itrEnd = buf + bufLength;
+
+ if (!buf || !func) return false;
+
+ while (itr < itrEnd) {
+ if (itr[0] == '<') {
+ //Invalid case
+ if (itr + 1 >= itrEnd) return false;
+
+ size_t toff = 0;
+ SimpleXMLType type = _getXMLType(itr, itrEnd, toff);
+
+ const char* p;
+ if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd);
+ else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
+ else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd);
+ else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd);
+
+ if (p) {
+ //Invalid case: '<' nested
+ if (*p == '<') return false;
+ const char *start, *end;
+
+ start = itr + 1 + toff;
+ end = p;
+
+ switch (type) {
+ case SimpleXMLType::Open: {
+ if (p[-1] == '/') {
+ type = SimpleXMLType::OpenEmpty;
+ end--;
+ }
+ break;
+ }
+ case SimpleXMLType::CData: {
+ if (!memcmp(p - 2, "]]", 2)) end -= 2;
+ break;
+ }
+ case SimpleXMLType::Processing: {
+ if (p[-1] == '?') end--;
+ break;
+ }
+ case SimpleXMLType::Comment: {
+ if (!memcmp(p - 2, "--", 2)) end -= 2;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ if (strip && (type != SimpleXMLType::CData)) {
+ start = _skipWhiteSpacesAndXmlEntities(start, end);
+ end = _unskipWhiteSpacesAndXmlEntities(end, start);
+ }
+
+ if (!func((void*)data, type, start, (unsigned int)(end - start))) return false;
+
+ itr = p + 1;
+ } else {
+ return false;
+ }
+ } else {
+ const char *p, *end;
+
+ if (strip) {
+ p = itr;
+ p = _skipWhiteSpacesAndXmlEntities(p, itrEnd);
+ if (p) {
+ if (!func((void*)data, SimpleXMLType::Ignored, itr, (unsigned int)(p - itr))) return false;
+ itr = p;
+ }
+ }
+
+ p = _simpleXmlFindStartTag(itr, itrEnd);
+ if (!p) p = itrEnd;
+
+ end = p;
+ if (strip) end = _unskipWhiteSpacesAndXmlEntities(end, itr);
+
+ if (itr != end && !func((void*)data, SimpleXMLType::Data, itr, (unsigned int)(end - itr))) return false;
+
+ if (strip && (end < p) && !func((void*)data, SimpleXMLType::Ignored, end, (unsigned int)(p - end))) return false;
+
+ itr = p;
+ }
+ }
+ return true;
+}
+
+
+bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data)
+{
+ const char* end;
+ char* key;
+ char* val;
+ char* next;
+
+ if (!buf) return false;
+
+ end = buf + strlen(buf);
+ key = (char*)alloca(end - buf + 1);
+ val = (char*)alloca(end - buf + 1);
+
+ if (buf == end) return true;
+
+ do {
+ char* sep = (char*)strchr(buf, ':');
+ next = (char*)strchr(buf, ';');
+
+ key[0] = '\0';
+ val[0] = '\0';
+
+ if (next == nullptr && sep != nullptr) {
+ memcpy(key, buf, sep - buf);
+ key[sep - buf] = '\0';
+
+ memcpy(val, sep + 1, end - sep - 1);
+ val[end - sep - 1] = '\0';
+ } else if (sep < next && sep != nullptr) {
+ memcpy(key, buf, sep - buf);
+ key[sep - buf] = '\0';
+
+ memcpy(val, sep + 1, next - sep - 1);
+ val[next - sep - 1] = '\0';
+ } else if (next) {
+ memcpy(key, buf, next - buf);
+ key[next - buf] = '\0';
+ }
+
+ if (key[0]) {
+ key = const_cast<char*>(_simpleXmlSkipWhiteSpace(key, key + strlen(key)));
+ key[_simpleXmlUnskipWhiteSpace(key + strlen(key) , key) - key] = '\0';
+ val = const_cast<char*>(_simpleXmlSkipWhiteSpace(val, val + strlen(val)));
+ val[_simpleXmlUnskipWhiteSpace(val + strlen(val) , val) - val] = '\0';
+
+ if (!func((void*)data, key, val)) {
+ if (!_isIgnoreUnsupportedLogAttributes(key, val)) {
+ TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", key, val ? val : "NONE");
+ }
+ }
+ }
+
+ buf = next + 1;
+ } while (next != nullptr);
+
+ return true;
+}
+
+
+const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
+{
+ const char *itr = buf, *itrEnd = buf + bufLength;
+
+ for (; itr < itrEnd; itr++) {
+ if (!isspace((unsigned char)*itr)) {
+ //User skip tagname and already gave it the attributes.
+ if (*itr == '=') return buf;
+ } else {
+ itr = _simpleXmlUnskipXmlEntities(itr, buf);
+ if (itr == itrEnd) return nullptr;
+ return itr;
+ }
+ }
+
+ return nullptr;
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
new file mode 100644
index 0000000000..d96a631528
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _TVG_SIMPLE_XML_PARSER_H_
+#define _TVG_SIMPLE_XML_PARSER_H_
+
+#include "tvgSvgLoaderCommon.h"
+
+#define NUMBER_OF_XML_ENTITIES 8
+const char* const xmlEntity[] = {"&quot;", "&nbsp;", "&apos;", "&amp;", "&lt;", "&gt;", "&#035;", "&#039;"};
+const int xmlEntityLength[] = {6, 6, 6, 5, 4, 4, 6, 6};
+
+enum class SimpleXMLType
+{
+ Open = 0, //!< \<tag attribute="value"\>
+ OpenEmpty, //!< \<tag attribute="value" /\>
+ Close, //!< \</tag\>
+ Data, //!< tag text data
+ CData, //!< \<![cdata[something]]\>
+ Error, //!< error contents
+ Processing, //!< \<?xml ... ?\> \<?php .. ?\>
+ Doctype, //!< \<!doctype html
+ Comment, //!< \<!-- something --\>
+ Ignored, //!< whatever is ignored by parser, like whitespace
+ DoctypeChild //!< \<!doctype_child
+};
+
+typedef bool (*simpleXMLCb)(void* data, SimpleXMLType type, const char* content, unsigned int length);
+typedef bool (*simpleXMLAttributeCb)(void* data, const char* key, const char* value);
+
+bool simpleXmlParseAttributes(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data);
+bool simpleXmlParse(const char* buf, unsigned buflen, bool strip, simpleXMLCb func, const void* data);
+bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data);
+const char *simpleXmlFindAttributesTag(const char* buf, unsigned buflen);
+bool isIgnoreUnsupportedLogElements(const char* tagName);
+const char* simpleXmlNodeTypeToString(SvgNodeType type);
+
+#endif //_TVG_SIMPLE_XML_PARSER_H_
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
new file mode 100644
index 0000000000..b0364b1055
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <memory.h>
+
+#ifdef _WIN32
+ #include <malloc.h>
+#else
+ #include <alloca.h>
+#endif
+
+#include "tvgTvgCommon.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct TvgBinBlock
+{
+ TvgBinTag type;
+ TvgBinCounter length;
+ const char* data;
+ const char* end;
+};
+
+static Paint* _parsePaint(TvgBinBlock baseBlock);
+
+
+static TvgBinBlock _readBlock(const char *ptr)
+{
+ TvgBinBlock block;
+ block.type = *ptr;
+ READ_UI32(&block.length, ptr + SIZE(TvgBinTag));
+ block.data = ptr + SIZE(TvgBinTag) + SIZE(TvgBinCounter);
+ block.end = block.data + block.length;
+ return block;
+}
+
+
+static bool _parseCmpTarget(const char *ptr, const char *end, Paint *paint)
+{
+ auto block = _readBlock(ptr);
+ if (block.end > end) return false;
+
+ if (block.type != TVG_TAG_PAINT_CMP_METHOD) return false;
+ if (block.length != SIZE(TvgBinFlag)) return false;
+
+ auto cmpMethod = static_cast<CompositeMethod>(*block.data);
+
+ ptr = block.end;
+
+ auto cmpBlock = _readBlock(ptr);
+ if (cmpBlock.end > end) return false;
+
+ paint->composite(unique_ptr<Paint>(_parsePaint(cmpBlock)), cmpMethod);
+
+ return true;
+}
+
+
+static bool _parsePaintProperty(TvgBinBlock block, Paint *paint)
+{
+ switch (block.type) {
+ case TVG_TAG_PAINT_OPACITY: {
+ if (block.length != SIZE(uint8_t)) return false;
+ paint->opacity(*block.data);
+ return true;
+ }
+ case TVG_TAG_PAINT_TRANSFORM: {
+ if (block.length != SIZE(Matrix)) return false;
+ Matrix matrix;
+ memcpy(&matrix, block.data, SIZE(Matrix));
+ paint->transform(matrix);
+ return true;
+ }
+ case TVG_TAG_PAINT_CMP_TARGET: {
+ if (block.length < SIZE(TvgBinTag) + SIZE(TvgBinCounter)) return false;
+ return _parseCmpTarget(block.data, block.end, paint);
+ }
+ }
+ return false;
+}
+
+
+static bool _parseScene(TvgBinBlock block, Paint *paint)
+{
+ auto scene = static_cast<Scene*>(paint);
+
+ //Case1: scene reserve count
+ if (block.type == TVG_TAG_SCENE_RESERVEDCNT) {
+ if (block.length != SIZE(uint32_t)) return false;
+ uint32_t reservedCnt;
+ READ_UI32(&reservedCnt, block.data);
+ scene->reserve(reservedCnt);
+ return true;
+ }
+
+ //Case2: Base Paint Properties
+ if (_parsePaintProperty(block, scene)) return true;
+
+ //Case3: A Child paint
+ if (auto paint = _parsePaint(block)) {
+ scene->push(unique_ptr<Paint>(paint));
+ return true;
+ }
+
+ return false;
+}
+
+
+static bool _parseShapePath(const char *ptr, const char *end, Shape *shape)
+{
+ uint32_t cmdCnt, ptsCnt;
+
+ READ_UI32(&cmdCnt, ptr);
+ ptr += SIZE(cmdCnt);
+
+ READ_UI32(&ptsCnt, ptr);
+ ptr += SIZE(ptsCnt);
+
+ auto cmds = (TvgBinFlag*) ptr;
+ ptr += SIZE(TvgBinFlag) * cmdCnt;
+
+ auto pts = (Point*) ptr;
+ ptr += SIZE(Point) * ptsCnt;
+
+ if (ptr > end) return false;
+
+ /* Recover to PathCommand(4 bytes) from TvgBinFlag(1 byte) */
+ PathCommand* inCmds = (PathCommand*)alloca(sizeof(PathCommand) * cmdCnt);
+ for (uint32_t i = 0; i < cmdCnt; ++i) {
+ inCmds[i] = static_cast<PathCommand>(cmds[i]);
+ }
+
+ shape->appendPath(inCmds, cmdCnt, pts, ptsCnt);
+
+ return true;
+}
+
+
+static unique_ptr<Fill> _parseShapeFill(const char *ptr, const char *end)
+{
+ unique_ptr<Fill> fillGrad;
+
+ while (ptr < end) {
+ auto block = _readBlock(ptr);
+ if (block.end > end) return nullptr;
+
+ switch (block.type) {
+ case TVG_TAG_FILL_RADIAL_GRADIENT: {
+ if (block.length != 3 * SIZE(float)) return nullptr;
+
+ auto ptr = block.data;
+ float x, y, radius;
+
+ READ_FLOAT(&x, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&y, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&radius, ptr);
+
+ auto fillGradRadial = RadialGradient::gen();
+ fillGradRadial->radial(x, y, radius);
+ fillGrad = move(fillGradRadial);
+ break;
+ }
+ case TVG_TAG_FILL_LINEAR_GRADIENT: {
+ if (block.length != 4 * SIZE(float)) return nullptr;
+
+ auto ptr = block.data;
+ float x1, y1, x2, y2;
+
+ READ_FLOAT(&x1, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&y1, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&x2, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&y2, ptr);
+
+ auto fillGradLinear = LinearGradient::gen();
+ fillGradLinear->linear(x1, y1, x2, y2);
+ fillGrad = move(fillGradLinear);
+ break;
+ }
+ case TVG_TAG_FILL_FILLSPREAD: {
+ if (!fillGrad) return nullptr;
+ if (block.length != SIZE(TvgBinFlag)) return nullptr;
+ fillGrad->spread((FillSpread) *block.data);
+ break;
+ }
+ case TVG_TAG_FILL_COLORSTOPS: {
+ if (!fillGrad) return nullptr;
+ if (block.length == 0 || block.length & 0x07) return nullptr;
+ uint32_t stopsCnt = block.length >> 3; // 8 bytes per ColorStop
+ if (stopsCnt > 1023) return nullptr;
+ Fill::ColorStop* stops = (Fill::ColorStop*)alloca(sizeof(Fill::ColorStop) * stopsCnt);
+ auto p = block.data;
+ for (uint32_t i = 0; i < stopsCnt; i++, p += 8) {
+ READ_FLOAT(&stops[i].offset, p);
+ stops[i].r = p[4];
+ stops[i].g = p[5];
+ stops[i].b = p[6];
+ stops[i].a = p[7];
+ }
+ fillGrad->colorStops(stops, stopsCnt);
+ break;
+ }
+ case TVG_TAG_FILL_TRANSFORM: {
+ if (!fillGrad || block.length != SIZE(Matrix)) return nullptr;
+ Matrix gradTransform;
+ memcpy(&gradTransform, block.data, SIZE(Matrix));
+ fillGrad->transform(gradTransform);
+ break;
+ }
+ default: {
+ TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of the fill properties, %d bytes skipped", block.type, block.type, block.length);
+ break;
+ }
+ }
+ ptr = block.end;
+ }
+ return fillGrad;
+}
+
+
+static bool _parseShapeStrokeDashPattern(const char *ptr, const char *end, Shape *shape)
+{
+ uint32_t dashPatternCnt;
+ READ_UI32(&dashPatternCnt, ptr);
+ ptr += SIZE(uint32_t);
+ if (dashPatternCnt > 0) {
+ float* dashPattern = static_cast<float*>(malloc(sizeof(float) * dashPatternCnt));
+ if (!dashPattern) return false;
+ memcpy(dashPattern, ptr, sizeof(float) * dashPatternCnt);
+ ptr += SIZE(float) * dashPatternCnt;
+
+ if (ptr > end) {
+ free(dashPattern);
+ return false;
+ }
+
+ shape->stroke(dashPattern, dashPatternCnt);
+ free(dashPattern);
+ }
+ return true;
+}
+
+
+static bool _parseShapeStroke(const char *ptr, const char *end, Shape *shape)
+{
+ while (ptr < end) {
+ auto block = _readBlock(ptr);
+ if (block.end > end) return false;
+
+ switch (block.type) {
+ case TVG_TAG_SHAPE_STROKE_CAP: {
+ if (block.length != SIZE(TvgBinFlag)) return false;
+ shape->stroke((StrokeCap) *block.data);
+ break;
+ }
+ case TVG_TAG_SHAPE_STROKE_JOIN: {
+ if (block.length != SIZE(TvgBinFlag)) return false;
+ shape->stroke((StrokeJoin) *block.data);
+ break;
+ }
+ case TVG_TAG_SHAPE_STROKE_WIDTH: {
+ if (block.length != SIZE(float)) return false;
+ float width;
+ READ_FLOAT(&width, block.data);
+ shape->stroke(width);
+ break;
+ }
+ case TVG_TAG_SHAPE_STROKE_COLOR: {
+ if (block.length != 4) return false;
+ shape->stroke(block.data[0], block.data[1], block.data[2], block.data[3]);
+ break;
+ }
+ case TVG_TAG_SHAPE_STROKE_FILL: {
+ auto fill = _parseShapeFill(block.data, block.end);
+ if (!fill) return false;
+ shape->stroke(move(move(fill)));
+ break;
+ }
+ case TVG_TAG_SHAPE_STROKE_DASHPTRN: {
+ if (!_parseShapeStrokeDashPattern(block.data, block.end, shape)) return false;
+ break;
+ }
+ default: {
+ TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of stroke properties, %d bytes skipped", block.type, block.type, block.length);
+ break;
+ }
+ }
+ ptr = block.end;
+ }
+ return true;
+}
+
+
+static bool _parseShape(TvgBinBlock block, Paint* paint)
+{
+ auto shape = static_cast<Shape*>(paint);
+
+ //Case1: Shape specific properties
+ switch (block.type) {
+ case TVG_TAG_SHAPE_PATH: {
+ return _parseShapePath(block.data, block.end, shape);
+ }
+ case TVG_TAG_SHAPE_STROKE: {
+ return _parseShapeStroke(block.data, block.end, shape);
+ }
+ case TVG_TAG_SHAPE_FILL: {
+ auto fill = _parseShapeFill(block.data, block.end);
+ if (!fill) return false;
+ shape->fill(move(fill));
+ return true;
+ }
+ case TVG_TAG_SHAPE_COLOR: {
+ if (block.length != 4) return false;
+ shape->fill(block.data[0], block.data[1], block.data[2], block.data[3]);
+ return true;
+ }
+ case TVG_TAG_SHAPE_FILLRULE: {
+ if (block.length != SIZE(TvgBinFlag)) return false;
+ shape->fill((FillRule)*block.data);
+ return true;
+ }
+ }
+
+ //Case2: Base Paint Properties
+ return _parsePaintProperty(block, shape);
+}
+
+
+static bool _parsePicture(TvgBinBlock block, Paint* paint)
+{
+ auto picture = static_cast<Picture*>(paint);
+
+ //Case1: Image Picture
+ if (block.type == TVG_TAG_PICTURE_RAW_IMAGE) {
+ if (block.length < 2 * SIZE(uint32_t)) return false;
+
+ auto ptr = block.data;
+ uint32_t w, h;
+
+ READ_UI32(&w, ptr);
+ ptr += SIZE(uint32_t);
+ READ_UI32(&h, ptr);
+ ptr += SIZE(uint32_t);
+
+ auto size = w * h * SIZE(uint32_t);
+ if (block.length != 2 * SIZE(uint32_t) + size) return false;
+
+ picture->load((uint32_t*) ptr, w, h, true);
+ return true;
+ }
+
+ //Case2: Base Paint Properties
+ if (_parsePaintProperty(block, picture)) return true;
+
+ //Vector Picture won't be requested since Saver replaces it with the Scene
+ return false;
+}
+
+
+static Paint* _parsePaint(TvgBinBlock baseBlock)
+{
+ bool (*parser)(TvgBinBlock, Paint*);
+ Paint *paint;
+
+ //1. Decide the type of paint.
+ switch (baseBlock.type) {
+ case TVG_TAG_CLASS_SCENE: {
+ paint = Scene::gen().release();
+ parser = _parseScene;
+ break;
+ }
+ case TVG_TAG_CLASS_SHAPE: {
+ paint = Shape::gen().release();
+ parser = _parseShape;
+ break;
+ }
+ case TVG_TAG_CLASS_PICTURE: {
+ paint = Picture::gen().release();
+ parser = _parsePicture;
+ break;
+ }
+ default: {
+ TVGERR("TVG", "Invalid Paint Type %d (0x%x)", baseBlock.type, baseBlock.type);
+ return nullptr;
+ }
+ }
+
+ auto ptr = baseBlock.data;
+
+ //2. Read Subsquent properties of the current paint.
+ while (ptr < baseBlock.end) {
+ auto block = _readBlock(ptr);
+ if (block.end > baseBlock.end) return paint;
+ if (!parser(block, paint)) {
+ TVGERR("TVG", "Encountered the wrong paint properties... Paint Class %d (0x%x)", baseBlock.type, baseBlock.type);
+ return paint;
+ }
+ ptr = block.end;
+ }
+ return paint;
+}
+
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+unique_ptr<Scene> TvgBinInterpreter::run(const char *ptr, const char* end)
+{
+ auto scene = Scene::gen();
+ if (!scene) return nullptr;
+
+ while (ptr < end) {
+ auto block = _readBlock(ptr);
+ if (block.end > end) {
+ TVGERR("TVG", "Corrupted tvg file.");
+ return nullptr;
+ }
+ scene->push(unique_ptr<Paint>(_parsePaint(block)));
+ ptr = block.end;
+ }
+
+ return scene;
+}
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h b/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h
new file mode 100644
index 0000000000..e7c3eba488
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _TVG_TVG_COMMON_H_
+#define _TVG_TVG_COMMON_H_
+
+#include "tvgCommon.h"
+#include "tvgBinaryDesc.h"
+
+#define SIZE(A) sizeof(A)
+#define READ_UI32(dst, src) memcpy(dst, (src), sizeof(uint32_t))
+#define READ_FLOAT(dst, src) memcpy(dst, (src), sizeof(float))
+
+
+/* Interface for Tvg Binary Interpreter */
+class TvgBinInterpreterBase
+{
+public:
+ virtual ~TvgBinInterpreterBase() {}
+
+ /* ptr: points the tvg binary body (after header)
+ end: end of the tvg binary data */
+ virtual unique_ptr<Scene> run(const char* ptr, const char* end) = 0;
+};
+
+
+/* Version 0 */
+class TvgBinInterpreter : public TvgBinInterpreterBase
+{
+public:
+ unique_ptr<Scene> run(const char* ptr, const char* end) override;
+};
+
+
+#endif //_TVG_TVG_COMMON_H_ \ No newline at end of file
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp
new file mode 100644
index 0000000000..d7f3184435
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <memory.h>
+#include <fstream>
+#include "tvgLoader.h"
+#include "tvgTvgLoader.h"
+#include "tvgLzw.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+void TvgLoader::clear()
+{
+ if (copy) free((char*)data);
+ ptr = data = nullptr;
+ size = 0;
+ copy = false;
+
+ if (interpreter) {
+ delete(interpreter);
+ interpreter = nullptr;
+ }
+}
+
+
+/* WARNING: Header format shall not change! */
+bool TvgLoader::readHeader()
+{
+ if (!ptr) return false;
+
+ //Make sure the size is large enough to hold the header
+ if (size < TVG_HEADER_SIZE) return false;
+
+ //1. Signature
+ if (memcmp(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH)) return false;
+ ptr += TVG_HEADER_SIGNATURE_LENGTH;
+
+ //2. Version
+ char version[TVG_HEADER_VERSION_LENGTH + 1];
+ memcpy(version, ptr, TVG_HEADER_VERSION_LENGTH);
+ version[TVG_HEADER_VERSION_LENGTH - 1] = '\0';
+ ptr += TVG_HEADER_VERSION_LENGTH;
+ this->version = atoi(version);
+ if (this->version > THORVG_VERSION_NUMBER()) {
+ TVGLOG("TVG", "This TVG file expects a higher version(%d) of ThorVG symbol(%d)", this->version, THORVG_VERSION_NUMBER());
+ }
+
+ //3. View Size
+ READ_FLOAT(&w, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&h, ptr);
+ ptr += SIZE(float);
+
+ //4. Reserved
+ if (*ptr & TVG_HEAD_FLAG_COMPRESSED) compressed = true;
+ ptr += TVG_HEADER_RESERVED_LENGTH;
+
+ //5. Compressed Size if any
+ if (compressed) {
+ auto p = ptr;
+
+ //TVG_HEADER_UNCOMPRESSED_SIZE
+ memcpy(&uncompressedSize, p, sizeof(uint32_t));
+ p += SIZE(uint32_t);
+
+ //TVG_HEADER_COMPRESSED_SIZE
+ memcpy(&compressedSize, p, sizeof(uint32_t));
+ p += SIZE(uint32_t);
+
+ //TVG_HEADER_COMPRESSED_SIZE_BITS
+ memcpy(&compressedSizeBits, p, sizeof(uint32_t));
+ }
+
+ ptr += TVG_HEADER_COMPRESS_SIZE;
+
+ //Decide the proper Tvg Binary Interpreter based on the current file version
+ if (this->version >= 0) interpreter = new TvgBinInterpreter;
+
+ return true;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+TvgLoader::~TvgLoader()
+{
+ close();
+}
+
+
+bool TvgLoader::open(const string &path)
+{
+ clear();
+
+ ifstream f;
+ f.open(path, ifstream::in | ifstream::binary | ifstream::ate);
+
+ if (!f.is_open()) return false;
+
+ size = f.tellg();
+ f.seekg(0, ifstream::beg);
+
+ copy = true;
+ data = (char*)malloc(size);
+ if (!data) {
+ clear();
+ f.close();
+ return false;
+ }
+
+ if (!f.read((char*)data, size))
+ {
+ clear();
+ f.close();
+ return false;
+ }
+
+ f.close();
+
+ ptr = data;
+
+ return readHeader();
+}
+
+
+bool TvgLoader::open(const char *data, uint32_t size, bool copy)
+{
+ clear();
+
+ if (copy) {
+ this->data = (char*)malloc(size);
+ if (!this->data) return false;
+ memcpy((char*)this->data, data, size);
+ } else this->data = data;
+
+ this->ptr = this->data;
+ this->size = size;
+ this->copy = copy;
+
+ return readHeader();
+}
+
+
+bool TvgLoader::resize(Paint* paint, float w, float h)
+{
+ if (!paint) return false;
+
+ auto sx = w / this->w;
+ auto sy = h / this->h;
+
+ //Scale
+ auto scale = sx < sy ? sx : sy;
+ paint->scale(scale);
+
+ //Align
+ float tx = 0, ty = 0;
+ auto sw = this->w * scale;
+ auto sh = this->h * scale;
+ if (sw > sh) ty -= (h - sh) * 0.5f;
+ else tx -= (w - sw) * 0.5f;
+ paint->translate(-tx, -ty);
+
+ return true;
+}
+
+
+bool TvgLoader::read()
+{
+ if (!ptr || size == 0) return false;
+
+ TaskScheduler::request(this);
+
+ return true;
+}
+
+
+bool TvgLoader::close()
+{
+ this->done();
+ clear();
+ return true;
+}
+
+
+void TvgLoader::run(unsigned tid)
+{
+ if (root) root.reset();
+
+ auto data = const_cast<char*>(ptr);
+
+ if (compressed) {
+ data = (char*) lzwDecode((uint8_t*) data, compressedSize, compressedSizeBits, uncompressedSize);
+ root = interpreter->run(data, data + uncompressedSize);
+ free(data);
+ } else {
+ root = interpreter->run(data, this->data + size);
+ }
+
+ if (!root) clear();
+}
+
+
+unique_ptr<Paint> TvgLoader::paint()
+{
+ this->done();
+ if (root) return move(root);
+ return nullptr;
+}
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h
new file mode 100644
index 0000000000..d276ded33a
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _TVG_TVG_LOADER_H_
+#define _TVG_TVG_LOADER_H_
+
+#include "tvgTaskScheduler.h"
+#include "tvgTvgCommon.h"
+
+
+class TvgLoader : public LoadModule, public Task
+{
+public:
+ const char* data = nullptr;
+ const char* ptr = nullptr;
+ uint32_t size = 0;
+ uint16_t version = 0;
+ unique_ptr<Scene> root = nullptr;
+ TvgBinInterpreterBase* interpreter = nullptr;
+ uint32_t uncompressedSize = 0;
+ uint32_t compressedSize = 0;
+ uint32_t compressedSizeBits = 0;
+ bool copy = false;
+ bool compressed = false;
+
+ ~TvgLoader();
+
+ using LoadModule::open;
+ bool open(const string &path) override;
+ bool open(const char *data, uint32_t size, bool copy) override;
+ bool read() override;
+ bool close() override;
+ bool resize(Paint* paint, float w, float h) override;
+ unique_ptr<Paint> paint() override;
+
+private:
+ bool readHeader();
+ void run(unsigned tid) override;
+ void clear();
+};
+
+#endif //_TVG_TVG_LOADER_H_
diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
new file mode 100644
index 0000000000..9dd57e5a89
--- /dev/null
+++ b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
@@ -0,0 +1,775 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgSaveModule.h"
+#include "tvgTvgSaver.h"
+#include "tvgLzw.h"
+
+#include <cstring>
+
+#ifdef _WIN32
+ #include <malloc.h>
+#else
+ #include <alloca.h>
+#endif
+
+static FILE* _fopen(const char* filename, const char* mode)
+{
+#if defined(_MSC_VER) && defined(__clang__)
+ FILE *fp;
+ auto err = fopen_s(&fp, filename, mode);
+ if (err != 0) return nullptr;
+ return fp;
+#else
+ auto fp = fopen(filename, mode);
+ if (!fp) return nullptr;
+ return fp;
+#endif
+}
+
+#define SIZE(A) sizeof(A)
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static inline TvgBinCounter SERIAL_DONE(TvgBinCounter cnt)
+{
+ return SIZE(TvgBinTag) + SIZE(TvgBinCounter) + cnt;
+}
+
+
+/* if the properties are identical, we can merge the shapes. */
+static bool _merge(Shape* from, Shape* to)
+{
+ uint8_t r, g, b, a;
+ uint8_t r2, g2, b2, a2;
+
+ //fill
+ if (from->fill() || to->fill()) return false;
+
+ r = g = b = a = r2 = g2 = b2 = a2 = 0;
+
+ from->fillColor(&r, &g, &b, &a);
+ to->fillColor(&r2, &g2, &b2, &a2);
+
+ if (r != r2 || g != g2 || b != b2 || a != a2) return false;
+
+ //composition
+ if (from->composite(nullptr) != CompositeMethod::None) return false;
+ if (to->composite(nullptr) != CompositeMethod::None) return false;
+
+ //opacity
+ if (from->opacity() != to->opacity()) return false;
+
+ //transform
+ auto t1 = from->transform();
+ auto t2 = to->transform();
+
+ if (!mathEqual(t1.e11, t2.e11) || !mathEqual(t1.e12, t2.e12) || !mathEqual(t1.e13, t2.e13) ||
+ !mathEqual(t1.e21, t2.e21) || !mathEqual(t1.e22, t2.e22) || !mathEqual(t1.e23, t2.e23) ||
+ !mathEqual(t1.e31, t2.e31) || !mathEqual(t1.e32, t2.e32) || !mathEqual(t1.e33, t2.e33)) {
+ return false;
+ }
+
+ //stroke
+ r = g = b = a = r2 = g2 = b2 = a2 = 0;
+
+ from->strokeColor(&r, &g, &b, &a);
+ to->strokeColor(&r2, &g2, &b2, &a2);
+
+ if (r != r2 || g != g2 || b != b2 || a != a2) return false;
+
+ if (fabs(from->strokeWidth() - to->strokeWidth()) > FLT_EPSILON) return false;
+
+ //OPTIMIZE: Yet we can't merge outlining shapes unless we can support merging shapes feature.
+ if (from->strokeWidth() > 0 || to->strokeWidth() > 0) return false;
+
+ if (from->strokeCap() != to->strokeCap()) return false;
+ if (from->strokeJoin() != to->strokeJoin()) return false;
+ if (from->strokeDash(nullptr) > 0 || to->strokeDash(nullptr) > 0) return false;
+ if (from->strokeFill() || to->strokeFill()) return false;
+
+ //fill rule
+ if (from->fillRule() != to->fillRule()) return false;
+
+ //Good, identical shapes, we can merge them.
+ const PathCommand* cmds = nullptr;
+ auto cmdCnt = from->pathCommands(&cmds);
+
+ const Point* pts = nullptr;
+ auto ptsCnt = from->pathCoords(&pts);
+
+ to->appendPath(cmds, cmdCnt, pts, ptsCnt);
+
+ return true;
+}
+
+
+bool TvgSaver::saveEncoding(const std::string& path)
+{
+ if (!compress) return flushTo(path);
+
+ //Try encoding
+ auto uncompressed = buffer.data + headerSize;
+ auto uncompressedSize = buffer.count - headerSize;
+
+ uint32_t compressedSize, compressedSizeBits;
+
+ auto compressed = lzwEncode(uncompressed, uncompressedSize, &compressedSize, &compressedSizeBits);
+
+ //Failed compression.
+ if (!compressed) return flushTo(path);
+
+ //Optimization is ineffective.
+ if (compressedSize >= uncompressedSize) {
+ free(compressed);
+ return flushTo(path);
+ }
+
+ TVGLOG("TVG_SAVER", "%s, compressed: %d -> %d, saved rate: %3.2f%%", path.c_str(), uncompressedSize, compressedSize, (1 - ((float) compressedSize / (float) uncompressedSize)) * 100);
+
+ //Update compress size in the header.
+ uncompressed -= (TVG_HEADER_COMPRESS_SIZE + TVG_HEADER_RESERVED_LENGTH);
+
+ //Compression Flag
+ *uncompressed |= TVG_HEAD_FLAG_COMPRESSED;
+ uncompressed += TVG_HEADER_RESERVED_LENGTH;
+
+ //Uncompressed Size
+ memcpy(uncompressed, &uncompressedSize, TVG_HEADER_UNCOMPRESSED_SIZE);
+ uncompressed += TVG_HEADER_UNCOMPRESSED_SIZE;
+
+ //Comprssed Size
+ memcpy(uncompressed, &compressedSize, TVG_HEADER_COMPRESSED_SIZE);
+ uncompressed += TVG_HEADER_COMPRESSED_SIZE;
+
+ //Compressed Size Bits
+ memcpy(uncompressed, &compressedSizeBits, TVG_HEADER_COMPRESSED_SIZE_BITS);
+
+ //Good optimization, flush to file.
+ auto fp = _fopen(path.c_str(), "w+");
+ if (!fp) goto fail;
+
+ //write header
+ if (fwrite(buffer.data, SIZE(uint8_t), headerSize, fp) == 0) goto fail;
+
+ //write compressed data
+ if (fwrite(compressed, SIZE(uint8_t), compressedSize, fp) == 0) goto fail;
+
+ fclose(fp);
+ free(compressed);
+
+ return true;
+
+fail:
+ if (fp) fclose(fp);
+ if (compressed) free(compressed);
+ return false;
+}
+
+
+bool TvgSaver::flushTo(const std::string& path)
+{
+ auto fp = _fopen(path.c_str(), "w+");
+ if (!fp) return false;
+
+ if (fwrite(buffer.data, SIZE(uint8_t), buffer.count, fp) == 0) {
+ fclose(fp);
+ return false;
+ }
+ fclose(fp);
+
+ return true;
+}
+
+
+/* WARNING: Header format shall not changed! */
+bool TvgSaver::writeHeader()
+{
+ headerSize = TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + SIZE(vsize) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE;
+
+ buffer.grow(headerSize);
+
+ //1. Signature
+ auto ptr = buffer.ptr();
+ memcpy(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH);
+ ptr += TVG_HEADER_SIGNATURE_LENGTH;
+
+ //2. Version
+ memcpy(ptr, TVG_HEADER_VERSION, TVG_HEADER_VERSION_LENGTH);
+ ptr += TVG_HEADER_VERSION_LENGTH;
+
+ buffer.count += (TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH);
+
+ //3. View Size
+ writeData(vsize, SIZE(vsize));
+ ptr += SIZE(vsize);
+
+ //4. Reserved data + Compress size
+ memset(ptr, 0x00, TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE);
+ buffer.count += (TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE);
+
+ return true;
+}
+
+
+void TvgSaver::writeTag(TvgBinTag tag)
+{
+ buffer.grow(SIZE(TvgBinTag));
+ memcpy(buffer.ptr(), &tag, SIZE(TvgBinTag));
+ buffer.count += SIZE(TvgBinTag);
+}
+
+
+void TvgSaver::writeCount(TvgBinCounter cnt)
+{
+ buffer.grow(SIZE(TvgBinCounter));
+ memcpy(buffer.ptr(), &cnt, SIZE(TvgBinCounter));
+ buffer.count += SIZE(TvgBinCounter);
+}
+
+
+void TvgSaver::writeReservedCount(TvgBinCounter cnt)
+{
+ memcpy(buffer.ptr() - cnt - SIZE(TvgBinCounter), &cnt, SIZE(TvgBinCounter));
+}
+
+
+void TvgSaver::reserveCount()
+{
+ buffer.grow(SIZE(TvgBinCounter));
+ buffer.count += SIZE(TvgBinCounter);
+}
+
+
+TvgBinCounter TvgSaver::writeData(const void* data, TvgBinCounter cnt)
+{
+ buffer.grow(cnt);
+ memcpy(buffer.ptr(), data, cnt);
+ buffer.count += cnt;
+
+ return cnt;
+}
+
+
+TvgBinCounter TvgSaver::writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data)
+{
+ auto growCnt = SERIAL_DONE(cnt);
+
+ buffer.grow(growCnt);
+
+ auto ptr = buffer.ptr();
+
+ *ptr = tag;
+ ++ptr;
+
+ memcpy(ptr, &cnt, SIZE(TvgBinCounter));
+ ptr += SIZE(TvgBinCounter);
+
+ memcpy(ptr, data, cnt);
+ ptr += cnt;
+
+ buffer.count += growCnt;
+
+ return growCnt;
+}
+
+
+TvgBinCounter TvgSaver::writeTransform(const Matrix* transform, TvgBinTag tag)
+{
+ if (!mathIdentity(transform)) return writeTagProperty(tag, SIZE(Matrix), transform);
+ return 0;
+}
+
+
+TvgBinCounter TvgSaver::serializePaint(const Paint* paint, const Matrix* pTransform)
+{
+ TvgBinCounter cnt = 0;
+
+ //opacity
+ auto opacity = paint->opacity();
+ if (opacity < 255) {
+ cnt += writeTagProperty(TVG_TAG_PAINT_OPACITY, SIZE(opacity), &opacity);
+ }
+
+ //composite
+ const Paint* cmpTarget = nullptr;
+ auto cmpMethod = paint->composite(&cmpTarget);
+ if (cmpMethod != CompositeMethod::None && cmpTarget) {
+ cnt += serializeComposite(cmpTarget, cmpMethod, pTransform);
+ }
+
+ return cnt;
+}
+
+
+/* Propagate parents properties to the child so that we can skip saving the parent. */
+TvgBinCounter TvgSaver::serializeChild(const Paint* parent, const Paint* child, const Matrix* transform)
+{
+ const Paint* compTarget = nullptr;
+ auto compMethod = parent->composite(&compTarget);
+
+ /* If the parent & the only child have composition, we can't skip the parent...
+ Or if the parent has the transform and composition, we can't skip the parent... */
+ if (compMethod != CompositeMethod::None) {
+ if (transform || child->composite(nullptr) != CompositeMethod::None) return 0;
+ }
+
+ //propagate opacity
+ uint32_t opacity = parent->opacity();
+
+ if (opacity < 255) {
+ uint32_t tmp = (child->opacity() * opacity);
+ if (tmp > 0) tmp /= 255;
+ const_cast<Paint*>(child)->opacity(tmp);
+ }
+
+ //propagate composition
+ if (compTarget) const_cast<Paint*>(child)->composite(unique_ptr<Paint>(compTarget->duplicate()), compMethod);
+
+ return serialize(child, transform);
+}
+
+
+TvgBinCounter TvgSaver::serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform)
+{
+ auto it = this->iterator(scene);
+ if (it->count() == 0) {
+ delete(it);
+ return 0;
+ }
+
+ //Case - Only Child: Skip saving this scene.
+ if (it->count() == 1) {
+ auto cnt = serializeChild(scene, it->next(), cTransform);
+ if (cnt > 0) {
+ delete(it);
+ return cnt;
+ }
+ }
+
+ it->begin();
+
+ //Case - Delegator Scene: This scene is just a delegator, we can skip this:
+ if (scene->composite(nullptr) == CompositeMethod::None && scene->opacity() == 255) {
+ auto ret = serializeChildren(it, cTransform, false);
+ delete(it);
+ return ret;
+ }
+
+ //Case - Serialize Scene & its children
+ writeTag(TVG_TAG_CLASS_SCENE);
+ reserveCount();
+
+ auto cnt = serializeChildren(it, cTransform, true) + serializePaint(scene, pTransform);
+
+ delete(it);
+
+ writeReservedCount(cnt);
+
+ return SERIAL_DONE(cnt);
+}
+
+
+TvgBinCounter TvgSaver::serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform)
+{
+ const Fill::ColorStop* stops = nullptr;
+ auto stopsCnt = fill->colorStops(&stops);
+ if (!stops || stopsCnt == 0) return 0;
+
+ writeTag(tag);
+ reserveCount();
+
+ TvgBinCounter cnt = 0;
+
+ //radial fill
+ if (fill->identifier() == TVG_CLASS_ID_RADIAL) {
+ float args[3];
+ static_cast<const RadialGradient*>(fill)->radial(args, args + 1, args + 2);
+ cnt += writeTagProperty(TVG_TAG_FILL_RADIAL_GRADIENT, SIZE(args), args);
+ //linear fill
+ } else {
+ float args[4];
+ static_cast<const LinearGradient*>(fill)->linear(args, args + 1, args + 2, args + 3);
+ cnt += writeTagProperty(TVG_TAG_FILL_LINEAR_GRADIENT, SIZE(args), args);
+ }
+
+ if (auto flag = static_cast<TvgBinFlag>(fill->spread()))
+ cnt += writeTagProperty(TVG_TAG_FILL_FILLSPREAD, SIZE(TvgBinFlag), &flag);
+ cnt += writeTagProperty(TVG_TAG_FILL_COLORSTOPS, stopsCnt * SIZE(Fill::ColorStop), stops);
+
+ auto gTransform = fill->transform();
+ if (pTransform) gTransform = mathMultiply(pTransform, &gTransform);
+
+ cnt += writeTransform(&gTransform, TVG_TAG_FILL_TRANSFORM);
+
+ writeReservedCount(cnt);
+
+ return SERIAL_DONE(cnt);
+}
+
+
+TvgBinCounter TvgSaver::serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform)
+{
+ writeTag(TVG_TAG_SHAPE_STROKE);
+ reserveCount();
+
+ //width
+ auto width = shape->strokeWidth();
+ if (preTransform) width *= sqrtf(powf(pTransform->e11, 2.0f) + powf(pTransform->e21, 2.0f)); //we know x/y scaling factors are same.
+ auto cnt = writeTagProperty(TVG_TAG_SHAPE_STROKE_WIDTH, SIZE(width), &width);
+
+ //cap
+ if (auto flag = static_cast<TvgBinFlag>(shape->strokeCap()))
+ cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_CAP, SIZE(TvgBinFlag), &flag);
+
+ //join
+ if (auto flag = static_cast<TvgBinFlag>(shape->strokeJoin()))
+ cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_JOIN, SIZE(TvgBinFlag), &flag);
+
+ //fill
+ if (auto fill = shape->strokeFill()) {
+ cnt += serializeFill(fill, TVG_TAG_SHAPE_STROKE_FILL, (preTransform ? pTransform : nullptr));
+ } else {
+ uint8_t color[4] = {0, 0, 0, 0};
+ shape->strokeColor(color, color + 1, color + 2, color + 3);
+ cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_COLOR, SIZE(color), &color);
+ }
+
+ //dash
+ const float* dashPattern = nullptr;
+ auto dashCnt = shape->strokeDash(&dashPattern);
+ if (dashPattern && dashCnt > 0) {
+ TvgBinCounter dashCntSize = SIZE(dashCnt);
+ TvgBinCounter dashPtrnSize = dashCnt * SIZE(dashPattern[0]);
+
+ writeTag(TVG_TAG_SHAPE_STROKE_DASHPTRN);
+ writeCount(dashCntSize + dashPtrnSize);
+ cnt += writeData(&dashCnt, dashCntSize);
+ cnt += writeData(dashPattern, dashPtrnSize);
+ cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
+ }
+
+ writeReservedCount(cnt);
+
+ return SERIAL_DONE(cnt);
+}
+
+
+TvgBinCounter TvgSaver::serializePath(const Shape* shape, const Matrix* transform, bool preTransform)
+{
+ const PathCommand* cmds = nullptr;
+ auto cmdCnt = shape->pathCommands(&cmds);
+ const Point* pts = nullptr;
+ auto ptsCnt = shape->pathCoords(&pts);
+
+ if (!cmds || !pts || cmdCnt == 0 || ptsCnt == 0) return 0;
+
+ writeTag(TVG_TAG_SHAPE_PATH);
+ reserveCount();
+
+ /* Reduce the binary size.
+ Convert PathCommand(4 bytes) to TvgBinFlag(1 byte) */
+ TvgBinFlag* outCmds = (TvgBinFlag*)alloca(SIZE(TvgBinFlag) * cmdCnt);
+ for (uint32_t i = 0; i < cmdCnt; ++i) {
+ outCmds[i] = static_cast<TvgBinFlag>(cmds[i]);
+ }
+
+ auto cnt = writeData(&cmdCnt, SIZE(cmdCnt));
+ cnt += writeData(&ptsCnt, SIZE(ptsCnt));
+ cnt += writeData(outCmds, SIZE(TvgBinFlag) * cmdCnt);
+
+ //transform?
+ if (preTransform) {
+ if (!mathEqual(transform->e11, 1.0f) || !mathZero(transform->e12) || !mathZero(transform->e13) ||
+ !mathZero(transform->e21) || !mathEqual(transform->e22, 1.0f) || !mathZero(transform->e23) ||
+ !mathZero(transform->e31) || !mathZero(transform->e32) || !mathEqual(transform->e33, 1.0f)) {
+ auto p = const_cast<Point*>(pts);
+ for (uint32_t i = 0; i < ptsCnt; ++i) mathMultiply(p++, transform);
+ }
+ }
+
+ cnt += writeData(pts, ptsCnt * SIZE(pts[0]));
+
+ writeReservedCount(cnt);
+
+ return SERIAL_DONE(cnt);
+}
+
+
+TvgBinCounter TvgSaver::serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform)
+{
+ writeTag(TVG_TAG_CLASS_SHAPE);
+ reserveCount();
+ TvgBinCounter cnt = 0;
+
+ //fill rule
+ if (auto flag = static_cast<TvgBinFlag>(shape->fillRule())) {
+ cnt = writeTagProperty(TVG_TAG_SHAPE_FILLRULE, SIZE(TvgBinFlag), &flag);
+ }
+
+ //the pre-transformation can't be applied in the case when the stroke is dashed or irregulary scaled
+ bool preTransform = true;
+
+ //stroke
+ if (shape->strokeWidth() > 0) {
+ uint8_t color[4] = {0, 0, 0, 0};
+ shape->strokeColor(color, color + 1, color + 2, color + 3);
+ auto fill = shape->strokeFill();
+ if (fill || color[3] > 0) {
+ if (!mathEqual(cTransform->e11, cTransform->e22) || (mathZero(cTransform->e11) && !mathEqual(cTransform->e12, cTransform->e21)) || shape->strokeDash(nullptr) > 0) preTransform = false;
+ cnt += serializeStroke(shape, cTransform, preTransform);
+ }
+ }
+
+ //fill
+ if (auto fill = shape->fill()) {
+ cnt += serializeFill(fill, TVG_TAG_SHAPE_FILL, (preTransform ? cTransform : nullptr));
+ } else {
+ uint8_t color[4] = {0, 0, 0, 0};
+ shape->fillColor(color, color + 1, color + 2, color + 3);
+ if (color[3] > 0) cnt += writeTagProperty(TVG_TAG_SHAPE_COLOR, SIZE(color), color);
+ }
+
+ cnt += serializePath(shape, cTransform, preTransform);
+
+ if (!preTransform) cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
+ cnt += serializePaint(shape, pTransform);
+
+ writeReservedCount(cnt);
+
+ return SERIAL_DONE(cnt);
+}
+
+
+/* Picture has either a vector scene or a bitmap. */
+TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform)
+{
+ auto it = this->iterator(picture);
+
+ //Case - Vector Scene:
+ if (it->count() == 1) {
+ auto cnt = serializeChild(picture, it->next(), cTransform);
+ //Only child, Skip to save Picture...
+ if (cnt > 0) {
+ delete(it);
+ return cnt;
+ /* Unfortunately, we can't skip the Picture because it might have a compositor,
+ Serialize Scene(instead of the Picture) & its scene. */
+ } else {
+ writeTag(TVG_TAG_CLASS_SCENE);
+ reserveCount();
+ auto cnt = serializeChildren(it, cTransform, true) + serializePaint(picture, pTransform);
+ writeReservedCount(cnt);
+ delete(it);
+ return SERIAL_DONE(cnt);
+ }
+ }
+ delete(it);
+
+ //Case - Bitmap Image:
+ uint32_t w, h;
+ auto pixels = picture->data(&w, &h);
+ if (!pixels) return 0;
+
+ writeTag(TVG_TAG_CLASS_PICTURE);
+ reserveCount();
+
+ TvgBinCounter cnt = 0;
+ TvgBinCounter sizeCnt = SIZE(w);
+ TvgBinCounter imgSize = w * h * SIZE(pixels[0]);
+
+ writeTag(TVG_TAG_PICTURE_RAW_IMAGE);
+ writeCount(2 * sizeCnt + imgSize);
+
+ cnt += writeData(&w, sizeCnt);
+ cnt += writeData(&h, sizeCnt);
+ cnt += writeData(pixels, imgSize);
+ cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
+
+ //Bitmap picture needs the transform info.
+ cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
+
+ cnt += serializePaint(picture, pTransform);
+
+ writeReservedCount(cnt);
+
+ return SERIAL_DONE(cnt);
+}
+
+
+TvgBinCounter TvgSaver::serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform)
+{
+ writeTag(TVG_TAG_PAINT_CMP_TARGET);
+ reserveCount();
+
+ auto flag = static_cast<TvgBinFlag>(cmpMethod);
+ auto cnt = writeTagProperty(TVG_TAG_PAINT_CMP_METHOD, SIZE(TvgBinFlag), &flag);
+
+ cnt += serialize(cmpTarget, pTransform, true);
+
+ writeReservedCount(cnt);
+
+ return SERIAL_DONE(cnt);
+}
+
+
+TvgBinCounter TvgSaver::serializeChildren(Iterator* it, const Matrix* pTransform, bool reserved)
+{
+ TvgBinCounter cnt = 0;
+
+ //Merging shapes. the result is written in the children.
+ Array<const Paint*> children;
+ children.reserve(it->count());
+ children.push(it->next());
+
+ while (auto child = it->next()) {
+ if (child->identifier() == TVG_CLASS_ID_SHAPE) {
+ //only dosable if the previous child is a shape.
+ auto target = children.ptr() - 1;
+ if ((*target)->identifier() == TVG_CLASS_ID_SHAPE) {
+ if (_merge((Shape*)child, (Shape*)*target)) {
+ continue;
+ }
+ }
+ }
+ children.push(child);
+ }
+
+ //The children of a reserved scene
+ if (reserved && children.count > 1) {
+ cnt += writeTagProperty(TVG_TAG_SCENE_RESERVEDCNT, SIZE(children.count), &children.count);
+ }
+
+ //Serialize merged children.
+ auto child = children.data;
+ for (uint32_t i = 0; i < children.count; ++i, ++child) {
+ cnt += serialize(*child, pTransform);
+ }
+
+ return cnt;
+}
+
+
+TvgBinCounter TvgSaver::serialize(const Paint* paint, const Matrix* pTransform, bool compTarget)
+{
+ if (!paint) return 0;
+
+ //Invisible paint, no point to save it if the paint is not the composition target...
+ if (!compTarget && paint->opacity() == 0) return 0;
+
+ auto transform = const_cast<Paint*>(paint)->transform();
+ if (pTransform) transform = mathMultiply(pTransform, &transform);
+
+ switch (paint->identifier()) {
+ case TVG_CLASS_ID_SHAPE: return serializeShape(static_cast<const Shape*>(paint), pTransform, &transform);
+ case TVG_CLASS_ID_SCENE: return serializeScene(static_cast<const Scene*>(paint), pTransform, &transform);
+ case TVG_CLASS_ID_PICTURE: return serializePicture(static_cast<const Picture*>(paint), pTransform, &transform);
+ }
+
+ return 0;
+}
+
+
+void TvgSaver::run(unsigned tid)
+{
+ if (!writeHeader()) return;
+
+ //Serialize Root Paint, without its transform.
+ Matrix transform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+
+ if (paint->opacity() > 0) {
+ switch (paint->identifier()) {
+ case TVG_CLASS_ID_SHAPE: {
+ serializeShape(static_cast<const Shape*>(paint), nullptr, &transform);
+ break;
+ }
+ case TVG_CLASS_ID_SCENE: {
+ serializeScene(static_cast<const Scene*>(paint), nullptr, &transform);
+ break;
+ }
+ case TVG_CLASS_ID_PICTURE: {
+ serializePicture(static_cast<const Picture*>(paint), nullptr, &transform);
+ break;
+ }
+ }
+ }
+
+ if (!saveEncoding(path)) return;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+TvgSaver::~TvgSaver()
+{
+ close();
+}
+
+
+bool TvgSaver::close()
+{
+ this->done();
+
+ if (paint) {
+ delete(paint);
+ paint = nullptr;
+ }
+ if (path) {
+ free(path);
+ path = nullptr;
+ }
+ buffer.reset();
+ return true;
+}
+
+
+bool TvgSaver::save(Paint* paint, const string& path, bool compress)
+{
+ close();
+
+ float x, y;
+ x = y = 0;
+ paint->bounds(&x, &y, &vsize[0], &vsize[1], false);
+
+ //cut off the negative space
+ if (x < 0) vsize[0] += x;
+ if (y < 0) vsize[1] += y;
+
+ if (vsize[0] < FLT_EPSILON || vsize[1] < FLT_EPSILON) {
+ TVGLOG("TVG_SAVER", "Saving paint(%p) has zero view size.", paint);
+ return false;
+ }
+
+ this->path = strdup(path.c_str());
+ if (!this->path) return false;
+
+ this->paint = paint;
+ this->compress = compress;
+
+ TaskScheduler::request(this);
+
+ return true;
+}
diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h
new file mode 100644
index 0000000000..27186b5d4a
--- /dev/null
+++ b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_TVGSAVER_H_
+#define _TVG_TVGSAVER_H_
+
+#include "tvgArray.h"
+#include "tvgBinaryDesc.h"
+#include "tvgTaskScheduler.h"
+
+namespace tvg
+{
+
+class TvgSaver : public SaveModule, public Task
+{
+private:
+ Array<TvgBinByte> buffer;
+ Paint* paint = nullptr;
+ char *path = nullptr;
+ uint32_t headerSize;
+ float vsize[2] = {0.0f, 0.0f};
+ bool compress;
+
+ bool flushTo(const std::string& path);
+ bool saveEncoding(const std::string& path);
+ void reserveCount();
+
+ bool writeHeader();
+ bool writeViewSize();
+ void writeTag(TvgBinTag tag);
+ void writeCount(TvgBinCounter cnt);
+ void writeReservedCount(TvgBinCounter cnt);
+ TvgBinCounter writeData(const void* data, TvgBinCounter cnt);
+ TvgBinCounter writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data);
+ TvgBinCounter writeTransform(const Matrix* transform, TvgBinTag tag);
+
+ TvgBinCounter serialize(const Paint* paint, const Matrix* pTransform, bool compTarget = false);
+ TvgBinCounter serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform);
+ TvgBinCounter serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform);
+ TvgBinCounter serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform);
+ TvgBinCounter serializePaint(const Paint* paint, const Matrix* pTransform);
+ TvgBinCounter serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform);
+ TvgBinCounter serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform);
+ TvgBinCounter serializePath(const Shape* shape, const Matrix* transform, bool preTransform);
+ TvgBinCounter serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform);
+ TvgBinCounter serializeChildren(Iterator* it, const Matrix* transform, bool reserved);
+ TvgBinCounter serializeChild(const Paint* parent, const Paint* child, const Matrix* pTransform);
+
+public:
+ ~TvgSaver();
+
+ bool save(Paint* paint, const string& path, bool compress) override;
+ bool close() override;
+ void run(unsigned tid) override;
+};
+
+}
+
+#endif //_TVG_SAVE_MODULE_H_
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
new file mode 100755
index 0000000000..ce3d5eed1c
--- /dev/null
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -0,0 +1,28 @@
+VERSION=0.7.1
+rm -rf AUTHORS inc LICENSE src *.zip
+curl -L -O https://github.com/Samsung/thorvg/archive/refs/tags/v$VERSION.zip
+bsdtar --strip-components=1 -xvf *.zip
+rm *.zip
+rm -rf .github docs pc res test tools .git* *.md *.txt wasm_build.sh
+find . -type f -name 'meson.build' -delete
+rm -rf src/bin src/bindings src/examples src/wasm
+rm -rf src/lib/gl_engine tvgcompat
+cat << EOF > inc/config.h
+#ifndef THORVG_CONFIG_H
+#define THORVG_CONFIG_H
+
+#define THORVG_SW_RASTER_SUPPORT 1
+
+#define THORVG_SVG_LOADER_SUPPORT 1
+
+#define THORVG_PNG_LOADER_SUPPORT 1
+
+#define THORVG_TVG_LOADER_SUPPORT 1
+
+#define THORVG_TVG_SAVER_SUPPORT 1
+
+#define THORVG_JPG_LOADER_SUPPORT 1
+
+#define THORVG_VERSION_STRING "$VERSION"
+#endif
+EOF
diff --git a/version.py b/version.py
index 66cb145528..f9aa1dd0f4 100644
--- a/version.py
+++ b/version.py
@@ -3,7 +3,7 @@ name = "Godot Engine"
major = 4
minor = 0
patch = 0
-status = "dev"
+status = "alpha"
module_config = ""
year = 2022
website = "https://godotengine.org"