summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct2
-rw-r--r--core/globals.cpp4
-rw-r--r--core/helper/value_evaluator.h43
-rw-r--r--core/io/http_client.cpp1
-rw-r--r--core/math/math_funcs.cpp4
-rw-r--r--demos/LICENSE.md27
-rw-r--r--doc/base/classes.xml657
-rwxr-xr-xmethods.py138
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp6
-rw-r--r--platform/android/detect.py4
-rw-r--r--platform/android/export/export.cpp2
-rw-r--r--platform/bb10/export/export.cpp2
-rw-r--r--platform/iphone/Appirater.m553
-rw-r--r--platform/iphone/AppiraterDelegate.h23
-rw-r--r--platform/iphone/SCsub7
-rw-r--r--platform/iphone/ios.h20
-rw-r--r--platform/iphone/ios.mm36
-rw-r--r--platform/iphone/os_iphone.cpp3
-rw-r--r--platform/javascript/export/export.cpp2
-rw-r--r--platform/osx/export/export.cpp2
-rw-r--r--platform/osx/godot_main_osx.mm10
-rw-r--r--platform/osx/os_osx.mm2
-rw-r--r--platform/windows/os_windows.cpp5
-rw-r--r--platform/windows/os_windows.h1
-rw-r--r--scene/2d/physics_body_2d.cpp39
-rw-r--r--scene/2d/physics_body_2d.h10
-rw-r--r--scene/animation/animation_tree_player.cpp90
-rw-r--r--scene/animation/animation_tree_player.h8
-rw-r--r--scene/gui/button.cpp4
-rw-r--r--scene/gui/button_array.cpp27
-rw-r--r--scene/gui/dialogs.cpp2
-rw-r--r--scene/gui/file_dialog.cpp2
-rw-r--r--scene/gui/range.cpp4
-rw-r--r--scene/gui/tabs.cpp181
-rw-r--r--scene/gui/text_edit.cpp54
-rw-r--r--scene/gui/text_edit.h3
-rw-r--r--scene/gui/tree.cpp26
-rw-r--r--scene/gui/tree.h6
-rw-r--r--scene/register_scene_types.cpp15
-rw-r--r--scene/resources/default_theme/default_theme.cpp18
-rw-r--r--scene/resources/dynamic_font.cpp519
-rw-r--r--scene/resources/dynamic_font.h175
-rw-r--r--scene/resources/font.cpp239
-rw-r--r--scene/resources/font.h74
-rw-r--r--scene/resources/stb_truetype.h3267
-rw-r--r--servers/physics_2d/body_2d_sw.cpp17
-rw-r--r--servers/physics_2d/body_2d_sw.h12
-rw-r--r--servers/physics_2d/physics_2d_server_sw.cpp9
-rw-r--r--servers/physics_2d/physics_2d_server_sw.h2
-rw-r--r--servers/physics_2d/physics_2d_server_wrap_mt.h1
-rw-r--r--servers/physics_2d_server.cpp2
-rw-r--r--servers/physics_2d_server.h5
-rw-r--r--tools/editor/editor_dir_dialog.cpp1
-rw-r--r--tools/editor/editor_file_dialog.cpp2
-rw-r--r--tools/editor/editor_fonts.cpp10
-rw-r--r--tools/editor/editor_import_export.cpp45
-rw-r--r--tools/editor/editor_import_export.h15
-rw-r--r--tools/editor/editor_node.cpp6
-rw-r--r--tools/editor/editor_node.h1
-rw-r--r--tools/editor/icons/icon_bitmap_font.pngbin0 -> 362 bytes
-rw-r--r--tools/editor/icons/icon_dynamic_font.pngbin0 -> 414 bytes
-rw-r--r--tools/editor/icons/icon_dynamic_font_data.pngbin0 -> 397 bytes
-rw-r--r--tools/editor/io_plugins/editor_font_import_plugin.cpp42
-rw-r--r--tools/editor/io_plugins/editor_font_import_plugin.h2
-rw-r--r--tools/editor/plugins/animation_player_editor_plugin.cpp3
-rw-r--r--tools/editor/plugins/animation_tree_editor_plugin.cpp52
-rw-r--r--tools/editor/plugins/canvas_item_editor_plugin.cpp17
-rw-r--r--tools/editor/plugins/shader_editor_plugin.cpp38
-rw-r--r--tools/editor/plugins/shader_editor_plugin.h2
-rw-r--r--tools/editor/project_export.cpp11
-rw-r--r--tools/editor/project_export.h1
-rw-r--r--tools/editor/project_manager.cpp2
-rw-r--r--tools/editor/project_settings.cpp42
-rw-r--r--tools/editor/property_editor.cpp189
-rw-r--r--tools/editor/property_editor.h28
-rw-r--r--tools/editor/script_editor_debugger.cpp2
76 files changed, 6061 insertions, 815 deletions
diff --git a/SConstruct b/SConstruct
index 6d6fe2859c..a2c5edf5e0 100644
--- a/SConstruct
+++ b/SConstruct
@@ -23,7 +23,7 @@ platform_exporters=[]
global_defaults=[]
for x in glob.glob("platform/*"):
- if (not os.path.isdir(x)):
+ if (not os.path.isdir(x) or not os.path.exists(x+"/detect.py")):
continue
tmppath="./"+x
diff --git a/core/globals.cpp b/core/globals.cpp
index c0c1171ae6..020e05fb8f 100644
--- a/core/globals.cpp
+++ b/core/globals.cpp
@@ -54,7 +54,7 @@ String Globals::localize_path(const String& p_path) const {
if (resource_path=="")
return p_path; //not initialied yet
- if (p_path.find(":/") != -1)
+ if (p_path.begins_with("res://") || p_path.begins_with("user://"))
return p_path.simplify_path();
@@ -82,6 +82,8 @@ String Globals::localize_path(const String& p_path) const {
if (sep == -1) {
return "res://"+path;
};
+
+
String parent = path.substr(0, sep);
String plocal = localize_path(parent);
diff --git a/core/helper/value_evaluator.h b/core/helper/value_evaluator.h
new file mode 100644
index 0000000000..a03602bc61
--- /dev/null
+++ b/core/helper/value_evaluator.h
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* value_evaluator.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 VALUE_EVALUATOR_H
+#define VALUE_EVALUATOR_H
+
+#include "core/object.h"
+
+class ValueEvaluator : public Object {
+
+ OBJ_TYPE(ValueEvaluator, Object);
+public:
+ virtual double eval(const String& p_text) {
+ return p_text.to_double();
+ }
+};
+
+#endif // VALUE_EVALUATOR_H
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 8c58e0ba5e..d7098b4c43 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -303,6 +303,7 @@ Error HTTPClient::poll(){
chunked=false;
body_left=0;
chunk_left=0;
+ response_str.clear();
response_headers.clear();
response_num = RESPONSE_OK;
diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp
index 07f114725d..0fbd031214 100644
--- a/core/math/math_funcs.cpp
+++ b/core/math/math_funcs.cpp
@@ -135,18 +135,20 @@ double Math::rad2deg(double p_y) {
double Math::round(double p_val) {
- if (p_val>0) {
+ if (p_val>=0) {
return ::floor(p_val+0.5);
} else {
p_val=-p_val;
return -::floor(p_val+0.5);
}
}
+
double Math::asin(double p_x) {
return ::asin(p_x);
}
+
double Math::acos(double p_x) {
return ::acos(p_x);
diff --git a/demos/LICENSE.md b/demos/LICENSE.md
new file mode 100644
index 0000000000..2f3e879c8c
--- /dev/null
+++ b/demos/LICENSE.md
@@ -0,0 +1,27 @@
+ GODOT ENGINE
+ http://www.godotengine.org
+
+************************************************************************
+
+ Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur.
+
+ 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/doc/base/classes.xml b/doc/base/classes.xml
index de19b1c8ca..774cea50fb 100644
--- a/doc/base/classes.xml
+++ b/doc/base/classes.xml
@@ -2041,6 +2041,8 @@
<argument index="2" name="action" type="String" default="&quot;&quot;">
</argument>
<description>
+ Add custom button to the dialog and return the created button.
+ The button titled with [i]text[/i] and the [i]action[/i] will be passed to [custom_action] signal when it is pressed.
</description>
</method>
<method name="add_cancel">
@@ -2049,6 +2051,7 @@
<argument index="0" name="name" type="String">
</argument>
<description>
+ Add custom cancel button to the dialog and return the created button.
</description>
</method>
<method name="register_text_enter">
@@ -2202,6 +2205,7 @@
<signals>
<signal name="frame_changed">
<description>
+ Emmited when frame is changed.
</description>
</signal>
</signals>
@@ -2900,6 +2904,12 @@
</method>
</methods>
<signals>
+ <signal name="animation_started">
+ <argument index="0" name="name" type="String">
+ </argument>
+ <description>
+ </description>
+ </signal>
<signal name="animation_changed">
<argument index="0" name="old_name" type="String">
</argument>
@@ -3023,6 +3033,16 @@
<description>
</description>
</method>
+ <method name="animation_node_set_filter_path">
+ <argument index="0" name="id" type="String">
+ </argument>
+ <argument index="1" name="path" type="NodePath">
+ </argument>
+ <argument index="2" name="enable" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="oneshot_node_set_fadein_time">
<argument index="0" name="id" type="String">
</argument>
@@ -4162,10 +4182,12 @@
</method>
<method name="pop_back">
<description>
+ Remove the last element of the array.
</description>
</method>
<method name="pop_front">
<description>
+ Remove the first element of the array.
</description>
</method>
<method name="push_back">
@@ -4179,6 +4201,7 @@
<argument index="0" name="value" type="var">
</argument>
<description>
+ Add an element at the beginning of the array.
</description>
</method>
<method name="remove">
@@ -5415,12 +5438,14 @@
<methods>
<method name="_pressed" qualifiers="virtual">
<description>
+ Called when button is pressed.
</description>
</method>
<method name="_toggled" qualifiers="virtual">
<argument index="0" name="pressed" type="bool">
</argument>
<description>
+ Called when button is toggled (only if toggle_mode is active).
</description>
</method>
<method name="set_pressed">
@@ -5441,6 +5466,7 @@
<return type="bool">
</return>
<description>
+ Return true if mouse entered the button before it exit.
</description>
</method>
<method name="set_toggle_mode">
@@ -5496,6 +5522,7 @@
<signals>
<signal name="released">
<description>
+ This signal is emitted when the button was released.
</description>
</signal>
<signal name="toggled">
@@ -5513,12 +5540,16 @@
</signals>
<constants>
<constant name="DRAW_NORMAL" value="0">
+ The normal state (i.e. not pressed, not hovered, not toggled and enabled) of buttons.
</constant>
<constant name="DRAW_PRESSED" value="1">
+ The state of buttons are pressed.
</constant>
<constant name="DRAW_HOVER" value="2">
+ The state of buttons are hovered.
</constant>
<constant name="DRAW_DISABLED" value="3">
+ The state of buttons are disabled.
</constant>
</constants>
</class>
@@ -5580,6 +5611,130 @@
<constants>
</constants>
</class>
+<class name="BitmapFont" inherits="Font" category="Core">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <methods>
+ <method name="create_from_fnt">
+ <return type="int">
+ </return>
+ <argument index="0" name="path" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_height">
+ <argument index="0" name="px" type="float">
+ </argument>
+ <description>
+ Set the total font height (ascent plus descent) in pixels.
+ </description>
+ </method>
+ <method name="set_ascent">
+ <argument index="0" name="px" type="float">
+ </argument>
+ <description>
+ Set the font ascent (number of pixels above the baseline).
+ </description>
+ </method>
+ <method name="add_kerning_pair">
+ <argument index="0" name="char_a" type="int">
+ </argument>
+ <argument index="1" name="char_b" type="int">
+ </argument>
+ <argument index="2" name="kerning" type="int">
+ </argument>
+ <description>
+ Add a kerning pair to the [BitmapFont] as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character.
+ </description>
+ </method>
+ <method name="get_kerning_pair" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="char_a" type="int">
+ </argument>
+ <argument index="1" name="char_b" type="int">
+ </argument>
+ <description>
+ Return a kerning pair as a difference.
+ </description>
+ </method>
+ <method name="add_texture">
+ <argument index="0" name="texture" type="Texture">
+ </argument>
+ <description>
+ Add a texture to the [BitmapFont].
+ </description>
+ </method>
+ <method name="add_char">
+ <argument index="0" name="character" type="int">
+ </argument>
+ <argument index="1" name="texture" type="int">
+ </argument>
+ <argument index="2" name="rect" type="Rect2">
+ </argument>
+ <argument index="3" name="align" type="Vector2" default="Vector2(0,0)">
+ </argument>
+ <argument index="4" name="advance" type="float" default="-1">
+ </argument>
+ <description>
+ Add a character to the font, where [i]character[/i] is the unicode value, [i]texture[/i] is the texture index, [i]rect[/i] is the region in the texture (in pixels!), [i]align[/i] is the (optional) alignment for the character and [i]advance[/i] is the (optional) advance.
+ </description>
+ </method>
+ <method name="get_texture_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_texture" qualifiers="const">
+ <return type="Texture">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_char_size" qualifiers="const">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="char" type="int">
+ </argument>
+ <argument index="1" name="next" type="int" default="0">
+ </argument>
+ <description>
+ Return the size of a character, optionally taking kerning into account if the next character is provided.
+ </description>
+ </method>
+ <method name="set_distance_field_hint">
+ <argument index="0" name="enable" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="clear">
+ <description>
+ Clear all the font data.
+ </description>
+ </method>
+ <method name="set_fallback">
+ <argument index="0" name="fallback" type="Object">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_fallback" qualifiers="const">
+ <return type="Object">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <constants>
+ </constants>
+</class>
<class name="BoneAttachment" inherits="Spatial" category="Core">
<brief_description>
</brief_description>
@@ -5602,27 +5757,34 @@
<argument index="0" name="begin" type="bool">
</argument>
<description>
+ Add a control to the box as a spacer.
+ If [i]begin[/i] is true the spacer control will be inserted in front of other children.
</description>
</method>
<method name="get_alignment" qualifiers="const">
<return type="int">
</return>
<description>
+ Return the alignment of children in the container.
</description>
</method>
<method name="set_alignment">
<argument index="0" name="alignment" type="int">
</argument>
<description>
+ Set the alignment of children in the container(Must be one of ALIGN_BEGIN, ALIGN_CENTER or ALIGN_END).
</description>
</method>
</methods>
<constants>
<constant name="ALIGN_BEGIN" value="0">
+ Align children with beginning of the container.
</constant>
<constant name="ALIGN_CENTER" value="1">
+ Align children with center of the container.
</constant>
<constant name="ALIGN_END" value="2">
+ Align children with end of the container.
</constant>
</constants>
</class>
@@ -5678,12 +5840,14 @@
<argument index="0" name="texture" type="Texture">
</argument>
<description>
+ Set the icon that will be displayed next to the text inside the button area.
</description>
</method>
<method name="get_button_icon" qualifiers="const">
<return type="Texture">
</return>
<description>
+ Return the button icon.
</description>
</method>
<method name="set_flat">
@@ -5711,23 +5875,34 @@
<argument index="0" name="align" type="int">
</argument>
<description>
+ Set the text alignment policy, using one of the ALIGN_* constants.
</description>
</method>
<method name="get_text_align" qualifiers="const">
<return type="int">
</return>
<description>
+ Return the text alignment policy.
</description>
</method>
<method name="is_flat" qualifiers="const">
<return type="bool">
</return>
<description>
- Return the state of the [i]flat[/i] property (see [method set_flat])
+ Return the state of the [i]flat[/i] property (see [method set_flat]).
</description>
</method>
</methods>
<constants>
+ <constant name="ALIGN_LEFT" value="0">
+ Align the text to the left.
+ </constant>
+ <constant name="ALIGN_CENTER" value="1">
+ Center the text.
+ </constant>
+ <constant name="ALIGN_RIGHT" value="2">
+ Align the text to the right.
+ </constant>
</constants>
<theme_items>
<theme_item name="hseparation" type="int">
@@ -5759,57 +5934,60 @@
Array of Buttons.
</brief_description>
<description>
- Array of Buttons. A Button array is useful to have an array of buttons laid out vertically or horizontally. Only one can be selected. This is useful for joy pad based interfaces and option menus.
+ Array of Buttons. A ButtonArray is useful to have an array of buttons laid out vertically or horizontally. Only one button can be selected, and is referenced by its index in the array (first button is 0, second button is 1, etc.).
+ This is useful [i]e.g.[/i] for joypad-friendly interfaces and option menus.
</description>
<methods>
<method name="add_button">
<argument index="0" name="text" type="String">
</argument>
<description>
- Add a new button.
+ Append a new button to the array, with the specified text.
</description>
</method>
<method name="add_icon_button">
- <argument index="0" name="icon" type="Object">
+ <argument index="0" name="icon" type="Texture">
</argument>
<argument index="1" name="text" type="String" default="&quot;&quot;">
</argument>
<description>
+ Append a new button to the array, with the specified icon and text.
</description>
</method>
<method name="set_button_text">
- <argument index="0" name="button" type="int">
+ <argument index="0" name="button_idx" type="int">
</argument>
<argument index="1" name="text" type="String">
</argument>
<description>
+ Define the text of the specified button.
</description>
</method>
<method name="set_button_icon">
- <argument index="0" name="button" type="int">
+ <argument index="0" name="button_idx" type="int">
</argument>
- <argument index="1" name="icon" type="Object">
+ <argument index="1" name="icon" type="Texture">
</argument>
<description>
- Set the icon of an existing button.
+ Set the icon of the specified button.
</description>
</method>
<method name="get_button_text" qualifiers="const">
<return type="String">
</return>
- <argument index="0" name="button" type="int">
+ <argument index="0" name="button_idx" type="int">
</argument>
<description>
- Return the text of an existing button.
+ Return the text of the specified button.
</description>
</method>
<method name="get_button_icon" qualifiers="const">
- <return type="Object">
+ <return type="Texture">
</return>
- <argument index="0" name="button" type="int">
+ <argument index="0" name="button_idx" type="int">
</argument>
<description>
- Return the icon of an existing button.
+ Return the icon of the specified button.
</description>
</method>
<method name="get_button_count" qualifiers="const">
@@ -5823,42 +6001,42 @@
<return type="int">
</return>
<description>
- Return the currently selected button in the array.
+ Return the index of the currently selected button in the array.
</description>
</method>
<method name="get_hovered" qualifiers="const">
<return type="int">
</return>
<description>
- Return the currently hovered button in the array.
+ Return the index of the currently hovered button in the array.
</description>
</method>
<method name="set_selected">
- <argument index="0" name="button" type="int">
+ <argument index="0" name="button_idx" type="int">
</argument>
<description>
- Select a button in the array.
+ Select a button in the array based on the given index.
</description>
</method>
<method name="erase_button">
- <argument index="0" name="button" type="int">
+ <argument index="0" name="button_idx" type="int">
</argument>
<description>
- Remove a button in the array, by index.
+ Remove the specified button in the array.
</description>
</method>
<method name="clear">
<description>
- Clear the button array.
+ Remove all buttons from the array.
</description>
</method>
</methods>
<signals>
<signal name="button_selected">
- <argument index="0" name="button" type="int">
+ <argument index="0" name="button_idx" type="int">
</argument>
<description>
- A Button was selected (returns the index).
+ A button has been selected, its index is given as the argument.
</description>
</signal>
</signals>
@@ -6983,20 +7161,24 @@
</class>
<class name="CanvasModulate" inherits="Node2D" category="Core">
<brief_description>
+ Tint the entire canvas
</brief_description>
<description>
+ CanvasModulate tints the canvas elements using its asigned color
</description>
<methods>
<method name="set_color">
<argument index="0" name="color" type="Color">
</argument>
<description>
+ Sets the canvas tint color
</description>
</method>
<method name="get_color" qualifiers="const">
<return type="Color">
</return>
<description>
+ Gets the canvas tint color
</description>
</method>
</methods>
@@ -7095,12 +7277,14 @@
<argument index="0" name="enable" type="bool">
</argument>
<description>
+ This function will anchor the container children to the top left corner of the the container boundaries, moving all its children to that position, (the children new center will be the top left corner of the container).
</description>
</method>
<method name="is_using_top_left" qualifiers="const">
<return type="bool">
</return>
<description>
+ Should put children to the top left corner instead of center of the container.
</description>
</method>
</methods>
@@ -7109,8 +7293,10 @@
</class>
<class name="CheckBox" inherits="Button" category="Core">
<brief_description>
+ Binary choice user interface widget
</brief_description>
<description>
+ A checkbox allows the user to make a binary choice (choosing only one of two posible options), for example Answer 'yes' or 'no'.
</description>
<methods>
</methods>
@@ -7758,6 +7944,7 @@
<argument index="0" name="over" type="Color">
</argument>
<description>
+ Return a new color blended with anothor one.
</description>
</method>
<method name="contrasted">
@@ -7966,30 +8153,35 @@
<argument index="0" name="mode" type="bool">
</argument>
<description>
+ When set to true, every color channel will be represented as a value from 0 to 1, insetead of 0, 255.
</description>
</method>
<method name="is_raw_mode" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns whether this color picker is in raw mode or not
</description>
</method>
<method name="set_edit_alpha">
<argument index="0" name="show" type="bool">
</argument>
<description>
+ Set true if you want the color to have an alpha channel (transparency), or false if you want a solid color.
</description>
</method>
<method name="is_editing_alpha" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns whether the color has transparency or not.
</description>
</method>
<method name="add_preset">
<argument index="0" name="arg0" type="Color">
</argument>
<description>
+ Adds the current selected to color to a list of colors (presets), the presets will be displayed in the color picker and the user will be able to select them, notice that the presets list is only for this color picker.
</description>
</method>
</methods>
@@ -7998,7 +8190,7 @@
<argument index="0" name="color" type="Color">
</argument>
<description>
- Emitted when the color changes.
+ Emitted when the color is changed.
</description>
</signal>
</signals>
@@ -8023,32 +8215,38 @@
</class>
<class name="ColorPickerButton" inherits="Button" category="Core">
<brief_description>
+ Button that pops out a [ColorPicker]
</brief_description>
<description>
+ Encapsulates a [ColorPicker] making it accesible by pressing a button, pressing the button will toggle the [ColorPicker] visibility
</description>
<methods>
<method name="set_color">
<argument index="0" name="color" type="Color">
</argument>
<description>
+ Sets the current color
</description>
</method>
<method name="get_color" qualifiers="const">
<return type="Color">
</return>
<description>
+ Gets the current color
</description>
</method>
<method name="set_edit_alpha">
<argument index="0" name="show" type="bool">
</argument>
<description>
+ See [method ColorPicker.set_edit_alpha]
</description>
</method>
<method name="is_editing_alpha" qualifiers="const">
<return type="bool">
</return>
<description>
+ See [method ColorPicker.is_edit_alpha]
</description>
</method>
</methods>
@@ -8057,6 +8255,7 @@
<argument index="0" name="color" type="Color">
</argument>
<description>
+ Emitted when the color is changed.
</description>
</signal>
</signals>
@@ -8089,8 +8288,10 @@
</class>
<class name="ColorRamp" inherits="Resource" category="Core">
<brief_description>
+ Color interpolator node
</brief_description>
<description>
+ Given a set of colors, this node will interpolate them in order, meaning, that if you have color 1, color 2 and color3, the ramp will interpolate (generate the colors between two colors) from color 1 to color 2 and from color 2 to color 3. Initially the ramp will have 2 colors (black and white), one (black) at ramp lower offset offset 0 and the other (white) at the ramp higher offset 1.
</description>
<methods>
<method name="add_point">
@@ -8099,12 +8300,14 @@
<argument index="1" name="color" type="Color">
</argument>
<description>
+ Adds the specified color to the end of the ramp, with the specified offset
</description>
</method>
<method name="remove_point">
<argument index="0" name="offset" type="int">
</argument>
<description>
+ Removes the color at the index [i]offset[/i]
</description>
</method>
<method name="set_offset">
@@ -8113,6 +8316,7 @@
<argument index="1" name="offset" type="float">
</argument>
<description>
+ Sets the offset for the ramp color at index [i]point[/i]
</description>
</method>
<method name="get_offset" qualifiers="const">
@@ -8121,6 +8325,7 @@
<argument index="0" name="point" type="int">
</argument>
<description>
+ Returns the offset of the ramp color at index [i]point[/i]
</description>
</method>
<method name="set_color">
@@ -8129,6 +8334,7 @@
<argument index="1" name="color" type="Color">
</argument>
<description>
+ Sets the color of the ramp color at index [i]point[/i]
</description>
</method>
<method name="get_color" qualifiers="const">
@@ -8137,6 +8343,7 @@
<argument index="0" name="point" type="int">
</argument>
<description>
+ Returns the color of the ramp color at index [i]point[/i]
</description>
</method>
<method name="interpolate">
@@ -8145,36 +8352,42 @@
<argument index="0" name="offset" type="float">
</argument>
<description>
+ Returns the interpolated color specified by [i]offset[/i]
</description>
</method>
<method name="get_point_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of colors in the ramp
</description>
</method>
<method name="set_offsets">
<argument index="0" name="offsets" type="RealArray">
</argument>
<description>
+ Sets the offset for the specified amount of elements. Calling this function with a different number of elements than previously defined causes the ramp to resize its colors and offsets array to accomodate the new elements, all new colors will be black by default.
</description>
</method>
<method name="get_offsets" qualifiers="const">
<return type="RealArray">
</return>
<description>
+ Returns the offsets for the colors in this ramp
</description>
</method>
<method name="set_colors">
<argument index="0" name="colors" type="ColorArray">
</argument>
<description>
+ Sets the colors for the specified amount of elements. Calling this function with a different number of elements than previously defined causes the ramp to resize its colors and offsets array to accomodate the new elements.
</description>
</method>
<method name="get_colors" qualifiers="const">
<return type="ColorArray">
</return>
<description>
+ Returns the colors in the ramp
</description>
</method>
</methods>
@@ -9745,6 +9958,7 @@ This approximation makes straight segments between each point, then subdivides t
<argument index="0" name="values" type="Array">
</argument>
<description>
+ Return true if the dictionary has all of the keys in the given array.
</description>
</method>
<method name="hash">
@@ -9767,6 +9981,7 @@ This approximation makes straight segments between each point, then subdivides t
<argument index="0" name="json" type="String">
</argument>
<description>
+ Parse json text to the dictionary. Return OK when successed or the error code when failed.
</description>
</method>
<method name="size">
@@ -9780,6 +9995,7 @@ This approximation makes straight segments between each point, then subdivides t
<return type="String">
</return>
<description>
+ Return the dictionary as json text.
</description>
</method>
</methods>
@@ -11808,24 +12024,28 @@ Returns an empty String "" at the end of the list.
<argument index="0" name="access" type="int">
</argument>
<description>
+ Set the file access permission of the dialog(Must be one of [ACCESS_RESOURCES], [ACCESS_USERDATA] or [ACCESS_FILESYSTEM]).
</description>
</method>
<method name="get_access" qualifiers="const">
<return type="int">
</return>
<description>
+ Return the file access permission of the dialog.
</description>
</method>
<method name="set_show_hidden_files">
<argument index="0" name="show" type="bool">
</argument>
<description>
+ Set the dialog should show hidden files.
</description>
</method>
<method name="is_showing_hidden_files" qualifiers="const">
<return type="bool">
</return>
<description>
+ Return true if the diaog allows show hidden files.
</description>
</method>
<method name="invalidate">
@@ -11838,12 +12058,14 @@ Returns an empty String "" at the end of the list.
<argument index="0" name="paths" type="StringArray">
</argument>
<description>
+ Event emitted when the user selects multiple files.
</description>
</signal>
<signal name="dir_selected">
<argument index="0" name="dir" type="String">
</argument>
<description>
+ Event emitted when the user selects a directory.
</description>
</signal>
<signal name="file_selected">
@@ -11870,10 +12092,13 @@ Returns an empty String "" at the end of the list.
The dialog will warn when a file exists.
</constant>
<constant name="ACCESS_RESOURCES" value="0">
+ The dialog allows the selection of file and directory.
</constant>
<constant name="ACCESS_USERDATA" value="1">
+ The dialog allows ascess files under [Resource] path(res://) .
</constant>
<constant name="ACCESS_FILESYSTEM" value="2">
+ The dialog allows ascess files in whole file system.
</constant>
</constants>
<theme_items>
@@ -12064,33 +12289,19 @@ Returns an empty String "" at the end of the list.
Font contains an unicode compatible character set, as well as the ability to draw it with variable width, ascent, descent and kerning. For creating fonts from TTF files (or other font formats), see the editor support for fonts. TODO check wikipedia for graph of ascent/baseline/descent/height/etc.
</description>
<methods>
- <method name="create_from_fnt">
- <return type="int">
- </return>
- <argument index="0" name="path" type="String">
+ <method name="draw" qualifiers="const">
+ <argument index="0" name="canvas_item" type="RID">
</argument>
- <description>
- </description>
- </method>
- <method name="set_height">
- <argument index="0" name="px" type="float">
+ <argument index="1" name="pos" type="Vector2">
</argument>
- <description>
- Set the total font height (ascent plus descent) in pixels.
- </description>
- </method>
- <method name="get_height" qualifiers="const">
- <return type="float">
- </return>
- <description>
- Return the total font height (ascent plus descent) in pixels.
- </description>
- </method>
- <method name="set_ascent">
- <argument index="0" name="px" type="float">
+ <argument index="2" name="string" type="String">
+ </argument>
+ <argument index="3" name="modulate" type="Color" default="Color(1,1,1,1)">
+ </argument>
+ <argument index="4" name="clip_w" type="int" default="-1">
</argument>
<description>
- Set the font ascent (number of pixels above the baseline).
+ Draw "string" into a canvas item using the font at a given "pos" position, with "modulate" color, and optionally clipping the width. "pos" specifies the baseline, not the top. To draw from the top, [i]ascent[/i] must be added to the Y axis.
</description>
</method>
<method name="get_ascent" qualifiers="const">
@@ -12107,73 +12318,17 @@ Returns an empty String "" at the end of the list.
Return the font descent (number of pixels below the baseline).
</description>
</method>
- <method name="add_kerning_pair">
- <argument index="0" name="char_a" type="int">
- </argument>
- <argument index="1" name="char_b" type="int">
- </argument>
- <argument index="2" name="kerning" type="int">
- </argument>
- <description>
- Add a kerning pair to the [Font] as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character.
- </description>
- </method>
- <method name="get_kerning_pair" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="char_a" type="int">
- </argument>
- <argument index="1" name="char_b" type="int">
- </argument>
- <description>
- Return a kerning pair as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character.
- </description>
- </method>
- <method name="add_texture">
- <argument index="0" name="texture" type="Texture">
- </argument>
- <description>
- Add a texture to the [Font].
- </description>
- </method>
- <method name="add_char">
- <argument index="0" name="character" type="int">
- </argument>
- <argument index="1" name="texture" type="int">
- </argument>
- <argument index="2" name="rect" type="Rect2">
- </argument>
- <argument index="3" name="align" type="Vector2" default="Vector2(0,0)">
- </argument>
- <argument index="4" name="advance" type="float" default="-1">
- </argument>
- <description>
- Add a character to the font, where "character" is the unicode value, "texture" is the texture index, "rect" is the region in the texture (in pixels!), "align" is the (optional) alignment for the character and "advance" is the (optional) advance.
- </description>
- </method>
- <method name="get_texture_count" qualifiers="const">
- <return type="int">
- </return>
- <description>
- </description>
- </method>
- <method name="get_texture" qualifiers="const">
- <return type="Texture">
+ <method name="get_height" qualifiers="const">
+ <return type="float">
</return>
- <argument index="0" name="idx" type="int">
- </argument>
<description>
+ Return the total font height (ascent plus descent) in pixels.
</description>
</method>
- <method name="get_char_size" qualifiers="const">
- <return type="Vector2">
+ <method name="is_distance_field_hint" qualifiers="const">
+ <return type="bool">
</return>
- <argument index="0" name="char" type="int">
- </argument>
- <argument index="1" name="next" type="int" default="0">
- </argument>
<description>
- Return the size of a character, optionally taking kerning into account if the next character is provided.
</description>
</method>
<method name="get_string_size" qualifiers="const">
@@ -12185,38 +12340,6 @@ Returns an empty String "" at the end of the list.
Return the size of a string, taking kerning and advance into account.
</description>
</method>
- <method name="set_distance_field_hint">
- <argument index="0" name="enable" type="bool">
- </argument>
- <description>
- </description>
- </method>
- <method name="is_distance_field_hint" qualifiers="const">
- <return type="bool">
- </return>
- <description>
- </description>
- </method>
- <method name="clear">
- <description>
- Clear all the font data.
- </description>
- </method>
- <method name="draw" qualifiers="const">
- <argument index="0" name="canvas_item" type="RID">
- </argument>
- <argument index="1" name="pos" type="Vector2">
- </argument>
- <argument index="2" name="string" type="String">
- </argument>
- <argument index="3" name="modulate" type="Color" default="Color(1,1,1,1)">
- </argument>
- <argument index="4" name="clip_w" type="int" default="-1">
- </argument>
- <description>
- Draw "string" into a canvas item using the font at a given "pos" position, with "modulate" color, and optionally clipping the width. "pos" specifies the baseline, not the top. To draw from the top, [i]ascent[/i] must be added to the Y axis.
- </description>
- </method>
<method name="draw_char" qualifiers="const">
<return type="float">
</return>
@@ -12234,18 +12357,6 @@ Returns an empty String "" at the end of the list.
Draw character "char" into a canvas item using the font at a given "pos" position, with "modulate" color, and optionally kerning if "next" is passed. clipping the width. "pos" specifies the baseline, not the top. To draw from the top, [i]ascent[/i] must be added to the Y axis. The width used by the character is returned, making this function useful for drawing strings character by character.
</description>
</method>
- <method name="set_fallback">
- <argument index="0" name="fallback" type="Object">
- </argument>
- <description>
- </description>
- </method>
- <method name="get_fallback" qualifiers="const">
- <return type="Object">
- </return>
- <description>
- </description>
- </method>
</methods>
<constants>
</constants>
@@ -12314,6 +12425,7 @@ Returns an empty String "" at the end of the list.
<return type="bool">
</return>
<description>
+ Should put children to the top left corner instead of center of the container.
</description>
</method>
</methods>
@@ -13388,20 +13500,24 @@ Returns an empty String "" at the end of the list.
</class>
<class name="GridContainer" inherits="Container" category="Core">
<brief_description>
+ Grid container used to arrange elements in a grid like layout
</brief_description>
<description>
+ Grid container will arrange its children in a grid like structure, the grid columns are specified using the [method set_columns] method and the number of rows will be equal to the number of children in the container divided by the number of columns, for example: if the container has 5 children, and 2 columns, there will be 3 rows in the container. Notice that grid layout will preserve the columns and rows for every size of the container.
</description>
<methods>
<method name="set_columns">
<argument index="0" name="columns" type="int">
</argument>
<description>
+ Sets the numbers of columns in the container, then reorder its children to accommodate the new layout
</description>
</method>
<method name="get_columns" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of columns in this container
</description>
</method>
</methods>
@@ -13895,6 +14011,12 @@ Returns an empty String "" at the end of the list.
<description>
</description>
</method>
+ <method name="get_connection" qualifiers="const">
+ <return type="StreamPeer">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="request">
<return type="int">
</return>
@@ -19126,6 +19248,14 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
Set the mesh of the item.
</description>
</method>
+ <method name="set_item_navmesh">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="navmesh" type="NavigationMesh">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_item_shape">
<argument index="0" name="id" type="int">
</argument>
@@ -19152,6 +19282,14 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
Return the mesh of the item.
</description>
</method>
+ <method name="get_item_navmesh" qualifiers="const">
+ <return type="NavigationMesh">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_item_shape" qualifiers="const">
<return type="Shape">
</return>
@@ -22869,56 +23007,66 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
</class>
<class name="Particles2D" inherits="Node2D" category="Core">
<brief_description>
+ 2D Particle emitter
</brief_description>
<description>
+ Particles2D is a particle system 2D [Node] that is used to simulate several types of particle effects, such as explosions, rain, snow, fireflies, or other magical-like shinny sparkles. Particles are drawn using impostors, and given their dynamic behavior, the user must provide a visibility AABB (although helpers to create one automatically exist).
</description>
<methods>
<method name="set_emitting">
<argument index="0" name="active" type="bool">
</argument>
<description>
+ If this is set to true then the particle emitter will emit particles, if its false it will not.
</description>
</method>
<method name="is_emitting" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns whether this emitter is currently emitting or not
</description>
</method>
<method name="set_amount">
<argument index="0" name="amount" type="int">
</argument>
<description>
+ Sets the amount of particles spawned at each emission
</description>
</method>
<method name="get_amount" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the amount of particles spawned at each emission
</description>
</method>
<method name="set_lifetime">
<argument index="0" name="lifetime" type="float">
</argument>
<description>
+ Sets the amount of seconds that each particle will be visible.
</description>
</method>
<method name="get_lifetime" qualifiers="const">
<return type="float">
</return>
<description>
+ Gets the amount of seconds that each particle will be visible.
</description>
</method>
<method name="set_time_scale">
<argument index="0" name="time_scale" type="float">
</argument>
<description>
+ Sets the increment or decrement for the particle lifetime. for example: if the time scale is set to 2, the particles will die and move twice as fast.
</description>
</method>
<method name="get_time_scale" qualifiers="const">
<return type="float">
</return>
<description>
+ Returns the emitter time scale
</description>
</method>
<method name="set_pre_process_time">
@@ -22937,12 +23085,14 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
<argument index="0" name="value" type="float">
</argument>
<description>
+ Sets the amount of seconds during which the emitter will spawn particles, after the specified seconds the emitter state will be set to non emitting, so calling [method is_emitting] will return false. If the timeout is 0 the emitter will spawn forever.
</description>
</method>
<method name="get_emit_timeout" qualifiers="const">
<return type="float">
</return>
<description>
+ Returns the amount of seconds during which the emitter will spawn particles
</description>
</method>
<method name="set_param">
@@ -22951,6 +23101,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
<argument index="1" name="value" type="float">
</argument>
<description>
+ Sets the value of the specified emitter parameter (see the constants secction for the list of parameters)
</description>
</method>
<method name="get_param" qualifiers="const">
@@ -22959,6 +23110,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
<argument index="0" name="param" type="int">
</argument>
<description>
+ Returns the value of the specified emitter parameter
</description>
</method>
<method name="set_randomness">
@@ -22967,6 +23119,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
<argument index="1" name="value" type="float">
</argument>
<description>
+ Sets the randomness value of the specified emitter parameter (see the constants secction for the list of parameters), 0 means no randomness, so every particle will have the parameters specified, 1 means that the parameter will be choosen at random, the closer the randomness value gets to 0 the more conservative the variation of the parameter will be.
</description>
</method>
<method name="get_randomness" qualifiers="const">
@@ -22975,6 +23128,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
<argument index="0" name="param" type="int">
</argument>
<description>
+ Returns the randomness value of the specified emitter parameter
</description>
</method>
<method name="set_texture">
@@ -22983,24 +23137,28 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
<argument index="0" name="texture" type="Object">
</argument>
<description>
+ Sets the texture for each particle
</description>
</method>
<method name="get_texture" qualifiers="const">
<return type="Texture">
</return>
<description>
+ Returns the texture for emitted particles
</description>
</method>
<method name="set_color">
<argument index="0" name="color" type="Color">
</argument>
<description>
+ Set the tint color for each particle.
</description>
</method>
<method name="get_color" qualifiers="const">
<return type="Color">
</return>
<description>
+ Returns the tint color for each particle.
</description>
</method>
<method name="set_color_ramp">
@@ -23009,24 +23167,28 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
<argument index="0" name="color_ramp" type="Object">
</argument>
<description>
+ Sets the [ColorRamp] used to tint each particle. Particle will be tinted according to their lifetimes.
</description>
</method>
<method name="get_color_ramp" qualifiers="const">
<return type="ColorRamp">
</return>
<description>
+ Returns the [ColorRamp] used to tint each particle
</description>
</method>
<method name="set_emissor_offset">
<argument index="0" name="offset" type="Vector2">
</argument>
<description>
+ Sets the particle spawn origin position relative to the emitter center. for example if this value is set to (50, 50), the particle will spawn 50 units to the right and 50 units to the bottom of the emitter center.
</description>
</method>
<method name="get_emissor_offset" qualifiers="const">
<return type="Vector2">
</return>
<description>
+ Returns the particle spawn origin position relative to the emitter.
</description>
</method>
<method name="set_flip_h">
@@ -23081,12 +23243,14 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
<argument index="0" name="extents" type="Vector2">
</argument>
<description>
+ Sets the half extents of the emission box, particles will be spawned at random inside this box.
</description>
</method>
<method name="get_emission_half_extents" qualifiers="const">
<return type="Vector2">
</return>
<description>
+ Returns the half extents of the emission box.
</description>
</method>
<method name="set_color_phases">
@@ -23194,30 +23358,40 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8)
</methods>
<constants>
<constant name="PARAM_DIRECTION" value="0">
+ Direction in degrees at which the particles will be launched, Notice that when the direction is set to 0 the particles will be launched to the negative
</constant>
<constant name="PARAM_SPREAD" value="1">
</constant>
<constant name="PARAM_LINEAR_VELOCITY" value="2">
+ Velocity at which the particles will be launched.
</constant>
<constant name="PARAM_SPIN_VELOCITY" value="3">
+ The speed at which particles will spin around its own center.
</constant>
<constant name="PARAM_ORBIT_VELOCITY" value="4">
+ Velocity at which the particles will orbit around the emitter center
</constant>
<constant name="PARAM_GRAVITY_DIRECTION" value="5">
+ Direction in degrees at which the particles will be attracted
</constant>
<constant name="PARAM_GRAVITY_STRENGTH" value="6">
+ Strength of the gravitation attraction for each particle
</constant>
<constant name="PARAM_RADIAL_ACCEL" value="7">
</constant>
<constant name="PARAM_TANGENTIAL_ACCEL" value="8">
</constant>
<constant name="PARAM_DAMPING" value="9">
+ Amount of damping for each particle
</constant>
<constant name="PARAM_INITIAL_ANGLE" value="10">
+ Initial angle at which each particle will be spawned
</constant>
<constant name="PARAM_INITIAL_SIZE" value="11">
+ Initial size of each particle
</constant>
<constant name="PARAM_FINAL_SIZE" value="12">
+ Final size of each particle, the particle size will interpolate to this value during its lifetime.
</constant>
<constant name="PARAM_HUE_VARIATION" value="13">
</constant>
@@ -24577,6 +24751,17 @@ This method controls whether the position between two cached points is interpola
<description>
</description>
</method>
+ <method name="body_add_force">
+ <argument index="0" name="body" type="RID">
+ </argument>
+ <argument index="1" name="offset" type="Vector2">
+ </argument>
+ <argument index="2" name="force" type="Vector2">
+ </argument>
+ <description>
+ Add a positioned force to the applied force and torque. As with [method body_apply_impulse], both the force and the offset from the body origin are in global coordinates.
+ </description>
+ </method>
<method name="body_set_axis_velocity">
<argument index="0" name="body" type="RID">
</argument>
@@ -24864,13 +25049,15 @@ This method controls whether the position between two cached points is interpola
</constant>
<constant name="BODY_PARAM_MASS" value="2">
</constant>
- <constant name="BODY_PARAM_GRAVITY_SCALE" value="3">
+ <constant name="BODY_PARAM_INERTIA" value="3">
</constant>
- <constant name="BODY_PARAM_LINEAR_DAMP" value="4">
+ <constant name="BODY_PARAM_GRAVITY_SCALE" value="4">
</constant>
- <constant name="BODY_PARAM_ANGULAR_DAMP" value="5">
+ <constant name="BODY_PARAM_LINEAR_DAMP" value="5">
</constant>
- <constant name="BODY_PARAM_MAX" value="6">
+ <constant name="BODY_PARAM_ANGULAR_DAMP" value="6">
+ </constant>
+ <constant name="BODY_PARAM_MAX" value="7">
</constant>
<constant name="BODY_STATE_TRANSFORM" value="0">
</constant>
@@ -27133,80 +27320,94 @@ This method controls whether the position between two cached points is interpola
</class>
<class name="Polygon2D" inherits="Node2D" category="Core">
<brief_description>
+ 2D polygon representation
</brief_description>
<description>
+ A Polygon2D is defined by a set of n points connected together by line segments, meaning that the point 1 will be connected with point 2, point 2 with point 3 ..., point n-1 with point n and point n with point 1 in order to close the loop and define a plane.
</description>
<methods>
<method name="set_polygon">
<argument index="0" name="polygon" type="Vector2Array">
</argument>
<description>
+ Defines the set of points that will represent the polygon.
</description>
</method>
<method name="get_polygon" qualifiers="const">
<return type="Vector2Array">
</return>
<description>
+ Returns the set of points that defines this polygon
</description>
</method>
<method name="set_uv">
<argument index="0" name="uv" type="Vector2Array">
</argument>
<description>
+ Sets the uv value for every point of the polygon
</description>
</method>
<method name="get_uv" qualifiers="const">
<return type="Vector2Array">
</return>
<description>
+ Returns the uv value associated with every point of the polygon
</description>
</method>
<method name="set_color">
<argument index="0" name="color" type="Color">
</argument>
<description>
+ Sets the polygon fill color, if the polygon has a texture defined, the defined texture will be tinted to the polygon fill color.
</description>
</method>
<method name="get_color" qualifiers="const">
<return type="Color">
</return>
<description>
+ Returns the polygon fill color
</description>
</method>
<method name="set_texture">
<argument index="0" name="texture" type="Object">
</argument>
<description>
+ Sets the polygon texture.
</description>
</method>
<method name="get_texture" qualifiers="const">
<return type="Object">
</return>
<description>
+ Returns the polygon texture
</description>
</method>
<method name="set_texture_offset">
<argument index="0" name="texture_offset" type="Vector2">
</argument>
<description>
+ Sets the offset of the polygon texture. Initially the texture will appear anchored to the polygon position, the offset is used to move the texture location away from that point (notice that the texture origin is set to its top left corner, so when offset is 0,0 the top left corner of the texture is at the polygon position), for example setting the offset to 10, 10 will move the texture 10 units to the left and 10 units to the top.
</description>
</method>
<method name="get_texture_offset" qualifiers="const">
<return type="Vector2">
</return>
<description>
+ Returns the polygon texture offset
</description>
</method>
<method name="set_texture_rotation">
<argument index="0" name="texture_rotation" type="float">
</argument>
<description>
+ Sets the amount of rotation of the polygon texture, [code]texture_rotation[/code] is specified in degrees and clockwise rotation, meaning that if the texture rotation is set to 45 degrees, the texture will be rotated 45 degrees clockwise along the polygon position plus the texture offset.
</description>
</method>
<method name="get_texture_rotation" qualifiers="const">
<return type="float">
</return>
<description>
+ Returns the rotation in degrees of the texture polygon
</description>
</method>
<method name="set_texture_scale">
@@ -27225,12 +27426,14 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="invert" type="bool">
</argument>
<description>
+ Sets the polygon as the defined polygon bounding box minus the defined polygon (the defined polygon will appear as a hole on square that contains the defined polygon).
</description>
</method>
<method name="get_invert" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns whether this polygon is inverted or not
</description>
</method>
<method name="set_invert_border">
@@ -27249,12 +27452,14 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="offset" type="Vector2">
</argument>
<description>
+ Sets the amount of distance from the polygon points from the polygon position, for example if the offset is set to 10,10 then all the polygon points will move 10 units to the right and 10 units to the bottom.
</description>
</method>
<method name="get_offset" qualifiers="const">
<return type="Vector2">
</return>
<description>
+ Returns the polygon points offset to the polygon position.
</description>
</method>
</methods>
@@ -27363,6 +27568,7 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="minsize" type="Vector2" default="Vector2(0,0)">
</argument>
<description>
+ Popup (show the control in modal form) in the center of the screen, ensuring the size is never smaller than [code]minsize[/code].
</description>
</method>
<method name="popup">
@@ -27374,18 +27580,21 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="enable" type="bool">
</argument>
<description>
+ Make the popup hide other popups when shown on the screen.
</description>
</method>
<method name="is_exclusive" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns whether the popup will hide other popups when shown on the screen.
</description>
</method>
</methods>
<signals>
<signal name="popup_hide">
<description>
+ This signal is emitted when a popup is hidden.
</description>
</signal>
<signal name="about_to_show">
@@ -27396,8 +27605,10 @@ This method controls whether the position between two cached points is interpola
</signals>
<constants>
<constant name="NOTIFICATION_POST_POPUP" value="80">
+ Notification sent right after the popup is shown.
</constant>
<constant name="NOTIFICATION_POPUP_HIDE" value="81">
+ Notification sent right after the popup is hidden.
</constant>
</constants>
</class>
@@ -27406,6 +27617,7 @@ This method controls whether the position between two cached points is interpola
Base class for Popup Dialogs.
</brief_description>
<description>
+ PopupDialog is a base class for popup dialogs, along with [WindowDialog].
</description>
<methods>
</methods>
@@ -27430,7 +27642,7 @@ This method controls whether the position between two cached points is interpola
<argument index="3" name="accel" type="int" default="0">
</argument>
<description>
- Add a new item with text "label" and icon "texture". An id can optionally be provided, as well as an accelerator. If no id is provided, one will be created from the index.
+ Add a new item with text "label" and icon "texture". An id can optionally be provided, as well as an accelerator keybinding. If no id is provided, one will be created from the index.
</description>
</method>
<method name="add_item">
@@ -27441,7 +27653,7 @@ This method controls whether the position between two cached points is interpola
<argument index="2" name="accel" type="int" default="0">
</argument>
<description>
- Add a new item with text "label". An id can optionally be provided, as well as an accelerator. If no id is provided, one will be created from the index.
+ Add a new item with text "label". An id can optionally be provided, as well as an accelerator keybinding. If no id is provided, one will be created from the index.
</description>
</method>
<method name="add_icon_check_item">
@@ -27454,7 +27666,7 @@ This method controls whether the position between two cached points is interpola
<argument index="3" name="accel" type="int" default="0">
</argument>
<description>
- Add a new check able item with text "label" and icon "texture". An id can optionally be provided, as well as an accelerator. If no id is provided, one will be created from the index. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually.
+ Add a new checkable item with text "label" and icon "texture". An id can optionally be provided, as well as an accelerator. If no id is provided, one will be created from the index. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually.
</description>
</method>
<method name="add_check_item">
@@ -27476,6 +27688,7 @@ This method controls whether the position between two cached points is interpola
<argument index="2" name="id" type="int" default="-1">
</argument>
<description>
+ Adds an item with a submenu. The submenu is the name of a child PopupMenu node that would be shown when the item is clicked. An id can optionally be provided, but if is isn't provided, one will be created from the index.
</description>
</method>
<method name="set_item_text">
@@ -27511,6 +27724,7 @@ This method controls whether the position between two cached points is interpola
<argument index="1" name="metadata" type="Variant">
</argument>
<description>
+ Sets the metadata of an item, which might be of any type. You can later get it with [method get_item_metadata], which provides a simple way of assigning context data to items.
</description>
</method>
<method name="set_item_checked">
@@ -27528,6 +27742,7 @@ This method controls whether the position between two cached points is interpola
<argument index="1" name="disabled" type="bool">
</argument>
<description>
+ Sets whether the item at index "idx" is disabled or not. When it is disabled it can't be selected, or its action invoked.
</description>
</method>
<method name="set_item_submenu">
@@ -27536,6 +27751,7 @@ This method controls whether the position between two cached points is interpola
<argument index="1" name="submenu" type="String">
</argument>
<description>
+ Sets the submenu of the item at index "idx". The submenu is the name of a child PopupMenu node that would be shown when the item is clicked.
</description>
</method>
<method name="set_item_as_separator">
@@ -27544,6 +27760,7 @@ This method controls whether the position between two cached points is interpola
<argument index="1" name="enable" type="bool">
</argument>
<description>
+ Mark the item at index "idx" as a seperator, which means that it would be displayed as a mere line.
</description>
</method>
<method name="set_item_as_checkable">
@@ -27552,6 +27769,7 @@ This method controls whether the position between two cached points is interpola
<argument index="1" name="enable" type="bool">
</argument>
<description>
+ Set whether the item at index "idx" has a checkbox. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually.
</description>
</method>
<method name="set_item_ID">
@@ -27585,6 +27803,7 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the metadata of an item, which might be of any type. You can set it with [method set_item_metadata], which provides a simple way of assigning context data to items.
</description>
</method>
<method name="get_item_accelerator" qualifiers="const">
@@ -27602,6 +27821,7 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the submenu name of the item at index "idx".
</description>
</method>
<method name="is_item_separator" qualifiers="const">
@@ -27610,6 +27830,7 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return whether the item is a seperator. If it is, it would be displayed as a line.
</description>
</method>
<method name="is_item_checkable" qualifiers="const">
@@ -27618,6 +27839,7 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return whether the item at index "idx" has a checkbox. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually.
</description>
</method>
<method name="is_item_checked" qualifiers="const">
@@ -27635,6 +27857,7 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return whether the item at index "idx" is disabled. When it is disabled it can't be selected, or its action invoked.
</description>
</method>
<method name="get_item_ID" qualifiers="const">
@@ -27671,11 +27894,12 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Removes the item at index "idx" from the menu. Note that the indexes of items after the removed item are going to be shifted by one.
</description>
</method>
<method name="clear">
<description>
- Clear the popup menu.
+ Clear the popup menu, in effect removing all items.
</description>
</method>
</methods>
@@ -27723,9 +27947,10 @@ This method controls whether the position between two cached points is interpola
</class>
<class name="PopupPanel" inherits="Popup" category="Core">
<brief_description>
- Base class for Popup Panels
+ Class for displaying popups with a panel background.
</brief_description>
<description>
+ Class for displaying popups with a panel background. In some cases it might be simpler to use than [Popup], since it provides a configurable background. If you are making windows, better check [WindowDialog].
</description>
<methods>
</methods>
@@ -28480,39 +28705,45 @@ This method controls whether the position between two cached points is interpola
</class>
<class name="RayCast2D" inherits="Node2D" category="Core">
<brief_description>
+ Query the closest object intersecting a ray
</brief_description>
<description>
+ A RayCast2D represents a line from its origin to its destination position [code]cast_to[/code], it is used to query the 2D space in order to find the closest object intersecting with the ray.
</description>
<methods>
<method name="set_enabled">
<argument index="0" name="enabled" type="bool">
</argument>
<description>
+ Enables the RayCast2D. Only enabled raycasts will be able to query the space and report collisions.
</description>
</method>
<method name="is_enabled" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns whether this raycast is enabled or not
</description>
</method>
<method name="set_cast_to">
<argument index="0" name="local_point" type="Vector2">
</argument>
<description>
+ Sets the ray destination point, so that the ray will test from the ray's origin to [code]local_point[/code]
</description>
</method>
<method name="get_cast_to" qualifiers="const">
<return type="Vector2">
</return>
<description>
+ Return the destination point of this ray object
</description>
</method>
<method name="is_colliding" qualifiers="const">
<return type="bool">
</return>
<description>
- Return whether the closest object the ray is pointing to is colliding with the vector, with the vector length considered.
+ Return whether the closest object the ray is pointing to is colliding with the vector (considering the vector length).
</description>
</method>
<method name="get_collider" qualifiers="const">
@@ -28526,18 +28757,21 @@ This method controls whether the position between two cached points is interpola
<return type="int">
</return>
<description>
+ Returns the collision shape of the closest object the ray is pointing to.
</description>
</method>
<method name="get_collision_point" qualifiers="const">
<return type="Vector2">
</return>
<description>
+ Returns the collision point in which the ray intersects the closest object.
</description>
</method>
<method name="get_collision_normal" qualifiers="const">
<return type="Vector2">
</return>
<description>
+ Returns the normal of the intersecting object shape face containing the collision point.
</description>
</method>
<method name="add_exception_rid">
@@ -28550,6 +28784,7 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="node" type="Object">
</argument>
<description>
+ Adds a collision exception so the ray does not report collisions with the specified [code]node[/code].
</description>
</method>
<method name="remove_exception_rid">
@@ -28562,10 +28797,12 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="node" type="Object">
</argument>
<description>
+ Removes a collision exception so the ray does report collisions with the specified [code]node[/code].
</description>
</method>
<method name="clear_exceptions">
<description>
+ Removes all collision exception for this ray.
</description>
</method>
<method name="set_layer_mask">
@@ -28578,6 +28815,7 @@ This method controls whether the position between two cached points is interpola
<return type="int">
</return>
<description>
+ Returns the layer mask for this ray.
</description>
</method>
<method name="set_type_mask">
@@ -30032,6 +30270,20 @@ This method controls whether the position between two cached points is interpola
Return the body mass.
</description>
</method>
+ <method name="get_inertia" qualifiers="const">
+ <return type="float">
+ </return>
+ <description>
+ Return the body's moment of inertia. This is usually automatically computed from the mass and the shapes. Note that this doesn't seem to work in a [code]_ready[/code] function: it apparently has not been auto-computed yet.
+ </description>
+ </method>
+ <method name="set_inertia">
+ <argument index="0" name="inertia" type="float">
+ </argument>
+ <description>
+ Set the body's moment of inertia. This is like mass, but for rotation: it determines how much torque it takes to rotate the body. The moment of inertia is usually computed automatically from the mass and the shapes, but this function allows you to set a custom value. Set 0 (or negative) inertia to return to automatically computing it.
+ </description>
+ </method>
<method name="set_weight">
<argument index="0" name="weight" type="float">
</argument>
@@ -30209,12 +30461,12 @@ This method controls whether the position between two cached points is interpola
</description>
</method>
<method name="apply_impulse">
- <argument index="0" name="pos" type="Vector2">
+ <argument index="0" name="offset" type="Vector2">
</argument>
<argument index="1" name="impulse" type="Vector2">
</argument>
<description>
- Apply a positioned impulse (which will be affected by the body mass and shape). This is the equivalent of hitting a billiard ball with a cue: a force that is applied once, and only once.
+ Apply a positioned impulse (which will be affected by the body mass and shape). This is the equivalent of hitting a billiard ball with a cue: a force that is applied once, and only once. Both the impulse and the offset from the body origin are in global coordinates.
</description>
</method>
<method name="set_applied_force">
@@ -30231,6 +30483,29 @@ This method controls whether the position between two cached points is interpola
Return the applied force vector.
</description>
</method>
+ <method name="set_applied_torque">
+ <argument index="0" name="torque" type="float">
+ </argument>
+ <description>
+ Set a constant torque which will be applied to this body.
+ </description>
+ </method>
+ <method name="get_applied_torque" qualifiers="const">
+ <return type="float">
+ </return>
+ <description>
+ Return the torque which is being applied to this body.
+ </description>
+ </method>
+ <method name="add_force">
+ <argument index="0" name="offset" type="Vector2">
+ </argument>
+ <argument index="1" name="force" type="Vector2">
+ </argument>
+ <description>
+ Add a positioned force to the applied force and torque. As with [method apply_impulse], both the force and the offset from the body origin are in global coordinates.
+ </description>
+ </method>
<method name="set_sleeping">
<argument index="0" name="sleeping" type="bool">
</argument>
@@ -31772,48 +32047,56 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="enable" type="bool">
</argument>
<description>
+ Set allows horizontal scrool.
</description>
</method>
<method name="is_h_scroll_enabled" qualifiers="const">
<return type="bool">
</return>
<description>
+ Return true if horizontal scrool is allowed.
</description>
</method>
<method name="set_enable_v_scroll">
<argument index="0" name="enable" type="bool">
</argument>
<description>
+ Set allows vertical scrool.
</description>
</method>
<method name="is_v_scroll_enabled" qualifiers="const">
<return type="bool">
</return>
<description>
+ Return true if vertical scrool is allowed.
</description>
</method>
<method name="set_h_scroll">
<argument index="0" name="val" type="int">
</argument>
<description>
+ Set horizontal scroll value.
</description>
</method>
<method name="get_h_scroll" qualifiers="const">
<return type="int">
</return>
<description>
+ Return current horizontal scroll value.
</description>
</method>
<method name="set_v_scroll">
<argument index="0" name="val" type="int">
</argument>
<description>
+ Set vertical scroll value.
</description>
</method>
<method name="get_v_scroll" qualifiers="const">
<return type="int">
</return>
<description>
+ Return current vertical scroll value.
</description>
</method>
</methods>
@@ -34073,19 +34356,21 @@ This method controls whether the position between two cached points is interpola
<return type="bool">
</return>
<description>
- Return if the split is collapsed.
+ Return true if the split is collapsed.
</description>
</method>
<method name="set_dragger_visibility">
<argument index="0" name="mode" type="int">
</argument>
<description>
+ Set visibility of the split dragger([i]mode[/i] must be one of [DRAGGER_VISIBLE], [DRAGGER_HIDDEN] or [DRAGGER_HIDDEN_COLLAPSED]).
</description>
</method>
<method name="get_dragger_visibility" qualifiers="const">
<return type="int">
</return>
<description>
+ Return visibility of the split dragger(One of [DRAGGER_VISIBLE], [DRAGGER_HIDDEN] or [DRAGGER_HIDDEN_COLLAPSED]).
</description>
</method>
</methods>
@@ -34094,15 +34379,19 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="offset" type="int">
</argument>
<description>
+ Emmited when the dragger is gragged by user.
</description>
</signal>
</signals>
<constants>
<constant name="DRAGGER_VISIBLE" value="0">
+ The split dragger is visible.
</constant>
<constant name="DRAGGER_HIDDEN" value="1">
+ The split dragger is invisible.
</constant>
<constant name="DRAGGER_HIDDEN_COLLAPSED" value="2">
+ The split dragger is invisible and collapsed.
</constant>
</constants>
</class>
@@ -35692,12 +35981,14 @@ This method controls whether the position between two cached points is interpola
<argument index="0" name="string" type="String">
</argument>
<description>
+ Append a string element at end of the array.
</description>
</method>
<method name="resize">
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Reset the size of the array.
</description>
</method>
<method name="set">
@@ -35712,6 +36003,7 @@ This method controls whether the position between two cached points is interpola
<return type="int">
</return>
<description>
+ Return the size of the array.
</description>
</method>
<method name="StringArray">
@@ -36094,6 +36386,22 @@ This method controls whether the position between two cached points is interpola
<description>
</description>
</method>
+ <method name="add_triangle_fan">
+ <argument index="0" name="vertexes" type="Vector3Array">
+ </argument>
+ <argument index="1" name="uvs" type="Vector2Array" default="[Vector2Array]">
+ </argument>
+ <argument index="2" name="colors" type="ColorArray" default="ColorArray([ColorArray])">
+ </argument>
+ <argument index="3" name="uv2s" type="Vector2Array" default="[Vector2Array]">
+ </argument>
+ <argument index="4" name="normals" type="Vector3Array" default="Vector3Array()">
+ </argument>
+ <argument index="5" name="tangents" type="Array" default="Array()">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_material">
<argument index="0" name="material" type="Material">
</argument>
@@ -37336,6 +37644,12 @@ This method controls whether the position between two cached points is interpola
<description>
</description>
</method>
+ <method name="get_stylebox_types" qualifiers="const">
+ <return type="StringArray">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="set_font">
<argument index="0" name="name" type="String">
</argument>
@@ -39394,9 +39708,11 @@ This method controls whether the position between two cached points is interpola
</constant>
<constant name="CELL_MODE_RANGE" value="2">
</constant>
- <constant name="CELL_MODE_ICON" value="3">
+ <constant name="CELL_MODE_RANGE_EXPRESSION" value="3">
</constant>
- <constant name="CELL_MODE_CUSTOM" value="4">
+ <constant name="CELL_MODE_ICON" value="4">
+ </constant>
+ <constant name="CELL_MODE_CUSTOM" value="5">
</constant>
</constants>
</class>
@@ -40149,6 +40465,7 @@ This method controls whether the position between two cached points is interpola
Vector used for 2D Math.
</brief_description>
<description>
+ 2-element structure that can be used to represent positions in 2d-space, or any other pair of numeric values.
</description>
<methods>
<method name="angle">
@@ -40230,6 +40547,7 @@ This method controls whether the position between two cached points is interpola
<return type="Vector2">
</return>
<description>
+ Remove the fractional part of x and y.
</description>
</method>
<method name="get_aspect">
@@ -43248,14 +43566,17 @@ This method controls whether the position between two cached points is interpola
</class>
<class name="WeakRef" inherits="Reference" category="Core">
<brief_description>
+ Holds an [Object], but does not contribute to the reference count if the object is a reference.
</brief_description>
<description>
+ A weakref can hold a [Reference], without contributing to the reference counter. A weakref can be created from an [Object] using [method @GDScript.weakref]. If this object is not a reference, weakref still works, however, it does not have any effect on the object. Weakrefs are useful in cases where multiple classes have variables that refer to eachother. Without weakrefs, using these classes could lead to memory leaks, since both references keep eachother from being released. Making part of the variables a weakref can prevent this cyclic dependency, and allows the references to be released.
</description>
<methods>
<method name="get_ref" qualifiers="const">
<return type="Object">
</return>
<description>
+ Returns the [Object] this weakref is referring to.
</description>
</method>
</methods>
@@ -43563,20 +43884,24 @@ This method controls whether the position between two cached points is interpola
</class>
<class name="YSort" inherits="Node2D" category="Core">
<brief_description>
+ Sort all child nodes based on their Y positions.
</brief_description>
<description>
+ Sort all child nodes based on their Y positions. The child node must inherit from [CanvasItem] for it to be sorted. Nodes that have a higher Y position will be drawn later, so they will appear on top of nodes that have a lower Y position.
</description>
<methods>
<method name="set_sort_enabled">
<argument index="0" name="enabled" type="bool">
</argument>
<description>
+ Set whether the children nodes are sorted or not. (default true)
</description>
</method>
<method name="is_sort_enabled" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns true if the children nodes are being sorted.
</description>
</method>
</methods>
diff --git a/methods.py b/methods.py
index a428338499..d3895fd7a3 100755
--- a/methods.py
+++ b/methods.py
@@ -664,11 +664,7 @@ def build_hlsl_dx9_headers( target, source, env ):
return 0
-def build_legacygl_header( filename, include, class_suffix, output_attribs ):
-
- fs = open(filename,"r")
- line=fs.readline()
-
+class LegacyGLHeaderStruct:
vertex_lines=[]
fragment_lines=[]
uniforms=[]
@@ -676,32 +672,57 @@ def build_legacygl_header( filename, include, class_suffix, output_attribs ):
fbos=[]
conditionals=[]
enums={}
- enum_constants=[]
texunits=[]
texunit_names=[]
ubos=[]
ubo_names=[]
-
- reading=""
+
+ vertex_included_files=[]
+ fragment_included_files=[]
+
line_offset=0
vertex_offset=0
fragment_offset=0
+
+def include_file_in_legacygl_header( filename, header_data, depth ):
+ fs = open(filename,"r")
+
+ line=fs.readline()
+ reading=""
+
while(line):
if (line.find("[vertex]")!=-1):
reading="vertex"
line=fs.readline()
- line_offset+=1
- vertex_offset=line_offset
+ header_data.line_offset+=1
+ header_data.vertex_offset=header_data.line_offset
continue
if (line.find("[fragment]")!=-1):
reading="fragment"
line=fs.readline()
- line_offset+=1
- fragment_offset=line_offset
+ header_data.line_offset+=1
+ header_data.fragment_offset=header_data.line_offset
continue
+
+ while(line.find("#include ")!=-1):
+ includeline = line.replace("#include ","").strip()[1:-1]
+
+ import os.path
+
+ included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline)
+ if (not included_file in header_data.vertex_included_files and reading=="vertex"):
+ header_data.vertex_included_files+=[included_file]
+ if(include_file_in_legacygl_header( included_file, header_data, depth + 1 ) == None):
+ print "Error in file '" + filename + "': #include " + includeline + "could not be found!"
+ elif (not included_file in header_data.fragment_included_files and reading=="fragment"):
+ header_data.fragment_included_files+=[included_file]
+ if(include_file_in_legacygl_header( included_file, header_data, depth + 1 ) == None):
+ print "Error in file '" + filename + "': #include " + includeline + "could not be found!"
+
+ line=fs.readline()
if (line.find("#ifdef ")!=-1 or line.find("#elif defined(")!=-1):
if (line.find("#ifdef ")!=-1):
@@ -715,13 +736,13 @@ def build_legacygl_header( filename, include, class_suffix, output_attribs ):
ifdefline = ifdefline.replace("_EN_","_")
line = line.replace("_EN_","_")
# print(enumbase+":"+ifdefline);
- if (enumbase not in enums):
- enums[enumbase]=[]
- if (ifdefline not in enums[enumbase]):
- enums[enumbase].append(ifdefline);
+ if (enumbase not in header_data.enums):
+ header_data.enums[enumbase]=[]
+ if (ifdefline not in header_data.enums[enumbase]):
+ header_data.enums[enumbase].append(ifdefline);
- elif (not ifdefline in conditionals):
- conditionals+=[ifdefline]
+ elif (not ifdefline in header_data.conditionals):
+ header_data.conditionals+=[ifdefline]
if (line.find("uniform")!=-1 and line.lower().find("texunit:")!=-1):
#texture unit
@@ -743,9 +764,9 @@ def build_legacygl_header( filename, include, class_suffix, output_attribs ):
#unfiorm array
x = x[ :x.find("[") ]
- if (not x in texunit_names):
- texunits+=[(x,texunit)]
- texunit_names+=[x]
+ if (not x in header_data.texunit_names):
+ header_data.texunits+=[(x,texunit)]
+ header_data.texunit_names+=[x]
@@ -761,8 +782,8 @@ def build_legacygl_header( filename, include, class_suffix, output_attribs ):
#unfiorm array
x = x[ :x.find("[") ]
- if (not x in uniforms):
- uniforms+=[x]
+ if (not x in header_data.uniforms):
+ header_data.uniforms+=[x]
if ((line.strip().find("in ")==0 or line.strip().find("attribute ")==0) and line.find("attrib:")!=-1):
@@ -778,7 +799,7 @@ def build_legacygl_header( filename, include, class_suffix, output_attribs ):
if (bind.find("attrib:")!=-1):
name=name.strip()
bind=bind.replace("attrib:","").strip()
- attributes+=[(name,bind)]
+ header_data.attributes+=[(name,bind)]
line=line.replace("\r","")
@@ -788,17 +809,28 @@ def build_legacygl_header( filename, include, class_suffix, output_attribs ):
#line=line+"\\n\\"
if (reading=="vertex"):
- vertex_lines+=[line]
+ header_data.vertex_lines+=[line]
if (reading=="fragment"):
- fragment_lines+=[line]
+ header_data.fragment_lines+=[line]
line=fs.readline()
- line_offset+=1
-
+ header_data.line_offset+=1
+
fs.close();
+
+ return header_data
+
+
+
+def build_legacygl_header( filename, include, class_suffix, output_attribs ):
+
+ header_data = LegacyGLHeaderStruct()
+ include_file_in_legacygl_header( filename, header_data, 0 )
out_file = filename+".h"
fd = open(out_file,"w")
+
+ enum_constants=[]
fd.write("/* WARNING, THIS FILE WAS GENERATED, DO NOT EDIT */\n");
@@ -819,21 +851,21 @@ def build_legacygl_header( filename, include, class_suffix, output_attribs ):
fd.write("public:\n\n");
- if (len(conditionals)):
+ if (len(header_data.conditionals)):
fd.write("\tenum Conditionals {\n");
- for x in conditionals:
+ for x in header_data.conditionals:
fd.write("\t\t"+x.upper()+",\n");
fd.write("\t};\n\n");
- if (len(uniforms)):
+ if (len(header_data.uniforms)):
fd.write("\tenum Uniforms {\n");
- for x in uniforms:
+ for x in header_data.uniforms:
fd.write("\t\t"+x.upper()+",\n");
fd.write("\t};\n\n");
fd.write("\t_FORCE_INLINE_ int get_uniform(Uniforms p_uniform) const { return _get_uniform(p_uniform); }\n\n");
- if (len(conditionals)):
+ if (len(header_data.conditionals)):
fd.write("\t_FORCE_INLINE_ void set_conditional(Conditionals p_conditional,bool p_enable) { _set_conditional(p_conditional,p_enable); }\n\n");
fd.write("\t#define _FU if (get_uniform(p_uniform)<0) return; ERR_FAIL_COND( get_active()!=this );\n\n ");
@@ -940,16 +972,16 @@ def build_legacygl_header( filename, include, class_suffix, output_attribs ):
enum_value_count=0;
- if (len(enums)):
+ if (len(header_data.enums)):
fd.write("\t\t//Written using math, given nonstandarity of 64 bits integer constants..\n");
fd.write("\t\tstatic const Enum _enums[]={\n")
- bitofs=len(conditionals)
+ bitofs=len(header_data.conditionals)
enum_vals=[]
- for xv in enums:
- x=enums[xv]
+ for xv in header_data.enums:
+ x=header_data.enums[xv]
bits=1
amt = len(x);
# print(x)
@@ -985,70 +1017,70 @@ def build_legacygl_header( filename, include, class_suffix, output_attribs ):
fd.write("\t\tstatic const Enum *_enums=NULL;\n")
fd.write("\t\tstatic const EnumValue *_enum_values=NULL;\n")
- if (len(conditionals)):
+ if (len(header_data.conditionals)):
fd.write("\t\tstatic const char* _conditional_strings[]={\n")
- if (len(conditionals)):
- for x in conditionals:
+ if (len(header_data.conditionals)):
+ for x in header_data.conditionals:
fd.write("\t\t\t\"#define "+x+"\\n\",\n");
fd.write("\t\t};\n\n");
else:
fd.write("\t\tstatic const char **_conditional_strings=NULL;\n")
- if (len(uniforms)):
+ if (len(header_data.uniforms)):
fd.write("\t\tstatic const char* _uniform_strings[]={\n")
- if (len(uniforms)):
- for x in uniforms:
+ if (len(header_data.uniforms)):
+ for x in header_data.uniforms:
fd.write("\t\t\t\""+x+"\",\n");
fd.write("\t\t};\n\n");
else:
fd.write("\t\tstatic const char **_uniform_strings=NULL;\n")
if output_attribs:
- if (len(attributes)):
+ if (len(header_data.attributes)):
fd.write("\t\tstatic AttributePair _attribute_pairs[]={\n")
- for x in attributes:
+ for x in header_data.attributes:
fd.write("\t\t\t{\""+x[0]+"\","+x[1]+"},\n");
fd.write("\t\t};\n\n");
else:
fd.write("\t\tstatic AttributePair *_attribute_pairs=NULL;\n")
- if (len(texunits)):
+ if (len(header_data.texunits)):
fd.write("\t\tstatic TexUnitPair _texunit_pairs[]={\n")
- for x in texunits:
+ for x in header_data.texunits:
fd.write("\t\t\t{\""+x[0]+"\","+x[1]+"},\n");
fd.write("\t\t};\n\n");
else:
fd.write("\t\tstatic TexUnitPair *_texunit_pairs=NULL;\n")
fd.write("\t\tstatic const char _vertex_code[]={\n")
- for x in vertex_lines:
+ for x in header_data.vertex_lines:
for i in range(len(x)):
fd.write(str(ord(x[i]))+",");
fd.write(str(ord('\n'))+",");
fd.write("\t\t0};\n\n");
- fd.write("\t\tstatic const int _vertex_code_start="+str(vertex_offset)+";\n")
+ fd.write("\t\tstatic const int _vertex_code_start="+str(header_data.vertex_offset)+";\n")
fd.write("\t\tstatic const char _fragment_code[]={\n")
- for x in fragment_lines:
+ for x in header_data.fragment_lines:
for i in range(len(x)):
fd.write(str(ord(x[i]))+",");
fd.write(str(ord('\n'))+",");
fd.write("\t\t0};\n\n");
- fd.write("\t\tstatic const int _fragment_code_start="+str(fragment_offset)+";\n")
+ fd.write("\t\tstatic const int _fragment_code_start="+str(header_data.fragment_offset)+";\n")
if output_attribs:
- fd.write("\t\tsetup(_conditional_strings,"+str(len(conditionals))+",_uniform_strings,"+str(len(uniforms))+",_attribute_pairs,"+str(len(attributes))+", _texunit_pairs,"+str(len(texunits))+",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n")
+ fd.write("\t\tsetup(_conditional_strings,"+str(len(header_data.conditionals))+",_uniform_strings,"+str(len(header_data.uniforms))+",_attribute_pairs,"+str(len(header_data.attributes))+", _texunit_pairs,"+str(len(header_data.texunits))+",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n")
else:
- fd.write("\t\tsetup(_conditional_strings,"+str(len(conditionals))+",_uniform_strings,"+str(len(uniforms))+",_texunit_pairs,"+str(len(texunits))+",_enums,"+str(len(enums))+",_enum_values,"+str(enum_value_count)+",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n")
+ fd.write("\t\tsetup(_conditional_strings,"+str(len(header_data.conditionals))+",_uniform_strings,"+str(len(header_data.uniforms))+",_texunit_pairs,"+str(len(header_data.texunits))+",_enums,"+str(len(header_data.enums))+",_enum_values,"+str(enum_value_count)+",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n")
fd.write("\t};\n\n")
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 6043807db3..87afe3d5a4 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -1236,9 +1236,9 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
options->get_popup()->add_item("Cursor Rotate X",MENU_OPTION_CURSOR_ROTATE_X,KEY_A);
options->get_popup()->add_item("Cursor Rotate Y",MENU_OPTION_CURSOR_ROTATE_Y,KEY_S);
options->get_popup()->add_item("Cursor Rotate Z",MENU_OPTION_CURSOR_ROTATE_Z,KEY_D);
- options->get_popup()->add_item("Cursor Back Rotate X",MENU_OPTION_CURSOR_ROTATE_X,KEY_MASK_SHIFT+KEY_A);
- options->get_popup()->add_item("Cursor Back Rotate Y",MENU_OPTION_CURSOR_ROTATE_Y,KEY_MASK_SHIFT+KEY_S);
- options->get_popup()->add_item("Cursor Back Rotate Z",MENU_OPTION_CURSOR_ROTATE_Z,KEY_MASK_SHIFT+KEY_D);
+ options->get_popup()->add_item("Cursor Back Rotate X",MENU_OPTION_CURSOR_BACK_ROTATE_X,KEY_MASK_SHIFT+KEY_A);
+ options->get_popup()->add_item("Cursor Back Rotate Y",MENU_OPTION_CURSOR_BACK_ROTATE_Y,KEY_MASK_SHIFT+KEY_S);
+ options->get_popup()->add_item("Cursor Back Rotate Z",MENU_OPTION_CURSOR_BACK_ROTATE_Z,KEY_MASK_SHIFT+KEY_D);
options->get_popup()->add_item("Cursor Clear Rotation",MENU_OPTION_CURSOR_CLEAR_ROTATION,KEY_W);
options->get_popup()->add_separator();
options->get_popup()->add_check_item("Duplicate Selects",MENU_OPTION_DUPLICATE_SELECTS);
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 366ec6321d..7d7fd174e5 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -102,9 +102,7 @@ def configure(env):
env["x86_opt_gcc"]=True
if env['PLATFORM'] == 'win32':
- import methods
env.Tool('gcc')
- #env['SPAWN'] = methods.win32_spawn
env['SHLIBSUFFIX'] = '.so'
@@ -127,7 +125,6 @@ def configure(env):
gcc_path=env["ANDROID_NDK_ROOT"]+"/toolchains/"+env["NDK_TARGET"]+"/prebuilt/";
- import os
if (sys.platform.find("linux")==0):
if (platform.architecture()[0]=='64bit' or os.path.isdir(gcc_path+"linux-x86_64/bin")): # check was not working
gcc_path=gcc_path+"/linux-x86_64/bin"
@@ -136,6 +133,7 @@ def configure(env):
elif (sys.platform=="darwin"):
gcc_path=gcc_path+"/darwin-x86_64/bin" #this may be wrong
env['SHLINKFLAGS'][1] = '-shared'
+ env['SHLIBSUFFIX'] = '.so'
elif (os.name=="nt"):
gcc_path=gcc_path+"/windows-x86_64/bin" #this may be wrong
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 60f4e61c68..bd63cadc57 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -251,7 +251,7 @@ public:
virtual String get_device_info(int p_device) const;
virtual Error run(int p_device,int p_flags=0);
- virtual bool requieres_password(bool p_debug) const { return !p_debug; }
+ virtual bool requires_password(bool p_debug) const { return !p_debug; }
virtual String get_binary_extension() const { return "apk"; }
virtual Error export_project(const String& p_path, bool p_debug, int p_flags=0);
diff --git a/platform/bb10/export/export.cpp b/platform/bb10/export/export.cpp
index a17c4cb134..da3a958a56 100644
--- a/platform/bb10/export/export.cpp
+++ b/platform/bb10/export/export.cpp
@@ -69,7 +69,7 @@ public:
virtual String get_device_info(int p_device) const;
virtual Error run(int p_device,int p_flags=0);
- virtual bool requieres_password(bool p_debug) const { return !p_debug; }
+ virtual bool requires_password(bool p_debug) const { return !p_debug; }
virtual String get_binary_extension() const { return "bar"; }
virtual Error export_project(const String& p_path,bool p_debug,int p_flags=0);
diff --git a/platform/iphone/Appirater.m b/platform/iphone/Appirater.m
index d9144eda3e..951b892032 100644
--- a/platform/iphone/Appirater.m
+++ b/platform/iphone/Appirater.m
@@ -1,9 +1,7 @@
-#ifdef APPIRATER_ENABLED
-
/*
This file is part of Appirater.
- Copyright (c) 2010, Arash Payan
+ Copyright (c) 2012, Arash Payan
All rights reserved.
Permission is hereby granted, free of charge, to any person
@@ -33,13 +31,17 @@
*
* Created by Arash Payan on 9/5/09.
* http://arashpayan.com
- * Copyright 2010 Arash Payan. All rights reserved.
+ * Copyright 2012 Arash Payan. All rights reserved.
*/
#import "Appirater.h"
#import <SystemConfiguration/SCNetworkReachability.h>
#include <netinet/in.h>
+#if ! __has_feature(objc_arc)
+#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
NSString *const kAppiraterFirstUseDate = @"kAppiraterFirstUseDate";
NSString *const kAppiraterUseCount = @"kAppiraterUseCount";
NSString *const kAppiraterSignificantEventCount = @"kAppiraterSignificantEventCount";
@@ -49,18 +51,175 @@ NSString *const kAppiraterDeclinedToRate = @"kAppiraterDeclinedToRate";
NSString *const kAppiraterReminderRequestDate = @"kAppiraterReminderRequestDate";
NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID";
+NSString *templateReviewURLiOS7 = @"itms-apps://itunes.apple.com/app/idAPP_ID";
+NSString *templateReviewURLiOS8 = @"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software";
-static int app_id = 0;
+static NSString *_appId;
+static double _daysUntilPrompt = 30;
+static NSInteger _usesUntilPrompt = 20;
+static NSInteger _significantEventsUntilPrompt = -1;
+static double _timeBeforeReminding = 1;
+static BOOL _debug = NO;
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
+ static id<AppiraterDelegate> _delegate;
+#else
+ __weak static id<AppiraterDelegate> _delegate;
+#endif
+static BOOL _usesAnimation = TRUE;
+static UIStatusBarStyle _statusBarStyle;
+static BOOL _modalOpen = false;
+static BOOL _alwaysUseMainBundle = NO;
-@interface Appirater (hidden)
+@interface Appirater ()
+@property (nonatomic, copy) NSString *alertTitle;
+@property (nonatomic, copy) NSString *alertMessage;
+@property (nonatomic, copy) NSString *alertCancelTitle;
+@property (nonatomic, copy) NSString *alertRateTitle;
+@property (nonatomic, copy) NSString *alertRateLaterTitle;
- (BOOL)connectedToNetwork;
+ (Appirater*)sharedInstance;
+- (void)showPromptWithChecks:(BOOL)withChecks
+ displayRateLaterButton:(BOOL)displayRateLaterButton;
+- (void)showRatingAlert:(BOOL)displayRateLaterButton;
- (void)showRatingAlert;
+- (BOOL)ratingAlertIsAppropriate;
- (BOOL)ratingConditionsHaveBeenMet;
- (void)incrementUseCount;
+- (void)hideRatingAlert;
@end
-@implementation Appirater (hidden)
+@implementation Appirater
+
+@synthesize ratingAlert;
+
++ (void) setAppId:(NSString *)appId {
+ _appId = appId;
+}
+
++ (void) setDaysUntilPrompt:(double)value {
+ _daysUntilPrompt = value;
+}
+
++ (void) setUsesUntilPrompt:(NSInteger)value {
+ _usesUntilPrompt = value;
+}
+
++ (void) setSignificantEventsUntilPrompt:(NSInteger)value {
+ _significantEventsUntilPrompt = value;
+}
+
++ (void) setTimeBeforeReminding:(double)value {
+ _timeBeforeReminding = value;
+}
+
++ (void) setCustomAlertTitle:(NSString *)title
+{
+ [self sharedInstance].alertTitle = title;
+}
+
++ (void) setCustomAlertMessage:(NSString *)message
+{
+ [self sharedInstance].alertMessage = message;
+}
+
++ (void) setCustomAlertCancelButtonTitle:(NSString *)cancelTitle
+{
+ [self sharedInstance].alertCancelTitle = cancelTitle;
+}
+
++ (void) setCustomAlertRateButtonTitle:(NSString *)rateTitle
+{
+ [self sharedInstance].alertRateTitle = rateTitle;
+}
+
++ (void) setCustomAlertRateLaterButtonTitle:(NSString *)rateLaterTitle
+{
+ [self sharedInstance].alertRateLaterTitle = rateLaterTitle;
+}
+
++ (void) setDebug:(BOOL)debug {
+ _debug = debug;
+}
++ (void)setDelegate:(id<AppiraterDelegate>)delegate{
+ _delegate = delegate;
+}
++ (void)setUsesAnimation:(BOOL)animation {
+ _usesAnimation = animation;
+}
++ (void)setOpenInAppStore:(BOOL)openInAppStore {
+ [Appirater sharedInstance].openInAppStore = openInAppStore;
+}
++ (void)setStatusBarStyle:(UIStatusBarStyle)style {
+ _statusBarStyle = style;
+}
++ (void)setModalOpen:(BOOL)open {
+ _modalOpen = open;
+}
++ (void)setAlwaysUseMainBundle:(BOOL)alwaysUseMainBundle {
+ _alwaysUseMainBundle = alwaysUseMainBundle;
+}
+
++ (NSBundle *)bundle
+{
+ NSBundle *bundle;
+
+ if (_alwaysUseMainBundle) {
+ bundle = [NSBundle mainBundle];
+ } else {
+ NSURL *appiraterBundleURL = [[NSBundle mainBundle] URLForResource:@"Appirater" withExtension:@"bundle"];
+
+ if (appiraterBundleURL) {
+ // Appirater.bundle will likely only exist when used via CocoaPods
+ bundle = [NSBundle bundleWithURL:appiraterBundleURL];
+ } else {
+ bundle = [NSBundle mainBundle];
+ }
+ }
+
+ return bundle;
+}
+
+- (NSString *)alertTitle
+{
+ return _alertTitle ? _alertTitle : APPIRATER_MESSAGE_TITLE;
+}
+
+- (NSString *)alertMessage
+{
+ return _alertMessage ? _alertMessage : APPIRATER_MESSAGE;
+}
+
+- (NSString *)alertCancelTitle
+{
+ return _alertCancelTitle ? _alertCancelTitle : APPIRATER_CANCEL_BUTTON;
+}
+
+- (NSString *)alertRateTitle
+{
+ return _alertRateTitle ? _alertRateTitle : APPIRATER_RATE_BUTTON;
+}
+
+- (NSString *)alertRateLaterTitle
+{
+ return _alertRateLaterTitle ? _alertRateLaterTitle : APPIRATER_RATE_LATER;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ if ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0) {
+ self.openInAppStore = YES;
+ } else {
+ self.openInAppStore = NO;
+ }
+ }
+
+ return self;
+}
- (BOOL)connectedToNetwork {
// Create zero addy
@@ -73,7 +232,7 @@ static int app_id = 0;
SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
SCNetworkReachabilityFlags flags;
- BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
+ Boolean didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
CFRelease(defaultRouteReachability);
if (!didRetrieveFlags)
@@ -97,61 +256,110 @@ static int app_id = 0;
static Appirater *appirater = nil;
if (appirater == nil)
{
- @synchronized(self) {
- if (appirater == nil) {
- appirater = [[Appirater alloc] init];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive) name:@"UIApplicationWillResignActiveNotification" object:nil];
- }
- }
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ appirater = [[Appirater alloc] init];
+ appirater.delegate = _delegate;
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive) name:
+ UIApplicationWillResignActiveNotification object:nil];
+ });
}
return appirater;
}
-- (void)showRatingAlert {
- UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:APPIRATER_MESSAGE_TITLE
- message:APPIRATER_MESSAGE
- delegate:self
- cancelButtonTitle:APPIRATER_CANCEL_BUTTON
- otherButtonTitles:APPIRATER_RATE_BUTTON, APPIRATER_RATE_LATER, nil] autorelease];
+- (void)showRatingAlert:(BOOL)displayRateLaterButton {
+ UIAlertView *alertView = nil;
+ id <AppiraterDelegate> delegate = _delegate;
+
+ if(delegate && [delegate respondsToSelector:@selector(appiraterShouldDisplayAlert:)] && ![delegate appiraterShouldDisplayAlert:self]) {
+ return;
+ }
+
+ if (displayRateLaterButton) {
+ alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
+ message:self.alertMessage
+ delegate:self
+ cancelButtonTitle:self.alertCancelTitle
+ otherButtonTitles:self.alertRateTitle, self.alertRateLaterTitle, nil];
+ } else {
+ alertView = [[UIAlertView alloc] initWithTitle:self.alertTitle
+ message:self.alertMessage
+ delegate:self
+ cancelButtonTitle:self.alertCancelTitle
+ otherButtonTitles:self.alertRateTitle, nil];
+ }
+
self.ratingAlert = alertView;
- [alertView show];
+ [alertView show];
+
+ if (delegate && [delegate respondsToSelector:@selector(appiraterDidDisplayAlert:)]) {
+ [delegate appiraterDidDisplayAlert:self];
+ }
+}
+
+- (void)showRatingAlert
+{
+ [self showRatingAlert:true];
}
+// is this an ok time to show the alert? (regardless of whether the rating conditions have been met)
+//
+// things checked here:
+// * connectivity with network
+// * whether user has rated before
+// * whether user has declined to rate
+// * whether rating alert is currently showing visibly
+// things NOT checked here:
+// * time since first launch
+// * number of uses of app
+// * number of significant events
+// * time since last reminder
+- (BOOL)ratingAlertIsAppropriate {
+ return ([self connectedToNetwork]
+ && ![self userHasDeclinedToRate]
+ && !self.ratingAlert.visible
+ && ![self userHasRatedCurrentVersion]);
+}
+
+// have the rating conditions been met/earned? (regardless of whether this would be a moment when it's appropriate to show a new rating alert)
+//
+// things checked here:
+// * time since first launch
+// * number of uses of app
+// * number of significant events
+// * time since last reminder
+// things NOT checked here:
+// * connectivity with network
+// * whether user has rated before
+// * whether user has declined to rate
+// * whether rating alert is currently showing visibly
- (BOOL)ratingConditionsHaveBeenMet {
- if (APPIRATER_DEBUG)
+ if (_debug)
return YES;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDate *dateOfFirstLaunch = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterFirstUseDate]];
NSTimeInterval timeSinceFirstLaunch = [[NSDate date] timeIntervalSinceDate:dateOfFirstLaunch];
- NSTimeInterval timeUntilRate = 60 * 60 * 24 * APPIRATER_DAYS_UNTIL_PROMPT;
+ NSTimeInterval timeUntilRate = 60 * 60 * 24 * _daysUntilPrompt;
if (timeSinceFirstLaunch < timeUntilRate)
return NO;
// check if the app has been used enough
- int useCount = [userDefaults integerForKey:kAppiraterUseCount];
- if (useCount <= APPIRATER_USES_UNTIL_PROMPT)
+ NSInteger useCount = [userDefaults integerForKey:kAppiraterUseCount];
+ if (useCount < _usesUntilPrompt)
return NO;
// check if the user has done enough significant events
- int sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount];
- if (sigEventCount <= APPIRATER_SIG_EVENTS_UNTIL_PROMPT)
- return NO;
-
- // has the user previously declined to rate this version of the app?
- if ([userDefaults boolForKey:kAppiraterDeclinedToRate])
- return NO;
-
- // has the user already rated the app?
- if ([userDefaults boolForKey:kAppiraterRatedCurrentVersion])
+ NSInteger sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount];
+ if (sigEventCount < _significantEventsUntilPrompt)
return NO;
// if the user wanted to be reminded later, has enough time passed?
NSDate *reminderRequestDate = [NSDate dateWithTimeIntervalSince1970:[userDefaults doubleForKey:kAppiraterReminderRequestDate]];
NSTimeInterval timeSinceReminderRequest = [[NSDate date] timeIntervalSinceDate:reminderRequestDate];
- NSTimeInterval timeUntilReminder = 60 * 60 * 24 * APPIRATER_TIME_BEFORE_REMINDING;
+ NSTimeInterval timeUntilReminder = 60 * 60 * 24 * _timeBeforeReminding;
if (timeSinceReminderRequest < timeUntilReminder)
return NO;
@@ -171,7 +379,7 @@ static int app_id = 0;
[userDefaults setObject:version forKey:kAppiraterCurrentVersion];
}
- if (APPIRATER_DEBUG)
+ if (_debug)
NSLog(@"APPIRATER Tracking version: %@", trackingVersion);
if ([trackingVersion isEqualToString:version])
@@ -185,11 +393,11 @@ static int app_id = 0;
}
// increment the use count
- int useCount = [userDefaults integerForKey:kAppiraterUseCount];
+ NSInteger useCount = [userDefaults integerForKey:kAppiraterUseCount];
useCount++;
[userDefaults setInteger:useCount forKey:kAppiraterUseCount];
- if (APPIRATER_DEBUG)
- NSLog(@"APPIRATER Use count: %d", useCount);
+ if (_debug)
+ NSLog(@"APPIRATER Use count: %@", @(useCount));
}
else
{
@@ -219,7 +427,7 @@ static int app_id = 0;
[userDefaults setObject:version forKey:kAppiraterCurrentVersion];
}
- if (APPIRATER_DEBUG)
+ if (_debug)
NSLog(@"APPIRATER Tracking version: %@", trackingVersion);
if ([trackingVersion isEqualToString:version])
@@ -233,11 +441,11 @@ static int app_id = 0;
}
// increment the significant event count
- int sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount];
+ NSInteger sigEventCount = [userDefaults integerForKey:kAppiraterSignificantEventCount];
sigEventCount++;
[userDefaults setInteger:sigEventCount forKey:kAppiraterSignificantEventCount];
- if (APPIRATER_DEBUG)
- NSLog(@"APPIRATER Significant event count: %d", sigEventCount);
+ if (_debug)
+ NSLog(@"APPIRATER Significant event count: %@", @(sigEventCount));
}
else
{
@@ -254,105 +462,214 @@ static int app_id = 0;
[userDefaults synchronize];
}
-@end
-
-
-@interface Appirater ()
-- (void)hideRatingAlert;
-@end
-
-@implementation Appirater
-
-@synthesize ratingAlert;
-
-- (void)incrementAndRate:(NSNumber*)_canPromptForRating {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
+- (void)incrementAndRate:(BOOL)canPromptForRating {
[self incrementUseCount];
- if ([_canPromptForRating boolValue] == YES &&
- [self ratingConditionsHaveBeenMet] &&
- [self connectedToNetwork])
+ if (canPromptForRating &&
+ [self ratingConditionsHaveBeenMet] &&
+ [self ratingAlertIsAppropriate])
{
- [self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO];
+ dispatch_async(dispatch_get_main_queue(),
+ ^{
+ [self showRatingAlert];
+ });
}
-
- [pool release];
}
-- (void)incrementSignificantEventAndRate:(NSNumber*)_canPromptForRating {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
+- (void)incrementSignificantEventAndRate:(BOOL)canPromptForRating {
[self incrementSignificantEventCount];
- if ([_canPromptForRating boolValue] == YES &&
- [self ratingConditionsHaveBeenMet] &&
- [self connectedToNetwork])
+ if (canPromptForRating &&
+ [self ratingConditionsHaveBeenMet] &&
+ [self ratingAlertIsAppropriate])
{
- [self performSelectorOnMainThread:@selector(showRatingAlert) withObject:nil waitUntilDone:NO];
+ dispatch_async(dispatch_get_main_queue(),
+ ^{
+ [self showRatingAlert];
+ });
}
-
- [pool release];
}
-+ (void)appLaunched:(int)p_app_id {
- app_id = p_app_id;
+- (BOOL)userHasDeclinedToRate {
+ return [[NSUserDefaults standardUserDefaults] boolForKey:kAppiraterDeclinedToRate];
+}
+
+- (BOOL)userHasRatedCurrentVersion {
+ return [[NSUserDefaults standardUserDefaults] boolForKey:kAppiraterRatedCurrentVersion];
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-implementations"
++ (void)appLaunched {
[Appirater appLaunched:YES];
}
+#pragma GCC diagnostic pop
-+ (void)appLaunched:(BOOL)canPromptForRating app_id:(int)p_app_id {
- app_id = p_app_id;
- NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating];
- [NSThread detachNewThreadSelector:@selector(incrementAndRate:)
- toTarget:[Appirater sharedInstance]
- withObject:_canPromptForRating];
- [_canPromptForRating release];
++ (void)appLaunched:(BOOL)canPromptForRating {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
+ ^{
+ Appirater *a = [Appirater sharedInstance];
+ if (_debug) {
+ dispatch_async(dispatch_get_main_queue(),
+ ^{
+ [a showRatingAlert];
+ });
+ } else {
+ [a incrementAndRate:canPromptForRating];
+ }
+ });
}
- (void)hideRatingAlert {
if (self.ratingAlert.visible) {
- if (APPIRATER_DEBUG)
+ if (_debug)
NSLog(@"APPIRATER Hiding Alert");
[self.ratingAlert dismissWithClickedButtonIndex:-1 animated:NO];
}
}
+ (void)appWillResignActive {
- if (APPIRATER_DEBUG)
+ if (_debug)
NSLog(@"APPIRATER appWillResignActive");
[[Appirater sharedInstance] hideRatingAlert];
}
+ (void)appEnteredForeground:(BOOL)canPromptForRating {
- NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating];
- [NSThread detachNewThreadSelector:@selector(incrementAndRate:)
- toTarget:[Appirater sharedInstance]
- withObject:_canPromptForRating];
- [_canPromptForRating release];
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
+ ^{
+ [[Appirater sharedInstance] incrementAndRate:canPromptForRating];
+ });
}
+ (void)userDidSignificantEvent:(BOOL)canPromptForRating {
- NSNumber *_canPromptForRating = [[NSNumber alloc] initWithBool:canPromptForRating];
- [NSThread detachNewThreadSelector:@selector(incrementSignificantEventAndRate:)
- toTarget:[Appirater sharedInstance]
- withObject:_canPromptForRating];
- [_canPromptForRating release];
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
+ ^{
+ [[Appirater sharedInstance] incrementSignificantEventAndRate:canPromptForRating];
+ });
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-implementations"
++ (void)showPrompt {
+ [Appirater tryToShowPrompt];
+}
+#pragma GCC diagnostic pop
+
++ (void)tryToShowPrompt {
+ [[Appirater sharedInstance] showPromptWithChecks:true
+ displayRateLaterButton:true];
+}
+
++ (void)forceShowPrompt:(BOOL)displayRateLaterButton {
+ [[Appirater sharedInstance] showPromptWithChecks:false
+ displayRateLaterButton:displayRateLaterButton];
+}
+
+- (void)showPromptWithChecks:(BOOL)withChecks
+ displayRateLaterButton:(BOOL)displayRateLaterButton {
+ if (withChecks == NO || [self ratingAlertIsAppropriate]) {
+ [self showRatingAlert:displayRateLaterButton];
+ }
+}
+
++ (id)getRootViewController {
+ UIWindow *window = [[UIApplication sharedApplication] keyWindow];
+ if (window.windowLevel != UIWindowLevelNormal) {
+ NSArray *windows = [[UIApplication sharedApplication] windows];
+ for(window in windows) {
+ if (window.windowLevel == UIWindowLevelNormal) {
+ break;
+ }
+ }
+ }
+
+ return [Appirater iterateSubViewsForViewController:window]; // iOS 8+ deep traverse
+}
+
++ (id)iterateSubViewsForViewController:(UIView *) parentView {
+ for (UIView *subView in [parentView subviews]) {
+ UIResponder *responder = [subView nextResponder];
+ if([responder isKindOfClass:[UIViewController class]]) {
+ return [self topMostViewController: (UIViewController *) responder];
+ }
+ id found = [Appirater iterateSubViewsForViewController:subView];
+ if( nil != found) {
+ return found;
+ }
+ }
+ return nil;
+}
+
++ (UIViewController *) topMostViewController: (UIViewController *) controller {
+ BOOL isPresenting = NO;
+ do {
+ // this path is called only on iOS 6+, so -presentedViewController is fine here.
+ UIViewController *presented = [controller presentedViewController];
+ isPresenting = presented != nil;
+ if(presented != nil) {
+ controller = presented;
+ }
+
+ } while (isPresenting);
+
+ return controller;
}
+ (void)rateApp {
-#if TARGET_IPHONE_SIMULATOR
- NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page.");
-#else
+
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
- NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%d", app_id]];
[userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion];
[userDefaults synchronize];
- [[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]];
+
+ //Use the in-app StoreKit view if available (iOS 6) and imported. This works in the simulator.
+ if (![Appirater sharedInstance].openInAppStore && NSStringFromClass([SKStoreProductViewController class]) != nil) {
+
+ SKStoreProductViewController *storeViewController = [[SKStoreProductViewController alloc] init];
+ NSNumber *appId = [NSNumber numberWithInteger:_appId.integerValue];
+ [storeViewController loadProductWithParameters:@{SKStoreProductParameterITunesItemIdentifier:appId} completionBlock:nil];
+ storeViewController.delegate = self.sharedInstance;
+
+ id <AppiraterDelegate> delegate = self.sharedInstance.delegate;
+ if ([delegate respondsToSelector:@selector(appiraterWillPresentModalView:animated:)]) {
+ [delegate appiraterWillPresentModalView:self.sharedInstance animated:_usesAnimation];
+ }
+ [[self getRootViewController] presentViewController:storeViewController animated:_usesAnimation completion:^{
+ [self setModalOpen:YES];
+ //Temporarily use a black status bar to match the StoreKit view.
+ [self setStatusBarStyle:[UIApplication sharedApplication].statusBarStyle];
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
+ [[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent animated:_usesAnimation];
#endif
+ }];
+
+ //Use the standard openUrl method if StoreKit is unavailable.
+ } else {
+
+ #if TARGET_IPHONE_SIMULATOR
+ NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page.");
+ #else
+ NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]];
+
+ // iOS 7 needs a different templateReviewURL @see https://github.com/arashpayan/appirater/issues/131
+ // Fixes condition @see https://github.com/arashpayan/appirater/issues/205
+ if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
+ reviewURL = [templateReviewURLiOS7 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]];
+ }
+ // iOS 8 needs a different templateReviewURL also @see https://github.com/arashpayan/appirater/issues/182
+ else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
+ {
+ reviewURL = [templateReviewURLiOS8 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", _appId]];
+ }
+
+ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]];
+ #endif
+ }
}
-- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
+- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+
+ id <AppiraterDelegate> delegate = _delegate;
switch (buttonIndex) {
case 0:
@@ -360,24 +677,56 @@ static int app_id = 0;
// they don't want to rate it
[userDefaults setBool:YES forKey:kAppiraterDeclinedToRate];
[userDefaults synchronize];
+ if(delegate && [delegate respondsToSelector:@selector(appiraterDidDeclineToRate:)]){
+ [delegate appiraterDidDeclineToRate:self];
+ }
break;
}
case 1:
{
// they want to rate it
[Appirater rateApp];
+ if(delegate&& [delegate respondsToSelector:@selector(appiraterDidOptToRate:)]){
+ [delegate appiraterDidOptToRate:self];
+ }
break;
}
case 2:
// remind them later
[userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterReminderRequestDate];
[userDefaults synchronize];
+ if(delegate && [delegate respondsToSelector:@selector(appiraterDidOptToRemindLater:)]){
+ [delegate appiraterDidOptToRemindLater:self];
+ }
break;
default:
break;
}
}
-@end
+//Delegate call from the StoreKit view.
+- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
+ [Appirater closeModal];
+}
-#endif
+//Close the in-app rating (StoreKit) view and restore the previous status bar style.
++ (void)closeModal {
+ if (_modalOpen) {
+ [[UIApplication sharedApplication]setStatusBarStyle:_statusBarStyle animated:_usesAnimation];
+ BOOL usedAnimation = _usesAnimation;
+ [self setModalOpen:NO];
+
+ // get the top most controller (= the StoreKit Controller) and dismiss it
+ UIViewController *presentingController = [UIApplication sharedApplication].keyWindow.rootViewController;
+ presentingController = [self topMostViewController: presentingController];
+ [presentingController dismissViewControllerAnimated:_usesAnimation completion:^{
+ id <AppiraterDelegate> delegate = self.sharedInstance.delegate;
+ if ([delegate respondsToSelector:@selector(appiraterDidDismissModalView:animated:)]) {
+ [delegate appiraterDidDismissModalView:(Appirater *)self animated:usedAnimation];
+ }
+ }];
+ [self.class setStatusBarStyle:(UIStatusBarStyle)nil];
+ }
+}
+
+@end
diff --git a/platform/iphone/AppiraterDelegate.h b/platform/iphone/AppiraterDelegate.h
new file mode 100644
index 0000000000..cbe0cfad5b
--- /dev/null
+++ b/platform/iphone/AppiraterDelegate.h
@@ -0,0 +1,23 @@
+//
+// AppiraterDelegate.h
+// Banana Stand
+//
+// Created by Robert Haining on 9/25/12.
+// Copyright (c) 2012 News.me. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class Appirater;
+
+@protocol AppiraterDelegate <NSObject>
+
+@optional
+-(BOOL)appiraterShouldDisplayAlert:(Appirater *)appirater;
+-(void)appiraterDidDisplayAlert:(Appirater *)appirater;
+-(void)appiraterDidDeclineToRate:(Appirater *)appirater;
+-(void)appiraterDidOptToRate:(Appirater *)appirater;
+-(void)appiraterDidOptToRemindLater:(Appirater *)appirater;
+-(void)appiraterWillPresentModalView:(Appirater *)appirater animated:(BOOL)animated;
+-(void)appiraterDidDismissModalView:(Appirater *)appirater animated:(BOOL)animated;
+@end
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index 922a324694..5b2e1533da 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -13,7 +13,8 @@ iphone_lib = [
'game_center.mm',
'in_app_store.mm',
'icloud.mm',
- 'Appirater.m',
+ #'Appirater.m',
+ 'ios.mm',
]
#env.Depends('#core/math/vector3.h', 'vector3_psp.h')
@@ -27,8 +28,8 @@ if env['ios_gles22_override'] == "yes":
env_ios.Append(CPPFLAGS=['-DGLES2_OVERRIDE'])
-if env['ios_appirater'] == "yes":
- env_ios.Append(CPPFLAGS=['-DAPPIRATER_ENABLED'])
+#if env['ios_appirater'] == "yes":
+# env_ios.Append(CPPFLAGS=['-DAPPIRATER_ENABLED'])
obj = env_ios.Object('godot_iphone.cpp')
diff --git a/platform/iphone/ios.h b/platform/iphone/ios.h
new file mode 100644
index 0000000000..0e4661520b
--- /dev/null
+++ b/platform/iphone/ios.h
@@ -0,0 +1,20 @@
+#ifndef IOS_H
+#define IOS_H
+
+#include "core/object.h"
+
+class iOS : public Object {
+
+ OBJ_TYPE(iOS, Object);
+
+ static void _bind_methods();
+
+public:
+
+ String get_rate_url(int p_app_id) const;
+
+ iOS();
+
+};
+
+#endif
diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm
new file mode 100644
index 0000000000..deb63feacf
--- /dev/null
+++ b/platform/iphone/ios.mm
@@ -0,0 +1,36 @@
+#include "ios.h"
+
+#import <UIKit/UIKit.h>
+
+void iOS::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("get_rate_url","app_id"),&iOS::get_rate_url);
+};
+
+String iOS::get_rate_url(int p_app_id) const {
+ String templ = "itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID";
+ String templ_iOS7 = "itms-apps://itunes.apple.com/app/idAPP_ID";
+ String templ_iOS8 = "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software";
+
+ //ios7 before
+ String ret = templ;
+
+ // iOS 7 needs a different templateReviewURL @see https://github.com/arashpayan/appirater/issues/131
+ if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 7.1)
+ {
+ ret = templ_iOS7;
+ }
+ // iOS 8 needs a different templateReviewURL also @see https://github.com/arashpayan/appirater/issues/182
+ else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
+ {
+ ret = templ_iOS8;
+ }
+
+ // ios7 for everything?
+ ret = templ_iOS7.replace("APP_ID", String::num(p_app_id));
+
+ printf("returning rate url %ls\n", ret.c_str());
+ return ret;
+};
+
+iOS::iOS() {};
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index 56dffc8aa4..93496e8225 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -46,6 +46,8 @@
#include "sem_iphone.h"
+#include "ios.h"
+
int OSIPhone::get_video_driver_count() const {
return 1;
@@ -167,6 +169,7 @@ void OSIPhone::initialize(const VideoMode& p_desired,int p_video_driver,int p_au
Globals::get_singleton()->add_singleton(Globals::Singleton("ICloud", icloud));
//icloud->connect();
#endif
+ Globals::get_singleton()->add_singleton(Globals::Singleton("iOS", memnew(iOS)));
};
MainLoop *OSIPhone::get_main_loop() const {
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index e055aeea56..de57d770c4 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -86,7 +86,7 @@ public:
virtual String get_device_info(int p_device) const { return "Run exported HTML in the system's default browser."; }
virtual Error run(int p_device,int p_flags=0);
- virtual bool requieres_password(bool p_debug) const { return false; }
+ virtual bool requires_password(bool p_debug) const { return false; }
virtual String get_binary_extension() const { return "html"; }
virtual Error export_project(const String& p_path,bool p_debug,int p_flags=0);
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 0bece6ec76..3bc4ebdb1a 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -59,7 +59,7 @@ public:
virtual String get_device_info(int p_device) const { return String(); }
virtual Error run(int p_device,int p_flags=0);
- virtual bool requieres_password(bool p_debug) const { return false; }
+ virtual bool requires_password(bool p_debug) const { return false; }
virtual String get_binary_extension() const { return "zip"; }
virtual Error export_project(const String& p_path,bool p_debug,int p_flags=0);
diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm
index 391fd1f74a..7beb5248b4 100644
--- a/platform/osx/godot_main_osx.mm
+++ b/platform/osx/godot_main_osx.mm
@@ -36,6 +36,14 @@
int main(int argc, char** argv) {
+ int first_arg = 1;
+ const char* dbg_arg = "-NSDocumentRevisionsDebugMode";
+ printf("arguments\n");
+ for (int i=0; i<argc; i++) {
+ if (strcmp(dbg_arg, argv[i]) == 0)
+ first_arg = i+2;
+ printf("%i: %s\n", i, argv[i]);
+ };
if (argc>=1 && argv[0][0]=='/') {
@@ -74,7 +82,7 @@ int main(int argc, char** argv) {
OS_OSX os;
- Error err = Main::setup(argv[0],argc-1,&argv[1]);
+ Error err = Main::setup(argv[0],argc-first_arg,&argv[first_arg]);
if (err!=OK)
return 255;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 6af2f347aa..c814932dc4 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -921,7 +921,7 @@ void OS_OSX::initialize(const VideoMode& p_desired,int p_video_driver,int p_audi
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) {
[window_view setWantsBestResolutionOpenGLSurface:YES];
- if (current_videomode.resizable)
+ //if (current_videomode.resizable)
[window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
}
#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 9def8d4f9c..d0453f0d8e 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -55,6 +55,7 @@
#include "shlobj.h"
#include <regstr.h>
+#include <process.h>
static const WORD MAX_CONSOLE_LINES = 1500;
@@ -1884,6 +1885,10 @@ Error OS_Windows::kill(const ProcessID& p_pid) {
return ret != 0?OK:FAILED;
};
+int OS_Windows::get_process_ID() const {
+ return _getpid();
+}
+
Error OS_Windows::set_cwd(const String& p_cwd) {
if (_wchdir(p_cwd.c_str())!=0)
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 3116847daa..81cf313849 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -245,6 +245,7 @@ public:
virtual Error execute(const String& p_path, const List<String>& p_arguments,bool p_blocking,ProcessID *r_child_id=NULL,String* r_pipe=NULL,int *r_exitcode=NULL);
virtual Error kill(const ProcessID& p_pid);
+ virtual int get_process_ID() const;
virtual bool has_environment(const String& p_var) const;
virtual String get_environment(const String& p_var) const;
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index dc038f010c..8f0474b765 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -602,6 +602,17 @@ real_t RigidBody2D::get_mass() const{
return mass;
}
+void RigidBody2D::set_inertia(real_t p_inertia) {
+
+ ERR_FAIL_COND(p_inertia<=0);
+ Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_INERTIA,p_inertia);
+}
+
+real_t RigidBody2D::get_inertia() const{
+
+ return Physics2DServer::get_singleton()->body_get_param(get_rid(),Physics2DServer::BODY_PARAM_INERTIA);
+}
+
void RigidBody2D::set_weight(real_t p_weight){
set_mass(p_weight/9.8);
@@ -767,9 +778,9 @@ int RigidBody2D::get_max_contacts_reported() const{
return max_contacts_reported;
}
-void RigidBody2D::apply_impulse(const Vector2& p_pos, const Vector2& p_impulse) {
+void RigidBody2D::apply_impulse(const Vector2& p_offset, const Vector2& p_impulse) {
- Physics2DServer::get_singleton()->body_apply_impulse(get_rid(),p_pos,p_impulse);
+ Physics2DServer::get_singleton()->body_apply_impulse(get_rid(),p_offset,p_impulse);
}
void RigidBody2D::set_applied_force(const Vector2& p_force) {
@@ -782,6 +793,20 @@ Vector2 RigidBody2D::get_applied_force() const {
return Physics2DServer::get_singleton()->body_get_applied_force(get_rid());
};
+void RigidBody2D::set_applied_torque(const float p_torque) {
+
+ Physics2DServer::get_singleton()->body_set_applied_torque(get_rid(), p_torque);
+};
+
+float RigidBody2D::get_applied_torque() const {
+
+ return Physics2DServer::get_singleton()->body_get_applied_torque(get_rid());
+};
+
+void RigidBody2D::add_force(const Vector2& p_offset, const Vector2& p_force) {
+
+ Physics2DServer::get_singleton()->body_add_force(get_rid(),p_offset,p_force);
+}
void RigidBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) {
@@ -858,6 +883,9 @@ void RigidBody2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_mass","mass"),&RigidBody2D::set_mass);
ObjectTypeDB::bind_method(_MD("get_mass"),&RigidBody2D::get_mass);
+ ObjectTypeDB::bind_method(_MD("get_inertia"),&RigidBody2D::get_inertia);
+ ObjectTypeDB::bind_method(_MD("set_inertia","inertia"),&RigidBody2D::set_inertia);
+
ObjectTypeDB::bind_method(_MD("set_weight","weight"),&RigidBody2D::set_weight);
ObjectTypeDB::bind_method(_MD("get_weight"),&RigidBody2D::get_weight);
@@ -895,11 +923,16 @@ void RigidBody2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_continuous_collision_detection_mode"),&RigidBody2D::get_continuous_collision_detection_mode);
ObjectTypeDB::bind_method(_MD("set_axis_velocity","axis_velocity"),&RigidBody2D::set_axis_velocity);
- ObjectTypeDB::bind_method(_MD("apply_impulse","pos","impulse"),&RigidBody2D::apply_impulse);
+ ObjectTypeDB::bind_method(_MD("apply_impulse","offset","impulse"),&RigidBody2D::apply_impulse);
ObjectTypeDB::bind_method(_MD("set_applied_force","force"),&RigidBody2D::set_applied_force);
ObjectTypeDB::bind_method(_MD("get_applied_force"),&RigidBody2D::get_applied_force);
+ ObjectTypeDB::bind_method(_MD("set_applied_torque","torque"),&RigidBody2D::set_applied_torque);
+ ObjectTypeDB::bind_method(_MD("get_applied_torque"),&RigidBody2D::get_applied_torque);
+
+ ObjectTypeDB::bind_method(_MD("add_force","offset","force"),&RigidBody2D::add_force);
+
ObjectTypeDB::bind_method(_MD("set_sleeping","sleeping"),&RigidBody2D::set_sleeping);
ObjectTypeDB::bind_method(_MD("is_sleeping"),&RigidBody2D::is_sleeping);
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 999e63dd5d..5af65bff33 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -217,6 +217,9 @@ public:
void set_mass(real_t p_mass);
real_t get_mass() const;
+ void set_inertia(real_t p_inertia);
+ real_t get_inertia() const;
+
void set_weight(real_t p_weight);
real_t get_weight() const;
@@ -261,11 +264,16 @@ public:
void set_continuous_collision_detection_mode(CCDMode p_mode);
CCDMode get_continuous_collision_detection_mode() const;
- void apply_impulse(const Vector2& p_pos, const Vector2& p_impulse);
+ void apply_impulse(const Vector2& p_offset, const Vector2& p_impulse);
void set_applied_force(const Vector2& p_force);
Vector2 get_applied_force() const;
+ void set_applied_torque(const float p_torque);
+ float get_applied_torque() const;
+
+ void add_force(const Vector2& p_offset, const Vector2& p_force);
+
Array get_colliding_bodies() const; //function for script
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index c732dd54cf..211c5961b0 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -139,6 +139,11 @@ bool AnimationTreePlayer::_set(const StringName& p_name, const Variant& p_value)
animation_node_set_master_animation(id,node.get_valid("from"));
else
animation_node_set_animation(id,node.get_valid("animation"));
+ Array filters= node.get_valid("filter");
+ for(int i=0;i<filters.size();i++) {
+
+ animation_node_set_filter_path(id,filters[i],true);
+ }
} break;
case NODE_ONESHOT: {
@@ -276,6 +281,15 @@ bool AnimationTreePlayer::_get(const StringName& p_name,Variant &r_ret) const {
} else {
node["animation"]=an->animation;
}
+ Array k;
+ List<NodePath> keys;
+ an->filter.get_key_list(&keys);
+ k.resize(keys.size());
+ int i=0;
+ for(List<NodePath>::Element *E=keys.front();E;E=E->next()) {
+ k[i++]=E->get();
+ }
+ node["filter"]=k;
} break;
case NODE_ONESHOT: {
OneShotNode *osn = static_cast<OneShotNode*>(n);
@@ -439,7 +453,6 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
//transform to seconds...
-
switch(nb->type) {
case NODE_OUTPUT: {
@@ -464,7 +477,7 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
an->time=p_time;
an->step=0;
} else {
- an->time+=p_time;
+ an->time=MAX(0,an->time+p_time);
an->step=p_time;
}
@@ -482,14 +495,12 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
an->skip=true;
for (List<AnimationNode::TrackRef>::Element *E=an->tref.front();E;E=E->next()) {
-
- if (p_filter && p_filter->has(an->animation->track_get_path(E->get().local_track))) {
-
- if (p_reverse_weight<0)
- E->get().weight=0;
- else
- E->get().weight=p_reverse_weight;
-
+ NodePath track_path = an->animation->track_get_path(E->get().local_track);
+ if (p_filter && p_filter->has(track_path)) {
+ E->get().weight = MAX(0, p_reverse_weight);
+ } else if(an->filter.has(track_path)) {
+ E->get().weight = 0;
+ E->get().track->skip = true;
} else {
E->get().weight=p_weight;
}
@@ -552,18 +563,17 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
float main_rem;
float os_rem;
+ float os_reverse_weight = p_reverse_weight;
if (!osn->filter.empty()) {
-
- main_rem = _process_node(osn->inputs[0].node,r_prev_anim,(osn->mix?p_weight:p_weight*(1.0-blend)),p_time,p_seek,&osn->filter,p_weight);
- os_rem = _process_node(osn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,os_seek,&osn->filter,-1);
-
- } else {
-
- main_rem = _process_node(osn->inputs[0].node,r_prev_anim,(osn->mix?p_weight:p_weight*(1.0-blend)),p_time,p_seek);
- os_rem = _process_node(osn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,os_seek);
+ p_filter = &osn->filter;
+ p_reverse_weight = p_weight;
+ os_reverse_weight = -1;
}
+ main_rem = _process_node(osn->inputs[0].node,r_prev_anim,(osn->mix?p_weight:p_weight*(1.0-blend)),p_time,p_seek,p_filter,p_reverse_weight);
+ os_rem = _process_node(osn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,os_seek,p_filter,os_reverse_weight);
+
if (osn->start) {
osn->remaining=os_rem;
osn->start=false;
@@ -768,6 +778,8 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
t.value = t.object->get(t.property);
t.value.zero();
+
+ t.skip = false;
}
@@ -816,16 +828,9 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
if (a->value_track_is_continuous(tr.local_track)) {
Variant value = a->value_track_interpolate(tr.local_track,anim_list->time);
Variant::blend(tr.track->value,value,blend,tr.track->value);
- tr.track->object->set(tr.track->property,tr.track->value);
} else {
-
- List<int> indices;
- a->value_track_get_key_indices(tr.local_track,anim_list->time,anim_list->step,&indices);
- for(List<int>::Element *E=indices.front();E;E=E->next()) {
-
- Variant value = a->track_get_key_value(tr.local_track,E->get());
- tr.track->object->set(tr.track->property,value);
- }
+ int index = a->track_find_key(tr.local_track,anim_list->time);
+ tr.track->value = a->track_get_key_value(tr.local_track, index);
}
} break;
case Animation::TYPE_METHOD: { ///< Call any method on a specific node.
@@ -854,11 +859,13 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
Track &t = E->get();
- if (!t.object)
+ if (t.skip || !t.object)
continue;
- if(t.property) // value track; was applied in step 2
+ if(t.property) { // value track
+ t.object->set(t.property,t.value);
continue;
+ }
Transform xform;
xform.basis=t.rot;
@@ -983,6 +990,24 @@ void AnimationTreePlayer::animation_node_set_master_animation(const StringName&
}
+void AnimationTreePlayer::animation_node_set_filter_path(const StringName& p_node,const NodePath& p_track_path,bool p_filter) {
+
+ GET_NODE( NODE_ANIMATION, AnimationNode );
+
+ if (p_filter)
+ n->filter[p_track_path]=true;
+ else
+ n->filter.erase(p_track_path);
+
+}
+
+void AnimationTreePlayer::animation_node_set_get_filtered_paths(const StringName& p_node,List<NodePath> *r_paths) const{
+
+ GET_NODE( NODE_ANIMATION, AnimationNode );
+
+ n->filter.get_key_list(r_paths);
+}
+
void AnimationTreePlayer::oneshot_node_set_fadein_time(const StringName& p_node,float p_time) {
GET_NODE( NODE_ONESHOT, OneShotNode );
@@ -1210,6 +1235,12 @@ String AnimationTreePlayer::animation_node_get_master_animation(const StringName
}
+bool AnimationTreePlayer::animation_node_is_path_filtered(const StringName& p_node,const NodePath& p_path) const {
+
+ GET_NODE_V(NODE_ANIMATION, AnimationNode, 0 );
+ return n->filter.has(p_path);
+}
+
float AnimationTreePlayer::oneshot_node_get_fadein_time(const StringName& p_node) const {
@@ -1750,6 +1781,7 @@ void AnimationTreePlayer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("animation_node_set_master_animation","id","source"),&AnimationTreePlayer::animation_node_set_master_animation);
ObjectTypeDB::bind_method(_MD("animation_node_get_master_animation","id"),&AnimationTreePlayer::animation_node_get_master_animation);
+ ObjectTypeDB::bind_method(_MD("animation_node_set_filter_path","id","path","enable"),&AnimationTreePlayer::animation_node_set_filter_path);
ObjectTypeDB::bind_method(_MD("oneshot_node_set_fadein_time","id","time_sec"),&AnimationTreePlayer::oneshot_node_set_fadein_time);
ObjectTypeDB::bind_method(_MD("oneshot_node_get_fadein_time","id"),&AnimationTreePlayer::oneshot_node_get_fadein_time);
diff --git a/scene/animation/animation_tree_player.h b/scene/animation/animation_tree_player.h
index 2e44d69aa1..0e78281e4c 100644
--- a/scene/animation/animation_tree_player.h
+++ b/scene/animation/animation_tree_player.h
@@ -111,6 +111,7 @@ private:
Variant value;
+ bool skip;
};
@@ -162,6 +163,9 @@ private:
float step;
String from;
bool skip;
+
+ HashMap<NodePath,bool> filter;
+
AnimationNode() { type=NODE_ANIMATION; next=NULL; last_version=0; skip=false; }
};
@@ -310,6 +314,10 @@ public:
void animation_node_set_master_animation(const StringName& p_node,const String& p_master_animation);
String animation_node_get_master_animation(const StringName& p_node) const;
+ void animation_node_set_filter_path(const StringName& p_node,const NodePath& p_filter,bool p_enable);
+ void animation_node_set_get_filtered_paths(const StringName& p_node,List<NodePath> *r_paths) const;
+ bool animation_node_is_path_filtered(const StringName& p_node,const NodePath& p_path) const;
+
/* ONE SHOT NODE */
void oneshot_node_set_fadein_time(const StringName& p_node,float p_time);
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 0f3f762ba1..0f1622a838 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -227,6 +227,10 @@ void Button::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_text_align"),&Button::get_text_align);
ObjectTypeDB::bind_method(_MD("is_flat"),&Button::is_flat);
+ BIND_CONSTANT( ALIGN_LEFT );
+ BIND_CONSTANT( ALIGN_CENTER );
+ BIND_CONSTANT( ALIGN_RIGHT );
+
ADD_PROPERTYNZ( PropertyInfo( Variant::STRING, "text", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_DEFAULT_INTL ), _SCS("set_text"),_SCS("get_text") );
ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture" ), _SCS("set_button_icon"),_SCS("get_button_icon") );
ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flat" ), _SCS("set_flat"),_SCS("is_flat") );
diff --git a/scene/gui/button_array.cpp b/scene/gui/button_array.cpp
index de77b83403..be48296110 100644
--- a/scene/gui/button_array.cpp
+++ b/scene/gui/button_array.cpp
@@ -362,10 +362,10 @@ ButtonArray::Align ButtonArray::get_align() const {
}
-void ButtonArray::add_button(const String& p_button) {
+void ButtonArray::add_button(const String& p_text) {
Button button;
- button.text=p_button;
+ button.text=p_text;
buttons.push_back(button);
update();
@@ -375,10 +375,10 @@ void ButtonArray::add_button(const String& p_button) {
minimum_size_changed();
}
-void ButtonArray::add_icon_button(const Ref<Texture>& p_icon,const String& p_button) {
+void ButtonArray::add_icon_button(const Ref<Texture>& p_icon,const String& p_text) {
Button button;
- button.text=p_button;
+ button.text=p_text;
button.icon=p_icon;
buttons.push_back(button);
if (selected==-1)
@@ -396,6 +396,7 @@ void ButtonArray::set_button_text(int p_button, const String& p_text) {
minimum_size_changed();
}
+
void ButtonArray::set_button_icon(int p_button, const Ref<Texture>& p_icon) {
ERR_FAIL_INDEX(p_button,buttons.size());
@@ -403,11 +404,13 @@ void ButtonArray::set_button_icon(int p_button, const Ref<Texture>& p_icon) {
update();
minimum_size_changed();
}
+
String ButtonArray::get_button_text(int p_button) const {
ERR_FAIL_INDEX_V(p_button,buttons.size(),"");
return buttons[p_button].text;
}
+
Ref<Texture> ButtonArray::get_button_icon(int p_button) const {
ERR_FAIL_INDEX_V(p_button,buttons.size(),Ref<Texture>());
@@ -470,16 +473,16 @@ void ButtonArray::get_translatable_strings(List<String> *p_strings) const {
void ButtonArray::_bind_methods() {
ObjectTypeDB::bind_method(_MD("add_button","text"),&ButtonArray::add_button);
- ObjectTypeDB::bind_method(_MD("add_icon_button","icon","text"),&ButtonArray::add_icon_button,DEFVAL(""));
- ObjectTypeDB::bind_method(_MD("set_button_text","button","text"),&ButtonArray::set_button_text);
- ObjectTypeDB::bind_method(_MD("set_button_icon","button","icon"),&ButtonArray::set_button_icon);
- ObjectTypeDB::bind_method(_MD("get_button_text","button"),&ButtonArray::get_button_text);
- ObjectTypeDB::bind_method(_MD("get_button_icon","button"),&ButtonArray::get_button_icon);
+ ObjectTypeDB::bind_method(_MD("add_icon_button","icon:Texture","text"),&ButtonArray::add_icon_button,DEFVAL(""));
+ ObjectTypeDB::bind_method(_MD("set_button_text","button_idx","text"),&ButtonArray::set_button_text);
+ ObjectTypeDB::bind_method(_MD("set_button_icon","button_idx","icon:Texture"),&ButtonArray::set_button_icon);
+ ObjectTypeDB::bind_method(_MD("get_button_text","button_idx"),&ButtonArray::get_button_text);
+ ObjectTypeDB::bind_method(_MD("get_button_icon:Texture","button_idx"),&ButtonArray::get_button_icon);
ObjectTypeDB::bind_method(_MD("get_button_count"),&ButtonArray::get_button_count);
ObjectTypeDB::bind_method(_MD("get_selected"),&ButtonArray::get_selected);
ObjectTypeDB::bind_method(_MD("get_hovered"),&ButtonArray::get_hovered);
- ObjectTypeDB::bind_method(_MD("set_selected","button"),&ButtonArray::set_selected);
- ObjectTypeDB::bind_method(_MD("erase_button","button"),&ButtonArray::erase_button);
+ ObjectTypeDB::bind_method(_MD("set_selected","button_idx"),&ButtonArray::set_selected);
+ ObjectTypeDB::bind_method(_MD("erase_button","button_idx"),&ButtonArray::erase_button);
ObjectTypeDB::bind_method(_MD("clear"),&ButtonArray::clear);
ObjectTypeDB::bind_method(_MD("_input_event"),&ButtonArray::_input_event);
@@ -490,7 +493,7 @@ void ButtonArray::_bind_methods() {
BIND_CONSTANT( ALIGN_FILL );
BIND_CONSTANT( ALIGN_EXPAND_FILL );
- ADD_SIGNAL( MethodInfo("button_selected",PropertyInfo(Variant::INT,"button")));
+ ADD_SIGNAL( MethodInfo("button_selected",PropertyInfo(Variant::INT,"button_idx")));
}
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index d00dacd256..51242d89bd 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -285,7 +285,7 @@ Button* AcceptDialog::add_cancel(const String &p_cancel) {
String c = p_cancel;
if (p_cancel=="")
c="Cancel";
- Button *b = swap_ok_cancel ? add_button("Cancel",true) : add_button("Cancel");
+ Button *b = swap_ok_cancel ? add_button(c,true) : add_button(c);
b->connect("pressed",this,"_closed");
return b;
}
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 64fdfdfefe..705ce55d42 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -613,7 +613,6 @@ FileDialog::Access FileDialog::get_access() const{
void FileDialog::_make_dir_confirm() {
-
Error err = dir_access->make_dir( makedirname->get_text() );
if (err==OK) {
dir_access->change_dir(makedirname->get_text());
@@ -623,6 +622,7 @@ void FileDialog::_make_dir_confirm() {
} else {
mkdirerr->popup_centered_minsize(Size2(250,50));
}
+ makedirname->set_text(""); // reset label
}
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index b00fcfe42c..e056c55f71 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -78,10 +78,6 @@ void Range::set_val(double p_val) {
if (p_val<shared->min)
p_val=shared->min;
- //avoid to set -0
- if (p_val == 0)
- p_val = 0;
-
if (shared->val==p_val)
return;
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index 9692d08882..eb060aa6b8 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -32,25 +32,23 @@
Size2 Tabs::get_minimum_size() const {
-
Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
Ref<Font> font = get_font("font");
- Size2 ms(0, MAX( tab_bg->get_minimum_size().height,tab_fg->get_minimum_size().height)+font->get_height() );
-
-// h+=MIN( get_constant("label_valign_fg"), get_constant("label_valign_bg") );
+ Size2 ms(0, MAX(tab_bg->get_minimum_size().height, tab_fg->get_minimum_size().height)+font->get_height());
for(int i=0;i<tabs.size();i++) {
Ref<Texture> tex = tabs[i].icon;
if (tex.is_valid()) {
- ms.height = MAX( ms.height, tex->get_size().height );
+ ms.height = MAX(ms.height, tex->get_size().height);
if (tabs[i].text!="")
ms.width+=get_constant("hseparation");
-
}
+
ms.width+=font->get_string_size(tabs[i].text).width;
+
if (current==i)
ms.width+=tab_fg->get_minimum_size().width;
else
@@ -58,28 +56,26 @@ Size2 Tabs::get_minimum_size() const {
if (tabs[i].right_button.is_valid()) {
Ref<Texture> rb=tabs[i].right_button;
- Size2 bms = rb->get_size();//+get_stylebox("button")->get_minimum_size();
+ Size2 bms = rb->get_size();
bms.width+=get_constant("hseparation");
-
ms.width+=bms.width;
ms.height=MAX(bms.height+tab_bg->get_minimum_size().height,ms.height);
}
if (cb_displaypolicy==CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy==CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i==current)) {
Ref<Texture> cb=get_icon("close");
- Size2 bms = cb->get_size();//+get_stylebox("button")->get_minimum_size();
+ Size2 bms = cb->get_size();
bms.width+=get_constant("hseparation");
ms.width+=bms.width;
ms.height=MAX(bms.height+tab_bg->get_minimum_size().height,ms.height);
}
}
- ms.width=0; //should make this optional
+ ms.width=0; //TODO: should make this optional
return ms;
}
-
void Tabs::_input_event(const InputEvent& p_event) {
if (p_event.type==InputEvent::MOUSE_MOTION) {
@@ -101,15 +97,14 @@ void Tabs::_input_event(const InputEvent& p_event) {
}
}
-
-
+ // test hovering to display right or close button
int hover_buttons=-1;
hover=-1;
for(int i=0;i<tabs.size();i++) {
- // test hovering tab to display close button if policy says so
+ if (i<offset)
+ continue;
- // test hovering right button and close button
if (tabs[i].rb_rect.has_point(pos)) {
rb_hover=i;
cb_hover=-1;
@@ -123,11 +118,9 @@ void Tabs::_input_event(const InputEvent& p_event) {
break;
}
-
-
}
- if (hover_buttons == -1) { // no hover
+ if (hover_buttons == -1) { // no hover
rb_hover= hover_buttons;
cb_hover= hover_buttons;
}
@@ -137,8 +130,6 @@ void Tabs::_input_event(const InputEvent& p_event) {
}
-
-
if (rb_pressing && p_event.type==InputEvent::MOUSE_BUTTON &&
!p_event.mouse_button.pressed &&
p_event.mouse_button.button_index==BUTTON_LEFT) {
@@ -152,9 +143,10 @@ void Tabs::_input_event(const InputEvent& p_event) {
update();
}
+
if (cb_pressing && p_event.type==InputEvent::MOUSE_BUTTON &&
- !p_event.mouse_button.pressed &&
- p_event.mouse_button.button_index==BUTTON_LEFT) {
+ !p_event.mouse_button.pressed &&
+ p_event.mouse_button.button_index==BUTTON_LEFT) {
if (cb_hover!=-1) {
//pressed
@@ -195,12 +187,12 @@ void Tabs::_input_event(const InputEvent& p_event) {
}
}
-
int found=-1;
for(int i=0;i<tabs.size();i++) {
if (i<offset)
continue;
+
if (tabs[i].rb_rect.has_point(pos)) {
rb_pressing=true;
update();
@@ -213,10 +205,7 @@ void Tabs::_input_event(const InputEvent& p_event) {
return;
}
- int ofs=tabs[i].ofs_cache;
- int size = tabs[i].ofs_cache;
if (pos.x >=tabs[i].ofs_cache && pos.x<tabs[i].ofs_cache+tabs[i].size_cache) {
-
found=i;
break;
}
@@ -232,8 +221,8 @@ void Tabs::_input_event(const InputEvent& p_event) {
}
-void Tabs::_notification(int p_what) {
+void Tabs::_notification(int p_what) {
switch(p_what) {
@@ -259,39 +248,20 @@ void Tabs::_notification(int p_what) {
Ref<Texture> close=get_icon("close");
int h = get_size().height;
-
- int label_valign_fg = get_constant("label_valign_fg");
- int label_valign_bg = get_constant("label_valign_bg");
-
-
- int w=0;
-
+ int w = 0;
int mw = 0;
- {
-
-
- // h+=MIN( get_constant("label_valign_fg"), get_constant("label_valign_bg") );
-
- for(int i=0;i<tabs.size();i++) {
-
- int sz = get_tab_width(i);
-
- tabs[i].ofs_cache=mw;
- mw+=sz;
-
-
- }
+ for(int i=0;i<tabs.size();i++) {
+ tabs[i].ofs_cache = mw;
+ mw += get_tab_width(i);
}
-
if (tab_align==ALIGN_CENTER) {
w=(get_size().width-mw)/2;
} else if (tab_align==ALIGN_RIGHT) {
w=get_size().width-mw;
-
}
if (w<0) {
@@ -311,45 +281,13 @@ void Tabs::_notification(int p_what) {
if (i<offset)
continue;
- tabs[i].ofs_cache=w;
-
- String s = tabs[i].text;
- int lsize=0;
- int slen=font->get_string_size(s).width;
- lsize+=slen;
-
+ tabs[i].ofs_cache=w;
- Ref<Texture> icon;
- if (tabs[i].icon.is_valid()) {
- icon = tabs[i].icon;
- if (icon.is_valid()) {
- lsize+=icon->get_width();
- if (s!="")
- lsize+=get_constant("hseparation");
-
- }
- }
-
- if (tabs[i].right_button.is_valid()) {
- Ref<StyleBox> style = get_stylebox("button");
- Ref<Texture> rb=tabs[i].right_button;
-
- lsize+=get_constant("hseparation");
- //lsize+=style->get_margin(MARGIN_LEFT);
- lsize+=rb->get_width();
- //lsize+=style->get_margin(MARGIN_RIGHT);
-
- }
-
+ int lsize = get_tab_width(i);
- if (cb_displaypolicy==CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy==CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i==current)) {
-
- lsize+=get_constant("hseparation");
- //lsize+=style->get_margin(MARGIN_LEFT);
- lsize+=close->get_width();
- //lsize+=style->get_margin(MARGIN_RIGHT);
- }
+ String text = tabs[i].text;
+ int slen = font->get_string_size(text).width;
if (w+lsize > limit) {
max_drawn_tab=i-1;
@@ -360,42 +298,39 @@ void Tabs::_notification(int p_what) {
}
-
Ref<StyleBox> sb;
- int va;
Color col;
if (i==current) {
-
sb=tab_fg;
- va=label_valign_fg;
col=color_fg;
} else {
sb=tab_bg;
- va=label_valign_bg;
col=color_bg;
}
- Size2i sb_ms = sb->get_minimum_size();
- Rect2 sb_rect = Rect2( w, 0, lsize+sb_ms.width, h);
- sb->draw(ci, sb_rect );
+ Rect2 sb_rect = Rect2(w, 0, lsize, h);
+ sb->draw(ci, sb_rect);
w+=sb->get_margin(MARGIN_LEFT);
+ Size2i sb_ms = sb->get_minimum_size();
+ Ref<Texture> icon = tabs[i].icon;
if (icon.is_valid()) {
icon->draw(ci, Point2i( w, sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-icon->get_height())/2 ) );
- if (s!="")
+ if (text!="")
w+=icon->get_width()+get_constant("hseparation");
}
- font->draw(ci, Point2i( w, sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-font->get_height())/2+font->get_ascent() ), s, col );
+ font->draw(ci, Point2i( w, sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-font->get_height())/2+font->get_ascent() ), text, col );
w+=slen;
if (tabs[i].right_button.is_valid()) {
+
Ref<StyleBox> style = get_stylebox("button");
Ref<Texture> rb=tabs[i].right_button;
@@ -413,17 +348,12 @@ void Tabs::_notification(int p_what) {
style->draw(ci,rb_rect);
}
- w+=style->get_margin(MARGIN_LEFT);
-
- rb->draw(ci,Point2i( w,rb_rect.pos.y+style->get_margin(MARGIN_TOP) ));
+ rb->draw(ci,Point2i( w+style->get_margin(MARGIN_LEFT), rb_rect.pos.y+style->get_margin(MARGIN_TOP) ));
w+=rb->get_width();
- w+=style->get_margin(MARGIN_RIGHT);
tabs[i].rb_rect=rb_rect;
-
}
-
if (cb_displaypolicy==CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy==CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i==current)) {
Ref<StyleBox> style = get_stylebox("button");
@@ -443,11 +373,8 @@ void Tabs::_notification(int p_what) {
style->draw(ci,cb_rect);
}
- //w+=style->get_margin(MARGIN_LEFT);
-
- cb->draw(ci,Point2i( w,cb_rect.pos.y+style->get_margin(MARGIN_TOP) ));
+ cb->draw(ci,Point2i( w+style->get_margin(MARGIN_LEFT), cb_rect.pos.y+style->get_margin(MARGIN_TOP) ));
w+=cb->get_width();
- //w+=style->get_margin(MARGIN_RIGHT);
tabs[i].cb_rect=cb_rect;
}
@@ -462,28 +389,26 @@ void Tabs::_notification(int p_what) {
int vofs = (get_size().height-incr->get_size().height)/2;
if (offset>0)
- draw_texture(hilite_arrow==0?decr_hl:decr,Point2(limit,vofs));
+ draw_texture(hilite_arrow==0 ? decr_hl : decr, Point2(limit,vofs));
else
- draw_texture(decr,Point2(limit,vofs),Color(1,1,1,0.5));
+ draw_texture(decr,Point2(limit,vofs), Color(1,1,1,0.5));
if (missing_right)
- draw_texture(hilite_arrow==1?incr_hl:incr,Point2(limit+decr->get_size().width,vofs));
+ draw_texture(hilite_arrow==1 ? incr_hl : incr, Point2(limit+decr->get_size().width,vofs));
else
- draw_texture(incr,Point2(limit+decr->get_size().width,vofs),Color(1,1,1,0.5));
+ draw_texture(incr,Point2(limit+decr->get_size().width,vofs), Color(1,1,1,0.5));
buttons_visible=true;
} else {
buttons_visible=false;
}
-
} break;
}
}
int Tabs::get_tab_count() const {
-
return tabs.size();
}
@@ -492,11 +417,9 @@ void Tabs::set_current_tab(int p_current) {
ERR_FAIL_INDEX( p_current, get_tab_count() );
- //printf("DEBUG %p: set_current_tab to %i\n", this, p_current);
current=p_current;
_change_notify("current_tab");
- //emit_signal("tab_changed",current);
update();
}
@@ -520,9 +443,9 @@ String Tabs::get_tab_title(int p_tab) const{
ERR_FAIL_INDEX_V(p_tab,tabs.size(),"");
return tabs[p_tab].text;
-
}
+
void Tabs::set_tab_icon(int p_tab,const Ref<Texture>& p_icon){
ERR_FAIL_INDEX(p_tab,tabs.size());
@@ -531,6 +454,7 @@ void Tabs::set_tab_icon(int p_tab,const Ref<Texture>& p_icon){
minimum_size_changed();
}
+
Ref<Texture> Tabs::get_tab_icon(int p_tab) const{
ERR_FAIL_INDEX_V(p_tab,tabs.size(),Ref<Texture>());
@@ -539,7 +463,6 @@ Ref<Texture> Tabs::get_tab_icon(int p_tab) const{
}
-
void Tabs::set_tab_right_button(int p_tab,const Ref<Texture>& p_right_button){
ERR_FAIL_INDEX(p_tab,tabs.size());
@@ -589,8 +512,6 @@ void Tabs::remove_tab(int p_idx) {
if (current>=tabs.size())
current=tabs.size()-1;
- //emit_signal("tab_changed",current);
-
_ensure_no_over_offset();
}
@@ -614,19 +535,20 @@ int Tabs::get_tab_width(int p_idx) const {
Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
Ref<Font> font = get_font("font");
- Ref<Texture> close=get_icon("close");
+
int x=0;
Ref<Texture> tex = tabs[p_idx].icon;
if (tex.is_valid()) {
+ x+=tex->get_width();
if (tabs[p_idx].text!="")
x+=get_constant("hseparation");
}
-
x+=font->get_string_size(tabs[p_idx].text).width;
+
if (current==p_idx)
x+=tab_fg->get_minimum_size().width;
else
@@ -634,17 +556,14 @@ int Tabs::get_tab_width(int p_idx) const {
if (tabs[p_idx].right_button.is_valid()) {
Ref<Texture> rb=tabs[p_idx].right_button;
- Size2 bms = rb->get_size();//+get_stylebox("button")->get_minimum_size();
- bms.width+=get_constant("hseparation");
-
- x+=bms.width;
+ x+=rb->get_width();
+ x+=get_constant("hseparation");
}
if (cb_displaypolicy==CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy==CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx==current)) {
-
- Size2 bms = close->get_size();//+get_stylebox("button")->get_minimum_size();
- bms.width+=get_constant("hseparation");
- x+=bms.width;
+ Ref<Texture> cb=get_icon("close");
+ x+=cb->get_width();
+ x+=get_constant("hseparation");
}
return x;
@@ -700,11 +619,9 @@ void Tabs::ensure_tab_visible(int p_idx) {
Ref<Texture> incr = get_icon("increment");
Ref<Texture> decr = get_icon("decrement");
-
int limit=get_size().width-incr->get_width()-decr->get_width();
-
int x=0;
for(int i=0;i<tabs.size();i++) {
@@ -750,7 +667,6 @@ void Tabs::_bind_methods() {
ADD_SIGNAL(MethodInfo("right_button_pressed",PropertyInfo(Variant::INT,"tab")));
ADD_SIGNAL(MethodInfo("tab_close",PropertyInfo(Variant::INT,"tab")));
-
ADD_PROPERTY( PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE,"-1,4096,1",PROPERTY_USAGE_EDITOR), _SCS("set_current_tab"), _SCS("get_current_tab") );
BIND_CONSTANT( ALIGN_LEFT );
@@ -774,9 +690,8 @@ Tabs::Tabs() {
cb_hover=-1;
cb_pressing=false;
- cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER; // Default : no close button
+ cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER;
offset=0;
max_drawn_tab=0;
-
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index f7461a736c..41d546c6f7 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -2057,7 +2057,17 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
scancode_handled=false;
break;
}
-#ifdef APPLE_STYLE_KEYS
+#ifndef APPLE_STYLE_KEYS
+ if (k.mod.command) {
+ _scroll_lines_up();
+ break;
+ }
+#else
+ if (k.mod.command && k.mod.alt) {
+ _scroll_lines_up();
+ break;
+ }
+
if (k.mod.command)
cursor_set_line(0);
else
@@ -2084,7 +2094,17 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
scancode_handled=false;
break;
}
-#ifdef APPLE_STYLE_KEYS
+#ifndef APPLE_STYLE_KEYS
+ if (k.mod.command) {
+ _scroll_lines_down();
+ break;
+ }
+#else
+ if (k.mod.command && k.mod.alt) {
+ _scroll_lines_down();
+ break;
+ }
+
if (k.mod.command)
cursor_set_line(text.size()-1);
else
@@ -2498,6 +2518,36 @@ void TextEdit::_post_shift_selection() {
selection.selecting_text=true;
}
+void TextEdit::_scroll_lines_up() {
+ // adjust the vertical scroll
+ if (get_v_scroll() > 0) {
+ set_v_scroll(get_v_scroll() - 1);
+ }
+
+ // adjust the cursor
+ if (cursor_get_line() >= (get_visible_rows() + get_v_scroll()) && !selection.active) {
+ cursor_set_line((get_visible_rows() + get_v_scroll()) - 1, false);
+ }
+}
+
+void TextEdit::_scroll_lines_down() {
+ // calculate the maximum vertical scroll position
+ int max_v_scroll = get_line_count() - 1;
+ if (!scroll_past_end_of_file_enabled) {
+ max_v_scroll -= get_visible_rows() - 1;
+ }
+
+ // adjust the vertical scroll
+ if (get_v_scroll() < max_v_scroll) {
+ set_v_scroll(get_v_scroll() + 1);
+ }
+
+ // adjust the cursor
+ if ((cursor_get_line()) <= get_v_scroll() - 1 && !selection.active) {
+ cursor_set_line(get_v_scroll(), false);
+ }
+}
+
/**** TEXT EDIT CORE API ****/
void TextEdit::_base_insert_text(int p_line, int p_char,const String& p_text,int &r_end_line,int &r_end_column) {
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 09c2a4d729..193dd236d1 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -259,6 +259,9 @@ class TextEdit : public Control {
void _pre_shift_selection();
void _post_shift_selection();
+ void _scroll_lines_up();
+ void _scroll_lines_down();
+
// void mouse_motion(const Point& p_pos, const Point& p_rel, int p_button_mask);
Size2 get_minimum_size();
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 718206dee1..278e6584fa 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -154,7 +154,7 @@ void TreeItem::set_text(int p_column,String p_text) {
ERR_FAIL_INDEX( p_column, cells.size() );
cells[p_column].text=p_text;
- if (cells[p_column].mode==TreeItem::CELL_MODE_RANGE) {
+ if (cells[p_column].mode==TreeItem::CELL_MODE_RANGE || cells[p_column].mode==TreeItem::CELL_MODE_RANGE_EXPRESSION) {
cells[p_column].min=0;
cells[p_column].max=p_text.get_slice_count(",");
@@ -704,6 +704,7 @@ void TreeItem::_bind_methods() {
BIND_CONSTANT( CELL_MODE_STRING );
BIND_CONSTANT( CELL_MODE_CHECK );
BIND_CONSTANT( CELL_MODE_RANGE );
+ BIND_CONSTANT( CELL_MODE_RANGE_EXPRESSION );
BIND_CONSTANT( CELL_MODE_ICON );
BIND_CONSTANT( CELL_MODE_CUSTOM );
@@ -1127,7 +1128,8 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2&
//font->draw( ci, text_pos, p_item->cells[i].text, col,item_rect.size.x-check_w );
} break;
- case TreeItem::CELL_MODE_RANGE: {
+ case TreeItem::CELL_MODE_RANGE:
+ case TreeItem::CELL_MODE_RANGE_EXPRESSION: {
if (p_item->cells[i].text!="") {
@@ -1594,7 +1596,8 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_
}
} break;
- case TreeItem::CELL_MODE_RANGE: {
+ case TreeItem::CELL_MODE_RANGE:
+ case TreeItem::CELL_MODE_RANGE_EXPRESSION: {
if (c.text!="") {
@@ -1794,6 +1797,13 @@ void Tree::text_editor_enter(String p_text) {
//popup_edited_item->edited_signal.call( popup_edited_item_col );
} break;
+ case TreeItem::CELL_MODE_RANGE_EXPRESSION: {
+
+ if(evaluator)
+ c.val=evaluator->eval(p_text);
+ else
+ c.val=p_text.to_double();
+ } break;
default: { ERR_FAIL(); }
}
@@ -2372,7 +2382,7 @@ bool Tree::edit_selected() {
item_edited(col,s);
return true;
- } else if (c.mode==TreeItem::CELL_MODE_RANGE && c.text!="") {
+ } else if ((c.mode==TreeItem::CELL_MODE_RANGE||c.mode==TreeItem::CELL_MODE_RANGE_EXPRESSION) && c.text!="") {
popup_menu->clear();
for (int i=0;i<c.text.get_slice_count(",");i++) {
@@ -2389,7 +2399,7 @@ bool Tree::edit_selected() {
popup_edited_item_col=col;
return true;
- } else if (c.mode==TreeItem::CELL_MODE_STRING || c.mode==TreeItem::CELL_MODE_RANGE) {
+ } else if (c.mode==TreeItem::CELL_MODE_STRING || c.mode==TreeItem::CELL_MODE_RANGE || c.mode==TreeItem::CELL_MODE_RANGE_EXPRESSION ) {
Point2i textedpos=get_global_pos() + rect.pos;
text_editor->set_pos( textedpos );
@@ -2398,7 +2408,7 @@ bool Tree::edit_selected() {
text_editor->set_text( c.mode==TreeItem::CELL_MODE_STRING?c.text:rtos(c.val) );
text_editor->select_all();
- if (c.mode==TreeItem::CELL_MODE_RANGE) {
+ if (c.mode==TreeItem::CELL_MODE_RANGE || c.mode==TreeItem::CELL_MODE_RANGE_EXPRESSION ) {
value_editor->set_pos(textedpos + Point2i(0,text_editor->get_size().height) );
value_editor->set_size( Size2(rect.size.width,1));
@@ -3227,6 +3237,9 @@ bool Tree::is_folding_hidden() const {
return hide_folding;
}
+void Tree::set_value_evaluator(ValueEvaluator *p_evaluator) {
+ evaluator = p_evaluator;
+}
void Tree::_bind_methods() {
@@ -3367,6 +3380,7 @@ Tree::Tree() {
hide_folding=false;
+ evaluator=NULL;
}
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 1ba1c6a494..75bcfd8e33 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -34,6 +34,7 @@
#include "scene/gui/line_edit.h"
#include "scene/gui/scroll_bar.h"
#include "scene/gui/slider.h"
+#include "core/helper/value_evaluator.h"
/**
@author Juan Linietsky <reduzio@gmail.com>
@@ -52,6 +53,7 @@ public:
CELL_MODE_STRING, ///< just a string
CELL_MODE_CHECK, ///< string + check
CELL_MODE_RANGE, ///< Contains a range
+ CELL_MODE_RANGE_EXPRESSION, ///< Contains a range
CELL_MODE_ICON, ///< Contains a icon, not editable
CELL_MODE_CUSTOM, ///< Contains a custom value, show a string, and an edit button
};
@@ -422,6 +424,8 @@ friend class TreeItem;
bool hide_folding;
+ ValueEvaluator *evaluator;
+
protected:
static void _bind_methods();
@@ -482,7 +486,7 @@ public:
void set_hide_folding(bool p_hide);
bool is_folding_hidden() const;
-
+ void set_value_evaluator(ValueEvaluator *p_evaluator);
Tree();
~Tree();
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 9930aa07f6..8327473a60 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -126,11 +126,13 @@
#include "scene/main/scene_main_loop.h"
#include "scene/main/resource_preloader.h"
#include "scene/resources/packed_scene.h"
+#include "scene/main/scene_main_loop.h"
#include "scene/resources/surface_tool.h"
#include "scene/resources/mesh_data_tool.h"
#include "scene/resources/scene_preloader.h"
+#include "scene/resources/dynamic_font.h"
#include "scene/main/timer.h"
@@ -235,6 +237,8 @@ static ResourceFormatLoaderShader *resource_loader_shader=NULL;
static ResourceFormatSaverText *resource_saver_text=NULL;
static ResourceFormatLoaderText *resource_loader_text=NULL;
+static ResourceFormatLoaderDynamicFont *resource_loader_dynamic_font=NULL;
+
//static SceneStringNames *string_names;
void register_scene_types() {
@@ -251,6 +255,8 @@ void register_scene_types() {
resource_loader_wav = memnew( ResourceFormatLoaderWAV );
ResourceLoader::add_resource_format_loader( resource_loader_wav );
+ resource_loader_dynamic_font = memnew( ResourceFormatLoaderDynamicFont );
+ ResourceLoader::add_resource_format_loader( resource_loader_dynamic_font );
#ifdef TOOLS_ENABLED
@@ -570,13 +576,19 @@ void register_scene_types() {
ObjectTypeDB::register_type<LargeTexture>();
ObjectTypeDB::register_type<CubeMap>();
ObjectTypeDB::register_type<Animation>();
- ObjectTypeDB::register_type<Font>();
+ ObjectTypeDB::register_virtual_type<Font>();
+ ObjectTypeDB::register_type<BitmapFont>();
+ ObjectTypeDB::register_type<DynamicFontData>();
+ ObjectTypeDB::register_type<DynamicFont>();
ObjectTypeDB::register_type<StyleBoxEmpty>();
ObjectTypeDB::register_type<StyleBoxTexture>();
ObjectTypeDB::register_type<StyleBoxFlat>();
ObjectTypeDB::register_type<StyleBoxImageMask>();
ObjectTypeDB::register_type<Theme>();
+ ObjectTypeDB::add_compatibility_type("Font","BitmapFont");
+
+
ObjectTypeDB::register_type<PolygonPathFinder>();
ObjectTypeDB::register_type<BitMap>();
ObjectTypeDB::register_type<ColorRamp>();
@@ -634,6 +646,7 @@ void unregister_scene_types() {
memdelete( resource_loader_image );
memdelete( resource_loader_wav );
+ memdelete( resource_loader_dynamic_font );
#ifdef TOOLS_ENABLED
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 005a46c0bc..727c8eee29 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -87,10 +87,10 @@ static Ref<Shader> make_shader(const char*vertex_code,const char*fragment_code,c
return shader;
}
-static Ref<Font> make_font(int p_height,int p_ascent, int p_valign, int p_charcount, const int *p_chars,const Ref<Texture> &p_texture) {
+static Ref<BitmapFont> make_font(int p_height,int p_ascent, int p_valign, int p_charcount, const int *p_chars,const Ref<Texture> &p_texture) {
- Ref<Font> font( memnew( Font ) );
+ Ref<BitmapFont> font( memnew( BitmapFont ) );
font->add_texture( p_texture );
for (int i=0;i<p_charcount;i++) {
@@ -117,10 +117,10 @@ static Ref<Font> make_font(int p_height,int p_ascent, int p_valign, int p_charco
return font;
}
-static Ref<Font> make_font2(int p_height,int p_ascent, int p_charcount, const int *p_char_rects,int p_kerning_count,const int *p_kernings,int p_w, int p_h, const unsigned char *p_img) {
+static Ref<BitmapFont> make_font2(int p_height,int p_ascent, int p_charcount, const int *p_char_rects,int p_kerning_count,const int *p_kernings,int p_w, int p_h, const unsigned char *p_img) {
- Ref<Font> font( memnew( Font ) );
+ Ref<BitmapFont> font( memnew( BitmapFont ) );
DVector<uint8_t> img;
img.resize(p_w*p_h*2);
@@ -187,10 +187,10 @@ void make_default_theme() {
Ref<Theme> t( memnew( Theme ) );
- //Ref<Font> default_font = make_font(_bi_font_normal_height,_bi_font_normal_ascent,_bi_font_normal_valign,_bi_font_normal_charcount,_bi_font_normal_characters,make_icon(font_normal_png));
- Ref<Font> default_font=make_font2(_builtin_normal_font_height,_builtin_normal_font_ascent,_builtin_normal_font_charcount,&_builtin_normal_font_charrects[0][0],_builtin_normal_font_kerning_pair_count,&_builtin_normal_font_kerning_pairs[0][0],_builtin_normal_font_img_width,_builtin_normal_font_img_height,_builtin_normal_font_img_data);
- Ref<Font> source_font=make_font2(_builtin_source_font_height,_builtin_source_font_ascent,_builtin_source_font_charcount,&_builtin_source_font_charrects[0][0],_builtin_source_font_kerning_pair_count,&_builtin_source_font_kerning_pairs[0][0],_builtin_source_font_img_width,_builtin_source_font_img_height,_builtin_source_font_img_data);
- Ref<Font> large_font=make_font2(_builtin_large_font_height,_builtin_large_font_ascent,_builtin_large_font_charcount,&_builtin_large_font_charrects[0][0],_builtin_large_font_kerning_pair_count,&_builtin_large_font_kerning_pairs[0][0],_builtin_large_font_img_width,_builtin_large_font_img_height,_builtin_large_font_img_data);
+ //Ref<BitmapFont> default_font = make_font(_bi_font_normal_height,_bi_font_normal_ascent,_bi_font_normal_valign,_bi_font_normal_charcount,_bi_font_normal_characters,make_icon(font_normal_png));
+ Ref<BitmapFont> default_font=make_font2(_builtin_normal_font_height,_builtin_normal_font_ascent,_builtin_normal_font_charcount,&_builtin_normal_font_charrects[0][0],_builtin_normal_font_kerning_pair_count,&_builtin_normal_font_kerning_pairs[0][0],_builtin_normal_font_img_width,_builtin_normal_font_img_height,_builtin_normal_font_img_data);
+ Ref<BitmapFont> source_font=make_font2(_builtin_source_font_height,_builtin_source_font_ascent,_builtin_source_font_charcount,&_builtin_source_font_charrects[0][0],_builtin_source_font_kerning_pair_count,&_builtin_source_font_kerning_pairs[0][0],_builtin_source_font_img_width,_builtin_source_font_img_height,_builtin_source_font_img_data);
+ Ref<BitmapFont> large_font=make_font2(_builtin_large_font_height,_builtin_large_font_ascent,_builtin_large_font_charcount,&_builtin_large_font_charrects[0][0],_builtin_large_font_kerning_pair_count,&_builtin_large_font_kerning_pairs[0][0],_builtin_large_font_img_width,_builtin_large_font_img_height,_builtin_large_font_img_data);
// Font Colors
@@ -939,7 +939,7 @@ void make_default_theme() {
style->set_default_margin( Margin(),8);
}
- Ref<Font> f = make_default_font();
+ Ref<BitmapFont> f = make_default_font();
Theme::set_default( t );
Theme::set_default_icon( texture );
Theme::set_default_style( style );
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
new file mode 100644
index 0000000000..b9fd67bc2f
--- /dev/null
+++ b/scene/resources/dynamic_font.cpp
@@ -0,0 +1,519 @@
+#include "dynamic_font.h"
+#define STB_TRUETYPE_IMPLEMENTATION
+#include "stb_truetype.h"
+#include "os/file_access.h"
+
+void DynamicFontData::lock() {
+
+ fr=font_data.read();
+
+ if (fr.ptr()!=last_data_ptr) {
+
+ last_data_ptr=fr.ptr();
+
+ if (!stbtt_InitFont(&info, last_data_ptr, 0)) {
+ valid=false;
+ } else {
+ valid=true;
+ }
+
+ last_data_ptr=fr.ptr();
+ }
+}
+
+void DynamicFontData::unlock() {
+
+ fr = DVector<uint8_t>::Read();
+}
+
+void DynamicFontData::set_font_data(const DVector<uint8_t>& p_font) {
+ //clear caches and stuff
+ ERR_FAIL_COND(font_data.size()) ;
+ font_data=p_font;
+
+ lock();
+
+ if (valid) {
+ stbtt_GetFontVMetrics(&info, &ascent, &descent, &linegap);
+ descent=-descent + linegap;
+
+ for(int i=32;i<1024;i++) {
+ for(int j=32;j<1024;j++) {
+
+ int kern = stbtt_GetCodepointKernAdvance(&info, i,j);
+ if (kern!=0) {
+ KerningPairKey kpk;
+ kpk.A=i;
+ kpk.B=j;
+ kerning_map[kpk]=kern;
+ }
+ }
+ }
+ }
+
+ unlock();
+ //clear existing stuff
+
+ ERR_FAIL_COND(!valid);
+}
+
+Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(int p_size) {
+
+ ERR_FAIL_COND_V(!valid,Ref<DynamicFontAtSize>());
+
+ if (size_cache.has(p_size)) {
+ return Ref<DynamicFontAtSize>( size_cache[p_size] );
+ }
+
+
+ Ref<DynamicFontAtSize> dfas;
+ dfas.instance();
+
+ dfas->font=Ref<DynamicFontData>( this );
+
+ size_cache[p_size]=dfas.ptr();
+
+ dfas->size=p_size;
+
+ lock();
+
+ dfas->scale = stbtt_ScaleForPixelHeight(&info, p_size);
+
+ unlock();
+
+ return dfas;
+
+}
+
+DynamicFontData::DynamicFontData()
+{
+ last_data_ptr=NULL;
+ valid=false;
+}
+
+DynamicFontData::~DynamicFontData()
+{
+
+}
+
+
+
+////////////////////
+
+float DynamicFontAtSize::get_height() const {
+
+ return (font->ascent+font->descent)*scale;
+}
+
+float DynamicFontAtSize::get_ascent() const {
+
+ return font->ascent*scale;
+}
+float DynamicFontAtSize::get_descent() const {
+
+ return font->descent*scale;
+}
+
+Size2 DynamicFontAtSize::get_char_size(CharType p_char,CharType p_next) const {
+
+ const_cast<DynamicFontAtSize*>(this)->_update_char(p_char);
+
+ const Character *c = char_map.getptr(p_char);
+ ERR_FAIL_COND_V(!c,Size2());
+
+ Size2 ret( c->advance, get_height());
+
+ if (p_next) {
+ DynamicFontData::KerningPairKey kpk;
+ kpk.A=p_char;
+ kpk.B=p_next;
+
+ const Map<DynamicFontData::KerningPairKey,int>::Element *K=font->kerning_map.find(kpk);
+ if (K) {
+ ret.x+=K->get()*scale;
+ }
+
+ }
+
+ return ret;
+}
+
+
+float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const {
+
+ const_cast<DynamicFontAtSize*>(this)->_update_char(p_char);
+
+ const Character * c = char_map.getptr(p_char);
+
+ if (!c) {
+ return 0;
+ }
+
+ Point2 cpos=p_pos;
+ cpos.x+=c->h_align;
+ cpos.y-=get_ascent();
+ cpos.y+=c->v_align;
+ ERR_FAIL_COND_V( c->texture_idx<-1 || c->texture_idx>=textures.size(),0);
+ if (c->texture_idx!=-1)
+ VisualServer::get_singleton()->canvas_item_add_texture_rect_region( p_canvas_item, Rect2( cpos, c->rect.size ), textures[c->texture_idx].texture->get_rid(),c->rect, p_modulate );
+
+ //textures[c->texture_idx].texture->draw(p_canvas_item,Vector2());
+
+ float ret = c->advance;
+ if (p_next) {
+ DynamicFontData::KerningPairKey kpk;
+ kpk.A=p_char;
+ kpk.B=p_next;
+
+ const Map<DynamicFontData::KerningPairKey,int>::Element *K=font->kerning_map.find(kpk);
+ if (K) {
+ ret+=K->get()*scale;
+ }
+
+ }
+
+ return ret;
+}
+
+
+void DynamicFontAtSize::_update_char(CharType p_char) {
+
+ if (char_map.has(p_char))
+ return;
+
+ font->lock();
+
+
+ int w,h,xofs,yofs;
+ unsigned char * cpbitmap = stbtt_GetCodepointBitmap(&font->info, scale, scale, p_char, &w, &h, &xofs, &yofs );
+
+ if (!cpbitmap) {
+ //no glyph
+
+ int advance;
+ stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0);
+ //print_line("char has no bitmap: "+itos(p_char)+" but advance is "+itos(advance*scale));
+ Character ch;
+ ch.texture_idx=-1;
+ ch.advance=advance*scale;
+ ch.h_align=0;
+ ch.v_align=0;
+
+ char_map[p_char]=ch;
+
+ font->unlock();
+
+ return;
+ }
+
+ int mw=w+rect_margin*2;
+ int mh=h+rect_margin*2;
+
+ if (mw>4096 || mh>4096) {
+
+ stbtt_FreeBitmap(cpbitmap,NULL);
+ font->unlock();
+ ERR_FAIL_COND(mw>4096);
+ ERR_FAIL_COND(mh>4096);
+ }
+
+ //find a texture to fit this...
+
+ int tex_index=-1;
+ int tex_x=0;
+ int tex_y=0;
+
+ for(int i=0;i<textures.size();i++) {
+
+ CharTexture &ct=textures[i];
+
+ if (mw > ct.texture_size || mh > ct.texture_size) //too big for this texture
+ continue;
+
+ tex_y=0x7FFFFFFF;
+ tex_x=0;
+
+ for(int j=0;j<ct.texture_size-mw;j++) {
+
+ int max_y=0;
+
+ for(int k=j;k<j+mw;k++) {
+
+ int y = ct.offsets[k];
+ if (y>max_y)
+ max_y=y;
+ }
+
+ if (max_y<tex_y) {
+ tex_y=max_y;
+ tex_x=j;
+ }
+ }
+
+ if (tex_y==0x7FFFFFFF || tex_y+mh > ct.texture_size)
+ continue; //fail, could not fit it here
+
+ tex_index=i;
+ break;
+ }
+
+// print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" X: "+itos(tex_x)+" Y: "+itos(tex_y));
+
+ if (tex_index==-1) {
+ //could not find texture to fit, create one
+
+ int texsize = MAX(size*8,256);
+ if (mw>texsize)
+ texsize=mw; //special case, adapt to it?
+ if (mh>texsize)
+ texsize=mh; //special case, adapt to it?
+
+ texsize=nearest_power_of_2(texsize);
+
+ texsize=MIN(texsize,4096);
+
+
+ CharTexture tex;
+ tex.texture_size=texsize;
+ tex.imgdata.resize(texsize*texsize*2); //grayscale alpha
+
+ {
+ //zero texture
+ DVector<uint8_t>::Write w = tex.imgdata.write();
+ for(int i=0;i<texsize*texsize*2;i++) {
+ w[i]=0;
+ }
+ }
+ tex.offsets.resize(texsize);
+ for(int i=0;i<texsize;i++) //zero offsets
+ tex.offsets[i]=0;
+
+ textures.push_back(tex);
+ tex_index=textures.size()-1;
+
+ }
+
+
+ //fit character in char texture
+
+ CharTexture &tex=textures[tex_index];
+
+ {
+ DVector<uint8_t>::Write wr = tex.imgdata.write();
+
+ for(int i=0;i<h;i++) {
+ for(int j=0;j<w;j++) {
+
+ int ofs = ( (i+tex_y+rect_margin)*tex.texture_size+j+tex_x+rect_margin)*2;
+ wr[ofs+0]=255; //grayscale as 1
+ wr[ofs+1]=cpbitmap[i*w+j]; //alpha as 0
+ }
+ }
+ }
+
+ //blit to image and texture
+ {
+
+ Image img(tex.texture_size,tex.texture_size,0,Image::FORMAT_GRAYSCALE_ALPHA,tex.imgdata);
+
+ if (tex.texture.is_null()) {
+ tex.texture.instance();
+ tex.texture->create_from_image(img,Texture::FLAG_FILTER);
+ } else {
+ tex.texture->set_data(img); //update
+ }
+
+ }
+
+
+ // update height array
+
+ for(int k=tex_x;k<tex_x+mw;k++) {
+
+ tex.offsets[k]=tex_y+mh;
+ }
+
+ int advance;
+ stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0);
+
+ Character chr;
+ chr.h_align=xofs;
+ chr.v_align=yofs + get_ascent();
+ chr.advance=advance*scale;
+ chr.texture_idx=tex_index;
+
+
+ chr.rect=Rect2(tex_x+rect_margin,tex_y+rect_margin,w,h);
+
+ //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" RECT: "+chr.rect+" X OFS: "+itos(xofs)+" Y OFS: "+itos(yofs));
+
+ char_map[p_char]=chr;
+
+ stbtt_FreeBitmap(cpbitmap,NULL);
+
+ font->unlock();
+
+}
+
+DynamicFontAtSize::DynamicFontAtSize() {
+
+ rect_margin=1;
+}
+
+DynamicFontAtSize::~DynamicFontAtSize(){
+
+ ERR_FAIL_COND(!font.ptr());
+ font->size_cache.erase(size);
+}
+
+/////////////////////////
+
+
+void DynamicFont::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_font_data","data:DynamicFontData"),&DynamicFont::set_font_data);
+ ObjectTypeDB::bind_method(_MD("get_font_data:DynamicFontData"),&DynamicFont::get_font_data);
+
+ ObjectTypeDB::bind_method(_MD("set_size","data"),&DynamicFont::set_size);
+ ObjectTypeDB::bind_method(_MD("get_size"),&DynamicFont::get_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"size"),_SCS("set_size"),_SCS("get_size"));
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"font",PROPERTY_HINT_RESOURCE_TYPE,"DynamicFontData"),_SCS("set_font_data"),_SCS("get_font_data"));
+}
+
+
+void DynamicFont::set_font_data(const Ref<DynamicFontData>& p_data) {
+
+ data=p_data;
+ data_at_size=data->_get_dynamic_font_at_size(size);
+}
+
+Ref<DynamicFontData> DynamicFont::get_font_data() const{
+
+ return data;
+}
+
+void DynamicFont::set_size(int p_size){
+
+ if (size==p_size)
+ return;
+ size=p_size;
+ ERR_FAIL_COND(p_size<1);
+ if (!data.is_valid())
+ return;
+ data_at_size=data->_get_dynamic_font_at_size(size);
+
+}
+int DynamicFont::get_size() const{
+
+ return size;
+}
+
+float DynamicFont::get_height() const{
+
+ if (!data_at_size.is_valid())
+ return 1;
+
+ return data_at_size->get_height();
+}
+
+float DynamicFont::get_ascent() const{
+
+ if (!data_at_size.is_valid())
+ return 1;
+
+ return data_at_size->get_ascent();
+}
+
+float DynamicFont::get_descent() const{
+
+ if (!data_at_size.is_valid())
+ return 1;
+
+ return data_at_size->get_descent();
+
+}
+
+Size2 DynamicFont::get_char_size(CharType p_char,CharType p_next) const{
+
+ if (!data_at_size.is_valid())
+ return Size2(1,1);
+
+ return data_at_size->get_char_size(p_char,p_next);
+
+}
+
+bool DynamicFont::is_distance_field_hint() const{
+
+ return false;
+}
+
+float DynamicFont::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const {
+
+ if (!data_at_size.is_valid())
+ return 0;
+
+ return data_at_size->draw_char(p_canvas_item,p_pos,p_char,p_next,p_modulate);
+
+}
+
+DynamicFont::DynamicFont() {
+
+ size=16;
+}
+
+DynamicFont::~DynamicFont() {
+
+}
+
+/////////////////////////
+
+
+RES ResourceFormatLoaderDynamicFont::load(const String &p_path, const String& p_original_path, Error *r_error) {
+
+ if (r_error)
+ *r_error=ERR_FILE_CANT_OPEN;
+
+
+ FileAccess *f = FileAccess::open(p_path,FileAccess::READ);
+ ERR_FAIL_COND_V(!f,RES());
+
+ DVector<uint8_t> data;
+
+ data.resize(f->get_len());
+
+ ERR_FAIL_COND_V(data.size()==0,RES());
+
+ {
+ DVector<uint8_t>::Write w = data.write();
+ f->get_buffer(w.ptr(),data.size());
+ }
+
+ Ref<DynamicFontData> dfd;
+ dfd.instance();
+ dfd->set_font_data(data);
+
+ if (r_error)
+ *r_error=OK;
+
+ return dfd;
+}
+
+void ResourceFormatLoaderDynamicFont::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("ttf");
+}
+
+bool ResourceFormatLoaderDynamicFont::handles_type(const String& p_type) const {
+
+ return (p_type=="DynamicFontData");
+}
+
+String ResourceFormatLoaderDynamicFont::get_resource_type(const String &p_path) const {
+
+ String el = p_path.extension().to_lower();
+ if (el=="ttf")
+ return "DynamicFontData";
+ return "";
+}
+
diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h
new file mode 100644
index 0000000000..ba7249a7b7
--- /dev/null
+++ b/scene/resources/dynamic_font.h
@@ -0,0 +1,175 @@
+#ifndef DYNAMICFONT_H
+#define DYNAMICFONT_H
+
+#include "font.h"
+#include "stb_truetype.h"
+#include "io/resource_loader.h"
+
+
+class DynamicFontAtSize;
+class DynamicFont;
+
+class DynamicFontData : public Resource {
+
+ OBJ_TYPE(DynamicFontData,Resource);
+
+ bool valid;
+
+ DVector<uint8_t> font_data;
+ DVector<uint8_t>::Read fr;
+ const uint8_t* last_data_ptr;
+
+ struct KerningPairKey {
+
+ union {
+ struct {
+ uint32_t A,B;
+ };
+
+ uint64_t pair;
+ };
+
+ _FORCE_INLINE_ bool operator<(const KerningPairKey& p_r) const { return pair<p_r.pair; }
+ };
+
+ Map<KerningPairKey,int> kerning_map;
+
+
+ Map<int,DynamicFontAtSize*> size_cache;
+
+friend class DynamicFontAtSize;
+
+ stbtt_fontinfo info;
+ int ascent;
+ int descent;
+ int linegap;
+
+ void lock();
+ void unlock();
+
+friend class DynamicFont;
+
+
+ Ref<DynamicFontAtSize> _get_dynamic_font_at_size(int p_size);
+public:
+
+ void set_font_data(const DVector<uint8_t>& p_font);
+ DynamicFontData();
+ ~DynamicFontData();
+};
+
+
+class DynamicFontAtSize : public Reference {
+
+ OBJ_TYPE(DynamicFontAtSize,Reference);
+
+
+ int rect_margin;
+
+ struct CharTexture {
+
+ DVector<uint8_t> imgdata;
+ int texture_size;
+ Vector<int> offsets;
+ Ref<ImageTexture> texture;
+ };
+
+ Vector<CharTexture> textures;
+
+ struct Character {
+
+ int texture_idx;
+ Rect2 rect;
+ float v_align;
+ float h_align;
+ float advance;
+
+ Character() { texture_idx=0; v_align=0; }
+ };
+
+
+
+ HashMap< CharType, Character > char_map;
+
+ _FORCE_INLINE_ void _update_char(CharType p_char);
+
+friend class DynamicFontData;
+ Ref<DynamicFontData> font;
+ float scale;
+ int size;
+
+protected:
+
+public:
+
+ float get_height() const;
+
+ float get_ascent() const;
+ float get_descent() const;
+
+ Size2 get_char_size(CharType p_char,CharType p_next=0) const;
+
+ float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const;
+
+
+
+ DynamicFontAtSize();
+ ~DynamicFontAtSize();
+};
+
+///////////////
+
+class DynamicFont : public Font {
+
+ OBJ_TYPE( DynamicFont, Font );
+
+ Ref<DynamicFontData> data;
+ Ref<DynamicFontAtSize> data_at_size;
+ int size;
+
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ void set_font_data(const Ref<DynamicFontData>& p_data);
+ Ref<DynamicFontData> get_font_data() const;
+
+ void set_size(int p_size);
+ int get_size() const;
+
+ virtual float get_height() const;
+
+ virtual float get_ascent() const;
+ virtual float get_descent() const;
+
+ virtual Size2 get_char_size(CharType p_char,CharType p_next=0) const;
+
+ virtual bool is_distance_field_hint() const;
+
+ virtual float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const;
+
+ DynamicFont();
+ ~DynamicFont();
+
+};
+
+
+
+/////////////
+
+class ResourceFormatLoaderDynamicFont : public ResourceFormatLoader {
+public:
+
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ 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;
+
+};
+
+
+
+#endif // DYNAMICFONT_H
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index ef5b7a3eb0..0d8d224037 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -31,7 +31,67 @@
#include "core/os/file_access.h"
#include "core/io/resource_loader.h"
-void Font::_set_chars(const DVector<int>& p_chars) {
+
+
+void Font::draw_halign(RID p_canvas_item, const Point2& p_pos, HAlign p_align,float p_width,const String& p_text,const Color& p_modulate) const {
+
+ float length=get_string_size(p_text).width;
+ if (length>=p_width) {
+ draw(p_canvas_item,p_pos,p_text,p_modulate,p_width);
+ return;
+ }
+
+ float ofs;
+ switch(p_align) {
+ case HALIGN_LEFT: {
+ ofs=0;
+ } break;
+ case HALIGN_CENTER: {
+ ofs = Math::floor( (p_width-length) / 2.0 );
+ } break;
+ case HALIGN_RIGHT: {
+ ofs=p_width-length;
+ } break;
+ }
+ draw(p_canvas_item,p_pos+Point2(ofs,0),p_text,p_modulate,p_width);
+}
+
+void Font::draw(RID p_canvas_item, const Point2& p_pos, const String& p_text, const Color& p_modulate,int p_clip_w) const {
+
+ Vector2 ofs;
+
+ for (int i=0;i<p_text.length();i++) {
+
+ int width = get_char_size(p_text[i]).width;
+
+ if (p_clip_w>=0 && (ofs.x+width)>p_clip_w)
+ break; //clip
+
+ ofs.x+=draw_char(p_canvas_item,p_pos+ofs,p_text[i],p_text[i+1],p_modulate);
+ }
+}
+
+void Font::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("draw","canvas_item","pos","string","modulate","clip_w"),&Font::draw,DEFVAL(Color(1,1,1)),DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("get_ascent"),&Font::get_ascent);
+ ObjectTypeDB::bind_method(_MD("get_descent"),&Font::get_descent);
+ ObjectTypeDB::bind_method(_MD("get_height"),&Font::get_height);
+ ObjectTypeDB::bind_method(_MD("is_distance_field_hint"),&Font::is_distance_field_hint);
+ ObjectTypeDB::bind_method(_MD("get_string_size","string"),&Font::get_string_size);
+ ObjectTypeDB::bind_method(_MD("draw_char","canvas_item","pos","char","next","modulate"),&Font::draw_char,DEFVAL(-1),DEFVAL(Color(1,1,1)));
+
+}
+
+
+Font::Font() {
+
+
+}
+
+/////////////////////////////////////////////////////////////////
+
+void BitmapFont::_set_chars(const DVector<int>& p_chars) {
int len = p_chars.size();
//char 1 charsize 1 texture, 4 rect, 2 align, advance 1
@@ -50,7 +110,7 @@ void Font::_set_chars(const DVector<int>& p_chars) {
}
-DVector<int> Font::_get_chars() const {
+DVector<int> BitmapFont::_get_chars() const {
DVector<int> chars;
@@ -74,7 +134,7 @@ DVector<int> Font::_get_chars() const {
return chars;
}
-void Font::_set_kernings(const DVector<int>& p_kernings) {
+void BitmapFont::_set_kernings(const DVector<int>& p_kernings) {
int len=p_kernings.size();
ERR_FAIL_COND(len%3);
@@ -89,7 +149,7 @@ void Font::_set_kernings(const DVector<int>& p_kernings) {
}
}
-DVector<int> Font::_get_kernings() const {
+DVector<int> BitmapFont::_get_kernings() const {
DVector<int> kernings;
@@ -104,7 +164,7 @@ DVector<int> Font::_get_kernings() const {
}
-void Font::_set_textures(const Vector<Variant> & p_textures) {
+void BitmapFont::_set_textures(const Vector<Variant> & p_textures) {
for(int i=0;i<p_textures.size();i++) {
Ref<Texture> tex = p_textures[i];
@@ -114,7 +174,7 @@ void Font::_set_textures(const Vector<Variant> & p_textures) {
}
-Vector<Variant> Font::_get_textures() const {
+Vector<Variant> BitmapFont::_get_textures() const {
Vector<Variant> rtextures;
for(int i=0;i<textures.size();i++)
@@ -122,7 +182,7 @@ Vector<Variant> Font::_get_textures() const {
return rtextures;
}
-Error Font::create_from_fnt(const String& p_string) {
+Error BitmapFont::create_from_fnt(const String& p_string) {
//fnt format used by angelcode bmfont
//http://www.angelcode.com/products/bmfont/
@@ -271,51 +331,51 @@ Error Font::create_from_fnt(const String& p_string) {
-void Font::set_height(float p_height) {
+void BitmapFont::set_height(float p_height) {
height=p_height;
}
-float Font::get_height() const{
+float BitmapFont::get_height() const{
return height;
}
-void Font::set_ascent(float p_ascent){
+void BitmapFont::set_ascent(float p_ascent){
ascent=p_ascent;
}
-float Font::get_ascent() const {
+float BitmapFont::get_ascent() const {
return ascent;
}
-float Font::get_descent() const {
+float BitmapFont::get_descent() const {
return height-ascent;
}
-void Font::add_texture(const Ref<Texture>& p_texture) {
+void BitmapFont::add_texture(const Ref<Texture>& p_texture) {
ERR_FAIL_COND( p_texture.is_null());
textures.push_back( p_texture );
}
-int Font::get_texture_count() const {
+int BitmapFont::get_texture_count() const {
return textures.size();
};
-Ref<Texture> Font::get_texture(int p_idx) const {
+Ref<Texture> BitmapFont::get_texture(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, textures.size(), Ref<Texture>());
return textures[p_idx];
};
-int Font::get_character_count() const {
+int BitmapFont::get_character_count() const {
return char_map.size();
};
-Vector<CharType> Font::get_char_keys() const {
+Vector<CharType> BitmapFont::get_char_keys() const {
Vector<CharType> chars;
chars.resize(char_map.size());
@@ -329,7 +389,7 @@ Vector<CharType> Font::get_char_keys() const {
return chars;
};
-Font::Character Font::get_character(CharType p_char) const {
+BitmapFont::Character BitmapFont::get_character(CharType p_char) const {
if (!char_map.has(p_char)) {
ERR_FAIL_V(Character());
@@ -338,7 +398,7 @@ Font::Character Font::get_character(CharType p_char) const {
return char_map[p_char];
};
-void Font::add_char(CharType p_char, int p_texture_idx, const Rect2& p_rect, const Size2& p_align, float p_advance) {
+void BitmapFont::add_char(CharType p_char, int p_texture_idx, const Rect2& p_rect, const Size2& p_align, float p_advance) {
if (p_advance<0)
p_advance=p_rect.size.width;
@@ -353,7 +413,7 @@ void Font::add_char(CharType p_char, int p_texture_idx, const Rect2& p_rect, con
char_map[p_char]=c;
}
-void Font::add_kerning_pair(CharType p_A,CharType p_B,int p_kerning) {
+void BitmapFont::add_kerning_pair(CharType p_A,CharType p_B,int p_kerning) {
KerningPairKey kpk;
@@ -369,10 +429,10 @@ void Font::add_kerning_pair(CharType p_A,CharType p_B,int p_kerning) {
}
}
-Vector<Font::KerningPairKey> Font::get_kerning_pair_keys() const {
+Vector<BitmapFont::KerningPairKey> BitmapFont::get_kerning_pair_keys() const {
- Vector<Font::KerningPairKey> ret;
+ Vector<BitmapFont::KerningPairKey> ret;
ret.resize(kerning_map.size());
int i=0;
@@ -385,7 +445,7 @@ Vector<Font::KerningPairKey> Font::get_kerning_pair_keys() const {
}
-int Font::get_kerning_pair(CharType p_A,CharType p_B) const {
+int BitmapFont::get_kerning_pair(CharType p_A,CharType p_B) const {
KerningPairKey kpk;
kpk.A=p_A;
@@ -398,19 +458,19 @@ int Font::get_kerning_pair(CharType p_A,CharType p_B) const {
return 0;
}
-void Font::set_distance_field_hint(bool p_distance_field) {
+void BitmapFont::set_distance_field_hint(bool p_distance_field) {
distance_field_hint=p_distance_field;
emit_changed();
}
-bool Font::is_distance_field_hint() const{
+bool BitmapFont::is_distance_field_hint() const{
return distance_field_hint;
}
-void Font::clear() {
+void BitmapFont::clear() {
height=1;
ascent=0;
@@ -426,7 +486,7 @@ Size2 Font::get_string_size(const String& p_string) const {
int l = p_string.length();
if (l==0)
- return Size2(0,height);
+ return Size2(0,get_height());
const CharType *sptr = &p_string[0];
for (int i=0;i<l;i++) {
@@ -434,48 +494,19 @@ Size2 Font::get_string_size(const String& p_string) const {
w+=get_char_size(sptr[i],sptr[i+1]).width;
}
- return Size2(w,height);
+ return Size2(w,get_height());
}
+void BitmapFont::set_fallback(const Ref<BitmapFont> &p_fallback) {
-void Font::draw_halign(RID p_canvas_item, const Point2& p_pos, HAlign p_align,float p_width,const String& p_text,const Color& p_modulate) const {
-
- float length=get_string_size(p_text).width;
- if (length>=p_width) {
- draw(p_canvas_item,p_pos,p_text,p_modulate,p_width);
- return;
- }
-
- float ofs;
- switch(p_align) {
- case HALIGN_LEFT: {
- ofs=0;
- } break;
- case HALIGN_CENTER: {
- ofs = Math::floor( (p_width-length) / 2.0 );
- } break;
- case HALIGN_RIGHT: {
- ofs=p_width-length;
- } break;
- }
- draw(p_canvas_item,p_pos+Point2(ofs,0),p_text,p_modulate,p_width);
+ fallback=p_fallback;
}
-void Font::draw(RID p_canvas_item, const Point2& p_pos, const String& p_text, const Color& p_modulate,int p_clip_w) const {
+Ref<BitmapFont> BitmapFont::get_fallback() const{
- Vector2 ofs;
-
- for (int i=0;i<p_text.length();i++) {
-
- int width = get_char_size(p_text[i]).width;
-
- if (p_clip_w>=0 && (ofs.x+width)>p_clip_w)
- break; //clip
-
- ofs.x+=draw_char(p_canvas_item,p_pos+ofs,p_text[i],p_text[i+1],p_modulate);
- }
+ return fallback;
}
-float Font::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const {
+float BitmapFont::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const {
const Character * c = char_map.getptr(p_char);
@@ -496,58 +527,70 @@ float Font::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_
return get_char_size(p_char,p_next).width;
}
-void Font::set_fallback(const Ref<Font> &p_fallback) {
- fallback=p_fallback;
-}
+Size2 BitmapFont::get_char_size(CharType p_char,CharType p_next) const {
-Ref<Font> Font::get_fallback() const{
+ const Character * c = char_map.getptr(p_char);
- return fallback;
+ if (!c) {
+ if (fallback.is_valid())
+ return fallback->get_char_size(p_char,p_next);
+ return Size2();
+ }
+
+ Size2 ret(c->advance,c->rect.size.y);
+
+ if (p_next) {
+
+ KerningPairKey kpk;
+ kpk.A=p_char;
+ kpk.B=p_next;
+
+ const Map<KerningPairKey,int>::Element *E=kerning_map.find(kpk);
+ if (E) {
+
+ ret.width-=E->get();
+ }
+ }
+
+ return ret;
}
-void Font::_bind_methods() {
+void BitmapFont::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("create_from_fnt","path"),&Font::create_from_fnt);
- ObjectTypeDB::bind_method(_MD("set_height","px"),&Font::set_height);
- ObjectTypeDB::bind_method(_MD("get_height"),&Font::get_height);
+ ObjectTypeDB::bind_method(_MD("create_from_fnt","path"),&BitmapFont::create_from_fnt);
+ ObjectTypeDB::bind_method(_MD("set_height","px"),&BitmapFont::set_height);
- ObjectTypeDB::bind_method(_MD("set_ascent","px"),&Font::set_ascent);
- ObjectTypeDB::bind_method(_MD("get_ascent"),&Font::get_ascent);
- ObjectTypeDB::bind_method(_MD("get_descent"),&Font::get_descent);
+ ObjectTypeDB::bind_method(_MD("set_ascent","px"),&BitmapFont::set_ascent);
- ObjectTypeDB::bind_method(_MD("add_kerning_pair","char_a","char_b","kerning"),&Font::add_kerning_pair);
- ObjectTypeDB::bind_method(_MD("get_kerning_pair","char_a","char_b"),&Font::get_kerning_pair);
+ ObjectTypeDB::bind_method(_MD("add_kerning_pair","char_a","char_b","kerning"),&BitmapFont::add_kerning_pair);
+ ObjectTypeDB::bind_method(_MD("get_kerning_pair","char_a","char_b"),&BitmapFont::get_kerning_pair);
- ObjectTypeDB::bind_method(_MD("add_texture","texture:Texture"),&Font::add_texture);
- ObjectTypeDB::bind_method(_MD("add_char","character","texture","rect","align","advance"),&Font::add_char,DEFVAL(Point2()),DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("add_texture","texture:Texture"),&BitmapFont::add_texture);
+ ObjectTypeDB::bind_method(_MD("add_char","character","texture","rect","align","advance"),&BitmapFont::add_char,DEFVAL(Point2()),DEFVAL(-1));
- ObjectTypeDB::bind_method(_MD("get_texture_count"),&Font::get_texture_count);
- ObjectTypeDB::bind_method(_MD("get_texture:Texture","idx"),&Font::get_texture);
+ ObjectTypeDB::bind_method(_MD("get_texture_count"),&BitmapFont::get_texture_count);
+ ObjectTypeDB::bind_method(_MD("get_texture:Texture","idx"),&BitmapFont::get_texture);
- ObjectTypeDB::bind_method(_MD("get_char_size","char","next"),&Font::get_char_size,DEFVAL(0));
- ObjectTypeDB::bind_method(_MD("get_string_size","string"),&Font::get_string_size);
+ ObjectTypeDB::bind_method(_MD("get_char_size","char","next"),&BitmapFont::get_char_size,DEFVAL(0));
- ObjectTypeDB::bind_method(_MD("set_distance_field_hint","enable"),&Font::set_distance_field_hint);
- ObjectTypeDB::bind_method(_MD("is_distance_field_hint"),&Font::is_distance_field_hint);
+ ObjectTypeDB::bind_method(_MD("set_distance_field_hint","enable"),&BitmapFont::set_distance_field_hint);
- ObjectTypeDB::bind_method(_MD("clear"),&Font::clear);
+ ObjectTypeDB::bind_method(_MD("clear"),&BitmapFont::clear);
- ObjectTypeDB::bind_method(_MD("draw","canvas_item","pos","string","modulate","clip_w"),&Font::draw,DEFVAL(Color(1,1,1)),DEFVAL(-1));
- ObjectTypeDB::bind_method(_MD("draw_char","canvas_item","pos","char","next","modulate"),&Font::draw_char,DEFVAL(-1),DEFVAL(Color(1,1,1)));
- ObjectTypeDB::bind_method(_MD("_set_chars"),&Font::_set_chars);
- ObjectTypeDB::bind_method(_MD("_get_chars"),&Font::_get_chars);
+ ObjectTypeDB::bind_method(_MD("_set_chars"),&BitmapFont::_set_chars);
+ ObjectTypeDB::bind_method(_MD("_get_chars"),&BitmapFont::_get_chars);
- ObjectTypeDB::bind_method(_MD("_set_kernings"),&Font::_set_kernings);
- ObjectTypeDB::bind_method(_MD("_get_kernings"),&Font::_get_kernings);
+ ObjectTypeDB::bind_method(_MD("_set_kernings"),&BitmapFont::_set_kernings);
+ ObjectTypeDB::bind_method(_MD("_get_kernings"),&BitmapFont::_get_kernings);
- ObjectTypeDB::bind_method(_MD("_set_textures"),&Font::_set_textures);
- ObjectTypeDB::bind_method(_MD("_get_textures"),&Font::_get_textures);
+ ObjectTypeDB::bind_method(_MD("_set_textures"),&BitmapFont::_set_textures);
+ ObjectTypeDB::bind_method(_MD("_get_textures"),&BitmapFont::_get_textures);
- ObjectTypeDB::bind_method(_MD("set_fallback","fallback"),&Font::set_fallback);
- ObjectTypeDB::bind_method(_MD("get_fallback"),&Font::get_fallback);
+ ObjectTypeDB::bind_method(_MD("set_fallback","fallback"),&BitmapFont::set_fallback);
+ ObjectTypeDB::bind_method(_MD("get_fallback"),&BitmapFont::get_fallback);
ADD_PROPERTY( PropertyInfo( Variant::ARRAY, "textures", PROPERTY_HINT_NONE,"", PROPERTY_USAGE_NOEDITOR ), _SCS("_set_textures"), _SCS("_get_textures") );
ADD_PROPERTY( PropertyInfo( Variant::INT_ARRAY, "chars", PROPERTY_HINT_NONE,"", PROPERTY_USAGE_NOEDITOR ), _SCS("_set_chars"), _SCS("_get_chars") );
@@ -556,11 +599,11 @@ void Font::_bind_methods() {
ADD_PROPERTY( PropertyInfo( Variant::REAL, "height", PROPERTY_HINT_RANGE,"-1024,1024,1" ), _SCS("set_height"), _SCS("get_height") );
ADD_PROPERTY( PropertyInfo( Variant::REAL, "ascent", PROPERTY_HINT_RANGE,"-1024,1024,1" ), _SCS("set_ascent"), _SCS("get_ascent") );
ADD_PROPERTY( PropertyInfo( Variant::BOOL, "distance_field" ), _SCS("set_distance_field_hint"), _SCS("is_distance_field_hint") );
- ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "fallback", PROPERTY_HINT_RESOURCE_TYPE,"Font" ), _SCS("set_fallback"), _SCS("get_fallback") );
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "fallback", PROPERTY_HINT_RESOURCE_TYPE,"BitmapFont" ), _SCS("set_fallback"), _SCS("get_fallback") );
}
-Font::Font() {
+BitmapFont::BitmapFont() {
clear();
@@ -569,7 +612,7 @@ Font::Font() {
}
-Font::~Font() {
+BitmapFont::~BitmapFont() {
clear();
}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 03b1ec5191..91f4874932 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -35,9 +35,40 @@
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
+
+
class Font : public Resource {
OBJ_TYPE( Font, Resource );
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ virtual float get_height() const=0;
+
+ virtual float get_ascent() const=0;
+ virtual float get_descent() const=0;
+
+ virtual Size2 get_char_size(CharType p_char,CharType p_next=0) const=0;
+ Size2 get_string_size(const String& p_string) const;
+
+ virtual bool is_distance_field_hint() const=0;
+
+ void draw(RID p_canvas_item, const Point2& p_pos, const String& p_text,const Color& p_modulate=Color(1,1,1),int p_clip_w=-1) const;
+ void draw_halign(RID p_canvas_item, const Point2& p_pos, HAlign p_align,float p_width,const String& p_text,const Color& p_modulate=Color(1,1,1)) const;
+ virtual float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const=0;
+
+ Font();
+
+};
+
+
+class BitmapFont : public Font {
+
+ OBJ_TYPE( BitmapFont, Font );
RES_BASE_EXTENSION("fnt");
Vector< Ref<Texture> > textures;
@@ -84,7 +115,7 @@ private:
void _set_textures(const Vector<Variant> & p_textures);
Vector<Variant> _get_textures() const;
- Ref<Font> fallback;
+ Ref<BitmapFont> fallback;
protected:
static void _bind_methods();
@@ -114,54 +145,23 @@ public:
int get_kerning_pair(CharType p_A,CharType p_B) const;
Vector<KerningPairKey> get_kerning_pair_keys() const;
- inline Size2 get_char_size(CharType p_char,CharType p_next=0) const;
- Size2 get_string_size(const String& p_string) const;
-
+ Size2 get_char_size(CharType p_char,CharType p_next=0) const;
- void set_fallback(const Ref<Font> &p_fallback);
- Ref<Font> get_fallback() const;
+ void set_fallback(const Ref<BitmapFont> &p_fallback);
+ Ref<BitmapFont> get_fallback() const;
void clear();
void set_distance_field_hint(bool p_distance_field);
bool is_distance_field_hint() const;
- void draw(RID p_canvas_item, const Point2& p_pos, const String& p_text,const Color& p_modulate=Color(1,1,1),int p_clip_w=-1) const;
- void draw_halign(RID p_canvas_item, const Point2& p_pos, HAlign p_align,float p_width,const String& p_text,const Color& p_modulate=Color(1,1,1)) const;
float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const;
- Font();
- ~Font();
+ BitmapFont();
+ ~BitmapFont();
};
-Size2 Font::get_char_size(CharType p_char,CharType p_next) const {
-
- const Character * c = char_map.getptr(p_char);
-
- if (!c) {
- if (fallback.is_valid())
- return fallback->get_char_size(p_char,p_next);
- return Size2();
- }
-
- Size2 ret(c->advance,c->rect.size.y);
-
- if (p_next) {
-
- KerningPairKey kpk;
- kpk.A=p_char;
- kpk.B=p_next;
-
- const Map<KerningPairKey,int>::Element *E=kerning_map.find(kpk);
- if (E) {
-
- ret.width-=E->get();
- }
- }
-
- return ret;
-}
diff --git a/scene/resources/stb_truetype.h b/scene/resources/stb_truetype.h
new file mode 100644
index 0000000000..d360d60920
--- /dev/null
+++ b/scene/resources/stb_truetype.h
@@ -0,0 +1,3267 @@
+// stb_truetype.h - v1.11 - public domain
+// authored from 2009-2015 by Sean Barrett / RAD Game Tools
+//
+// This library processes TrueType files:
+// parse files
+// extract glyph metrics
+// extract glyph shapes
+// render glyphs to one-channel bitmaps with antialiasing (box filter)
+//
+// Todo:
+// non-MS cmaps
+// crashproof on bad data
+// hinting? (no longer patented)
+// cleartype-style AA?
+// optimize: use simple memory allocator for intermediates
+// optimize: build edge-list directly from curves
+// optimize: rasterize directly from curves?
+//
+// ADDITIONAL CONTRIBUTORS
+//
+// Mikko Mononen: compound shape support, more cmap formats
+// Tor Andersson: kerning, subpixel rendering
+//
+// Misc other:
+// Ryan Gordon
+// Simon Glass
+//
+// Bug/warning reports/fixes:
+// "Zer" on mollyrocket (with fix)
+// Cass Everitt
+// stoiko (Haemimont Games)
+// Brian Hook
+// Walter van Niftrik
+// David Gow
+// David Given
+// Ivan-Assen Ivanov
+// Anthony Pesch
+// Johan Duparc
+// Hou Qiming
+// Fabian "ryg" Giesen
+// Martins Mozeiko
+// Cap Petschulat
+// Omar Cornut
+// github:aloucks
+// Peter LaValle
+// Sergey Popov
+// Giumo X. Clanjor
+// Higor Euripedes
+// Thomas Fields
+// Derek Vinyard
+//
+// VERSION HISTORY
+//
+// 1.11 (2016-04-02) fix unused-variable warning
+// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef
+// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly
+// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
+// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
+// variant PackFontRanges to pack and render in separate phases;
+// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
+// fixed an assert() bug in the new rasterizer
+// replace assert() with STBTT_assert() in new rasterizer
+// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)
+// also more precise AA rasterizer, except if shapes overlap
+// remove need for STBTT_sort
+// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
+// 1.04 (2015-04-15) typo in example
+// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
+//
+// Full history can be found at the end of this file.
+//
+// LICENSE
+//
+// This software is dual-licensed to the public domain and under the following
+// license: you are granted a perpetual, irrevocable license to copy, modify,
+// publish, and distribute this file as you see fit.
+//
+// USAGE
+//
+// Include this file in whatever places neeed to refer to it. In ONE C/C++
+// file, write:
+// #define STB_TRUETYPE_IMPLEMENTATION
+// before the #include of this file. This expands out the actual
+// implementation into that C/C++ file.
+//
+// To make the implementation private to the file that generates the implementation,
+// #define STBTT_STATIC
+//
+// Simple 3D API (don't ship this, but it's fine for tools and quick start)
+// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture
+// stbtt_GetBakedQuad() -- compute quad to draw for a given char
+//
+// Improved 3D API (more shippable):
+// #include "stb_rect_pack.h" -- optional, but you really want it
+// stbtt_PackBegin()
+// stbtt_PackSetOversample() -- for improved quality on small fonts
+// stbtt_PackFontRanges() -- pack and renders
+// stbtt_PackEnd()
+// stbtt_GetPackedQuad()
+//
+// "Load" a font file from a memory buffer (you have to keep the buffer loaded)
+// stbtt_InitFont()
+// stbtt_GetFontOffsetForIndex() -- use for TTC font collections
+//
+// Render a unicode codepoint to a bitmap
+// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
+// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
+// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
+//
+// Character advance/positioning
+// stbtt_GetCodepointHMetrics()
+// stbtt_GetFontVMetrics()
+// stbtt_GetCodepointKernAdvance()
+//
+// Starting with version 1.06, the rasterizer was replaced with a new,
+// faster and generally-more-precise rasterizer. The new rasterizer more
+// accurately measures pixel coverage for anti-aliasing, except in the case
+// where multiple shapes overlap, in which case it overestimates the AA pixel
+// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If
+// this turns out to be a problem, you can re-enable the old rasterizer with
+// #define STBTT_RASTERIZER_VERSION 1
+// which will incur about a 15% speed hit.
+//
+// ADDITIONAL DOCUMENTATION
+//
+// Immediately after this block comment are a series of sample programs.
+//
+// After the sample programs is the "header file" section. This section
+// includes documentation for each API function.
+//
+// Some important concepts to understand to use this library:
+//
+// Codepoint
+// Characters are defined by unicode codepoints, e.g. 65 is
+// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
+// the hiragana for "ma".
+//
+// Glyph
+// A visual character shape (every codepoint is rendered as
+// some glyph)
+//
+// Glyph index
+// A font-specific integer ID representing a glyph
+//
+// Baseline
+// Glyph shapes are defined relative to a baseline, which is the
+// bottom of uppercase characters. Characters extend both above
+// and below the baseline.
+//
+// Current Point
+// As you draw text to the screen, you keep track of a "current point"
+// which is the origin of each character. The current point's vertical
+// position is the baseline. Even "baked fonts" use this model.
+//
+// Vertical Font Metrics
+// The vertical qualities of the font, used to vertically position
+// and space the characters. See docs for stbtt_GetFontVMetrics.
+//
+// Font Size in Pixels or Points
+// The preferred interface for specifying font sizes in stb_truetype
+// is to specify how tall the font's vertical extent should be in pixels.
+// If that sounds good enough, skip the next paragraph.
+//
+// Most font APIs instead use "points", which are a common typographic
+// measurement for describing font size, defined as 72 points per inch.
+// stb_truetype provides a point API for compatibility. However, true
+// "per inch" conventions don't make much sense on computer displays
+// since they different monitors have different number of pixels per
+// inch. For example, Windows traditionally uses a convention that
+// there are 96 pixels per inch, thus making 'inch' measurements have
+// nothing to do with inches, and thus effectively defining a point to
+// be 1.333 pixels. Additionally, the TrueType font data provides
+// an explicit scale factor to scale a given font's glyphs to points,
+// but the author has observed that this scale factor is often wrong
+// for non-commercial fonts, thus making fonts scaled in points
+// according to the TrueType spec incoherently sized in practice.
+//
+// ADVANCED USAGE
+//
+// Quality:
+//
+// - Use the functions with Subpixel at the end to allow your characters
+// to have subpixel positioning. Since the font is anti-aliased, not
+// hinted, this is very import for quality. (This is not possible with
+// baked fonts.)
+//
+// - Kerning is now supported, and if you're supporting subpixel rendering
+// then kerning is worth using to give your text a polished look.
+//
+// Performance:
+//
+// - Convert Unicode codepoints to glyph indexes and operate on the glyphs;
+// if you don't do this, stb_truetype is forced to do the conversion on
+// every call.
+//
+// - There are a lot of memory allocations. We should modify it to take
+// a temp buffer and allocate from the temp buffer (without freeing),
+// should help performance a lot.
+//
+// NOTES
+//
+// The system uses the raw data found in the .ttf file without changing it
+// and without building auxiliary data structures. This is a bit inefficient
+// on little-endian systems (the data is big-endian), but assuming you're
+// caching the bitmaps or glyph shapes this shouldn't be a big deal.
+//
+// It appears to be very hard to programmatically determine what font a
+// given file is in a general way. I provide an API for this, but I don't
+// recommend it.
+//
+//
+// SOURCE STATISTICS (based on v0.6c, 2050 LOC)
+//
+// Documentation & header file 520 LOC \___ 660 LOC documentation
+// Sample code 140 LOC /
+// Truetype parsing 620 LOC ---- 620 LOC TrueType
+// Software rasterization 240 LOC \ .
+// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation
+// Bitmap management 100 LOC /
+// Baked bitmap interface 70 LOC /
+// Font name matching & access 150 LOC ---- 150
+// C runtime library abstraction 60 LOC ---- 60
+//
+//
+// PERFORMANCE MEASUREMENTS FOR 1.06:
+//
+// 32-bit 64-bit
+// Previous release: 8.83 s 7.68 s
+// Pool allocations: 7.72 s 6.34 s
+// Inline sort : 6.54 s 5.65 s
+// New rasterizer : 5.63 s 5.00 s
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+////
+//// SAMPLE PROGRAMS
+////
+//
+// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
+//
+#if 0
+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
+#include "stb_truetype.h"
+
+unsigned char ttf_buffer[1<<20];
+unsigned char temp_bitmap[512*512];
+
+stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
+GLuint ftex;
+
+void my_stbtt_initfont(void)
+{
+ fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
+ stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
+ // can free ttf_buffer at this point
+ glGenTextures(1, &ftex);
+ glBindTexture(GL_TEXTURE_2D, ftex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
+ // can free temp_bitmap at this point
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+}
+
+void my_stbtt_print(float x, float y, char *text)
+{
+ // assume orthographic projection with units = screen pixels, origin at top left
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, ftex);
+ glBegin(GL_QUADS);
+ while (*text) {
+ if (*text >= 32 && *text < 128) {
+ stbtt_aligned_quad q;
+ stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
+ glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
+ glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
+ glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
+ glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
+ }
+ ++text;
+ }
+ glEnd();
+}
+#endif
+//
+//
+//////////////////////////////////////////////////////////////////////////////
+//
+// Complete program (this compiles): get a single bitmap, print as ASCII art
+//
+#if 0
+#include <stdio.h>
+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
+#include "stb_truetype.h"
+
+char ttf_buffer[1<<25];
+
+int main(int argc, char **argv)
+{
+ stbtt_fontinfo font;
+ unsigned char *bitmap;
+ int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
+
+ fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
+
+ stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
+ bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
+
+ for (j=0; j < h; ++j) {
+ for (i=0; i < w; ++i)
+ putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
+ putchar('\n');
+ }
+ return 0;
+}
+#endif
+//
+// Output:
+//
+// .ii.
+// @@@@@@.
+// V@Mio@@o
+// :i. V@V
+// :oM@@M
+// :@@@MM@M
+// @@o o@M
+// :@@. M@M
+// @@@o@@@@
+// :M@@V:@@.
+//
+//////////////////////////////////////////////////////////////////////////////
+//
+// Complete program: print "Hello World!" banner, with bugs
+//
+#if 0
+char buffer[24<<20];
+unsigned char screen[20][79];
+
+int main(int arg, char **argv)
+{
+ stbtt_fontinfo font;
+ int i,j,ascent,baseline,ch=0;
+ float scale, xpos=2; // leave a little padding in case the character extends left
+ char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness
+
+ fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
+ stbtt_InitFont(&font, buffer, 0);
+
+ scale = stbtt_ScaleForPixelHeight(&font, 15);
+ stbtt_GetFontVMetrics(&font, &ascent,0,0);
+ baseline = (int) (ascent*scale);
+
+ while (text[ch]) {
+ int advance,lsb,x0,y0,x1,y1;
+ float x_shift = xpos - (float) floor(xpos);
+ stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
+ stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
+ stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);
+ // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
+ // because this API is really for baking character bitmaps into textures. if you want to render
+ // a sequence of characters, you really need to render each bitmap to a temp buffer, then
+ // "alpha blend" that into the working buffer
+ xpos += (advance * scale);
+ if (text[ch+1])
+ xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);
+ ++ch;
+ }
+
+ for (j=0; j < 20; ++j) {
+ for (i=0; i < 78; ++i)
+ putchar(" .:ioVM@"[screen[j][i]>>5]);
+ putchar('\n');
+ }
+
+ return 0;
+}
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+////
+//// INTEGRATION WITH YOUR CODEBASE
+////
+//// The following sections allow you to supply alternate definitions
+//// of C library functions used by stb_truetype.
+
+#ifdef STB_TRUETYPE_IMPLEMENTATION
+ // #define your own (u)stbtt_int8/16/32 before including to override this
+ #ifndef stbtt_uint8
+ typedef unsigned char stbtt_uint8;
+ typedef signed char stbtt_int8;
+ typedef unsigned short stbtt_uint16;
+ typedef signed short stbtt_int16;
+ typedef unsigned int stbtt_uint32;
+ typedef signed int stbtt_int32;
+ #endif
+
+ typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
+ typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
+
+ // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
+ #ifndef STBTT_ifloor
+ #include <math.h>
+ #define STBTT_ifloor(x) ((int) floor(x))
+ #define STBTT_iceil(x) ((int) ceil(x))
+ #endif
+
+ #ifndef STBTT_sqrt
+ #include <math.h>
+ #define STBTT_sqrt(x) sqrt(x)
+ #endif
+
+ #ifndef STBTT_fabs
+ #include <math.h>
+ #define STBTT_fabs(x) fabs(x)
+ #endif
+
+ // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
+ #ifndef STBTT_malloc
+ #include <stdlib.h>
+ #define STBTT_malloc(x,u) ((void)(u),malloc(x))
+ #define STBTT_free(x,u) ((void)(u),free(x))
+ #endif
+
+ #ifndef STBTT_assert
+ #include <assert.h>
+ #define STBTT_assert(x) assert(x)
+ #endif
+
+ #ifndef STBTT_strlen
+ #include <string.h>
+ #define STBTT_strlen(x) strlen(x)
+ #endif
+
+ #ifndef STBTT_memcpy
+ #include <memory.h>
+ #define STBTT_memcpy memcpy
+ #define STBTT_memset memset
+ #endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+////
+//// INTERFACE
+////
+////
+
+#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
+#define __STB_INCLUDE_STB_TRUETYPE_H__
+
+#ifdef STBTT_STATIC
+#define STBTT_DEF static
+#else
+#define STBTT_DEF extern
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// TEXTURE BAKING API
+//
+// If you use this API, you only have to call two functions ever.
+//
+
+typedef struct
+{
+ unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
+ float xoff,yoff,xadvance;
+} stbtt_bakedchar;
+
+STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
+ float pixel_height, // height of font in pixels
+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in
+ int first_char, int num_chars, // characters to bake
+ stbtt_bakedchar *chardata); // you allocate this, it's num_chars long
+// if return is positive, the first unused row of the bitmap
+// if return is negative, returns the negative of the number of characters that fit
+// if return is 0, no characters fit and no rows were used
+// This uses a very crappy packing.
+
+typedef struct
+{
+ float x0,y0,s0,t0; // top-left
+ float x1,y1,s1,t1; // bottom-right
+} stbtt_aligned_quad;
+
+STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above
+ int char_index, // character to display
+ float *xpos, float *ypos, // pointers to current position in screen pixel space
+ stbtt_aligned_quad *q, // output: quad to draw
+ int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier
+// Call GetBakedQuad with char_index = 'character - first_char', and it
+// creates the quad you need to draw and advances the current position.
+//
+// The coordinate system used assumes y increases downwards.
+//
+// Characters will extend both above and below the current position;
+// see discussion of "BASELINE" above.
+//
+// It's inefficient; you might want to c&p it and optimize it.
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// NEW TEXTURE BAKING API
+//
+// This provides options for packing multiple fonts into one atlas, not
+// perfectly but better than nothing.
+
+typedef struct
+{
+ unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
+ float xoff,yoff,xadvance;
+ float xoff2,yoff2;
+} stbtt_packedchar;
+
+typedef struct stbtt_pack_context stbtt_pack_context;
+typedef struct stbtt_fontinfo stbtt_fontinfo;
+#ifndef STB_RECT_PACK_VERSION
+typedef struct stbrp_rect stbrp_rect;
+#endif
+
+STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
+// Initializes a packing context stored in the passed-in stbtt_pack_context.
+// Future calls using this context will pack characters into the bitmap passed
+// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is
+// the distance from one row to the next (or 0 to mean they are packed tightly
+// together). "padding" is the amount of padding to leave between each
+// character (normally you want '1' for bitmaps you'll use as textures with
+// bilinear filtering).
+//
+// Returns 0 on failure, 1 on success.
+
+STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc);
+// Cleans up the packing context and frees all memory.
+
+#define STBTT_POINT_SIZE(x) (-(x))
+
+STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
+ int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);
+// Creates character bitmaps from the font_index'th font found in fontdata (use
+// font_index=0 if you don't know what that is). It creates num_chars_in_range
+// bitmaps for characters with unicode values starting at first_unicode_char_in_range
+// and increasing. Data for how to render them is stored in chardata_for_range;
+// pass these to stbtt_GetPackedQuad to get back renderable quads.
+//
+// font_size is the full height of the character from ascender to descender,
+// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed
+// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()
+// and pass that result as 'font_size':
+// ..., 20 , ... // font max minus min y is 20 pixels tall
+// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall
+
+typedef struct
+{
+ float font_size;
+ int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint
+ int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints
+ int num_chars;
+ stbtt_packedchar *chardata_for_range; // output
+ unsigned char h_oversample, v_oversample; // don't set these, they're used internally
+} stbtt_pack_range;
+
+STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
+// Creates character bitmaps from multiple ranges of characters stored in
+// ranges. This will usually create a better-packed bitmap than multiple
+// calls to stbtt_PackFontRange. Note that you can call this multiple
+// times within a single PackBegin/PackEnd.
+
+STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);
+// Oversampling a font increases the quality by allowing higher-quality subpixel
+// positioning, and is especially valuable at smaller text sizes.
+//
+// This function sets the amount of oversampling for all following calls to
+// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given
+// pack context. The default (no oversampling) is achieved by h_oversample=1
+// and v_oversample=1. The total number of pixels required is
+// h_oversample*v_oversample larger than the default; for example, 2x2
+// oversampling requires 4x the storage of 1x1. For best results, render
+// oversampled textures with bilinear filtering. Look at the readme in
+// stb/tests/oversample for information about oversampled fonts
+//
+// To use with PackFontRangesGather etc., you must set it before calls
+// call to PackFontRangesGatherRects.
+
+STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above
+ int char_index, // character to display
+ float *xpos, float *ypos, // pointers to current position in screen pixel space
+ stbtt_aligned_quad *q, // output: quad to draw
+ int align_to_integer);
+
+STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
+STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);
+STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
+// Calling these functions in sequence is roughly equivalent to calling
+// stbtt_PackFontRanges(). If you more control over the packing of multiple
+// fonts, or if you want to pack custom data into a font texture, take a look
+// at the source to of stbtt_PackFontRanges() and create a custom version
+// using these functions, e.g. call GatherRects multiple times,
+// building up a single array of rects, then call PackRects once,
+// then call RenderIntoRects repeatedly. This may result in a
+// better packing than calling PackFontRanges multiple times
+// (or it may not).
+
+// this is an opaque structure that you shouldn't mess with which holds
+// all the context needed from PackBegin to PackEnd.
+struct stbtt_pack_context {
+ void *user_allocator_context;
+ void *pack_info;
+ int width;
+ int height;
+ int stride_in_bytes;
+ int padding;
+ unsigned int h_oversample, v_oversample;
+ unsigned char *pixels;
+ void *nodes;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// FONT LOADING
+//
+//
+
+STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
+// Each .ttf/.ttc file may have more than one font. Each font has a sequential
+// index number starting from 0. Call this function to get the font offset for
+// a given index; it returns -1 if the index is out of range. A regular .ttf
+// file will only define one font and it always be at offset 0, so it will
+// return '0' for index 0, and -1 for all other indices. You can just skip
+// this step if you know it's that kind of font.
+
+
+// The following structure is defined publically so you can declare one on
+// the stack or as a global or etc, but you should treat it as opaque.
+struct stbtt_fontinfo
+{
+ void * userdata;
+ unsigned char * data; // pointer to .ttf file
+ int fontstart; // offset of start of font
+
+ int numGlyphs; // number of glyphs, needed for range checking
+
+ int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
+ int index_map; // a cmap mapping for our chosen character encoding
+ int indexToLocFormat; // format needed to map from glyph index to glyph
+};
+
+STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
+// Given an offset into the file that defines a font, this function builds
+// the necessary cached info for the rest of the system. You must allocate
+// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
+// need to do anything special to free it, because the contents are pure
+// value data with no additional data structures. Returns 0 on failure.
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// CHARACTER TO GLYPH-INDEX CONVERSIOn
+
+STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
+// If you're going to perform multiple operations on the same character
+// and you want a speed-up, call this function with the character you're
+// going to process, then use glyph-based functions instead of the
+// codepoint-based functions.
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// CHARACTER PROPERTIES
+//
+
+STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
+// computes a scale factor to produce a font whose "height" is 'pixels' tall.
+// Height is measured as the distance from the highest ascender to the lowest
+// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
+// and computing:
+// scale = pixels / (ascent - descent)
+// so if you prefer to measure height by the ascent only, use a similar calculation.
+
+STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);
+// computes a scale factor to produce a font whose EM size is mapped to
+// 'pixels' tall. This is probably what traditional APIs compute, but
+// I'm not positive.
+
+STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
+// ascent is the coordinate above the baseline the font extends; descent
+// is the coordinate below the baseline the font extends (i.e. it is typically negative)
+// lineGap is the spacing between one row's descent and the next row's ascent...
+// so you should advance the vertical position by "*ascent - *descent + *lineGap"
+// these are expressed in unscaled coordinates, so you must multiply by
+// the scale factor for a given size
+
+STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
+// the bounding box around all possible characters
+
+STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
+// leftSideBearing is the offset from the current horizontal position to the left edge of the character
+// advanceWidth is the offset from the current horizontal position to the next horizontal position
+// these are expressed in unscaled coordinates
+
+STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
+// an additional amount to add to the 'advance' value between ch1 and ch2
+
+STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
+// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
+
+STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
+STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
+STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
+// as above, but takes one or more glyph indices for greater efficiency
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// GLYPH SHAPES (you probably don't need these, but they have to go before
+// the bitmaps for C declaration-order reasons)
+//
+
+#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
+ enum {
+ STBTT_vmove=1,
+ STBTT_vline,
+ STBTT_vcurve
+ };
+#endif
+
+#ifndef stbtt_vertex // you can predefine this to use different values
+ // (we share this with other code at RAD)
+ #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
+ typedef struct
+ {
+ stbtt_vertex_type x,y,cx,cy;
+ unsigned char type,padding;
+ } stbtt_vertex;
+#endif
+
+STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);
+// returns non-zero if nothing is drawn for this glyph
+
+STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
+STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
+// returns # of vertices and fills *vertices with the pointer to them
+// these are expressed in "unscaled" coordinates
+//
+// The shape is a series of countours. Each one starts with
+// a STBTT_moveto, then consists of a series of mixed
+// STBTT_lineto and STBTT_curveto segments. A lineto
+// draws a line from previous endpoint to its x,y; a curveto
+// draws a quadratic bezier from previous endpoint to
+// its x,y, using cx,cy as the bezier control point.
+
+STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
+// frees the data allocated above
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// BITMAP RENDERING
+//
+
+STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
+// frees the bitmap allocated below
+
+STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
+// allocates a large-enough single-channel 8bpp bitmap and renders the
+// specified character/glyph at the specified scale into it, with
+// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
+// *width & *height are filled out with the width & height of the bitmap,
+// which is stored left-to-right, top-to-bottom.
+//
+// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
+
+STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
+// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
+// shift for the character
+
+STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
+// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap
+// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
+// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the
+// width and height and positioning info for it first.
+
+STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
+// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
+// shift for the character
+
+STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
+// get the bbox of the bitmap centered around the glyph origin; so the
+// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
+// the bitmap top left is (leftSideBearing*scale,iy0).
+// (Note that the bitmap uses y-increases-down, but the shape uses
+// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
+
+STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
+// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel
+// shift for the character
+
+// the following functions are equivalent to the above functions, but operate
+// on glyph indices instead of Unicode codepoints (for efficiency)
+STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
+STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
+STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
+STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
+STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
+STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
+
+
+// @TODO: don't expose this structure
+typedef struct
+{
+ int w,h,stride;
+ unsigned char *pixels;
+} stbtt__bitmap;
+
+// rasterize a shape with quadratic beziers into a bitmap
+STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into
+ float flatness_in_pixels, // allowable error of curve in pixels
+ stbtt_vertex *vertices, // array of vertices defining shape
+ int num_verts, // number of vertices in above array
+ float scale_x, float scale_y, // scale applied to input vertices
+ float shift_x, float shift_y, // translation applied to input vertices
+ int x_off, int y_off, // another translation applied to input
+ int invert, // if non-zero, vertically flip shape
+ void *userdata); // context for to STBTT_MALLOC
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Finding the right font...
+//
+// You should really just solve this offline, keep your own tables
+// of what font is what, and don't try to get it out of the .ttf file.
+// That's because getting it out of the .ttf file is really hard, because
+// the names in the file can appear in many possible encodings, in many
+// possible languages, and e.g. if you need a case-insensitive comparison,
+// the details of that depend on the encoding & language in a complex way
+// (actually underspecified in truetype, but also gigantic).
+//
+// But you can use the provided functions in two possible ways:
+// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
+// unicode-encoded names to try to find the font you want;
+// you can run this before calling stbtt_InitFont()
+//
+// stbtt_GetFontNameString() lets you get any of the various strings
+// from the file yourself and do your own comparisons on them.
+// You have to have called stbtt_InitFont() first.
+
+
+STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
+// returns the offset (not index) of the font that matches, or -1 if none
+// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
+// if you use any other flag, use a font name like "Arial"; this checks
+// the 'macStyle' header field; i don't know if fonts set this consistently
+#define STBTT_MACSTYLE_DONTCARE 0
+#define STBTT_MACSTYLE_BOLD 1
+#define STBTT_MACSTYLE_ITALIC 2
+#define STBTT_MACSTYLE_UNDERSCORE 4
+#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0
+
+STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
+// returns 1/0 whether the first string interpreted as utf8 is identical to
+// the second string interpreted as big-endian utf16... useful for strings from next func
+
+STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
+// returns the string (which may be big-endian double byte, e.g. for unicode)
+// and puts the length in bytes in *length.
+//
+// some of the values for the IDs are below; for more see the truetype spec:
+// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
+// http://www.microsoft.com/typography/otspec/name.htm
+
+enum { // platformID
+ STBTT_PLATFORM_ID_UNICODE =0,
+ STBTT_PLATFORM_ID_MAC =1,
+ STBTT_PLATFORM_ID_ISO =2,
+ STBTT_PLATFORM_ID_MICROSOFT =3
+};
+
+enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
+ STBTT_UNICODE_EID_UNICODE_1_0 =0,
+ STBTT_UNICODE_EID_UNICODE_1_1 =1,
+ STBTT_UNICODE_EID_ISO_10646 =2,
+ STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
+ STBTT_UNICODE_EID_UNICODE_2_0_FULL=4
+};
+
+enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
+ STBTT_MS_EID_SYMBOL =0,
+ STBTT_MS_EID_UNICODE_BMP =1,
+ STBTT_MS_EID_SHIFTJIS =2,
+ STBTT_MS_EID_UNICODE_FULL =10
+};
+
+enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
+ STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4,
+ STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5,
+ STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6,
+ STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7
+};
+
+enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
+ // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
+ STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410,
+ STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411,
+ STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412,
+ STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419,
+ STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409,
+ STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D
+};
+
+enum { // languageID for STBTT_PLATFORM_ID_MAC
+ STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11,
+ STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23,
+ STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32,
+ STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 ,
+ STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 ,
+ STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
+ STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __STB_INCLUDE_STB_TRUETYPE_H__
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+////
+//// IMPLEMENTATION
+////
+////
+
+#ifdef STB_TRUETYPE_IMPLEMENTATION
+
+#ifndef STBTT_MAX_OVERSAMPLE
+#define STBTT_MAX_OVERSAMPLE 8
+#endif
+
+#if STBTT_MAX_OVERSAMPLE > 255
+#error "STBTT_MAX_OVERSAMPLE cannot be > 255"
+#endif
+
+typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];
+
+#ifndef STBTT_RASTERIZER_VERSION
+#define STBTT_RASTERIZER_VERSION 2
+#endif
+
+#ifdef _MSC_VER
+#define STBTT__NOTUSED(v) (void)(v)
+#else
+#define STBTT__NOTUSED(v) (void)sizeof(v)
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+//
+// accessors to parse data from file
+//
+
+// on platforms that don't allow misaligned reads, if we want to allow
+// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
+
+#define ttBYTE(p) (* (stbtt_uint8 *) (p))
+#define ttCHAR(p) (* (stbtt_int8 *) (p))
+#define ttFixed(p) ttLONG(p)
+
+#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE)
+
+ #define ttUSHORT(p) (* (stbtt_uint16 *) (p))
+ #define ttSHORT(p) (* (stbtt_int16 *) (p))
+ #define ttULONG(p) (* (stbtt_uint32 *) (p))
+ #define ttLONG(p) (* (stbtt_int32 *) (p))
+
+#else
+
+ static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
+ static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
+ static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
+ static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
+
+#endif
+
+#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
+#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])
+
+static int stbtt__isfont(const stbtt_uint8 *font)
+{
+ // check the version number
+ if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1
+ if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!
+ if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF
+ if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
+ return 0;
+}
+
+// @OPTIMIZE: binary search
+static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
+{
+ stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
+ stbtt_uint32 tabledir = fontstart + 12;
+ stbtt_int32 i;
+ for (i=0; i < num_tables; ++i) {
+ stbtt_uint32 loc = tabledir + 16*i;
+ if (stbtt_tag(data+loc+0, tag))
+ return ttULONG(data+loc+8);
+ }
+ return 0;
+}
+
+STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)
+{
+ // if it's just a font, there's only one valid index
+ if (stbtt__isfont(font_collection))
+ return index == 0 ? 0 : -1;
+
+ // check if it's a TTC
+ if (stbtt_tag(font_collection, "ttcf")) {
+ // version 1?
+ if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
+ stbtt_int32 n = ttLONG(font_collection+8);
+ if (index >= n)
+ return -1;
+ return ttULONG(font_collection+12+index*4);
+ }
+ }
+ return -1;
+}
+
+STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart)
+{
+ stbtt_uint8 *data = (stbtt_uint8 *) data2;
+ stbtt_uint32 cmap, t;
+ stbtt_int32 i,numTables;
+
+ info->data = data;
+ info->fontstart = fontstart;
+
+ cmap = stbtt__find_table(data, fontstart, "cmap"); // required
+ info->loca = stbtt__find_table(data, fontstart, "loca"); // required
+ info->head = stbtt__find_table(data, fontstart, "head"); // required
+ info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required
+ info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
+ info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
+ info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
+ if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx)
+ return 0;
+
+ t = stbtt__find_table(data, fontstart, "maxp");
+ if (t)
+ info->numGlyphs = ttUSHORT(data+t+4);
+ else
+ info->numGlyphs = 0xffff;
+
+ // find a cmap encoding table we understand *now* to avoid searching
+ // later. (todo: could make this installable)
+ // the same regardless of glyph.
+ numTables = ttUSHORT(data + cmap + 2);
+ info->index_map = 0;
+ for (i=0; i < numTables; ++i) {
+ stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
+ // find an encoding we understand:
+ switch(ttUSHORT(data+encoding_record)) {
+ case STBTT_PLATFORM_ID_MICROSOFT:
+ switch (ttUSHORT(data+encoding_record+2)) {
+ case STBTT_MS_EID_UNICODE_BMP:
+ case STBTT_MS_EID_UNICODE_FULL:
+ // MS/Unicode
+ info->index_map = cmap + ttULONG(data+encoding_record+4);
+ break;
+ }
+ break;
+ case STBTT_PLATFORM_ID_UNICODE:
+ // Mac/iOS has these
+ // all the encodingIDs are unicode, so we don't bother to check it
+ info->index_map = cmap + ttULONG(data+encoding_record+4);
+ break;
+ }
+ }
+ if (info->index_map == 0)
+ return 0;
+
+ info->indexToLocFormat = ttUSHORT(data+info->head + 50);
+ return 1;
+}
+
+STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
+{
+ stbtt_uint8 *data = info->data;
+ stbtt_uint32 index_map = info->index_map;
+
+ stbtt_uint16 format = ttUSHORT(data + index_map + 0);
+ if (format == 0) { // apple byte encoding
+ stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
+ if (unicode_codepoint < bytes-6)
+ return ttBYTE(data + index_map + 6 + unicode_codepoint);
+ return 0;
+ } else if (format == 6) {
+ stbtt_uint32 first = ttUSHORT(data + index_map + 6);
+ stbtt_uint32 count = ttUSHORT(data + index_map + 8);
+ if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
+ return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
+ return 0;
+ } else if (format == 2) {
+ STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
+ return 0;
+ } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
+ stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
+ stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
+ stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
+ stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
+
+ // do a binary search of the segments
+ stbtt_uint32 endCount = index_map + 14;
+ stbtt_uint32 search = endCount;
+
+ if (unicode_codepoint > 0xffff)
+ return 0;
+
+ // they lie from endCount .. endCount + segCount
+ // but searchRange is the nearest power of two, so...
+ if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
+ search += rangeShift*2;
+
+ // now decrement to bias correctly to find smallest
+ search -= 2;
+ while (entrySelector) {
+ stbtt_uint16 end;
+ searchRange >>= 1;
+ end = ttUSHORT(data + search + searchRange*2);
+ if (unicode_codepoint > end)
+ search += searchRange*2;
+ --entrySelector;
+ }
+ search += 2;
+
+ {
+ stbtt_uint16 offset, start;
+ stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
+
+ STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
+ start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
+ if (unicode_codepoint < start)
+ return 0;
+
+ offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
+ if (offset == 0)
+ return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));
+
+ return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
+ }
+ } else if (format == 12 || format == 13) {
+ stbtt_uint32 ngroups = ttULONG(data+index_map+12);
+ stbtt_int32 low,high;
+ low = 0; high = (stbtt_int32)ngroups;
+ // Binary search the right group.
+ while (low < high) {
+ stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
+ stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
+ stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
+ if ((stbtt_uint32) unicode_codepoint < start_char)
+ high = mid;
+ else if ((stbtt_uint32) unicode_codepoint > end_char)
+ low = mid+1;
+ else {
+ stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
+ if (format == 12)
+ return start_glyph + unicode_codepoint-start_char;
+ else // format == 13
+ return start_glyph;
+ }
+ }
+ return 0; // not found
+ }
+ // @TODO
+ STBTT_assert(0);
+ return 0;
+}
+
+STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
+{
+ return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
+}
+
+static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)
+{
+ v->type = type;
+ v->x = (stbtt_int16) x;
+ v->y = (stbtt_int16) y;
+ v->cx = (stbtt_int16) cx;
+ v->cy = (stbtt_int16) cy;
+}
+
+static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
+{
+ int g1,g2;
+
+ if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
+ if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format
+
+ if (info->indexToLocFormat == 0) {
+ g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
+ g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
+ } else {
+ g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
+ g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
+ }
+
+ return g1==g2 ? -1 : g1; // if length is 0, return -1
+}
+
+STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
+{
+ int g = stbtt__GetGlyfOffset(info, glyph_index);
+ if (g < 0) return 0;
+
+ if (x0) *x0 = ttSHORT(info->data + g + 2);
+ if (y0) *y0 = ttSHORT(info->data + g + 4);
+ if (x1) *x1 = ttSHORT(info->data + g + 6);
+ if (y1) *y1 = ttSHORT(info->data + g + 8);
+ return 1;
+}
+
+STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
+{
+ return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
+}
+
+STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
+{
+ stbtt_int16 numberOfContours;
+ int g = stbtt__GetGlyfOffset(info, glyph_index);
+ if (g < 0) return 1;
+ numberOfContours = ttSHORT(info->data + g);
+ return numberOfContours == 0;
+}
+
+static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,
+ stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)
+{
+ if (start_off) {
+ if (was_off)
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);
+ } else {
+ if (was_off)
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
+ else
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
+ }
+ return num_vertices;
+}
+
+STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
+{
+ stbtt_int16 numberOfContours;
+ stbtt_uint8 *endPtsOfContours;
+ stbtt_uint8 *data = info->data;
+ stbtt_vertex *vertices=0;
+ int num_vertices=0;
+ int g = stbtt__GetGlyfOffset(info, glyph_index);
+
+ *pvertices = NULL;
+
+ if (g < 0) return 0;
+
+ numberOfContours = ttSHORT(data + g);
+
+ if (numberOfContours > 0) {
+ stbtt_uint8 flags=0,flagcount;
+ stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;
+ stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;
+ stbtt_uint8 *points;
+ endPtsOfContours = (data + g + 10);
+ ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
+ points = data + g + 10 + numberOfContours * 2 + 2 + ins;
+
+ n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
+
+ m = n + 2*numberOfContours; // a loose bound on how many vertices we might need
+ vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
+ if (vertices == 0)
+ return 0;
+
+ next_move = 0;
+ flagcount=0;
+
+ // in first pass, we load uninterpreted data into the allocated array
+ // above, shifted to the end of the array so we won't overwrite it when
+ // we create our final data starting from the front
+
+ off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
+
+ // first load flags
+
+ for (i=0; i < n; ++i) {
+ if (flagcount == 0) {
+ flags = *points++;
+ if (flags & 8)
+ flagcount = *points++;
+ } else
+ --flagcount;
+ vertices[off+i].type = flags;
+ }
+
+ // now load x coordinates
+ x=0;
+ for (i=0; i < n; ++i) {
+ flags = vertices[off+i].type;
+ if (flags & 2) {
+ stbtt_int16 dx = *points++;
+ x += (flags & 16) ? dx : -dx; // ???
+ } else {
+ if (!(flags & 16)) {
+ x = x + (stbtt_int16) (points[0]*256 + points[1]);
+ points += 2;
+ }
+ }
+ vertices[off+i].x = (stbtt_int16) x;
+ }
+
+ // now load y coordinates
+ y=0;
+ for (i=0; i < n; ++i) {
+ flags = vertices[off+i].type;
+ if (flags & 4) {
+ stbtt_int16 dy = *points++;
+ y += (flags & 32) ? dy : -dy; // ???
+ } else {
+ if (!(flags & 32)) {
+ y = y + (stbtt_int16) (points[0]*256 + points[1]);
+ points += 2;
+ }
+ }
+ vertices[off+i].y = (stbtt_int16) y;
+ }
+
+ // now convert them to our format
+ num_vertices=0;
+ sx = sy = cx = cy = scx = scy = 0;
+ for (i=0; i < n; ++i) {
+ flags = vertices[off+i].type;
+ x = (stbtt_int16) vertices[off+i].x;
+ y = (stbtt_int16) vertices[off+i].y;
+
+ if (next_move == i) {
+ if (i != 0)
+ num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
+
+ // now start the new one
+ start_off = !(flags & 1);
+ if (start_off) {
+ // if we start off with an off-curve point, then when we need to find a point on the curve
+ // where we can start, and we need to save some state for when we wraparound.
+ scx = x;
+ scy = y;
+ if (!(vertices[off+i+1].type & 1)) {
+ // next point is also a curve point, so interpolate an on-point curve
+ sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;
+ sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;
+ } else {
+ // otherwise just use the next point as our start point
+ sx = (stbtt_int32) vertices[off+i+1].x;
+ sy = (stbtt_int32) vertices[off+i+1].y;
+ ++i; // we're using point i+1 as the starting point, so skip it
+ }
+ } else {
+ sx = x;
+ sy = y;
+ }
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);
+ was_off = 0;
+ next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
+ ++j;
+ } else {
+ if (!(flags & 1)) { // if it's a curve
+ if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
+ cx = x;
+ cy = y;
+ was_off = 1;
+ } else {
+ if (was_off)
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
+ else
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
+ was_off = 0;
+ }
+ }
+ }
+ num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
+ } else if (numberOfContours == -1) {
+ // Compound shapes.
+ int more = 1;
+ stbtt_uint8 *comp = data + g + 10;
+ num_vertices = 0;
+ vertices = 0;
+ while (more) {
+ stbtt_uint16 flags, gidx;
+ int comp_num_verts = 0, i;
+ stbtt_vertex *comp_verts = 0, *tmp = 0;
+ float mtx[6] = {1,0,0,1,0,0}, m, n;
+
+ flags = ttSHORT(comp); comp+=2;
+ gidx = ttSHORT(comp); comp+=2;
+
+ if (flags & 2) { // XY values
+ if (flags & 1) { // shorts
+ mtx[4] = ttSHORT(comp); comp+=2;
+ mtx[5] = ttSHORT(comp); comp+=2;
+ } else {
+ mtx[4] = ttCHAR(comp); comp+=1;
+ mtx[5] = ttCHAR(comp); comp+=1;
+ }
+ }
+ else {
+ // @TODO handle matching point
+ STBTT_assert(0);
+ }
+ if (flags & (1<<3)) { // WE_HAVE_A_SCALE
+ mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
+ mtx[1] = mtx[2] = 0;
+ } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
+ mtx[1] = mtx[2] = 0;
+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
+ } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
+ mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
+ mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
+ }
+
+ // Find transformation scales.
+ m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
+ n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
+
+ // Get indexed glyph.
+ comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
+ if (comp_num_verts > 0) {
+ // Transform vertices.
+ for (i = 0; i < comp_num_verts; ++i) {
+ stbtt_vertex* v = &comp_verts[i];
+ stbtt_vertex_type x,y;
+ x=v->x; y=v->y;
+ v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
+ v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+ x=v->cx; y=v->cy;
+ v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
+ v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+ }
+ // Append vertices.
+ tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
+ if (!tmp) {
+ if (vertices) STBTT_free(vertices, info->userdata);
+ if (comp_verts) STBTT_free(comp_verts, info->userdata);
+ return 0;
+ }
+ if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
+ STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
+ if (vertices) STBTT_free(vertices, info->userdata);
+ vertices = tmp;
+ STBTT_free(comp_verts, info->userdata);
+ num_vertices += comp_num_verts;
+ }
+ // More components ?
+ more = flags & (1<<5);
+ }
+ } else if (numberOfContours < 0) {
+ // @TODO other compound variations?
+ STBTT_assert(0);
+ } else {
+ // numberOfCounters == 0, do nothing
+ }
+
+ *pvertices = vertices;
+ return num_vertices;
+}
+
+STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
+{
+ stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
+ if (glyph_index < numOfLongHorMetrics) {
+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index);
+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
+ } else {
+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
+ }
+}
+
+STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
+{
+ stbtt_uint8 *data = info->data + info->kern;
+ stbtt_uint32 needle, straw;
+ int l, r, m;
+
+ // we only look at the first table. it must be 'horizontal' and format 0.
+ if (!info->kern)
+ return 0;
+ if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
+ return 0;
+ if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
+ return 0;
+
+ l = 0;
+ r = ttUSHORT(data+10) - 1;
+ needle = glyph1 << 16 | glyph2;
+ while (l <= r) {
+ m = (l + r) >> 1;
+ straw = ttULONG(data+18+(m*6)); // note: unaligned read
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else
+ return ttSHORT(data+22+(m*6));
+ }
+ return 0;
+}
+
+STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
+{
+ if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs
+ return 0;
+ return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
+}
+
+STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
+{
+ stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
+}
+
+STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
+{
+ if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);
+ if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
+ if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
+}
+
+STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
+{
+ *x0 = ttSHORT(info->data + info->head + 36);
+ *y0 = ttSHORT(info->data + info->head + 38);
+ *x1 = ttSHORT(info->data + info->head + 40);
+ *y1 = ttSHORT(info->data + info->head + 42);
+}
+
+STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
+{
+ int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
+ return (float) height / fheight;
+}
+
+STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)
+{
+ int unitsPerEm = ttUSHORT(info->data + info->head + 18);
+ return pixels / unitsPerEm;
+}
+
+STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
+{
+ STBTT_free(v, info->userdata);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// antialiasing software rasterizer
+//
+
+STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning
+ if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
+ // e.g. space character
+ if (ix0) *ix0 = 0;
+ if (iy0) *iy0 = 0;
+ if (ix1) *ix1 = 0;
+ if (iy1) *iy1 = 0;
+ } else {
+ // move to integral bboxes (treating pixels as little squares, what pixels get touched)?
+ if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);
+ if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);
+ if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);
+ if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);
+ }
+}
+
+STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);
+}
+
+STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);
+}
+
+STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Rasterizer
+
+typedef struct stbtt__hheap_chunk
+{
+ struct stbtt__hheap_chunk *next;
+} stbtt__hheap_chunk;
+
+typedef struct stbtt__hheap
+{
+ struct stbtt__hheap_chunk *head;
+ void *first_free;
+ int num_remaining_in_head_chunk;
+} stbtt__hheap;
+
+static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
+{
+ if (hh->first_free) {
+ void *p = hh->first_free;
+ hh->first_free = * (void **) p;
+ return p;
+ } else {
+ if (hh->num_remaining_in_head_chunk == 0) {
+ int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);
+ stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);
+ if (c == NULL)
+ return NULL;
+ c->next = hh->head;
+ hh->head = c;
+ hh->num_remaining_in_head_chunk = count;
+ }
+ --hh->num_remaining_in_head_chunk;
+ return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk;
+ }
+}
+
+static void stbtt__hheap_free(stbtt__hheap *hh, void *p)
+{
+ *(void **) p = hh->first_free;
+ hh->first_free = p;
+}
+
+static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)
+{
+ stbtt__hheap_chunk *c = hh->head;
+ while (c) {
+ stbtt__hheap_chunk *n = c->next;
+ STBTT_free(c, userdata);
+ c = n;
+ }
+}
+
+typedef struct stbtt__edge {
+ float x0,y0, x1,y1;
+ int invert;
+} stbtt__edge;
+
+
+typedef struct stbtt__active_edge
+{
+ struct stbtt__active_edge *next;
+ #if STBTT_RASTERIZER_VERSION==1
+ int x,dx;
+ float ey;
+ int direction;
+ #elif STBTT_RASTERIZER_VERSION==2
+ float fx,fdx,fdy;
+ float direction;
+ float sy;
+ float ey;
+ #else
+ #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
+ #endif
+} stbtt__active_edge;
+
+#if STBTT_RASTERIZER_VERSION == 1
+#define STBTT_FIXSHIFT 10
+#define STBTT_FIX (1 << STBTT_FIXSHIFT)
+#define STBTT_FIXMASK (STBTT_FIX-1)
+
+static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
+{
+ stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+ STBTT_assert(z != NULL);
+ if (!z) return z;
+
+ // round dx down to avoid overshooting
+ if (dxdy < 0)
+ z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);
+ else
+ z->dx = STBTT_ifloor(STBTT_FIX * dxdy);
+
+ z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount
+ z->x -= off_x * STBTT_FIX;
+
+ z->ey = e->y1;
+ z->next = 0;
+ z->direction = e->invert ? 1 : -1;
+ return z;
+}
+#elif STBTT_RASTERIZER_VERSION == 2
+static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
+{
+ stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+ STBTT_assert(z != NULL);
+ //STBTT_assert(e->y0 <= start_point);
+ if (!z) return z;
+ z->fdx = dxdy;
+ z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;
+ z->fx = e->x0 + dxdy * (start_point - e->y0);
+ z->fx -= off_x;
+ z->direction = e->invert ? 1.0f : -1.0f;
+ z->sy = e->y0;
+ z->ey = e->y1;
+ z->next = 0;
+ return z;
+}
+#else
+#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
+#endif
+
+#if STBTT_RASTERIZER_VERSION == 1
+// 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 stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
+{
+ // non-zero winding fill
+ int x0=0, w=0;
+
+ while (e) {
+ if (w == 0) {
+ // if we're currently at zero, we need to record the edge start point
+ x0 = e->x; w += e->direction;
+ } else {
+ int x1 = e->x; w += e->direction;
+ // if we went to zero, we need to draw
+ if (w == 0) {
+ int i = x0 >> STBTT_FIXSHIFT;
+ int j = x1 >> STBTT_FIXSHIFT;
+
+ if (i < len && j >= 0) {
+ if (i == j) {
+ // x0,x1 are the same pixel, so compute combined coverage
+ scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);
+ } else {
+ if (i >= 0) // add antialiasing for x0
+ scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);
+ else
+ i = -1; // clip
+
+ if (j < len) // add antialiasing for x1
+ scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);
+ else
+ j = len; // clip
+
+ for (++i; i < j; ++i) // fill pixels between x0 and x1
+ scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
+ }
+ }
+ }
+ }
+
+ e = e->next;
+ }
+}
+
+static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
+{
+ stbtt__hheap hh = { 0, 0, 0 };
+ stbtt__active_edge *active = NULL;
+ int y,j=0;
+ int max_weight = (255 / vsubsample); // weight per vertical scanline
+ int s; // vertical subsample index
+ unsigned char scanline_data[512], *scanline;
+
+ if (result->w > 512)
+ scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
+ else
+ scanline = scanline_data;
+
+ y = off_y * vsubsample;
+ e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
+
+ while (j < result->h) {
+ STBTT_memset(scanline, 0, result->w);
+ for (s=0; s < vsubsample; ++s) {
+ // find center of pixel for this scanline
+ float scan_y = y + 0.5f;
+ stbtt__active_edge **step = &active;
+
+ // update all active edges;
+ // remove all active edges that terminate before the center of this scanline
+ while (*step) {
+ stbtt__active_edge * z = *step;
+ if (z->ey <= scan_y) {
+ *step = z->next; // delete from list
+ STBTT_assert(z->direction);
+ z->direction = 0;
+ stbtt__hheap_free(&hh, 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) {
+ stbtt__active_edge *t = *step;
+ stbtt__active_edge *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->y0 <= scan_y) {
+ if (e->y1 > scan_y) {
+ stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);
+ if (z != NULL) {
+ // 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
+ stbtt__active_edge *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 XOR fashion
+ if (active)
+ stbtt__fill_active_edges(scanline, result->w, active, max_weight);
+
+ ++y;
+ }
+ STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
+ ++j;
+ }
+
+ stbtt__hheap_cleanup(&hh, userdata);
+
+ if (scanline != scanline_data)
+ STBTT_free(scanline, userdata);
+}
+
+#elif STBTT_RASTERIZER_VERSION == 2
+
+// the edge passed in here does not cross the vertical line at x or the vertical line at x+1
+// (i.e. it has already been clipped to those)
+static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)
+{
+ if (y0 == y1) return;
+ STBTT_assert(y0 < y1);
+ STBTT_assert(e->sy <= e->ey);
+ if (y0 > e->ey) return;
+ if (y1 < e->sy) return;
+ if (y0 < e->sy) {
+ x0 += (x1-x0) * (e->sy - y0) / (y1-y0);
+ y0 = e->sy;
+ }
+ if (y1 > e->ey) {
+ x1 += (x1-x0) * (e->ey - y1) / (y1-y0);
+ y1 = e->ey;
+ }
+
+ if (x0 == x)
+ STBTT_assert(x1 <= x+1);
+ else if (x0 == x+1)
+ STBTT_assert(x1 >= x);
+ else if (x0 <= x)
+ STBTT_assert(x1 <= x);
+ else if (x0 >= x+1)
+ STBTT_assert(x1 >= x+1);
+ else
+ STBTT_assert(x1 >= x && x1 <= x+1);
+
+ if (x0 <= x && x1 <= x)
+ scanline[x] += e->direction * (y1-y0);
+ else if (x0 >= x+1 && x1 >= x+1)
+ ;
+ else {
+ STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);
+ scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position
+ }
+}
+
+static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
+{
+ float y_bottom = y_top+1;
+
+ while (e) {
+ // brute force every pixel
+
+ // compute intersection points with top & bottom
+ STBTT_assert(e->ey >= y_top);
+
+ if (e->fdx == 0) {
+ float x0 = e->fx;
+ if (x0 < len) {
+ if (x0 >= 0) {
+ stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);
+ stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);
+ } else {
+ stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);
+ }
+ }
+ } else {
+ float x0 = e->fx;
+ float dx = e->fdx;
+ float xb = x0 + dx;
+ float x_top, x_bottom;
+ float sy0,sy1;
+ float dy = e->fdy;
+ STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);
+
+ // compute endpoints of line segment clipped to this scanline (if the
+ // line segment starts on this scanline. x0 is the intersection of the
+ // line with y_top, but that may be off the line segment.
+ if (e->sy > y_top) {
+ x_top = x0 + dx * (e->sy - y_top);
+ sy0 = e->sy;
+ } else {
+ x_top = x0;
+ sy0 = y_top;
+ }
+ if (e->ey < y_bottom) {
+ x_bottom = x0 + dx * (e->ey - y_top);
+ sy1 = e->ey;
+ } else {
+ x_bottom = xb;
+ sy1 = y_bottom;
+ }
+
+ if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {
+ // from here on, we don't have to range check x values
+
+ if ((int) x_top == (int) x_bottom) {
+ float height;
+ // simple case, only spans one pixel
+ int x = (int) x_top;
+ height = sy1 - sy0;
+ STBTT_assert(x >= 0 && x < len);
+ scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
+ scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
+ } else {
+ int x,x1,x2;
+ float y_crossing, step, sign, area;
+ // covers 2+ pixels
+ if (x_top > x_bottom) {
+ // flip scanline vertically; signed area is the same
+ float t;
+ sy0 = y_bottom - (sy0 - y_top);
+ sy1 = y_bottom - (sy1 - y_top);
+ t = sy0, sy0 = sy1, sy1 = t;
+ t = x_bottom, x_bottom = x_top, x_top = t;
+ dx = -dx;
+ dy = -dy;
+ t = x0, x0 = xb, xb = t;
+ }
+
+ x1 = (int) x_top;
+ x2 = (int) x_bottom;
+ // compute intersection with y axis at x1+1
+ y_crossing = (x1+1 - x0) * dy + y_top;
+
+ sign = e->direction;
+ // area of the rectangle covered from y0..y_crossing
+ area = sign * (y_crossing-sy0);
+ // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
+ scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2);
+
+ step = sign * dy;
+ for (x = x1+1; x < x2; ++x) {
+ scanline[x] += area + step/2;
+ area += step;
+ }
+ y_crossing += dy * (x2 - (x1+1));
+
+ STBTT_assert(STBTT_fabs(area) <= 1.01f);
+
+ scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing);
+
+ scanline_fill[x2] += sign * (sy1-sy0);
+ }
+ } else {
+ // if edge goes outside of box we're drawing, we require
+ // clipping logic. since this does not match the intended use
+ // of this library, we use a different, very slow brute
+ // force implementation
+ int x;
+ for (x=0; x < len; ++x) {
+ // cases:
+ //
+ // there can be up to two intersections with the pixel. any intersection
+ // with left or right edges can be handled by splitting into two (or three)
+ // regions. intersections with top & bottom do not necessitate case-wise logic.
+ //
+ // the old way of doing this found the intersections with the left & right edges,
+ // then used some simple logic to produce up to three segments in sorted order
+ // from top-to-bottom. however, this had a problem: if an x edge was epsilon
+ // across the x border, then the corresponding y position might not be distinct
+ // from the other y segment, and it might ignored as an empty segment. to avoid
+ // that, we need to explicitly produce segments based on x positions.
+
+ // rename variables to clear pairs
+ float y0 = y_top;
+ float x1 = (float) (x);
+ float x2 = (float) (x+1);
+ float x3 = xb;
+ float y3 = y_bottom;
+ float y1,y2;
+
+ // x = e->x + e->dx * (y-y_top)
+ // (y-y_top) = (x - e->x) / e->dx
+ // y = (x - e->x) / e->dx + y_top
+ y1 = (x - x0) / dx + y_top;
+ y2 = (x+1 - x0) / dx + y_top;
+
+ if (x0 < x1 && x3 > x2) { // three segments descending down-right
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);
+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+ } else if (x3 < x1 && x0 > x2) { // three segments descending down-left
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);
+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
+ } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
+ } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
+ } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+ } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+ } else { // one segment
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);
+ }
+ }
+ }
+ }
+ e = e->next;
+ }
+}
+
+// directly AA rasterize edges w/o supersampling
+static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
+{
+ stbtt__hheap hh = { 0, 0, 0 };
+ stbtt__active_edge *active = NULL;
+ int y,j=0, i;
+ float scanline_data[129], *scanline, *scanline2;
+
+ STBTT__NOTUSED(vsubsample);
+
+ if (result->w > 64)
+ scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);
+ else
+ scanline = scanline_data;
+
+ scanline2 = scanline + result->w;
+
+ y = off_y;
+ e[n].y0 = (float) (off_y + result->h) + 1;
+
+ while (j < result->h) {
+ // find center of pixel for this scanline
+ float scan_y_top = y + 0.0f;
+ float scan_y_bottom = y + 1.0f;
+ stbtt__active_edge **step = &active;
+
+ STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));
+ STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));
+
+ // update all active edges;
+ // remove all active edges that terminate before the top of this scanline
+ while (*step) {
+ stbtt__active_edge * z = *step;
+ if (z->ey <= scan_y_top) {
+ *step = z->next; // delete from list
+ STBTT_assert(z->direction);
+ z->direction = 0;
+ stbtt__hheap_free(&hh, z);
+ } else {
+ step = &((*step)->next); // advance through list
+ }
+ }
+
+ // insert all edges that start before the bottom of this scanline
+ while (e->y0 <= scan_y_bottom) {
+ if (e->y0 != e->y1) {
+ stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);
+ if (z != NULL) {
+ STBTT_assert(z->ey >= scan_y_top);
+ // insert at front
+ z->next = active;
+ active = z;
+ }
+ }
+ ++e;
+ }
+
+ // now process all active edges
+ if (active)
+ stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);
+
+ {
+ float sum = 0;
+ for (i=0; i < result->w; ++i) {
+ float k;
+ int m;
+ sum += scanline2[i];
+ k = scanline[i] + sum;
+ k = (float) STBTT_fabs(k)*255 + 0.5f;
+ m = (int) k;
+ if (m > 255) m = 255;
+ result->pixels[j*result->stride + i] = (unsigned char) m;
+ }
+ }
+ // advance all the edges
+ step = &active;
+ while (*step) {
+ stbtt__active_edge *z = *step;
+ z->fx += z->fdx; // advance to position for current scanline
+ step = &((*step)->next); // advance through list
+ }
+
+ ++y;
+ ++j;
+ }
+
+ stbtt__hheap_cleanup(&hh, userdata);
+
+ if (scanline != scanline_data)
+ STBTT_free(scanline, userdata);
+}
+#else
+#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
+#endif
+
+#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0)
+
+static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)
+{
+ int i,j;
+ for (i=1; i < n; ++i) {
+ stbtt__edge t = p[i], *a = &t;
+ j = i;
+ while (j > 0) {
+ stbtt__edge *b = &p[j-1];
+ int c = STBTT__COMPARE(a,b);
+ if (!c) break;
+ p[j] = p[j-1];
+ --j;
+ }
+ if (i != j)
+ p[j] = t;
+ }
+}
+
+static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)
+{
+ /* threshhold for transitioning to insertion sort */
+ while (n > 12) {
+ stbtt__edge t;
+ int c01,c12,c,m,i,j;
+
+ /* compute median of three */
+ m = n >> 1;
+ c01 = STBTT__COMPARE(&p[0],&p[m]);
+ c12 = STBTT__COMPARE(&p[m],&p[n-1]);
+ /* if 0 >= mid >= end, or 0 < mid < end, then use mid */
+ if (c01 != c12) {
+ /* otherwise, we'll need to swap something else to middle */
+ int z;
+ c = STBTT__COMPARE(&p[0],&p[n-1]);
+ /* 0>mid && mid<n: 0>n => n; 0<n => 0 */
+ /* 0<mid && mid>n: 0>n => 0; 0<n => n */
+ z = (c == c12) ? 0 : n-1;
+ t = p[z];
+ p[z] = p[m];
+ p[m] = t;
+ }
+ /* now p[m] is the median-of-three */
+ /* swap it to the beginning so it won't move around */
+ t = p[0];
+ p[0] = p[m];
+ p[m] = t;
+
+ /* partition loop */
+ i=1;
+ j=n-1;
+ for(;;) {
+ /* handling of equality is crucial here */
+ /* for sentinels & efficiency with duplicates */
+ for (;;++i) {
+ if (!STBTT__COMPARE(&p[i], &p[0])) break;
+ }
+ for (;;--j) {
+ if (!STBTT__COMPARE(&p[0], &p[j])) break;
+ }
+ /* make sure we haven't crossed */
+ if (i >= j) break;
+ t = p[i];
+ p[i] = p[j];
+ p[j] = t;
+
+ ++i;
+ --j;
+ }
+ /* recurse on smaller side, iterate on larger */
+ if (j < (n-i)) {
+ stbtt__sort_edges_quicksort(p,j);
+ p = p+i;
+ n = n-i;
+ } else {
+ stbtt__sort_edges_quicksort(p+i, n-i);
+ n = j;
+ }
+ }
+}
+
+static void stbtt__sort_edges(stbtt__edge *p, int n)
+{
+ stbtt__sort_edges_quicksort(p, n);
+ stbtt__sort_edges_ins_sort(p, n);
+}
+
+typedef struct
+{
+ float x,y;
+} stbtt__point;
+
+static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)
+{
+ float y_scale_inv = invert ? -scale_y : scale_y;
+ stbtt__edge *e;
+ int n,i,j,k,m;
+#if STBTT_RASTERIZER_VERSION == 1
+ int vsubsample = result->h < 8 ? 15 : 5;
+#elif STBTT_RASTERIZER_VERSION == 2
+ int vsubsample = 1;
+#else
+ #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
+#endif
+ // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
+
+ // now we have to blow out the windings into explicit edge lists
+ n = 0;
+ for (i=0; i < windings; ++i)
+ n += wcount[i];
+
+ e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
+ if (e == 0) return;
+ n = 0;
+
+ m=0;
+ for (i=0; i < windings; ++i) {
+ stbtt__point *p = pts + m;
+ m += wcount[i];
+ j = wcount[i]-1;
+ for (k=0; k < wcount[i]; j=k++) {
+ int a=k,b=j;
+ // skip the edge if horizontal
+ if (p[j].y == p[k].y)
+ continue;
+ // add edge from j to k to the list
+ e[n].invert = 0;
+ if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
+ e[n].invert = 1;
+ a=j,b=k;
+ }
+ e[n].x0 = p[a].x * scale_x + shift_x;
+ e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;
+ e[n].x1 = p[b].x * scale_x + shift_x;
+ e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;
+ ++n;
+ }
+ }
+
+ // now sort the edges by their highest point (should snap to integer, and then by x)
+ //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
+ stbtt__sort_edges(e, n);
+
+ // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
+ stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
+
+ STBTT_free(e, userdata);
+}
+
+static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
+{
+ if (!points) return; // during first pass, it's unallocated
+ points[n].x = x;
+ points[n].y = y;
+}
+
+// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching
+static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
+{
+ // midpoint
+ float mx = (x0 + 2*x1 + x2)/4;
+ float my = (y0 + 2*y1 + y2)/4;
+ // versus directly drawn line
+ float dx = (x0+x2)/2 - mx;
+ float dy = (y0+y2)/2 - my;
+ if (n > 16) // 65536 segments on one curve better be enough!
+ return 1;
+ if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
+ stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
+ stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
+ } else {
+ stbtt__add_point(points, *num_points,x2,y2);
+ *num_points = *num_points+1;
+ }
+ return 1;
+}
+
+// returns number of contours
+static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
+{
+ stbtt__point *points=0;
+ int num_points=0;
+
+ float objspace_flatness_squared = objspace_flatness * objspace_flatness;
+ int i,n=0,start=0, pass;
+
+ // count how many "moves" there are to get the contour count
+ for (i=0; i < num_verts; ++i)
+ if (vertices[i].type == STBTT_vmove)
+ ++n;
+
+ *num_contours = n;
+ if (n == 0) return 0;
+
+ *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
+
+ if (*contour_lengths == 0) {
+ *num_contours = 0;
+ return 0;
+ }
+
+ // make two passes through the points so we don't need to realloc
+ for (pass=0; pass < 2; ++pass) {
+ float x=0,y=0;
+ if (pass == 1) {
+ points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
+ if (points == NULL) goto error;
+ }
+ num_points = 0;
+ n= -1;
+ for (i=0; i < num_verts; ++i) {
+ switch (vertices[i].type) {
+ case STBTT_vmove:
+ // start the next contour
+ if (n >= 0)
+ (*contour_lengths)[n] = num_points - start;
+ ++n;
+ start = num_points;
+
+ x = vertices[i].x, y = vertices[i].y;
+ stbtt__add_point(points, num_points++, x,y);
+ break;
+ case STBTT_vline:
+ x = vertices[i].x, y = vertices[i].y;
+ stbtt__add_point(points, num_points++, x, y);
+ break;
+ case STBTT_vcurve:
+ stbtt__tesselate_curve(points, &num_points, x,y,
+ vertices[i].cx, vertices[i].cy,
+ vertices[i].x, vertices[i].y,
+ objspace_flatness_squared, 0);
+ x = vertices[i].x, y = vertices[i].y;
+ break;
+ }
+ }
+ (*contour_lengths)[n] = num_points - start;
+ }
+
+ return points;
+error:
+ STBTT_free(points, userdata);
+ STBTT_free(*contour_lengths, userdata);
+ *contour_lengths = 0;
+ *num_contours = 0;
+ return NULL;
+}
+
+STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
+{
+ float scale = scale_x > scale_y ? scale_y : scale_x;
+ int winding_count, *winding_lengths;
+ stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
+ if (windings) {
+ stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);
+ STBTT_free(winding_lengths, userdata);
+ STBTT_free(windings, userdata);
+ }
+}
+
+STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
+{
+ STBTT_free(bitmap, userdata);
+}
+
+STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)
+{
+ int ix0,iy0,ix1,iy1;
+ stbtt__bitmap gbm;
+ stbtt_vertex *vertices;
+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
+
+ if (scale_x == 0) scale_x = scale_y;
+ if (scale_y == 0) {
+ if (scale_x == 0) {
+ STBTT_free(vertices, info->userdata);
+ return NULL;
+ }
+ scale_y = scale_x;
+ }
+
+ stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);
+
+ // now we get the size
+ gbm.w = (ix1 - ix0);
+ gbm.h = (iy1 - iy0);
+ gbm.pixels = NULL; // in case we error
+
+ if (width ) *width = gbm.w;
+ if (height) *height = gbm.h;
+ if (xoff ) *xoff = ix0;
+ if (yoff ) *yoff = iy0;
+
+ if (gbm.w && gbm.h) {
+ gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
+ if (gbm.pixels) {
+ gbm.stride = gbm.w;
+
+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);
+ }
+ }
+ STBTT_free(vertices, info->userdata);
+ return gbm.pixels;
+}
+
+STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
+{
+ return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);
+}
+
+STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)
+{
+ int ix0,iy0;
+ stbtt_vertex *vertices;
+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
+ stbtt__bitmap gbm;
+
+ stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
+ gbm.pixels = output;
+ gbm.w = out_w;
+ gbm.h = out_h;
+ gbm.stride = out_stride;
+
+ if (gbm.w && gbm.h)
+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);
+
+ STBTT_free(vertices, info->userdata);
+}
+
+STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
+{
+ stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);
+}
+
+STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
+{
+ return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
+}
+
+STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
+{
+ stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));
+}
+
+STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
+{
+ return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);
+}
+
+STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
+{
+ stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// bitmap baking
+//
+// This is SUPER-CRAPPY packing to keep source code small
+
+STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
+ float pixel_height, // height of font in pixels
+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in
+ int first_char, int num_chars, // characters to bake
+ stbtt_bakedchar *chardata)
+{
+ float scale;
+ int x,y,bottom_y, i;
+ stbtt_fontinfo f;
+ f.userdata = NULL;
+ if (!stbtt_InitFont(&f, data, offset))
+ return -1;
+ STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
+ x=y=1;
+ bottom_y = 1;
+
+ scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
+
+ for (i=0; i < num_chars; ++i) {
+ int advance, lsb, x0,y0,x1,y1,gw,gh;
+ int g = stbtt_FindGlyphIndex(&f, first_char + i);
+ stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
+ stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
+ gw = x1-x0;
+ gh = y1-y0;
+ if (x + gw + 1 >= pw)
+ y = bottom_y, x = 1; // advance to next row
+ if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
+ return -i;
+ STBTT_assert(x+gw < pw);
+ STBTT_assert(y+gh < ph);
+ stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
+ chardata[i].x0 = (stbtt_int16) x;
+ chardata[i].y0 = (stbtt_int16) y;
+ chardata[i].x1 = (stbtt_int16) (x + gw);
+ chardata[i].y1 = (stbtt_int16) (y + gh);
+ chardata[i].xadvance = scale * advance;
+ chardata[i].xoff = (float) x0;
+ chardata[i].yoff = (float) y0;
+ x = x + gw + 1;
+ if (y+gh+1 > bottom_y)
+ bottom_y = y+gh+1;
+ }
+ return bottom_y;
+}
+
+STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
+{
+ float d3d_bias = opengl_fillrule ? 0 : -0.5f;
+ float ipw = 1.0f / pw, iph = 1.0f / ph;
+ stbtt_bakedchar *b = chardata + char_index;
+ int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);
+ int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);
+
+ q->x0 = round_x + d3d_bias;
+ q->y0 = round_y + d3d_bias;
+ q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
+ q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
+
+ q->s0 = b->x0 * ipw;
+ q->t0 = b->y0 * iph;
+ q->s1 = b->x1 * ipw;
+ q->t1 = b->y1 * iph;
+
+ *xpos += b->xadvance;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// rectangle packing replacement routines if you don't have stb_rect_pack.h
+//
+
+#ifndef STB_RECT_PACK_VERSION
+
+typedef int stbrp_coord;
+
+////////////////////////////////////////////////////////////////////////////////////
+// //
+// //
+// COMPILER WARNING ?!?!? //
+// //
+// //
+// if you get a compile warning due to these symbols being defined more than //
+// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" //
+// //
+////////////////////////////////////////////////////////////////////////////////////
+
+typedef struct
+{
+ int width,height;
+ int x,y,bottom_y;
+} stbrp_context;
+
+typedef struct
+{
+ unsigned char x;
+} stbrp_node;
+
+struct stbrp_rect
+{
+ stbrp_coord x,y;
+ int id,w,h,was_packed;
+};
+
+static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)
+{
+ con->width = pw;
+ con->height = ph;
+ con->x = 0;
+ con->y = 0;
+ con->bottom_y = 0;
+ STBTT__NOTUSED(nodes);
+ STBTT__NOTUSED(num_nodes);
+}
+
+static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)
+{
+ int i;
+ for (i=0; i < num_rects; ++i) {
+ if (con->x + rects[i].w > con->width) {
+ con->x = 0;
+ con->y = con->bottom_y;
+ }
+ if (con->y + rects[i].h > con->height)
+ break;
+ rects[i].x = con->x;
+ rects[i].y = con->y;
+ rects[i].was_packed = 1;
+ con->x += rects[i].w;
+ if (con->y + rects[i].h > con->bottom_y)
+ con->bottom_y = con->y + rects[i].h;
+ }
+ for ( ; i < num_rects; ++i)
+ rects[i].was_packed = 0;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// bitmap baking
+//
+// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If
+// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.
+
+STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)
+{
+ stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context);
+ int num_nodes = pw - padding;
+ stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context);
+
+ if (context == NULL || nodes == NULL) {
+ if (context != NULL) STBTT_free(context, alloc_context);
+ if (nodes != NULL) STBTT_free(nodes , alloc_context);
+ return 0;
+ }
+
+ spc->user_allocator_context = alloc_context;
+ spc->width = pw;
+ spc->height = ph;
+ spc->pixels = pixels;
+ spc->pack_info = context;
+ spc->nodes = nodes;
+ spc->padding = padding;
+ spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
+ spc->h_oversample = 1;
+ spc->v_oversample = 1;
+
+ stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
+
+ if (pixels)
+ STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
+
+ return 1;
+}
+
+STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc)
+{
+ STBTT_free(spc->nodes , spc->user_allocator_context);
+ STBTT_free(spc->pack_info, spc->user_allocator_context);
+}
+
+STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)
+{
+ STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);
+ STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);
+ if (h_oversample <= STBTT_MAX_OVERSAMPLE)
+ spc->h_oversample = h_oversample;
+ if (v_oversample <= STBTT_MAX_OVERSAMPLE)
+ spc->v_oversample = v_oversample;
+}
+
+#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1)
+
+static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
+{
+ unsigned char buffer[STBTT_MAX_OVERSAMPLE];
+ int safe_w = w - kernel_width;
+ int j;
+ STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
+ for (j=0; j < h; ++j) {
+ int i;
+ unsigned int total;
+ STBTT_memset(buffer, 0, kernel_width);
+
+ total = 0;
+
+ // make kernel_width a constant in common cases so compiler can optimize out the divide
+ switch (kernel_width) {
+ case 2:
+ for (i=0; i <= safe_w; ++i) {
+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+ pixels[i] = (unsigned char) (total / 2);
+ }
+ break;
+ case 3:
+ for (i=0; i <= safe_w; ++i) {
+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+ pixels[i] = (unsigned char) (total / 3);
+ }
+ break;
+ case 4:
+ for (i=0; i <= safe_w; ++i) {
+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+ pixels[i] = (unsigned char) (total / 4);
+ }
+ break;
+ case 5:
+ for (i=0; i <= safe_w; ++i) {
+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+ pixels[i] = (unsigned char) (total / 5);
+ }
+ break;
+ default:
+ for (i=0; i <= safe_w; ++i) {
+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+ pixels[i] = (unsigned char) (total / kernel_width);
+ }
+ break;
+ }
+
+ for (; i < w; ++i) {
+ STBTT_assert(pixels[i] == 0);
+ total -= buffer[i & STBTT__OVER_MASK];
+ pixels[i] = (unsigned char) (total / kernel_width);
+ }
+
+ pixels += stride_in_bytes;
+ }
+}
+
+static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
+{
+ unsigned char buffer[STBTT_MAX_OVERSAMPLE];
+ int safe_h = h - kernel_width;
+ int j;
+ STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
+ for (j=0; j < w; ++j) {
+ int i;
+ unsigned int total;
+ STBTT_memset(buffer, 0, kernel_width);
+
+ total = 0;
+
+ // make kernel_width a constant in common cases so compiler can optimize out the divide
+ switch (kernel_width) {
+ case 2:
+ for (i=0; i <= safe_h; ++i) {
+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / 2);
+ }
+ break;
+ case 3:
+ for (i=0; i <= safe_h; ++i) {
+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / 3);
+ }
+ break;
+ case 4:
+ for (i=0; i <= safe_h; ++i) {
+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / 4);
+ }
+ break;
+ case 5:
+ for (i=0; i <= safe_h; ++i) {
+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / 5);
+ }
+ break;
+ default:
+ for (i=0; i <= safe_h; ++i) {
+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
+ }
+ break;
+ }
+
+ for (; i < h; ++i) {
+ STBTT_assert(pixels[i*stride_in_bytes] == 0);
+ total -= buffer[i & STBTT__OVER_MASK];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
+ }
+
+ pixels += 1;
+ }
+}
+
+static float stbtt__oversample_shift(int oversample)
+{
+ if (!oversample)
+ return 0.0f;
+
+ // The prefilter is a box filter of width "oversample",
+ // which shifts phase by (oversample - 1)/2 pixels in
+ // oversampled space. We want to shift in the opposite
+ // direction to counter this.
+ return (float)-(oversample - 1) / (2.0f * (float)oversample);
+}
+
+// rects array must be big enough to accommodate all characters in the given ranges
+STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
+{
+ int i,j,k;
+
+ k=0;
+ for (i=0; i < num_ranges; ++i) {
+ float fh = ranges[i].font_size;
+ float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
+ ranges[i].h_oversample = (unsigned char) spc->h_oversample;
+ ranges[i].v_oversample = (unsigned char) spc->v_oversample;
+ for (j=0; j < ranges[i].num_chars; ++j) {
+ int x0,y0,x1,y1;
+ int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
+ int glyph = stbtt_FindGlyphIndex(info, codepoint);
+ stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
+ scale * spc->h_oversample,
+ scale * spc->v_oversample,
+ 0,0,
+ &x0,&y0,&x1,&y1);
+ rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
+ rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
+ ++k;
+ }
+ }
+
+ return k;
+}
+
+// rects array must be big enough to accommodate all characters in the given ranges
+STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
+{
+ int i,j,k, return_value = 1;
+
+ // save current values
+ int old_h_over = spc->h_oversample;
+ int old_v_over = spc->v_oversample;
+
+ k = 0;
+ for (i=0; i < num_ranges; ++i) {
+ float fh = ranges[i].font_size;
+ float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
+ float recip_h,recip_v,sub_x,sub_y;
+ spc->h_oversample = ranges[i].h_oversample;
+ spc->v_oversample = ranges[i].v_oversample;
+ recip_h = 1.0f / spc->h_oversample;
+ recip_v = 1.0f / spc->v_oversample;
+ sub_x = stbtt__oversample_shift(spc->h_oversample);
+ sub_y = stbtt__oversample_shift(spc->v_oversample);
+ for (j=0; j < ranges[i].num_chars; ++j) {
+ stbrp_rect *r = &rects[k];
+ if (r->was_packed) {
+ stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
+ int advance, lsb, x0,y0,x1,y1;
+ int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
+ int glyph = stbtt_FindGlyphIndex(info, codepoint);
+ stbrp_coord pad = (stbrp_coord) spc->padding;
+
+ // pad on left and top
+ r->x += pad;
+ r->y += pad;
+ r->w -= pad;
+ r->h -= pad;
+ stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);
+ stbtt_GetGlyphBitmapBox(info, glyph,
+ scale * spc->h_oversample,
+ scale * spc->v_oversample,
+ &x0,&y0,&x1,&y1);
+ stbtt_MakeGlyphBitmapSubpixel(info,
+ spc->pixels + r->x + r->y*spc->stride_in_bytes,
+ r->w - spc->h_oversample+1,
+ r->h - spc->v_oversample+1,
+ spc->stride_in_bytes,
+ scale * spc->h_oversample,
+ scale * spc->v_oversample,
+ 0,0,
+ glyph);
+
+ if (spc->h_oversample > 1)
+ stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
+ r->w, r->h, spc->stride_in_bytes,
+ spc->h_oversample);
+
+ if (spc->v_oversample > 1)
+ stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
+ r->w, r->h, spc->stride_in_bytes,
+ spc->v_oversample);
+
+ bc->x0 = (stbtt_int16) r->x;
+ bc->y0 = (stbtt_int16) r->y;
+ bc->x1 = (stbtt_int16) (r->x + r->w);
+ bc->y1 = (stbtt_int16) (r->y + r->h);
+ bc->xadvance = scale * advance;
+ bc->xoff = (float) x0 * recip_h + sub_x;
+ bc->yoff = (float) y0 * recip_v + sub_y;
+ bc->xoff2 = (x0 + r->w) * recip_h + sub_x;
+ bc->yoff2 = (y0 + r->h) * recip_v + sub_y;
+ } else {
+ return_value = 0; // if any fail, report failure
+ }
+
+ ++k;
+ }
+ }
+
+ // restore original values
+ spc->h_oversample = old_h_over;
+ spc->v_oversample = old_v_over;
+
+ return return_value;
+}
+
+STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)
+{
+ stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);
+}
+
+STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
+{
+ stbtt_fontinfo info;
+ int i,j,n, return_value = 1;
+ //stbrp_context *context = (stbrp_context *) spc->pack_info;
+ stbrp_rect *rects;
+
+ // flag all characters as NOT packed
+ for (i=0; i < num_ranges; ++i)
+ for (j=0; j < ranges[i].num_chars; ++j)
+ ranges[i].chardata_for_range[j].x0 =
+ ranges[i].chardata_for_range[j].y0 =
+ ranges[i].chardata_for_range[j].x1 =
+ ranges[i].chardata_for_range[j].y1 = 0;
+
+ n = 0;
+ for (i=0; i < num_ranges; ++i)
+ n += ranges[i].num_chars;
+
+ rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
+ if (rects == NULL)
+ return 0;
+
+ info.userdata = spc->user_allocator_context;
+ stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
+
+ n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);
+
+ stbtt_PackFontRangesPackRects(spc, rects, n);
+
+ return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);
+
+ STBTT_free(rects, spc->user_allocator_context);
+ return return_value;
+}
+
+STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
+ int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)
+{
+ stbtt_pack_range range;
+ range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;
+ range.array_of_unicode_codepoints = NULL;
+ range.num_chars = num_chars_in_range;
+ range.chardata_for_range = chardata_for_range;
+ range.font_size = font_size;
+ return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
+}
+
+STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
+{
+ float ipw = 1.0f / pw, iph = 1.0f / ph;
+ stbtt_packedchar *b = chardata + char_index;
+
+ if (align_to_integer) {
+ float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);
+ float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);
+ q->x0 = x;
+ q->y0 = y;
+ q->x1 = x + b->xoff2 - b->xoff;
+ q->y1 = y + b->yoff2 - b->yoff;
+ } else {
+ q->x0 = *xpos + b->xoff;
+ q->y0 = *ypos + b->yoff;
+ q->x1 = *xpos + b->xoff2;
+ q->y1 = *ypos + b->yoff2;
+ }
+
+ q->s0 = b->x0 * ipw;
+ q->t0 = b->y0 * iph;
+ q->s1 = b->x1 * ipw;
+ q->t1 = b->y1 * iph;
+
+ *xpos += b->xadvance;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// font name matching -- recommended not to use this
+//
+
+// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
+static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2)
+{
+ stbtt_int32 i=0;
+
+ // convert utf16 to utf8 and compare the results while converting
+ while (len2) {
+ stbtt_uint16 ch = s2[0]*256 + s2[1];
+ if (ch < 0x80) {
+ if (i >= len1) return -1;
+ if (s1[i++] != ch) return -1;
+ } else if (ch < 0x800) {
+ if (i+1 >= len1) return -1;
+ if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
+ if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
+ } else if (ch >= 0xd800 && ch < 0xdc00) {
+ stbtt_uint32 c;
+ stbtt_uint16 ch2 = s2[2]*256 + s2[3];
+ if (i+3 >= len1) return -1;
+ c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
+ if (s1[i++] != 0xf0 + (c >> 18)) return -1;
+ if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
+ if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1;
+ if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1;
+ s2 += 2; // plus another 2 below
+ len2 -= 2;
+ } else if (ch >= 0xdc00 && ch < 0xe000) {
+ return -1;
+ } else {
+ if (i+2 >= len1) return -1;
+ if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
+ if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
+ if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1;
+ }
+ s2 += 2;
+ len2 -= 2;
+ }
+ return i;
+}
+
+STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
+{
+ return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2);
+}
+
+// returns results in whatever encoding you request... but note that 2-byte encodings
+// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
+STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
+{
+ stbtt_int32 i,count,stringOffset;
+ stbtt_uint8 *fc = font->data;
+ stbtt_uint32 offset = font->fontstart;
+ stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
+ if (!nm) return NULL;
+
+ count = ttUSHORT(fc+nm+2);
+ stringOffset = nm + ttUSHORT(fc+nm+4);
+ for (i=0; i < count; ++i) {
+ stbtt_uint32 loc = nm + 6 + 12 * i;
+ if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
+ && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
+ *length = ttUSHORT(fc+loc+8);
+ return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
+ }
+ }
+ return NULL;
+}
+
+static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
+{
+ stbtt_int32 i;
+ stbtt_int32 count = ttUSHORT(fc+nm+2);
+ stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
+
+ for (i=0; i < count; ++i) {
+ stbtt_uint32 loc = nm + 6 + 12 * i;
+ stbtt_int32 id = ttUSHORT(fc+loc+6);
+ if (id == target_id) {
+ // find the encoding
+ stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
+
+ // is this a Unicode encoding?
+ if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
+ stbtt_int32 slen = ttUSHORT(fc+loc+8);
+ stbtt_int32 off = ttUSHORT(fc+loc+10);
+
+ // check if there's a prefix match
+ stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
+ if (matchlen >= 0) {
+ // check for target_id+1 immediately following, with same encoding & language
+ if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
+ slen = ttUSHORT(fc+loc+12+8);
+ off = ttUSHORT(fc+loc+12+10);
+ if (slen == 0) {
+ if (matchlen == nlen)
+ return 1;
+ } else if (matchlen < nlen && name[matchlen] == ' ') {
+ ++matchlen;
+ if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
+ return 1;
+ }
+ } else {
+ // if nothing immediately following
+ if (matchlen == nlen)
+ return 1;
+ }
+ }
+ }
+
+ // @TODO handle other encodings
+ }
+ }
+ return 0;
+}
+
+static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
+{
+ stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);
+ stbtt_uint32 nm,hd;
+ if (!stbtt__isfont(fc+offset)) return 0;
+
+ // check italics/bold/underline flags in macStyle...
+ if (flags) {
+ hd = stbtt__find_table(fc, offset, "head");
+ if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
+ }
+
+ nm = stbtt__find_table(fc, offset, "name");
+ if (!nm) return 0;
+
+ if (flags) {
+ // if we checked the macStyle flags, then just check the family and ignore the subfamily
+ if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1;
+ if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1;
+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
+ } else {
+ if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1;
+ if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1;
+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
+ }
+
+ return 0;
+}
+
+STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags)
+{
+ stbtt_int32 i;
+ for (i=0;;++i) {
+ stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
+ if (off < 0) return off;
+ if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
+ return off;
+ }
+}
+
+#endif // STB_TRUETYPE_IMPLEMENTATION
+
+
+// FULL VERSION HISTORY
+//
+// 1.11 (2016-04-02) fix unused-variable warning
+// 1.10 (2016-04-02) allow user-defined fabs() replacement
+// fix memory leak if fontsize=0.0
+// fix warning from duplicate typedef
+// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges
+// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
+// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
+// allow PackFontRanges to pack and render in separate phases;
+// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
+// fixed an assert() bug in the new rasterizer
+// replace assert() with STBTT_assert() in new rasterizer
+// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)
+// also more precise AA rasterizer, except if shapes overlap
+// remove need for STBTT_sort
+// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
+// 1.04 (2015-04-15) typo in example
+// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
+// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++
+// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match
+// non-oversampled; STBTT_POINT_SIZE for packed case only
+// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling
+// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)
+// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID
+// 0.8b (2014-07-07) fix a warning
+// 0.8 (2014-05-25) fix a few more warnings
+// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back
+// 0.6c (2012-07-24) improve documentation
+// 0.6b (2012-07-20) fix a few more warnings
+// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,
+// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty
+// 0.5 (2011-12-09) bugfixes:
+// subpixel glyph renderer computed wrong bounding box
+// first vertex of shape can be off-curve (FreeSans)
+// 0.4b (2011-12-03) fixed an error in the font baking example
+// 0.4 (2011-12-01) kerning, subpixel rendering (tor)
+// bugfixes for:
+// codepoint-to-glyph conversion using table fmt=12
+// codepoint-to-glyph conversion using table fmt=4
+// stbtt_GetBakedQuad with non-square texture (Zer)
+// updated Hello World! sample to use kerning and subpixel
+// fixed some warnings
+// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM)
+// userdata, malloc-from-userdata, non-zero fill (stb)
+// 0.2 (2009-03-11) Fix unsigned/signed char warnings
+// 0.1 (2009-03-09) First public release
+//
diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp
index ab1c7ef66f..12ac0bd1ca 100644
--- a/servers/physics_2d/body_2d_sw.cpp
+++ b/servers/physics_2d/body_2d_sw.cpp
@@ -33,7 +33,7 @@
void Body2DSW::_update_inertia() {
- if (get_space() && !inertia_update_list.in_list())
+ if (!user_inertia && get_space() && !inertia_update_list.in_list())
get_space()->body_add_to_inertia_update_list(&inertia_update_list);
}
@@ -48,6 +48,8 @@ void Body2DSW::update_inertias() {
case Physics2DServer::BODY_MODE_RIGID: {
+ if(user_inertia) break;
+
//update tensor for allshapes, not the best way but should be somehow OK. (inspired from bullet)
float total_area=0;
@@ -157,6 +159,15 @@ void Body2DSW::set_param(Physics2DServer::BodyParameter p_param, float p_value)
_update_inertia();
} break;
+ case Physics2DServer::BODY_PARAM_INERTIA: {
+ if(p_value<=0) {
+ user_inertia = false;
+ _update_inertia();
+ } else {
+ user_inertia = true;
+ _inv_inertia = 1.0 / p_value;
+ }
+ } break;
case Physics2DServer::BODY_PARAM_GRAVITY_SCALE: {
gravity_scale=p_value;
} break;
@@ -186,6 +197,9 @@ float Body2DSW::get_param(Physics2DServer::BodyParameter p_param) const {
case Physics2DServer::BODY_PARAM_MASS: {
return mass;
} break;
+ case Physics2DServer::BODY_PARAM_INERTIA: {
+ return _inv_inertia==0 ? 0 : 1.0 / _inv_inertia;
+ } break;
case Physics2DServer::BODY_PARAM_GRAVITY_SCALE: {
return gravity_scale;
} break;
@@ -665,6 +679,7 @@ Body2DSW::Body2DSW() : CollisionObject2DSW(TYPE_BODY), active_list(this), inerti
angular_velocity=0;
biased_angular_velocity=0;
mass=1;
+ user_inertia=false;
_inv_inertia=0;
_inv_mass=1;
bounce=0;
diff --git a/servers/physics_2d/body_2d_sw.h b/servers/physics_2d/body_2d_sw.h
index b7f3ab01db..ed98017629 100644
--- a/servers/physics_2d/body_2d_sw.h
+++ b/servers/physics_2d/body_2d_sw.h
@@ -57,6 +57,7 @@ class Body2DSW : public CollisionObject2DSW {
real_t _inv_mass;
real_t _inv_inertia;
+ bool user_inertia;
Vector2 gravity;
real_t area_linear_damp;
@@ -204,10 +205,10 @@ public:
_FORCE_INLINE_ real_t get_biased_angular_velocity() const { return biased_angular_velocity; }
- _FORCE_INLINE_ void apply_impulse(const Vector2& p_pos, const Vector2& p_j) {
+ _FORCE_INLINE_ void apply_impulse(const Vector2& p_offset, const Vector2& p_impulse) {
- linear_velocity += p_j * _inv_mass;
- angular_velocity += _inv_inertia * p_pos.cross(p_j);
+ linear_velocity += p_impulse * _inv_mass;
+ angular_velocity += _inv_inertia * p_offset.cross(p_impulse);
}
_FORCE_INLINE_ void apply_bias_impulse(const Vector2& p_pos, const Vector2& p_j) {
@@ -243,6 +244,11 @@ public:
void set_applied_torque(real_t p_torque) { applied_torque=p_torque; }
real_t get_applied_torque() const { return applied_torque; }
+ _FORCE_INLINE_ void add_force(const Vector2& p_force, const Vector2& p_offset) {
+
+ applied_force += p_force;
+ applied_torque += p_offset.cross(p_force);
+ }
_FORCE_INLINE_ void set_continuous_collision_detection_mode(Physics2DServer::CCDMode p_mode) { continuous_cd_mode=p_mode; }
_FORCE_INLINE_ Physics2DServer::CCDMode get_continuous_collision_detection_mode() const { return continuous_cd_mode; }
diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp
index c571331498..3796ddd961 100644
--- a/servers/physics_2d/physics_2d_server_sw.cpp
+++ b/servers/physics_2d/physics_2d_server_sw.cpp
@@ -860,6 +860,15 @@ void Physics2DServerSW::body_apply_impulse(RID p_body, const Vector2& p_pos, con
body->wakeup();
};
+void Physics2DServerSW::body_add_force(RID p_body, const Vector2& p_offset, const Vector2& p_force) {
+
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->add_force(p_force,p_offset);
+ body->wakeup();
+};
+
void Physics2DServerSW::body_set_axis_velocity(RID p_body, const Vector2& p_axis_velocity) {
Body2DSW *body = body_owner.get(p_body);
diff --git a/servers/physics_2d/physics_2d_server_sw.h b/servers/physics_2d/physics_2d_server_sw.h
index cd4dfc1a8b..6415786803 100644
--- a/servers/physics_2d/physics_2d_server_sw.h
+++ b/servers/physics_2d/physics_2d_server_sw.h
@@ -205,6 +205,8 @@ public:
virtual void body_set_applied_torque(RID p_body, float p_torque);
virtual float body_get_applied_torque(RID p_body) const;
+ virtual void body_add_force(RID p_body, const Vector2& p_offset, const Vector2& p_force);
+
virtual void body_apply_impulse(RID p_body, const Vector2& p_pos, const Vector2& p_impulse);
virtual void body_set_axis_velocity(RID p_body, const Vector2& p_axis_velocity);
diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.h b/servers/physics_2d/physics_2d_server_wrap_mt.h
index 60f8a4c879..891c45addf 100644
--- a/servers/physics_2d/physics_2d_server_wrap_mt.h
+++ b/servers/physics_2d/physics_2d_server_wrap_mt.h
@@ -205,6 +205,7 @@ public:
FUNC2(body_set_applied_torque,RID,float);
FUNC1RC(float,body_get_applied_torque,RID);
+ FUNC3(body_add_force,RID,const Vector2&,const Vector2&);
FUNC3(body_apply_impulse,RID,const Vector2&,const Vector2&);
FUNC2(body_set_axis_velocity,RID,const Vector2&);
diff --git a/servers/physics_2d_server.cpp b/servers/physics_2d_server.cpp
index 2d267a5749..a85fefe5ad 100644
--- a/servers/physics_2d_server.cpp
+++ b/servers/physics_2d_server.cpp
@@ -597,6 +597,7 @@ void Physics2DServer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("body_get_state","body","state"),&Physics2DServer::body_get_state);
ObjectTypeDB::bind_method(_MD("body_apply_impulse","body","pos","impulse"),&Physics2DServer::body_apply_impulse);
+ ObjectTypeDB::bind_method(_MD("body_add_force","body","offset","force"),&Physics2DServer::body_add_force);
ObjectTypeDB::bind_method(_MD("body_set_axis_velocity","body","axis_velocity"),&Physics2DServer::body_set_axis_velocity);
ObjectTypeDB::bind_method(_MD("body_add_collision_exception","body","excepted_body"),&Physics2DServer::body_add_collision_exception);
@@ -677,6 +678,7 @@ void Physics2DServer::_bind_methods() {
BIND_CONSTANT( BODY_PARAM_BOUNCE );
BIND_CONSTANT( BODY_PARAM_FRICTION );
BIND_CONSTANT( BODY_PARAM_MASS );
+ BIND_CONSTANT( BODY_PARAM_INERTIA );
BIND_CONSTANT( BODY_PARAM_GRAVITY_SCALE );
BIND_CONSTANT( BODY_PARAM_LINEAR_DAMP);
BIND_CONSTANT( BODY_PARAM_ANGULAR_DAMP);
diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h
index 25875f16d3..dd04f0f6b8 100644
--- a/servers/physics_2d_server.h
+++ b/servers/physics_2d_server.h
@@ -421,6 +421,7 @@ public:
BODY_PARAM_BOUNCE,
BODY_PARAM_FRICTION,
BODY_PARAM_MASS, ///< unused for static, always infinite
+ BODY_PARAM_INERTIA, // read-only: computed from mass & shapes
BODY_PARAM_GRAVITY_SCALE,
BODY_PARAM_LINEAR_DAMP,
BODY_PARAM_ANGULAR_DAMP,
@@ -450,7 +451,9 @@ public:
virtual void body_set_applied_torque(RID p_body, float p_torque)=0;
virtual float body_get_applied_torque(RID p_body) const=0;
- virtual void body_apply_impulse(RID p_body, const Vector2& p_pos, const Vector2& p_impulse)=0;
+ virtual void body_add_force(RID p_body, const Vector2& p_offset, const Vector2& p_force)=0;
+
+ virtual void body_apply_impulse(RID p_body, const Vector2& p_offset, const Vector2& p_impulse)=0;
virtual void body_set_axis_velocity(RID p_body, const Vector2& p_axis_velocity)=0;
//fix
diff --git a/tools/editor/editor_dir_dialog.cpp b/tools/editor/editor_dir_dialog.cpp
index 8512154485..1a92a61882 100644
--- a/tools/editor/editor_dir_dialog.cpp
+++ b/tools/editor/editor_dir_dialog.cpp
@@ -189,6 +189,7 @@ void EditorDirDialog::_make_dir_confirm() {
} else {
reload();
}
+ makedirname->set_text(""); // reset label
}
diff --git a/tools/editor/editor_file_dialog.cpp b/tools/editor/editor_file_dialog.cpp
index 8e83726f96..a889c76a69 100644
--- a/tools/editor/editor_file_dialog.cpp
+++ b/tools/editor/editor_file_dialog.cpp
@@ -831,7 +831,6 @@ EditorFileDialog::Access EditorFileDialog::get_access() const{
void EditorFileDialog::_make_dir_confirm() {
-
Error err = dir_access->make_dir( makedirname->get_text() );
if (err==OK) {
dir_access->change_dir(makedirname->get_text());
@@ -843,6 +842,7 @@ void EditorFileDialog::_make_dir_confirm() {
} else {
mkdirerr->popup_centered_minsize(Size2(250,50));
}
+ makedirname->set_text(""); // reset label
}
diff --git a/tools/editor/editor_fonts.cpp b/tools/editor/editor_fonts.cpp
index b12b041f16..a3ec08f986 100644
--- a/tools/editor/editor_fonts.cpp
+++ b/tools/editor/editor_fonts.cpp
@@ -31,10 +31,10 @@
#include "doc_title_font.h"
#include "doc_code_font.h"
-static Ref<Font> make_font(int p_height,int p_ascent, int p_valign, int p_charcount, const int *p_chars,const Ref<Texture> &p_texture) {
+static Ref<BitmapFont> make_font(int p_height,int p_ascent, int p_valign, int p_charcount, const int *p_chars,const Ref<Texture> &p_texture) {
- Ref<Font> font( memnew( Font ) );
+ Ref<BitmapFont> font( memnew( BitmapFont ) );
font->add_texture( p_texture );
for (int i=0;i<p_charcount;i++) {
@@ -65,9 +65,9 @@ static Ref<Font> make_font(int p_height,int p_ascent, int p_valign, int p_charco
void editor_register_fonts(Ref<Theme> p_theme) {
- Ref<Font> doc_font = make_font(_bi_font_doc_font_height,_bi_font_doc_font_ascent,0,_bi_font_doc_font_charcount,_bi_font_doc_font_characters,p_theme->get_icon("DocFont","EditorIcons"));
- Ref<Font> doc_code_font = make_font(_bi_font_doc_code_font_height,_bi_font_doc_code_font_ascent,0,_bi_font_doc_code_font_charcount,_bi_font_doc_code_font_characters,p_theme->get_icon("DocCodeFont","EditorIcons"));
- Ref<Font> doc_title_font = make_font(_bi_font_doc_title_font_height,_bi_font_doc_title_font_ascent,0,_bi_font_doc_title_font_charcount,_bi_font_doc_title_font_characters,p_theme->get_icon("DocTitleFont","EditorIcons"));
+ Ref<BitmapFont> doc_font = make_font(_bi_font_doc_font_height,_bi_font_doc_font_ascent,0,_bi_font_doc_font_charcount,_bi_font_doc_font_characters,p_theme->get_icon("DocFont","EditorIcons"));
+ Ref<BitmapFont> doc_code_font = make_font(_bi_font_doc_code_font_height,_bi_font_doc_code_font_ascent,0,_bi_font_doc_code_font_charcount,_bi_font_doc_code_font_characters,p_theme->get_icon("DocCodeFont","EditorIcons"));
+ Ref<BitmapFont> doc_title_font = make_font(_bi_font_doc_title_font_height,_bi_font_doc_title_font_ascent,0,_bi_font_doc_title_font_charcount,_bi_font_doc_title_font_characters,p_theme->get_icon("DocTitleFont","EditorIcons"));
p_theme->set_font("doc","EditorFonts",doc_font);
p_theme->set_font("doc_code","EditorFonts",doc_code_font);
p_theme->set_font("doc_title","EditorFonts",doc_title_font);
diff --git a/tools/editor/editor_import_export.cpp b/tools/editor/editor_import_export.cpp
index e02ffd337b..bdf1726d85 100644
--- a/tools/editor/editor_import_export.cpp
+++ b/tools/editor/editor_import_export.cpp
@@ -294,6 +294,39 @@ static void _remove_filter_from_list(Set<StringName>& r_list,const String& p_fil
_edit_filter_list(r_list,p_filter,true);
}
+bool EditorExportPlatform::_set(const StringName& p_name, const Variant& p_value) {
+
+ String n = p_name;
+
+ if (n=="debug/debugging_enabled") {
+ set_debugging_enabled(p_value);
+ } else {
+ return false;
+ }
+
+ return true;
+
+}
+
+bool EditorExportPlatform::_get(const StringName& p_name,Variant &r_ret) const {
+
+ String n = p_name;
+
+ if (n=="debug/debugging_enabled") {
+ r_ret=is_debugging_enabled();
+ } else {
+ return false;
+ }
+
+ return true;
+
+}
+
+void EditorExportPlatform::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_front( PropertyInfo( Variant::BOOL, "debug/debugging_enabled"));
+}
+
Vector<uint8_t> EditorExportPlatform::get_exported_file_default(String& p_fname) const {
FileAccess *f = FileAccess::open(p_fname,FileAccess::READ);
@@ -481,8 +514,15 @@ bool EditorExportPlatform::exists_export_template(String template_file_name, Str
+bool EditorExportPlatform::is_debugging_enabled() const {
+
+ return debugging_enabled;
+}
+void EditorExportPlatform::set_debugging_enabled(bool p_enabled) {
+ debugging_enabled = p_enabled;
+}
bool EditorExportPlatformPC::_set(const StringName& p_name, const Variant& p_value) {
@@ -1260,6 +1300,11 @@ Error EditorExportPlatform::save_pack(FileAccess *dst,bool p_make_bundles, int p
return OK;
}
+EditorExportPlatform::EditorExportPlatform() {
+
+ debugging_enabled = true;
+}
+
Error EditorExportPlatformPC::export_project(const String& p_path, bool p_debug, int p_flags) {
diff --git a/tools/editor/editor_import_export.h b/tools/editor/editor_import_export.h
index fc8ad58bd9..c131488d18 100644
--- a/tools/editor/editor_import_export.h
+++ b/tools/editor/editor_import_export.h
@@ -86,8 +86,17 @@ class EditorExportPlatform : public Reference {
public:
typedef Error (*EditorExportSaveFunction)(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total);
+
+private:
+
+ bool debugging_enabled;
+
protected:
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
Vector<uint8_t> get_exported_file_default(String& p_fname) const;
virtual Vector<uint8_t> get_exported_file(String& p_fname) const;
virtual Vector<StringName> get_dependencies(bool p_bundles) const;
@@ -145,6 +154,8 @@ public:
EXPORT_VIEW_NAVIGATION=16,
};
+ bool is_debugging_enabled() const;
+ void set_debugging_enabled( bool p_enabled );
Error export_project_files(EditorExportSaveFunction p_func, void* p_udata,bool p_make_bundles);
@@ -164,11 +175,11 @@ public:
virtual bool can_export(String *r_error=NULL) const=0;
- virtual bool requieres_password(bool p_debug) const { return false; }
+ virtual bool requires_password(bool p_debug) const { return false; }
virtual String get_binary_extension() const=0;
virtual Error export_project(const String& p_path,bool p_debug,int p_flags=0)=0;
- EditorExportPlatform() {};
+ EditorExportPlatform();
};
class EditorExportPlatformPC : public EditorExportPlatform {
diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp
index 316485f42a..09f85b99f6 100644
--- a/tools/editor/editor_node.cpp
+++ b/tools/editor/editor_node.cpp
@@ -3554,6 +3554,7 @@ Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps,bo
load_errors->clear();
String lpath = Globals::get_singleton()->localize_path(p_scene);
+ print_line("LOCAL PATH: "+lpath+" from "+p_scene);
if (!lpath.begins_with("res://")) {
current_option=-1;
@@ -6011,11 +6012,6 @@ EditorNode::EditorNode() {
- file_export_check = memnew( CheckButton );
- file_export_check->set_text("Enable Debugging");
- file_export_check->set_pressed(true);
- file_export_check->connect("pressed",this,"_export_debug_toggled");
- file_export->get_vbox()->add_margin_child("Debug:",file_export_check);
file_export_password = memnew( LineEdit );
file_export_password->set_secret(true);
file_export_password->set_editable(false);
diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h
index e4939afd34..c3b7a817c5 100644
--- a/tools/editor/editor_node.h
+++ b/tools/editor/editor_node.h
@@ -299,7 +299,6 @@ private:
FileDialog *file_export;
FileDialog *file_export_lib;
FileDialog *file_script;
- CheckButton *file_export_check;
CheckButton *file_export_lib_merge;
LineEdit *file_export_password;
String current_path;
diff --git a/tools/editor/icons/icon_bitmap_font.png b/tools/editor/icons/icon_bitmap_font.png
new file mode 100644
index 0000000000..45de2b0f0a
--- /dev/null
+++ b/tools/editor/icons/icon_bitmap_font.png
Binary files differ
diff --git a/tools/editor/icons/icon_dynamic_font.png b/tools/editor/icons/icon_dynamic_font.png
new file mode 100644
index 0000000000..52b423f75b
--- /dev/null
+++ b/tools/editor/icons/icon_dynamic_font.png
Binary files differ
diff --git a/tools/editor/icons/icon_dynamic_font_data.png b/tools/editor/icons/icon_dynamic_font_data.png
new file mode 100644
index 0000000000..568349e4db
--- /dev/null
+++ b/tools/editor/icons/icon_dynamic_font_data.png
Binary files differ
diff --git a/tools/editor/io_plugins/editor_font_import_plugin.cpp b/tools/editor/io_plugins/editor_font_import_plugin.cpp
index ff126a8e8c..ec251c546e 100644
--- a/tools/editor/io_plugins/editor_font_import_plugin.cpp
+++ b/tools/editor/io_plugins/editor_font_import_plugin.cpp
@@ -254,7 +254,7 @@ public:
p_list->push_back(PropertyInfo(Variant::INT,"character_set/mode",PROPERTY_HINT_ENUM,"Ascii,Latin,Unicode,Custom,Custom&Latin"));
if (character_set>=CHARSET_CUSTOM)
- p_list->push_back(PropertyInfo(Variant::STRING,"character_set/custom",PROPERTY_HINT_FILE));
+ p_list->push_back(PropertyInfo(Variant::STRING,"character_set/custom",PROPERTY_HINT_GLOBAL_FILE));
int usage = PROPERTY_USAGE_DEFAULT;
@@ -290,7 +290,7 @@ public:
p_list->push_back(PropertyInfo(Variant::COLOR,"color/end",PROPERTY_HINT_NONE,"",usage));
}
if (color_type==COLOR_GRADIENT_IMAGE) {
- p_list->push_back(PropertyInfo(Variant::STRING,"color/image",PROPERTY_HINT_FILE,"",usage));
+ p_list->push_back(PropertyInfo(Variant::STRING,"color/image",PROPERTY_HINT_GLOBAL_FILE,"",usage));
}
p_list->push_back(PropertyInfo(Variant::BOOL,"color/monochrome",PROPERTY_HINT_NONE,"",usage));
}
@@ -437,7 +437,7 @@ class EditorFontImportDialog : public ConfirmationDialog {
void _update() {
Ref<ResourceImportMetadata> imd = get_rimd();
- Ref<Font> font = plugin->generate_font(imd);
+ Ref<BitmapFont> font = plugin->generate_font(imd);
test_label->add_font_override("font",font);
_update_text();
}
@@ -454,7 +454,7 @@ class EditorFontImportDialog : public ConfirmationDialog {
void _import_inc(String p_font) {
- Ref<Font> font = ResourceLoader::load(p_font);
+ Ref<BitmapFont> font = ResourceLoader::load(p_font);
if (!font.is_valid())
return;
Ref<ImageTexture> tex = font->get_texture(0);
@@ -471,12 +471,12 @@ class EditorFontImportDialog : public ConfirmationDialog {
for(int i=0;i<ck.size();i++) {
CharType k=ck[i];
- Font::Character c=font->get_character(k);
+ BitmapFont::Character c=font->get_character(k);
f->store_line("{"+itos(k)+","+rtos(c.rect.pos.x)+","+rtos(c.rect.pos.y)+","+rtos(c.rect.size.x)+","+rtos(c.rect.size.y)+","+rtos(c.v_align)+","+rtos(c.h_align)+","+rtos(c.advance)+"},");
}
f->store_line("};");
- Vector<Font::KerningPairKey> kp=font->get_kerning_pair_keys();
+ Vector<BitmapFont::KerningPairKey> kp=font->get_kerning_pair_keys();
f->store_line("static const int _builtin_font_kerning_pair_count="+itos(kp.size())+";");
f->store_line("static const int _builtin_font_kerning_pairs["+itos(kp.size())+"][3]={");
for(int i=0;i<kp.size();i++) {
@@ -634,7 +634,7 @@ public:
dest = memnew( EditorLineEditFileChooser );
//
List<String> fl;
- Ref<Font> font= memnew(Font);
+ Ref<BitmapFont> font= memnew(BitmapFont);
dest->get_file_dialog()->add_filter("*.fnt ; Font" );
//ResourceSaver::get_recognized_extensions(font,&fl);
//for(List<String>::Element *E=fl.front();E;E=E->next()) {
@@ -875,12 +875,12 @@ static unsigned char get_SDF_radial(
}
-Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata>& p_from, const String &p_existing) {
+Ref<BitmapFont> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata>& p_from, const String &p_existing) {
Ref<ResourceImportMetadata> from = p_from;
- ERR_FAIL_COND_V(from->get_source_count()!=1,Ref<Font>());
+ ERR_FAIL_COND_V(from->get_source_count()!=1,Ref<BitmapFont>());
String src_path = EditorImportPlugin::expand_source_path(from->get_source_path(0));
@@ -888,15 +888,15 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
if (ResourceLoader::load(src_path).is_valid()) {
EditorNode::get_singleton()->show_warning("Path: "+src_path+"\nIs a Godot font file, please supply a BMFont type file instead.");
- return Ref<Font>();
+ return Ref<BitmapFont>();
}
- Ref<Font> font;
+ Ref<BitmapFont> font;
font.instance();
Error err = font->create_from_fnt(src_path);
if (err) {
EditorNode::get_singleton()->show_warning("Path: "+src_path+"\nFailed opening as BMFont file.");
- return Ref<Font>();
+ return Ref<BitmapFont>();
}
return font;
@@ -913,7 +913,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
int error = FT_Init_FreeType( &library );
ERR_EXPLAIN("Error initializing FreeType.");
- ERR_FAIL_COND_V( error !=0, Ref<Font>() );
+ ERR_FAIL_COND_V( error !=0, Ref<BitmapFont>() );
print_line("loadfrom: "+src_path);
error = FT_New_Face( library, src_path.utf8().get_data(),0,&face );
@@ -928,7 +928,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
}
- ERR_FAIL_COND_V(error,Ref<Font>());
+ ERR_FAIL_COND_V(error,Ref<BitmapFont>());
int height=0;
@@ -940,7 +940,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
if ( error ) {
FT_Done_FreeType( library );
ERR_EXPLAIN("Invalid font size. ");
- ERR_FAIL_COND_V( error,Ref<Font>() );
+ ERR_FAIL_COND_V( error,Ref<BitmapFont>() );
}
@@ -987,7 +987,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
FT_Done_FreeType( library );
ERR_EXPLAIN("Invalid font custom source. ");
- ERR_FAIL_COND_V( !fa,Ref<Font>() );
+ ERR_FAIL_COND_V( !fa,Ref<BitmapFont>() );
}
@@ -1546,15 +1546,15 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
int bottom_space = from->get_option("extra_space/bottom");
bool disable_filter = from->get_option("advanced/disable_filter");
- Ref<Font> font;
+ Ref<BitmapFont> font;
if (p_existing!=String() && ResourceCache::has(p_existing)) {
- font = Ref<Font>( ResourceCache::get(p_existing)->cast_to<Font>());
+ font = Ref<BitmapFont>( ResourceCache::get(p_existing)->cast_to<BitmapFont>());
}
if (font.is_null()) {
- font = Ref<Font>( memnew( Font ) );
+ font = Ref<BitmapFont>( memnew( BitmapFont ) );
}
font->clear();
@@ -1596,7 +1596,7 @@ Ref<Font> EditorFontImportPlugin::generate_font(const Ref<ResourceImportMetadata
return font;
#else
- return Ref<Font>();
+ return Ref<BitmapFont>();
#endif
}
@@ -1616,7 +1616,7 @@ void EditorFontImportPlugin::import_dialog(const String& p_from){
Error EditorFontImportPlugin::import(const String& p_path, const Ref<ResourceImportMetadata>& p_from){
- Ref<Font> font = EditorFontImportPlugin::generate_font(p_from,p_path);
+ Ref<BitmapFont> font = EditorFontImportPlugin::generate_font(p_from,p_path);
if (!font.is_valid())
return ERR_CANT_CREATE;
diff --git a/tools/editor/io_plugins/editor_font_import_plugin.h b/tools/editor/io_plugins/editor_font_import_plugin.h
index 814897c5f0..ce26ef91e2 100644
--- a/tools/editor/io_plugins/editor_font_import_plugin.h
+++ b/tools/editor/io_plugins/editor_font_import_plugin.h
@@ -42,7 +42,7 @@ class EditorFontImportPlugin : public EditorImportPlugin {
EditorFontImportDialog *dialog;
public:
- Ref<Font> generate_font(const Ref<ResourceImportMetadata>& p_from,const String& p_existing=String()); //used by editor
+ Ref<BitmapFont> generate_font(const Ref<ResourceImportMetadata>& p_from,const String& p_existing=String()); //used by editor
virtual String get_name() const;
virtual String get_visible_name() const;
diff --git a/tools/editor/plugins/animation_player_editor_plugin.cpp b/tools/editor/plugins/animation_player_editor_plugin.cpp
index 67f730f300..da7c083c5a 100644
--- a/tools/editor/plugins/animation_player_editor_plugin.cpp
+++ b/tools/editor/plugins/animation_player_editor_plugin.cpp
@@ -616,6 +616,9 @@ void AnimationPlayerEditor::_blend_edited() {
void AnimationPlayerEditor::ensure_visibility() {
+ if (player && pin->is_pressed())
+ return; // another player is pinned, don't reset
+
_animation_edit();
}
diff --git a/tools/editor/plugins/animation_tree_editor_plugin.cpp b/tools/editor/plugins/animation_tree_editor_plugin.cpp
index 08c2a1c3ae..7e1214ec83 100644
--- a/tools/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/tools/editor/plugins/animation_tree_editor_plugin.cpp
@@ -239,8 +239,12 @@ void AnimationTreeEditor::_play_toggled() {
void AnimationTreeEditor::_master_anim_menu_item(int p_item) {
- String str = master_anim_popup->get_item_text(p_item);
- anim_tree->animation_node_set_master_animation(edited_node,str);
+ if(p_item == 0) _edit_filters();
+ else {
+
+ String str = master_anim_popup->get_item_text(p_item);
+ anim_tree->animation_node_set_master_animation(edited_node,str);
+ }
update();
}
@@ -291,6 +295,8 @@ void AnimationTreeEditor::_popup_edit_dialog() {
AnimationPlayer *ap = anim_tree->get_node(anim_tree->get_master_player())->cast_to<AnimationPlayer>();
master_anim_popup->clear();
+ master_anim_popup->add_item("Edit Filters");
+ master_anim_popup->add_separator();
List<StringName> sn;
ap->get_animation_list(&sn);
sn.sort_custom<StringName::AlphCompare>();
@@ -652,39 +658,35 @@ AnimationTreeEditor::ClickType AnimationTreeEditor::_locate_click(const Point2&
float y = pos.y-style->get_offset().height;
- if (y<h)
- return CLICK_NODE;
- y-=h;
-
- if (y<h)
+ if (y<2*h)
return CLICK_NODE;
+ y-=2*h;
- y-=h;
-
- int count=0; // title and name
int inputs = anim_tree->node_get_input_count(node);
- count += inputs?inputs:1;
+ int count = MAX(inputs,1);
- for(int i=0;i<count;i++) {
+ if (inputs==0 || (pos.x > size.width/2 && type != AnimationTreePlayer::NODE_OUTPUT)) {
- if (y<h) {
+ if (y<count*h) {
- if (inputs==0 || ( type!=AnimationTreePlayer::NODE_OUTPUT && pos.x > size.width/2)) {
+ if (p_slot_index)
+ *p_slot_index=0;
+ return CLICK_OUTPUT_SLOT;
+ }
+ }
- if (p_slot_index)
- *p_slot_index=0;
- return CLICK_OUTPUT_SLOT;
- } else {
+ for(int i=0;i<count;i++) {
- if (p_slot_index)
- *p_slot_index=i;
- return CLICK_INPUT_SLOT;
- }
+ if (y<h) {
+ if (p_slot_index)
+ *p_slot_index=i;
+ return CLICK_INPUT_SLOT;
}
y-=h;
}
- return (type!=AnimationTreePlayer::NODE_OUTPUT && type!=AnimationTreePlayer::NODE_TIMESEEK)?CLICK_PARAMETER:CLICK_NODE;
+ bool has_parameters = type!=AnimationTreePlayer::NODE_OUTPUT && type!=AnimationTreePlayer::NODE_TIMESEEK;
+ return has_parameters ? CLICK_PARAMETER : CLICK_NODE;
}
return CLICK_NONE;
@@ -1243,6 +1245,8 @@ void AnimationTreeEditor::_filter_edited() {
anim_tree->oneshot_node_set_filter_path(edited_node,ed->get_metadata(0),ed->is_checked(0));
} else if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_BLEND2) {
anim_tree->blend2_node_set_filter_path(edited_node,ed->get_metadata(0),ed->is_checked(0));
+ } else if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_ANIMATION) {
+ anim_tree->animation_node_set_filter_path(edited_node,ed->get_metadata(0),ed->is_checked(0));
}
}
@@ -1310,6 +1314,8 @@ void AnimationTreeEditor::_edit_filters() {
it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node,E->get()));
} else if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_BLEND2) {
it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node,E->get()));
+ } else if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_ANIMATION) {
+ it->set_checked(0, anim_tree->animation_node_is_path_filtered(edited_node,E->get()));
}
pm[E->get()]=it;
}
diff --git a/tools/editor/plugins/canvas_item_editor_plugin.cpp b/tools/editor/plugins/canvas_item_editor_plugin.cpp
index 91c26d9d59..31f50b65f5 100644
--- a/tools/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/tools/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1578,8 +1578,21 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
}
+ bool uniform = m.mod.shift;
+ bool symmetric=m.mod.alt;
+
+ dto = dto - (drag == DRAG_ALL ? drag_from - drag_point_from : Vector2(0, 0));
+
+ if(uniform && drag == DRAG_ALL) {
+ if(ABS(dto.x - drag_point_from.x) > ABS(dto.y - drag_point_from.y)) {
+ dto.y = drag_point_from.y;
+ } else {
+ dto.x = drag_point_from.x;
+ }
+ }
+
dfrom = drag_point_from;
- dto = snap_point(dto - (drag == DRAG_ALL ? drag_from - drag_point_from : Vector2(0, 0)), drag_point_from);
+ dto = snap_point(dto, drag_point_from);
Vector2 drag_vector =
canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) -
@@ -1589,8 +1602,6 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
Vector2 begin=local_rect.pos;
Vector2 end=local_rect.pos+local_rect.size;
Vector2 minsize = canvas_item->edit_get_minimum_size();
- bool uniform = m.mod.shift;
- bool symmetric=m.mod.alt;
if (uniform) {
float aspect = local_rect.size.get_aspect();
diff --git a/tools/editor/plugins/shader_editor_plugin.cpp b/tools/editor/plugins/shader_editor_plugin.cpp
index 2d3100ac0d..d1e892a513 100644
--- a/tools/editor/plugins/shader_editor_plugin.cpp
+++ b/tools/editor/plugins/shader_editor_plugin.cpp
@@ -78,15 +78,19 @@ void ShaderTextEditor::_load_theme_settings() {
get_text_edit()->set_custom_bg_color(EDITOR_DEF("text_editor/background_color",Color(0,0,0,0)));
get_text_edit()->add_color_override("font_color",EDITOR_DEF("text_editor/text_color",Color(0,0,0)));
+ get_text_edit()->add_color_override("line_number_color",EDITOR_DEF("text_editor/line_number_color",Color(0,0,0)));
+ get_text_edit()->add_color_override("caret_color",EDITOR_DEF("text_editor/caret_color",Color(0,0,0)));
get_text_edit()->add_color_override("font_selected_color",EDITOR_DEF("text_editor/text_selected_color",Color(1,1,1)));
get_text_edit()->add_color_override("selection_color",EDITOR_DEF("text_editor/selection_color",Color(0.2,0.2,1)));
get_text_edit()->add_color_override("brace_mismatch_color",EDITOR_DEF("text_editor/brace_mismatch_color",Color(1,0.2,0.2)));
get_text_edit()->add_color_override("current_line_color",EDITOR_DEF("text_editor/current_line_color",Color(0.3,0.5,0.8,0.15)));
+ get_text_edit()->add_color_override("word_highlighted_color",EDITOR_DEF("text_editor/word_highlighted_color",Color(0.8,0.9,0.9,0.15)));
+ get_text_edit()->add_color_override("number_color",EDITOR_DEF("text_editor/number_color",Color(0.9,0.6,0.0,2)));
+ get_text_edit()->add_color_override("function_color",EDITOR_DEF("text_editor/function_color",Color(0.4,0.6,0.8)));
+ get_text_edit()->add_color_override("member_variable_color",EDITOR_DEF("text_editor/member_variable_color",Color(0.9,0.3,0.3)));
Color keyword_color= EDITOR_DEF("text_editor/keyword_color",Color(0.5,0.0,0.2));
- get_text_edit()->set_syntax_coloring(true);
-
List<String> keywords;
ShaderLanguage::get_keyword_list(type,&keywords);
@@ -355,9 +359,36 @@ void ShaderEditor::_params_changed() {
light_editor->_validate_script();
}
+void ShaderEditor::_editor_settings_changed() {
+
+ vertex_editor->get_text_edit()->set_auto_brace_completion(EditorSettings::get_singleton()->get("text_editor/auto_brace_complete"));
+ vertex_editor->get_text_edit()->set_scroll_pass_end_of_file(EditorSettings::get_singleton()->get("text_editor/scroll_past_end_of_file"));
+ vertex_editor->get_text_edit()->set_tab_size(EditorSettings::get_singleton()->get("text_editor/tab_size"));
+ vertex_editor->get_text_edit()->set_draw_tabs(EditorSettings::get_singleton()->get("text_editor/draw_tabs"));
+ vertex_editor->get_text_edit()->set_show_line_numbers(EditorSettings::get_singleton()->get("text_editor/show_line_numbers"));
+ vertex_editor->get_text_edit()->set_syntax_coloring(EditorSettings::get_singleton()->get("text_editor/syntax_highlighting"));
+ vertex_editor->get_text_edit()->set_highlight_all_occurrences(EditorSettings::get_singleton()->get("text_editor/highlight_all_occurrences"));
+
+ fragment_editor->get_text_edit()->set_auto_brace_completion(EditorSettings::get_singleton()->get("text_editor/auto_brace_complete"));
+ fragment_editor->get_text_edit()->set_scroll_pass_end_of_file(EditorSettings::get_singleton()->get("text_editor/scroll_past_end_of_file"));
+ fragment_editor->get_text_edit()->set_tab_size(EditorSettings::get_singleton()->get("text_editor/tab_size"));
+ fragment_editor->get_text_edit()->set_draw_tabs(EditorSettings::get_singleton()->get("text_editor/draw_tabs"));
+ fragment_editor->get_text_edit()->set_show_line_numbers(EditorSettings::get_singleton()->get("text_editor/show_line_numbers"));
+ fragment_editor->get_text_edit()->set_syntax_coloring(EditorSettings::get_singleton()->get("text_editor/syntax_highlighting"));
+ fragment_editor->get_text_edit()->set_highlight_all_occurrences(EditorSettings::get_singleton()->get("text_editor/highlight_all_occurrences"));
+
+ light_editor->get_text_edit()->set_auto_brace_completion(EditorSettings::get_singleton()->get("text_editor/auto_brace_complete"));
+ light_editor->get_text_edit()->set_scroll_pass_end_of_file(EditorSettings::get_singleton()->get("text_editor/scroll_past_end_of_file"));
+ light_editor->get_text_edit()->set_tab_size(EditorSettings::get_singleton()->get("text_editor/tab_size"));
+ light_editor->get_text_edit()->set_draw_tabs(EditorSettings::get_singleton()->get("text_editor/draw_tabs"));
+ light_editor->get_text_edit()->set_show_line_numbers(EditorSettings::get_singleton()->get("text_editor/show_line_numbers"));
+ light_editor->get_text_edit()->set_syntax_coloring(EditorSettings::get_singleton()->get("text_editor/syntax_highlighting"));
+ light_editor->get_text_edit()->set_highlight_all_occurrences(EditorSettings::get_singleton()->get("text_editor/highlight_all_occurrences"));
+}
void ShaderEditor::_bind_methods() {
+ ObjectTypeDB::bind_method("_editor_settings_changed",&ShaderEditor::_editor_settings_changed);
ObjectTypeDB::bind_method("_tab_changed",&ShaderEditor::_tab_changed);
ObjectTypeDB::bind_method("_menu_option",&ShaderEditor::_menu_option);
ObjectTypeDB::bind_method("_params_changed",&ShaderEditor::_params_changed);
@@ -506,6 +537,9 @@ ShaderEditor::ShaderEditor() {
vertex_editor->connect("script_changed", this,"apply_shaders");
fragment_editor->connect("script_changed", this,"apply_shaders");
light_editor->connect("script_changed", this,"apply_shaders");
+ EditorSettings::get_singleton()->connect("settings_changed",this,"_editor_settings_changed");
+
+ _editor_settings_changed();
}
diff --git a/tools/editor/plugins/shader_editor_plugin.h b/tools/editor/plugins/shader_editor_plugin.h
index 26d20b80b4..e10c10a446 100644
--- a/tools/editor/plugins/shader_editor_plugin.h
+++ b/tools/editor/plugins/shader_editor_plugin.h
@@ -105,7 +105,7 @@ class ShaderEditor : public Control {
void _close_callback();
-
+ void _editor_settings_changed();
protected:
void _notification(int p_what);
diff --git a/tools/editor/project_export.cpp b/tools/editor/project_export.cpp
index df8ebad08d..199d7646d4 100644
--- a/tools/editor/project_export.cpp
+++ b/tools/editor/project_export.cpp
@@ -481,7 +481,8 @@ void ProjectExportDialog::_export_action(const String& p_file) {
return;
String platform = selected->get_metadata(0);
- Error err = export_platform(platform,p_file,file_export_check->is_pressed(),file_export_password->get_text(),false);
+ bool debugging_enabled = EditorImportExport::get_singleton()->get_export_platform(platform)->is_debugging_enabled();
+ Error err = export_platform(platform,p_file,debugging_enabled,file_export_password->get_text(),false);
if (err!=OK) {
error->set_text("Error exporting project!");
error->popup_centered_minsize();
@@ -592,7 +593,7 @@ void ProjectExportDialog::custom_action(const String&) {
String extension = exporter->get_binary_extension();
- file_export_password->set_editable( exporter->requieres_password(file_export_check->is_pressed()));
+ file_export_password->set_editable( exporter->requires_password(exporter->is_debugging_enabled()) );
file_export->clear_filters();
if (extension!="") {
@@ -1453,12 +1454,6 @@ ProjectExportDialog::ProjectExportDialog(EditorNode *p_editor) {
file_export->set_title("Export Project");
file_export->connect("file_selected", this,"_export_action");
- file_export_check = memnew( CheckButton );
- file_export_check->set_text("Enable Debugging");
- file_export_check->set_pressed(true);
- file_export_check->connect("pressed",this,"_export_debug_toggled");
- file_export->get_vbox()->add_margin_child("Debug:",file_export_check);
-
file_export_password = memnew( LineEdit );
file_export_password->set_secret(true);
file_export_password->set_editable(false);
diff --git a/tools/editor/project_export.h b/tools/editor/project_export.h
index dc076ce201..8cf2bf3afc 100644
--- a/tools/editor/project_export.h
+++ b/tools/editor/project_export.h
@@ -86,7 +86,6 @@ private:
EditorFileDialog *pck_export;
EditorFileDialog *file_export;
- CheckButton *file_export_check;
LineEdit *file_export_password;
Button *button_export;
diff --git a/tools/editor/project_manager.cpp b/tools/editor/project_manager.cpp
index 579eb8c7c7..4386717c4c 100644
--- a/tools/editor/project_manager.cpp
+++ b/tools/editor/project_manager.cpp
@@ -539,7 +539,7 @@ void ProjectManager::_load_recent_projects() {
String project_name="Unnamed Project";
if (cf->has_section_key("application","name")) {
- project_name = cf->get_value("application","name");
+ project_name = static_cast<String>(cf->get_value("application","name")).xml_unescape();
}
if (filter_option==ProjectListFilter::FILTER_NAME && search_term!="" && project_name.findn(search_term)==-1)
diff --git a/tools/editor/project_settings.cpp b/tools/editor/project_settings.cpp
index 34bc31d20c..179af051a9 100644
--- a/tools/editor/project_settings.cpp
+++ b/tools/editor/project_settings.cpp
@@ -56,6 +56,20 @@ static const char* _button_names[JOY_BUTTON_MAX]={
"D-Pad Right"
};
+static const char* _axis_names[JOY_AXIS_MAX*2] = {
+ " (Left Stick Left)",
+ " (Left Stick Right)",
+ " (Left Stick Up)",
+ " (Left Stick Down)",
+ " (Right Stick Left)",
+ " (Right Stick Right)",
+ " (Right Stick Up)",
+ " (Right Stick Down)",
+ "","","","",
+ "", " (L2)",
+ "", " (R2)"
+};
+
void ProjectSettings::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
@@ -342,19 +356,7 @@ void ProjectSettings::_add_item(int p_item){
device_index->clear();
for(int i=0;i<JOY_AXIS_MAX*2;i++) {
- String desc;
-
- int ax=i/2;
- if (ax==0 || ax==1)
- desc=" (Left Stick)";
- else if (ax==2 || ax==3)
- desc=" (Right Stick)";
- else if (ax==6)
- desc=" (L2)";
- else if (ax==7)
- desc=" (R2)";
-
-
+ String desc = _axis_names[i];
device_index->add_item("Axis "+itos(i/2)+" "+(i&1?"+":"-")+desc);
}
device_input->popup_centered(Size2(350,95));
@@ -541,18 +543,10 @@ void ProjectSettings::_update_actions() {
} break;
case InputEvent::JOYSTICK_MOTION: {
- String desc;
int ax = ie.joy_motion.axis;
-
- if (ax==0 || ax==1)
- desc=" (Left Stick).";
- else if (ax==2 || ax==3)
- desc=" (Right Stick).";
- else if (ax==6)
- desc=" (L2).";
- else if (ax==7)
- desc=" (R2).";
- String str = "Device "+itos(ie.device)+", Axis "+itos(ie.joy_motion.axis)+" "+(ie.joy_motion.axis_value<0?"-":"+")+desc;
+ int n = 2*ax + (ie.joy_motion.axis_value<0 ? 0:1);
+ String desc = _axis_names[n];
+ String str = "Device "+itos(ie.device)+", Axis "+itos(ax)+" "+(ie.joy_motion.axis_value<0?"-":"+")+desc +".";
action->set_text(0,str);
action->set_icon(0,get_icon("JoyAxis","EditorIcons"));
} break;
diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp
index 1340670db1..110f5bb7f5 100644
--- a/tools/editor/property_editor.cpp
+++ b/tools/editor/property_editor.cpp
@@ -1313,7 +1313,10 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::REAL: {
if (hint!=PROPERTY_HINT_EXP_EASING) {
- v=value_editor[0]->get_text().to_double();
+ if (evaluator)
+ evaluator->eval(value_editor[0]->get_text());
+ else
+ v=value_editor[0]->get_text().to_double();
emit_signal("variant_changed");
}
@@ -1327,8 +1330,13 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::VECTOR2: {
Vector2 vec;
- vec.x=value_editor[0]->get_text().to_double();
- vec.y=value_editor[1]->get_text().to_double();
+ if (evaluator) {
+ vec.x=evaluator->eval(value_editor[0]->get_text());
+ vec.y=evaluator->eval(value_editor[1]->get_text());
+ } else {
+ vec.x=value_editor[0]->get_text().to_double();
+ vec.y=value_editor[1]->get_text().to_double();
+ }
v=vec;
emit_signal("variant_changed");
@@ -1336,10 +1344,17 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::RECT2: {
Rect2 r2;
- r2.pos.x=value_editor[0]->get_text().to_double();
- r2.pos.y=value_editor[1]->get_text().to_double();
- r2.size.x=value_editor[2]->get_text().to_double();
- r2.size.y=value_editor[3]->get_text().to_double();
+ if (evaluator) {
+ r2.pos.x=evaluator->eval(value_editor[0]->get_text());
+ r2.pos.y=evaluator->eval(value_editor[1]->get_text());
+ r2.size.x=evaluator->eval(value_editor[2]->get_text());
+ r2.size.y=evaluator->eval(value_editor[3]->get_text());
+ } else {
+ r2.pos.x=value_editor[0]->get_text().to_double();
+ r2.pos.y=value_editor[1]->get_text().to_double();
+ r2.size.x=value_editor[2]->get_text().to_double();
+ r2.size.y=value_editor[3]->get_text().to_double();
+ }
v=r2;
emit_signal("variant_changed");
@@ -1348,9 +1363,15 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::VECTOR3: {
Vector3 vec;
- vec.x=value_editor[0]->get_text().to_double();
- vec.y=value_editor[1]->get_text().to_double();
- vec.z=value_editor[2]->get_text().to_double();
+ if (evaluator) {
+ vec.x=evaluator->eval(value_editor[0]->get_text());
+ vec.y=evaluator->eval(value_editor[1]->get_text());
+ vec.z=evaluator->eval(value_editor[2]->get_text());
+ } else {
+ vec.x=value_editor[0]->get_text().to_double();
+ vec.y=value_editor[1]->get_text().to_double();
+ vec.z=value_editor[2]->get_text().to_double();
+ }
v=vec;
emit_signal("variant_changed");
@@ -1358,10 +1379,17 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::PLANE: {
Plane pl;
- pl.normal.x=value_editor[0]->get_text().to_double();
- pl.normal.y=value_editor[1]->get_text().to_double();
- pl.normal.z=value_editor[2]->get_text().to_double();
- pl.d=value_editor[3]->get_text().to_double();
+ if (evaluator) {
+ pl.normal.x=evaluator->eval(value_editor[0]->get_text());
+ pl.normal.y=evaluator->eval(value_editor[1]->get_text());
+ pl.normal.z=evaluator->eval(value_editor[2]->get_text());
+ pl.d=evaluator->eval(value_editor[3]->get_text());
+ } else {
+ pl.normal.x=value_editor[0]->get_text().to_double();
+ pl.normal.y=value_editor[1]->get_text().to_double();
+ pl.normal.z=value_editor[2]->get_text().to_double();
+ pl.d=value_editor[3]->get_text().to_double();
+ }
v=pl;
emit_signal("variant_changed");
@@ -1369,10 +1397,17 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::QUAT: {
Quat q;
- q.x=value_editor[0]->get_text().to_double();
- q.y=value_editor[1]->get_text().to_double();
- q.z=value_editor[2]->get_text().to_double();
- q.w=value_editor[3]->get_text().to_double();
+ if (evaluator) {
+ q.x=evaluator->eval(value_editor[0]->get_text());
+ q.y=evaluator->eval(value_editor[1]->get_text());
+ q.z=evaluator->eval(value_editor[2]->get_text());
+ q.w=evaluator->eval(value_editor[3]->get_text());
+ } else {
+ q.x=value_editor[0]->get_text().to_double();
+ q.y=value_editor[1]->get_text().to_double();
+ q.z=value_editor[2]->get_text().to_double();
+ q.w=value_editor[3]->get_text().to_double();
+ }
v=q;
emit_signal("variant_changed");
@@ -1380,14 +1415,23 @@ void CustomPropertyEditor::_modified(String p_string) {
case Variant::_AABB: {
Vector3 pos;
- pos.x=value_editor[0]->get_text().to_double();
- pos.y=value_editor[1]->get_text().to_double();
- pos.z=value_editor[2]->get_text().to_double();
Vector3 size;
- size.x=value_editor[3]->get_text().to_double();
- size.y=value_editor[4]->get_text().to_double();
- size.z=value_editor[5]->get_text().to_double();
+ if (evaluator) {
+ pos.x=evaluator->eval(value_editor[0]->get_text());
+ pos.y=evaluator->eval(value_editor[1]->get_text());
+ pos.z=evaluator->eval(value_editor[2]->get_text());
+ size.x=evaluator->eval(value_editor[3]->get_text());
+ size.y=evaluator->eval(value_editor[4]->get_text());
+ size.z=evaluator->eval(value_editor[5]->get_text());
+ } else {
+ pos.x=value_editor[0]->get_text().to_double();
+ pos.y=value_editor[1]->get_text().to_double();
+ pos.z=value_editor[2]->get_text().to_double();
+ size.x=value_editor[3]->get_text().to_double();
+ size.y=value_editor[4]->get_text().to_double();
+ size.z=value_editor[5]->get_text().to_double();
+ }
v=AABB(pos,size);
emit_signal("variant_changed");
@@ -1396,8 +1440,11 @@ void CustomPropertyEditor::_modified(String p_string) {
Matrix32 m;
for(int i=0;i<6;i++) {
-
- m.elements[i/2][i%2]=value_editor[i]->get_text().to_double();
+ if (evaluator) {
+ m.elements[i/2][i%2]=evaluator->eval(value_editor[i]->get_text());
+ } else {
+ m.elements[i/2][i%2]=value_editor[i]->get_text().to_double();
+ }
}
v=m;
@@ -1409,7 +1456,11 @@ void CustomPropertyEditor::_modified(String p_string) {
Matrix3 m;
for(int i=0;i<9;i++) {
- m.elements[i/3][i%3]=value_editor[i]->get_text().to_double();
+ if (evaluator) {
+ m.elements[i/3][i%3]=evaluator->eval(value_editor[i]->get_text());
+ } else {
+ m.elements[i/3][i%3]=value_editor[i]->get_text().to_double();
+ }
}
v=m;
@@ -1421,13 +1472,24 @@ void CustomPropertyEditor::_modified(String p_string) {
Matrix3 basis;
for(int i=0;i<9;i++) {
- basis.elements[i/3][i%3]=value_editor[(i/3)*4+i%3]->get_text().to_double();
+ if (evaluator) {
+ basis.elements[i/3][i%3]=evaluator->eval(value_editor[(i/3)*4+i%3]->get_text());
+ } else {
+ basis.elements[i/3][i%3]=value_editor[(i/3)*4+i%3]->get_text().to_double();
+ }
}
Vector3 origin;
- origin.x=value_editor[3]->get_text().to_double();
- origin.y=value_editor[7]->get_text().to_double();
- origin.z=value_editor[11]->get_text().to_double();
+
+ if (evaluator) {
+ origin.x=evaluator->eval(value_editor[3]->get_text());
+ origin.y=evaluator->eval(value_editor[7]->get_text());
+ origin.z=evaluator->eval(value_editor[11]->get_text());
+ } else {
+ origin.x=value_editor[3]->get_text().to_double();
+ origin.y=value_editor[7]->get_text().to_double();
+ origin.z=value_editor[11]->get_text().to_double();
+ }
v=Transform(basis,origin);
emit_signal("variant_changed");
@@ -1736,6 +1798,8 @@ CustomPropertyEditor::CustomPropertyEditor() {
add_child(menu);
menu->connect("item_pressed",this,"_menu_option");
+ evaluator = NULL;
+
spinbox = memnew ( SpinBox );
add_child(spinbox);
spinbox->set_area_as_parent_rect(5);
@@ -1750,7 +1814,7 @@ CustomPropertyEditor::CustomPropertyEditor() {
bool PropertyEditor::_might_be_in_instance() {
if (!obj)
- return NULL;
+ return false;
Node *node = obj->cast_to<Node>();
@@ -2695,8 +2759,11 @@ void PropertyEditor::update_tree() {
}
+ if (p.hint==PROPERTY_HINT_ENUM)
+ item->set_cell_mode( 1, TreeItem::CELL_MODE_RANGE );
+ else
+ item->set_cell_mode( 1, TreeItem::CELL_MODE_RANGE_EXPRESSION );
- item->set_cell_mode( 1, TreeItem::CELL_MODE_RANGE );
if (p.hint==PROPERTY_HINT_SPRITE_FRAME) {
item->set_range_config(1,0,99999,1);
@@ -3390,6 +3457,9 @@ void PropertyEditor::edit(Object* p_object) {
}
obj=p_object;
+
+ evaluator->edit(p_object);
+
update_tree();
if (obj) {
@@ -3719,6 +3789,10 @@ PropertyEditor::PropertyEditor() {
custom_editor->connect("resource_edit_request", this,"_resource_edit_request",make_binds(),CONNECT_DEFERRED);
tree->set_hide_folding(true);
+ evaluator = memnew (PropertyValueEvaluator);
+ tree->set_value_evaluator(evaluator);
+ custom_editor->set_value_evaluator(evaluator);
+
capitalize_paths=true;
autoclear=false;
tree->set_column_titles_visible(false);
@@ -3737,6 +3811,7 @@ PropertyEditor::PropertyEditor() {
PropertyEditor::~PropertyEditor()
{
+ memdelete(evaluator);
}
@@ -3975,3 +4050,51 @@ SectionedPropertyEditor::~SectionedPropertyEditor() {
memdelete(filter);
}
+
+double PropertyValueEvaluator::eval(const String& p_text) {
+
+ if (!obj)
+ return _default_eval(p_text);
+
+ Ref<Script> script= Ref<Script>(script_language ->create_script());
+ script->set_source_code(_build_script(p_text));
+ Error err = script->reload();
+ if (err) {
+ print_line("[PropertyValueEvaluator] Error loading script for expression: " + p_text);
+ return _default_eval(p_text);
+ }
+
+ ScriptInstance *script_instance = script->instance_create(this);
+
+ Variant::CallError call_err;
+ script_instance->call("set_this",obj);
+ double result = script_instance->call("e", NULL, 0, call_err );
+ if (call_err.error == Variant::CallError::CALL_OK) {
+ return result;
+ }
+ print_line("[PropertyValueEvaluator]: Error eval! Error code: " + itos(call_err.error));
+
+ memdelete(script_instance);
+
+ return _default_eval(p_text);
+}
+
+
+void PropertyValueEvaluator::edit(Object *p_obj) {
+ obj = p_obj;
+}
+
+String PropertyValueEvaluator::_build_script(const String& p_text) {
+ String script_text = "tool\nvar this\nfunc set_this(p_this):\n\tthis=p_this\nfunc e():\n\treturn ";
+ script_text += p_text.strip_edges();
+ script_text += "\n";
+ return script_text;
+}
+
+PropertyValueEvaluator::PropertyValueEvaluator() {
+ script_language = ScriptServer::get_language(0); // todo: get script language from editor setting
+}
+
+PropertyValueEvaluator::~PropertyValueEvaluator() {
+
+}
diff --git a/tools/editor/property_editor.h b/tools/editor/property_editor.h
index b870a618e9..1fedb832ed 100644
--- a/tools/editor/property_editor.h
+++ b/tools/editor/property_editor.h
@@ -46,6 +46,8 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
+class PropertyValueEvaluator;
+
class CustomPropertyEditor : public Popup {
OBJ_TYPE( CustomPropertyEditor, Popup );
@@ -104,6 +106,8 @@ class CustomPropertyEditor : public Popup {
bool updating;
+ PropertyValueEvaluator *evaluator;
+
void _text_edit_changed();
void _file_selected(String p_file);
void _scroll_modified(double p_value);
@@ -137,6 +141,8 @@ public:
void set_read_only(bool p_read_only) { read_only=p_read_only; }
+ void set_value_evaluator( PropertyValueEvaluator *p_evaluator) { evaluator=p_evaluator; }
+
bool edit(Object* p_owner,const String& p_name,Variant::Type p_type, const Variant& p_variant,int p_hint,String p_hint_text);
CustomPropertyEditor();
@@ -151,6 +157,8 @@ class PropertyEditor : public Control {
//Object *object;
LineEdit *search_box;
+ PropertyValueEvaluator *evaluator;
+
Object* obj;
Array _prop_edited_name;
@@ -283,4 +291,24 @@ public:
~SectionedPropertyEditor();
};
+class PropertyValueEvaluator : public ValueEvaluator {
+ OBJ_TYPE( PropertyValueEvaluator, ValueEvaluator );
+
+ Object *obj;
+ ScriptLanguage *script_language;
+ String _build_script(const String& p_text);
+
+ _FORCE_INLINE_ double _default_eval(const String& p_text) {
+ return p_text.to_double();
+ }
+
+public:
+
+ void edit(Object *p_obj);
+ double eval(const String& p_text);
+
+ PropertyValueEvaluator();
+ ~PropertyValueEvaluator();
+};
+
#endif
diff --git a/tools/editor/script_editor_debugger.cpp b/tools/editor/script_editor_debugger.cpp
index fed5ab1a16..90d162c708 100644
--- a/tools/editor/script_editor_debugger.cpp
+++ b/tools/editor/script_editor_debugger.cpp
@@ -1199,7 +1199,7 @@ void ScriptEditorDebugger::_bind_methods() {
ObjectTypeDB::bind_method(_MD("live_debug_reparent_node"),&ScriptEditorDebugger::live_debug_reparent_node);
ADD_SIGNAL(MethodInfo("goto_script_line"));
- ADD_SIGNAL(MethodInfo("breaked",PropertyInfo(Variant::BOOL,"reallydid")));
+ ADD_SIGNAL(MethodInfo("breaked",PropertyInfo(Variant::BOOL,"reallydid"),PropertyInfo(Variant::BOOL,"can_debug")));
ADD_SIGNAL(MethodInfo("show_debugger",PropertyInfo(Variant::BOOL,"reallydid")));
}