diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 3c4268b926..0aa9abbde4 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -37,7 +37,9 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: Codename Engine (Executable Only)
- path: export/release/windows/bin/CodenameEngine.exe
+ path: |
+ export/release/windows/bin/CodenameEngine.exe
+ export/release/windows/bin/lime.ndll
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v4
with:
diff --git a/building/alsoft.txt b/building/alsoft.txt
deleted file mode 100644
index 58d8c74cc1..0000000000
--- a/building/alsoft.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-[General]
-sample-type=float32
-stereo-mode=speakers
-hrtf=false
-cf_level=0
-output-limiter=false
-front-stabilizer=false
-volume-adjust=0
-period_size=441
-sources=512
-sends=64
-dither=false
-
-[decoder]
-hq-mode=true
-distance-comp=true
-nfc=false
\ No newline at end of file
diff --git a/building/libs.xml b/building/libs.xml
index 9b5f8287a9..363fce7779 100644
--- a/building/libs.xml
+++ b/building/libs.xml
@@ -3,36 +3,32 @@
Preparing installation...
-
-
-
-
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
@@ -44,12 +40,12 @@
-
+
+
+
+
diff --git a/project.xml b/project.xml
index b129dbb377..c14bf08538 100644
--- a/project.xml
+++ b/project.xml
@@ -1,84 +1,54 @@
-
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
-
-
+
+
-
+
-
+
-
+
@@ -87,36 +57,60 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -124,32 +118,46 @@
-
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
-
+
@@ -165,81 +173,66 @@
-
-
-
-
-
-
-
+
-
-
-
+
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
+
-
-
-
-
-
-
-
+
+
-
-
+
+
-
-
-
-
+
-
-
-
-
-
+
+
+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/StringTools.hx b/source/StringTools.hx
deleted file mode 100644
index 2eac4b10f1..0000000000
--- a/source/StringTools.hx
+++ /dev/null
@@ -1,636 +0,0 @@
-/*
- * Copyright (C)2005-2019 Haxe Foundation
- *
- * 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.
- */
-
-import haxe.iterators.StringIterator;
-import haxe.iterators.StringKeyValueIterator;
-
-#if cpp
-using cpp.NativeString;
-#end
-
-/**
- This class provides advanced methods on Strings. It is ideally used with
- `using StringTools` and then acts as an [extension](https://haxe.org/manual/lf-static-extension.html)
- to the `String` class.
-
- If the first argument to any of the methods is null, the result is
- unspecified.
-**/
-class StringTools {
- /**
- Encode an URL by using the standard format.
- **/
- #if (!java && !cpp && !lua && !eval) inline #end public static function urlEncode(s:String):String {
- #if flash
- return untyped __global__["encodeURIComponent"](s);
- #elseif neko
- return untyped new String(_urlEncode(s.__s));
- #elseif js
- return untyped encodeURIComponent(s);
- #elseif cpp
- return untyped s.__URLEncode();
- #elseif java
- return postProcessUrlEncode(java.net.URLEncoder.encode(s, "UTF-8"));
- #elseif python
- return python.lib.urllib.Parse.quote(s, "");
- #elseif hl
- var len = 0;
- var b = @:privateAccess s.bytes.urlEncode(len);
- return @:privateAccess String.__alloc__(b, len);
- #elseif lua
- s = lua.NativeStringTools.gsub(s, "\n", "\r\n");
- s = lua.NativeStringTools.gsub(s, "([^%w %-%_%.%~])", function(c) {
- return lua.NativeStringTools.format("%%%02X", lua.NativeStringTools.byte(c) + '');
- });
- s = lua.NativeStringTools.gsub(s, " ", "+");
- return s;
- #else
- return null;
- #end
- }
-
- #if java
- private static function postProcessUrlEncode(s:String):String {
- var ret = new StringBuf();
- var i = 0, len = s.length;
- while (i < len) {
- switch (_charAt(s, i++)) {
- case '+'.code:
- ret.add('%20');
- case '%'.code if (i <= len - 2):
- var c1 = _charAt(s, i++), c2 = _charAt(s, i++);
- switch [c1, c2] {
- case ['2'.code, '1'.code]:
- ret.addChar('!'.code);
- case ['2'.code, '7'.code]:
- ret.addChar('\''.code);
- case ['2'.code, '8'.code]:
- ret.addChar('('.code);
- case ['2'.code, '9'.code]:
- ret.addChar(')'.code);
- case ['7'.code, 'E'.code] | ['7'.code, 'e'.code]:
- ret.addChar('~'.code);
- case _:
- ret.addChar('%'.code);
- ret.addChar(cast c1);
- ret.addChar(cast c2);
- }
- case var chr:
- ret.addChar(cast chr);
- }
- }
- return ret.toString();
- }
- #end
-
- /**
- Decode an URL using the standard format.
- **/
- #if (!java && !cpp && !lua && !eval) inline #end public static function urlDecode(s:String):String {
- #if flash
- return untyped __global__["decodeURIComponent"](s.split("+").join(" "));
- #elseif neko
- return untyped new String(_urlDecode(s.__s));
- #elseif js
- return untyped decodeURIComponent(s.split("+").join(" "));
- #elseif cpp
- return untyped s.__URLDecode();
- #elseif java
- try
- return java.net.URLDecoder.decode(s, "UTF-8")
- catch (e:Dynamic)
- throw e;
- #elseif python
- return python.lib.urllib.Parse.unquote(s);
- #elseif hl
- var len = 0;
- var b = @:privateAccess s.bytes.urlDecode(len);
- return @:privateAccess String.__alloc__(b, len);
- #elseif lua
- s = lua.NativeStringTools.gsub(s, "+", " ");
- s = lua.NativeStringTools.gsub(s, "%%(%x%x)", function(h) {
- return lua.NativeStringTools.char(lua.Lua.tonumber(h, 16));
- });
- s = lua.NativeStringTools.gsub(s, "\r\n", "\n");
- return s;
- #else
- return null;
- #end
- }
-
- /**
- Escapes HTML special characters of the string `s`.
-
- The following replacements are made:
-
- - `&` becomes `&`;
- - `<` becomes `<`;
- - `>` becomes `>`;
-
- If `quotes` is true, the following characters are also replaced:
-
- - `"` becomes `"`;
- - `'` becomes `'`;
- **/
- public static function htmlEscape(s:String, ?quotes:Bool):String {
- var buf = new StringBuf();
- for (code in #if neko iterator(s) #else new haxe.iterators.StringIteratorUnicode(s) #end) {
- switch (code) {
- case '&'.code:
- buf.add("&");
- case '<'.code:
- buf.add("<");
- case '>'.code:
- buf.add(">");
- case '"'.code if (quotes):
- buf.add(""");
- case '\''.code if (quotes):
- buf.add("'");
- case _:
- buf.addChar(code);
- }
- }
- return buf.toString();
- }
-
- /**
- Unescapes HTML special characters of the string `s`.
-
- This is the inverse operation to htmlEscape, i.e. the following always
- holds: `htmlUnescape(htmlEscape(s)) == s`
-
- The replacements follow:
-
- - `&` becomes `&`
- - `<` becomes `<`
- - `>` becomes `>`
- - `"` becomes `"`
- - `'` becomes `'`
- **/
- public static function htmlUnescape(s:String):String {
- return s.split(">")
- .join(">")
- .split("<")
- .join("<")
- .split(""")
- .join('"')
- .split("'")
- .join("'")
- .split("&")
- .join("&");
- }
-
- /**
- Returns `true` if `s` contains `value` and `false` otherwise.
-
- When `value` is `null`, the result is unspecified.
- **/
- public static inline function contains(s:String, value:String):Bool {
- #if (js && js_es >= 6)
- return (cast s).includes(value);
- #else
- return s.indexOf(value) != -1;
- #end
- }
-
- /**
- Tells if the string `s` starts with the string `start`.
-
- If `start` is `null`, the result is unspecified.
-
- If `start` is the empty String `""`, the result is true.
- **/
- public static #if (java || python || (js && js_es >= 6)) inline #end function startsWith(s:String, start:String):Bool {
- #if java
- return (cast s : java.NativeString).startsWith(start);
- #elseif hl
- return @:privateAccess (s.length >= start.length && s.bytes.compare(0, start.bytes, 0, start.length << 1) == 0);
- #elseif python
- return python.NativeStringTools.startswith(s, start);
- #elseif (js && js_es >= 6)
- return (cast s).startsWith(start);
- #elseif lua
- return untyped __lua__("{0}:sub(1, #{1}) == {1}", s, start);
- #elseif cpp
- return untyped s.__StartsWith(start);
- #else
- return (s.length >= start.length && s.indexOf(start, 0) == 0);
- #end
- }
-
- /**
- Tells if the string `s` ends with the string `end`.
-
- If `end` is `null`, the result is unspecified.
-
- If `end` is the empty String `""`, the result is true.
- **/
- public static #if (java || python || (js && js_es >= 6)) inline #end function endsWith(s:String, end:String):Bool {
- #if java
- return (cast s : java.NativeString).endsWith(end);
- #elseif hl
- var elen = end.length;
- var slen = s.length;
- return @:privateAccess (slen >= elen && s.bytes.compare((slen - elen) << 1, end.bytes, 0, elen << 1) == 0);
- #elseif python
- return python.NativeStringTools.endswith(s, end);
- #elseif (js && js_es >= 6)
- return (cast s).endsWith(end);
- #elseif lua
- return end == "" || untyped __lua__("{0}:sub(-#{1}) == {1}", s, end);
- #elseif cpp
- return untyped s.__EndsWith(end);
- #else
- var elen = end.length;
- var slen = s.length;
- return (slen >= elen && s.lastIndexOf(end, (slen - elen)) == (slen - elen));
- #end
- }
-
- /**
- Tells if the character in the string `s` at position `pos` is a space.
-
- A character is considered to be a space character if its character code
- is 9,10,11,12,13 or 32.
-
- If `s` is the empty String `""`, or if pos is not a valid position within
- `s`, the result is false.
- **/
- public static function isSpace(s:String, pos:Int):Bool {
- #if (python || lua)
- if (s.length == 0 || pos < 0 || pos >= s.length)
- return false;
- #end
- var c = StringTools.fastCodeAt(s, pos);
- return (c > 8 && c < 14) || c == 32;
- }
-
- /**
- Removes leading space characters of `s`.
-
- This function internally calls `isSpace()` to decide which characters to
- remove.
-
- If `s` is the empty String `""` or consists only of space characters, the
- result is the empty String `""`.
- **/
- public inline static function ltrim(s:String):String {
- var l = s.length;
- var r = 0;
- while (r < l && isSpace(s, r)) {
- r++;
- }
- if (r > 0)
- return s.substr(r, l - r);
- else
- return s;
- }
-
- /**
- Removes trailing space characters of `s`.
-
- This function internally calls `isSpace()` to decide which characters to
- remove.
-
- If `s` is the empty String `""` or consists only of space characters, the
- result is the empty String `""`.
- **/
- public inline static function rtrim(s:String):String {
- var l = s.length;
- var r = 0;
- while (r < l && isSpace(s, l - r - 1)) {
- r++;
- }
- if (r > 0) {
- return s.substr(0, l - r);
- } else {
- return s;
- }
- }
-
- /**
- Removes leading and trailing space characters of `s`.
-
- This is a convenience function for `ltrim(rtrim(s))`.
- **/
- public #if java inline #end static function trim(s:String):String {
- #if java
- return (cast s : java.NativeString).trim();
- #else
- //return ltrim(rtrim(s));
- var l = s.length;
- var start = 0;
- var end = l;
-
- // Trim leading spaces
- while (start < l && isSpace(s, start)) {
- start++;
- }
-
- // Trim trailing spaces
- while (end > start && isSpace(s, end - 1)) {
- end--;
- }
-
- return (start > 0 || end < l) ? s.substr(start, end - start) : s;
- #end
- }
-
- /**
- Concatenates `c` to `s` until `s.length` is at least `l`.
-
- If `c` is the empty String `""` or if `l` does not exceed `s.length`,
- `s` is returned unchanged.
-
- If `c.length` is 1, the resulting String length is exactly `l`.
-
- Otherwise the length may exceed `l`.
-
- If `c` is null, the result is unspecified.
- **/
- public static function lpad(s:String, c:String, l:Int):String {
- if (c.length <= 0)
- return s;
-
- var buf = new StringBuf();
- l -= s.length;
- while (buf.length < l) {
- buf.add(c);
- }
- buf.add(s);
- return buf.toString();
- }
-
- /**
- Appends `c` to `s` until `s.length` is at least `l`.
-
- If `c` is the empty String `""` or if `l` does not exceed `s.length`,
- `s` is returned unchanged.
-
- If `c.length` is 1, the resulting String length is exactly `l`.
-
- Otherwise the length may exceed `l`.
-
- If `c` is null, the result is unspecified.
- **/
- public static function rpad(s:String, c:String, l:Int):String {
- if (c.length <= 0)
- return s;
-
- var buf = new StringBuf();
- buf.add(s);
- while (buf.length < l) {
- buf.add(c);
- }
- return buf.toString();
- }
-
- /**
- Replace all occurrences of the String `sub` in the String `s` by the
- String `by`.
-
- If `sub` is the empty String `""`, `by` is inserted after each character
- of `s` except the last one. If `by` is also the empty String `""`, `s`
- remains unchanged.
-
- If `sub` or `by` are null, the result is unspecified.
- **/
- public static function replace(s:String, sub:String, by:String):String {
- #if java
- if (sub.length == 0)
- return s.split(sub).join(by);
- else
- return (cast s : java.NativeString).replace(sub, by);
- #else
- return s.split(sub).join(by);
- #end
- }
-
- /**
- Encodes `n` into a hexadecimal representation.
-
- If `digits` is specified, the resulting String is padded with "0" until
- its `length` equals `digits`.
- **/
- public static function hex(n:Int, ?digits:Int) {
- #if flash
- var n:UInt = n;
- var s:String = untyped n.toString(16);
- s = s.toUpperCase();
- #else
- var s = "";
- var hexChars = "0123456789ABCDEF";
- do {
- s = hexChars.charAt(n & 15) + s;
- n >>>= 4;
- } while (n > 0);
- #end
- #if python
- if (digits != null && s.length < digits) {
- var diff = digits - s.length;
- for (_ in 0...diff) {
- s = "0" + s;
- }
- }
- #else
- if (digits != null)
- while (s.length < digits)
- s = "0" + s;
- #end
- return s;
- }
-
- /**
- Returns the character code at position `index` of String `s`, or an
- end-of-file indicator at if `position` equals `s.length`.
-
- This method is faster than `String.charCodeAt()` on some platforms, but
- the result is unspecified if `index` is negative or greater than
- `s.length`.
-
- End of file status can be checked by calling `StringTools.isEof()` with
- the returned value as argument.
-
- This operation is not guaranteed to work if `s` contains the `\0`
- character.
- **/
- public static #if !eval inline #end function fastCodeAt(s:String, index:Int):Int {
- #if neko
- return untyped __dollar__sget(s.__s, index);
- #elseif cpp
- return untyped s.cca(index);
- #elseif flash
- return untyped s.cca(index);
- #elseif java
- return (index < s.length) ? cast(_charAt(s, index), Int) : -1;
- #elseif js
- return (cast s).charCodeAt(index);
- #elseif python
- return if (index >= s.length) -1 else python.internal.UBuiltins.ord(python.Syntax.arrayAccess(s, index));
- #elseif hl
- return @:privateAccess s.bytes.getUI16(index << 1);
- #elseif lua
- #if lua_vanilla
- return lua.NativeStringTools.byte(s, index + 1);
- #else
- return lua.lib.luautf8.Utf8.byte(s, index + 1);
- #end
- #else
- return untyped s.cca(index);
- #end
- }
-
- /**
- Returns the character code at position `index` of String `s`, or an
- end-of-file indicator at if `position` equals `s.length`.
-
- This method is faster than `String.charCodeAt()` on some platforms, but
- the result is unspecified if `index` is negative or greater than
- `s.length`.
-
- This operation is not guaranteed to work if `s` contains the `\0`
- character.
- **/
- public static #if !eval inline #end function unsafeCodeAt(s:String, index:Int):Int {
- #if neko
- return untyped __dollar__sget(s.__s, index);
- #elseif cpp
- return untyped s.cca(index);
- #elseif flash
- return untyped s.cca(index);
- #elseif java
- return cast(_charAt(s, index), Int);
- #elseif js
- return (cast s).charCodeAt(index);
- #elseif python
- return python.internal.UBuiltins.ord(python.Syntax.arrayAccess(s, index));
- #elseif hl
- return @:privateAccess s.bytes.getUI16(index << 1);
- #elseif lua
- #if lua_vanilla
- return lua.NativeStringTools.byte(s, index + 1);
- #else
- return lua.lib.luautf8.Utf8.byte(s, index + 1);
- #end
- #else
- return untyped s.cca(index);
- #end
- }
-
- /**
- Returns an iterator of the char codes.
-
- Note that char codes may differ across platforms because of different
- internal encoding of strings in different runtimes.
- For the consistent cross-platform UTF8 char codes see `haxe.iterators.StringIteratorUnicode`.
- **/
- public static inline function iterator(s:String):StringIterator {
- return new StringIterator(s);
- }
-
- /**
- Returns an iterator of the char indexes and codes.
-
- Note that char codes may differ across platforms because of different
- internal encoding of strings in different of runtimes.
- For the consistent cross-platform UTF8 char codes see `haxe.iterators.StringKeyValueIteratorUnicode`.
- **/
- public static inline function keyValueIterator(s:String):StringKeyValueIterator {
- return new StringKeyValueIterator(s);
- }
-
- /**
- Tells if `c` represents the end-of-file (EOF) character.
- **/
- @:noUsing public static inline function isEof(c:Int):Bool {
- #if (flash || cpp || hl)
- return c == 0;
- #elseif js
- return c != c; // fast NaN
- #elseif (neko || lua || eval)
- return c == null;
- #elseif (java || python)
- return c == -1;
- #else
- return false;
- #end
- }
-
- /**
- Returns a String that can be used as a single command line argument
- on Unix.
- The input will be quoted, or escaped if necessary.
- **/
- @:noCompletion
- @:deprecated('StringTools.quoteUnixArg() is deprecated. Use haxe.SysTools.quoteUnixArg() instead.')
- public static function quoteUnixArg(argument:String):String {
- return inline haxe.SysTools.quoteUnixArg(argument);
- }
-
- /**
- Character codes of the characters that will be escaped by `quoteWinArg(_, true)`.
- **/
- @:noCompletion
- @:deprecated('StringTools.winMetaCharacters is deprecated. Use haxe.SysTools.winMetaCharacters instead.')
- public static var winMetaCharacters:Array = cast haxe.SysTools.winMetaCharacters;
-
- /**
- Returns a String that can be used as a single command line argument
- on Windows.
- The input will be quoted, or escaped if necessary, such that the output
- will be parsed as a single argument using the rule specified in
- http://msdn.microsoft.com/en-us/library/ms880421
-
- Examples:
- ```haxe
- quoteWinArg("abc") == "abc";
- quoteWinArg("ab c") == '"ab c"';
- ```
- **/
- @:noCompletion
- @:deprecated('StringTools.quoteWinArg() is deprecated. Use haxe.SysTools.quoteWinArg() instead.')
- public static function quoteWinArg(argument:String, escapeMetaCharacters:Bool):String {
- return inline haxe.SysTools.quoteWinArg(argument, escapeMetaCharacters);
- }
-
- #if java
- private static inline function _charAt(str:String, idx:Int):java.StdTypes.Char16
- return (cast str : java.NativeString).charAt(idx);
- #end
-
- #if neko
- private static var _urlEncode = neko.Lib.load("std", "url_encode", 1);
- private static var _urlDecode = neko.Lib.load("std", "url_decode", 1);
- #end
-
- #if utf16
- static inline var MIN_SURROGATE_CODE_POINT = 65536;
-
- static inline function utf16CodePointAt(s:String, index:Int):Int {
- var c = StringTools.fastCodeAt(s, index);
- if (c >= 0xD800 && c <= 0xDBFF) {
- c = ((c - 0xD7C0) << 10) | (StringTools.fastCodeAt(s, index + 1) & 0x3FF);
- }
- return c;
- }
- #end
-}
diff --git a/source/flixel/sound/FlxSound.hx b/source/flixel/sound/FlxSound.hx
deleted file mode 100644
index 9891bc934a..0000000000
--- a/source/flixel/sound/FlxSound.hx
+++ /dev/null
@@ -1,960 +0,0 @@
-package flixel.sound;
-
-import lime.media.AudioBuffer;
-import lime.media.AudioSource;
-import lime.media.AudioManager;
-
-import openfl.Assets;
-import openfl.events.IEventDispatcher;
-import openfl.events.Event;
-import openfl.media.Sound;
-import openfl.media.SoundChannel;
-import openfl.media.SoundTransform;
-import openfl.media.SoundMixer;
-import openfl.net.URLRequest;
-import openfl.utils.AssetType;
-#if flash11
-import openfl.utils.ByteArray;
-#end
-
-import flixel.math.FlxMath;
-import flixel.math.FlxPoint;
-import flixel.system.FlxAssets.FlxSoundAsset;
-import flixel.tweens.FlxTween;
-import flixel.util.FlxDestroyUtil;
-import flixel.util.FlxSignal;
-import flixel.util.FlxStringUtil;
-import flixel.FlxBasic;
-import flixel.FlxObject;
-import flixel.FlxG;
-
-@:access(flixel.FlxGame)
-class FlxSound extends FlxBasic {
- /**
- * The x position of this sound in world coordinates.
- * Only really matters if you are doing proximity/panning stuff.
- */
- public var x:Float;
-
- /**
- * The y position of this sound in world coordinates.
- * Only really matters if you are doing proximity/panning stuff.
- */
- public var y:Float;
-
- /**
- * Whether or not this sound should be automatically destroyed when you switch states.
- */
- public var persist:Bool;
-
- /**
- * Whether or not the sound is currently playing.
- */
- public var playing(get, never):Bool;
-
- /**
- * Set volume to a value between 0 and 1* to change how this sound is.
- */
- public var volume(get, set):Float;
-
- /**
- * Whether to make this sound muted or not.
- */
- public var muted(default, set):Bool;
-
- #if FLX_PITCH
- /**
- * Set pitch, which also alters the playback speed. Default is 1.
- * @since 5.0.0
- */
- public var pitch(get, set):Float;
-
- /**
- * Alters the pitch of the sound depends on the current FlxG.timeScale. Default is true.
- * @since raltyMod
- */
- public var timeScaleBased:Bool;
- #end
-
- /**
- * Pan amount. -1 = full left, 1 = full right. Proximity based panning overrides this.
- */
- public var pan(get, set):Float;
-
- /**
- * The position in runtime of the music playback in milliseconds.
- * If set while paused, changes only come into effect after a `resume()` call.
- */
- public var time(get, set):Float;
-
- /**
- * The offset for this FlxSound.
- * Useful for just generally offsetting this sound without affecting time.
- * @since raltyMod
- */
- public var offset(get, set):Float;
-
- /**
- * The length of the sound in milliseconds.
- * @since 4.2.0
- */
- public var length(get, never):Float;
-
- /**
- * The latency of the sound in milliseconds.
- * @since raltyMod
- */
- //public var latency(get, never):Float;
-
- /**
- * Whether or not this sound should loop.
- */
- public var looped(default, set):Bool;
-
- /**
- * In case of looping, the point (in milliseconds) from where to restart the sound when it loops back
- * @since 4.1.0
- */
- public var loopTime(default, set):Float;
-
- /**
- * At which point to stop playing the sound, in milliseconds.
- * If not set / `null`, the sound completes normally.
- * @since 4.2.0
- */
- public var endTime(default, set):Null;
-
- /**
- * The sound's "target" (for proximity and panning).
- * @since raltyMod
- */
- public var target:Null;
-
- /**
- * The maximum effective radius of this sound (for proximity and panning).
- * @since raltyMod
- */
- public var radius:Float;
-
- /**
- * Whether the proximity alters the pan or not.
- * @since raltyMod
- */
- public var proximityPan:Bool;
-
- /**
- * Controls how much this object is affected by camera scrolling. `0` = no movement (e.g. a static sound),
- * This is only useful if used with proximity (Initialized once proximity is used).
- * @since raltyMod
- */
- public var scrollFactor(default, null):FlxPoint;
-
- /**
- * Stores for how much channels are in the loaded sound.
- * @since raltyMod
- */
- public var channels(get, never):Int;
-
- /**
- * Whether or not this sound is stereo instead of mono.
- * @since raltyMod
- */
- public var stereo(get, never):Bool;
-
- /**
- * Whether or not this sound is loaded yet.
- * @since raltyMod
- */
- public var loaded(get, never):Bool;
-
- /**
- * Whether or not this sound is streamed, only vorbis though.
- * @since raltyMod
- */
- public var streamed(get, never):Bool;
-
- /**
- * Stores the average wave amplitude of both stereo channels.
- * @since raltyMod
- */
- public var amplitude(get, never):Float;
-
- /**
- * Just the amplitude of the left stereo channel
- * @since raltyMod
- */
- public var amplitudeLeft(get, never):Float;
-
- /**
- * Just the amplitude of the right stereo channel
- * @since raltyMod
- */
- public var amplitudeRight(get, never):Float;
-
- /**
- * The ID3 song name. Defaults to null. Currently only works for MP3 streamed sounds.
- */
- public var name(default, null):String;
-
- /**
- * The ID3 artist name. Defaults to null. Currently only works for MP3 streamed sounds.
- */
- public var artist(default, null):String;
-
- /**
- * Whether to call `destroy()` when the sound has finished playing.
- */
- public var autoDestroy:Bool;
-
- /**
- * Signal that is dispatched on sound complete.
- * Seperates the looping from onComplete.
- */
- public final onFinish:FlxSignal;
-
- /**
- * Tracker for sound complete callback. If assigned, will be called
- * each time when sound reaches its end.
- */
- //@:deprecated("`FlxSound.onComplete` is deprecated! Use `FlxSound.onFinish` instead.")
- public var onComplete:Void->Void;
-
- /**
- * The sound group this sound belongs to
- */
- public var group(default, set):FlxSoundGroup;
-
- /**
- * Stores the sound lime AudioBuffer if exists.
- * @since raltyMod
- */
- public var buffer(get, never):AudioBuffer;
-
- /**
- * The tween used to fade this sound's volume in and out (set via `fadeIn()` and `fadeOut()`)
- * @since 4.1.0
- */
- public var fadeTween:FlxTween;
-
- @:allow(flixel.system.frontEnds.SoundFrontEnd.load) var _sound:Sound;
- var _transform:SoundTransform;
- var _channel:SoundChannel;
- var _source:AudioSource;
- var _paused:Bool;
- var _volume:Float;
- var _volumeAdjust:Float;
- var _pan:Float;
- var _panAdjust:Float;
- var _time:Float;
- var _offset:Float;
- var _timeInterpolation:Float;
- var _lastTime:Null; // FlxG.game.getTicks(), in MS
- var _length:Float;
- #if FLX_PITCH
- var _pitch:Float;
- var _timeScaleAdjust:Float;
- var _realPitch:Float;
- #end
- var _amplitudeLeft:Float;
- var _amplitudeRight:Float;
- var _amplitudeUpdate:Bool;
- var _alreadyPaused:Null;
-
- public function new() {
- super();
- onFinish = new FlxSignal();
- revive();
- }
-
- /**
- * Resets this FlxSound properties.
- *
- * @param clean Whether if this FlxSound also needs to be cleaned up too.
- */
- public function reset():Void {
- if (_source != null) stop();
- onFinish.removeAll();
-
- x = y = 0;
- @:bypassAccessor muted = false;
- @:bypassAccessor looped = false;
- @:bypassAccessor loopTime = 0;
- @:bypassAccessor endTime = null;
- autoDestroy = false;
- visible = false;
- target = null;
- radius = 0;
- proximityPan = true;
- onComplete = null;
- timeScaleBased = true;
-
- _alreadyPaused = null;
- _cameras = null;
- _lastTime = null;
- _paused = false;
- _offset = _length = _time = 0;
- _volume = _volumeAdjust = 1;
- _amplitudeLeft = _amplitudeRight = 0;
- _amplitudeUpdate = true;
- #if FLX_PITCH _pitch = _realPitch = _timeScaleAdjust = 1; #end
-
- if (_transform == null) _transform = new SoundTransform();
- }
-
- /**
- * Internal cleanup function for cleaning up this FlxSound.
- * @since raltyMod
- */
- function cleanup(destroySound:Bool, resetPosition:Bool = true) @:privateAccess {
- active = false;
- _lastTime = null;
-
- if (destroySound) {
- scrollFactor = FlxDestroyUtil.put(scrollFactor);
-
- if (group != null) group.remove(this);
-
- if (_channel != null) {
- _channel.stop();
- _channel = null;
- }
- if (_source != null) {
- _source.onComplete.remove(stopped);
- _source.onLoop.remove(source_looped);
- _source.dispose();
- }
- _source = null;
- _sound = null;
-
- if (autoDestroy) persist = false;
- reset();
- }
- else if (_channel != null && _channel.__isValid) {
- if (resetPosition) {
- _source.stop();
-
- _time = 0;
- _paused = false;
- }
- else if (!_paused) {
- get_time();
- _source.pause();
- }
- }
- }
-
- /**
- * Handles fade out, fade in, panning, proximity, and amplitude operations each frame.
- */
- override function update(elapsed:Float):Void {
- if (!playing) return;
-
- var timeScaleTarget = timeScaleBased ? FlxG.timeScale : 1.0;
- if (_timeScaleAdjust != timeScaleTarget) {
- _timeScaleAdjust = timeScaleTarget;
- pitch = _pitch;
- if (_channel == null) return;
- }
-
- _amplitudeUpdate = true;
-
- // Distance-based volume control
- if (target != null) {
- var targetPosition = target.getPosition(), position = getPosition();
- var camera = camera;
- if (camera != null) {
- targetPosition.subtract(camera.scroll.x * target.scrollFactor.x, camera.scroll.y * target.scrollFactor.y);
- if (scrollFactor != null) position.subtract(camera.scroll.x * scrollFactor.x, camera.scroll.y * scrollFactor.y);
- else position.subtract(camera.scroll.x, camera.scroll.y);
- }
-
- var radialMultiplier = targetPosition.distanceTo(position) / radius;
-
- // Make it so it affects the 3d position of the source and not just the panning?
- _volumeAdjust = 1 - FlxMath.bound(radialMultiplier, 0, 1);
- if (proximityPan) _panAdjust = (position.x - targetPosition.x) / radius;
-
- targetPosition.put();
- position.put();
- }
- else
- _volumeAdjust = 1.0;
-
- updateTransform();
- }
-
- override function revive() {
- reset();
- super.revive();
- }
-
- override function kill() {
- super.kill();
- reset();
- }
-
- override function destroy() {
- super.destroy();
- cleanup(true);
- }
-
- /**
- * One of the main setup functions for sounds, this function loads a sound from an embedded MP3.
- *
- * @param embeddedSound An embedded Class object representing an MP3 file.
- * @param looped Whether or not this sound should loop endlessly.
- * @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
- * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
- * @param onComplete Called when the sound finished playing
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function loadEmbedded(embeddedSound:FlxSoundAsset, looped = false, autoDestroy = false, ?onComplete:Void->Void):FlxSound {
- if (!exists || embeddedSound == null) return this;
- cleanup(true);
-
- if ((embeddedSound is Sound)) _sound = embeddedSound;
- else if ((embeddedSound is Class)) _sound = Type.createInstance(embeddedSound, []);
- else if ((embeddedSound is String)) {
- if (Assets.exists(embeddedSound, AssetType.MUSIC) && this == FlxG.sound.music)
- _sound = Assets.getMusic(embeddedSound);
- else if (Assets.exists(embeddedSound, AssetType.SOUND))
- _sound = Assets.getSound(embeddedSound);
- else
- FlxG.log.error('Could not find a Sound asset with an ID of \'$embeddedSound\'.');
- }
-
- return init(looped, autoDestroy, onComplete);
- }
-
- /**
- * One of the main setup functions for sounds, this function loads a sound from a URL.
- *
- * @param soundURL A string representing the URL of the MP3 file you want to play.
- * @param looped Whether or not this sound should loop endlessly.
- * @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
- * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
- * @param onComplete Called when the sound finished playing
- * @param onLoad Called when the sound finished loading.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function loadStream(soundURL:String, looped = false, autoDestroy = false, ?onComplete:Void->Void, ?onLoad:Void->Void):FlxSound {
- if (!exists) return this;
- cleanup(true);
-
- _sound = new Sound();
- _sound.addEventListener(Event.ID3, gotID3);
- var loadCallback:Event->Void = null;
- loadCallback = function(e:Event)
- {
- (e.target : IEventDispatcher).removeEventListener(e.type, loadCallback);
- // Check if the sound was destroyed before calling. Weak ref doesn't guarantee GC.
- if (_sound == e.target)
- {
- _length = _sound.length;
- if (onLoad != null)
- onLoad();
- }
- }
- // Use a weak reference so this can be garbage collected if destroyed before loading.
- _sound.addEventListener(Event.COMPLETE, loadCallback, false, 0, true);
- _sound.load(new URLRequest(soundURL));
-
- return init(looped, autoDestroy, onComplete);
- }
-
- #if flash11
- /**
- * One of the main setup functions for sounds, this function loads a sound from a ByteArray.
- *
- * @param bytes A ByteArray object.
- * @param looped Whether or not this sound should loop endlessly.
- * @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
- * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function loadByteArray(bytes:ByteArray, looped = false, autoDestroy = false, ?onComplete:Void->Void):FlxSound {
- if (!exists) return this;
- cleanup(true);
-
- _sound = new Sound();
- _sound.addEventListener(Event.ID3, gotID3);
- _sound.loadCompressedDataFromByteArray(bytes, bytes.length);
-
- return init(looped, autoDestroy, onComplete);
- }
- #end
-
- function init(?looped:Null, ?autoDestroy:Null, ?onComplete:NullVoid>):FlxSound {
- if (looped != null) this.looped = looped;
- if (autoDestroy != null) this.autoDestroy = autoDestroy;
- if (onComplete != null) this.onComplete = onComplete;
-
- if (_sound != null) makeChannel();
- else _length = 0;
-
- endTime = null;
-
- return this;
- }
-
- /**
- * Call inbetween init function if a sound asset does exists.
- * @since raltyMod
- */
- function makeChannel() @:privateAccess {
- if (_sound.__buffer == null) return;
- if (_source != null) _source.dispose();
-
- if (_channel == null) (_source = (_channel = new SoundChannel(null)).__source = new AudioSource(_sound.__buffer)).gain = 0;
- else {
- (_source = new AudioSource(_sound.__buffer)).gain = 0;
- _channel.__dispose();
- _channel.__source = _source;
- SoundMixer.__registerSoundChannel(_channel);
- }
- _length = _source.length;
-
- _source.onComplete.add(stopped);
- _source.onLoop.add(source_looped);
- _channel.__soundTransform = _transform;
- _channel.__isValid = true;
- }
-
- /**
- * Call after adjusting the volume to update the sound channel's settings.
- */
- @:allow(flixel.sound.FlxSoundGroup)
- function updateTransform() {
- _transform.volume = calcTransformVolume();
- _transform.pan = _pan + _panAdjust;
- if (_channel != null) _channel.soundTransform = _transform;
- }
-
- public function calcTransformVolume():Float {
- if (muted) return 0.0;
-
- #if FLX_SOUND_SYSTEM
- if (FlxG.sound.muted) return 0.0;
-
- // TODO: when flixel-cne is updated, enable this
- //return FlxG.sound.applySoundCurve(FlxG.sound.volume * volume);
- return FlxG.sound.volume * getActualVolume();
- #else
- return getActualVolume();
- #end
- }
-
- /**
- * Call this function if you want this sound's volume to change
- * based on distance from a particular FlxObject.
- *
- * @param X The X position of the sound.
- * @param Y The Y position of the sound.
- * @param TargetObject The object you want to track.
- * @param Radius The maximum distance this sound can travel.
- * @param Pan Whether panning should be used in addition to the volume changes.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function proximity(x = 0.0, y = 0.0, ?targetObject:FlxObject, ?radius:Float, pan = true, ?scrollFactor:FlxPoint):FlxSound {
- setPosition(x, y);
- if (targetObject != null) this.target = targetObject;
- if (radius != null) this.radius = radius;
- proximityPan = pan;
-
- if (this.scrollFactor == null) this.scrollFactor = FlxPoint.get(1, 1);
- if (scrollFactor != null) this.scrollFactor.copyFrom(scrollFactor);
-
- return this;
- }
-
- /**
- * Helper function to set the coordinates of this object.
- * Sound positioning is used in conjunction with proximity/panning.
- *
- * @param x The new x position
- * @param y The new y position
- */
- public function setPosition(x = 0.0, y = 0.0):Void {
- this.x = x;
- this.y = y;
- }
-
- /**
- * Returns the world position of this object.
- *
- * @param result Optional arg for the returning point.
- * @return The world position of this object.
- * @since raltyMod
- */
- public function getPosition(?result:FlxPoint):FlxPoint {
- if (result == null)
- result = FlxPoint.get();
-
- return result.set(x, y);
- }
-
- /**
- * Call this function to play the sound - also works on paused sounds.
- *
- * @param forceRestart Whether to start the sound over or not.
- * Default value is false, meaning if the sound is already playing or was
- * paused when you call play(), it will continue playing from its current
- * position, NOT start again from the beginning.
- * @param startTime At which point to start playing the sound, in milliseconds.
- * @param endTime At which point to stop playing the sound, in milliseconds.
- * If not set / `null`, the sound completes normally.
- */
- public function play(forceRestart = false, startTime = 0.0, ?endTime:Null):FlxSound {
- if (!exists) return this;
-
- if (forceRestart) cleanup(false, true);
- else if (playing) return this;
-
- if (endTime != null) this.endTime = endTime;
- if (_paused) resume();
- else startSound(startTime);
-
- return this;
- }
-
- /**
- * Unpause a sound. Only works on sounds that have been paused.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function resume():FlxSound {
- if (_paused) startSound(_time);
- return this;
- }
-
- /**
- * Call this function to pause this sound.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function pause():FlxSound {
- if (!playing) return this;
-
- cleanup(false, false);
- _paused = true;
- return this;
- }
-
- /**
- * Call this function to stop this sound.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function stop():FlxSound {
- cleanup(autoDestroy, true);
- return this;
- }
-
- /**
- * Helper function that tweens this sound's volume.
- *
- * @param duration The amount of time the fade-out operation should take.
- * @param to The volume to tween to, 0 by default.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function fadeOut(duration = 1.0, to = 0.0, ?onComplete:FlxTween->Void):FlxSound {
- if (fadeTween != null) fadeTween.cancel();
- fadeTween = FlxTween.num(volume, to, duration, {onComplete: onComplete}, volumeTween);
-
- return this;
- }
-
- /**
- * Helper function that tweens this sound's volume.
- * If the sound wasn't playing at all, it'll play before the tween starts.
- *
- * @param duration The amount of time the fade-in operation should take.
- * @param from The volume to tween from, 0 by default.
- * @param to The volume to tween to, 1 by default.
- * @return This FlxSound instance (nice for chaining stuff together, if you're into that).
- */
- public function fadeIn(duration = 1.0, from = 0.0, to = 1.0, ?onComplete:FlxTween->Void):FlxSound {
- if (!playing) play();
- if (fadeTween != null) fadeTween.cancel();
- fadeTween = FlxTween.num(from, to, duration, {onComplete: onComplete}, volumeTween);
-
- return this;
- }
-
- function volumeTween(f:Float) volume = f;
-
- /**
- * Returns the currently selected "real" volume of the sound (takes fades and proximity into account).
- *
- * @return The adjusted volume of the sound.
- */
- public inline function getActualVolume():Float
- return (group != null ? group.getVolume() : 1.0) * _volume * _volumeAdjust;
-
- #if FLX_PITCH
- /**
- * Returns the currently selected "real" pitch of the sound.
- *
- * @return The adjusted pitch of the sound.
- */
- public inline function getActualPitch():Float
- return _realPitch;
- #end
-
- /**
- * Returns the actual time coming from the internal, can be used for detecting sync error.
- *
- * @return The actual time of the sound.
- */
- public inline function getActualTime():Float {
- get_time();
- return _time;
- }
-
- /**
- * An internal helper function used to attempt to start playing
- * the sound and populate the _channel variable.
- */
- function startSound(startTime:Float) @:privateAccess {
- if (_sound == null) return;
-
- _paused = false;
- _time = startTime;
- _lastTime = FlxG.game.getTicks();
- if (_channel == null || !_channel.__isValid || _source == null #if lime_cffi || _source.__backend.disposed #end)
- makeChannel();
-
- if (_channel != null) {
- #if FLX_PITCH
- _timeScaleAdjust = timeScaleBased ? FlxG.timeScale : 1.0;
- pitch = _pitch;
- #end
-
- updateTransform();
- _channel.__lastPeakTime = -10;
- _channel.__leftPeak = 0;
- _channel.__rightPeak = 0;
-
- #if lime_cffi _source.__backend.playing = true; #end
- _source.offset = 0;
- _source.currentTime = startTime + _offset;
- #if !lime_cffi _source.play(); #end
-
- looped = looped;
- loopTime = loopTime;
- endTime = endTime;
-
- active = true;
- }
- else {
- exists = false;
- active = false;
- }
- }
-
- function stopped() {
- onFinish.dispatch();
-
- if (onComplete != null) onComplete();
-
- if (looped) {
- cleanup(false);
- play(false, loopTime, endTime);
- }
- else cleanup(autoDestroy);
- }
-
- function source_looped() {
- if (onComplete != null) onComplete();
-
- if (!looped) {
- cleanup(autoDestroy);
- _lastTime = FlxG.game.getTicks();
- _time = loopTime;
- }
- else if (_source != null)
- _source.loops = 999;
- }
-
- /**
- * Internal event handler for ID3 info (i.e. fetching the song name).
- */
- function gotID3(_) {
- name = _sound.id3.songName;
- artist = _sound.id3.artist;
- _sound.removeEventListener(Event.ID3, gotID3);
- }
-
- #if FLX_SOUND_SYSTEM
- @:allow(flixel.system.frontEnds.SoundFrontEnd)
- function onFocus() if (!_alreadyPaused) {
- resume();
- _alreadyPaused = null;
- }
-
- @:allow(flixel.system.frontEnds.SoundFrontEnd)
- function onFocusLost() if (_alreadyPaused == null && !(_alreadyPaused = _paused)) pause();
- #end
-
- function set_group(value:FlxSoundGroup):FlxSoundGroup {
- if (value != null) value.add(this);
- else group.remove(this);
- return group;
- }
-
- inline function get_playing():Bool @:privateAccess
- return _source != null && _source.playing;
-
- inline function get_volume():Float
- return _volume;
-
- inline function set_volume(v:Float):Float {
- _volume = FlxMath.bound(v, 0, 10);
- updateTransform();
- return _volume;
- }
-
- inline function set_muted(v:Bool):Bool {
- muted = v;
- updateTransform();
- return muted;
- }
-
- inline function get_pan():Float return _pan;
- inline function set_pan(v:Float):Float {
- _pan = FlxMath.bound(v, -1, 1);
- updateTransform();
- return _pan;
- }
-
- inline function get_loaded():Bool
- return buffer != null;
-
- inline function get_streamed():Bool
- @:privateAccess return #if lime_vorbis _sound != null && _sound.__buffer.__srcVorbisFile != null #else false #end;
-
- inline function get_buffer():AudioBuffer
- @:privateAccess return _sound != null ? _sound.__buffer : null;
-
- inline function update_amplitude():Void @:privateAccess {
- if (_channel != null && _channel.__updatePeaks(get_time()) && _amplitudeUpdate) {
- _amplitudeUpdate = false;
- _amplitudeLeft = _channel.__leftPeak;
- _amplitudeRight = _channel.__rightPeak;
- }
- }
-
- inline function get_amplitudeLeft():Float {
- update_amplitude();
- return _amplitudeLeft;
- }
-
- inline function get_amplitudeRight():Float {
- update_amplitude();
- return _amplitudeRight;
- }
-
- inline function get_amplitude():Float {
- update_amplitude();
- return Math.max(_amplitudeLeft, _amplitudeRight);//if (stereo) (_amplitudeLeft + _amplitudeRight) * 0.5; else _amplitudeLeft;
- }
-
- inline function get_channels():Int
- @:privateAccess return (buffer != null) ? buffer.channels : 0;
-
- inline function get_stereo():Bool
- return channels > 1;
-
- #if FLX_PITCH
- inline function get_pitch():Float
- return _pitch;
-
- inline function set_pitch(v:Float):Float {
- _realPitch = (_pitch = v) * _timeScaleAdjust;
- if (_source != null) _source.pitch = _realPitch;
- return _pitch;
- }
- #end
-
- function set_looped(v:Bool):Bool {
- if (playing) _source.loops = v ? 999 : 0;
- return looped = v;
- }
-
- function set_loopTime(v:Float):Float {
- if (playing) _source.loopTime = v;
- return loopTime = v;
- }
-
- function set_endTime(v:Null):Null {
- if (playing) {
- if (v != null && v > 0 && v < _length) _source.length = v;
- else _source.length = null;
- }
- return endTime = v;
- }
-
- inline function getFakeTime():Float {
- if (_source.playing && _realPitch > 0 && _lastTime != null)
- return _time + (FlxG.game.getTicks() - _lastTime) * _realPitch * _timeInterpolation;
- else
- return _time;
- }
- function get_time():Float {
- if (_source == null || /*AudioManager.context == null*/funkin.backend.system.Main.audioDisconnected) return _time;
-
- final sourceTime = _source.currentTime - _source.offset - _offset;
- if (!_source.playing || _realPitch <= 0) {
- _lastTime = null;
- return _time = sourceTime;
- }
-
- final fakeTime = getFakeTime();
- if (sourceTime != _time) {
- _lastTime = FlxG.game.getTicks();
- if ((_timeInterpolation = 1 - Math.min(fakeTime - sourceTime, 1000) * 0.001) < 1 && _timeInterpolation > .9)
- return _time = fakeTime;
- else {
- _timeInterpolation = 1;
- return _time = sourceTime;
- }
- }
- else
- return fakeTime;
- }
-
- function set_time(time:Float):Float @:privateAccess {
- time = FlxMath.bound(time, _offset, length - 1);
- if (_channel != null && _realPitch > 0) {
- if (!_channel.__isValid) {
- cleanup(false, true);
- startSound(time);
- }
- else {
- _source.offset = 0;
- _source.currentTime = time + _offset;
- }
- }
-
- _lastTime = null;
- return _time = time;
- }
-
- function get_offset():Float return _offset;
- function set_offset(offset:Float):Float {
- if (_offset == (_offset = offset)) return offset;
- //time = time + _offset;
- return offset;
- }
-
- function get_length():Float return _length - _offset;
-
- //function get_latency():Float return _source != null ? _source.latency : 0;
-
- override function toString():String {
- return FlxStringUtil.getDebugString([
- LabelValuePair.weak("playing", playing),
- LabelValuePair.weak("time", time),
- LabelValuePair.weak("length", length),
- LabelValuePair.weak("volume", volume),
- LabelValuePair.weak("pitch", pitch)
- ]);
- }
-}
\ No newline at end of file
diff --git a/source/flixel/sound/FlxSoundGroup.hx b/source/flixel/sound/FlxSoundGroup.hx
deleted file mode 100644
index 9c2fcc1cbd..0000000000
--- a/source/flixel/sound/FlxSoundGroup.hx
+++ /dev/null
@@ -1,120 +0,0 @@
-package flixel.sound;
-
-/**
- * A way of grouping sounds for things such as collective volume control
- */
-class FlxSoundGroup
-{
- /**
- * The sounds in this group
- */
- public var sounds:Array = [];
-
- /**
- * The volume of this group
- */
- public var volume(default, set):Float;
-
- /**
- * Whether or not this group is muted
- */
- public var muted(default, set):Bool;
-
- /**
- * Create a new sound group
- * @param volume The initial volume of this group
- */
- public function new(volume:Float = 1)
- {
- this.volume = volume;
- }
-
- /**
- * Add a sound to this group, will remove the sound from any group it is currently in
- * @param sound The sound to add to this group
- * @return True if sound was successfully added, false otherwise
- */
- public function add(sound:FlxSound):Bool
- {
- if (!sounds.contains(sound))
- {
- // remove from prev group
- if (sound.group != null)
- sound.group.sounds.remove(sound);
-
- sounds.push(sound);
- @:bypassAccessor
- sound.group = this;
- sound.updateTransform();
- return true;
- }
- return false;
- }
-
- /**
- * Remove a sound from this group
- * @param sound The sound to remove
- * @return True if sound was successfully removed, false otherwise
- */
- public function remove(sound:FlxSound):Bool
- {
- if (sounds.contains(sound))
- {
- @:bypassAccessor
- sound.group = null;
- sounds.remove(sound);
- sound.updateTransform();
- return true;
- }
- return false;
- }
-
- /**
- * Call this function to pause all sounds in this group.
- * @since 4.3.0
- */
- public function pause():Void
- {
- for (sound in sounds)
- sound.pause();
- }
-
- /**
- * Unpauses all sounds in this group. Only works on sounds that have been paused.
- * @since 4.3.0
- */
- public function resume():Void
- {
- for (sound in sounds)
- sound.resume();
- }
-
- /**
- * Returns the volume of this group, taking `muted` in account.
- * @return The volume of the group or 0 if the group is muted.
- */
- public function getVolume():Float
- {
- return muted ? 0.0 : volume;
- }
-
- function set_volume(volume:Float):Float
- {
- this.volume = volume;
- for (sound in sounds)
- {
- sound.updateTransform();
- }
- return volume;
- }
-
- function set_muted(value:Bool):Bool
- {
- muted = value;
- for (sound in sounds)
- {
- sound.updateTransform();
- }
- return muted;
- }
-}
diff --git a/source/funkin/backend/assets/MultiFramesCollection.hx b/source/funkin/backend/assets/MultiFramesCollection.hx
index 2a03c64cdd..d4bed77bf2 100644
--- a/source/funkin/backend/assets/MultiFramesCollection.hx
+++ b/source/funkin/backend/assets/MultiFramesCollection.hx
@@ -47,7 +47,7 @@ class MultiFramesCollection extends FlxFramesCollection
public function addFrames(collection:FlxFramesCollection) {
if (collection == null || collection.frames == null) return;
- collection.parent.useCount++;
+ collection.parent.incrementUseCount();
parentedFrames.push(collection);
for(f in collection.frames) {
@@ -63,7 +63,7 @@ class MultiFramesCollection extends FlxFramesCollection
if(parentedFrames != null) {
for(collection in parentedFrames) {
if(collection != null)
- collection.parent.useCount--;
+ collection.parent.decrementUseCount();
}
parentedFrames = null;
}
diff --git a/source/funkin/backend/shaders/BlendModeEffect.hx b/source/funkin/backend/shaders/BlendModeEffect.hx
deleted file mode 100644
index 2865829285..0000000000
--- a/source/funkin/backend/shaders/BlendModeEffect.hx
+++ /dev/null
@@ -1,36 +0,0 @@
-package funkin.backend.shaders;
-
-import flixel.util.FlxColor;
-import openfl.display.ShaderParameter;
-
-@:dox(hide)
-typedef BlendModeShader =
-{
- var uBlendColor:ShaderParameter;
-}
-
-@:dox(hide)
-class BlendModeEffect
-{
- public var shader(default, null):BlendModeShader;
-
- @:isVar
- public var color(default, set):FlxColor;
-
- public function new(shader:BlendModeShader, color:FlxColor):Void
- {
- shader.uBlendColor.value = [];
- this.shader = shader;
- this.color = color;
- }
-
- function set_color(color:FlxColor):FlxColor
- {
- shader.uBlendColor.value[0] = color.redFloat;
- shader.uBlendColor.value[1] = color.greenFloat;
- shader.uBlendColor.value[2] = color.blueFloat;
- shader.uBlendColor.value[3] = color.alphaFloat;
-
- return this.color = color;
- }
-}
diff --git a/source/funkin/backend/shaders/FunkinShader.hx b/source/funkin/backend/shaders/FunkinShader.hx
index 281616b03b..1853813dde 100644
--- a/source/funkin/backend/shaders/FunkinShader.hx
+++ b/source/funkin/backend/shaders/FunkinShader.hx
@@ -1,17 +1,20 @@
package funkin.backend.shaders;
+import haxe.Exception;
+import haxe.io.Path;
import flixel.graphics.FlxGraphic;
import flixel.system.FlxAssets.FlxShader;
import flixel.util.FlxSignal.FlxTypedSignal;
-import haxe.Exception;
+import flixel.util.FlxStringUtil;
import hscript.IHScriptCustomBehaviour;
-import lime.utils.Float32Array;
import openfl.display.BitmapData;
import openfl.display.ShaderInput;
import openfl.display.ShaderParameter;
import openfl.display.ShaderParameterType;
+import openfl.display.Shader;
import openfl.display3D._internal.GLProgram;
import openfl.display3D._internal.GLShader;
+import openfl.display3D.Program3D;
import openfl.utils._internal.Log;
using StringTools;
@@ -20,237 +23,144 @@ using StringTools;
@:access(openfl.display.ShaderInput)
@:access(openfl.display.ShaderParameter)
class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
+ #if REGION /* Backward Compatibility */
private static var __instanceFields = Type.getInstanceFields(FunkinShader);
private static var FRAGMENT_SHADER = 0;
private static var VERTEX_SHADER = 1;
- public var onGLUpdate:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
+ public var glslVer(get, set):String;
+ inline function get_glslVer():String return glVersion;
+ inline function set_glslVer(v:String):String return glVersion = v;
+
+ public var glRawFragmentSource(get, set):String;
+ inline function get_glRawFragmentSource():String return __glFragmentSourceRaw;
+ inline function set_glRawFragmentSource(v:String):String return __glFragmentSourceRaw = v;
+
+ public var glRawVertexSource(get, set):String;
+ inline function get_glRawVertexSource():String return __glVertexSourceRaw;
+ inline function set_glRawVertexSource(v:String):String return __glVertexSourceRaw = v;
+
+ // Unused... cne-openfl uses a different system
+ var __cancelNextProcessGLData:Bool = false;
public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>();
+ #end
- public var glslVer:String = Flags.DEFAULT_GLSL_VERSION;
- public var fileName:String = "FunkinShader";
- public var fragFileName:String = "FunkinShader";
- public var vertFileName:String = "FunkinShader";
+ public static function getShaderCode(key:String, isFragment = true):Null {
+ var path = "shaders/" + key;
+ key = Path.withoutExtension(key);
- public var shaderPrefix:String = "";
- public var fragmentPrefix:String = "";
- public var vertexPrefix:String = "";
+ final ext = Path.extension(path);
+ if (ext == "") path = path + (isFragment ? ".frag" : ".vert");
+ else isFragment = ext != "vert";
- /**
- * Creates a new shader from the specified fragment and vertex source.
- * Accepts `#pragma header`.
- * @param frag Fragment source (pass `null` to use default)
- * @param vert Vertex source (pass `null` to use default)
- * @param glslVer Version of GLSL to use (defaults to 120)
- */
- public override function new(frag:String, vert:String, glslVer:String = null) {
- if (glslVer == null) glslVer = Flags.DEFAULT_GLSL_VERSION;
- if (frag == null) frag = ShaderTemplates.defaultFragmentSource;
- if (vert == null) vert = ShaderTemplates.defaultVertexSource;
- this.glFragmentSource = frag;
- this.glVertexSource = vert;
-
- this.glslVer = glslVer;
- super();
+ path = Paths.getPath(path);
+ return Assets.exists(path) ? Assets.getText(path) : null;
}
- static var IMPORT_REGEX = ~/#import\s+<(.*)>/;
+ private static function processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map):String
+ return Shader.processGLSLText(_processGLSLText(source, glVersion, isFragment, pragmas), glVersion, isFragment);
+
+ private static function _processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map):String {
+ if (pragmas != null) {
+ final pragmaKeyword = ~/#pragma\s+(\w+)/g;
+ source = pragmaKeyword.map(source, (_) -> {
+ var name = pragmaKeyword.matched(1), pragma:String;
+ if (pragmas.exists(name)) pragma = pragmas.get(name);
+ else {
+ if (name != "header" && name != "body") return '#pragma $name';
+ pragma = "";
+ }
+ return _processGLSLText(pragma, glVersion, isFragment, pragmas);
+ });
+ }
- private function processImports(value:String, type:Int):String
- {
- while(IMPORT_REGEX.match(value))
- {
- var importPath = IMPORT_REGEX.matched(1);
- var importSource = Assets.getText("assets/shaders/" + importPath);
- if(importSource == null) {
- var fileName = type == FRAGMENT_SHADER ? fragFileName : vertFileName;
+ inline function tryGetShaderCode(key:String) {
+ final s = getShaderCode(key, isFragment);
+ if (s == null) {
Logs.traceColored([
Logs.logText('[Shader] ', RED),
- Logs.logText('Failed to import shader ${importPath} in ${fileName}', RED),
+ Logs.logText('Failed to import shader $key', RED),
]);
- } else {
- value = value.replace(IMPORT_REGEX.matched(0), importSource);
+ return "";
}
+ return s;
}
- return value;
- }
- static var ERROR_POS_REGEX = ~/(\d+):(\d+): (.*)/g;
- static var ERROR_REGEX = ~/ERROR: (\d+):(\d+): (.*)/g;
- static var ERROR_REGEX_2 = ~/(\d+)\((\d+)\) : error ([^:]+): (.*)/g;
- @:noCompletion private override function __createGLShader(source:String, type:Int):GLShader
- {
- var gl = __context.gl;
-
- var shader = gl.createShader(type);
- gl.shaderSource(shader, source);
- gl.compileShader(shader);
- var shaderInfoLog = gl.getShaderInfoLog(shader);
- var hasInfoLog = shaderInfoLog != null && StringTools.trim(shaderInfoLog) != "";
- var compileStatus = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
-
- if (hasInfoLog || compileStatus == 0)
- {
- var isVertexShader = type == gl.VERTEX_SHADER;
- var messageBuf = new StringBuf();
- messageBuf.add((compileStatus == 0) ? "Error" : "Info");
- if(isVertexShader) {
- messageBuf.add(" compiling vertex shader");
- if(vertFileName != null && vertFileName.length > 0) {
- messageBuf.add(" (" + vertFileName + ")");
- }
- } else {
- messageBuf.add(" compiling fragment shader");
- if(fragFileName != null && fragFileName.length > 0) {
- messageBuf.add(" (" + fragFileName + ")");
- }
- }
- messageBuf.add("\n");
- var errorPositions = [];
- var regex = null;
- var tmp = shaderInfoLog;
- if(shaderInfoLog.contains(" : error ")) {
- regex = ERROR_REGEX_2;
-
- while(regex.match(tmp)) {
- errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(4)));
- tmp = regex.matchedRight();
- }
- } else if(shaderInfoLog.contains("ERROR: ")) {
- regex = ERROR_REGEX;
+ final includeKeyword = ~/#include ['"](.+)['"]/g;
+ final importKeyword = ~/#import\s+<(.*)>/g;
+ source = importKeyword.map(source, (_) ->
+ return _processGLSLText(tryGetShaderCode(importKeyword.matched(1)), glVersion, isFragment, pragmas) ?? "");
- while(regex.match(tmp)) {
- errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3)));
- tmp = regex.matchedRight();
- }
- } else {
- regex = ERROR_POS_REGEX;
+ return source = includeKeyword.map(source, (_) ->
+ return _processGLSLText(tryGetShaderCode(includeKeyword.matched(1)), glVersion, isFragment, pragmas) ?? "");
+ }
- while(regex.match(tmp)) {
- errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3)));
- tmp = regex.matchedRight();
- }
- }
- var splitSource = source.split("\n");
- for(error in errorPositions) {
- messageBuf.add("ERROR: Line: " + error.line);
- if(error.column > 0) {
- messageBuf.add(", Column: " + error.column);
- }
- messageBuf.add(", " + error.message);
- if(error.line < splitSource.length) {
- messageBuf.add("\nLine: ");
- messageBuf.add(splitSource[error.line-1].trim());
- }
- messageBuf.add("\n\n");
- }
- var hasErrorPosition = errorPositions.length > 0;
- if(hasErrorPosition) {
- messageBuf.add("Raw shader info log:\n");
- }
- messageBuf.add(shaderInfoLog);
- messageBuf.add("\n");
- messageBuf.add(source);
+ private static var __defaultsAvailable:Bool;
+ private static var __glFragmentSourceDefault:String;
+ private static var __glVertexSourceDefault:String;
+ private static var __glFragmentPragmasDefault:Map;
+ private static var __glVertexPragmasDefault:Map;
+ private static var __glFragmentExtensionsDefault:Array;
+ private static var __glVertexExtensionsDefault:Array;
- var message = messageBuf.toString();
- if (compileStatus == 0) Log.error(message);
- else if (hasInfoLog) Log.debug(message);
- }
+ public var onGLUpdate:FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
- return shader;
- }
+ public var fileName:String = "FunkinShader";
+ public var fragFileName:String = "FunkinShader";
+ public var vertFileName:String = "FunkinShader";
- @:noCompletion private override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram
- {
- var program:GLProgram = null;
- try
- {
- var gl = __context.gl;
-
- var vertexShader = __createGLShader(vertexSource, gl.VERTEX_SHADER);
- var fragmentShader = __createGLShader(fragmentSource, gl.FRAGMENT_SHADER);
-
- program = gl.createProgram();
-
- // Fix support for drivers that don't draw if attribute 0 is disabled
- for (param in __paramFloat)
- {
- if (param.name.indexOf("Position") > -1 && StringTools.startsWith(param.name, "openfl_"))
- {
- gl.bindAttribLocation(program, 0, param.name);
- break;
- }
- }
+ public var shaderPrefix:String = "";
+ public var fragmentPrefix:String = "";
+ public var vertexPrefix:String = "";
- gl.attachShader(program, vertexShader);
- gl.attachShader(program, fragmentShader);
- gl.linkProgram(program);
-
- if (gl.getProgramParameter(program, gl.LINK_STATUS) == 0)
- {
- var messageBuf = new StringBuf();
- messageBuf.add("Unable to initialize the shader program");
- messageBuf.add("\n");
- messageBuf.add(gl.getProgramInfoLog(program));
- var message = messageBuf.toString();
- Log.error(message);
- }
+ private var __immediate:Bool;
+
+ public function new(?fragmentSource:String, ?vertexSource:String, ?version:String,
+ ?fragmentExtensions:Array, ?vertexExtensions:Array, immediate = false
+ ) {
+ if (!__defaultsAvailable) {
+ __glFragmentSourceDefault = __glFragmentSourceRaw;
+ __glVertexSourceDefault = __glVertexSourceRaw;
+ __glFragmentPragmasDefault = __glFragmentPragmas.copy();
+ __glVertexPragmasDefault = __glVertexPragmas.copy();
+ __glFragmentExtensionsDefault = __glFragmentExtensions ?? [];
+ __glVertexExtensionsDefault = __glVertexExtensions ?? [];
+ __defaultsAvailable = true;
}
- catch (error:Dynamic)
- {
- Logs.traceColored([
- Logs.logText('[Shader] ', BLUE),
- Logs.logText('Failed to compile shader ${fileName}: ', RED),
- Logs.logText(Std.string(error))
- ], TRACE);
- }
- return program;
- return program;
- }
+ __immediate = immediate;
- var glRawFragmentSource:String;
- var glRawVertexSource:String;
-
- @:noCompletion override private function set_glFragmentSource(value:String):String
- {
- if(value == null)
- value = ShaderTemplates.defaultFragmentSource;
- glRawFragmentSource = value;
- value = processImports(value, FRAGMENT_SHADER);
- value = value.replace("#pragma header", ShaderTemplates.fragHeader).replace("#pragma body", ShaderTemplates.fragBody);
- if (value != __glFragmentSource)
- {
- __glSourceDirty = true;
- }
+ if (version != null) glVersion = version;
+ if (vertexExtensions != null) glVertexExtensions = vertexExtensions;
+ if (fragmentExtensions != null) glFragmentExtensions = fragmentExtensions;
+ if (vertexSource != null) glVertexSource = vertexSource;
+ if (fragmentSource != null) glFragmentSource = fragmentSource;
- return __glFragmentSource = value;
- }
+ super();
- @:noCompletion override private function set_glVertexSource(value:String):String
- {
- if(value == null)
- value = ShaderTemplates.defaultVertexSource;
- glRawVertexSource = value;
- value = processImports(value, VERTEX_SHADER);
-
- var useBackCompat:Bool = true;
- for (regex in ShaderTemplates.vertBackCompatVarList) if (!regex.match(value)) {
- useBackCompat = false;
- break;
+ if (!__isGenerated) {
+ __isGenerated = true;
+ __init();
}
+ }
- var header = useBackCompat ? ShaderTemplates.vertHeaderBackCompat : ShaderTemplates.vertHeader;
- var body = useBackCompat ? ShaderTemplates.vertBodyBackCompat : ShaderTemplates.vertBody;
+ public function loadShader(name:String, ?version:String, immediate = false):FunkinShader {
+ final fragment = getShaderCode(name, true), vertex = getShaderCode(name, false);
- value = value.replace("#pragma header", header).replace("#pragma body", body);
+ glVersion = version;
+ glVertexSource = vertex ?? __glVertexSourceDefault;
+ glFragmentSource = fragment ?? __glFragmentSourceDefault;
- if (value != __glVertexSource)
- {
- __glSourceDirty = true;
- }
+ if (immediate) __init();
+ return this;
+ }
- return __glVertexSource = value;
+ override function __initGL():Void {
+ if (__immediate) {
+ __context = FlxG.stage.context3D;
+ __enable();
+ }
+ super.__initGL();
}
override function __updateGL():Void {
@@ -258,276 +168,14 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
super.__updateGL();
}
- @:noCompletion private override function __initGL():Void
- {
- if (__glSourceDirty || __paramBool == null)
- {
- __glSourceDirty = false;
- program = null;
-
- __inputBitmapData = new Array();
- __paramBool = new Array();
- __paramFloat = new Array();
- __paramInt = new Array();
-
- __processGLData(glVertexSource, "attribute");
- __processGLData(glVertexSource, "uniform");
- __processGLData(glFragmentSource, "uniform");
- }
-
- if (__context != null && program == null)
- {
- var prefixBuf = new StringBuf();
- prefixBuf.add('#version ${glslVer}\n');
- prefixBuf.add(shaderPrefix);
-
- var gl = __context.gl;
-
- prefixBuf.add("#ifdef GL_ES\n");
- if (precisionHint == FULL) {
- prefixBuf.add("#ifdef GL_FRAGMENT_PRECISION_HIGH\n");
- prefixBuf.add("precision highp float;\n");
- prefixBuf.add("#else\n");
- prefixBuf.add("precision mediump float;\n");
- prefixBuf.add("#endif\n");
- } else {
- prefixBuf.add("precision lowp float;\n");
- }
- prefixBuf.add("#endif\n");
-
- var prefix = prefixBuf.toString();
-
- var vertex = prefix + vertexPrefix + glVertexSource;
- var fragment = prefix + fragmentPrefix + glFragmentSource;
-
- var id = vertex + fragment;
-
- if (__context.__programs.exists(id))
- {
- program = __context.__programs.get(id);
- }
- else
- {
- program = __context.createProgram(GLSL);
- program.__glProgram = __createGLProgram(vertex, fragment);
- __context.__programs.set(id, program);
- }
-
- if (program != null)
- {
- glProgram = program.__glProgram;
-
- for (input in __inputBitmapData) {
-
- if (input.__isUniform) {
- input.index = gl.getUniformLocation(glProgram, input.name);
- } else {
- input.index = gl.getAttribLocation(glProgram, input.name);
- }
- }
-
- for (parameter in __paramBool) {
- if (parameter.__isUniform) {
- parameter.index = gl.getUniformLocation(glProgram, parameter.name);
- } else {
- parameter.index = gl.getAttribLocation(glProgram, parameter.name);
- }
- }
-
- for (parameter in __paramFloat) {
- if (parameter.__isUniform) {
- parameter.index = gl.getUniformLocation(glProgram, parameter.name);
- } else {
- parameter.index = gl.getAttribLocation(glProgram, parameter.name);
- }
- }
-
- for (parameter in __paramInt) {
- if (parameter.__isUniform) {
- parameter.index = gl.getUniformLocation(glProgram, parameter.name);
- } else {
- parameter.index = gl.getAttribLocation(glProgram, parameter.name);
- }
- }
- }
- // initInstance(vertex, fragment); // btw make sure to disable the prefixes for ._isInstance
- }
- }
-
- var __cancelNextProcessGLData:Bool = false;
- @:noCompletion private override function __processGLData(source:String, storageType:String):Void
- {
- onProcessGLData.dispatch(source, storageType);
- if (__cancelNextProcessGLData != (__cancelNextProcessGLData = false))
- return;
- var lastMatch = 0, position, regex, name, type;
-
- if (storageType == "uniform")
- {
- regex = ~/uniform ([A-Za-z0-9]+) ([A-Za-z0-9_]+)/;
- }
- else
- {
- regex = ~/attribute ([A-Za-z0-9]+) ([A-Za-z0-9_]+)/;
- }
-
- while (regex.matchSub(source, lastMatch))
- {
- type = regex.matched(1);
- name = regex.matched(2);
-
- if (StringTools.startsWith(name, "gl_"))
- {
- continue;
- }
-
- var isUniform = (storageType == "uniform");
- registerParameter(name, type, isUniform);
-
- position = regex.matchedPos();
- lastMatch = position.pos + position.len;
- }
- }
-
- function registerParameter(name:String, type:String, isUniform:Bool):Void
- {
- if (StringTools.startsWith(type, "sampler"))
- {
- var input = new ShaderInput();
- input.name = name;
- input.__isUniform = isUniform;
- __inputBitmapData.push(input);
-
- switch (name)
- {
- case "openfl_Texture":
- __texture = input;
- case "bitmap":
- __bitmap = input;
- default:
- }
-
- Reflect.setField(__data, name, input);
- try{Reflect.setField(this, name, input);} catch(e) {}
- }
- else if (!Reflect.hasField(__data, name) || Reflect.field(__data, name) == null)
- {
- var parameterType:ShaderParameterType = switch (type)
- {
- case "bool": BOOL;
- case "double", "float": FLOAT;
- case "int", "uint": INT;
- case "bvec2": BOOL2;
- case "bvec3": BOOL3;
- case "bvec4": BOOL4;
- case "ivec2", "uvec2": INT2;
- case "ivec3", "uvec3": INT3;
- case "ivec4", "uvec4": INT4;
- case "vec2", "dvec2": FLOAT2;
- case "vec3", "dvec3": FLOAT3;
- case "vec4", "dvec4": FLOAT4;
- case "mat2", "mat2x2": MATRIX2X2;
- case "mat2x3": MATRIX2X3;
- case "mat2x4": MATRIX2X4;
- case "mat3x2": MATRIX3X2;
- case "mat3", "mat3x3": MATRIX3X3;
- case "mat3x4": MATRIX3X4;
- case "mat4x2": MATRIX4X2;
- case "mat4x3": MATRIX4X3;
- case "mat4", "mat4x4": MATRIX4X4;
- default: null;
- }
+ public function hget(name:String):Dynamic {
+ if (__glSourceDirty || __data == null) __init();
- var length = switch (parameterType)
- {
- case BOOL2, INT2, FLOAT2: 2;
- case BOOL3, INT3, FLOAT3: 3;
- case BOOL4, INT4, FLOAT4, MATRIX2X2: 4;
- case MATRIX3X3: 9;
- case MATRIX4X4: 16;
- default: 1;
- }
+ if (thisHasField(name) || thisHasField('get_${name}')) return Reflect.getProperty(this, name);
+ else if (!Reflect.hasField(__data, name)) return null;
- var arrayLength = switch (parameterType)
- {
- case MATRIX2X2: 2;
- case MATRIX3X3: 3;
- case MATRIX4X4: 4;
- default: 1;
- }
+ final field:Dynamic = Reflect.field(__data, name);
- switch (parameterType)
- {
- case BOOL, BOOL2, BOOL3, BOOL4:
- var parameter = new ShaderParameter();
- parameter.name = name;
- parameter.type = parameterType;
- parameter.__arrayLength = arrayLength;
- parameter.__isBool = true;
- parameter.__isUniform = isUniform;
- parameter.__length = length;
- __paramBool.push(parameter);
-
- if (name == "openfl_HasColorTransform")
- {
- __hasColorTransform = parameter;
- }
-
- Reflect.setField(__data, name, parameter);
- try{Reflect.setField(this, name, parameter);} catch(e) {}
-
- case INT, INT2, INT3, INT4:
- var parameter = new ShaderParameter();
- parameter.name = name;
- parameter.type = parameterType;
- parameter.__arrayLength = arrayLength;
- parameter.__isInt = true;
- parameter.__isUniform = isUniform;
- parameter.__length = length;
- __paramInt.push(parameter);
- Reflect.setField(__data, name, parameter);
- try{Reflect.setField(this, name, parameter);} catch(e) {}
-
- default:
- var parameter = new ShaderParameter();
- parameter.name = name;
- parameter.type = parameterType;
- parameter.__arrayLength = arrayLength;
- #if lime
- if (arrayLength > 0) parameter.__uniformMatrix = new Float32Array(arrayLength * arrayLength);
- #end
- parameter.__isFloat = true;
- parameter.__isUniform = isUniform;
- parameter.__length = length;
- __paramFloat.push(parameter);
-
- if (StringTools.startsWith(name, "openfl_"))
- {
- switch (name)
- {
- case "openfl_Alpha": __alpha = parameter;
- case "openfl_ColorMultiplier": __colorMultiplier = parameter;
- case "openfl_ColorOffset": __colorOffset = parameter;
- case "openfl_Matrix": __matrix = parameter;
- case "openfl_Position": __position = parameter;
- case "openfl_TextureCoord": __textureCoord = parameter;
- case "openfl_TextureSize": __textureSize = parameter;
- default:
- }
- }
-
- Reflect.setField(__data, name, parameter);
- try{Reflect.setField(this, name, parameter);} catch(e) {}
- }
- }
- }
-
- public function hget(name:String):Dynamic {
- if (__instanceFields.contains(name) || __instanceFields.contains('get_${name}'))
- return Reflect.getProperty(this, name);
- if (!Reflect.hasField(data, name))
- return null;
- var field:Dynamic = Reflect.field(data, name);
var cl:String = Type.getClassName(Type.getClass(field));
// little problem we are facing boys...
@@ -546,17 +194,19 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
}
public function hset(name:String, val:Dynamic):Dynamic {
- if (__instanceFields.contains(name) || __instanceFields.contains('set_${name}')) {
+ if (__glSourceDirty || __data == null) __init();
+
+ if (thisHasField(name) || thisHasField('set_${name}')) {
Reflect.setProperty(this, name, val);
return val;
}
-
- if (!Reflect.hasField(data, name)) {
- Reflect.setField(data, name, val);
+ else if (!Reflect.hasField(__data, name)) {
+ // ??? huh
+ Reflect.setField(__data, name, val);
return val;
}
- var field = Reflect.field(data, name);
+ var field = Reflect.field(__data, name);
var cl = Type.getClassName(Type.getClass(field));
var isNotNull = val != null;
// cant do "field is ShaderInput" for some reason
@@ -598,67 +248,87 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
return val;
}
-}
-class ShaderTemplates {
- public static final fragHeader:String = "varying float openfl_Alphav;
-varying vec4 openfl_ColorMultiplierv;
-varying vec4 openfl_ColorOffsetv;
-varying vec2 openfl_TextureCoordv;
+ override function __buildSourcePrefix(isFragment:Bool):String {
+ var result = super.__buildSourcePrefix(isFragment) + '\n$shaderPrefix';
+ return isFragment ? result + '\n$fragmentPrefix' : result + '\n$vertexPrefix';
+ }
-uniform bool openfl_HasColorTransform;
-uniform vec2 openfl_TextureSize;
-uniform sampler2D bitmap;
+ override function set_glFragmentExtensions(value:Array):Array {
+ if (value == null) value = __glFragmentExtensionsDefault;
+ if (value != __glFragmentExtensions) __glSourceDirty = true;
+ return __glFragmentExtensions = value;
+ }
-uniform bool hasTransform;
-uniform bool hasColorTransform;
+ override function set_glVertexExtensions(value:Array):Array {
+ if (value == null) value = __glVertexExtensionsDefault;
+ if (value != __glVertexExtensions) __glSourceDirty = true;
+ return __glVertexExtensions = value;
+ }
+
+ override function set_glVersion(value:Null):String {
+ if (value == null || value == "") value = Flags.DEFAULT_GLSL_VERSION;
+ if ((__glVersionRaw = value) != __glVersion) {
+ __glSourceDirty = true;
+ if (__glVertexSourceRaw != null) __glVertexSource = processGLSLText(__glVertexSourceRaw, value, false, __glVertexPragmas);
+ if (__glFragmentSourceRaw != null) __glFragmentSource = processGLSLText(__glFragmentSourceRaw, value, true, __glFragmentPragmas);
+ }
-vec4 applyFlixelEffects(vec4 color) {
- if(!hasTransform) {
- return color;
+ return __glVersion = value;
}
- if(color.a == 0.0) {
- return vec4(0.0, 0.0, 0.0, 0.0);
+ override function set_glFragmentSource(value:String):String {
+ if (value == null || value == "") value = __glFragmentSourceDefault;
+ if ((__glFragmentSourceRaw = value) != null) {
+ if (__glVersion != (__glVersion = Shader.getGLSLTextVersion(value, __glVersionRaw)))
+ __glSourceDirty = true;
+
+ value = processGLSLText(value, __glVersion, true, __glFragmentPragmas);
+ }
+
+ if (value != __glFragmentSource) __glSourceDirty = true;
+ return __glFragmentSource = value;
}
- if(!hasColorTransform) {
- return color * openfl_Alphav;
+ override function set_glVertexSource(value:String):String {
+ if (value == null || value == "") value = __glVertexSourceDefault;
+ if ((__glVertexSourceRaw = value) != null) {
+ if (__glVersion != (__glVersion = Shader.getGLSLTextVersion(value, __glVersionRaw)))
+ __glSourceDirty = true;
+
+ value = processGLSLText(value, __glVersion, false, __glVertexPragmas);
+ }
+
+ if (value != __glVertexSource) __glSourceDirty = true;
+ return __glVertexSource = value;
}
- color.rgb = color.rgb / color.a;
- color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
+ override function set_glFragmentPragmas(value:Map):Map {
+ if (value == null) value = __glFragmentPragmasDefault;
+ if (value != __glFragmentPragmas)
+ __glSourceDirty = true;
- if(color.a > 0.0) {
- return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
+ return __glFragmentPragmas = value;
}
- return vec4(0.0, 0.0, 0.0, 0.0);
-}
-vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) {
- vec4 color = texture2D(bitmap, coord);
- return applyFlixelEffects(color);
-}
+ override function set_glVertexPragmas(value:Map):Map {
+ if (value == null) value = __glVertexPragmasDefault;
+ if (value != __glVertexPragmas)
+ __glSourceDirty = true;
-uniform vec4 _camSize;
+ return __glVertexPragmas = value;
+ }
-float map(float value, float min1, float max1, float min2, float max2) {
- return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
-}
+ function registerParameter(name:String, type:String, isUniform:Bool):Void {
+ __registerParameter(name, Program3D.getParameterTypeFromGLString(type, 1), 1, -1, isUniform, false, null);
+ }
-vec2 getCamPos(vec2 pos) {
- vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
- return vec2(map(pos.x, size.x, size.x + size.z, 0.0, 1.0), map(pos.y, size.y, size.y + size.w, 0.0, 1.0));
+ public function toString():String
+ return FlxStringUtil.getDebugString([for (field in Reflect.fields(data)) LabelValuePair.weak(field, Reflect.field(data, field))]);
}
-vec2 camToOg(vec2 pos) {
- vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
- return vec2(map(pos.x, 0.0, 1.0, size.x, size.x + size.z), map(pos.y, 0.0, 1.0, size.y, size.y + size.w));
-}
-vec4 textureCam(sampler2D bitmap, vec2 pos) {
- return flixel_texture2D(bitmap, camToOg(pos));
-}";
- public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);";
+class ShaderTemplates {
+ #if REGION /* Backward Compatibility */
public static final vertHeader:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
@@ -677,34 +347,90 @@ uniform vec2 openfl_TextureSize;
attribute float alpha;
attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
+
uniform bool hasColorTransform;";
- public static final vertBody:String = "openfl_Alphav = openfl_Alpha;
-openfl_TextureCoordv = openfl_TextureCoord;
+ public static final vertBody:String = "openfl_TextureCoordv = openfl_TextureCoord;
-if(openfl_HasColorTransform) {
- openfl_ColorMultiplierv = openfl_ColorMultiplier;
- openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
+if (hasColorTransform) {
+ openfl_Alphav = openfl_Alpha * colorMultiplier.a;
+ if (openfl_HasColorTransform) {
+ openfl_ColorOffsetv = (openfl_ColorOffset / 255.0 * colorMultiplier) + (colorOffset / 255.0);
+ openfl_ColorMultiplierv = openfl_ColorMultiplier * vec4(colorMultiplier.rgb, 1.0);
+ }
+ else {
+ openfl_ColorOffsetv = colorOffset / 255.0;
+ openfl_ColorMultiplierv = vec4(colorMultiplier.rgb, 1.0);
+ }
+}
+else {
+ openfl_Alphav = openfl_Alpha * alpha;
+ if (openfl_HasColorTransform) {
+ openfl_ColorOffsetv = (openfl_ColorOffset + colorOffset) / 255.0;
+ openfl_ColorMultiplierv = openfl_ColorMultiplier;
+ }
+ else {
+ openfl_ColorOffsetv = colorOffset / 255.0;
+ openfl_ColorMultiplierv = vec4(1.0);
+ }
+}";
+
+ public static final fragHeader:String = "varying float openfl_Alphav;
+varying vec4 openfl_ColorMultiplierv;
+varying vec4 openfl_ColorOffsetv;
+varying vec2 openfl_TextureCoordv;
+
+uniform bool openfl_HasColorTransform;
+uniform vec2 openfl_TextureSize;
+uniform sampler2D bitmap;
+
+uniform bool hasTransform;
+uniform bool hasColorTransform;
+
+vec4 apply_flixel_transform(vec4 color) {
+ if (!hasTransform) return color;
+ else if (color.a <= 0.0 || openfl_Alphav == 0.0) return vec4(0.0);
+
+ color.rgb /= color.a;
+ color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0);
+ return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
}
-openfl_Alphav = openfl_Alpha * alpha;
+#define applyFlixelEffects(color) apply_flixel_transform(color)
-if(hasColorTransform) {
- openfl_ColorOffsetv = colorOffset / 255.0;
- openfl_ColorMultiplierv = colorMultiplier;
+vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) {
+ return apply_flixel_transform(texture2D(bitmap, coord));
}
-gl_Position = openfl_Matrix * openfl_Position;";
+uniform vec4 _camSize;
+
+float map(float value, float min1, float max1, float min2, float max2) {
+ return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
+}
+
+vec2 getCamPos(vec2 pos) {
+ vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
+ return vec2(map(pos.x, size.x, size.x + size.z, 0.0, 1.0), map(pos.y, size.y, size.y + size.w, 0.0, 1.0));
+}
+vec2 camToOg(vec2 pos) {
+ vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize);
+ return vec2(map(pos.x, 0.0, 1.0, size.x, size.x + size.z), map(pos.y, 0.0, 1.0, size.y, size.y + size.w));
+}
+vec4 textureCam(sampler2D bitmap, vec2 pos) {
+ return flixel_texture2D(bitmap, camToOg(pos));
+}";
-// TODO: make this ignore comments
-public static final vertBackCompatVarList:Array = [
- ~/attribute float alpha/,
- ~/attribute vec4 colorMultiplier/,
- ~/attribute vec4 colorOffset/,
- ~/uniform bool hasColorTransform/
-];
+ public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);
+if (gl_FragColor.a == 0.0) discard;";
-public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
+ public static final vertBackCompatVarList:Array = [
+ ~/attribute float alpha/,
+ ~/attribute vec4 colorMultiplier/,
+ ~/attribute vec4 colorOffset/,
+ ~/uniform bool hasColorTransform/
+ ];
+
+ public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
attribute vec4 openfl_Position;
@@ -719,7 +445,7 @@ uniform mat4 openfl_Matrix;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;";
-public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
+ public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
openfl_TextureCoordv = openfl_TextureCoord;
if(openfl_HasColorTransform) {
@@ -728,18 +454,7 @@ if(openfl_HasColorTransform) {
}
gl_Position = openfl_Matrix * openfl_Position;";
-
- public static final defaultVertexSource:String = "#pragma header
-
-void main(void) {
- #pragma body
-}";
-
- public static final defaultFragmentSource:String = "#pragma header
-
-void main(void) {
- #pragma body
-}";
+ #end
}
class ShaderTypeException extends Exception {
@@ -753,16 +468,4 @@ class ShaderTypeException extends Exception {
this.name = name;
super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.');
}
-}
-
-class ShaderErrorPosition {
- public var column:Int;
- public var line:Int;
- public var message:String;
-
- public function new(line:String, column:String, message:String) {
- this.line = Std.parseInt(line);
- this.column = Std.parseInt(column);
- this.message = message;
- }
}
\ No newline at end of file
diff --git a/source/funkin/backend/system/FakeCamera.hx b/source/funkin/backend/system/FakeCamera.hx
index 99d0eb1b88..31caaac51a 100644
--- a/source/funkin/backend/system/FakeCamera.hx
+++ b/source/funkin/backend/system/FakeCamera.hx
@@ -11,6 +11,11 @@ import openfl.display.BlendMode;
import openfl.geom.ColorTransform;
import openfl.geom.Point;
import openfl.geom.Rectangle;
+import openfl.display.TriangleCulling;
+import openfl.display3D.Context3DWrapMode;
+import openfl.display3D.Context3DCompareMode;
+import openfl.filters.BitmapFilter;
+import openfl.filters.ShaderFilter;
class FakeCamera extends FlxCamera {
public static final instance = new FakeCamera();
@@ -21,14 +26,14 @@ class FakeCamera extends FlxCamera {
visible = false;
}
- override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool, ?shader:FlxShader) { return null;}
- override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false, ?shader:FlxShader) { return null;}
+ override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode, ?culling:TriangleCulling) { return null;}
+ override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) { return null;}
override function clearDrawStack() {}
override function render() {}
- public override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {}
- public override function copyPixels(?frame:FlxFrame, ?pixels:BitmapData, ?sourceRect:Rectangle, destPoint:Point, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {}
- public override function drawTriangles(graphic:FlxGraphic, vertices:DrawData, indices:DrawData, uvtData:DrawData, ?colors:DrawData, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader):Void {}
+ public override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {}
+ public override function copyPixels(?frame:FlxFrame, ?pixels:BitmapData, ?sourceRect:Rectangle, destPoint:Point, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {}
+ public override function drawTriangles(graphic:FlxGraphic, vertices:DrawData, indices:DrawData, uvtData:DrawData, ?colors:DrawData, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode, ?culling:TriangleCulling):Void {}
public override function update(elapsed:Float) {}
@@ -41,10 +46,10 @@ class FakeCallCamera extends FakeCamera {
public static final instance = new FakeCallCamera();
public var ignoreDraws:Bool = false;
- public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
+ public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {
}
- override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
- if (!ignoreDraws) onDraw(frame, pixels, matrix, transform, blend, smoothing, shader);
+ override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, ?wrapMode:Context3DWrapMode, ?depthCompareMode:Context3DCompareMode) {
+ if (!ignoreDraws) onDraw(frame, pixels, matrix, transform, blend, smoothing, shader, wrapMode, depthCompareMode);
}
}
\ No newline at end of file
diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx
index df73b06df4..c18104dd36 100644
--- a/source/funkin/backend/system/Flags.hx
+++ b/source/funkin/backend/system/Flags.hx
@@ -275,7 +275,7 @@ class Flags {
public static var DEFAULT_CHARACTER_GHOSTDISABLE_SOUND:String = "editors/character/ghostDisable";
public static var DEFAULT_CHARACTER_GHOSTENABLE_SOUND:String = "editors/character/ghostEnable";
- public static var DEFAULT_GLSL_VERSION:String = "120";
+ @:lazy public static var DEFAULT_GLSL_VERSION:String = null;
@:also(funkin.backend.utils.HttpUtil.userAgent)
public static var USER_AGENT:String = 'request';
// -- End of Codename's Default Flags --
@@ -310,6 +310,15 @@ class Flags {
if (USE_LEGACY_TIMING == null) USE_LEGACY_TIMING = MOD_API_VERSION < 2;
if (USE_LEGACY_ZOOM_FACTOR == null) USE_LEGACY_ZOOM_FACTOR = MOD_API_VERSION < 2;
if (SUSTAINS_AS_ONE_NOTE == null) SUSTAINS_AS_ONE_NOTE = MOD_API_VERSION >= 2;
+ if (DEFAULT_GLSL_VERSION == null) {
+ if (MOD_API_VERSION < 2) {
+ DEFAULT_GLSL_VERSION = "120";
+ Logs.warn("Blend Mode Extensions won't work in MOD_API_VERSION below than 2");
+ }
+ else {
+ DEFAULT_GLSL_VERSION = openfl.display.Shader.getDefaultGLVersion();
+ }
+ }
}
public static function loadFromDatas(datas:Array):Map {
diff --git a/source/funkin/backend/system/GraphicCacheSprite.hx b/source/funkin/backend/system/GraphicCacheSprite.hx
index 6ff61905b3..cba41b61bd 100644
--- a/source/funkin/backend/system/GraphicCacheSprite.hx
+++ b/source/funkin/backend/system/GraphicCacheSprite.hx
@@ -36,7 +36,7 @@ class GraphicCacheSprite extends FlxSprite {
if (graphic == null) return;
// make their useCount one time higher to prevent them from auto being cleared from cache
- graphic.useCount++;
+ graphic.incrementUseCount();
graphic.destroyOnNoUse = false;
cachedGraphics.push(graphic);
nonRenderedCachedGraphics.push(graphic);
@@ -45,7 +45,7 @@ class GraphicCacheSprite extends FlxSprite {
public override function destroy() {
for(g in cachedGraphics) {
g.destroyOnNoUse = true;
- g.useCount--;
+ g.decrementUseCount();
}
graphic = null;
super.destroy();
diff --git a/source/funkin/backend/system/Logs.hx b/source/funkin/backend/system/Logs.hx
index 1913e4abc6..3903caf61b 100644
--- a/source/funkin/backend/system/Logs.hx
+++ b/source/funkin/backend/system/Logs.hx
@@ -115,7 +115,7 @@ final class Logs {
Sys.print(t.text);
}
NativeAPI.setConsoleColors();
- Sys.print("\r\n");
+ Sys.println("\r");
__showing = false;
#elseif mobile
while(__showing) {
diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx
index 31d270d9d2..517ce1f3f6 100644
--- a/source/funkin/backend/system/Main.hx
+++ b/source/funkin/backend/system/Main.hx
@@ -58,9 +58,11 @@ class Main extends Sprite
// You can pretty much ignore everything from here on - your code should go in your states.
public static function preInit() {
+ #if sys
funkin.backend.utils.NativeAPI.registerAsDPICompatible();
funkin.backend.system.CommandLineHandler.parseCommandLine(Sys.args());
funkin.backend.system.Main.fixWorkingDirectory();
+ #end
}
public function new()
@@ -134,7 +136,6 @@ class Main extends Sprite
FlxG.scaleMode = scaleMode = new FunkinRatioScaleMode();
Conductor.init();
- AudioSwitchFix.init();
EventManager.init();
FlxG.signals.focusGained.add(onFocus);
FlxG.signals.preStateSwitch.add(onStateSwitch);
@@ -206,16 +207,6 @@ class Main extends Sprite
// manual asset clearing since base openfl one does'nt clear lime one
// does'nt clear bitmaps since flixel fork does it auto
- @:privateAccess {
- // clear uint8 pools
- for(length=>pool in openfl.display3D.utils.UInt8Buff._pools) {
- for(b in pool.clear())
- b.destroy();
- }
-
- openfl.display3D.utils.UInt8Buff._pools.clear();
- }
-
MemoryUtil.clearMajor();
}
diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx
index 982152d5da..d98caf7fe2 100644
--- a/source/funkin/backend/system/MainState.hx
+++ b/source/funkin/backend/system/MainState.hx
@@ -40,6 +40,7 @@ class MainState extends FlxState {
ControlsUtil.resetCustomControls();
FlxG.bitmap.reset();
FlxG.sound.destroy(true);
+ FlxG.sound.resetCache();
Paths.assetsTree.reset();
diff --git a/source/funkin/backend/system/OptimizedBitmapData.hx b/source/funkin/backend/system/OptimizedBitmapData.hx
index f6cf1117a9..ecdcd3eb9a 100644
--- a/source/funkin/backend/system/OptimizedBitmapData.hx
+++ b/source/funkin/backend/system/OptimizedBitmapData.hx
@@ -4,7 +4,9 @@ import lime.graphics.Image;
import lime.graphics.cairo.CairoImageSurface;
import openfl.display.BitmapData;
import openfl.geom.Rectangle;
+import openfl.utils.BitmapDataUtil;
+@:deprecated("Use openfl.utils.BitmapDataUtil.toHardware instead.")
class OptimizedBitmapData extends BitmapData {
@SuppressWarnings("checkstyle:Dynamic")
@:noCompletion private override function __fromImage(image:#if lime Image #else Dynamic #end):Void
@@ -29,34 +31,8 @@ class OptimizedBitmapData extends BitmapData {
__isValid = true;
readable = true;
- if(FlxG.stage.context3D != null) {
- lock();
- getTexture(FlxG.stage.context3D);
- getSurface();
-
- readable = true;
- this.image = null;
-
- // @:privateAccess
- // if (FlxG.bitmap.__doNotDelete)
- // MemoryUtil.clearMinor();
- }
+ BitmapDataUtil.toHardware(this);
}
#end
}
-
- @SuppressWarnings("checkstyle:Dynamic")
- @:dox(hide) public override function getSurface():#if lime CairoImageSurface #else Dynamic #end
- {
- #if lime
- if (__surface == null)
- {
- __surface = CairoImageSurface.fromImage(image);
- }
-
- return __surface;
- #else
- return null;
- #end
- }
}
\ No newline at end of file
diff --git a/source/funkin/backend/system/RotatingSpriteGroup.hx b/source/funkin/backend/system/RotatingSpriteGroup.hx
index ad3eea575e..520e77417f 100644
--- a/source/funkin/backend/system/RotatingSpriteGroup.hx
+++ b/source/funkin/backend/system/RotatingSpriteGroup.hx
@@ -8,7 +8,12 @@ class RotatingSpriteGroup extends FlxSpriteGroup {
if (maxSize <= 0)
return super.recycle(ObjectClass, ObjectFactory, Force, Revive);
if (group.members.length < maxSize)
- return group.recycleCreateObject(ObjectClass, ObjectFactory);
+ {
+ if (ObjectFactory != null) return add(ObjectFactory());
+ if (ObjectClass != null) return add(Type.createInstance(ObjectClass, []));
+
+ return null;
+ }
var spr = group.members.shift();
group.members.push(spr);
if (Revive)
diff --git a/source/funkin/backend/system/macros/Macros.hx b/source/funkin/backend/system/macros/Macros.hx
index 2ecbc97ffc..d5dfa6c2be 100644
--- a/source/funkin/backend/system/macros/Macros.hx
+++ b/source/funkin/backend/system/macros/Macros.hx
@@ -58,6 +58,9 @@ class Macros {
final macroPath = 'funkin.backend.system.macros.Macros';
Compiler.addMetadata('@:build($macroPath.buildLimeAssetLibrary())', 'lime.utils.AssetLibrary');
+ Compiler.addMetadata('@:build($macroPath.buildLimeApplication())', 'lime.app.Application');
+ Compiler.addMetadata('@:build($macroPath.buildLimeWindow())', 'lime.ui.Window');
+ Compiler.addMetadata('@:build($macroPath.buildOpenflAssets())', 'openfl.utils.Assets');
//Adds Compat for #if hscript blocks when you have hscript improved
if (Context.defined("hscript_improved") && !Context.defined("hscript")) {
@@ -72,5 +75,57 @@ class Macros {
return fields;
}
+
+ public static function buildLimeApplication():Array {
+ final fields:Array = Context.getBuildFields(), pos:Position = Context.currentPos();
+ for (f in fields) switch (f.kind) {
+ case FFun(func): switch (f.name) {
+ case "exec": switch (func.expr.expr) {
+ case EBlock(exprs): exprs.insert(1, macro funkin.backend.system.Main.preInit());
+ default:
+ }
+ }
+ default:
+ }
+
+ return fields;
+ }
+
+ public static function buildLimeWindow():Array {
+ final fields:Array = Context.getBuildFields(), pos:Position = Context.currentPos();
+ if (!Context.defined("DARK_MODE_WINDOW")) return fields;
+
+ for (f in fields) switch (f.kind) {
+ case FFun(func): switch (f.name) {
+ case "new": switch (func.expr.expr) {
+ case EBlock(exprs): exprs.push(macro funkin.backend.utils.NativeAPI.setDarkMode(title, true));
+ default:
+ }
+ }
+ default:
+ }
+
+ return fields;
+ }
+
+ public static function buildOpenflAssets():Array {
+ final fields:Array = Context.getBuildFields(), pos:Position = Context.currentPos();
+ for (f in fields) switch (f.name) {
+ case "allowCompressedTextures": fields.remove(f);
+ default:
+ }
+
+ fields.push({name: 'allowCompressedTextures', access: [APublic, AStatic], pos: pos, kind: FProp("get", "set", macro :Null), meta: [{pos: pos, name: ":isVar"}]});
+
+ fields.push({name: "get_allowCompressedTextures", access: [APublic, AStatic, AInline], pos: pos, kind: FFun({ret: macro :Bool, args: [], expr: macro {
+ return allowCompressedTextures != null ? allowCompressedTextures : !funkin.backend.system.Main.forceGPUOnlyBitmapsOff && funkin.options.Options.gpuOnlyBitmaps;
+ }})});
+ fields.push({name: "set_allowCompressedTextures", access: [APublic, AStatic, AInline], pos: pos, kind: FFun({ret: macro :Bool, args: [{name: "value", type: macro :Bool}], expr: macro {
+ allowCompressedTextures = value;
+ return get_allowCompressedTextures();
+ }})});
+
+ return fields;
+ }
}
#end
\ No newline at end of file
diff --git a/source/funkin/backend/system/modules/ALSoftConfig.hx b/source/funkin/backend/system/modules/ALSoftConfig.hx
deleted file mode 100644
index 0b1b57a035..0000000000
--- a/source/funkin/backend/system/modules/ALSoftConfig.hx
+++ /dev/null
@@ -1,31 +0,0 @@
-package funkin.backend.system.modules;
-
-import haxe.io.Path;
-
-/*
- * A class that simply points OpenALSoft to a custom configuration file when
- * the game starts up.
- *
- * The config overrides a few global OpenALSoft settings with the aim of
- * improving audio quality on desktop targets.
- */
-@:keepInit class ALSoftConfig
-{
- #if desktop
- static function __init__():Void
- {
- var origin:String = #if hl Sys.getCwd() #else Sys.programPath() #end;
-
- var configPath:String = Path.directory(Path.withoutExtension(origin));
- #if windows
- configPath += "/plugins/alsoft.ini";
- #elseif mac
- configPath = Path.directory(configPath) + "/Resources/plugins/alsoft.conf";
- #else
- configPath += "/plugins/alsoft.conf";
- #end
-
- Sys.putEnv("ALSOFT_CONF", configPath);
- }
- #end
-}
\ No newline at end of file
diff --git a/source/funkin/backend/system/modules/AudioSwitchFix.hx b/source/funkin/backend/system/modules/AudioSwitchFix.hx
deleted file mode 100644
index 3e89e373b4..0000000000
--- a/source/funkin/backend/system/modules/AudioSwitchFix.hx
+++ /dev/null
@@ -1,69 +0,0 @@
-package funkin.backend.system.modules;
-
-import flixel.FlxState;
-import flixel.sound.FlxSound;
-import funkin.backend.utils.NativeAPI;
-import lime.media.AudioManager;
-import lime.media.AudioSource;
-import haxe.Timer;
-
-/**
- * if you are stealing this keep this comment at least please lol
- *
- * hi gray itsa me yoshicrafter29 i fixed it hehe
- */
-@:dox(hide)
-class AudioSwitchFix {
- public static function onAudioDisconnected() @:privateAccess {
- var sources:Array<{source:AudioSource, playing:Bool, time:Float, gain:Float, pitch:Float, position:lime.math.Vector4}> = [];
- for (source in AudioSource.activeSources) {
- var wasPlaying = source.playing;
- sources.push({
- source: source,
- playing: wasPlaying,
- time: source.currentTime,
- gain: source.gain,
- pitch: source.pitch,
- position: source.position
- });
-
- source.__backend.dispose();
- if (wasPlaying) source.__backend.playing = true;
- }
-
- AudioManager.shutdown();
- AudioManager.init();
- // #if !lime_doc_gen
- // if (AudioManager.context.type == OPENAL)
- // {
- // var alc = AudioManager.context.openal;
-
- // var device = alc.openDevice();
- // var ctx = alc.createContext(device);
- // alc.makeContextCurrent(ctx);
- // alc.processContext(ctx);
- // }
- // #end
-
- for (d in sources) {
- d.source.__backend.init();
- d.source.currentTime = d.time;
- d.source.gain = d.gain;
- d.source.pitch = d.pitch;
- d.source.position = d.position;
-
- if (d.playing) d.source.play();
- }
-
- Main.changeID++;
- Main.audioDisconnected = false;
- }
-
- private static var timer:Timer;
-
- private static function onRun() if (Main.audioDisconnected) onAudioDisconnected();
- public static function init() {
- NativeAPI.registerAudio();
- if (timer == null) (timer = new Timer(1000)).run = onRun;
- }
-}
\ No newline at end of file
diff --git a/source/funkin/backend/utils/AudioAnalyzer.hx b/source/funkin/backend/utils/AudioAnalyzer.hx
index c6f02cd6b9..3ad9d02dbb 100644
--- a/source/funkin/backend/utils/AudioAnalyzer.hx
+++ b/source/funkin/backend/utils/AudioAnalyzer.hx
@@ -1,67 +1,94 @@
package funkin.backend.utils;
-import flixel.sound.FlxSound;
-import lime.media.AudioBuffer;
+#if lime_openal
+import sys.thread.Mutex;
+
import lime.utils.ArrayBufferView.ArrayBufferIO;
import lime.utils.ArrayBuffer;
-#if (lime_cffi && lime_vorbis)
-import lime.media.vorbis.Vorbis;
-import lime.media.vorbis.VorbisFile;
-#end
+import flixel.sound.FlxSound;
+import flixel.sound.FlxSoundData;
-#if (target.threaded)
-import sys.thread.Mutex;
-#end
+typedef ReadCallback = Int->Int->Void;
+typedef WindowFunction = Float->Float;
+
+final class WindowFunctions {
+ static inline final TWO_PI:Float = 6.283185307179586;
+ static inline final FOUR_PI:Float = 12.566370614359172;
+ static inline final SIX_PI:Float = 18.84955592153876;
+ static inline final EIGHT_PI:Float = 25.132741228718345;
+
+ public static inline function triangular(x:Float):Float
+ return 1.0 - Math.abs(x - 0.5) * 2.0;
-typedef AudioAnalyzerCallback = Int->Int->Void;
+ public static inline function hann(x:Float):Float
+ return 0.5 - 0.5 * FlxMath.fastCos(TWO_PI * x);
+
+ public static inline function hamming(x:Float):Float
+ return 0.53836 - 0.46164 * FlxMath.fastCos(TWO_PI * x);
+
+ public static inline function blackmanNuttall(x:Float):Float
+ return 0.3635819 - 0.4891775 * FlxMath.fastCos(TWO_PI * x) + 0.1365995 * FlxMath.fastCos(FOUR_PI * x)
+ - 0.0106411 * FlxMath.fastCos(SIX_PI * x);
+
+ public static inline function blackmanHarris(x:Float):Float
+ return 0.4243801 - 0.4973406 * FlxMath.fastCos(TWO_PI * x) + 0.0782793 * FlxMath.fastCos(FOUR_PI * x);
+
+ public static inline function flatTop(x:Float):Float
+ return 0.21557895 - 0.41663158 * FlxMath.fastCos(TWO_PI * x) + 0.277263158 * FlxMath.fastCos(FOUR_PI * x)
+ + 0.083578947 * FlxMath.fastCos(SIX_PI * x) + 0.006947368 * FlxMath.fastCos(EIGHT_PI * x);
+}
+
+enum abstract TimeUnit(Int) from Int to Int {
+ var MILLISECOND = 0;
+ var SECOND = 1;
+ var SAMPLE = 2;
+}
/**
- * An utility that analyze FlxSounds,
+ * An utility that analyze FlxSound,
* can be used to make waveform or real-time audio visualizer.
- *
- * FlxSound.amplitude works so if any case if your only checking for peak of current time, use that instead.
*/
final class AudioAnalyzer {
/**
* Get bytes from an audio buffer with specified position and wordSize
- * @param buffer The audio buffer to get byte from.
- * @param position The specified position to get the byte from the audio buffer.
- * @param wordSize How many bytes to get with to one byte (Usually it's bitsPerSample / 8 or bitsPerSample >> 3).
+ * @param buffer The audio buffer to get byte from.
+ * @param position The specified position to get the byte from the audio buffer.
+ * @param wordSize How many bytes to get with to one byte (Usually it's bitsPerSample / 8 or bitsPerSample >> 3).
* @return Byte from the audio buffer with specified position.
*/
public static function getByte(buffer:ArrayBuffer, position:Int, wordSize:Int):Int {
- if (wordSize == 2) return inline ArrayBufferIO.getInt16(buffer, position);
+ if (wordSize == 2) return ArrayBufferIO.getInt16(buffer, position);
else if (wordSize == 3) {
- var b = inline ArrayBufferIO.getUint16(buffer, position) | (buffer.get(position + 2) << 16);
- if (b & 0x800000 != 0) return b - 0x1000000;
- else return b;
+ wordSize = ArrayBufferIO.getUint16(buffer, position) | (buffer.get(position + 2) << 16);
+ if (wordSize & 0x800000 != 0) return wordSize - 0x1000000;
+ else return wordSize;
}
- else if (wordSize == 4) return inline ArrayBufferIO.getInt32(buffer, position);
- else return inline ArrayBufferIO.getUint8(buffer, position) - 128;
+ else if (wordSize == 4) return ArrayBufferIO.getInt32(buffer, position);
+ else return ArrayBufferIO.getInt8(buffer, position);
}
/**
- * Gets levels from the frequencies with specified sample rate.
- * @param frequencies Frequencies input.
- * @param sampleRate Sample Rate input.
- * @param barCount How much bars to get.
- * @param levels The output for getting the values, to avoid memory leaks (Optional).
- * @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
- * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
- * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
- * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
- * @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
- * @return Output of levels/bars that ranges from 0 to 1.
+ * Gets spectrum from the frequencies with specified sample rate.
+ * @param frequencies Frequencies input.
+ * @param sampleRate Sample Rate input.
+ * @param barCount How much bars to get.
+ * @param spectrum The output for getting the values, to avoid memory leaks (Optional).
+ * @param ratio How much ratio for smoothen the values from the previous spectrum values (Optional, use FlxMath.getElapsedLerp(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
+ * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
+ * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
+ * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
+ * @param maxFreq The maximum frequency to cap (Optional, default 20000.0, Above 23000.0 is not recommended).
+ * @return Output of spectrum/bars that ranges from 0 to 1.
*/
- public static function getLevelsFromFrequencies(frequencies:Array, sampleRate:Int, barCount:Int, ?levels:Array, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 22000.0):Array {
- if (levels == null) levels = [];
- levels.resize(barCount);
+ public static function getSpectrumFromFrequencies(frequencies:Array, sampleRate:Int, barCount:Int, ?spectrum:Array, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 20000.0):Array {
+ if (spectrum == null) spectrum = [];
+ if (spectrum.length != barCount) spectrum.resize(barCount);
- var logMin = Math.log(minFreq), logMax = Math.log(maxFreq);
- var logRange = logMax - logMin, dbRange = maxDb - minDb, n = frequencies.length;
+ var logMin = Math.log(minFreq), n = frequencies.length - 1;
+ var logRange = Math.log(maxFreq) - logMin, dbRangeRate = 1 / (maxDb - minDb), rate = frequencies.length * 2 / sampleRate;
inline function calculateScale(i:Int)
- return CoolUtil.bound(Math.exp(logMin + (logRange * i / (barCount + 1))) * n * 2 / sampleRate, 0, n - 1);
+ return FlxMath.bound(Math.exp(logMin + (logRange * i / (barCount + 1))) * rate, 0, n);
var s1 = calculateScale(0), s2;
var i1 = Math.floor(s1), i2;
@@ -81,138 +108,146 @@ final class AudioAnalyzer {
}
i1 = Math.floor(s1 = s2);
- v = CoolUtil.bound(((20 * Math.log(v) / 2.302585092994046) - minDb) / dbRange, 0, 1);
- if (ratio > 0 && ratio < 1 && v < levels[i]) levels[i] -= (levels[i] - v) * ratio;
- else levels[i] = v;
+ v = FlxMath.bound((Math.log(v) * 8.685889638065035 - minDb) * dbRangeRate, 0, 1);
+ if (ratio > 0 && ratio < 1 && v < spectrum[i]) spectrum[i] -= (spectrum[i] - v) * ratio;
+ else spectrum[i] = v;
}
- return levels;
+ return spectrum;
}
- static var __reverseIndices:Array> = [];
- static var __windows:Array> = [];
- static var __twiddleReals:Array> = [];
- static var __twiddleImags:Array> = [];
- static var __freqReals:Array> = [];
- static var __freqImags:Array> = [];
- static var __freqCalculating:Int = 0;
- #if (target.threaded)
- static var __mutex:Mutex = new Mutex();
- #end
+ /**
+ * Gets levels from the frequencies with specified sample rate.
+ * @param frequencies Frequencies input.
+ * @param sampleRate Sample Rate input.
+ * @param barCount How much bars to get.
+ * @param levels The output for getting the values, to avoid memory leaks (Optional).
+ * @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
+ * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
+ * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
+ * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
+ * @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
+ * @return Output of levels/bars that ranges from 0 to 1.
+ *
+ * deprecated, use getSpectrumFromFrequencies instead.
+ */
+ @:deprecated("Use getSpectrumFromFrequencies instead of getLevelsFromFrequencies.")
+ public static function getLevelsFromFrequencies(frequencies:Array, sampleRate:Int, barCount:Int, ?levels:Array, ratio = 0.0, minDb = -63.0, maxDb = -10.0, minFreq = 20.0, maxFreq = 22000.0):Array
+ return inline getSpectrumFromFrequencies(frequencies, sampleRate, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
+
+ static final _permutations:Map> = [];
+ static final _twiddleReals:Map> = [];
+ static final _twiddleImags:Map> = [];
+ static final _reals:Array> = [];
+ static final _imags:Array> = [];
+ static var _freqCalculating:Int = 0;
+ static final _mutex = new Mutex();
/**
* Gets frequencies from the samples.
- * @param samples The samples (can be from AudioAnalyzer.getSamples).
- * @param fftN How much samples for the fft to get, Has to be power of two, or it won't work.
- * @param useWindowing Should fft related stuff use blackman windowing? (Web AnalyzerNode windowing), Most of the time it's not worth it.
- * @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
- * @return Output of frequencies.
+ * @param samples The samples (can be from FunkinAudioAnalyzer.getSamples).
+ * @param window The windowing function to use when passed.
+ * @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
+ * @return Output of frequencies.
*/
- public static function getFrequenciesFromSamples(samples:Array, fftN = 2048, useWindowing = false, ?frequencies:Array):Array {
- var log = Math.floor(Math.log(fftN) / 0.6931471805599453);
- if (log == 0) throw "AudioAnalyzer.getFrequenciesFromSamples: Cannot insert a fftN of 1";
-
- var i = log - 1;
- fftN = 1 << log;
-
- #if (target.threaded) __mutex.acquire(); #end
- var reals:Array = __freqReals[__freqCalculating], imags:Array = __freqImags[__freqCalculating];
- if (reals == null) {
- __freqReals.push(reals = []);
- __freqImags.push(imags = []);
- }
- __freqCalculating++;
+ public static function getFrequenciesFromSamples(samples:Array, ?window:WindowFunction, ?frequencies:Array, ?fftN:Int):Array {
+ if (fftN == null) fftN = samples.length;
- var reverseIndices:Array = __reverseIndices[i];
- var windows:Array = __windows[i];
- var twiddleReals:Array = __twiddleReals[i];
- var twiddleImags:Array = __twiddleImags[i];
+ var bits = 0;
+ while ((fftN >>= 1) > 0) bits++;
+ if (bits == 0) throw "FunkinAudioAnalyzer.getFrequenciesFromSamples: Cannot insert a sample length or fftN of 1";
- if (reverseIndices == null) {
- __reverseIndices.resize(log);
- __windows.resize(log);
- __twiddleReals.resize(log);
- __twiddleImags.resize(log);
+ fftN = 1 << bits;
+ var fftN2 = fftN >> 1, n = fftN - 1;
- (reverseIndices = []).resize(fftN);
- (windows = []).resize(fftN);
- (twiddleReals = []).resize(fftN);
- (twiddleImags = []).resize(fftN);
+ var permutation:Array, twiddleReal:Array, twiddleImag:Array;
+ _mutex.acquire();
- var f;
+ var real:Array = _reals[_freqCalculating], imag:Array = _imags[_freqCalculating];
+ if (real == null) {
+ _reals.push(real = []);
+ _imags.push(imag = []);
+ }
+ _freqCalculating++;
+
+ if (_permutations.exists(bits)) {
+ permutation = _permutations.get(bits);
+ twiddleReal = _twiddleReals.get(bits);
+ twiddleImag = _twiddleImags.get(bits);
+ }
+ else {
+ (permutation = []).resize(fftN);
+ (twiddleReal = []).resize(fftN2);
+ (twiddleImag = []).resize(fftN2);
+
+ var ang:Float;
for (i in 0...fftN) {
- f = 2 * Math.PI * (i / fftN);
- windows[i] = 0.42 - 0.5 * Math.cos(f) + 0.08 * Math.cos(2 * f);
- reverseIndices[i] = __bitReverse(i, log);
- twiddleReals[i] = Math.cos(-f);
- twiddleImags[i] = Math.sin(-f);
+ permutation[i] = _bitReverse(i, bits);
+ if (i < fftN2) {
+ twiddleReal[i] = Math.cos((ang = -6.283185307179586 * i / n));
+ twiddleImag[i] = Math.sin(ang);
+ }
}
- __reverseIndices[i] = reverseIndices;
- __windows[i] = windows;
- __twiddleReals[i] = twiddleReals;
- __twiddleImags[i] = twiddleImags;
+ _permutations.set(bits, permutation);
+ _twiddleReals.set(bits, twiddleReal);
+ _twiddleImags.set(bits, twiddleImag);
}
- #if (target.threaded) __mutex.release(); #end
+ _mutex.release();
- if (fftN > reals.length) {
- reals.resize(fftN);
- imags.resize(fftN);
+ if (fftN > real.length) {
+ real.resize(fftN);
+ imag.resize(fftN);
}
if (frequencies == null) frequencies = [];
- frequencies.resize(1 << i);
+ if (frequencies.length != fftN2) frequencies.resize(fftN2);
- i = samples.length;
- while (i > 0) {
- i--;
- if (useWindowing) reals[reverseIndices[i]] = samples[i] * windows[i];
- else reals[reverseIndices[i]] = samples[i];
- imags[i] = 0;
+ var tr = 1 / n;
+ for (i in 0...fftN) {
+ real[permutation[i]] = samples[i];
+ if (window != null) real[permutation[i]] *= window(i * tr);
+ imag[i] = 0;
}
- var size = 1, n = fftN, half = 1, k, i0, i1, t, tr:Float, ti:Float;
- while ((size <<= 1) < fftN) {
- n >>= 1;
- i = 0;
- while (i < fftN) {
- k = 0;
- while (k < half) {
- i1 = (i0 = i + k) + half;
- t = (k * n) % fftN;
-
- tr = reals[i1] * twiddleReals[t] - imags[i1] * twiddleImags[t];
- ti = reals[i1] * twiddleImags[t] + imags[i1] * twiddleReals[t];
- reals[i1] = reals[i0] - tr;
- imags[i1] = imags[i0] - ti;
- reals[i0] += tr;
- imags[i0] += ti;
-
- k++;
+ var half = 1, g:Int, b:Int, r:Int, i0:Int, i1:Int, ti:Float;
+ while (fftN2 > 0) {
+ g = 0;
+ while (g < fftN) {
+ b = r = 0;
+ while (b < half) {
+ i1 = (i0 = g + b) + half;
+ tr = real[i1] * twiddleReal[r] - imag[i1] * twiddleImag[r];
+ ti = real[i1] * twiddleImag[r] + imag[i1] * twiddleReal[r];
+ real[i1] = real[i0] - tr;
+ imag[i1] = imag[i0] - ti;
+ real[i0] += tr;
+ imag[i0] += ti;
+ b++;
+ r += fftN2;
}
- i += size;
+ g += half << 1;
}
- half = size;
+ half <<= 1;
+ fftN2 >>= 1;
}
tr = 1.0 / fftN;
- i = 1 << (log - 1);
- while (i > 1) {
- i--;
- frequencies[i] = 2 * Math.sqrt(reals[i] * reals[i] + imags[i] * imags[i]) * tr;
- }
- frequencies[0] = Math.sqrt(reals[0] * reals[0] + imags[0] * imags[0]) * tr;
+ i0 = frequencies.length - 1;
+ for (i in 1...i0) frequencies[i] = 2 * Math.sqrt(real[i] * real[i] + imag[i] * imag[i]) * tr;
+ frequencies[0] = Math.sqrt(real[0] * real[0] + imag[0] * imag[0]) * tr;
+ frequencies[i0] = Math.sqrt(real[i0] * real[i0] + imag[i0] * imag[i0]) * tr;
- #if (target.threaded) __mutex.acquire(); #end
- __freqCalculating--;
- #if (target.threaded) __mutex.release(); #end
+ _mutex.acquire();
+ _freqCalculating--;
+ _mutex.release();
return frequencies;
}
- static function __bitReverse(x:Int, log:Int):Int {
- var y = 0, i = log;
+ static function _bitReverse(x:Int, bits:Int):Int {
+ var y = 0, i = bits;
while (i > 0) {
y = (y << 1) | (x & 1);
x >>= 1;
@@ -227,346 +262,357 @@ final class AudioAnalyzer {
public var sound:FlxSound;
/**
- * How much samples for the fft to get.
- * Usually for getting the levels or frequencies of the sound.
- *
- * Has to be power of two, or it won't work.
- */
- public var fftN:Int;
-
- /**
- * Should fft related stuff use blackman windowing? (Web AnalyzerNode windowing).
- * Most of the time looks bad with this.
+ * The current data from sound.
*/
- public var useWindowingFFT:Bool;
+ public var data(default, null):FlxSoundData;
/**
- * The current buffer from sound.
+ * How much samples for the fourier transform to get.
+ * Has to be power of two, or it won't work.
*/
- public var buffer(default, null):AudioBuffer;
+ public var fftN:Int;
/**
* The current byteSize from buffer.
- * Example the byteSize of 16 BitsPerSample is 32768 (1 << 16-1)
+ * Example the byteSize of 16 BitsPerSample is 32768 (1 << (16 - 1))
*/
public var byteSize(default, null):Int;
- var __toBits:Float;
- var __wordSize:Int;
- var __sampleSize:Int;
-
- #if (lime_cffi && lime_vorbis)
- var __vorbis:VorbisFile;
- var __buffer:ArrayBuffer;
- var __bufferSize:Int;
- var __bufferLastSize:Int;
- var __bufferTime:Float;
- var __bufferLastTime:Float;
- #end
-
- // analyze
- var __min:Array = [];
- var __max:Array = [];
- var __minByte:Int;
- var __maxByte:Int;
-
- // samples
- var __sampleIndex:Int;
- var __sampleChannel:Int;
- var __sampleToValue:Float;
- var __sampleOutputMerge:Bool;
- var __sampleOutputLength:Int;
- var __sampleOutput:Array;
-
- // frequencies
- var __freqSamples:Array;
- var __frequencies:Array;
-
- /**
- * Creates an analyzer for specified FlxSound
- * @param sound An FlxSound to analyze.
- * @param fftN How much samples for fft to get (Optional, default 2048, 4096 is recommended for highest quality).
- * @param useWindowingFFT Should fft related stuff use blackman windowing? (Web AnalyzerNode windowing).
- */
- public function new(sound:FlxSound, fftN = 2048, useWindowingFFT = false) {
+ var _sampleSize:Int;
+ var _mins:Array = [];
+ var _maxs:Array = [];
+ //var _decoder:FunkinAudioDecoder;
+ //var _buffer:ArrayBuffer;
+ //var _bufferLen:Int;
+ //var _bufferLastSize:Int;
+ //var _bufferLastSample:Int;
+ var _sampleIndex:Int;
+ var _sampleChannel:Int;
+ var _sampleValue:Int;
+ var _sampleValueGain:Float;
+ var _sampleOutputMerge:Bool;
+ var _sampleOutputLength:Int;
+ var _sampleOutput:Array;
+ var _freqSamples:Array;
+ var _frequencies:Array;
+
+ public function new(sound:FlxSound, fftN = 4096) {
this.sound = sound;
this.fftN = fftN;
- this.useWindowingFFT = useWindowingFFT;
- __check();
+ _check();
}
- function __check() if (sound != null && sound.buffer != buffer) {
- byteSize = 1 << ((buffer = sound.buffer).bitsPerSample - 1);
-
- #if (lime_cffi && lime_vorbis)
- __vorbis = null;
- __bufferLastSize = 0;
- __bufferTime = Math.NaN;
- __bufferLastTime = Math.NaN;
- #end
+ function _check() {
+ if (sound != null && !sound.data.isDestroyed) {
+ if (sound.data != data)
+ {
+ byteSize = 1 << ((data = sound.data).bitsPerSample - 1);
+ _sampleSize = data.channels * (data.bitsPerSample >> 3);
+ _mins.resize(data.channels);
+ _maxs.resize(data.channels);
+ //_decoder?.destroy();
+ }
+ }
+ else data = null;
+ }
- __toBits = buffer.sampleRate / 1000 * (__sampleSize = buffer.channels * (__wordSize = buffer.bitsPerSample >> 3));
- __min.resize(buffer.channels);
- __max.resize(buffer.channels);
+ /**
+ * Gets spectrum from an attached sound from position.
+ * @param pos Position to get (Optional).
+ * @param timeUnit TimeUnit to use for positions (Optional).
+ * @param gain How much gain multiplier will it affect the output. (Optional, default 1.0).
+ * @param barCount How much bars to get.
+ * @param spectrum The output for getting the values, to avoid memory leaks (Optional).
+ * @param ratio How much ratio for smoothen the values from the previous spectrum values (Optional, use FlxMath.getElapsedLerp(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
+ * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
+ * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
+ * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
+ * @param maxFreq The maximum frequency to cap (Optional, default 20000.0, Above 23000.0 is not recommended).
+ * @return Output of spectrum/bars that ranges from 0 to 1.
+ */
+ public function getSpectrum(?pos:Float, ?timeUnit:TimeUnit, ?gain:Float, ?window:WindowFunction, barCount:Int, ?spectrum:Array, ?ratio:Float, ?minDb:Float, ?maxDb:Float, ?minFreq:Float, ?maxFreq:Float):Array {
+ return getSpectrumFromFrequencies(_frequencies = getFrequencies(pos, timeUnit, gain, window, _frequencies), data.sampleRate, barCount, spectrum, ratio, minDb, maxDb, minFreq, maxFreq);
}
/**
* Gets levels from an attached FlxSound from startPos, basically a minimized of frequencies.
- * @param startPos Start Position to get from sound in milliseconds.
- * @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
- * @param barCount How much bars to get.
- * @param levels The output for getting the values, to avoid memory leaks (Optional).
- * @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
- * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
- * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
- * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
- * @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
- * @return Output of levels/bars that ranges from 0 to 1.
+ * @param startPos Start Position to get from sound in milliseconds.
+ * @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
+ * @param barCount How much bars to get.
+ * @param levels The output for getting the values, to avoid memory leaks (Optional).
+ * @param ratio How much ratio for smoothen the values from the previous levels values (Optional, use CoolUtil.getFPSRatio(1 - ratio) to simulate web AnalyserNode.smoothingTimeConstant, 0.35 of smoothingTime works most of the time).
+ * @param minDb The minimum decibels to cap (Optional, default -63.0, -120 is pure silence).
+ * @param maxDb The maximum decibels to cap (Optional, default -10.0, Above 0 is not recommended).
+ * @param minFreq The minimum frequency to cap (Optional, default 20.0, Below 8.0 is not recommended).
+ * @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
+ * @return Output of levels/bars that ranges from 0 to 1.
+ *
+ * deprecated, use getLevels instead.
*/
+ @:deprecated("Use getSpectrum instead of getLevels.")
public function getLevels(?startPos:Float, ?volume:Float, barCount:Int, ?levels:Array, ?ratio:Float, ?minDb:Float, ?maxDb:Float, ?minFreq:Float, ?maxFreq:Float):Array
- return inline getLevelsFromFrequencies(__frequencies = getFrequencies(startPos, volume, __frequencies), buffer.sampleRate, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
+ return inline getSpectrum(startPos, MILLISECOND, volume, null, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
/**
- * Gets frequencies from an attached FlxSound from startPos.
- * @param startPos Start Position to get from sound in milliseconds.
- * @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
- * @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
- * @return Output of frequencies.
+ * Gets frequencies from an attached sound from position.
+ * @param pos Position to get. (Optional).
+ * @param timeUnit TimeUnit to use for positions. (Optional).
+ * @param gain How much gain multiplier will it affect the output. (Optional, default 1.0).
+ * @param window The windowing function to use when passed.
+ * @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
+ * @return Output of frequencies.
*/
- public function getFrequencies(?startPos:Float, ?volume:Float, ?frequencies:Array):Array
- return inline getFrequenciesFromSamples(__freqSamples = getSamples(startPos != null ? startPos : sound.time, fftN, true, -1, volume, __freqSamples), fftN, useWindowingFFT, frequencies);
+ public function getFrequencies(?pos:Float, ?timeUnit:TimeUnit, ?gain:Float, ?window:WindowFunction, ?frequencies:Array):Array {
+ if (pos == null) {
+ if (sound == null) return frequencies;
+ _check();
+ if ((pos = sound.time / 1000 * data.sampleRate - fftN) < 0) pos = 0;
+ timeUnit = SAMPLE;
+ }
+ return getFrequenciesFromSamples(_freqSamples = getSamples(pos, timeUnit, fftN, true, -1, gain, _freqSamples), window, frequencies);
+ }
/**
- * Analyzes an attached FlxSound from startPos to endPos in milliseconds to get the amplitudes.
- * @param startPos Start Position to get from sound in milliseconds.
- * @param endPos End Position to get from sound in milliseconds.
- * @param outOrOutMin The output minimum value from the analyzer, indices is in channels (0 to -0.5 -> 0 to 0.5) (Optional, if outMax doesn't get passed in, it will be [min, max] with all channels combined instead).
- * @param outMax The output maximum value from the analyzer, indices is in channels (Optional).
- * @return Output of amplitude from given position.
+ * Analyzes an attached sound from startPos to endPos in milliseconds to get the amplitudes.
+ * @param startPos Start Position to get.
+ * @param endPos End Position to get.
+ * @param timeUnit TimeUnit to use for positions.
+ * @param outOrOutMins The output minimum value from the analyzer, indices is in channels (0 to -0.5 -> 0 to 0.5) (Optional, if outMax doesn't get passed in, it will be [min, max] with all channels combined instead).
+ * @param outMaxs The output maximum value from the analyzer, indices is in channels (Optional).
+ * @return Output of amplitude from given position.
*/
- public function analyze(startPos:Float, endPos:Float, ?outOrOutMin:Array, ?outMax:Array):Float {
- var hasOut = outOrOutMin != null;
- var hasTwoOut = hasOut && outMax != null;
-
- if (hasTwoOut) for (i in 0...buffer.channels) __min[i] = __max[i] = 0;
- __minByte = __maxByte = 0;
-
- __check();
- __read(startPos, endPos, hasTwoOut ? __analyzeCallback : __analyzeCallbackSimple);
-
- if (hasOut) {
- var f:Float;
- if (hasTwoOut) for (i in 0...buffer.channels) {
- if (outOrOutMin[i] < (f = __min[i] / byteSize)) outOrOutMin[i] = f;
- if (outMax[i] < (f = __max[i] / byteSize)) outMax[i] = f;
- }
- else {
- outOrOutMin.resize(2);
- if (outOrOutMin[0] < (f = __minByte / byteSize)) outOrOutMin[0] = f;
- if (outOrOutMin[1] < (f = __maxByte / byteSize)) outOrOutMin[1] = f;
+ public function analyze(startPos:Float, endPos:Float, ?timeUnit:TimeUnit, ?outOrOutMins:Array, ?outMaxs:Array):Float {
+ var hasOut = outOrOutMins != null;
+ var hasTwoOut = hasOut && outMaxs != null;
+
+ _check();
+ var conversion:Float = switch (timeUnit) {
+ case SAMPLE: 1;
+ case SECOND: data.sampleRate;
+ default: data.sampleRate / 1000;
+ }
+ for (i in 0...data.channels) _mins[i] = _maxs[i] = -0x7FFFFFFF;
+ if (startPos > endPos) _read(Math.floor(startPos * conversion), Math.floor(endPos * conversion), _analyzeRead);
+
+ var min = -0x7FFFFFFF, max = -0x7FFFFFFF, v = 1 / byteSize, f:Float;
+ for (i in 0...data.channels) {
+ if (hasTwoOut) {
+ if ((f = _mins[i] * v) > outOrOutMins[i]) outOrOutMins[i] = f;
+ if ((f = _maxs[i] * v) > outMaxs[i]) outMaxs[i] = f;
}
+ if (_maxs[i] > max) max = _maxs[i];
+ if (_mins[i] > min) min = _mins[i];
}
- return (__maxByte + __minByte) / byteSize;
+ if (hasOut && outMaxs == null) {
+ if ((f = min * v) > outOrOutMins[0]) outOrOutMins[0] = f;
+ if ((f = max * v) > outOrOutMins[1]) outOrOutMins[1] = f;
+ }
+ return (max + min) * v;
}
- function __analyzeCallback(b:Int, c:Int):Void
- ((b > __max[c]) ? (if ((__max[c] = b) > __maxByte) (__maxByte = b)) : (if (-b > __min[c]) (if ((__min[c] = -b) > __minByte) (__minByte = __min[c]))));
-
- function __analyzeCallbackSimple(b:Int, c:Int):Void
- ((b > __maxByte) ? (__maxByte = b) : (if (-b > __minByte) (__minByte = -b)));
+ function _analyzeRead(b:Int, c:Int) ((b > _maxs[c]) ? (_maxs[c] = b) : (if (-b > _mins[c]) (_mins[c] = -b)));
/**
* Gets samples from startPos with given length of samples.
- * @param startPos Start Position to get from sound in milliseconds.
- * @param length Length of Samples.
- * @param mono Merge all of the byte channels of samples in one channel instead (Optional).
- * @param channel What channels to get from? (-1 == All Channels, Optional, this will be ignored if mono is enabled).
- * @param volume How much volume multiplier will it affect the output. (Optional, default 1.0).
- * @param output An Output that gets passed into this function, usually for to avoid memory leaks (Optional).
- * @param outputMerge Merge with previous values (Optional, default false).
- * @return Output of samples.
+ * @param startPos Start Position to get.
+ * @param timeUnit TimeUnit to use for positions.
+ * @param length Length of Samples.
+ * @param mono Merge all of the byte channels of samples in one channel instead (Optional).
+ * @param channel What channels to get from? (-1 == All Channels, Optional, this will be ignored if mono is enabled).
+ * @param gain How much gain multiplier will it affect the output. (Optional, default 1.0).
+ * @param output An Output that gets passed into this function, usually for to avoid memory leaks (Optional).
+ * @param outputMerge Merge with previous values (Optional, default false).
+ * @return Output of samples.
*/
- public function getSamples(startPos:Float, length:Int, mono = true, channel = -1, volume = 1.0, ?output:Array, ?outputMerge = false):Array {
- ((!mono && (__sampleChannel = channel) == -1) ? (__sampleOutputLength = length * buffer.channels) : (__sampleOutputLength = length));
- ((output == null) ? (__sampleOutput = output = []) : (__sampleOutput = output)).resize(__sampleOutputLength);
- ((mono) ? (__sampleToValue = volume / (byteSize * buffer.channels)) : (__sampleToValue = 1.0 / byteSize));
- __sampleOutputMerge = outputMerge;
- __sampleIndex = 0;
-
- __check();
- __read(startPos, startPos + (length / __toBits * buffer.channels), mono ? __getSamplesCallbackMono : (channel == -1 ? __getSamplesCallback : __getSamplesCallbackChannel));
-
- __sampleOutput = null;
+ public function getSamples(startPos:Float, ?timeUnit:TimeUnit, length:Int, mono = true, channel = -1, gain = 1.0, ?output:Array, ?outputMerge = false):Array {
+ _check();
+ ((!mono && channel == -1) ? (_sampleOutputLength = length * data.channels) : (_sampleOutputLength = length));
+ if (((output == null) ? (_sampleOutput = output = []) : (_sampleOutput = output)).length != _sampleOutputLength) output.resize(_sampleOutputLength);
+ _sampleValueGain = gain;
+ _sampleOutputMerge = outputMerge;
+ _sampleIndex = 0;
+ _sampleValue = 0;
+
+ final samplePos = Math.floor(switch (timeUnit) {
+ case SAMPLE: startPos;
+ case SECOND: startPos * data.sampleRate;
+ default: startPos * data.sampleRate / 1000;
+ });
+ _sampleChannel = mono ? data.channels - 1 : channel;
+ if (length > 0) _read(samplePos, samplePos + length, mono ? _getSamplesCallbackMono : (channel == -1 ? _getSamplesCallback : _getSamplesCallbackChannel));
+
+ _sampleOutput = null;
return output;
}
- function __getSamplesCallbackMono(b:Int, c:Int):Void if (__sampleIndex < __sampleOutputLength) {
- if (c == 0) {
- if (__sampleOutputMerge) __sampleOutput[__sampleIndex] += b * __sampleToValue;
- else __sampleOutput[__sampleIndex] = b * __sampleToValue;
- }
- else if (c == buffer.channels) {
- __sampleOutput[__sampleIndex] += b * __sampleToValue;
- __sampleIndex++;
+ function _getSamplesCallbackMono(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
+ if (c == 0) _sampleValue = idiv(b, data.channels);
+ else _sampleValue += idiv(b, data.channels);
+
+ if (c == _sampleChannel) {
+ if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += _sampleValue / byteSize;
+ else _sampleOutput[_sampleIndex] = _sampleValue / byteSize;
+ _sampleIndex++;
}
- else
- __sampleOutput[__sampleIndex] += b * __sampleToValue;
}
- function __getSamplesCallbackChannel(b:Int, c:Int):Void if (__sampleIndex < __sampleOutputLength) {
- if (c == __sampleChannel) {
- if (__sampleOutputMerge) __sampleOutput[__sampleIndex] += b * __sampleToValue;
- else __sampleOutput[__sampleIndex] = b * __sampleToValue;
- __sampleIndex++;
+ function _getSamplesCallbackChannel(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
+ if (c == _sampleChannel) {
+ if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += b / byteSize;
+ else _sampleOutput[_sampleIndex] = b / byteSize;
+ _sampleIndex++;
}
}
- function __getSamplesCallback(b:Int, c:Int):Void if (__sampleIndex < __sampleOutputLength) {
- if (__sampleOutputMerge) __sampleOutput[__sampleIndex] += b * __sampleToValue;
- else __sampleOutput[__sampleIndex] = b * __sampleToValue;
- __sampleIndex++;
+ function _getSamplesCallback(b:Int, c:Int):Void if (_sampleIndex < _sampleOutputLength) {
+ if (_sampleOutputMerge) _sampleOutput[_sampleIndex] += b / byteSize;
+ else _sampleOutput[_sampleIndex] = b / byteSize;
+ _sampleIndex++;
}
/**
- * Read an attached FlxSound from startPos to endPos in milliseconds with a callback.
- * @param startPos Start Position to get from sound in milliseconds.
- * @param endPos End Position to get from sound in milliseconds.
- * @param callback Int->Int->Void Byte->Channels->Void Callback to get the byte of a sample.
+ * Read an attached sound from startPos to endPos in milliseconds with a callback.
+ * @param startPos Start Position to get.
+ * @param endPos End Position to get.
+ * @param timeUnitTimeUnit to use for positions.
+ * @param callback Byte:Int->Channels:Int->Void Callback to get the byte of a sample.
*/
- public function read(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
- __check();
- __read(startPos, endPos, callback);
+ public function read(startPos:Float, endPos:Float, ?timeUnit:TimeUnit, callback:ReadCallback) {
+ _check();
+ var conversion:Float = switch (timeUnit) {
+ case SAMPLE: 1;
+ case SECOND: data.sampleRate;
+ default: data.sampleRate / 1000;
+ }
+ if (startPos > endPos) _read(Math.floor(startPos * conversion), Math.floor(endPos * conversion), callback);
}
- inline function __read(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
- if (buffer.data != null) __readData(startPos, endPos, callback);
- #if lime_cffi
- else if (__canReadStream() && (startPos += __readStream(startPos, endPos, callback)) >= endPos) {}
- #if lime_vorbis
- else if (__prepareDecoder()) __readDecoder(startPos, endPos, callback);
- #end
- #end
- }
+ function _read(startSample:Int, endSample:Int, callback:ReadCallback) {
+ // use data in ram if available
+ if (data.buffer.data != null) _readData(startSample * _sampleSize, endSample * _sampleSize, callback);
+ // use decoded datas that have been used in streaming sound to reduce jumping disk seeking
+ // if not use decoder and use seeking instead*
+ else if (sound.loaded) _readStream(startSample, endSample, callback);
- inline function __readData(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
- var pos = Math.floor(startPos * __toBits), end = Math.min(Math.floor(endPos * __toBits), buffer.data.buffer.length), c = 0;
- pos -= pos % __sampleSize;
- end -= end % __sampleSize;
+ // TODO
+ //else if ((!sound.loaded || (startSample = _readStream(startSample, endSample, callback)) < endSample) && _prepareDecoder())
+ // _readDecoder(startSample, endSample, callback);
+ }
- while (pos < end) {
- callback(getByte(buffer.data.buffer, pos, __wordSize), c);
- if (++c > buffer.channels) c = 0;
- pos += __wordSize;
+ inline function _readData(startIndex:Int, endIndex:Int, callback:ReadCallback) {
+ if (endIndex > data.buffer.data.byteLength) endIndex = data.buffer.data.byteLength;
+ var buffer = data.buffer.data.buffer, byteRate = data.bitsPerSample >> 3, c = 0;
+ while (startIndex < endIndex) {
+ callback(getByte(buffer, startIndex, byteRate), c);
+ startIndex += byteRate;
+ if (++c == data.channels) c = 0;
}
}
- #if lime_cffi
- inline function __canReadStream():Bool
- @:privateAccess return sound._source != null && sound._source.__backend != null && sound._source.__backend.playing;
-
- inline function __readStream(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback):Float @:privateAccess {
- final backend = sound._source.__backend;
-
- // TODO: Wrap it with try until i figured it out an effective way to do this...
- // So... sometimes it just uses the decoder even if it looks good?? please help
- var n = Math.floor((endPos - startPos) * __toBits);
- var i = backend.bufferLengths.length - backend.requestBuffers - 1, time:Float;
- while (++i < backend.bufferLengths.length) if (startPos >= (time = backend.bufferTimes[i] * 1000)) {
- var pos = Math.floor((startPos - time) * __toBits), buf = backend.bufferDatas[i].buffer, size = backend.bufferLengths[i], c = 0;
- while (pos >= size) {
- if (++i >= backend.bufferLengths.length) break;
- pos -= size;
- buf = backend.bufferDatas[i].buffer;
- size = backend.bufferLengths[i];
- }
- if (i >= backend.bufferLengths.length) break;
- if ((pos -= pos % __sampleSize) < 0) pos = 0;
- n -= pos % __sampleSize;
-
- while (n > 0) {
- callback(getByte(buf, pos, __wordSize), c);
- if (++c > buffer.channels) c = 0;
- if ((pos += __wordSize) >= size) {
- if (++i >= backend.bufferLengths.length) break;
- pos = 0;
- buf = backend.bufferDatas[i].buffer;
- size = backend.bufferLengths[i];
+ function _readStream(startSample:Int, endSample:Int, callback:ReadCallback):Int @:privateAccess {
+ final backend = sound.source.__backend;
+ if (backend.filledBuffers == 0) return startSample;
+
+ backend.mutex.acquire();
+
+ final max = backend.bufferViews.length;
+ var byteRate = data.bitsPerSample >> 3, i = max - backend.queuedBuffers, buffer:ArrayBuffer, bufferLen:Int, bufferSample:Int, pos:Int, c:Int;
+
+ while (i < max && startSample < endSample) {
+ if (startSample >= (bufferSample = backend.bufferCurs[i])) {
+ if ((pos = (startSample - bufferSample) * _sampleSize) < (bufferLen = backend.bufferLens[i])) {
+ buffer = backend.bufferViews[i].buffer;
+ c = 0;
+ while (startSample < endSample) {
+ callback(getByte(buffer, pos, byteRate), c);
+ if ((pos += byteRate) >= bufferLen) {
+ startSample++;
+ break;
+ }
+ else if (++c == data.channels) {
+ c = 0;
+ startSample++;
+ }
+ }
}
- n -= __wordSize;
}
-
- break;
+ i++;
}
- return endPos - (n / __toBits);
+ backend.mutex.release();
+
+ return startSample;
}
- #if lime_vorbis
- inline function __prepareDecoder():Bool @:privateAccess {
- if (buffer.__srcVorbisFile == null) return __vorbis != null;
- if (__vorbis != null) return true;
- if ((__vorbis = buffer.__srcVorbisFile.clone()) != null) { // IM HOPING IT HAVE A GC CLOSURE.
- __buffer = new ArrayBuffer(__bufferSize = (buffer.sampleRate >> 1) * __sampleSize); // 0.5 seconds of buffers.
+ // TODO: Fix this and _readDecoder in the future.
+ inline function _prepareDecoder():Bool {
+ return false;
+ /*
+ if (_decoder != null) return true;
+ if (data.decoder != null && (_decoder = data.decoder.clone()) != null) {
+ _bufferLen = (data.sampleRate >> 2) * _sampleSize;
+ #if cpp
+ if (_buffer != null) {
+ if (_buffer.length < _bufferLen) {
+ _buffer.getData().resize(_bufferLen);
+ _buffer.fill(_buffer.length, _bufferLen - _buffer.length, 0);
+ @:privateAccess _buffer.length = _bufferLen;
+ }
+ }
+ else
+ #end
+ _buffer = new ArrayBuffer(_bufferLen);
return true;
}
return false;
+ */
}
- inline function __readDecoder(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback) {
- var n = Math.floor((endPos - startPos) * __toBits);
- if ((n -= n % __sampleSize) > 0) {
- var pos = Math.floor((startPos - __bufferTime * 1000) * __toBits);
- pos -= pos % __sampleSize;
-
- var doRead = __bufferLastSize == 0 || (pos < 0 && pos >= __bufferSize);
- if (doRead) {
- if (startPos < 1) {
- __vorbis.rawSeek(0);
- __bufferTime = 0;
- }
- else
- __vorbis.timeSeek(__bufferTime = startPos / 1000);
+ /*
+ function _readDecoder(startSample:Int, endSample:Int, callback:ReadCallback) {
+ var pos = (startSample - _bufferLastSample) * _sampleSize, n = endSample - startSample, c = 0;
- __bufferLastSize = pos = 0;
- }
+ var doDecode = _bufferLastSize == 0 || (pos < 0 && pos >= _bufferLastSize);
+ if (doDecode) {
+ _decoder.seek(startSample);
+ _bufferLastSize = pos = 0;
+ doDecode = true;
+ }
- var isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN, ranOut = false, c = 0, result;
- while (true) {
- if (doRead) {
- result = __vorbis.read(__buffer, pos, __bufferSize - pos, isBigEndian, __wordSize, true);
- if (result == Vorbis.HOLE) continue;
- else if (result < 0) break;
- else if (!(ranOut = result == 0)) {
- __bufferLastTime = __vorbis.timeTell();
- __bufferLastSize += result;
- while (pos < __bufferLastSize) {
- callback(getByte(__buffer, pos, __wordSize), c);
- if (++c > buffer.channels) c = 0;
- pos += __wordSize;
- if ((n -= __wordSize) <= 0) break;
- }
+ var result:Int;
+ while (n > 0) {
+ if (doDecode) {
+ _bufferLastSample = _decoder.tell();
+ result = _decoder.decode(_buffer, pos, _bufferLen - pos);
+ if (result == 0) break;
+
+ _bufferLastSize += result;
+ while (n > 0) {
+ callback(getByte(_buffer, pos, data.byteRate), c);
+ if (++c == data.channels) {
+ c = 0;
+ n--;
}
+ if ((pos += data.byteRate) >= _bufferLastSize) break;
}
- else {
- while (pos < __bufferLastSize) {
- callback(getByte(__buffer, pos, __wordSize), c);
- if (++c > buffer.channels) c = 0;
- pos += __wordSize;
- if ((n -= __wordSize) <= 0) break;
+ }
+ else {
+ while (n > 0) {
+ callback(getByte(_buffer, pos, data.byteRate), c);
+ if (++c == data.channels) {
+ c = 0;
+ n--;
}
- doRead = true;
- ranOut = pos >= __bufferSize;
- }
-
- if (n <= 0) break;
- else if (doRead && ranOut) {
- __bufferLastSize = pos = 0;
- __bufferTime = __bufferLastTime;
+ if ((pos += data.byteRate) >= _bufferLastSize) break;
}
+ doDecode = true;
+ _bufferLastSize = pos = 0;
}
}
}
- #end
- #end
-}
\ No newline at end of file
+ */
+
+ static inline function idiv(num:Int, denom:Int):Int return #if (cpp && !cppia) cpp.NativeMath.idiv(num, denom) #else Std.int(num / denom) #end;
+}
+#end
\ No newline at end of file
diff --git a/source/funkin/backend/utils/NativeAPI.hx b/source/funkin/backend/utils/NativeAPI.hx
index d3b5e758e6..16e08c19b5 100644
--- a/source/funkin/backend/utils/NativeAPI.hx
+++ b/source/funkin/backend/utils/NativeAPI.hx
@@ -13,12 +13,6 @@ import flixel.util.FlxColor;
* Some functions might not have effect on some platforms.
*/
class NativeAPI {
- @:dox(hide) public static function registerAudio() {
- #if windows
- Windows.registerAudio();
- #end
- }
-
@:dox(hide) public static function registerAsDPICompatible() {
#if windows
Windows.registerAsDPICompatible();
@@ -185,16 +179,7 @@ class NativeAPI {
public static function setConsoleColors(foregroundColor:ConsoleColor = NONE, ?backgroundColor:ConsoleColor = NONE) {
if(Main.noTerminalColor) return;
- #if (windows && !hl)
- if(foregroundColor == NONE)
- foregroundColor = LIGHTGRAY;
- if(backgroundColor == NONE)
- backgroundColor = BLACK;
-
- var fg:Int = cast foregroundColor;
- var bg:Int = cast backgroundColor;
- Windows.setConsoleColors((bg * 16) + fg);
- #elseif sys
+ #if sys
Sys.print("\x1b[0m");
if(foregroundColor != NONE)
Sys.print("\x1b[" + Std.int(consoleColorToANSI(foregroundColor)) + "m");
diff --git a/source/funkin/backend/utils/native/Windows.hx b/source/funkin/backend/utils/native/Windows.hx
index 545e34b62a..1ac32c5b94 100644
--- a/source/funkin/backend/utils/native/Windows.hx
+++ b/source/funkin/backend/utils/native/Windows.hx
@@ -27,107 +27,10 @@ import funkin.backend.utils.NativeAPI.MessageBoxIcon;
#include
#include
#include
-
-#define SAFE_RELEASE(punk) \\
- if ((punk) != NULL) \\
- { (punk)->Release(); (punk) = NULL; }
-
-static long lastDefId = 0;
-
-class AudioFixClient : public IMMNotificationClient {
- LONG _cRef;
- IMMDeviceEnumerator *_pEnumerator;
-
- public:
- AudioFixClient() :
- _cRef(1),
- _pEnumerator(NULL)
- {
- HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator),
- NULL, CLSCTX_INPROC_SERVER,
- __uuidof(IMMDeviceEnumerator),
- (void**)&_pEnumerator);
- if (result == S_OK) {
- _pEnumerator->RegisterEndpointNotificationCallback(this);
- }
- }
-
- ~AudioFixClient()
- {
- SAFE_RELEASE(_pEnumerator);
- }
-
- ULONG STDMETHODCALLTYPE AddRef()
- {
- return InterlockedIncrement(&_cRef);
- }
-
- ULONG STDMETHODCALLTYPE Release()
- {
- ULONG ulRef = InterlockedDecrement(&_cRef);
- if (0 == ulRef)
- {
- delete this;
- }
- return ulRef;
- }
-
- HRESULT STDMETHODCALLTYPE QueryInterface(
- REFIID riid, VOID **ppvInterface)
- {
- return S_OK;
- }
-
- HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
- {
- return S_OK;
- };
-
- HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
- {
- return S_OK;
- }
-
- HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
- LPCWSTR pwstrDeviceId,
- DWORD dwNewState)
- {
- return S_OK;
- }
-
- HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
- LPCWSTR pwstrDeviceId,
- const PROPERTYKEY key)
- {
- return S_OK;
- }
-
- HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
- EDataFlow flow, ERole role,
- LPCWSTR pwstrDeviceId)
- {
- ::funkin::backend::_hx_system::Main_obj::audioDisconnected = true;
- return S_OK;
- };
-};
-
-AudioFixClient *curAudioFix;
')
@:dox(hide)
final class Windows {
- public static var __audioChangeCallback:Void->Void = function() {
- trace("test");
- };
-
-
- @:functionCode('
- if (!curAudioFix) curAudioFix = new AudioFixClient();
- ')
- public static function registerAudio() {
- funkin.backend.system.Main.audioDisconnected = false;
- }
-
@:functionCode('
int darkMode = enable ? 1 : 0;
diff --git a/source/funkin/editors/ui/UITextBox.hx b/source/funkin/editors/ui/UITextBox.hx
index b8b630923d..81bd5c11f0 100644
--- a/source/funkin/editors/ui/UITextBox.hx
+++ b/source/funkin/editors/ui/UITextBox.hx
@@ -72,7 +72,10 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
framesOffset = (selected ? 18 : (hovered ? 9 : 0));
@:privateAccess {
if (selected) {
- __wasFocused = true;
+ if (!__wasFocused) {
+ __wasFocused = true;
+ FlxG.stage.window.textInputEnabled = true;
+ }
caretSpr.alpha = (FlxG.game.ticks % 666) >= 333 ? 1 : 0;
var curPos = switch (position) {
@@ -92,6 +95,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
} else {
if (__wasFocused) {
__wasFocused = false;
+ FlxG.stage.window.textInputEnabled = false;
if (onChange != null)
onChange(label.text);
}
@@ -133,6 +137,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
focused = false;
if (onChange != null)
onChange(label.text);
+ return;
case LEFT:
if (modifier.ctrlKey) {
if (position == 0)
@@ -149,6 +154,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
}
changeSelection(-1);
+ return;
case RIGHT:
if (modifier.ctrlKey) {
if (position == label.text.length)
@@ -165,6 +171,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
}
changeSelection(1);
+ return;
case BACKSPACE:
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND);
@@ -181,6 +188,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
label.text = label.text.substr(0, position - 1) + label.text.substr(position);
changeSelection(-1);
}
+ return;
case DELETE:
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND);
@@ -196,48 +204,46 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
if (position < label.text.length) {
label.text = label.text.substr(0, position) + label.text.substr(position + 1);
}
+ return;
case HOME:
position = 0;
+ return;
case END:
position = label.text.length;
+ return;
case V:
- UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
- // Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
- // but somehow it gave a int outside of the KeyModifier's range :sob:
- // apparently there is a boolean that just checks for you. yw :D
+ if (modifier.ctrlKey) {
+ UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
- // if we are not holding ctrl, ignore
- if (!modifier.ctrlKey)
- return;
- // we pasting
- var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
- if (data != null)
- onTextInput(data);
+ // Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
+ // but somehow it gave a int outside of the KeyModifier's range :sob:
+ // apparently there is a boolean that just checks for you. yw :D
+
+ // we pasting
+ var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
+ if (data != null)
+ onTextInput(data);
+ }
case C:
- UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
- // if we are not holding ctrl, ignore
- if (!modifier.ctrlKey)
- return;
+ if (modifier.ctrlKey) {
+ UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
- // copying
- Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
+ Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
+ }
case X:
- UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
+ if (modifier.ctrlKey) {
+ UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
- // if we are not holding ctrl, ignore
- if (!modifier.ctrlKey)
+ Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
+ position = 0;
+ label.text = "";
return;
-
- // cutting
- Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
- position = 0;
- label.text = "";
+ }
default:
- if (modifier.ctrlKey || modifier.altKey || modifier.shiftKey)
- return;
-
- UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND);
}
+
+ if (modifier.ctrlKey || modifier.altKey || modifier.shiftKey) return;
+ UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND);
}
public function changeSelection(change:Int) {
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index 1e7eac6d6e..d5bee0bfdc 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -1082,7 +1082,7 @@ class PlayState extends MusicBeatState
if (notNull) PlayState.instance.gameAndCharsCall("onStageDestroy", [stage]);
scripts.call("destroy");
- for (g in __cachedGraphics) g.useCount--;
+ for (g in __cachedGraphics) g.decrementUseCount();
@:privateAccess {
for (strumLine in strumLines.members) FlxG.sound.destroySound(strumLine.vocals);
if (FlxG.sound.music != inst) FlxG.sound.destroySound(inst);
@@ -1231,11 +1231,6 @@ class PlayState extends MusicBeatState
@:dox(hide)
override public function onFocus():Void
{
- if (!paused && FlxG.autoPause) {
- for (strumLine in strumLines.members) strumLine.vocals.resume();
- inst.resume();
- vocals.resume();
- }
gameAndCharsCall("onFocus");
updateDiscordPresence();
super.onFocus();
@@ -1244,11 +1239,6 @@ class PlayState extends MusicBeatState
@:dox(hide)
override public function onFocusLost():Void
{
- if (!paused && FlxG.autoPause) {
- for (strumLine in strumLines.members) strumLine.vocals.pause();
- inst.pause();
- vocals.pause();
- }
gameAndCharsCall("onFocusLost");
updateDiscordPresence();
super.onFocusLost();
diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx
index 69fc3d8de1..7ee605baf3 100644
--- a/source/funkin/menus/FreeplayState.hx
+++ b/source/funkin/menus/FreeplayState.hx
@@ -189,37 +189,6 @@ class FreeplayState extends MusicBeatState
interpColor = new FlxInterpolateColor(bg.color);
}
- #if PRELOAD_ALL
- /**
- * How much time a song stays selected until it autoplays.
- */
- public var timeUntilAutoplay:Float = 1;
- /**
- * Whenever the song autoplays when hovered over.
- */
- public var disableAutoPlay:Bool = false;
- /**
- * Whenever the autoplayed song gets async loaded.
- */
- public var disableAsyncLoading:Bool = #if desktop false #else true #end;
- /**
- * Time elapsed since last autoplay. If this time exceeds `timeUntilAutoplay`, the currently selected song will play.
- */
- public var autoplayElapsed:Float = 0;
- /**
- * Whenever the currently selected song instrumental is playing.
- */
- public var songInstPlaying:Bool = true;
- /**
- * Path to the currently playing song instrumental.
- */
- public var curPlayingInst:String = null;
- /**
- * If it should play the song automatically.
- */
- public var autoplayShouldPlay:Bool = true;
- #end
-
private var TEXT_FREEPLAY_SCORE = TU.getRaw("freeplay.score");
override function update(elapsed:Float)
@@ -255,46 +224,6 @@ class FreeplayState extends MusicBeatState
interpColor.fpsLerpTo(curSong.color, 0.0625);
bg.color = interpColor.color;
- #if PRELOAD_ALL
- var dontPlaySongThisFrame = false;
- autoplayElapsed += elapsed;
- if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay)) {
- if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curDifficulties[curDifficulty], curSong.instSuffix))) {
- var streamed = false;
- if (Options.streamedMusic) {
- var sound = Assets.getMusic(curPlayingInst, true, false);
- streamed = sound != null;
-
- if (streamed && autoplayShouldPlay) {
- FlxG.sound.playMusic(sound, 0);
- Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
- }
- }
-
- if (!streamed) {
- var huh:Void->Void = function() {
- var soundPath = curPlayingInst;
- var sound = null;
- if (Assets.exists(soundPath, SOUND) || Assets.exists(soundPath, MUSIC))
- sound = Assets.getSound(soundPath);
- else
- FlxG.log.error('Could not find a Sound asset with an ID of \'$soundPath\'.');
-
- if (sound != null && autoplayShouldPlay) {
- FlxG.sound.playMusic(sound, 0);
- Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
- }
- }
- if (!disableAsyncLoading) Main.execAsync(huh);
- else huh();
- }
- }
- songInstPlaying = true;
- if (disableAsyncLoading && !Options.streamedMusic) dontPlaySongThisFrame = true;
- }
- #end
-
-
if (controls.BACK)
{
CoolUtil.playMenuSFX(CANCEL, 0.7);
@@ -306,7 +235,7 @@ class FreeplayState extends MusicBeatState
convertChart();
#end
- if (controls.ACCEPT #if PRELOAD_ALL && !dontPlaySongThisFrame #end)
+ if (controls.ACCEPT)
select();
}
@@ -338,10 +267,6 @@ class FreeplayState extends MusicBeatState
if (event.cancelled) return;
- #if PRELOAD_ALL
- autoplayShouldPlay = false;
- #end
-
Options.freeplayLastSong = curSong.name;
Options.freeplayLastDifficulty = curDifficulties[curDifficulty];
Options.freeplayLastVariation = curSong.variant;
@@ -377,13 +302,6 @@ class FreeplayState extends MusicBeatState
updateCurSong();
updateScore();
- #if PRELOAD_ALL
- if (curSong != prevSong) {
- autoplayElapsed = 0;
- songInstPlaying = false;
- }
- #end
-
var text = validDifficulties ? curDifficulties[curDifficulty].toUpperCase() + (curSong != songs[curSelected] ? ' (${curSong.variant.toUpperCase()})' : '') : '-';
diffText.text = curDifficulties.length > 1 ? '< $text >' : text;
}
@@ -466,11 +384,6 @@ class FreeplayState extends MusicBeatState
changeDiff(0, true);
- #if PRELOAD_ALL
- autoplayElapsed = 0;
- songInstPlaying = false;
- #end
-
coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed;
}
@@ -478,8 +391,8 @@ class FreeplayState extends MusicBeatState
var event = event("onUpdateOptionsAlpha", EventManager.get(FreeplayAlphaUpdateEvent).recycle(0.6, 0.45, 1, 1, 0.25));
if (event.cancelled) return;
- final idleAlpha = #if PRELOAD_ALL songInstPlaying ? event.idlePlayingAlpha : #end event.idleAlpha;
- final selectedAlpha = #if PRELOAD_ALL songInstPlaying ? event.selectedPlayingAlpha : #end event.selectedAlpha;
+ final idleAlpha = event.idleAlpha;
+ final selectedAlpha = event.selectedAlpha;
for (i in 0...iconArray.length)
iconArray[i].alpha = lerp(iconArray[i].alpha, idleAlpha, event.lerp);
diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx
index 7e73075658..9f4e8ab971 100644
--- a/source/funkin/options/Options.hx
+++ b/source/funkin/options/Options.hx
@@ -229,6 +229,7 @@ class Options
applyKeybinds();
applyQuality();
+ flixel.sound.FlxSoundData.allowStreaming = streamedMusic;
FlxG.sound.defaultMusicGroup.volume = volumeMusic;
FlxG.autoPause = autoPause;
if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = framerate;
diff --git a/source/haxe/Timer.hx b/source/haxe/Timer.hx
deleted file mode 100644
index 5092af8cc9..0000000000
--- a/source/haxe/Timer.hx
+++ /dev/null
@@ -1,297 +0,0 @@
-package haxe;
-
-#if !lime_cffi
-// Original haxe.Timer class
-
-/*
- * Copyright (C)2005-2018 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-/**
- The Timer class allows you to create asynchronous timers on platforms that
- support events.
-
- The intended usage is to create an instance of the Timer class with a given
- interval, set its run() method to a custom function to be invoked and
- eventually call stop() to stop the Timer.
-
- Note that a running Timer may or may not prevent the program to exit
- automatically when main() returns.
-
- It is also possible to extend this class and override its run() method in
- the child class.
-**/
-class Timer
-{
- #if (flash || js)
- private var id:Null;
- #elseif java
- private var timer:java.util.Timer;
- private var task:java.util.TimerTask;
- #elseif (haxe_ver >= "3.4.0")
- private var event:MainLoop.MainEvent;
- #end
-
- /**
- Creates a new timer that will run every `time_ms` milliseconds.
-
- After creating the Timer instance, it calls `this.run` repeatedly,
- with delays of `time_ms` milliseconds, until `this.stop` is called.
-
- The first invocation occurs after `time_ms` milliseconds, not
- immediately.
-
- The accuracy of this may be platform-dependent.
- **/
- public function new(time_ms:Int)
- {
- #if flash
- var me = this;
- id = untyped __global__["flash.utils.setInterval"](function()
- {
- me.run();
- }, time_ms);
- #elseif js
- var me = this;
- id = untyped setInterval(function() me.run(), time_ms);
- #elseif java
- timer = new java.util.Timer();
- timer.scheduleAtFixedRate(task = new TimerTask(this), haxe.Int64.ofInt(time_ms), haxe.Int64.ofInt(time_ms));
- #elseif (haxe_ver >= "3.4.0")
- var dt = time_ms / 1000;
- event = MainLoop.add(function()
- {
- @:privateAccess event.nextRun += dt;
- run();
- });
- event.delay(dt);
- #end
- }
-
- /**
- Stops `this` Timer.
-
- After calling this method, no additional invocations of `this.run`
- will occur.
-
- It is not possible to restart `this` Timer once stopped.
- **/
- public function stop()
- {
- #if (flash || js)
- if (id == null) return;
- #if flash
- untyped __global__["flash.utils.clearInterval"](id);
- #elseif js
- untyped clearInterval(id);
- #end
- id = null;
- #elseif java
- if (timer != null)
- {
- timer.cancel();
- timer = null;
- }
- task = null;
- #elseif (haxe_ver >= "3.4.0")
- if (event != null)
- {
- event.stop();
- event = null;
- }
- #end
- }
-
- /**
- This method is invoked repeatedly on `this` Timer.
-
- It can be overridden in a subclass, or rebound directly to a custom
- function:
- var timer = new haxe.Timer(1000); // 1000ms delay
- timer.run = function() { ... }
-
- Once bound, it can still be rebound to different functions until `this`
- Timer is stopped through a call to `this.stop`.
- **/
- public dynamic function run() {}
-
- /**
- Invokes `f` after `time_ms` milliseconds.
-
- This is a convenience function for creating a new Timer instance with
- `time_ms` as argument, binding its run() method to `f` and then stopping
- `this` Timer upon the first invocation.
-
- If `f` is null, the result is unspecified.
- **/
- public static function delay(f:Void->Void, time_ms:Int)
- {
- var t = new haxe.Timer(time_ms);
- t.run = function()
- {
- t.stop();
- f();
- };
- return t;
- }
-
- /**
- Measures the time it takes to execute `f`, in seconds with fractions.
-
- This is a convenience function for calculating the difference between
- Timer.stamp() before and after the invocation of `f`.
-
- The difference is passed as argument to Log.trace(), with "s" appended
- to denote the unit. The optional `pos` argument is passed through.
-
- If `f` is null, the result is unspecified.
- **/
- public static function measure(f:Void->T, ?pos:PosInfos):T
- {
- var t0 = stamp();
- var r = f();
- Log.trace((stamp() - t0) + "s", pos);
- return r;
- }
-
- /**
- Returns a timestamp, in seconds with fractions.
-
- The value itself might differ depending on platforms, only differences
- between two values make sense.
- **/
- public static inline function stamp():Float
- {
- #if flash
- return flash.Lib.getTimer() / 1000;
- #elseif (neko || php)
- return Sys.time();
- #elseif js
- return Date.now().getTime() / 1000;
- #elseif cpp
- return untyped __global__.__time_stamp();
- #elseif python
- return Sys.cpuTime();
- #elseif sys
- return Sys.time();
- #else
- return 0;
- #end
- }
-}
-
-#if java
-@:nativeGen
-private class TimerTask extends java.util.TimerTask
-{
- var timer:Timer;
-
- public function new(timer:Timer):Void
- {
- super();
- this.timer = timer;
- }
-
- @:overload override public function run():Void
- {
- timer.run();
- }
-}
-#end
-#else
-import lime.system.System;
-
-class Timer
-{
- private static var sRunningTimers:Array = [];
-
- private var mTime:Float;
- private var mFireAt:Float;
- private var mRunning:Bool;
-
- public function new(time:Float)
- {
- mTime = time;
- sRunningTimers.push(this);
- mFireAt = getMS() + mTime;
- mRunning = true;
- }
-
- public static function delay(f:Void->Void, time:Int)
- {
- var t = new Timer(time);
-
- t.run = function()
- {
- t.stop();
- f();
- };
-
- return t;
- }
-
- private static function getMS():Float
- {
- return System.getTimer();
- }
-
- public static function measure(f:Void->T, ?pos:PosInfos):T
- {
- var t0 = stamp();
- var r = f();
- Log.trace((stamp() - t0) + "s", pos);
- return r;
- }
-
- dynamic public function run() {}
-
- public static inline function stamp():Float
- {
- var timer = System.getTimer();
- return (timer > 0 ? timer / 1000 : 0);
- }
-
- public function stop():Void
- {
- /*if (mRunning)
- {
-
- for (i in 0...sRunningTimers.length)
- {
- if (sRunningTimers[i] == this)
- {
- sRunningTimers[i] = null;
- break;
- }
- }
- }*/
- mRunning = false;
- }
-
- @:noCompletion private function __check(inTime:Float)
- {
- if (inTime >= mFireAt)
- {
- mFireAt += mTime;
- run();
- }
- }
-}
-#end
diff --git a/source/lime/_internal/backend/html5/HTML5AudioSource.hx b/source/lime/_internal/backend/html5/HTML5AudioSource.hx
deleted file mode 100644
index 5d66fc04a5..0000000000
--- a/source/lime/_internal/backend/html5/HTML5AudioSource.hx
+++ /dev/null
@@ -1,283 +0,0 @@
-package lime._internal.backend.html5;
-
-import lime.math.Vector4;
-import lime.media.AudioSource;
-import lime.media.AudioManager;
-
-@:access(lime.media.AudioBuffer)
-class HTML5AudioSource
-{
- private var completed:Bool;
- private var gain:Float;
- private var id:Int;
- private var length:Null;
- private var loops:Int;
- private var parent:AudioSource;
- private var playing:Bool;
- private var position:Vector4;
-
- public function new(parent:AudioSource)
- {
- this.parent = parent;
-
- id = -1;
- gain = 1;
- position = new Vector4();
- }
-
- public function dispose():Void {
- stop();
- }
-
- public function init():Void {}
-
- public function play():Void
- {
- #if lime_howlerjs
- if (playing || parent.buffer == null || parent.buffer.__srcHowl == null)
- {
- return;
- }
-
- playing = true;
-
- var time = getCurrentTime();
-
- completed = false;
-
- var cacheVolume = untyped parent.buffer.__srcHowl._volume;
- untyped parent.buffer.__srcHowl._volume = parent.gain;
-
- id = parent.buffer.__srcHowl.play();
-
- untyped parent.buffer.__srcHowl._volume = cacheVolume;
- // setGain (parent.gain);
-
- setPosition(parent.position);
-
- parent.buffer.__srcHowl.on("end", howl_onEnd, id);
-
- // Calling setCurrentTime causes html5 audio to replay from this position on next frame
- #if force_html5_audio
- if (time == 0) setCurrentTime(time);
- #else
- setCurrentTime(time);
- #end
- #end
- }
-
- public function pause():Void
- {
- #if lime_howlerjs
- playing = false;
-
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- parent.buffer.__srcHowl.pause(id);
- }
- #end
- }
-
- public function stop():Void
- {
- #if lime_howlerjs
- playing = false;
-
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- parent.buffer.__srcHowl.stop(id);
- parent.buffer.__srcHowl.off("end", howl_onEnd, id);
- }
- #end
- }
-
- // Event Handlers
- private function howl_onEnd()
- {
- #if lime_howlerjs
- playing = false;
-
- if (loops > 0)
- {
- loops--;
- stop();
- if (loopTime != null && loopTime > 0) setCurrentTime(loopTime);
- play();
- parent.onLoop.dispatch();
- return;
- }
- else if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- parent.buffer.__srcHowl.stop(id);
- parent.buffer.__srcHowl.off("end", howl_onEnd, id);
- }
-
- completed = true;
- parent.onComplete.dispatch();
- #end
- }
-
- // Get & Set Methods
- public function getCurrentTime():Float
- {
- if (id == -1)
- {
- return 0;
- }
-
- #if lime_howlerjs
- if (completed)
- {
- return getLength();
- }
- else if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- var time = parent.buffer.__srcHowl.seek(id) * 1000.0 - parent.offset;
- if (time < 0) return 0;
- return time;
- }
- #end
-
- return 0;
- }
-
- public function getLatency():Float
- {
- var ctx = AudioManager.context.web;
- if (ctx != null)
- {
- var baseLatency:Float = untyped ctx.baseLatency != null ? untyped ctx.baseLatency : 0;
- var outputLatency:Float = untyped ctx.outputLatency != null ? untyped ctx.outputLatency : 0;
-
- return (baseLatency + outputLatency) * 1000;
- }
-
- return 0;
- }
-
- public function setCurrentTime(value:Float):Float
- {
- #if lime_howlerjs
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- // if (playing) buffer.__srcHowl.play (id);
- var pos = (value + parent.offset) / 1000;
- if (pos < 0) pos = 0;
- parent.buffer.__srcHowl.seek(pos, id);
- }
- #end
-
- return value;
- }
-
- public function getGain():Float
- {
- return gain;
- }
-
- public function setGain(value:Float):Float
- {
- #if lime_howlerjs
- // set howler volume only if we have an active id.
- // Passing -1 might create issues in future play()'s.
-
- if (parent.buffer != null && parent.buffer.__srcHowl != null && id != -1)
- {
- parent.buffer.__srcHowl.volume(value, id);
- }
- #end
-
- return gain = value;
- }
-
- public function getLength():Null
- {
- if (length != 0)
- {
- return length;
- }
-
- #if lime_howlerjs
- if (parent.buffer != null && parent.buffer.__srcHowl != null)
- {
- return parent.buffer.__srcHowl.duration() * 1000.0;
- }
- #end
-
- return 0;
- }
-
- public function setLength(value:Null):Null
- {
- return length = value;
- }
-
- public function getLoops():Int
- {
- return loops;
- }
-
- public function setLoops(value:Int):Int
- {
- return loops = value;
- }
-
- public function getLoopTime():Float {
- return loopTime;
- }
-
- public function setLoopTime(value:Float):Float {
- return loopTime = value;
- }
-
- public function getPitch():Float
- {
- #if lime_howlerjs
- return parent.buffer.__srcHowl.rate();
- #else
- return 1;
- #end
- }
-
- public function setPitch(value:Float):Float
- {
- #if lime_howlerjs
- parent.buffer.__srcHowl.rate(value);
- #end
-
- return getPitch();
- }
-
-
- public function getPosition():Vector4
- {
- return position;
- }
-
- public function setPosition(value:Vector4):Vector4
- {
- position.x = value.x;
- position.y = value.y;
- position.z = value.z;
- position.w = value.w;
-
- #if lime_howlerjs
- if (parent.buffer != null && parent.buffer.__srcHowl != null && parent.buffer.__srcHowl.pos != null) parent.buffer.__srcHowl.pos(position.x, position.y, position.z, id);
- // There are more settings to the position of the sound on the "pannerAttr()" function of howler. Maybe somebody who understands sound should look into it?
- #end
-
- return position;
- }
-
- public function getPan():Float
- {
- return position.x;
- }
-
- public function setPan(value:Float):Float
- {
- position.setTo(value, 0, -Math.sqrt(1 - value * value));
- if (parent.buffer != null && parent.buffer.__srcHowl != null && parent.buffer.__srcHowl.stereo != null) parent.buffer.__srcHowl.stereo(value, id);
- return value;
- }
-}
diff --git a/source/lime/_internal/backend/native/NativeApplication.hx b/source/lime/_internal/backend/native/NativeApplication.hx
deleted file mode 100644
index ac8ab467ed..0000000000
--- a/source/lime/_internal/backend/native/NativeApplication.hx
+++ /dev/null
@@ -1,981 +0,0 @@
-package lime._internal.backend.native;
-
-import haxe.Timer;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Application;
-import lime.graphics.opengl.GL;
-import lime.graphics.OpenGLRenderContext;
-import lime.graphics.RenderContext;
-import lime.math.Rectangle;
-import lime.media.AudioManager;
-import lime.system.Clipboard;
-import lime.system.Display;
-import lime.system.DisplayMode;
-import lime.system.JNI;
-import lime.system.Sensor;
-import lime.system.SensorType;
-import lime.system.System;
-import lime.ui.Gamepad;
-import lime.ui.Joystick;
-import lime.ui.JoystickHatPosition;
-import lime.ui.KeyCode;
-import lime.ui.KeyModifier;
-import lime.ui.Touch;
-import lime.ui.Window;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(haxe.Timer)
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime._internal.backend.native.NativeOpenGLRenderContext)
-@:access(lime._internal.backend.native.NativeWindow)
-@:access(lime.app.Application)
-@:access(lime.graphics.opengl.GL)
-@:access(lime.graphics.OpenGLRenderContext)
-@:access(lime.graphics.Renderer)
-@:access(lime.system.Clipboard)
-@:access(lime.system.Sensor)
-@:access(lime.ui.Gamepad)
-@:access(lime.ui.Joystick)
-@:access(lime.ui.Window)
-class NativeApplication
-{
- private var applicationEventInfo = new ApplicationEventInfo(UPDATE);
- private var clipboardEventInfo = new ClipboardEventInfo();
- private var currentTouches = new Map();
- private var dropEventInfo = new DropEventInfo();
- private var gamepadEventInfo = new GamepadEventInfo();
- private var joystickEventInfo = new JoystickEventInfo();
- private var keyEventInfo = new KeyEventInfo();
- private var mouseEventInfo = new MouseEventInfo();
- private var renderEventInfo = new RenderEventInfo(RENDER);
- private var sensorEventInfo = new SensorEventInfo();
- private var textEventInfo = new TextEventInfo();
- private var touchEventInfo = new TouchEventInfo();
- private var unusedTouchesPool = new List();
- private var windowEventInfo = new WindowEventInfo();
-
- public var handle:Dynamic;
-
- private var pauseTimer:Int;
- private var parent:Application;
- private var toggleFullscreen:Bool;
-
- private static function __init__()
- {
- #if (lime_cffi && !macro)
- var init = NativeCFFI;
- #end
- }
-
- public function new(parent:Application):Void
- {
- this.parent = parent;
- pauseTimer = -1;
- toggleFullscreen = true;
-
- AudioManager.init();
-
- #if (ios || android || tvos)
- Sensor.registerSensor(SensorType.ACCELEROMETER, 0);
- #end
-
- #if (!macro && lime_cffi)
- handle = NativeCFFI.lime_application_create();
- #end
- }
-
- private function advanceTimer():Void
- {
- #if lime_cffi
- if (pauseTimer > -1)
- {
- var offset = System.getTimer() - pauseTimer;
- for(timer in Timer.sRunningTimers) {
- if(timer != null && timer.mRunning) timer.mFireAt += offset;
- }
- pauseTimer = -1;
- }
- #end
- }
-
- public function exec():Int
- {
- #if !macro
- #if lime_cffi
- NativeCFFI.lime_application_event_manager_register(handleApplicationEvent, applicationEventInfo);
- NativeCFFI.lime_clipboard_event_manager_register(handleClipboardEvent, clipboardEventInfo);
- NativeCFFI.lime_drop_event_manager_register(handleDropEvent, dropEventInfo);
- NativeCFFI.lime_gamepad_event_manager_register(handleGamepadEvent, gamepadEventInfo);
- NativeCFFI.lime_joystick_event_manager_register(handleJoystickEvent, joystickEventInfo);
- NativeCFFI.lime_key_event_manager_register(handleKeyEvent, keyEventInfo);
- NativeCFFI.lime_mouse_event_manager_register(handleMouseEvent, mouseEventInfo);
- NativeCFFI.lime_render_event_manager_register(handleRenderEvent, renderEventInfo);
- NativeCFFI.lime_text_event_manager_register(handleTextEvent, textEventInfo);
- NativeCFFI.lime_touch_event_manager_register(handleTouchEvent, touchEventInfo);
- NativeCFFI.lime_window_event_manager_register(handleWindowEvent, windowEventInfo);
- #if (ios || android || tvos)
- NativeCFFI.lime_sensor_event_manager_register(handleSensorEvent, sensorEventInfo);
- #end
- #end
-
- #if (nodejs && lime_cffi)
- NativeCFFI.lime_application_init(handle);
-
- var eventLoop = function()
- {
- var active = NativeCFFI.lime_application_update(handle);
-
- if (!active)
- {
- untyped process.exitCode = NativeCFFI.lime_application_quit(handle);
- parent.onExit.dispatch(untyped process.exitCode);
- }
- else
- {
- untyped setImmediate(eventLoop);
- }
- }
-
- untyped setImmediate(eventLoop);
- return 0;
- #elseif lime_cffi
- var result = NativeCFFI.lime_application_exec(handle);
-
- #if (!webassembly && !ios && !nodejs)
- parent.onExit.dispatch(result);
- #end
-
- return result;
- #end
- #end
-
- return 0;
- }
-
- public function exit():Void
- {
- AudioManager.shutdown();
-
- #if (!macro && lime_cffi)
- NativeCFFI.lime_application_quit(handle);
- #end
- }
-
- private function handleApplicationEvent():Void
- {
- switch (applicationEventInfo.type)
- {
- case UPDATE:
- updateTimer();
-
- parent.onUpdate.dispatch(applicationEventInfo.deltaTime);
-
- default:
- }
- }
-
- private function handleClipboardEvent():Void
- {
- Clipboard.__update();
- }
-
- private function handleDropEvent():Void
- {
- for (window in parent.windows)
- {
- window.onDropFile.dispatch(#if hl @:privateAccess String.fromUTF8(dropEventInfo.file) #else dropEventInfo.file #end);
- }
- }
-
- private function handleGamepadEvent():Void
- {
- switch (gamepadEventInfo.type)
- {
- case AXIS_MOVE:
- var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
- if (gamepad != null) gamepad.onAxisMove.dispatch(gamepadEventInfo.axis, gamepadEventInfo.axisValue);
-
- case BUTTON_DOWN:
- var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
- if (gamepad != null) gamepad.onButtonDown.dispatch(gamepadEventInfo.button);
-
- case BUTTON_UP:
- var gamepad = Gamepad.devices.get(gamepadEventInfo.id);
- if (gamepad != null) gamepad.onButtonUp.dispatch(gamepadEventInfo.button);
-
- case CONNECT:
- Gamepad.__connect(gamepadEventInfo.id);
-
- case DISCONNECT:
- Gamepad.__disconnect(gamepadEventInfo.id);
- }
- }
-
- private function handleJoystickEvent():Void
- {
- switch (joystickEventInfo.type)
- {
- case AXIS_MOVE:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onAxisMove.dispatch(joystickEventInfo.index, joystickEventInfo.x);
-
- case HAT_MOVE:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onHatMove.dispatch(joystickEventInfo.index, joystickEventInfo.eventValue);
-
- case TRACKBALL_MOVE: // I guess this was just removed ??
-
- case BUTTON_DOWN:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onButtonDown.dispatch(joystickEventInfo.index);
-
- case BUTTON_UP:
- var joystick = Joystick.devices.get(joystickEventInfo.id);
- if (joystick != null) joystick.onButtonUp.dispatch(joystickEventInfo.index);
-
- case CONNECT:
- Joystick.__connect(joystickEventInfo.id);
-
- case DISCONNECT:
- Joystick.__disconnect(joystickEventInfo.id);
- }
- }
-
- private function handleKeyEvent():Void
- {
- var window = parent.__windowByID.get(keyEventInfo.windowID);
-
- if (window != null)
- {
- var type:KeyEventType = keyEventInfo.type;
- var int32:Float = keyEventInfo.keyCode;
- var keyCode:KeyCode = Std.int(int32);
- var modifier:KeyModifier = keyEventInfo.modifier;
-
- switch (type)
- {
- case KEY_DOWN:
- window.onKeyDown.dispatch(keyCode, modifier);
-
- case KEY_UP:
- window.onKeyUp.dispatch(keyCode, modifier);
- }
-
- #if (windows || linux)
- if (keyCode == RETURN)
- {
- if (type == KEY_DOWN)
- {
- if (toggleFullscreen && modifier.altKey && (!modifier.ctrlKey && !modifier.shiftKey && !modifier.metaKey))
- {
- toggleFullscreen = false;
-
- if (!window.onKeyDown.canceled)
- {
- window.fullscreen = !window.fullscreen;
- }
- }
- }
- else
- {
- toggleFullscreen = true;
- }
- }
-
- #if rpi
- if (keyCode == ESCAPE && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled)
- {
- System.exit(0);
- }
- #end
- #elseif mac
- if (keyCode == F)
- {
- if (type == KEY_DOWN)
- {
- if (toggleFullscreen && (modifier.ctrlKey && modifier.metaKey) && (!modifier.altKey && !modifier.shiftKey))
- {
- toggleFullscreen = false;
-
- if (!window.onKeyDown.canceled)
- {
- window.fullscreen = !window.fullscreen;
- }
- }
- }
- else
- {
- toggleFullscreen = true;
- }
- }
- #elseif android
- if (keyCode == APP_CONTROL_BACK && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled)
- {
- var mainActivity = JNI.createStaticField("org/haxe/extension/Extension", "mainActivity", "Landroid/app/Activity;");
- var moveTaskToBack = JNI.createMemberMethod("android/app/Activity", "moveTaskToBack", "(Z)Z");
-
- moveTaskToBack(mainActivity.get(), true);
- }
- #end
- }
- }
-
- private function handleMouseEvent():Void
- {
- var window = parent.__windowByID.get(mouseEventInfo.windowID);
-
- if (window != null)
- {
- switch (mouseEventInfo.type)
- {
- case MOUSE_DOWN:
- window.clickCount = mouseEventInfo.clickCount;
- window.onMouseDown.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button);
- window.clickCount = 0;
-
- case MOUSE_UP:
- window.clickCount = mouseEventInfo.clickCount;
- window.onMouseUp.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button);
- window.clickCount = 0;
-
- case MOUSE_MOVE:
- window.onMouseMove.dispatch(mouseEventInfo.x, mouseEventInfo.y);
- window.onMouseMoveRelative.dispatch(mouseEventInfo.movementX, mouseEventInfo.movementY);
-
- case MOUSE_WHEEL:
- window.onMouseWheel.dispatch(mouseEventInfo.x, mouseEventInfo.y, UNKNOWN);
-
- default:
- }
- }
- }
-
- private function handleRenderEvent():Void
- {
- // TODO: Allow windows to render independently
-
- for (window in parent.__windows)
- {
- if (window == null) continue;
-
- // parent.renderer = renderer;
-
- switch (renderEventInfo.type)
- {
- case RENDER:
- if (window.context != null)
- {
- window.__backend.render();
- window.onRender.dispatch(window.context);
-
- if (!window.onRender.canceled)
- {
- window.__backend.contextFlip();
- }
- }
-
- case RENDER_CONTEXT_LOST:
- if (window.__backend.useHardware && window.context != null)
- {
- switch (window.context.type)
- {
- case OPENGL, OPENGLES, WEBGL:
- #if (lime_cffi && (lime_opengl || lime_opengles) && !display)
- var gl = window.context.gl;
- (gl : NativeOpenGLRenderContext).__contextLost();
- if (GL.context == gl) GL.context = null;
- #end
-
- default:
- }
-
- window.context = null;
- window.onRenderContextLost.dispatch();
- }
-
- case RENDER_CONTEXT_RESTORED:
- if (window.__backend.useHardware)
- {
- // GL.context = new OpenGLRenderContext ();
- // window.context.gl = GL.context;
-
- window.onRenderContextRestored.dispatch(window.context);
- }
- }
- }
- }
-
- private function handleSensorEvent():Void
- {
- var sensor = Sensor.sensorByID.get(sensorEventInfo.id);
-
- if (sensor != null)
- {
- sensor.onUpdate.dispatch(sensorEventInfo.x, sensorEventInfo.y, sensorEventInfo.z);
- }
- }
-
- private function handleTextEvent():Void
- {
- var window = parent.__windowByID.get(textEventInfo.windowID);
-
- if (window != null)
- {
- switch (textEventInfo.type)
- {
- case TEXT_INPUT:
- window.onTextInput.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end);
-
- case TEXT_EDIT:
- window.onTextEdit.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end, textEventInfo.start,
- textEventInfo.length);
-
- default:
- }
- }
- }
-
- private function handleTouchEvent():Void
- {
- switch (touchEventInfo.type)
- {
- case TOUCH_START:
- var touch = unusedTouchesPool.pop();
-
- if (touch == null)
- {
- touch = new Touch(touchEventInfo.x, touchEventInfo.y, touchEventInfo.id, touchEventInfo.dx, touchEventInfo.dy, touchEventInfo.pressure,
- touchEventInfo.device);
- }
- else
- {
- touch.x = touchEventInfo.x;
- touch.y = touchEventInfo.y;
- touch.id = touchEventInfo.id;
- touch.dx = touchEventInfo.dx;
- touch.dy = touchEventInfo.dy;
- touch.pressure = touchEventInfo.pressure;
- touch.device = touchEventInfo.device;
- }
-
- currentTouches.set(touch.id, touch);
-
- Touch.onStart.dispatch(touch);
-
- case TOUCH_END:
- var touch = currentTouches.get(touchEventInfo.id);
-
- if (touch != null)
- {
- touch.x = touchEventInfo.x;
- touch.y = touchEventInfo.y;
- touch.dx = touchEventInfo.dx;
- touch.dy = touchEventInfo.dy;
- touch.pressure = touchEventInfo.pressure;
-
- Touch.onEnd.dispatch(touch);
-
- currentTouches.remove(touchEventInfo.id);
- unusedTouchesPool.add(touch);
- }
-
- case TOUCH_MOVE:
- var touch = currentTouches.get(touchEventInfo.id);
-
- if (touch != null)
- {
- touch.x = touchEventInfo.x;
- touch.y = touchEventInfo.y;
- touch.dx = touchEventInfo.dx;
- touch.dy = touchEventInfo.dy;
- touch.pressure = touchEventInfo.pressure;
-
- Touch.onMove.dispatch(touch);
- }
-
- default:
- }
- }
-
- private function handleWindowEvent():Void
- {
- var window = parent.__windowByID.get(windowEventInfo.windowID);
-
- if (window != null)
- {
- switch (windowEventInfo.type)
- {
- case WINDOW_ACTIVATE:
- advanceTimer();
- window.onActivate.dispatch();
- AudioManager.resume();
-
- case WINDOW_CLOSE:
- window.close();
-
- case WINDOW_DEACTIVATE:
- window.onDeactivate.dispatch();
- AudioManager.suspend();
- pauseTimer = System.getTimer();
-
- case WINDOW_ENTER:
- window.onEnter.dispatch();
-
- case WINDOW_EXPOSE:
- window.onExpose.dispatch();
-
- case WINDOW_FOCUS_IN:
- window.onFocusIn.dispatch();
-
- case WINDOW_FOCUS_OUT:
- window.onFocusOut.dispatch();
-
- case WINDOW_LEAVE:
- window.onLeave.dispatch();
-
- case WINDOW_MAXIMIZE:
- window.__maximized = true;
- window.__fullscreen = false;
- window.__minimized = false;
- window.onMaximize.dispatch();
-
- case WINDOW_MINIMIZE:
- window.__minimized = true;
- window.__maximized = false;
- window.__fullscreen = false;
- window.onMinimize.dispatch();
-
- case WINDOW_MOVE:
- window.__x = windowEventInfo.x;
- window.__y = windowEventInfo.y;
- window.onMove.dispatch(windowEventInfo.x, windowEventInfo.y);
-
- case WINDOW_RESIZE:
- window.__width = windowEventInfo.width;
- window.__height = windowEventInfo.height;
- window.onResize.dispatch(windowEventInfo.width, windowEventInfo.height);
-
- case WINDOW_RESTORE:
- window.__fullscreen = false;
- window.__minimized = false;
- window.onRestore.dispatch();
-
- case WINDOW_SHOW:
- window.onShow.dispatch();
-
- case WINDOW_HIDE:
- window.onHide.dispatch();
- }
- }
- }
-
- private function updateTimer():Void
- {
- #if lime_cffi
- if (Timer.sRunningTimers.length > 0)
- {
- var currentTime = System.getTimer();
- var foundNull = false;
- var timer;
-
- for (i in 0...Timer.sRunningTimers.length)
- {
- timer = Timer.sRunningTimers[i];
-
- if (timer != null && timer.mRunning)
- {
- if (currentTime >= timer.mFireAt)
- {
- timer.mFireAt += timer.mTime;
- timer.run();
- }
- }
- else
- {
- foundNull = true;
- }
- }
-
- if (foundNull)
- {
- Timer.sRunningTimers = Timer.sRunningTimers.filter(function(val)
- {
- return val != null && val.mRunning;
- });
- }
- }
-
- #if (haxe_ver >= 4.2)
- #if target.threaded
- sys.thread.Thread.current().events.progress();
- #else
- // Duplicate code required because Haxe 3 can't handle
- // #if (haxe_ver >= 4.2 && target.threaded)
- @:privateAccess haxe.EntryPoint.processEvents();
- #end
- #else
- @:privateAccess haxe.EntryPoint.processEvents();
- #end
- #end
- }
-}
-
-@:keep /*private*/ class ApplicationEventInfo
-{
- public var deltaTime:Int;
- public var type:ApplicationEventType;
-
- public function new(type:ApplicationEventType = null, deltaTime:Int = 0)
- {
- this.type = type;
- this.deltaTime = deltaTime;
- }
-
- public function clone():ApplicationEventInfo
- {
- return new ApplicationEventInfo(type, deltaTime);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ApplicationEventType(Int)
-{
- var UPDATE = 0;
- var EXIT = 1;
-}
-
-@:keep /*private*/ class ClipboardEventInfo
-{
- public var type:ClipboardEventType;
-
- public function new(type:ClipboardEventType = null)
- {
- this.type = type;
- }
-
- public function clone():ClipboardEventInfo
- {
- return new ClipboardEventInfo(type);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ClipboardEventType(Int)
-{
- var UPDATE = 0;
-}
-
-@:keep /*private*/ class DropEventInfo
-{
- public var file:#if hl hl.Bytes #else String #end;
- public var type:DropEventType;
-
- public function new(type:DropEventType = null, file = null)
- {
- this.type = type;
- this.file = file;
- }
-
- public function clone():DropEventInfo
- {
- return new DropEventInfo(type, file);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract DropEventType(Int)
-{
- var DROP_FILE = 0;
-}
-
-@:keep /*private*/ class GamepadEventInfo
-{
- public var axis:Int;
- public var button:Int;
- public var id:Int;
- public var type:GamepadEventType;
- public var axisValue:Float;
-
- public function new(type:GamepadEventType = null, id:Int = 0, button:Int = 0, axis:Int = 0, value:Float = 0)
- {
- this.type = type;
- this.id = id;
- this.button = button;
- this.axis = axis;
- this.axisValue = value;
- }
-
- public function clone():GamepadEventInfo
- {
- return new GamepadEventInfo(type, id, button, axis, axisValue);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract GamepadEventType(Int)
-{
- var AXIS_MOVE = 0;
- var BUTTON_DOWN = 1;
- var BUTTON_UP = 2;
- var CONNECT = 3;
- var DISCONNECT = 4;
-}
-
-@:keep /*private*/ class JoystickEventInfo
-{
- public var id:Int;
- public var index:Int;
- public var type:JoystickEventType;
- public var eventValue:Int;
- public var x:Float;
- public var y:Float;
-
- public function new(type:JoystickEventType = null, id:Int = 0, index:Int = 0, value:Int = 0, x:Float = 0, y:Float = 0)
- {
- this.type = type;
- this.id = id;
- this.index = index;
- this.eventValue = value;
- this.x = x;
- this.y = y;
- }
-
- public function clone():JoystickEventInfo
- {
- return new JoystickEventInfo(type, id, index, eventValue, x, y);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract JoystickEventType(Int)
-{
- var AXIS_MOVE = 0;
- var HAT_MOVE = 1;
- var TRACKBALL_MOVE = 2;
- var BUTTON_DOWN = 3;
- var BUTTON_UP = 4;
- var CONNECT = 5;
- var DISCONNECT = 6;
-}
-
-@:keep /*private*/ class KeyEventInfo
-{
- public var keyCode: Float;
- public var modifier:Int;
- public var type:KeyEventType;
- public var windowID:Int;
-
- public function new(type:KeyEventType = null, windowID:Int = 0, keyCode: Float = 0, modifier:Int = 0)
- {
- this.type = type;
- this.windowID = windowID;
- this.keyCode = keyCode;
- this.modifier = modifier;
- }
-
- public function clone():KeyEventInfo
- {
- return new KeyEventInfo(type, windowID, keyCode, modifier);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract KeyEventType(Int)
-{
- var KEY_DOWN = 0;
- var KEY_UP = 1;
-}
-
-@:keep /*private*/ class MouseEventInfo
-{
- public var button:Int;
- public var movementX:Float;
- public var movementY:Float;
- public var type:MouseEventType;
- public var windowID:Int;
- public var x:Float;
- public var y:Float;
- public var clickCount:Int;
-
- public function new(type:MouseEventType = null, windowID:Int = 0, x:Float = 0, y:Float = 0, button:Int = 0, movementX:Float = 0, movementY:Float = 0, clickCount:Int = 0)
- {
- this.type = type;
- this.windowID = 0;
- this.x = x;
- this.y = y;
- this.button = button;
- this.movementX = movementX;
- this.movementY = movementY;
- this.clickCount = clickCount;
- }
-
- public function clone():MouseEventInfo
- {
- return new MouseEventInfo(type, windowID, x, y, button, movementX, movementY, clickCount);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract MouseEventType(Int)
-{
- var MOUSE_DOWN = 0;
- var MOUSE_UP = 1;
- var MOUSE_MOVE = 2;
- var MOUSE_WHEEL = 3;
-}
-
-@:keep /*private*/ class RenderEventInfo
-{
- public var type:RenderEventType;
-
- public function new(type:RenderEventType = null)
- {
- this.type = type;
- }
-
- public function clone():RenderEventInfo
- {
- return new RenderEventInfo(type);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract RenderEventType(Int)
-{
- var RENDER = 0;
- var RENDER_CONTEXT_LOST = 1;
- var RENDER_CONTEXT_RESTORED = 2;
-}
-
-@:keep /*private*/ class SensorEventInfo
-{
- public var id:Int;
- public var x:Float;
- public var y:Float;
- public var z:Float;
- public var type:SensorEventType;
-
- public function new(type:SensorEventType = null, id:Int = 0, x:Float = 0, y:Float = 0, z:Float = 0)
- {
- this.type = type;
- this.id = id;
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
- public function clone():SensorEventInfo
- {
- return new SensorEventInfo(type, id, x, y, z);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract SensorEventType(Int)
-{
- var ACCELEROMETER = 0;
-}
-
-@:keep /*private*/ class TextEventInfo
-{
- public var id:Int;
- public var length:Int;
- public var start:Int;
- public var text:#if hl hl.Bytes #else String #end;
- public var type:TextEventType;
- public var windowID:Int;
-
- public function new(type:TextEventType = null, windowID:Int = 0, text = null, start:Int = 0, length:Int = 0)
- {
- this.type = type;
- this.windowID = windowID;
- this.text = text;
- this.start = start;
- this.length = length;
- }
-
- public function clone():TextEventInfo
- {
- return new TextEventInfo(type, windowID, text, start, length);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TextEventType(Int)
-{
- var TEXT_INPUT = 0;
- var TEXT_EDIT = 1;
-}
-
-@:keep /*private*/ class TouchEventInfo
-{
- public var device:Int;
- public var dx:Float;
- public var dy:Float;
- public var id:Int;
- public var pressure:Float;
- public var type:TouchEventType;
- public var x:Float;
- public var y:Float;
-
- public function new(type:TouchEventType = null, x:Float = 0, y:Float = 0, id:Int = 0, dx:Float = 0, dy:Float = 0, pressure:Float = 0, device:Int = 0)
- {
- this.type = type;
- this.x = x;
- this.y = y;
- this.id = id;
- this.dx = dx;
- this.dy = dy;
- this.pressure = pressure;
- this.device = device;
- }
-
- public function clone():TouchEventInfo
- {
- return new TouchEventInfo(type, x, y, id, dx, dy, pressure, device);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TouchEventType(Int)
-{
- var TOUCH_START = 0;
- var TOUCH_END = 1;
- var TOUCH_MOVE = 2;
-}
-
-@:keep /*private*/ class WindowEventInfo
-{
- public var height:Int;
- public var type:WindowEventType;
- public var width:Int;
- public var windowID:Int;
- public var x:Int;
- public var y:Int;
-
- public function new(type:WindowEventType = null, windowID:Int = 0, width:Int = 0, height:Int = 0, x:Int = 0, y:Int = 0)
- {
- this.type = type;
- this.windowID = windowID;
- this.width = width;
- this.height = height;
- this.x = x;
- this.y = y;
- }
-
- public function clone():WindowEventInfo
- {
- return new WindowEventInfo(type, windowID, width, height, x, y);
- }
-}
-
-#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract WindowEventType(Int)
-{
- var WINDOW_ACTIVATE = 0;
- var WINDOW_CLOSE = 1;
- var WINDOW_DEACTIVATE = 2;
- var WINDOW_ENTER = 3;
- var WINDOW_EXPOSE = 4;
- var WINDOW_FOCUS_IN = 5;
- var WINDOW_FOCUS_OUT = 6;
- var WINDOW_LEAVE = 7;
- var WINDOW_MAXIMIZE = 8;
- var WINDOW_MINIMIZE = 9;
- var WINDOW_MOVE = 10;
- var WINDOW_RESIZE = 11;
- var WINDOW_RESTORE = 12;
- var WINDOW_SHOW = 13;
- var WINDOW_HIDE = 14;
-}
diff --git a/source/lime/_internal/backend/native/NativeAudioSource.hx b/source/lime/_internal/backend/native/NativeAudioSource.hx
deleted file mode 100644
index 9f09b24471..0000000000
--- a/source/lime/_internal/backend/native/NativeAudioSource.hx
+++ /dev/null
@@ -1,804 +0,0 @@
-package lime._internal.backend.native;
-
-import sys.thread.Thread;
-import sys.thread.Mutex;
-
-import haxe.Timer;
-import haxe.Int64;
-
-import lime.media.openal.AL;
-import lime.media.openal.ALBuffer;
-import lime.media.openal.ALSource;
-
-#if lime_vorbis
-import lime.media.vorbis.Vorbis;
-import lime.media.vorbis.VorbisFile;
-import lime.media.vorbis.VorbisInfo;
-#end
-
-import lime.math.Vector2;
-import lime.math.Vector4;
-import lime.media.AudioBuffer;
-import lime.media.AudioSource;
-import lime.system.Endian;
-import lime.system.System;
-import lime.utils.ArrayBuffer;
-import lime.utils.ArrayBufferView.TypedArrayType;
-import lime.utils.ArrayBufferView;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(haxe.Timer)
-@:access(lime.media.AudioBuffer)
-@:access(lime.utils.ArrayBufferView)
-class NativeAudioSource {
- // Can hold up to 3 hours 44100 sampleRate audio, if you are into that, theorically.
-
- public static var STREAM_BUFFER_SAMPLES:Int = 0x2000; // how much buffers will be generating every frequency (doesnt have to be pow of 2?).
- public static var STREAM_MIN_BUFFERS:Int = 2; // how much buffers can a stream hold on minimum or starting.
- public static var STREAM_MAX_BUFFERS:Int = 8; // how much limit of a buffers can be used for streamed audios, must be higher than minimum.
- public static var STREAM_MAX_FLUSH_BUFFERS:Int = 3; // how much buffers can it play.
- public static var STREAM_PROCESS_BUFFERS:Int = 2; // how much buffers can be processed in a frequency tick.
- public static var POOL_MAX_BUFFERS:Int = 32; // how much buffers for the pool to hold.
-
- public static var moreFormatsSupported:Null;
- public static var loopPointsSupported:Null;
- public static var stereoAnglesExtensionSupported:Null;
- public static var latencyExtensionSupported:Null;
-
- private static var bufferDataPool:Array = [];
- private static var isBigEndian:Bool = System.endianness == Endian.BIG_ENDIAN;
-
- public static function getALFormat(bitsPerSample:Int, channels:Int):Int {
- if (moreFormatsSupported == null) moreFormatsSupported = AL.isExtensionPresent("AL_EXT_MCFORMATS");
-
- // There was a code to also supports for X-Fi Renderer but, that kind of device is
- // rare nowadays and none of sounds should have more than 24 bitsPerSample.
- // https://github.com/kcat/openal-soft/issues/934
-
- if (channels > 2 && moreFormatsSupported) {
- if (channels == 3) return bitsPerSample == 32 ? 0x1209 : (bitsPerSample == 16 ? 0x1208 : 0x1207);
- else if (channels == 4) return bitsPerSample == 32 ? 0x1206 : (bitsPerSample == 16 ? 0x1205 : 0x1204);
- else if (channels == 6) return bitsPerSample == 32 ? 0x120C : (bitsPerSample == 16 ? 0x120B : 0x120A);
- else if (channels == 7) return bitsPerSample == 32 ? 0x120F : (bitsPerSample == 16 ? 0x120E : 0x120D);
- else if (channels == 8) return bitsPerSample == 32 ? 0x1212 : (bitsPerSample == 16 ? 0x1211 : 0x1210);
- else return AL.FORMAT_MONO8;
- }
- else if (bitsPerSample == 32 && moreFormatsSupported) return channels == 2 ? 0x1203 : 0x1202;
- else if (channels == 2) return bitsPerSample == 16 ? AL.FORMAT_STEREO16 : AL.FORMAT_STEREO8;
- else return bitsPerSample == 16 ? AL.FORMAT_MONO16 : AL.FORMAT_MONO8;
- }
-
- private static function resetTimer(timer:Timer, time:Float, callback:Void->Void):Timer {
- if (timer == null) (timer = new Timer(time)).run = callback;
- else {
- timer.mTime = time;
- timer.mFireAt = Timer.getMS() + time;
- timer.mRunning = true;
- timer.run = callback;
-
- if (!Timer.sRunningTimers.contains(timer)) Timer.sRunningTimers.push(timer);
- }
- return timer;
- }
-
- inline private static function getFloat(x:Int64):Float return x.high * 4294967296. + (x.low >> 0);
-
- // Backward Compatibility Variables
- var handle(get, set):ALSource; inline function get_handle() return source; inline function set_handle(v) return source = v;
- var timer(get, set):Timer; inline function get_timer() return completeTimer; inline function set_timer(v) return completeTimer = v;
- var length(get, set):Null; inline function get_length() return endTime; inline function set_length(v) return endTime = v;
- var toLoop(get, set):Int; inline function get_toLoop() return streamLoops; inline function set_toLoop(v) return streamLoops = v;
- var bufferSizes(get, set):Array; inline function get_bufferSizes() return bufferLengths; inline function set_bufferSizes(v) return bufferLengths = v;
-
- var parent:AudioSource;
- var disposed:Bool;
- var streamed:Bool;
- var playing:Bool;
- var completed:Bool;
- var lastTime:Float;
-
- var position:Vector4;
- var angles:Vector2;
- var anglesArray:Array;
- var endTime:Null;
- var loopTime:Float;
- var loops:Int;
-
- var channels:Int;
- var sampleRate:Int;
- var wordSize:Int; // is bitsPerSample >> 3, ex: 8 bits is 1, 16 bits is 2, 32 bits is 4, etc.
- var samples:Int;
- var dataLength:Int;
- var duration:Float;
-
- var completeTimer:Timer;
- var source:ALSource;
- var buffer:ALBuffer;
- var standaloneBuffer:Bool;
- var format:Int; // AL.FORMAT_...
- var arrayType:TypedArrayType;
- var loopPoints:Array; // In Samples
-
- static var streamSources:Array = [];
- static var queuedStreamSources:Array = [];
-
- static var streamMutex:Mutex = new Mutex();
- static var streamTimer:Timer;
-
- #if !ALLOW_MULTITHREADING
- static var wasEmpty:Bool = false;
- static var threadRunning:Bool = false;
- static var streamThread:Thread;
- #end
-
- var streamRemove:Bool;
-
- var bufferLength:Int; // Size in bytes for current streamed audio buffers.
- var requestBuffers:Int;
- var queuedBuffers:Int;
- var streamLoops:Int;
- var streamEnded:Bool;
-
- // ORDERING IS CURRENT TO NEXT, STARTS FROM THE LENGTH OF THE ARRAYS
- var bufferDatas:Array;
- var bufferTimes:Array;
- var bufferLengths:Array;
-
- var buffers:Array;
- var nextBuffer:Int = 0;
-
- public function new(parent:AudioSource) {
- this.parent = parent;
-
- if (loopPointsSupported == null) loopPointsSupported = AL.isExtensionPresent("AL_SOFT_loop_points");
- if (stereoAnglesExtensionSupported == null) stereoAnglesExtensionSupported = AL.isExtensionPresent("AL_EXT_STEREO_ANGLES");
- if (latencyExtensionSupported == null) latencyExtensionSupported = AL.isExtensionPresent("AL_SOFT_source_latency");
- }
-
- public function dispose() {
- streamMutex.acquire();
- removeStream();
-
- stop();
- disposed = true;
-
- position = null;
- angles = null;
- anglesArray = null;
-
- if (source != null) {
- if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
- else AL.sourcei(source, AL.BUFFER, AL.NONE);
-
- AL.deleteSource(source);
- source = null;
- }
-
- if (standaloneBuffer && buffer != null) {
- AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffer(buffer);
- buffer = null;
- }
- loopPoints = null;
-
- if (buffers != null) {
- for (buffer in buffers) AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffers(buffers);
- buffers = null;
- }
-
- if (bufferDatas != null) {
- for (data in bufferDatas) if (bufferDataPool.length < POOL_MAX_BUFFERS) bufferDataPool.push(data);
- bufferDatas = null;
- }
-
- completeTimer = null;
-
- bufferTimes = null;
- bufferLengths = null;
-
- streamMutex.release();
- }
-
- public function init() {
- if (source != null || (disposed = parent == null || (source = AL.createSource()) == null)) return;
- AL.sourcef(source, AL.MAX_GAIN, 32);
- AL.distanceModel(AL.NONE);
-
- if (position == null) position = new Vector4();
- if (angles == null) angles = new Vector2(Math.PI / 6, -Math.PI / 6); // https://github.com/kcat/openal-soft/issues/1032
- if (loopPoints == null) loopPoints = [0, 0];
- if (stereoAnglesExtensionSupported && anglesArray == null) anglesArray = [0, 0];
-
- resetBuffer();
- }
-
- public function resetBuffer() {
- if (parent.buffer == null) return;
-
- streamMutex.acquire();
- removeStream();
-
- stop();
-
- if (streamed) AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
- else AL.sourcei(source, AL.BUFFER, AL.NONE);
-
- streamMutex.release();
-
- final audioBuffer = parent.buffer;
- channels = audioBuffer.channels;
- sampleRate = audioBuffer.sampleRate;
- wordSize = audioBuffer.bitsPerSample >> 3;
- format = getALFormat(audioBuffer.bitsPerSample, channels);
- arrayType = wordSize == 4 ? TypedArrayType.Uint32 : (wordSize == 2 ? TypedArrayType.Uint16 : TypedArrayType.Int8);
- standaloneBuffer = false;
- loopTime = 0;
- endTime = null;
-
- if (buffer != null) {
- if (standaloneBuffer) {
- AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffer(buffer);
- }
- buffer = null;
- }
-
- if (audioBuffer.data != null) {
- streamed = false;
- samples = Std.int((dataLength = audioBuffer.data.byteLength) / wordSize / channels);
- }
- #if lime_vorbis
- else if (audioBuffer.__srcVorbisFile != null) {
- streamed = true;
- dataLength = Std.int(getFloat(samples = Int64.toInt(audioBuffer.__srcVorbisFile.pcmTotal())) * channels * wordSize);
- }
- #end
- else return;
-
- duration = getFloat(samples) / sampleRate * 1000;
-
- loopPoints[0] = 0;
- loopPoints[1] = samples - 1;
-
- if (streamed) {
- final length = STREAM_BUFFER_SAMPLES * channels;
- bufferLength = length * wordSize;
-
- if (buffers == null) buffers = AL.genBuffers(STREAM_MAX_FLUSH_BUFFERS);
- if (bufferDatas == null) {
- bufferDatas = [];
- bufferTimes = [];
- bufferLengths = [];
- }
-
- for (i in 0...STREAM_MAX_BUFFERS) {
- bufferTimes[i] = 0.0;
- bufferLengths[i] = 0;
-
- var data = bufferDataPool.pop();
- if (data == null) data = new ArrayBufferView(length, arrayType);
- else {
- data.type = arrayType;
- data.bytesPerElement = wordSize;
- data.length = length;
- if (data.byteLength != bufferLength) {
- #if cpp
- data.buffer.getData().resize(bufferLength);
- data.buffer.fill(data.byteLength, bufferLength - data.byteLength, 0);
- @:privateAccess data.buffer.length = bufferLength;
- #else
- data.buffer = new ArrayBuffer(bufferLength);
- #end
- }
- data.byteLength = bufferLength;
- }
- bufferDatas[i] = data;
- }
- }
- else {
- if (buffers != null) {
- for (buffer in buffers) AL.bufferData(buffer, 0, null, 0, 0);
- AL.deleteBuffers(buffers);
- buffers = null;
-
- for (data in bufferDatas) if (bufferDataPool.length < POOL_MAX_BUFFERS) bufferDataPool.push(data);
- bufferDatas.resize(0);
- }
-
- if (audioBuffer.__srcBuffer != null) {
- if (AL.getBufferi(audioBuffer.__srcBuffer, AL.SIZE) != dataLength) {
- AL.bufferData(audioBuffer.__srcBuffer, 0, null, 0, 0);
- AL.deleteBuffer(audioBuffer.__srcBuffer);
- if ((buffer = audioBuffer.__srcBuffer = AL.createBuffer()) != null)
- AL.bufferData(buffer, format, audioBuffer.data, dataLength, sampleRate);
- }
- else
- buffer = audioBuffer.__srcBuffer;
- }
- else if ((buffer = audioBuffer.__srcBuffer = AL.createBuffer()) != null)
- AL.bufferData(buffer, format, audioBuffer.data, dataLength, sampleRate);
-
- AL.sourcei(source, AL.BUFFER, buffer);
- }
-
- updateLoopPoints();
- }
-
- function updateLoopPoints() {
- if (loops <= 0) return AL.sourcei(source, AL.LOOPING, AL.FALSE);
-
- var time = getCurrentTime() + parent.offset, length = getRealLength();
- final fixed = time >= length;
- if (fixed) time = loopTime;
-
- if (!streamed) {
- var internalLoop = AL.TRUE;
- if (length < duration - 1 && loopTime > 1) {
- if (!loopPointsSupported) internalLoop = AL.FALSE;
- else {
- AL.sourceStop(source);
- AL.sourcei(source, AL.BUFFER, AL.NONE);
- if (!standaloneBuffer) {
- if (standaloneBuffer = (buffer = AL.createBuffer()) != null)
- AL.bufferData(buffer, format, parent.buffer.data, dataLength, sampleRate);
- else {
- buffer = parent.buffer.__srcBuffer;
- internalLoop = AL.FALSE;
- }
- }
- if (internalLoop == AL.TRUE) AL.bufferiv(buffer, 0x2015/*AL.LOOP_POINTS_SOFT*/, loopPoints);
- AL.sourcei(source, AL.BUFFER, buffer);
- }
- }
-
- AL.sourcei(source, AL.LOOPING, internalLoop);
- if (playing) setCurrentTime(time - parent.offset);
- else updateCompleteTimer();
- }
- else if (playing && (fixed || streamLoops > 0)) {
- AL.sourcei(source, AL.LOOPING, AL.FALSE);
- AL.sourceStop(source);
- snapBuffersToTime(time, streamLoops > 0);
- AL.sourcePlay(source);
- }
- }
-
- // https://github.com/xiph/vorbis/blob/master/CHANGES#L39 bug in libvorbis <= 1.3.4
- inline function streamSeek(samples:Int64)
- if (samples <= 1) parent.buffer.__srcVorbisFile.rawSeek(0); else parent.buffer.__srcVorbisFile.pcmSeek(samples);
-
- inline function streamTell():Int64
- return parent.buffer.__srcVorbisFile.pcmTell();
-
- inline function streamRead(buffer:ArrayBuffer, position:Int, length:Int, wordSize:Int):Int
- return parent.buffer.__srcVorbisFile.read(buffer, position, length, isBigEndian, wordSize, true);
-
- function readToBufferData(data:ArrayBufferView, currentPCM:Int64):Int {
- var length = (Int64.ofInt(loopPoints[1]) - currentPCM) * channels * wordSize;
- var n = length < bufferLength ? length.low : bufferLength, total = 0, result = 0, wasEOF = false;
-
- try while (total < bufferLength) {
- result = n > 0 ? streamRead(data.buffer, total, n, wordSize) : 0;
-
- if (result == Vorbis.HOLE) continue;
- else if (result <= Vorbis.EREAD) break;
- else if (result == 0) {
- if (streamEnded = wasEOF == (wasEOF = true) || loops <= streamLoops) break;
-
- streamSeek(Int64.ofInt(loopPoints[0]));
- streamLoops++;
- if ((length = Int64.ofInt(loopPoints[1] - loopPoints[0]) * channels * wordSize) < (n = bufferLength - total)) n = length.low;
- }
- else {
- total += result;
- n -= result;
- wasEOF = false;
- }
- }
- catch (e:haxe.Exception) {
- trace('NativeAudioSource readToBufferData Bug! error: ${e.details()}, streamEnded: $streamEnded, total: $total, n: $n');
- return result;
- }
-
- if (result < 0) {
- trace('NativeAudioSource readToBufferData Bug! reading result is $result, streamEnded: $streamEnded, total: $total, n: $n');
- return result;
- }
- return total;
- }
-
- function fillBuffers(n:Int) {
- final max = STREAM_MAX_BUFFERS - 1;
- var i:Int, j:Int, data:ArrayBufferView, pcm:Int64, decoded:Int;
- while (n-- > 0 && !streamEnded && (decoded = readToBufferData(data = bufferDatas[i = max - requestBuffers], pcm = streamTell())) > 0) {
- j = i;
- while (i < max) {
- bufferDatas[i] = bufferDatas[++j];
- bufferTimes[i] = bufferTimes[j];
- bufferLengths[i] = bufferLengths[j];
- i = j;
- }
- bufferDatas[max] = data;
- bufferTimes[max] = getFloat(pcm) / sampleRate;
- bufferLengths[max] = decoded;
- requestBuffers++;
- }
- }
-
- inline function flushBuffers() {
- var i = STREAM_MAX_BUFFERS - (requestBuffers - queuedBuffers);
- while (queuedBuffers < STREAM_MAX_FLUSH_BUFFERS && queuedBuffers < requestBuffers) {
- AL.bufferData(buffers[nextBuffer], format, bufferDatas[i], bufferLengths[i], sampleRate);
- AL.sourceQueueBuffer(source, buffers[nextBuffer]);
- if (++nextBuffer == STREAM_MAX_FLUSH_BUFFERS) nextBuffer = 0;
- queuedBuffers++;
- i++;
- }
- }
-
- inline function skipBuffers(n:Int) {
- queuedBuffers -= (n = AL.sourceUnqueueBuffers(source, n).length);
- requestBuffers -= n;
- }
-
- function snapBuffersToTime(time:Float, force:Bool) {
- if (source == null || parent.buffer == null || parent.buffer.__srcVorbisFile == null) return;
-
- streamMutex.acquire();
-
- final sec = time / 1000;
- if (!force) {
- var bufferTime:Float;
- for (i in (STREAM_MAX_BUFFERS - requestBuffers)...(STREAM_MAX_BUFFERS - STREAM_MIN_BUFFERS))
- if (sec >= (bufferTime = bufferTimes[i]) && sec < bufferTime + (bufferLengths[i] / wordSize / channels / sampleRate))
- {
- skipBuffers(i - STREAM_MAX_BUFFERS + requestBuffers);
- AL.sourcei(source, AL.SAMPLE_OFFSET, Math.floor((sec - bufferTime) * sampleRate));
- return streamMutex.release();
- }
- }
-
- AL.sourceUnqueueBuffers(source, AL.getSourcei(source, AL.BUFFERS_QUEUED));
-
- streamEnded = false;
- streamSeek(Int64.fromFloat(sec * sampleRate));
-
- requestBuffers = queuedBuffers = streamLoops = nextBuffer = 0;
- fillBuffers(STREAM_MIN_BUFFERS);
- flushBuffers();
- streamMutex.release();
- }
-
- static function streamBuffersUpdate() {
- streamMutex.acquire();
-
- var i:Int = streamSources.length, source:NativeAudioSource, process:Int, v:Int;
- while (i-- > 0) {
- if ((source = streamSources[i]).streamRemove) continue;
- else if (source.parent.buffer == null) {
- source.stopStream();
- continue;
- }
-
- process = source.requestBuffers < STREAM_MIN_BUFFERS ? STREAM_MIN_BUFFERS - source.requestBuffers : 0;
- process = STREAM_PROCESS_BUFFERS > process ? STREAM_PROCESS_BUFFERS : process;
- if ((process = (v = STREAM_MAX_BUFFERS - source.requestBuffers) > process ? process : v) > 0) source.fillBuffers(process);
- }
-
- streamMutex.release();
- }
-
- #if !ALLOW_MULTITHREADING
- static function streamThreadRun() {
- while (Thread.readMessage(true)) streamBuffersUpdate();
- threadRunning = false;
- }
- #end
-
- static function streamUpdate() {
- if (!streamMutex.tryAcquire()) return;
-
- var i = queuedStreamSources.length, source:NativeAudioSource;
- while (i-- > 0) streamSources.push(queuedStreamSources[i]);
- queuedStreamSources.resize(0);
-
- i = streamSources.length;
- while (i-- > 0) {
- if ((source = streamSources[i]).streamRemove || source.source == null) source.removeStream();
- else {
- source.skipBuffers(AL.getSourcei(source.source, AL.BUFFERS_PROCESSED));
- source.flushBuffers();
- if (AL.getSourcei(source.source, AL.SOURCE_STATE) == AL.STOPPED) {
- AL.sourcePlay(source.source);
- source.updateCompleteTimer();
- }
- if (source.streamEnded && source.requestBuffers == source.queuedBuffers) source.removeStream();
- }
- }
-
- #if ALLOW_MULTITHREADING
- if (streamSources.length != 0) funkin.backend.utils.ThreadUtil.execAsync(streamBuffersUpdate);
- #else
- if (streamSources.length == 0) {
- if (wasEmpty) {
- wasEmpty = false;
- streamTimer.stop();
- if (threadRunning) streamThread.sendMessage(1);
- }
- else {
- wasEmpty = true;
- streamTimer = resetTimer(streamTimer, 1000, streamUpdate);
- }
- }
- else {
- wasEmpty = false;
- if (threadRunning || (threadRunning = (streamThread = Thread.create(streamThreadRun)) != null))
- streamThread.sendMessage(1);
- }
- #end
-
- streamMutex.release();
- }
-
- function removeStream() {
- streamRemove = false;
- queuedStreamSources.remove(this);
- streamSources.remove(this);
- }
-
- function stopStream() {
- streamRemove = true;
- queuedStreamSources.remove(this);
- }
-
- function resetStream() {
- streamRemove = false;
- if (!queuedStreamSources.contains(this) && !streamSources.contains(this)) {
- queuedStreamSources.push(this);
- if (streamTimer == null || !streamTimer.mRunning) streamTimer = resetTimer(streamTimer, 0, streamUpdate);
- }
- }
-
- function timer_onRun() {
- final pitch = getPitch();
- var timeRemaining = (getLength() - getCurrentTime()) / pitch;
- if (timeRemaining > 50 && AL.getSourcei(source, AL.SOURCE_STATE) == AL.PLAYING && (!streamed || !streamEnded && streamLoops <= 0)) {
- completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
- return;
- }
-
- completeTimer.stop();
-
- if (loops == 0) return complete();
-
- if (streamLoops > 0) {
- loops -= streamLoops;
- streamLoops = 0;
- completeTimer = resetTimer(completeTimer, (getLength() + parent.offset - loopTime) / pitch, timer_onRun);
- }
- else if (!loopPointsSupported || AL.getSourcei(source, AL.LOOPING) == AL.FALSE) {
- loops--;
- playing = true;
- setCurrentTime(loopTime - parent.offset);
- }
-
- if (loops <= 0) {
- loops = 0;
- AL.sourcei(source, AL.LOOPING, AL.FALSE);
- }
-
- parent.onLoop.dispatch();
- }
-
- function updateCompleteTimer() {
- if (playing) {
- var timeRemaining = (getLength() - getCurrentTime()) / getPitch();
- if (timeRemaining > 50) completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
- else {
- if (completeTimer != null) completeTimer.stop();
- if (loops > 0) play();
- else complete();
- }
- }
- else if (completeTimer != null)
- completeTimer.stop();
- }
-
- public function play() {
- if (playing || disposed) return;
- final time = completed ? 0 : getCurrentTime();
- playing = true;
- setCurrentTime(time);
- }
-
- public function pause() {
- if (!disposed) AL.sourcePause(source);
- lastTime = getCurrentTime();
- playing = false;
- stopStream();
- if (completeTimer != null) completeTimer.stop();
- }
-
- public function stop() {
- if (!disposed) AL.sourceStop(source);
- lastTime = 0;
- streamLoops = 0;
- playing = false;
- stopStream();
- if (completeTimer != null) completeTimer.stop();
- }
-
- public function complete() {
- if (!completed) parent.onComplete.dispatch();
- stop();
- completed = true;
- }
-
- public function getCurrentTime():Float {
- if (disposed) return 0.0;
- else if (completed) return getLength();
- else if (!playing) return lastTime - parent.offset;
-
- var time = AL.getSourcef(source, AL.SEC_OFFSET);
- if (streamed) {
- if (playing && streamEnded && AL.getSourcei(source, AL.SOURCE_STATE) == AL.STOPPED) {
- complete();
- return getLength();
- }
- else if (bufferTimes != null)
- time += bufferTimes[STREAM_MAX_BUFFERS - requestBuffers];
- }
- time *= 1000;
-
- var length = getRealLength();
- return if (loops <= 0 || time <= length) time - parent.offset;
- else ((time - loopTime) % (length - loopTime)) + loopTime - parent.offset;
- }
-
- public function setCurrentTime(value:Float):Float {
- if (disposed) return 0.0;
-
- final length = getRealLength();
- value = Math.isFinite(value) ? Math.max(Math.min(value + parent.offset, length), parent.offset) : parent.offset;
-
- if (streamed) AL.sourceStop(source);
- else AL.sourcef(source, AL.SEC_OFFSET, value / 1000);
-
- final timeRemaining = (length - value) / getPitch();
- if (playing) {
- if (timeRemaining < 8 && value > 8) complete();
- else {
- completed = false;
- if (streamed) {
- snapBuffersToTime(value, false);
- if (!streamEnded) resetStream();
- }
- if (AL.getSourcei(source, AL.SOURCE_STATE) != AL.PLAYING) AL.sourcePlay(source);
- completeTimer = resetTimer(completeTimer, timeRemaining, timer_onRun);
- }
- }
- else {
- completed = timeRemaining < 8;
- lastTime = value;
- if (completeTimer != null) completeTimer.stop();
- }
-
- return value;
- }
-
- public function getPitch():Float {
- return if (disposed) 1;
- else AL.getSourcef(source, AL.PITCH);
- }
-
- public function setPitch(value:Float):Float {
- if (disposed || (value = Math.max(value, 0)) == AL.getSourcef(source, AL.PITCH)) return value;
- AL.sourcef(source, AL.PITCH, value);
- updateCompleteTimer();
- return value;
- }
-
- public function getGain():Float {
- return if (disposed) 1;
- else AL.getSourcef(source, AL.GAIN);
- }
-
- public function setGain(value:Float):Float {
- value = Math.max(value, 0);
- if (!disposed) AL.sourcef(source, AL.GAIN, value);
- return value;
- }
-
- public function getLoops():Int return loops;
-
- public function setLoops(value:Int):Int {
- if (loops == (loops = value < 0 ? 0 : value)) return loops;
- updateLoopPoints();
- return loops;
- }
-
- public function getLoopTime():Float return loopTime - parent.offset;
-
- public function setLoopTime(value:Float):Float {
- if (loopTime == (loopTime = Math.max(Math.min(value + parent.offset, duration), 0))) return loopTime - parent.offset;
- if ((loopPoints[0] = Std.int(value / 1000 * sampleRate)) >= samples) loopPoints[0] = samples - 1;
- updateLoopPoints();
- return loopTime - parent.offset;
- }
-
- public function getRealLength():Float return if (endTime == null) duration; else endTime;
- public function getLength():Float return if (disposed) 0; else (inline getRealLength()) - parent.offset;
-
- public function setLength(value:Null):Null {
- if (endTime == (endTime = (value == null ? value : Math.max(Math.min(value + parent.offset, duration), 0)))) return endTime - parent.offset;
- if ((loopPoints[1] = Std.int(getRealLength() / 1000 * sampleRate)) >= samples) loopPoints[1] = samples - 1;
- updateLoopPoints();
- return endTime - parent.offset;
- }
-
- public function getLatency():Float {
- //#if (lime >= "8.4.0")
- //if (latencyExtensionSupported) {
- // final offsets = AL.getSourcedvSOFT(source, AL.SEC_OFFSET_LATENCY_SOFT, 2);
- // if (offsets != null) return offsets[1] * 1000;
- //}
- //#end
- return 0;
- }
-
- public function getAngles():Vector2 {
- if (angles == null) angles = new Vector2(Math.PI / 6, -Math.PI / 6);
- return angles;
- }
-
- public function setAngles(left:Float, right:Float):Vector2 {
- if (angles == null) angles = new Vector2(left, right);
- else angles.setTo(left, right);
-
- if (!disposed && stereoAnglesExtensionSupported) {
- anglesArray[0] = angles.x;
- anglesArray[1] = angles.y;
- AL.sourcei(source, 0x1214/*AL.SOURCE_SPATIALIZE_SOFT*/, AL.FALSE);
- AL.sourcefv(source, 0x1030/*AL.STEREO_ANGLES*/, anglesArray);
- AL.source3f(source, AL.POSITION, 0, 0, 0);
- }
- return angles;
- }
-
- public function getPosition():Vector4 {
- if (position == null) position = new Vector4();
- return position;
- }
-
- public function setPosition(value:Vector4):Vector4 {
- position.x = value.x;
- position.y = value.y;
- position.z = value.z;
- position.w = value.w;
-
- // OpenAL Soft Positions doesn't seem to do anything but panning?
- if (!disposed) {
- if (stereoAnglesExtensionSupported) AL.sourcei(source, 0x1214/*AL.SOURCE_SPATIALIZE_SOFT*/, Math.abs(position.x) > 1e-04 ? AL.TRUE : AL.FALSE);
- AL.sourcei(source, AL.MAX_DISTANCE, 1);
- AL.source3f(source, AL.POSITION, position.x, position.y, position.z);
- }
- return position;
- }
-
- public function getPan():Float return getPosition().x;
-
- public function setPan(value:Float):Float {
- getPosition().setTo(value, 0, -Math.sqrt(1 - value * value));
- if (!disposed) {
- if (parent.buffer.channels > 1)
- setAngles(Math.PI * (Math.min(-value * 2 + 1, 1)) / 6, -Math.PI * Math.min(value * 2 + 1, 1) / 6);
- else
- setPosition(position);
- }
- return value;
- }
-}
\ No newline at end of file
diff --git a/source/lime/_internal/backend/native/NativeWindow.hx b/source/lime/_internal/backend/native/NativeWindow.hx
deleted file mode 100644
index 397d94ac90..0000000000
--- a/source/lime/_internal/backend/native/NativeWindow.hx
+++ /dev/null
@@ -1,766 +0,0 @@
-package lime._internal.backend.native;
-
-import haxe.io.Bytes;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Application;
-import lime.graphics.cairo.Cairo;
-import lime.graphics.cairo.CairoFormat;
-import lime.graphics.cairo.CairoImageSurface;
-import lime.graphics.cairo.CairoSurface;
-import lime.graphics.opengl.GL;
-import lime.graphics.CairoRenderContext;
-import lime.graphics.Image;
-import lime.graphics.ImageBuffer;
-import lime.graphics.OpenGLRenderContext;
-import lime.graphics.RenderContext;
-import lime.math.Rectangle;
-import lime.math.Vector2;
-import lime.system.Display;
-import lime.system.DisplayMode;
-import lime.system.JNI;
-import lime.system.System;
-import lime.ui.MouseCursor;
-import lime.ui.Window;
-import lime.utils.UInt8Array;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime._internal.backend.native.NativeOpenGLRenderContext)
-@:access(lime.app.Application)
-@:access(lime.graphics.cairo.Cairo)
-@:access(lime.graphics.opengl.GL)
-@:access(lime.graphics.OpenGLRenderContext)
-@:access(lime.graphics.RenderContext)
-@:access(lime.system.DisplayMode)
-@:access(lime.ui.Window)
-class NativeWindow
-{
- public var handle:Dynamic;
-
- private var closing:Bool;
- private var cursor:MouseCursor;
- private var displayMode:DisplayMode;
- private var frameRate:Float;
- private var mouseLock:Bool;
- private var parent:Window;
- private var useHardware:Bool;
- #if lime_cairo
- private var cacheLock:Dynamic;
- private var cairo:Cairo;
- private var primarySurface:CairoSurface;
- #end
-
- public function new(parent:Window)
- {
- this.parent = parent;
-
- cursor = DEFAULT;
- displayMode = new DisplayMode(0, 0, 0, 0);
-
- var attributes = parent.__attributes;
- var contextAttributes = Reflect.hasField(attributes, "context") ? attributes.context : {};
- var title = Reflect.hasField(attributes, "title") ? attributes.title : "Lime Application";
- var flags = 0;
-
- if (!Reflect.hasField(contextAttributes, "antialiasing")) contextAttributes.antialiasing = 0;
- if (!Reflect.hasField(contextAttributes, "background")) contextAttributes.background = 0;
- if (!Reflect.hasField(contextAttributes, "colorDepth")) contextAttributes.colorDepth = 24;
- if (!Reflect.hasField(contextAttributes, "depth")) contextAttributes.depth = true;
- if (!Reflect.hasField(contextAttributes, "hardware")) contextAttributes.hardware = true;
- if (!Reflect.hasField(contextAttributes, "stencil")) contextAttributes.stencil = true;
- if (!Reflect.hasField(contextAttributes, "vsync")) contextAttributes.vsync = false;
-
- #if (cairo || (!lime_opengl && !lime_opengles))
- contextAttributes.type = CAIRO;
- #end
- if (Reflect.hasField(contextAttributes, "type") && contextAttributes.type == CAIRO) contextAttributes.hardware = false;
-
- if (Reflect.hasField(attributes, "allowHighDPI") && attributes.allowHighDPI) flags |= cast WindowFlags.WINDOW_FLAG_ALLOW_HIGHDPI;
- if (Reflect.hasField(attributes, "alwaysOnTop") && attributes.alwaysOnTop) flags |= cast WindowFlags.WINDOW_FLAG_ALWAYS_ON_TOP;
- if (Reflect.hasField(attributes, "borderless") && attributes.borderless) flags |= cast WindowFlags.WINDOW_FLAG_BORDERLESS;
- if (Reflect.hasField(attributes, "fullscreen") && attributes.fullscreen) flags |= cast WindowFlags.WINDOW_FLAG_FULLSCREEN;
- if (Reflect.hasField(attributes, "hidden") && attributes.hidden) flags |= cast WindowFlags.WINDOW_FLAG_HIDDEN;
- if (Reflect.hasField(attributes, "maximized") && attributes.maximized) flags |= cast WindowFlags.WINDOW_FLAG_MAXIMIZED;
- if (Reflect.hasField(attributes, "minimized") && attributes.minimized) flags |= cast WindowFlags.WINDOW_FLAG_MINIMIZED;
- if (Reflect.hasField(attributes, "resizable") && attributes.resizable) flags |= cast WindowFlags.WINDOW_FLAG_RESIZABLE;
-
- if (contextAttributes.antialiasing >= 4)
- {
- flags |= cast WindowFlags.WINDOW_FLAG_HW_AA_HIRES;
- }
- else if (contextAttributes.antialiasing >= 2)
- {
- flags |= cast WindowFlags.WINDOW_FLAG_HW_AA;
- }
-
- if (contextAttributes.colorDepth == 32) flags |= cast WindowFlags.WINDOW_FLAG_COLOR_DEPTH_32_BIT;
- if (contextAttributes.depth) flags |= cast WindowFlags.WINDOW_FLAG_DEPTH_BUFFER;
- if (contextAttributes.hardware) flags |= cast WindowFlags.WINDOW_FLAG_HARDWARE;
- if (contextAttributes.stencil) flags |= cast WindowFlags.WINDOW_FLAG_STENCIL_BUFFER;
- if (contextAttributes.vsync) flags |= cast WindowFlags.WINDOW_FLAG_VSYNC;
-
- var width = Reflect.hasField(attributes, "width") ? attributes.width : #if desktop 800 #else 0 #end;
- var height = Reflect.hasField(attributes, "height") ? attributes.height : #if desktop 600 #else 0 #end;
-
- #if (!macro && lime_cffi)
- handle = NativeCFFI.lime_window_create(parent.application.__backend.handle, width, height, flags, title);
-
- #if (DARK_MODE_WINDOW && !macro)
- funkin.backend.utils.NativeAPI.setDarkMode(title, true);
- #end
-
- if (handle != null)
- {
- parent.__width = NativeCFFI.lime_window_get_width(handle);
- parent.__height = NativeCFFI.lime_window_get_height(handle);
- parent.__x = NativeCFFI.lime_window_get_x(handle);
- parent.__y = NativeCFFI.lime_window_get_y(handle);
- parent.__hidden = (Reflect.hasField(attributes, "hidden") && attributes.hidden);
- parent.id = NativeCFFI.lime_window_get_id(handle);
- }
-
- parent.__scale = NativeCFFI.lime_window_get_scale(handle);
-
- var context = new RenderContext();
- context.window = parent;
-
- #if hl
- var contextType = @:privateAccess String.fromUTF8(NativeCFFI.lime_window_get_context_type(handle));
- #else
- var contextType:String = NativeCFFI.lime_window_get_context_type(handle);
- #end
-
- switch (contextType)
- {
- case "opengl":
- var gl = new NativeOpenGLRenderContext();
-
- useHardware = true;
-
- #if lime_opengl
- context.gl = gl;
- #end
-
- context.gles2 = gl;
- context.webgl = gl;
- context.type = gl.type;
- context.version = Std.string(gl.version);
-
- if (gl.type == OPENGLES && gl.version >= 3)
- {
- context.gles3 = gl;
- context.webgl2 = gl;
- }
-
- if (GL.context == null)
- {
- GL.context = gl;
- }
-
- default:
- useHardware = false;
-
- #if lime_cairo
- context.cairo = cairo;
- context.type = CAIRO;
- context.version = "";
-
- parent.context = context;
- render();
- #end
- context.type = CAIRO;
- }
-
- contextAttributes.type = context.type;
- context.attributes = contextAttributes;
- parent.context = context;
-
- setFrameRate(Reflect.hasField(attributes, "frameRate") ? attributes.frameRate : 60);
- #end
- }
-
- public function alert(message:String, title:String):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_alert(handle, message, title);
- #end
- }
- }
-
- public function close():Void
- {
- if (!closing)
- {
- closing = true;
- parent.onClose.dispatch();
-
- if (!parent.onClose.canceled)
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_close(handle);
- #end
- handle = null;
- }
- }
- else
- {
- closing = false;
- }
- }
- }
-
- public function contextFlip():Void
- {
- #if (!macro && lime_cffi)
- if (!useHardware)
- {
- #if lime_cairo
- if (cairo != null)
- {
- primarySurface.flush();
- }
- #end
- NativeCFFI.lime_window_context_unlock(handle);
- }
-
- NativeCFFI.lime_window_context_flip(handle);
- #end
- }
-
- public function focus():Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_focus(handle);
- #end
- }
- }
-
- public function getCursor():MouseCursor
- {
- return cursor;
- }
-
- public function getDisplay():Display
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- var index = NativeCFFI.lime_window_get_display(handle);
-
- if (index > -1)
- {
- return System.getDisplay(index);
- }
- #end
- }
-
- return null;
- }
-
- public function getDisplayMode():DisplayMode
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- #if hl
- NativeCFFI.lime_window_get_display_mode(handle, displayMode);
- #else
- var data:Dynamic = NativeCFFI.lime_window_get_display_mode(handle);
- displayMode.width = data.width;
- displayMode.height = data.height;
- displayMode.pixelFormat = data.pixelFormat;
- displayMode.refreshRate = data.refreshRate;
- #end
- #end
- }
-
- return displayMode;
- }
-
- public function getFrameRate():Float
- {
- return frameRate;
- }
-
- public function getMouseLock():Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- mouseLock = NativeCFFI.lime_window_get_mouse_lock(handle);
- #end
- }
-
- return mouseLock;
- }
-
- public function getTextInputEnabled():Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_get_text_input_enabled(handle);
- #end
- }
-
- return false;
- }
-
- public function move(x:Int, y:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_move(handle, x, y);
- #end
- }
- }
-
- public function readPixels(rect:Rectangle):Image
- {
- var imageBuffer:ImageBuffer = null;
-
- switch (parent.context.type)
- {
- case OPENGL, OPENGLES, WEBGL:
- var gl = parent.context.webgl;
- var windowWidth = Std.int(parent.__width * parent.__scale);
- var windowHeight = Std.int(parent.__height * parent.__scale);
-
- var x, y, width, height;
-
- if (rect != null)
- {
- x = Std.int(rect.x);
- y = Std.int((windowHeight - rect.y) - rect.height);
- width = Std.int(rect.width);
- height = Std.int(rect.height);
- }
- else
- {
- x = 0;
- y = 0;
- width = windowWidth;
- height = windowHeight;
- }
-
- var data = new UInt8Array(width * height * 4);
-
- gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
-
- #if !js // TODO
-
- var rowLength = width * 4;
- var srcPosition = (height - 1) * rowLength;
- var destPosition = 0;
-
- var temp = Bytes.alloc(rowLength);
- var buffer = data.buffer;
- var rows = Std.int(height / 2);
-
- while (rows-- > 0)
- {
- temp.blit(0, buffer, destPosition, rowLength);
- buffer.blit(destPosition, buffer, srcPosition, rowLength);
- buffer.blit(srcPosition, temp, 0, rowLength);
-
- destPosition += rowLength;
- srcPosition -= rowLength;
- }
- #end
-
- imageBuffer = new ImageBuffer(data, width, height, 32, RGBA32);
-
- default:
- #if (!macro && lime_cffi)
- #if !cs
- imageBuffer = NativeCFFI.lime_window_read_pixels(handle, rect, new ImageBuffer(new UInt8Array(Bytes.alloc(0))));
- #else
- var data:Dynamic = NativeCFFI.lime_window_read_pixels(handle, rect, null);
- if (data != null)
- {
- imageBuffer = new ImageBuffer(new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b)), data.width, data.height,
- data.bitsPerPixel);
- }
- #end
- #end
-
- if (imageBuffer != null)
- {
- imageBuffer.format = RGBA32;
- }
- }
-
- if (imageBuffer != null)
- {
- return new Image(imageBuffer);
- }
-
- return null;
- }
-
- public function render():Void
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_context_make_current(handle);
-
- if (!useHardware)
- {
- #if lime_cairo
- var lock:Dynamic = NativeCFFI.lime_window_context_lock(handle);
-
- if (lock != null
- && (cacheLock == null || cacheLock.pixels != lock.pixels || cacheLock.width != lock.width || cacheLock.height != lock.height))
- {
- primarySurface = CairoImageSurface.create(lock.pixels, CairoFormat.ARGB32, lock.width, lock.height, lock.pitch);
-
- if (cairo != null)
- {
- cairo.recreate(primarySurface);
- }
- else
- {
- cairo = new Cairo(primarySurface);
- }
-
- parent.context.cairo = cairo;
- }
-
- cacheLock = lock;
- #else
- parent.context = null;
- #end
- }
- #end
- }
-
- public function resize(width:Int, height:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_resize(handle, width, height);
- #end
- }
- }
-
- #if (lime >= "8.1.0")
- public function setMinSize(width:Int, height:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_minimum_size(handle, width, height);
- #end
- }
- }
-
- public function setMaxSize(width:Int, height:Int):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_maximum_size(handle, width, height);
- #end
- }
- }
- #end
-
- public function setBorderless(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_borderless(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setCursor(value:MouseCursor):MouseCursor
- {
- if (cursor != value)
- {
- if (value == null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_cursor(handle, 0);
- #end
- }
- else
- {
- var type:MouseCursorType = switch (value)
- {
- case ARROW: ARROW;
- case CROSSHAIR: CROSSHAIR;
- case MOVE: MOVE;
- case POINTER: POINTER;
- case RESIZE_NESW: RESIZE_NESW;
- case RESIZE_NS: RESIZE_NS;
- case RESIZE_NWSE: RESIZE_NWSE;
- case RESIZE_WE: RESIZE_WE;
- case TEXT: TEXT;
- case WAIT: WAIT;
- case WAIT_ARROW: WAIT_ARROW;
- default: DEFAULT;
- }
-
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_cursor(handle, type);
- #end
- }
-
- cursor = value;
- }
-
- return cursor;
- }
-
- public function setDisplayMode(value:DisplayMode):DisplayMode
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- #if hl
- NativeCFFI.lime_window_set_display_mode(handle, value, displayMode);
- #else
- var data:Dynamic = NativeCFFI.lime_window_set_display_mode(handle, value);
- displayMode.width = data.width;
- displayMode.height = data.height;
- displayMode.pixelFormat = data.pixelFormat;
- displayMode.refreshRate = data.refreshRate;
- #end
- #end
- }
-
- return displayMode;
- }
-
- public function setMouseLock(value:Bool):Bool
- {
- if (mouseLock != value)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_mouse_lock(handle, value);
- #end
-
- mouseLock = value;
- }
-
- return mouseLock;
- }
-
- public function setTextInputEnabled(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_text_input_enabled(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setTextInputRect(value:Rectangle):Rectangle
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_text_input_rect(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setFrameRate(value:Float):Float
- {
- // TODO: Support multiple independent frame rates per window
-
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_application_set_frame_rate(parent.application.__backend.handle, value);
- #end
- }
-
- return frameRate = value;
- }
-
- public function setFullscreen(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- value = NativeCFFI.lime_window_set_fullscreen(handle, value);
-
- parent.__width = NativeCFFI.lime_window_get_width(handle);
- parent.__height = NativeCFFI.lime_window_get_height(handle);
- parent.__x = NativeCFFI.lime_window_get_x(handle);
- parent.__y = NativeCFFI.lime_window_get_y(handle);
- #end
-
- if (value)
- {
- parent.onFullscreen.dispatch();
- }
- }
-
- return value;
- }
-
- public function setIcon(image:Image):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_icon(handle, image.buffer);
- #end
- }
- }
-
- public function setMaximized(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_set_maximized(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setMinimized(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_set_minimized(handle, value);
- #end
- }
-
- return value;
- }
-
- public function setResizable(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_resizable(handle, value);
-
- // TODO: remove need for workaround
-
- NativeCFFI.lime_window_set_borderless(handle, !parent.__borderless);
- NativeCFFI.lime_window_set_borderless(handle, parent.__borderless);
- #end
- }
-
- return value;
- }
-
- public function setTitle(value:String):String
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_set_title(handle, value);
- #end
- }
-
- return value;
- }
-
- #if (lime >= "8.1.0")
- public function setVisible(value:Bool):Bool
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_visible(handle, value);
- #end
- }
-
- return value;
- }
-
- public function getOpacity():Float
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- return NativeCFFI.lime_window_get_opacity(handle);
- #end
- }
-
- return 1.0;
- }
-
- public function setOpacity(value:Float):Void
- {
- if (handle != null)
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_set_opacity(handle, value);
- #end
- }
- }
- #end
-
- public function warpMouse(x:Int, y:Int):Void
- {
- #if (!macro && lime_cffi)
- NativeCFFI.lime_window_warp_mouse(handle, x, y);
- #end
- }
-}
-
-enum abstract MouseCursorType(Int) from Int to Int
-{
- var HIDDEN = 0;
- var ARROW = 1;
- var CROSSHAIR = 2;
- var DEFAULT = 3;
- var MOVE = 4;
- var POINTER = 5;
- var RESIZE_NESW = 6;
- var RESIZE_NS = 7;
- var RESIZE_NWSE = 8;
- var RESIZE_WE = 9;
- var TEXT = 10;
- var WAIT = 11;
- var WAIT_ARROW = 12;
-}
-
-enum abstract WindowFlags(Int)
-{
- var WINDOW_FLAG_FULLSCREEN = 0x00000001;
- var WINDOW_FLAG_BORDERLESS = 0x00000002;
- var WINDOW_FLAG_RESIZABLE = 0x00000004;
- var WINDOW_FLAG_HARDWARE = 0x00000008;
- var WINDOW_FLAG_VSYNC = 0x00000010;
- var WINDOW_FLAG_HW_AA = 0x00000020;
- var WINDOW_FLAG_HW_AA_HIRES = 0x00000060;
- var WINDOW_FLAG_ALLOW_SHADERS = 0x00000080;
- var WINDOW_FLAG_REQUIRE_SHADERS = 0x00000100;
- var WINDOW_FLAG_DEPTH_BUFFER = 0x00000200;
- var WINDOW_FLAG_STENCIL_BUFFER = 0x00000400;
- var WINDOW_FLAG_ALLOW_HIGHDPI = 0x00000800;
- var WINDOW_FLAG_HIDDEN = 0x00001000;
- var WINDOW_FLAG_MINIMIZED = 0x00002000;
- var WINDOW_FLAG_MAXIMIZED = 0x00004000;
- var WINDOW_FLAG_ALWAYS_ON_TOP = 0x00008000;
- var WINDOW_FLAG_COLOR_DEPTH_32_BIT = 0x00010000;
-}
\ No newline at end of file
diff --git a/source/lime/media/AudioBuffer.hx b/source/lime/media/AudioBuffer.hx
deleted file mode 100644
index 30c5448baa..0000000000
--- a/source/lime/media/AudioBuffer.hx
+++ /dev/null
@@ -1,518 +0,0 @@
-package lime.media;
-
-import haxe.io.Bytes;
-import haxe.io.Path;
-import lime._internal.backend.native.NativeCFFI;
-import lime._internal.format.Base64;
-import lime.app.Future;
-import lime.app.Promise;
-import lime.media.openal.AL;
-import lime.media.openal.ALBuffer;
-#if lime_vorbis
-import lime.media.vorbis.Vorbis;
-import lime.media.vorbis.VorbisFile;
-#end
-import lime.net.HTTPRequest;
-import lime.utils.Log;
-import lime.utils.UInt8Array;
-#if lime_howlerjs
-import lime.media.howlerjs.Howl;
-#end
-#if (js && html5)
-import js.html.Audio;
-#elseif flash
-import flash.media.Sound;
-import flash.net.URLRequest;
-#end
-
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime.utils.Assets)
-#if hl
-@:keep
-#end
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-
-/**
- The `AudioBuffer` class represents a buffer of audio data that can be played back using an `AudioSource`.
- It supports a variety of audio formats and platforms, providing a consistent API for loading and managing audio data.
-
- Depending on the platform, the audio backend may differ, but the class provides a unified interface for accessing
- audio data, whether it's stored in memory, loaded from a file, or streamed.
-
- @see lime.media.AudioSource
-**/
-class AudioBuffer
-{
- /**
- The number of bits per sample in the audio data.
- **/
- public var bitsPerSample:Int;
-
- /**
- The number of audio channels (e.g., 1 for mono, 2 for stereo).
- **/
- public var channels:Int;
-
- /**
- The raw audio data stored as a `UInt8Array`.
- **/
- public var data:UInt8Array;
-
- /**
- The sample rate of the audio data, in Hz.
- **/
- public var sampleRate:Int;
-
- /**
- The source of the audio data. This can be an `Audio`, `Sound`, `Howl`, or other platform-specific object.
- **/
- public var src(get, set):Dynamic;
-
- @:noCompletion private var __srcAudio:#if (js && html5) Audio #else Dynamic #end;
- @:noCompletion private var __srcBuffer:#if lime_cffi ALBuffer #else Dynamic #end;
- @:noCompletion private var __srcCustom:Dynamic;
- @:noCompletion private var __srcHowl:#if lime_howlerjs Howl #else Dynamic #end;
- @:noCompletion private var __srcSound:#if flash Sound #else Dynamic #end;
- @:noCompletion private var __srcVorbisFile:#if lime_vorbis VorbisFile #else Dynamic #end;
-
- #if commonjs
- private static function __init__()
- {
- var p = untyped AudioBuffer.prototype;
- untyped Object.defineProperties(p,
- {
- "src": {get: p.get_src, set: p.set_src}
- });
- }
- #end
-
- /**
- Creates a new, empty `AudioBuffer` instance.
- **/
- public function new() {}
-
- /**
- Disposes of the resources used by this `AudioBuffer`, such as unloading any associated audio data.
- **/
- public function dispose():Void
- {
- #if (js && html5 && lime_howlerjs)
- if (__srcHowl != null) __srcHowl.unload();
- __srcHowl = null;
- #end
- #if lime_cffi
- if (__srcBuffer != null) {
- AL.bufferData(__srcBuffer, 0, null, 0, 0);
- AL.deleteBuffer(__srcBuffer);
- }
- __srcBuffer = null;
- #end
- #if lime_vorbis
- if (__srcVorbisFile != null) __srcVorbisFile.clear();
- __srcVorbisFile = null;
- #end
- }
-
- /**
- Creates an `AudioBuffer` from a Base64-encoded string.
-
- @param base64String The Base64-encoded audio data.
- @return An `AudioBuffer` instance with the decoded audio data.
- **/
- public static function fromBase64(base64String:String):AudioBuffer
- {
- if (base64String == null) return null;
-
- #if (js && html5 && lime_howlerjs)
- // if base64String doesn't contain codec data, add it.
- if (base64String.indexOf(",") == -1)
- {
- base64String = "data:" + __getCodec(Base64.decode(base64String)) + ";base64," + base64String;
- }
-
- var audioBuffer = new AudioBuffer();
- audioBuffer.src = new Howl({src: [base64String], preload: false});
- return audioBuffer;
- #elseif (lime_cffi && !macro)
- #if !cs
- // if base64String contains codec data, strip it then decode it.
- var base64StringSplit = base64String.split(",");
- var base64StringNoEncoding = base64StringSplit[base64StringSplit.length - 1];
- var bytes:Bytes = Base64.decode(base64StringNoEncoding);
- var audioBuffer = new AudioBuffer();
- audioBuffer.data = new UInt8Array(Bytes.alloc(0));
-
- return NativeCFFI.lime_audio_load_bytes(bytes, audioBuffer);
- #else
- // if base64String contains codec data, strip it then decode it.
- var base64StringSplit = base64String.split(",");
- var base64StringNoEncoding = base64StringSplit[base64StringSplit.length - 1];
- var bytes:Bytes = Base64.decode(base64StringNoEncoding);
- var data:Dynamic = NativeCFFI.lime_audio_load_bytes(bytes, null);
-
- if (data != null)
- {
- var audioBuffer = new AudioBuffer();
- audioBuffer.bitsPerSample = data.bitsPerSample;
- audioBuffer.channels = data.channels;
- audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
- audioBuffer.sampleRate = data.sampleRate;
- return audioBuffer;
- }
- #end
- #end
-
- return null;
- }
-
- /**
- Creates an `AudioBuffer` from a `Bytes` object.
-
- @param bytes The `Bytes` object containing the audio data.
- @return An `AudioBuffer` instance with the decoded audio data.
- **/
- public static function fromBytes(bytes:Bytes):AudioBuffer
- {
- if (bytes == null) return null;
-
- #if (js && html5 && lime_howlerjs)
- var audioBuffer = new AudioBuffer();
- audioBuffer.src = new Howl({src: ["data:" + __getCodec(bytes) + ";base64," + Base64.encode(bytes)], preload: false});
-
- return audioBuffer;
- #elseif (lime_cffi && !macro)
- #if lime_vorbis
- var vorbisFile = VorbisFile.fromBytes(bytes);
- if (vorbisFile != null) return fromVorbisFile(vorbisFile);
- #end
- #if !cs
- var audioBuffer = new AudioBuffer();
- audioBuffer.data = new UInt8Array(Bytes.alloc(0));
-
- return NativeCFFI.lime_audio_load_bytes(bytes, audioBuffer);
- #else
- var data:Dynamic = NativeCFFI.lime_audio_load_bytes(bytes, null);
-
- if (data != null)
- {
- var audioBuffer = new AudioBuffer();
- audioBuffer.bitsPerSample = data.bitsPerSample;
- audioBuffer.channels = data.channels;
- audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
- audioBuffer.sampleRate = data.sampleRate;
- return audioBuffer;
- }
- #end
- #end
-
- return null;
- }
-
- /**
- Creates an `AudioBuffer` from a file.
-
- @param path The file path to the audio data.
- @return An `AudioBuffer` instance with the audio data loaded from the file.
- **/
- public static function fromFile(path:String #if (js && html5 && lime_howlerjs), ?howlHtml5 = false #end):AudioBuffer
- {
- if (path == null) return null;
-
- #if (js && html5 && lime_howlerjs)
- var audioBuffer = new AudioBuffer();
-
- #if force_html5_audio
- audioBuffer.__srcHowl = new Howl({src: [path], html5: true, preload: false});
- #else
- audioBuffer.__srcHowl = new Howl({src: [path], html5: howlHtml5, preload: false});
- #end
-
- return audioBuffer;
- #elseif flash
- switch (Path.extension(path))
- {
- case "ogg", "wav":
- return null;
- default:
- }
-
- var audioBuffer = new AudioBuffer();
- audioBuffer.__srcSound = new Sound(new URLRequest(path));
- return audioBuffer;
- #elseif (lime_cffi && !macro)
- #if !cs
- var audioBuffer = new AudioBuffer();
- audioBuffer.data = new UInt8Array(Bytes.alloc(0));
-
- //audioBuffer = NativeCFFI.lime_audio_load_file(path, audioBuffer);
- //if (audioBuffer != null) audioBuffer.initBuffer();
- //return audioBuffer;
- return NativeCFFI.lime_audio_load_file(path, audioBuffer);
- #else
- var data:Dynamic = NativeCFFI.lime_audio_load_file(path, null);
-
- if (data != null)
- {
- var audioBuffer = new AudioBuffer();
- audioBuffer.bitsPerSample = data.bitsPerSample;
- audioBuffer.channels = data.channels;
- audioBuffer.data = new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b));
- audioBuffer.sampleRate = data.sampleRate;
- //audioBuffer.initBuffer();
- return audioBuffer;
- }
-
- return null;
- #end
- #else
- return null;
- #end
- }
-
- /**
- Creates an `AudioBuffer` from an array of file paths.
-
- @param paths An array of file paths to search for audio data.
- @return An `AudioBuffer` instance with the audio data loaded from the first valid file found.
- **/
- public static function fromFiles(paths:Array #if (js && html5 && lime_howlerjs), ?howlHtml5 = false #end):AudioBuffer
- {
- #if (js && html5 && lime_howlerjs)
- var audioBuffer = new AudioBuffer();
-
- #if force_html5_audio
- audioBuffer.__srcHowl = new Howl({src: paths, html5: true, preload: false});
- #else
- audioBuffer.__srcHowl = new Howl({src: paths, html5: howlHtml5, preload: false});
- #end
-
- return audioBuffer;
- #else
- var buffer = null;
-
- for (path in paths)
- {
- buffer = AudioBuffer.fromFile(path);
- if (buffer != null) break;
- }
-
- return buffer;
- #end
- }
-
- /**
- Creates an `AudioBuffer` from a `VorbisFile`.
-
- @param vorbisFile The `VorbisFile` object containing the audio data.
- @return An `AudioBuffer` instance with the decoded audio data.
- **/
- #if lime_vorbis
-
- public static function fromVorbisFile(vorbisFile:VorbisFile):AudioBuffer
- {
- if (vorbisFile == null) return null;
-
- var info = vorbisFile.info();
-
- var audioBuffer = new AudioBuffer();
- audioBuffer.channels = info.channels;
- audioBuffer.sampleRate = info.rate;
- audioBuffer.bitsPerSample = 16;
-
- final pcmTotal = vorbisFile.pcmTotal(-1);
- if (!vorbisFile.seekable() || pcmTotal < (audioBuffer.sampleRate << 2)) {
- vorbisFile.rawSeek(0);
-
- final isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN;
- final bytes = Bytes.alloc(Std.int((pcmTotal.high * 4294967296. + (pcmTotal.low >> 0)) * info.channels * (audioBuffer.bitsPerSample >> 3)));
- var total = 0, result = 0;
- do {
- result = vorbisFile.read(bytes, total, 0x1000, isBigEndian, 2, true);
- total += result;
- } while (result > 0 || result == Vorbis.HOLE);
-
- audioBuffer.data = new UInt8Array(bytes);
- vorbisFile.clear();
- }
- else
- audioBuffer.__srcVorbisFile = vorbisFile;
-
- return audioBuffer;
- }
- #else
- public static function fromVorbisFile(vorbisFile:Dynamic):AudioBuffer
- {
- return null;
- }
- #end
-
- /**
- Asynchronously loads an `AudioBuffer` from a file.
-
- @param path The file path to the audio data.
- @return A `Future` that resolves to the loaded `AudioBuffer`.
- **/
- public static function loadFromFile(path:String):Future
- {
- #if (flash || (js && html5))
- var promise = new Promise();
-
- var audioBuffer = AudioBuffer.fromFile(path);
-
- if (audioBuffer != null)
- {
- #if flash
- audioBuffer.__srcSound.addEventListener(flash.events.Event.COMPLETE, function(event)
- {
- promise.complete(audioBuffer);
- });
-
- audioBuffer.__srcSound.addEventListener(flash.events.ProgressEvent.PROGRESS, function(event)
- {
- promise.progress(Std.int(event.bytesLoaded), Std.int(event.bytesTotal));
- });
-
- audioBuffer.__srcSound.addEventListener(flash.events.IOErrorEvent.IO_ERROR, promise.error);
- #elseif (js && html5 && lime_howlerjs)
- if (audioBuffer != null)
- {
- audioBuffer.__srcHowl.on("load", function()
- {
- promise.complete(audioBuffer);
- });
-
- audioBuffer.__srcHowl.on("loaderror", function(id, msg)
- {
- promise.error(msg);
- });
-
- audioBuffer.__srcHowl.load();
- }
- #else
- promise.complete(audioBuffer);
- #end
- }
- else
- {
- promise.error(null);
- }
-
- return promise.future;
- #else
- // TODO: Streaming
-
- var request = new HTTPRequest();
- return request.load(path).then(function(buffer)
- {
- if (buffer != null)
- {
- return Future.withValue(buffer);
- }
- else
- {
- return cast Future.withError("");
- }
- });
- #end
- }
-
- /**
- Asynchronously loads an `AudioBuffer` from multiple files.
-
- @param paths An array of file paths to search for audio data.
- @return A `Future` that resolves to the loaded `AudioBuffer`.
- **/
- public static function loadFromFiles(paths:Array):Future
- {
- #if (js && html5 && lime_howlerjs)
- var promise = new Promise();
-
- var audioBuffer = AudioBuffer.fromFiles(paths);
-
- if (audioBuffer != null)
- {
- audioBuffer.__srcHowl.on("load", function()
- {
- promise.complete(audioBuffer);
- });
-
- audioBuffer.__srcHowl.on("loaderror", function()
- {
- promise.error(null);
- });
-
- audioBuffer.__srcHowl.load();
- }
- else
- {
- promise.error(null);
- }
-
- return promise.future;
- #else
- return new Future(fromFiles.bind(paths), true);
- #end
- }
-
- private static function __getCodec(bytes:Bytes):String
- {
- var signature = bytes.getString(0, 4);
-
- switch (signature)
- {
- case "OggS":
- return "audio/ogg";
- case "fLaC":
- return "audio/flac";
- case "RIFF" if (bytes.getString(8, 4) == "WAVE"):
- return "audio/wav";
- default:
- switch ([bytes.get(0), bytes.get(1), bytes.get(2)])
- {
- case [73, 68, 51] | [255, 251, _] | [255, 250, _] | [255, 243, _]: return "audio/mp3";
- default:
- }
- }
-
- Log.error("Unsupported sound format");
- return null;
- }
-
- // Get & Set Methods
- @:noCompletion private function get_src():Dynamic
- {
- #if (js && html5)
- #if lime_howlerjs
- return __srcHowl;
- #else
- return __srcAudio;
- #end
- #elseif flash
- return __srcSound;
- #elseif lime_vorbis
- return __srcVorbisFile;
- #else
- return __srcCustom;
- #end
- }
-
- @:noCompletion private function set_src(value:Dynamic):Dynamic
- {
- #if (js && html5)
- #if lime_howlerjs
- return __srcHowl = value;
- #else
- return __srcAudio = value;
- #end
- #elseif flash
- return __srcSound = value;
- #elseif lime_vorbis
- return __srcVorbisFile = value;
- #else
- return __srcCustom = value;
- #end
- }
-}
diff --git a/source/lime/media/AudioSource.hx b/source/lime/media/AudioSource.hx
deleted file mode 100644
index 992bfdb5bf..0000000000
--- a/source/lime/media/AudioSource.hx
+++ /dev/null
@@ -1,257 +0,0 @@
-package lime.media;
-
-import lime.app.Event;
-import lime.math.Vector4;
-
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-/**
- The `AudioSource` class provides a way to control audio playback in a Lime application.
- It allows for playing, pausing, and stopping audio, as well as controlling various
- audio properties such as gain, pitch, and looping.
-
- Depending on the platform, the audio backend may vary, but the API remains consistent.
-
- @see lime.media.AudioBuffer
-**/
-class AudioSource
-{
- private static var activeSources:Array = [];
-
- /**
- An event that is dispatched when the audio playback is complete.
- **/
- public var onComplete = new EventVoid>();
-
- /**
- An event that is dispatched when the audio playback looped.
- **/
- public var onLoop = new EventVoid>();
-
- /**
- The `AudioBuffer` associated with this `AudioSource`.
- **/
- public var buffer:AudioBuffer;
-
- /**
- An property if this 'AudioSource' is playing.
- **/
- public var playing(get, null):Bool;
-
- /**
- The current playback position of the audio, in milliseconds.
- **/
- public var currentTime(get, set):Float;
-
- /**
- The gain (volume) of the audio. A value of `1.0` represents the default volume.
- **/
- public var gain(get, set):Float;
-
- /**
- The length of the audio, in milliseconds.
- **/
- public var length(get, set):Null;
-
- /**
- The number of times the audio will loop. A value of `0` means the audio will not loop.
- **/
- public var loops(get, set):Int;
-
- /**
- In which audio playback time the audio will loop.
- **/
- public var loopTime(get, set):Float;
-
- /**
- The pitch of the audio. A value of `1.0` represents the default pitch.
- **/
- public var pitch(get, set):Float;
-
- /**
- The offset within the audio buffer to start playback, in samples.
- **/
- public var offset:Float;
-
- /**
- The 3D position of the audio source, represented as a `Vector4`.
- **/
- public var position(get, set):Vector4;
-
- /**
- The stereo pan of the audio source.
- **/
- public var pan(get, set):Float;
-
- /**
- The latency of the audio source.
- **/
- public var latency(get, never):Float;
-
- @:noCompletion private var __backend:AudioSourceBackend;
-
- /**
- Creates a new `AudioSource` instance.
- @param buffer The `AudioBuffer` to associate with this `AudioSource`.
- @param offset The starting offset within the audio buffer, in samples.
- @param length The length of the audio to play, in milliseconds. If `null`, the full buffer is used.
- @param loops The number of times to loop the audio. `0` means no looping.
- **/
- public function new(buffer:AudioBuffer = null, offset:Float = 0, length:Null = null, loops:Int = 0)
- {
- this.buffer = buffer;
- this.offset = offset;
-
- __backend = new AudioSourceBackend(this);
-
- if (length != null && length != 0)
- {
- this.length = length;
- }
-
- if (buffer != null)
- {
- init();
- }
-
- this.loops = loops;
- }
-
- /**
- Releases any resources used by this `AudioSource`.
- **/
- inline public function dispose():Void
- {
- __backend.dispose();
- activeSources.remove(this);
- }
-
- @:noCompletion inline private function init():Void
- {
- __backend.init();
- if (!activeSources.contains(this)) activeSources.push(this);
- }
-
- /**
- Starts or resumes audio playback.
- **/
- inline public function play():Void
- {
- __backend.play();
- }
-
- /**
- Pauses audio playback.
- **/
- inline public function pause():Void
- {
- __backend.pause();
- }
-
- /**
- Stops audio playback and resets the playback position to the beginning.
- **/
- inline public function stop():Void
- {
- __backend.stop();
- }
-
- // Get & Set Methods
- @:noCompletion inline private function get_playing():Bool
- {
- @:privateAccess return __backend.playing;
- }
-
- @:noCompletion inline private function get_currentTime():Float
- {
- return __backend.getCurrentTime();
- }
-
- @:noCompletion inline private function set_currentTime(value:Float):Float
- {
- return __backend.setCurrentTime(value);
- }
-
- @:noCompletion inline private function get_gain():Float
- {
- return __backend.getGain();
- }
-
- @:noCompletion inline private function set_gain(value:Float):Float
- {
- return __backend.setGain(value);
- }
-
- @:noCompletion inline private function get_length():Null
- {
- return __backend.getLength();
- }
-
- @:noCompletion inline private function set_length(value:Null):Null
- {
- return __backend.setLength(value);
- }
-
- @:noCompletion inline private function get_loops():Int
- {
- return __backend.getLoops();
- }
-
- @:noCompletion inline private function set_loops(value:Int):Int
- {
- return __backend.setLoops(value);
- }
-
- @:noCompletion inline private function get_loopTime():Float
- {
- return __backend.getLoopTime();
- }
-
- @:noCompletion inline private function set_loopTime(value:Float):Float
- {
- return __backend.setLoopTime(value);
- }
-
- @:noCompletion inline private function get_pitch():Float
- {
- return __backend.getPitch();
- }
-
- @:noCompletion inline private function set_pitch(value:Float):Float
- {
- return __backend.setPitch(value);
- }
-
- @:noCompletion inline private function get_position():Vector4
- {
- return __backend.getPosition();
- }
-
- @:noCompletion inline private function set_position(value:Vector4):Vector4
- {
- return __backend.setPosition(value);
- }
-
- @:noCompletion inline private function get_pan():Float
- {
- return __backend.getPan();
- }
-
- @:noCompletion inline private function set_pan(value:Float):Float
- {
- return __backend.setPan(value);
- }
-
- @:noCompletion inline private function get_latency():Float
- {
- return __backend.getLatency();
- }
-}
-
-#if (js && html5)
-@:noCompletion private typedef AudioSourceBackend = lime._internal.backend.html5.HTML5AudioSource;
-#else
-@:noCompletion private typedef AudioSourceBackend = lime._internal.backend.native.NativeAudioSource;
-#end
diff --git a/source/lime/media/vorbis/VorbisFile.hx b/source/lime/media/vorbis/VorbisFile.hx
deleted file mode 100644
index bd29a7a7f5..0000000000
--- a/source/lime/media/vorbis/VorbisFile.hx
+++ /dev/null
@@ -1,356 +0,0 @@
-package lime.media.vorbis;
-
-#if (!lime_doc_gen || lime_vorbis)
-import haxe.Int64;
-import haxe.io.Bytes;
-import lime._internal.backend.native.NativeCFFI;
-
-#if hl
-@:keep
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-class VorbisFile
-{
- public var bitstream(default, null):Int;
-
- @:noCompletion private var handle:Dynamic;
-
- @:noCompletion var _filePath:String;
- @:noCompletion var _bytes:Bytes;
-
- @:noCompletion private function new(handle:Dynamic)
- {
- this.handle = handle;
- }
-
- public function bitrate(bitstream:Int = -1):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_bitrate(handle, bitstream);
- #else
- return 0;
- #end
- }
-
- public function bitrateInstant():Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_bitrate_instant(handle);
- #else
- return 0;
- #end
- }
-
- public function clear():Void
- {
- #if (lime_cffi && lime_vorbis && !macro)
- NativeCFFI.lime_vorbis_file_clear(handle);
- #end
- handle = null;
- _filePath = null;
- _bytes = null;
- }
-
- public function clone():VorbisFile
- {
- if (_filePath != null) return fromFile(_filePath);
- if (_bytes != null) return fromBytes(_bytes);
- return null;
- }
-
- public function comment(bitstream:Int = -1):VorbisComment
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_comment(handle, bitstream);
-
- if (data != null)
- {
- var comment = new VorbisComment();
- comment.userComments = data.userComments;
- comment.vendor = data.vendor;
- return comment;
- }
- #end
-
- return null;
- }
-
- public function crosslap(other:VorbisFile):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_crosslap(handle, other.handle);
- #else
- return 0;
- #end
- }
-
- public static function fromBytes(bytes:Bytes):VorbisFile
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var handle = NativeCFFI.lime_vorbis_file_from_bytes(bytes);
-
- if (handle != null)
- {
- var vorbisFile = new VorbisFile(handle);
- vorbisFile._bytes = bytes;
- return vorbisFile;
- }
- #end
-
- return null;
- }
-
- public static function fromFile(path:String):VorbisFile
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var handle = NativeCFFI.lime_vorbis_file_from_file(path);
-
- if (handle != null)
- {
- var vorbisFile = new VorbisFile(handle);
- vorbisFile._filePath = path;
- return vorbisFile;
- }
- #end
-
- return null;
- }
-
- public function info(bitstream:Int = -1):VorbisInfo
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_info(handle, bitstream);
-
- if (data != null)
- {
- var info = new VorbisInfo();
- info.bitrateLower = data.bitrateLower;
- info.bitrateNominal = data.bitrateNominal;
- info.bitrateUpper = data.bitrateUpper;
- info.channels = data.channels;
- info.rate = data.rate;
- info.version = data.version;
- return info;
- }
- #end
-
- return null;
- }
-
- public function pcmSeek(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmSeekLap(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek_lap(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmSeekPage(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek_page(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmSeekPageLap(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_pcm_seek_page_lap(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function pcmTell():Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_pcm_tell(handle);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function pcmTotal(bitstream:Int = -1):Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_pcm_total(handle, bitstream);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function rawSeek(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_raw_seek(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function rawSeekLap(pos:Int64):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_raw_seek_lap(handle, pos.low, pos.high);
- #else
- return 0;
- #end
- }
-
- public function rawTell():Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_raw_tell(handle);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function rawTotal(bitstream:Int = -1):Int64
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_raw_total(handle, bitstream);
-
- if (data != null)
- {
- return Int64.make(data.high, data.low);
- }
- #end
-
- return Int64.ofInt(0);
- }
-
- public function read(buffer:Bytes, position:Int, length:Int = 4096, bigEndianPacking:Bool = false, wordSize:Int = 2, signed:Bool = true):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_read(handle, buffer, position, length, bigEndianPacking, wordSize, signed);
- if (data == null) return 0;
- bitstream = data.bitstream;
- return data.returnValue;
- #else
- return 0;
- #end
- }
-
- // public function readFilter (buffer:Bytes, length:Int = 4096, endianness:Endian = LITTLE_ENDIAN, wordSize:Int = 2, signed:Bool = true, bitstream:Int = 0, filter, filter_param
- public function readFloat(pcmChannels:Bytes, samples:Int):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- var data = NativeCFFI.lime_vorbis_file_read_float(handle, pcmChannels, samples);
- if (data == null) return 0;
- bitstream = data.bitstream;
- return data.returnValue;
- #else
- return 0;
- #end
- }
-
- public function seekable():Bool
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_seekable(handle);
- #else
- return false;
- #end
- }
-
- public function serialNumber(bitstream:Int = -1):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_serial_number(handle, bitstream);
- #else
- return 0;
- #end
- }
-
- public function streams():Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_streams(handle);
- #else
- return 0;
- #end
- }
-
- public function timeSeek(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeSeekLap(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek_lap(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeSeekPage(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek_page(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeSeekPageLap(s:Float):Int
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_seek_page_lap(handle, s);
- #else
- return 0;
- #end
- }
-
- public function timeTell():Float
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_tell(handle);
- #else
- return 0;
- #end
- }
-
- public function timeTotal(bitstream:Int = -1):Float
- {
- #if (lime_cffi && lime_vorbis && !macro)
- return NativeCFFI.lime_vorbis_file_time_total(handle, bitstream);
- #else
- return 0;
- #end
- }
-}
-#end
diff --git a/source/lime/system/System.hx b/source/lime/system/System.hx
deleted file mode 100644
index 2184f5fe62..0000000000
--- a/source/lime/system/System.hx
+++ /dev/null
@@ -1,905 +0,0 @@
-package lime.system;
-
-import haxe.Constraints;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Application;
-import lime.graphics.RenderContextAttributes;
-import lime.math.Rectangle;
-import lime.ui.WindowAttributes;
-import lime.utils.ArrayBuffer;
-import lime.utils.UInt8Array;
-import lime.utils.UInt16Array;
-#if flash
-import openfl.net.URLRequest;
-import openfl.system.Capabilities;
-import openfl.Lib;
-#end
-#if air
-import openfl.desktop.NativeApplication;
-#end
-#if ((js && html5) || electron)
-import js.html.Element;
-import js.Browser;
-#end
-#if sys
-import sys.io.Process;
-#end
-
-/**
- Access operating system level settings and operations.
-**/
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime.system.Display)
-@:access(lime.system.DisplayMode)
-#if (cpp && windows && !HXCPP_MINGW && !lime_disable_gpu_hint)
-@:cppFileCode('
-#if defined(HX_WINDOWS)
-extern "C" {
- _declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
- _declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
-}
-#endif
-')
-#end
-class System
-{
- /**
- Determines if the screen saver is allowed to start or not.
- **/
- public static var allowScreenTimeout(get, set):Bool;
-
- /**
- The path to the directory where the application is installed, along with
- its supporting files. In many cases, this directory is read-only, and
- attempts to write to files, create new files, or delete files in this
- directory are likely fail.
- **/
- public static var applicationDirectory(get, never):String;
-
- /**
- The application's dedicated storage directory, which unique to each
- application and user. Useful for storing settings on a user-specific
- and application-specific basis.
-
- This directory may or may not be removed when the application is
- uninstalled, and it depends on the platform and installer technology
- that is used.
- **/
- public static var applicationStorageDirectory(get, never):String;
-
- /**
- The path to the directory containing the user's desktop.
- **/
- public static var desktopDirectory(get, never):String;
-
- public static var deviceModel(get, never):String;
- public static var deviceVendor(get, never):String;
- public static var disableCFFI:Bool;
-
- /**
- The path to the directory containing the user's documents.
- **/
- public static var documentsDirectory(get, never):String;
-
- /**
- The platform's default endianness for bytes.
- **/
- public static var endianness(get, never):Endian;
-
- /**
- The path to the directory where fonts are installed.
- **/
- public static var fontsDirectory(get, never):String;
-
- /**
- The number of available video displays.
- **/
- public static var numDisplays(get, never):Int;
-
- public static var platformLabel(get, never):String;
- public static var platformName(get, never):String;
- public static var platformVersion(get, never):String;
-
- /**
- The path to the user's home directory.
- **/
- public static var userDirectory(get, never):String;
-
- @:noCompletion private static var __applicationDirectory:String;
- @:noCompletion private static var __applicationEntryPoint:Map;
- @:noCompletion private static var __applicationStorageDirectory:String;
- @:noCompletion private static var __desktopDirectory:String;
- @:noCompletion private static var __deviceModel:String;
- @:noCompletion private static var __deviceVendor:String;
- @:noCompletion private static var __directories = new Map();
- @:noCompletion private static var __documentsDirectory:String;
- @:noCompletion private static var __endianness:Endian;
- @:noCompletion private static var __fontsDirectory:String;
- @:noCompletion private static var __platformLabel:String;
- @:noCompletion private static var __platformName:String;
- @:noCompletion private static var __platformVersion:String;
- @:noCompletion private static var __userDirectory:String;
-
- #if (js && html5)
- @:keep @:expose("lime.embed")
- public static function embed(projectName:String, element:Dynamic, width:Null = null, height:Null = null, config:Dynamic = null):Void
- {
- if (__applicationEntryPoint == null) return;
-
- if (__applicationEntryPoint.exists(projectName))
- {
- var htmlElement:Element = null;
-
- if ((element is String))
- {
- htmlElement = cast Browser.document.getElementById(element);
- }
- else if (element == null)
- {
- htmlElement = cast Browser.document.createElement("div");
- }
- else
- {
- htmlElement = cast element;
- }
-
- if (htmlElement == null)
- {
- Browser.window.console.log("[lime.embed] ERROR: Cannot find target element: " + element);
- return;
- }
-
- if (width == null)
- {
- width = 0;
- }
-
- if (height == null)
- {
- height = 0;
- }
-
- if (config == null) config = {};
-
- if (Reflect.hasField(config, "background") && (config.background is String))
- {
- var background = StringTools.replace(Std.string(config.background), "#", "");
-
- if (background.indexOf("0x") > -1)
- {
- config.background = Std.parseInt(background);
- }
- else
- {
- config.background = Std.parseInt("0x" + background);
- }
- }
-
- config.element = htmlElement;
- config.width = width;
- config.height = height;
-
- __applicationEntryPoint[projectName](config);
- }
- }
- #end
-
- #if (!lime_doc_gen || sys)
- /**
- Attempts to exit the application. Dispatches `onExit`, and will not
- exit if the event is canceled.
- **/
- public static function exit(code:Int):Void
- {
- var currentApp = Application.current;
- #if ((sys || (js && html5) || air) && !macro)
- if (currentApp != null)
- {
- currentApp.onExit.dispatch(code);
-
- if (currentApp.onExit.canceled)
- {
- return;
- }
- }
- #end
-
- #if sys
- Sys.exit(code);
- #elseif (js && html5)
- if (currentApp != null && currentApp.window != null)
- {
- currentApp.window.close();
- }
- #elseif air
- NativeApplication.nativeApplication.exit(code);
- #end
- }
- #end
-
- /**
- Returns information about the video display with the specified ID.
- **/
- public static function getDisplay(id:Int):Display
- {
- #if (lime_cffi && !macro)
- var displayInfo:Dynamic = NativeCFFI.lime_system_get_display(id);
-
- if (displayInfo != null)
- {
- var display = new Display();
- display.id = id;
- #if hl
- display.name = @:privateAccess String.fromUTF8(displayInfo.name);
- #else
- display.name = displayInfo.name;
- #end
- display.bounds = new Rectangle(displayInfo.bounds.x, displayInfo.bounds.y, displayInfo.bounds.width, displayInfo.bounds.height);
-
- #if ios
- var tablet = NativeCFFI.lime_system_get_ios_tablet();
- var scale = Application.current.window.scale;
- if (!tablet && scale > 2.46)
- {
- display.dpi = 401; // workaround for iPhone Plus
- }
- else
- {
- display.dpi = (tablet ? 132 : 163) * scale;
- }
- #elseif android
- var getDisplayDPI = JNI.createStaticMethod("org/haxe/lime/GameActivity", "getDisplayXDPI", "()D");
- display.dpi = Math.round(getDisplayDPI());
- #else
- display.dpi = displayInfo.dpi;
- #end
-
- display.supportedModes = [];
-
- var displayMode;
-
- #if hl
- var supportedModes:hl.NativeArray = displayInfo.supportedModes;
- #else
- var supportedModes:Array = displayInfo.supportedModes;
- #end
- for (mode in supportedModes)
- {
- displayMode = new DisplayMode(mode.width, mode.height, mode.refreshRate, mode.pixelFormat);
- display.supportedModes.push(displayMode);
- }
-
- var mode = displayInfo.currentMode;
- var currentMode = new DisplayMode(mode.width, mode.height, mode.refreshRate, mode.pixelFormat);
-
- for (mode in display.supportedModes)
- {
- if (currentMode.pixelFormat == mode.pixelFormat
- && currentMode.width == mode.width
- && currentMode.height == mode.height
- && currentMode.refreshRate == mode.refreshRate)
- {
- currentMode = mode;
- break;
- }
- }
-
- display.currentMode = currentMode;
-
- return display;
- }
- #elseif (flash || html5)
- if (id == 0)
- {
- var display = new Display();
- display.id = 0;
- display.name = "Generic Display";
-
- #if flash
- display.dpi = Capabilities.screenDPI;
- display.currentMode = new DisplayMode(Std.int(Capabilities.screenResolutionX), Std.int(Capabilities.screenResolutionY), 60, ARGB32);
- #elseif (js && html5)
- // var div = Browser.document.createElement ("div");
- // div.style.width = "1in";
- // Browser.document.body.appendChild (div);
- // var ppi = Browser.document.defaultView.getComputedStyle (div, null).getPropertyValue ("width");
- // Browser.document.body.removeChild (div);
- // display.dpi = Std.parseFloat (ppi);
- display.dpi = 96 * Browser.window.devicePixelRatio;
- display.currentMode = new DisplayMode(Browser.window.screen.width, Browser.window.screen.height, 60, ARGB32);
- #end
-
- display.supportedModes = [display.currentMode];
- display.bounds = new Rectangle(0, 0, display.currentMode.width, display.currentMode.height);
- return display;
- }
- #end
-
- return null;
- }
-
- /**
- The number of milliseconds since the application was initialized.
- **/
- public static function getTimer():Int
- {
- #if flash
- return flash.Lib.getTimer();
- #elseif ((js && !nodejs) || electron)
- return Std.int(Browser.window.performance.now());
- #elseif (lime_cffi && !macro)
- return cast NativeCFFI.lime_system_get_timer();
- #elseif cpp
- return Std.int(untyped __global__.__time_stamp() * 1000);
- #elseif sys
- return Std.int(Sys.time() * 1000);
- #else
- return 0;
- #end
- }
-
- #if (!lime_doc_gen || lime_cffi)
- public static inline function load(library:String, method:String, args:Int = 0, lazy:Bool = false):Dynamic
- {
- #if !macro
- return CFFI.load(library, method, args, lazy);
- #else
- return null;
- #end
- }
- #end
-
- /**
- Opens a file with the suste, default application.
-
- In a web browser, opens a URL with target `_blank`.
- **/
- public static function openFile(path:String):Void
- {
- if (path != null)
- {
- #if (sys && windows)
- Sys.command("start", ["", path]);
- #elseif mac
- Sys.command("/usr/bin/open", [path]);
- #elseif linux
- // generally `xdg-open` should work in every distro
- var cmd = Sys.command("xdg-open", [path, "&"]);
- // run old command JUST IN CASE it fails, which it shouldn't
- if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [path, "&"]);
- #elseif (js && html5)
- Browser.window.open(path, "_blank");
- #elseif flash
- Lib.getURL(new URLRequest(path), "_blank");
- #elseif android
- var openFile = JNI.createStaticMethod("org/haxe/lime/GameActivity", "openFile", "(Ljava/lang/String;)V");
- openFile(path);
- #elseif (lime_cffi && !macro)
- NativeCFFI.lime_system_open_file(path);
- #end
- }
- }
-
- /**
- Opens a URL with the specified target web browser window.
- **/
- public static function openURL(url:String, target:String = "_blank"):Void
- {
- if (url != null)
- {
- #if desktop
- openFile(url);
- #elseif (js && html5)
- Browser.window.open(url, target);
- #elseif flash
- Lib.getURL(new URLRequest(url), target);
- #elseif android
- var openURL = JNI.createStaticMethod("org/haxe/lime/GameActivity", "openURL", "(Ljava/lang/String;Ljava/lang/String;)V");
- openURL(url, target);
- #elseif (lime_cffi && !macro)
- NativeCFFI.lime_system_open_url(url, target);
- #end
- }
- }
-
- @:noCompletion private static function __copyMissingFields(target:Dynamic, source:Dynamic):Void
- {
- if (source == null || target == null) return;
-
- for (field in Reflect.fields(source))
- {
- if (!Reflect.hasField(target, field))
- {
- Reflect.setField(target, field, Reflect.field(source, field));
- }
- }
- }
-
- @:noCompletion private static function __getDirectory(type:SystemDirectory):String
- {
- #if (lime_cffi && !macro)
- if (__directories.exists(type))
- {
- return __directories.get(type);
- }
- else
- {
- var path:String;
-
- if (type == APPLICATION_STORAGE)
- {
- var company = "MyCompany";
- var file = "MyApplication";
-
- if (Application.current != null)
- {
- if (Application.current.meta.exists("company"))
- {
- company = Application.current.meta.get("company");
- }
-
- if (Application.current.meta.exists("file"))
- {
- file = Application.current.meta.get("file");
- }
- }
-
- #if hl
- path = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_directory(type, company, file));
- #else
- path = NativeCFFI.lime_system_get_directory(type, company, file);
- #end
- }
- else
- {
- #if hl
- path = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_directory(type, null, null));
- #else
- path = NativeCFFI.lime_system_get_directory(type, null, null);
- #end
- }
-
- #if windows
- var seperator = "\\";
- #else
- var seperator = "/";
- #end
-
- if (path != null && path.length > 0 && !StringTools.endsWith(path, seperator))
- {
- path += seperator;
- }
-
- __directories.set(type, path);
- return path;
- }
- #elseif flash
- if (type != FONTS && Capabilities.playerType == "Desktop")
- {
- var propertyName = switch (type)
- {
- case APPLICATION: "applicationDirectory";
- case APPLICATION_STORAGE: "applicationStorageDirectory";
- case DESKTOP: "desktopDirectory";
- case DOCUMENTS: "documentsDirectory";
- default: "userDirectory";
- }
-
- return Reflect.getProperty(Type.resolveClass("flash.filesystem.File"), propertyName).nativePath;
- }
- #end
-
- return null;
- }
-
- #if sys
- private static function __parseArguments(attributes:WindowAttributes):Void
- {
- // TODO: Handle default arguments, like --window-fps=60
-
- var arguments = Sys.args();
- var stripQuotes = ~/^['"](.*)['"]$/;
- var equals, argValue, parameters = null;
- var windowParamPrefix = "--window-";
-
- if (arguments != null)
- {
- for (argument in arguments)
- {
- equals = argument.indexOf("=");
-
- if (equals > 0)
- {
- argValue = argument.substr(equals + 1);
-
- if (stripQuotes.match(argValue))
- {
- argValue = stripQuotes.matched(1);
- }
-
- if (parameters == null) parameters = new Map();
- parameters.set(argument.substr(0, equals), argValue);
- }
- }
- }
-
- if (parameters != null)
- {
- if (attributes.parameters == null) attributes.parameters = {};
- if (attributes.context == null) attributes.context = {};
-
- for (parameter in parameters.keys())
- {
- argValue = parameters.get(parameter);
-
- if (#if lime_disable_window_override false && #end StringTools.startsWith(parameter, windowParamPrefix))
- {
- switch (parameter.substr(windowParamPrefix.length))
- {
- case "allow-high-dpi":
- attributes.allowHighDPI = __parseBool(argValue);
- case "always-on-top":
- attributes.alwaysOnTop = __parseBool(argValue);
- case "antialiasing":
- attributes.context.antialiasing = Std.parseInt(argValue);
- case "background":
- attributes.context.background = (argValue == "" || argValue == "null") ? null : Std.parseInt(argValue);
- case "borderless":
- attributes.borderless = __parseBool(argValue);
- case "colorDepth":
- attributes.context.colorDepth = Std.parseInt(argValue);
- case "depth", "depth-buffer":
- attributes.context.depth = __parseBool(argValue);
- // case "display": windowConfig.display = Std.parseInt (argValue);
- case "fullscreen":
- attributes.fullscreen = __parseBool(argValue);
- case "hardware":
- attributes.context.hardware = __parseBool(argValue);
- case "height":
- attributes.height = Std.parseInt(argValue);
- case "hidden":
- attributes.hidden = __parseBool(argValue);
- case "maximized":
- attributes.maximized = __parseBool(argValue);
- case "minimized":
- attributes.minimized = __parseBool(argValue);
- case "render-type", "renderer":
- attributes.context.type = argValue;
- case "render-version", "renderer-version":
- attributes.context.version = argValue;
- case "resizable":
- attributes.resizable = __parseBool(argValue);
- case "stencil", "stencil-buffer":
- attributes.context.stencil = __parseBool(argValue);
- // case "title": windowConfig.title = argValue;
- case "vsync":
- attributes.context.vsync = __parseBool(argValue);
- case "width":
- attributes.width = Std.parseInt(argValue);
- case "x":
- attributes.x = Std.parseInt(argValue);
- case "y":
- attributes.y = Std.parseInt(argValue);
- default:
- }
- }
- else if (!Reflect.hasField(attributes.parameters, parameter))
- {
- Reflect.setField(attributes.parameters, parameter, argValue);
- }
- }
- }
- }
- #end
-
- @:noCompletion private static inline function __parseBool(value:String):Bool
- {
- return (value == "true");
- }
-
- @:noCompletion private static function __registerEntryPoint(projectName:String, entryPoint:Function):Void
- {
- // executes first!!
- #if (sys && !macro)
- funkin.backend.system.Main.preInit();
- #end
-
- if (__applicationEntryPoint == null)
- {
- __applicationEntryPoint = new Map();
- }
-
- __applicationEntryPoint[projectName] = entryPoint;
- }
-
- @:noCompletion private static function __runProcess(command:String, args:Array = null):String
- {
- #if sys
- try
- {
- if (args == null) args = [];
-
- var process = new Process(command, args);
- var value = StringTools.trim(process.stdout.readLine().toString());
- process.close();
- return value;
- }
- catch (e:Dynamic) {}
- #end
- return null;
- }
-
- // Get & Set Methods
- private static function get_allowScreenTimeout():Bool
- {
- #if (lime_cffi && !macro)
- return NativeCFFI.lime_system_get_allow_screen_timeout();
- #else
- return true;
- #end
- }
-
- private static function set_allowScreenTimeout(value:Bool):Bool
- {
- #if (lime_cffi && !macro)
- return NativeCFFI.lime_system_set_allow_screen_timeout(value);
- #else
- return true;
- #end
- }
-
- private static function get_applicationDirectory():String
- {
- if (__applicationDirectory == null)
- {
- __applicationDirectory = __getDirectory(APPLICATION);
- }
-
- return __applicationDirectory;
- }
-
- private static function get_applicationStorageDirectory():String
- {
- if (__applicationStorageDirectory == null)
- {
- __applicationStorageDirectory = __getDirectory(APPLICATION_STORAGE);
- }
-
- return __applicationStorageDirectory;
- }
-
- private static function get_deviceModel():String
- {
- if (__deviceModel == null)
- {
- #if (lime_cffi && !macro && (windows || ios || tvos))
- #if hl
- __deviceModel = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_device_model());
- #else
- __deviceModel = NativeCFFI.lime_system_get_device_model();
- #end
- #elseif android
- var manufacturer:String = JNI.createStaticField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").get();
- var model:String = JNI.createStaticField("android/os/Build", "MODEL", "Ljava/lang/String;").get();
- if (manufacturer != null && model != null)
- {
- if (StringTools.startsWith(model.toLowerCase(), manufacturer.toLowerCase()))
- {
- model = StringTools.trim(model.substr(manufacturer.length));
- while (StringTools.startsWith(model, "-"))
- {
- model = StringTools.trim(model.substr(1));
- }
- }
- __deviceModel = model;
- }
- #elseif mac
- __deviceModel = __runProcess("sysctl", ["-n", "hw.model"]);
- #elseif linux
- __deviceModel = __runProcess("cat", ["/sys/devices/virtual/dmi/id/sys_vendor"]);
- #end
- }
-
- return __deviceModel;
- }
-
- private static function get_deviceVendor():String
- {
- if (__deviceVendor == null)
- {
- #if (lime_cffi && !macro && windows && !html5)
- #if hl
- __deviceVendor = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_device_vendor());
- #else
- __deviceVendor = NativeCFFI.lime_system_get_device_vendor();
- #end
- #elseif android
- var vendor:String = JNI.createStaticField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").get();
- if (vendor != null)
- {
- __deviceVendor = vendor.charAt(0).toUpperCase() + vendor.substr(1);
- }
- #elseif (ios || mac || tvos)
- __deviceVendor = "Apple";
- #elseif linux
- __deviceVendor = __runProcess("cat", ["/sys/devices/virtual/dmi/id/product_name"]);
- #end
- }
-
- return __deviceVendor;
- }
-
- private static function get_desktopDirectory():String
- {
- if (__desktopDirectory == null)
- {
- __desktopDirectory = __getDirectory(DESKTOP);
- }
-
- return __desktopDirectory;
- }
-
- private static function get_documentsDirectory():String
- {
- if (__documentsDirectory == null)
- {
- __documentsDirectory = __getDirectory(DOCUMENTS);
- }
-
- return __documentsDirectory;
- }
-
- private static function get_endianness():Endian
- {
- if (__endianness == null)
- {
- #if (ps3 || wiiu || flash)
- __endianness = BIG_ENDIAN;
- #else
- var arrayBuffer = new ArrayBuffer(2);
- var uint8Array = new UInt8Array(arrayBuffer);
- var uint16array = new UInt16Array(arrayBuffer);
- uint8Array[0] = 0xAA;
- uint8Array[1] = 0xBB;
- if (uint16array[0] == 0xAABB) __endianness = BIG_ENDIAN;
- else
- __endianness = LITTLE_ENDIAN;
- #end
- }
-
- return __endianness;
- }
-
- private static function get_fontsDirectory():String
- {
- if (__fontsDirectory == null)
- {
- __fontsDirectory = __getDirectory(FONTS);
- }
-
- return __fontsDirectory;
- }
-
- private static function get_numDisplays():Int
- {
- #if (lime_cffi && !macro)
- return NativeCFFI.lime_system_get_num_displays();
- #else
- return 1;
- #end
- }
-
- private static function get_platformLabel():String
- {
- if (__platformLabel == null)
- {
- #if (lime_cffi && !macro && windows && !html5)
- #if hl
- var label:String = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_platform_label());
- #else
- var label:String = NativeCFFI.lime_system_get_platform_label();
- #end
- if (label != null) __platformLabel = StringTools.trim(label);
- #elseif linux
- __platformLabel = __runProcess("lsb_release", ["-ds"]);
- #else
- var name = System.platformName;
- var version = System.platformVersion;
- if (name != null && version != null) __platformLabel = name + " " + version;
- else if (name != null) __platformLabel = name;
- #end
- }
-
- return __platformLabel;
- }
-
- private static function get_platformName():String
- {
- if (__platformName == null)
- {
- #if windows
- __platformName = "Windows";
- #elseif mac
- __platformName = "macOS";
- #elseif linux
- __platformName = __runProcess("lsb_release", ["-is"]);
- #elseif ios
- __platformName = "iOS";
- #elseif android
- __platformName = "Android";
- #elseif air
- __platformName = "AIR";
- #elseif flash
- __platformName = "Flash Player";
- #elseif tvos
- __platformName = "tvOS";
- #elseif tizen
- __platformName = "Tizen";
- #elseif blackberry
- __platformName = "BlackBerry";
- #elseif firefox
- __platformName = "Firefox";
- #elseif webos
- __platformName = "webOS";
- #elseif nodejs
- __platformName = "Node.js";
- #elseif js
- __platformName = "HTML5";
- #end
- }
-
- return __platformName;
- }
-
- private static function get_platformVersion():String
- {
- if (__platformVersion == null)
- {
- #if (lime_cffi && !macro && windows && !html5)
- #if hl
- __platformVersion = @:privateAccess String.fromUTF8(NativeCFFI.lime_system_get_platform_version());
- #else
- __platformVersion = NativeCFFI.lime_system_get_platform_version();
- #end
- #elseif android
- var release = JNI.createStaticField("android/os/Build$VERSION", "RELEASE", "Ljava/lang/String;").get();
- var api = JNI.createStaticField("android/os/Build$VERSION", "SDK_INT", "I").get();
- if (release != null && api != null) __platformVersion = release + " (API " + api + ")";
- #elseif (lime_cffi && !macro && (ios || tvos))
- __platformVersion = NativeCFFI.lime_system_get_platform_version();
- #elseif mac
- __platformVersion = __runProcess("sw_vers", ["-productVersion"]);
- #elseif linux
- __platformVersion = __runProcess("lsb_release", ["-rs"]);
- #elseif flash
- __platformVersion = Capabilities.version;
- #end
- }
-
- return __platformVersion;
- }
-
- private static function get_userDirectory():String
- {
- if (__userDirectory == null)
- {
- __userDirectory = __getDirectory(USER);
- }
-
- return __userDirectory;
- }
-}
-
-#if (haxe_ver >= 4.0) enum #else @:enum #end abstract SystemDirectory(Int) from Int to Int from UInt to UInt
-{
- var APPLICATION = 0;
- var APPLICATION_STORAGE = 1;
- var DESKTOP = 2;
- var DOCUMENTS = 3;
- var FONTS = 4;
- var USER = 5;
-}
diff --git a/source/lime/ui/FileDialog.hx b/source/lime/ui/FileDialog.hx
deleted file mode 100644
index 418d711936..0000000000
--- a/source/lime/ui/FileDialog.hx
+++ /dev/null
@@ -1,411 +0,0 @@
-package lime.ui;
-
-import haxe.io.Bytes;
-import haxe.io.Path;
-import lime._internal.backend.native.NativeCFFI;
-import lime.app.Event;
-import lime.graphics.Image;
-import lime.system.BackgroundWorker;
-import lime.utils.ArrayBuffer;
-import lime.utils.Resource;
-#if hl
-import hl.Bytes as HLBytes;
-import hl.NativeArray;
-#end
-#if sys
-import sys.io.File;
-#end
-#if (js && html5)
-import js.html.Blob;
-#end
-
-/**
- Simple file dialog used for asking user where to save a file, or select files to open.
-
- Example usage:
- ```haxe
- var fileDialog = new FileDialog();
-
- fileDialog.onCancel.add( () -> trace("Canceled.") );
-
- fileDialog.onSave.add( path -> trace("File saved in " + path) );
-
- fileDialog.onOpen.add( res -> trace("Size of the file = " + (res:haxe.io.Bytes).length) );
-
- if ( fileDialog.open("jpg", null, "Load file") )
- trace("File dialog opened, waiting for selection...");
- else
- trace("This dialog is unsupported.");
- ```
-
- Availability note: most file dialog operations are only available on desktop targets, though
- `save()` is also available in HTML5.
-**/
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(lime._internal.backend.native.NativeCFFI)
-@:access(lime.graphics.Image)
-class FileDialog
-{
- /**
- Triggers when the user clicks "Cancel" during any operation, or when a function is unsupported
- (such as `open()` on HTML5).
- **/
- public var onCancel = new EventVoid>();
-
- /**
- Triggers when `open()` is successful. The `lime.utils.Resource` contains the file's data, and can
- be implicitly cast to `haxe.io.Bytes`.
- **/
- public var onOpen = new EventVoid>();
-
- /**
- Triggers when `open()` is successful. The `lime.utils.Resource` contains the file's data, and can
- be implicitly cast to `haxe.io.Bytes`, the String is the path to the file.
- **/
- public var onOpenFile = new Event<(Resource, String)->Void>(); // Added by @NeeEoo
-
- /**
- Triggers when `save()` is successful. The `String` is the path to the saved file.
- **/
- public var onSave = new EventVoid>();
-
- /**
- Triggers when `browse()` is successful and `type` is anything other than
- `FileDialogType.OPEN_MULTIPLE`. The `String` is the path to the selected file.
- **/
- public var onSelect = new EventVoid>();
-
- /**
- Triggers when `browse()` is successful and `type` is `FileDialogType.OPEN_MULTIPLE`. The
- `Array` contains all selected file paths.
- **/
- public var onSelectMultiple = new Event->Void>();
-
- public function new() {}
-
- /**
- Opens a file selection dialog. If successful, either `onSelect` or `onSelectMultiple` will trigger
- with the result(s).
-
- This function only works on desktop targets, and will return `false` otherwise.
- @param type Type of the file dialog: `OPEN`, `SAVE`, `OPEN_DIRECTORY` or `OPEN_MULTIPLE`.
- @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
- `"*.jpg"` will match any file ending in `.jpg`.
- @param defaultPath The directory in which to start browsing and/or the default filename to
- suggest. Defaults to `Sys.getCwd()`, with no default filename.
- @param title The title to give the dialog window.
- @return Whether `browse()` is supported on this target.
- **/
- public function browse(type:FileDialogType = null, filter:String = null, defaultPath:String = null, title:String = null):Bool
- {
- if (type == null) type = FileDialogType.OPEN;
-
- #if desktop
- var worker = new BackgroundWorker();
-
- worker.doWork.add(function(_)
- {
- switch (type)
- {
- case OPEN:
- #if linux
- if (title == null) title = "Open File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- if (bytes != null)
- {
- path = @:privateAccess String.fromUTF8(cast bytes);
- }
- #else
- path = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
-
- case OPEN_MULTIPLE:
- #if linux
- if (title == null) title = "Open Files";
- #end
-
- var paths = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes:NativeArray = cast NativeCFFI.lime_file_dialog_open_files(title, filter, defaultPath);
- if (bytes != null)
- {
- paths = [];
- for (i in 0...bytes.length)
- {
- paths[i] = @:privateAccess String.fromUTF8(bytes[i]);
- }
- }
- #else
- paths = NativeCFFI.lime_file_dialog_open_files(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(paths);
-
- case OPEN_DIRECTORY:
- #if linux
- if (title == null) title = "Open Directory";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_open_directory(title, filter, defaultPath);
- if (bytes != null)
- {
- path = @:privateAccess String.fromUTF8(cast bytes);
- }
- #else
- path = NativeCFFI.lime_file_dialog_open_directory(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
-
- case SAVE:
- #if linux
- if (title == null) title = "Save File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- if (bytes != null)
- {
- path = @:privateAccess String.fromUTF8(cast bytes);
- }
- #else
- path = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
- }
- });
-
- worker.onComplete.add(function(result)
- {
- switch (type)
- {
- case OPEN, OPEN_DIRECTORY, SAVE:
- var path:String = cast result;
-
- if (path != null)
- {
- // Makes sure the filename ends with extension
- if (type == SAVE && filter != null && path.indexOf(".") == -1)
- {
- path += "." + filter;
- }
-
- onSelect.dispatch(path);
- }
- else
- {
- onCancel.dispatch();
- }
-
- case OPEN_MULTIPLE:
- var paths:Array = cast result;
-
- if (paths != null && paths.length > 0)
- {
- onSelectMultiple.dispatch(paths);
- }
- else
- {
- onCancel.dispatch();
- }
- }
- });
-
- worker.run();
-
- return true;
- #else
- onCancel.dispatch();
- return false;
- #end
- }
-
- /**
- Shows an open file dialog. If successful, `onOpen` will trigger with the file contents.
-
- This function only works on desktop targets, and will return `false` otherwise.
- @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
- `"*.jpg"` will match any file ending in `.jpg`.
- @param defaultPath The directory in which to start browsing and/or the default filename to
- suggest. Defaults to `Sys.getCwd()`, with no default filename.
- @param title The title to give the dialog window.
- @return Whether `open()` is supported on this target.
- **/
- public function open(filter:String = null, defaultPath:String = null, title:String = null):Bool
- {
- #if (desktop && sys)
- var worker = new BackgroundWorker();
-
- worker.doWork.add(function(_)
- {
- #if linux
- if (title == null) title = "Open File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- if (bytes != null) path = @:privateAccess String.fromUTF8(cast bytes);
- #else
- path = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
- });
-
- worker.onComplete.add(function(path:String)
- {
- if (path != null)
- {
- try
- {
- var data = File.getBytes(path);
- onOpen.dispatch(data);
- onOpenFile.dispatch(data, path); // Added by @NeeEoo
- return;
- }
- catch (e:Dynamic) {}
- }
-
- onCancel.dispatch();
- });
-
- worker.run();
-
- return true;
- #else
- onCancel.dispatch();
- return false;
- #end
- }
-
- /**
- Shows an open file dialog. If successful, `onSave` will trigger with the selected path.
-
- This function only works on desktop and HMTL5 targets, and will return `false` otherwise.
- @param data The file contents, in `haxe.io.Bytes` format. (Implicit casting possible.)
- @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example,
- `"*.jpg"` will match any file ending in `.jpg`. Used only if targeting deskop.
- @param defaultPath The directory in which to start browsing and/or the default filename to
- suggest. When targeting destkop, this defaults to `Sys.getCwd()` with no default filename. When targeting
- HTML5, this defaults to the browser's download directory, with a default filename based on the MIME type.
- @param title The title to give the dialog window.
- @param type The default MIME type of the file, in case the type can't be determined from the
- file data. Used only if targeting HTML5.
- @return Whether `save()` is supported on this target.
- **/
- public function save(data:Resource, filter:String = null, defaultPath:String = null, title:String = null, type:String = "application/octet-stream"):Bool
- {
- if (data == null)
- {
- onCancel.dispatch();
- return false;
- }
-
- #if (desktop && sys)
- var worker = new BackgroundWorker();
-
- worker.doWork.add(function(_)
- {
- #if linux
- if (title == null) title = "Save File";
- #end
-
- var path = null;
- #if (!macro && lime_cffi)
- #if hl
- var bytes = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- path = @:privateAccess String.fromUTF8(cast bytes);
- #else
- path = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath);
- #end
- #end
-
- worker.sendComplete(path);
- });
-
- worker.onComplete.add(function(path:String)
- {
- if (path != null)
- {
- try
- {
- File.saveBytes(path, data);
- onSave.dispatch(path);
- return;
- }
- catch (e:Dynamic) {}
- }
-
- onCancel.dispatch();
- });
-
- worker.run();
-
- return true;
- #elseif (js && html5)
- // TODO: Cleaner API for mimeType detection
-
- var defaultExtension = "";
-
- if (Image.__isPNG(data))
- {
- type = "image/png";
- defaultExtension = ".png";
- }
- else if (Image.__isJPG(data))
- {
- type = "image/jpeg";
- defaultExtension = ".jpg";
- }
- else if (Image.__isGIF(data))
- {
- type = "image/gif";
- defaultExtension = ".gif";
- }
- else if (Image.__isWebP(data))
- {
- type = "image/webp";
- defaultExtension = ".webp";
- }
-
- var path = defaultPath != null ? Path.withoutDirectory(defaultPath) : "download" + defaultExtension;
- var buffer = (data : Bytes).getData();
- buffer = buffer.slice(0, (data : Bytes).length);
-
- #if commonjs
- untyped #if haxe4 js.Syntax.code #else __js__ #end ("require ('file-saver')")(new Blob([buffer], {type: type}), path, true);
- #else
- untyped window.saveAs(new Blob([buffer], {type: type}), path, true);
- #end
- onSave.dispatch(path);
- return true;
- #else
- onCancel.dispatch();
- return false;
- #end
- }
-}
diff --git a/source/lime/utils/ObjectPool.hx b/source/lime/utils/ObjectPool.hx
deleted file mode 100644
index f05715c35e..0000000000
--- a/source/lime/utils/ObjectPool.hx
+++ /dev/null
@@ -1,347 +0,0 @@
-package lime.utils;
-
-import haxe.ds.ObjectMap;
-
-
-/**
- A generic object pool for reusing objects.
- **/
-#if !lime_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-#if !js @:generic #end class ObjectPool
-{
- /**
- The number of active objects in the pool.
- **/
- public var activeObjects(default, null):Int;
-
- /**
- The number of inactive objects in the pool.
- **/
- public var inactiveObjects(default, null):Int;
-
- /**
- The total size of the object pool (both active and inactive objects).
- **/
- public var size(get, set):Null;
-
- @:noCompletion private var __inactiveObject0:T;
- @:noCompletion private var __inactiveObject1:T;
- @:noCompletion private var __inactiveObjectList:List;
- @:noCompletion private var __pool:Map;
- @:noCompletion private var __size:Null;
-
- /**
- Creates a new ObjectPool instance.
-
- @param create A function that creates a new instance of type T.
- @param clean A function that cleans up an instance of type T before it is reused.
- @param size The maximum size of the object pool.
- **/
- public function new(create:Void->T = null, clean:T->Void = null, size:Null = null)
- {
- __pool = cast new ObjectMap();
-
- activeObjects = 0;
- inactiveObjects = 0;
-
- __inactiveObject0 = null;
- __inactiveObject1 = null;
- __inactiveObjectList = new List();
-
- if (create != null)
- {
- this.create = create;
- }
- if (clean != null)
- {
- this.clean = clean;
- }
- if (size != null)
- {
- this.size = size;
- }
- }
- /**
- Adds an object to the object pool.
-
- @param object The object to add to the pool.
- **/
- public function add(object:T):Void
- {
- if (object != null && !__pool.exists(object))
- {
- __pool.set(object, false);
- clean(object);
- __addInactive(object);
- }
- }
-
- /**
- Dynamic function.
-
- Cleans up an object before returning it to the pool.
-
- @param object The object to clean up.
- **/
- public dynamic function clean(object:T):Void {}
-
- /**
- Clears the object pool, removing all objects.
- **/
- public function clear():Void
- {
- __pool = cast new ObjectMap();
-
- activeObjects = 0;
- inactiveObjects = 0;
-
- __inactiveObject0 = null;
- __inactiveObject1 = null;
- __inactiveObjectList.clear();
- }
-
- /**
- Dynamic function.
-
- Creates a new Object.
- **/
- public dynamic function create():T
- {
- return null;
- }
-
- /**
- Creates a new object and adds it to the pool, or returns an existing inactive object from the pool.
-
- @return The object retrieved from the pool, or null if the pool is full and no new objects can be created.
- **/
- public function get():T
- {
- var object = null;
-
- if (inactiveObjects > 0)
- {
- object = __getInactive();
- }
- else if (__size == null || activeObjects < __size)
- {
- object = create();
-
- if (object != null)
- {
- __pool.set(object, true);
- activeObjects++;
- }
- }
-
- return object;
- }
-
- /**
- Releases an active object back into the pool.
-
- @param object The object to release.
- **/
- public function release(object:T):Void
- {
- #if lime_pool_debug
- if (object == null || !__pool.exists(object))
- {
- Log.error("Object is not a member of the pool");
- }
- else if (!__pool.get(object))
- {
- Log.error("Object has already been released");
- }
- #end
-
- activeObjects--;
-
- if (__size == null || activeObjects + inactiveObjects < __size)
- {
- clean(object);
- __addInactive(object);
- }
- else
- {
- __pool.remove(object);
- }
- }
-
- /**
- Removes an object from the pool.
-
- @param object The object to remove from the pool.
- **/
- public function remove(object:T):Void
- {
- if (object != null && __pool.exists(object))
- {
- __pool.remove(object);
-
- if (__inactiveObject0 == object)
- {
- __inactiveObject0 = null;
- inactiveObjects--;
- }
- else if (__inactiveObject1 == object)
- {
- __inactiveObject1 = null;
- inactiveObjects--;
- }
- else if (__inactiveObjectList.remove(object))
- {
- inactiveObjects--;
- }
- else
- {
- activeObjects--;
- }
- }
- }
-
- @:noCompletion private inline function __addInactive(object:T):Void
- {
- #if lime_pool_debug
- __pool.set(object, false);
- #end
-
- if (__inactiveObject0 == null)
- {
- __inactiveObject0 = object;
- }
- else if (__inactiveObject1 == null)
- {
- __inactiveObject1 = object;
- }
- else
- {
- __inactiveObjectList.add(object);
- }
-
- inactiveObjects++;
- }
-
- @:noCompletion private inline function __getInactive():T
- {
- var object = null;
-
- if (__inactiveObject0 != null)
- {
- object = __inactiveObject0;
- __inactiveObject0 = null;
- }
- else if (__inactiveObject1 != null)
- {
- object = __inactiveObject1;
- __inactiveObject1 = null;
- }
- else
- {
- object = __inactiveObjectList.pop();
-
- if (__inactiveObjectList.length > 0)
- {
- __inactiveObject0 = __inactiveObjectList.pop();
- }
-
- if (__inactiveObjectList.length > 0)
- {
- __inactiveObject1 = __inactiveObjectList.pop();
- }
- }
-
- #if lime_pool_debug
- __pool.set(object, true);
- #end
-
- inactiveObjects--;
- activeObjects++;
-
- return object;
- }
-
- @:noCompletion private function __removeInactive(count:Int):Void
- {
- if (count <= 0 || inactiveObjects == 0) return;
-
- if (__inactiveObject0 != null)
- {
- __pool.remove(__inactiveObject0);
- __inactiveObject0 = null;
- inactiveObjects--;
- count--;
- }
-
- if (count == 0 || inactiveObjects == 0) return;
-
- if (__inactiveObject1 != null)
- {
- __pool.remove(__inactiveObject1);
- __inactiveObject1 = null;
- inactiveObjects--;
- count--;
- }
-
- if (count == 0 || inactiveObjects == 0) return;
-
- for (object in __inactiveObjectList)
- {
- __pool.remove(object);
- __inactiveObjectList.remove(object);
- inactiveObjects--;
- count--;
-
- if (count == 0 || inactiveObjects == 0) return;
- }
- }
-
- // Get & Set Methods
- @:noCompletion private function get_size():Null
- {
- return __size;
- }
-
- @:noCompletion private function set_size(value:Null):Null
- {
- if (value == null)
- {
- __size = null;
- }
- else
- {
- var current = inactiveObjects + activeObjects;
- __size = value;
-
- if (current > value)
- {
- __removeInactive(current - value);
- }
- else if (value > current)
- {
- var object;
-
- for (i in 0...(value - current))
- {
- object = create();
-
- if (object != null)
- {
- __pool.set(object, false);
- __inactiveObjectList.add(object);
- inactiveObjects++;
- }
- else
- {
- break;
- }
- }
- }
- }
-
- return value;
- }
-}
diff --git a/source/openfl/display/DisplayObjectRenderer.hx b/source/openfl/display/DisplayObjectRenderer.hx
deleted file mode 100644
index 0267742463..0000000000
--- a/source/openfl/display/DisplayObjectRenderer.hx
+++ /dev/null
@@ -1,864 +0,0 @@
-package openfl.display;
-
-#if !flash
-import openfl.display._internal.Context3DGraphics;
-import openfl.display.Bitmap;
-import openfl.display.DisplayObject;
-import openfl.display.Tilemap;
-import openfl.events.EventDispatcher;
-import openfl.events.RenderEvent;
-import openfl.geom.ColorTransform;
-import openfl.geom.Matrix;
-import openfl.geom.Point;
-import openfl.geom.Rectangle;
-import openfl.text.TextField;
-#if lime
-import lime._internal.graphics.ImageCanvasUtil; // TODO
-import lime.graphics.cairo.Cairo;
-import lime.graphics.RenderContext;
-import lime.graphics.RenderContextType;
-#end
-
-#if !openfl_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(openfl.display._internal.Context3DGraphics)
-@:access(lime.graphics.ImageBuffer)
-@:access(openfl.display.Bitmap)
-@:access(openfl.display.BitmapData)
-@:access(openfl.display.DisplayObject)
-@:access(openfl.display.Graphics)
-@:access(openfl.display.Tilemap)
-@:access(openfl.display3D.Context3D)
-@:access(openfl.events.RenderEvent)
-@:access(openfl.filters.BitmapFilter)
-@:access(openfl.geom.ColorTransform)
-@:access(openfl.geom.Rectangle)
-@:access(openfl.geom.Transform)
-@:access(openfl.text.TextField)
-@:allow(openfl.display._internal)
-@:allow(openfl.display)
-@:allow(openfl.text)
-class DisplayObjectRenderer extends EventDispatcher
-{
- @:noCompletion private var __allowSmoothing:Bool;
- @:noCompletion private var __blendMode:BlendMode;
- @:noCompletion private var __cleared:Bool;
- @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __context:#if lime RenderContext #else Dynamic #end;
- @:noCompletion private var __overrideBlendMode:BlendMode;
- @:noCompletion private var __pixelRatio:Float;
- @:noCompletion private var __roundPixels:Bool;
- @:noCompletion private var __stage:Stage;
- @:noCompletion private var __tempColorTransform:ColorTransform;
- @:noCompletion private var __transparent:Bool;
- @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __type:#if lime RenderContextType #else Dynamic #end;
- @:noCompletion private var __worldAlpha:Float;
- @:noCompletion private var __worldColorTransform:ColorTransform;
- @:noCompletion private var __worldTransform:Matrix;
-
- @:noCompletion private function new()
- {
- super();
-
- __allowSmoothing = true;
- __pixelRatio = 1;
- __tempColorTransform = new ColorTransform();
- __worldAlpha = 1;
- }
-
- @:noCompletion private function __clear():Void {}
-
- @:noCompletion private function __getAlpha(value:Float):Float
- {
- return value * __worldAlpha;
- }
-
- @:noCompletion private function __getColorTransform(value:ColorTransform):ColorTransform
- {
- if (__worldColorTransform != null)
- {
- __tempColorTransform.__copyFrom(__worldColorTransform);
- __tempColorTransform.__combine(value);
- return __tempColorTransform;
- }
- else
- {
- return value;
- }
- }
-
- @:noCompletion private function __popMask():Void {}
-
- @:noCompletion private function __popMaskObject(object:DisplayObject, handleScrollRect:Bool = true):Void {}
-
- @:noCompletion private function __popMaskRect():Void {}
-
- @:noCompletion private function __pushMask(mask:DisplayObject):Void {}
-
- @:noCompletion private function __pushMaskObject(object:DisplayObject, handleScrollRect:Bool = true):Void {}
-
- @:noCompletion private function __pushMaskRect(rect:Rectangle, transform:Matrix):Void {}
-
- @:noCompletion private function __render(object:IBitmapDrawable):Void {}
-
- @:noCompletion private function __renderEvent(displayObject:DisplayObject):Void
- {
- var renderer = this;
- #if lime
- if (displayObject.__customRenderEvent != null && displayObject.__renderable)
- {
- displayObject.__customRenderEvent.allowSmoothing = renderer.__allowSmoothing;
- displayObject.__customRenderEvent.objectMatrix.copyFrom(displayObject.__renderTransform);
- displayObject.__customRenderEvent.objectColorTransform.__copyFrom(displayObject.__worldColorTransform);
- displayObject.__customRenderEvent.renderer = renderer;
-
- switch (renderer.__type)
- {
- case OPENGL:
- if (!renderer.__cleared) renderer.__clear();
-
- var renderer:OpenGLRenderer = cast renderer;
- renderer.setShader(displayObject.__worldShader);
- renderer.__context3D.__flushGL();
-
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_OPENGL;
-
- case CAIRO:
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_CAIRO;
-
- case DOM:
- if (displayObject.stage != null && displayObject.__worldVisible)
- {
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_DOM;
- }
- else
- {
- displayObject.__customRenderEvent.type = RenderEvent.CLEAR_DOM;
- }
-
- case CANVAS:
- displayObject.__customRenderEvent.type = RenderEvent.RENDER_CANVAS;
-
- default:
- return;
- }
-
- renderer.__setBlendMode(displayObject.__worldBlendMode);
- renderer.__pushMaskObject(displayObject);
-
- displayObject.dispatchEvent(displayObject.__customRenderEvent);
-
- renderer.__popMaskObject(displayObject);
-
- if (renderer.__type == OPENGL)
- {
- var renderer:OpenGLRenderer = cast renderer;
- renderer.setViewport();
- }
- }
- #end
- }
-
- @:noCompletion private function __resize(width:Int, height:Int):Void {}
-
- @:noCompletion private function __setBlendMode(value:BlendMode):Void {}
-
- @:noCompletion private function __shouldCacheHardware(displayObject:DisplayObject, value:Null):Null
- {
- if (displayObject == null) return null;
-
- switch (displayObject.__drawableType)
- {
- case SPRITE, STAGE:
- if (value == true) return true;
- value = __shouldCacheHardware_DisplayObject(displayObject, value);
- if (value == true) return true;
-
- if (displayObject.__children != null)
- {
- for (child in displayObject.__children)
- {
- value = __shouldCacheHardware_DisplayObject(child, value);
- if (value == true) return true;
- }
- }
-
- return value;
-
- case TEXT_FIELD:
- return value == true ? true : false;
-
- case TILEMAP:
- return true;
-
- default:
- return __shouldCacheHardware_DisplayObject(displayObject, value);
- }
- }
-
- @:noCompletion private function __shouldCacheHardware_DisplayObject(displayObject:DisplayObject, value:Null):Null
- {
- if (value == true || displayObject.__filters != null) return true;
-
- if (value == false || (displayObject.__graphics != null && !Context3DGraphics.isCompatible(displayObject.__graphics)))
- {
- return false;
- }
-
- return null;
- }
-
- @:noCompletion private function __updateCacheBitmap(displayObject:DisplayObject, force:Bool):Bool
- {
- if (displayObject == null) return false;
- var renderer = this;
-
- switch (displayObject.__drawableType)
- {
- case BITMAP:
- var bitmap:Bitmap = cast displayObject;
- // TODO: Handle filters without an intermediate draw
- if (bitmap.__bitmapData == null
- || (bitmap.__filters == null #if lime && renderer.__type == OPENGL #end && bitmap.__cacheBitmap == null)) return false;
- force = (bitmap.__bitmapData.image != null && bitmap.__bitmapData.image.version != bitmap.__imageVersion);
-
- case TEXT_FIELD:
- var textField:TextField = cast displayObject;
- if (textField.__filters == null #if lime && renderer.__type == OPENGL #end && textField.__cacheBitmap == null
- && !textField.__domRender) return false;
- if (force) textField.__renderDirty = true;
- force = force || textField.__dirty;
-
- case TILEMAP:
- var tilemap:Tilemap = cast displayObject;
- if (tilemap.__filters == null #if lime && renderer.__type == OPENGL #end && tilemap.__cacheBitmap == null) return false;
-
- default:
- }
-
- #if lime
- if (displayObject.__isCacheBitmapRender) return false;
- #if openfl_disable_cacheasbitmap
- return false;
- #end
-
- var colorTransform = ColorTransform.__pool.get();
- colorTransform.__copyFrom(displayObject.__worldColorTransform);
- if (renderer.__worldColorTransform != null) colorTransform.__combine(renderer.__worldColorTransform);
- var updated = false;
-
- // TODO: Do not force cacheAsBitmap on OpenGL once Scale-9 is properly supported in Context3DShape
- if (displayObject.cacheAsBitmap
- || (renderer.__type != OPENGL && !colorTransform.__isDefault(true))
- || (renderer.__type == OPENGL && displayObject.scale9Grid != null))
- {
- var rect = null;
-
- var needRender = (displayObject.__cacheBitmap == null
- || (displayObject.__renderDirty && (force || (displayObject.__children != null && displayObject.__children.length > 0)))
- || displayObject.opaqueBackground != displayObject.__cacheBitmapBackground);
- var softwareDirty = needRender
- || (displayObject.__graphics != null && displayObject.__graphics.__softwareDirty)
- || !displayObject.__cacheBitmapColorTransform.__equals(colorTransform, true);
- var hardwareDirty = needRender || (displayObject.__graphics != null && displayObject.__graphics.__hardwareDirty);
-
- var renderType = renderer.__type;
-
- if (softwareDirty || hardwareDirty)
- {
- #if !openfl_force_gl_cacheasbitmap
- if (renderType == OPENGL)
- {
- if (#if !openfl_disable_gl_cacheasbitmap __shouldCacheHardware(displayObject, null) == false #else true #end)
- {
- #if (js && html5)
- renderType = CANVAS;
- #else
- renderType = CAIRO;
- #end
- }
- }
- #end
-
- if (softwareDirty && (renderType == CANVAS || renderType == CAIRO)) needRender = true;
- if (hardwareDirty && renderType == OPENGL) needRender = true;
- }
-
- var updateTransform = (needRender || !displayObject.__cacheBitmap.__worldTransform.equals(displayObject.__worldTransform));
- var hasFilters = #if !openfl_disable_filters displayObject.__filters != null #else false #end;
-
- #if !openfl_enable_cacheasbitmap
- if (renderer.__type == DOM && !hasFilters)
- {
- return false;
- }
- #end
-
- if (hasFilters && !needRender)
- {
- for (filter in displayObject.__filters)
- {
- if (filter.__renderDirty)
- {
- needRender = true;
- break;
- }
- }
- }
-
- if (displayObject.__cacheBitmapMatrix == null)
- {
- displayObject.__cacheBitmapMatrix = new Matrix();
- }
-
- var bitmapMatrix = (displayObject.__cacheAsBitmapMatrix != null ? displayObject.__cacheAsBitmapMatrix : displayObject.__renderTransform);
-
- if (!needRender
- && (bitmapMatrix.a != displayObject.__cacheBitmapMatrix.a
- || bitmapMatrix.b != displayObject.__cacheBitmapMatrix.b
- || bitmapMatrix.c != displayObject.__cacheBitmapMatrix.c
- || bitmapMatrix.d != displayObject.__cacheBitmapMatrix.d))
- {
- needRender = true;
- }
-
- if (!needRender
- && renderer.__type != OPENGL
- && displayObject.__cacheBitmapData != null
- && displayObject.__cacheBitmapData.image != null
- && displayObject.__cacheBitmapData.image.version < displayObject.__cacheBitmapData.__textureVersion)
- {
- needRender = true;
- }
-
- // Ensure that cached bitmap is updated after changes to scrollRect
- if (!needRender)
- {
- var current = displayObject;
- while (current != null)
- {
- if (current.scrollRect != null)
- {
- // TODO: do we need to update transform if scroll rects haven't changed?
- updateTransform = true;
- break;
- }
- current = current.parent;
- }
- }
-
- displayObject.__cacheBitmapMatrix.copyFrom(bitmapMatrix);
- displayObject.__cacheBitmapMatrix.tx = 0;
- displayObject.__cacheBitmapMatrix.ty = 0;
-
- // TODO: Handle dimensions better if object has a scrollRect?
-
- var bitmapWidth = 0, bitmapHeight = 0;
- var filterWidth = 0, filterHeight = 0;
- var offsetX = 0., offsetY = 0.;
-
- #if (openfl_disable_hdpi || openfl_disable_hdpi_cacheasbitmap)
- var pixelRatio = 1;
- #else
- var pixelRatio = __pixelRatio;
- #end
-
- if (updateTransform || needRender)
- {
- rect = Rectangle.__pool.get();
-
- displayObject.__getFilterBounds(rect, displayObject.__cacheBitmapMatrix);
-
- filterWidth = rect.width > 0 ? Math.floor(rect.width * pixelRatio) : 0;
- filterHeight = rect.height > 0 ? Math.floor(rect.height * pixelRatio) : 0;
-
- offsetX = rect.x > 0 ? Math.ceil(rect.x) : Math.floor(rect.x);
- offsetY = rect.y > 0 ? Math.ceil(rect.y) : Math.floor(rect.y);
-
- if (displayObject.__cacheBitmapData != null)
- {
- if (filterWidth > displayObject.__cacheBitmapData.width || filterHeight > displayObject.__cacheBitmapData.height)
- {
- bitmapWidth = Math.ceil(Math.max(filterWidth * 1.25, displayObject.__cacheBitmapData.width));
- bitmapHeight = Math.ceil(Math.max(filterHeight * 1.25, displayObject.__cacheBitmapData.height));
- needRender = true;
- }
- else
- {
- bitmapWidth = displayObject.__cacheBitmapData.width;
- bitmapHeight = displayObject.__cacheBitmapData.height;
- }
- }
- else
- {
- bitmapWidth = filterWidth;
- bitmapHeight = filterHeight;
- }
- }
-
- if (needRender)
- {
- updateTransform = true;
- displayObject.__cacheBitmapBackground = displayObject.opaqueBackground;
-
- if (filterWidth >= 0.5 && filterHeight >= 0.5)
- {
- var needsFill = (displayObject.opaqueBackground != null
- && (bitmapWidth != filterWidth || bitmapHeight != filterHeight));
- var fillColor = displayObject.opaqueBackground != null ? (0xFF << 24) | displayObject.opaqueBackground : 0;
- var bitmapColor = needsFill ? 0 : fillColor;
- var allowFramebuffer = (renderer.__type == OPENGL);
-
- if (displayObject.__cacheBitmapData == null
- || bitmapWidth > displayObject.__cacheBitmapData.width
- || bitmapHeight > displayObject.__cacheBitmapData.height)
- {
- displayObject.__cacheBitmapData = new BitmapData(bitmapWidth, bitmapHeight, true, bitmapColor);
-
- if (displayObject.__cacheBitmap == null) displayObject.__cacheBitmap = new Bitmap();
- displayObject.__cacheBitmap.__bitmapData = displayObject.__cacheBitmapData;
- displayObject.__cacheBitmapRenderer = null;
- }
- else
- {
- displayObject.__cacheBitmapData.__fillRect(displayObject.__cacheBitmapData.rect, bitmapColor, allowFramebuffer);
- }
-
- if (needsFill)
- {
- rect.setTo(0, 0, filterWidth, filterHeight);
- displayObject.__cacheBitmapData.__fillRect(rect, fillColor, allowFramebuffer);
- }
- }
- else
- {
- ColorTransform.__pool.release(colorTransform);
-
- displayObject.__cacheBitmap = null;
- displayObject.__cacheBitmapData = null;
- displayObject.__cacheBitmapData2 = null;
- displayObject.__cacheBitmapData3 = null;
- displayObject.__cacheBitmapRenderer = null;
-
- if (displayObject.__drawableType == TEXT_FIELD)
- {
- var textField:TextField = cast displayObject;
- if (textField.__cacheBitmap != null)
- {
- textField.__cacheBitmap.__renderTransform.tx -= textField.__offsetX * pixelRatio;
- textField.__cacheBitmap.__renderTransform.ty -= textField.__offsetY * pixelRatio;
- }
- }
-
- return true;
- }
- }
- else
- {
- // Should we retain these longer?
-
- displayObject.__cacheBitmapData = displayObject.__cacheBitmap.bitmapData;
- displayObject.__cacheBitmapData2 = null;
- displayObject.__cacheBitmapData3 = null;
- }
-
- if (updateTransform || needRender)
- {
- displayObject.__cacheBitmap.__worldTransform.copyFrom(displayObject.__worldTransform);
-
- if (bitmapMatrix == displayObject.__renderTransform)
- {
- displayObject.__cacheBitmap.__renderTransform.identity();
- displayObject.__cacheBitmap.__renderTransform.scale(1 / pixelRatio, 1 / pixelRatio);
- displayObject.__cacheBitmap.__renderTransform.tx = displayObject.__renderTransform.tx + offsetX;
- displayObject.__cacheBitmap.__renderTransform.ty = displayObject.__renderTransform.ty + offsetY;
- }
- else
- {
- displayObject.__cacheBitmap.__renderTransform.copyFrom(displayObject.__cacheBitmapMatrix);
- displayObject.__cacheBitmap.__renderTransform.invert();
- displayObject.__cacheBitmap.__renderTransform.concat(displayObject.__renderTransform);
- displayObject.__cacheBitmap.__renderTransform.a *= 1 / pixelRatio;
- displayObject.__cacheBitmap.__renderTransform.d *= 1 / pixelRatio;
- displayObject.__cacheBitmap.__renderTransform.tx += offsetX;
- displayObject.__cacheBitmap.__renderTransform.ty += offsetY;
- }
- }
-
- displayObject.__cacheBitmap.smoothing = renderer.__allowSmoothing;
- displayObject.__cacheBitmap.__renderable = displayObject.__renderable;
- displayObject.__cacheBitmap.__worldAlpha = displayObject.__worldAlpha;
- displayObject.__cacheBitmap.__worldBlendMode = displayObject.__worldBlendMode;
- displayObject.__cacheBitmap.__worldShader = displayObject.__worldShader;
- // displayObject.__cacheBitmap.__scrollRect = displayObject.__scrollRect;
- // displayObject.__cacheBitmap.filters = displayObject.filters;
- displayObject.__cacheBitmap.mask = displayObject.__mask;
-
- if (needRender)
- {
- #if lime
- if (displayObject.__cacheBitmapRenderer == null || renderType != displayObject.__cacheBitmapRenderer.__type)
- {
- if (renderType == OPENGL)
- {
- displayObject.__cacheBitmapRenderer = new OpenGLRenderer(cast(renderer, OpenGLRenderer).__context3D, displayObject.__cacheBitmapData);
- }
- else
- {
- if (displayObject.__cacheBitmapData.image == null)
- {
- var color = displayObject.opaqueBackground != null ? (0xFF << 24) | displayObject.opaqueBackground : 0;
- displayObject.__cacheBitmapData = new BitmapData(bitmapWidth, bitmapHeight, true, color);
- displayObject.__cacheBitmap.__bitmapData = displayObject.__cacheBitmapData;
- }
-
- #if (js && html5)
- ImageCanvasUtil.convertToCanvas(displayObject.__cacheBitmapData.image);
- displayObject.__cacheBitmapRenderer = new CanvasRenderer(displayObject.__cacheBitmapData.image.buffer.__srcContext);
- #else
- displayObject.__cacheBitmapRenderer = new CairoRenderer(new Cairo(displayObject.__cacheBitmapData.getSurface()));
- #end
- }
-
- displayObject.__cacheBitmapRenderer.__worldTransform = new Matrix();
- displayObject.__cacheBitmapRenderer.__worldColorTransform = new ColorTransform();
- }
- #else
- return false;
- #end
-
- if (displayObject.__cacheBitmapColorTransform == null) displayObject.__cacheBitmapColorTransform = new ColorTransform();
-
- displayObject.__cacheBitmapRenderer.__stage = displayObject.stage;
-
- displayObject.__cacheBitmapRenderer.__allowSmoothing = renderer.__allowSmoothing;
- displayObject.__cacheBitmapRenderer.__setBlendMode(NORMAL);
- displayObject.__cacheBitmapRenderer.__worldAlpha = 1 / displayObject.__worldAlpha;
-
- displayObject.__cacheBitmapRenderer.__worldTransform.copyFrom(displayObject.__renderTransform);
- displayObject.__cacheBitmapRenderer.__worldTransform.invert();
- displayObject.__cacheBitmapRenderer.__worldTransform.concat(displayObject.__cacheBitmapMatrix);
- displayObject.__cacheBitmapRenderer.__worldTransform.tx -= offsetX;
- displayObject.__cacheBitmapRenderer.__worldTransform.ty -= offsetY;
- displayObject.__cacheBitmapRenderer.__worldTransform.scale(pixelRatio, pixelRatio);
-
- displayObject.__cacheBitmapRenderer.__pixelRatio = pixelRatio;
-
- displayObject.__cacheBitmapRenderer.__worldColorTransform.__copyFrom(colorTransform);
- displayObject.__cacheBitmapRenderer.__worldColorTransform.__invert();
-
- displayObject.__isCacheBitmapRender = true;
-
- if (displayObject.__cacheBitmapRenderer.__type == OPENGL)
- {
- var parentRenderer:OpenGLRenderer = cast renderer;
- var childRenderer:OpenGLRenderer = cast displayObject.__cacheBitmapRenderer;
-
- var context = childRenderer.__context3D;
-
- var cacheRTT = context.__state.renderToTexture;
- var cacheRTTDepthStencil = context.__state.renderToTextureDepthStencil;
- var cacheRTTAntiAlias = context.__state.renderToTextureAntiAlias;
- var cacheRTTSurfaceSelector = context.__state.renderToTextureSurfaceSelector;
-
- // var cacheFramebuffer = context.__contextState.__currentGLFramebuffer;
-
- var cacheBlendMode = parentRenderer.__blendMode;
- parentRenderer.__suspendClipAndMask();
- childRenderer.__copyShader(parentRenderer);
- // childRenderer.__copyState (parentRenderer);
-
- displayObject.__cacheBitmapData.__setUVRect(context, 0, 0, filterWidth, filterHeight);
- childRenderer.__setRenderTarget(displayObject.__cacheBitmapData);
- if (displayObject.__cacheBitmapData.image != null)
- displayObject.__cacheBitmapData.__textureVersion = displayObject.__cacheBitmapData.image.version
- + 1;
-
- displayObject.__cacheBitmapData.__drawGL(displayObject, childRenderer);
-
- if (hasFilters)
- {
- var needSecondBitmapData = true;
- var needCopyOfOriginal = false;
-
- for (filter in displayObject.__filters)
- {
- // if (filter.__needSecondBitmapData) {
- // needSecondBitmapData = true;
- // }
- if (filter.__preserveObject)
- {
- needCopyOfOriginal = true;
- }
- }
-
- var bitmap = displayObject.__cacheBitmapData;
- var bitmap2 = null;
- var bitmap3 = null;
-
- // if (needSecondBitmapData) {
- if (displayObject.__cacheBitmapData2 == null
- || bitmapWidth > displayObject.__cacheBitmapData2.width
- || bitmapHeight > displayObject.__cacheBitmapData2.height)
- {
- displayObject.__cacheBitmapData2 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
- }
- else
- {
- displayObject.__cacheBitmapData2.fillRect(displayObject.__cacheBitmapData2.rect, 0);
- if (displayObject.__cacheBitmapData2.image != null)
- {
- displayObject.__cacheBitmapData2.__textureVersion = displayObject.__cacheBitmapData2.image.version + 1;
- }
- }
- displayObject.__cacheBitmapData2.__setUVRect(context, 0, 0, filterWidth, filterHeight);
- bitmap2 = displayObject.__cacheBitmapData2;
- // } else {
- // bitmap2 = bitmapData;
- // }
-
- if (needCopyOfOriginal)
- {
- if (displayObject.__cacheBitmapData3 == null
- || bitmapWidth > displayObject.__cacheBitmapData3.width
- || bitmapHeight > displayObject.__cacheBitmapData3.height)
- {
- displayObject.__cacheBitmapData3 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
- }
- else
- {
- displayObject.__cacheBitmapData3.fillRect(displayObject.__cacheBitmapData3.rect, 0);
- if (displayObject.__cacheBitmapData3.image != null)
- {
- displayObject.__cacheBitmapData3.__textureVersion = displayObject.__cacheBitmapData3.image.version + 1;
- }
- }
- displayObject.__cacheBitmapData3.__setUVRect(context, 0, 0, filterWidth, filterHeight);
- bitmap3 = displayObject.__cacheBitmapData3;
- }
-
- childRenderer.__setBlendMode(NORMAL);
- childRenderer.__worldAlpha = 1;
- childRenderer.__worldTransform.identity();
- childRenderer.__worldColorTransform.__identity();
-
- // var sourceRect = bitmap.rect;
- // if (__tempPoint == null) __tempPoint = new Point ();
- // var destPoint = __tempPoint;
- var shader, cacheBitmap;
-
- for (filter in displayObject.__filters)
- {
- if (filter.__preserveObject)
- {
- childRenderer.__setRenderTarget(bitmap3);
- childRenderer.__renderFilterPass(bitmap, childRenderer.__defaultDisplayShader, filter.__smooth);
- }
-
- for (i in 0...filter.__numShaderPasses)
- {
- shader = filter.__initShader(childRenderer, i, filter.__preserveObject ? bitmap3 : null);
- childRenderer.__setBlendMode(filter.__shaderBlendMode);
- childRenderer.__setRenderTarget(bitmap2);
- childRenderer.__renderFilterPass(bitmap, shader, filter.__smooth);
-
- cacheBitmap = bitmap;
- bitmap = bitmap2;
- bitmap2 = cacheBitmap;
- }
-
- filter.__renderDirty = false;
- }
-
- displayObject.__cacheBitmap.__bitmapData = bitmap;
- }
-
- parentRenderer.__blendMode = NORMAL;
- parentRenderer.__setBlendMode(cacheBlendMode);
- parentRenderer.__copyShader(childRenderer);
-
- if (cacheRTT != null)
- {
- context.setRenderToTexture(cacheRTT, cacheRTTDepthStencil, cacheRTTAntiAlias, cacheRTTSurfaceSelector);
- }
- else
- {
- context.setRenderToBackBuffer();
- }
-
- // context.__bindGLFramebuffer (cacheFramebuffer);
-
- // parentRenderer.__restoreState (childRenderer);
- parentRenderer.__resumeClipAndMask(childRenderer);
- parentRenderer.setViewport();
-
- displayObject.__cacheBitmapColorTransform.__copyFrom(colorTransform);
- }
- else
- {
- #if (js && html5)
- displayObject.__cacheBitmapData.__drawCanvas(displayObject, cast displayObject.__cacheBitmapRenderer);
- #else
- displayObject.__cacheBitmapData.__drawCairo(displayObject, cast displayObject.__cacheBitmapRenderer);
- #end
-
- if (hasFilters)
- {
- var needSecondBitmapData = false;
- var needCopyOfOriginal = false;
-
- for (filter in displayObject.__filters)
- {
- if (filter.__needSecondBitmapData)
- {
- needSecondBitmapData = true;
- }
- if (filter.__preserveObject)
- {
- needCopyOfOriginal = true;
- }
- }
-
- var bitmap = displayObject.__cacheBitmapData;
- var bitmap2 = null;
- var bitmap3 = null;
-
- if (needSecondBitmapData)
- {
- if (displayObject.__cacheBitmapData2 == null
- || displayObject.__cacheBitmapData2.image == null
- || bitmapWidth > displayObject.__cacheBitmapData2.width
- || bitmapHeight > displayObject.__cacheBitmapData2.height)
- {
- displayObject.__cacheBitmapData2 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
- }
- else
- {
- displayObject.__cacheBitmapData2.fillRect(displayObject.__cacheBitmapData2.rect, 0);
- }
- bitmap2 = displayObject.__cacheBitmapData2;
- }
- else
- {
- bitmap2 = bitmap;
- }
-
- if (needCopyOfOriginal)
- {
- if (displayObject.__cacheBitmapData3 == null
- || displayObject.__cacheBitmapData3.image == null
- || bitmapWidth > displayObject.__cacheBitmapData3.width
- || bitmapHeight > displayObject.__cacheBitmapData3.height)
- {
- displayObject.__cacheBitmapData3 = new BitmapData(bitmapWidth, bitmapHeight, true, 0);
- }
- else
- {
- displayObject.__cacheBitmapData3.fillRect(displayObject.__cacheBitmapData3.rect, 0);
- }
- bitmap3 = displayObject.__cacheBitmapData3;
- }
-
- if (displayObject.__tempPoint == null) displayObject.__tempPoint = new Point();
- var destPoint = displayObject.__tempPoint;
- var cacheBitmap, lastBitmap;
-
- for (filter in displayObject.__filters)
- {
- if (filter.__preserveObject)
- {
- bitmap3.copyPixels(bitmap, bitmap.rect, destPoint);
- }
-
- lastBitmap = filter.__applyFilter(bitmap2, bitmap, bitmap.rect, destPoint);
-
- if (filter.__preserveObject)
- {
- lastBitmap.draw(bitmap3, null,
- displayObject.__objectTransform != null ? displayObject.__objectTransform.__colorTransform : null);
- }
- filter.__renderDirty = false;
-
- if (needSecondBitmapData && lastBitmap == bitmap2)
- {
- cacheBitmap = bitmap;
- bitmap = bitmap2;
- bitmap2 = cacheBitmap;
- }
- }
-
- if (displayObject.__cacheBitmapData != bitmap)
- {
- // TODO: Fix issue with swapping __cacheBitmap.__bitmapData
- // __cacheBitmapData.copyPixels (bitmap, bitmap.rect, destPoint);
-
- // Adding __cacheBitmapRenderer = null; makes this work
- cacheBitmap = displayObject.__cacheBitmapData;
- displayObject.__cacheBitmapData = bitmap;
- displayObject.__cacheBitmapData2 = cacheBitmap;
- displayObject.__cacheBitmap.__bitmapData = displayObject.__cacheBitmapData;
- displayObject.__cacheBitmapRenderer = null;
- }
-
- displayObject.__cacheBitmap.__imageVersion = displayObject.__cacheBitmapData.__textureVersion;
- }
-
- displayObject.__cacheBitmapColorTransform.__copyFrom(colorTransform);
-
- if (!displayObject.__cacheBitmapColorTransform.__isDefault(true))
- {
- displayObject.__cacheBitmapColorTransform.alphaMultiplier = 1;
- displayObject.__cacheBitmapData.colorTransform(displayObject.__cacheBitmapData.rect, displayObject.__cacheBitmapColorTransform);
- }
- }
-
- displayObject.__isCacheBitmapRender = false;
- }
-
- if (updateTransform || needRender)
- {
- Rectangle.__pool.release(rect);
- }
-
- updated = updateTransform;
- }
- else if (displayObject.__cacheBitmap != null)
- {
- if (renderer.__type == DOM)
- {
- var domRenderer:DOMRenderer = cast renderer;
- domRenderer.__renderDrawableClear(displayObject.__cacheBitmap);
- }
-
- displayObject.__cacheBitmap = null;
- displayObject.__cacheBitmapData = null;
- displayObject.__cacheBitmapData2 = null;
- displayObject.__cacheBitmapData3 = null;
- displayObject.__cacheBitmapColorTransform = null;
- displayObject.__cacheBitmapRenderer = null;
-
- updated = true;
- }
-
- ColorTransform.__pool.release(colorTransform);
-
- if (updated && displayObject.__drawableType == TEXT_FIELD)
- {
- var textField:TextField = cast displayObject;
- if (textField.__cacheBitmap != null)
- {
- textField.__cacheBitmap.__renderTransform.tx -= textField.__offsetX;
- textField.__cacheBitmap.__renderTransform.ty -= textField.__offsetY;
- }
- }
-
- return updated;
- #else
- return false;
- #end
- }
-}
-#else
-typedef DisplayObjectRenderer = Dynamic;
-#end
diff --git a/source/openfl/display/GraphicsShader.hx b/source/openfl/display/GraphicsShader.hx
deleted file mode 100644
index 72c0ed9776..0000000000
--- a/source/openfl/display/GraphicsShader.hx
+++ /dev/null
@@ -1,89 +0,0 @@
-package openfl.display;
-
-import openfl.utils.ByteArray;
-
-#if !openfl_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-class GraphicsShader extends Shader
-{
- @:glVertexHeader("attribute float openfl_Alpha;
-attribute vec4 openfl_ColorMultiplier;
-attribute vec4 openfl_ColorOffset;
-attribute vec4 openfl_Position;
-attribute vec2 openfl_TextureCoord;
-
-varying float openfl_Alphav;
-varying vec4 openfl_ColorMultiplierv;
-varying vec4 openfl_ColorOffsetv;
-varying vec2 openfl_TextureCoordv;
-
-uniform mat4 openfl_Matrix;
-uniform bool openfl_HasColorTransform;
-uniform vec2 openfl_TextureSize;")
- @:glVertexBody("openfl_Alphav = openfl_Alpha;
-openfl_TextureCoordv = openfl_TextureCoord;
-
-if (openfl_HasColorTransform) {
- openfl_ColorMultiplierv = openfl_ColorMultiplier;
- openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
-}
-
-gl_Position = openfl_Matrix * openfl_Position;")
- @:glVertexSource("#pragma header
-
-void main(void) {
- #pragma body
-}")
- @:glFragmentHeader("varying float openfl_Alphav;
-varying vec4 openfl_ColorMultiplierv;
-varying vec4 openfl_ColorOffsetv;
-varying vec2 openfl_TextureCoordv;
-
-uniform bool openfl_HasColorTransform;
-uniform vec2 openfl_TextureSize;
-uniform sampler2D bitmap;")
- @:glFragmentBody("vec4 color = texture2D (bitmap, openfl_TextureCoordv);
-
-if (color.a == 0.0) {
- gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
-} else if (openfl_HasColorTransform) {
- color = vec4(color.rgb / color.a, color.a);
-
- mat4 colorMultiplier = mat4(0);
- colorMultiplier[0][0] = openfl_ColorMultiplierv.x;
- colorMultiplier[1][1] = openfl_ColorMultiplierv.y;
- colorMultiplier[2][2] = openfl_ColorMultiplierv.z;
- colorMultiplier[3][3] = 1.0; // openfl_ColorMultiplierv.w;
-
- color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0);
-
- if (color.a > 0.0) {
- gl_FragColor = vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
- } else {
- gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
- }
-} else {
- gl_FragColor = color * openfl_Alphav;
-}")
- #if emscripten
- @:glFragmentSource("#pragma header
-
-void main(void) {
- #pragma body
-
- gl_FragColor = gl_FragColor.bgra;
-}")
- #else
- @:glFragmentSource("#pragma header
-
-void main(void) {
- #pragma body
-}")
- #end
- public function new(code:ByteArray = null)
- {
- super(code);
- }
-}
diff --git a/source/openfl/display3D/Context3D.hx b/source/openfl/display3D/Context3D.hx
deleted file mode 100644
index 8443824dcb..0000000000
--- a/source/openfl/display3D/Context3D.hx
+++ /dev/null
@@ -1,2718 +0,0 @@
-package openfl.display3D;
-
-#if !flash
-import openfl.display3D.utils.UInt8Buff;
-import openfl.display3D._internal.Context3DState;
-import openfl.display3D._internal.GLBuffer;
-import openfl.display3D._internal.GLFramebuffer;
-import openfl.display3D._internal.GLTexture;
-import openfl.display._internal.SamplerState;
-import openfl.display3D.textures.CubeTexture;
-import openfl.display3D.textures.RectangleTexture;
-import openfl.display3D.textures.TextureBase;
-import openfl.display3D.textures.Texture;
-import openfl.display3D.textures.VideoTexture;
-import openfl.display.BitmapData;
-import openfl.display.Stage;
-import openfl.display.Stage3D;
-import openfl.errors.Error;
-import openfl.errors.IllegalOperationError;
-import openfl.events.EventDispatcher;
-import openfl.geom.Matrix3D;
-import openfl.geom.Point;
-import openfl.geom.Rectangle;
-import openfl.utils._internal.Float32Array;
-import openfl.utils._internal.UInt16Array;
-import openfl.utils._internal.UInt8Array;
-import openfl.utils.AGALMiniAssembler;
-import openfl.utils.ByteArray;
-#if lime
-import lime.graphics.opengl.GL;
-import lime.graphics.Image;
-import lime.graphics.ImageBuffer;
-import lime.graphics.RenderContext;
-import lime.graphics.WebGLRenderContext;
-import lime.math.Rectangle as LimeRectangle;
-import lime.math.Vector2;
-#end
-
-/**
- The Context3D class provides a context for rendering geometrically defined graphics.
- A rendering context includes a drawing surface and its associated resources and
- state. When possible, the rendering context uses the hardware graphics processing
- unit (GPU). Otherwise, the rendering context uses software. (If rendering through
- Context3D is not supported on a platform, the stage3Ds property of the Stage object
- contains an empty list.)
-
- The Context3D rendering context is a programmable pipeline that is very similar to
- OpenGL ES 2, but is abstracted so that it is compatible with a range of hardware and
- GPU interfaces. Although designed for 3D graphics, the rendering pipeline does not
- mandate that the rendering is three dimensional. Thus, you can create a 2D renderer
- by supplying the appropriate vertex and pixel fragment programs. In both the 3D and
- 2D cases, the only geometric primitive supported is the triangle.
-
- Get an instance of the Context3D class by calling the requestContext3D() method of a
- Stage3D object. A limited number of Context3D objects can exist per stage; one for
- each Stage3D in the Stage.stage3Ds list. When the context is created, the Stage3D
- object dispatches a context3DCreate event. A rendering context can be destroyed and
- recreated at any time, such as when another application that uses the GPU gains
- focus. Your code should anticipate receiving multiple context3DCreate events.
- Position the rendering area on the stage using the x and y properties of the
- associated Stage3D instance.
-
- To render and display a scene (after getting a Context3D object), the following steps
- are typical:
-
- 1. Configure the main display buffer attributes by calling `configureBackBuffer()`.
- 2. Create and initialize your rendering resources, including:
- * Vertex and index buffers defining the scene geometry
- * Vertex and pixel programs (shaders) for rendering the scene
- * Textures
- 3. Render a frame:
- * Set the render state as appropriate for an object or collection of objects in
- the scene.
- * Call the `drawTriangles()` method to render a set of triangles.
- * Change the rendering state for the next group of objects.
- * Call `drawTriangles()` to draw the triangles defining the objects.
- * Repeat until the scene is entirely rendered.
- * Call the `present()` method to display the rendered scene on the stage.
-
- The following limits apply to rendering:
-
- Resource limits:
-
- | Resource | Number allowed | Total memory |
- | --- | --- | --- |
- | Vertex buffers | 4096 | 256 MB |
- | Index buffers | 4096 | 128 MB |
- | Programs | 4096 | 16 MB |
- | Textures | 4096 | 128 MB |
- | Cube textures | 4096 | 256 MB |
-
- AGAL limits: 200 opcodes per program.
-
- Draw call limits: 32,768 `drawTriangles()` calls for each `present()` call.
-
- The following limits apply to textures:
-
- Texture limits for AIR 32 bit:
-
- | Texture | Maximum size | Total GPU memory |
- | --- | --- | --- |
- | Normal Texture (below Baseline extended) | 2048x2048 | 512 MB |
- | Normal Texture (Baseline extended and above) | 4096x4096 | 512 MB |
- | Rectangular Texture (below Baseline extended) | 2048x2048 | 512 MB |
- | Rectangular Texture (Baseline extended and above) | 4096x4096 | 512 MB |
- | Cube Texture | 1024x1024 | 256 MB |
-
- Texture limits for AIR 64 bit (Desktop):
-
- | Texture | Maximum size | Total GPU memory |
- | --- | --- | --- |
- | Normal Texture (below Baseline extended) | 2048x2048 | 512 MB |
- | Normal Texture (Baseline extended to Standard) | 4096x4096 | 512 MB |
- | Normal Texture (Standard extended and above) | 4096x4096 | 2048 MB |
- | Rectangular Texture (below Baseline extended) | 2048x2048 | 512 MB |
- | Rectangular Texture (Baseline extended to Standard) | 4096x4096 | 512 MB |
- | Rectangular Texture (Standard extended and above) | 4096x4096 | 2048 MB |
- | Cube Texture | 1024x1024 | 256 MB |
-
- 512 MB is the absolute limit for textures, including the texture memory required
- for mipmaps. However, for Cube Textures, the memory limit is 256 MB.
-
- You cannot create Context3D objects with the Context3D constructor. It is
- constructed and available as a property of a Stage3D instance. The Context3D class
- can be used on both desktop and mobile platforms, both when running in Flash Player
- and AIR.
-**/
-#if !openfl_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(openfl.display3D._internal.Context3DState)
-@:access(openfl.display3D.textures.CubeTexture)
-@:access(openfl.display3D.textures.RectangleTexture)
-@:access(openfl.display3D.textures.TextureBase)
-@:access(openfl.display3D.textures.Texture)
-@:access(openfl.display3D.textures.VideoTexture)
-@:access(openfl.display3D.IndexBuffer3D)
-@:access(openfl.display3D.Program3D)
-@:access(openfl.display3D.VertexBuffer3D)
-@:access(openfl.display.BitmapData)
-@:access(openfl.display.Bitmap)
-@:access(openfl.display.DisplayObjectRenderer)
-@:access(openfl.display.Shader)
-@:access(openfl.display.Stage)
-@:access(openfl.display.Stage3D)
-@:access(openfl.geom.Point)
-@:access(openfl.geom.Rectangle)
-@:final class Context3D extends EventDispatcher
-{
- /**
- Indicates if Context3D supports video texture.
- **/
- public static var supportsVideoTexture(default, null):Bool = #if (js && html5) true #else false #end;
-
- /**
- Specifies the height of the back buffer, which can be changed by a successful
- call to the `configureBackBuffer()` method. The height may be modified when the
- browser zoom factor changes if the `wantsBestResolutionOnBrowserZoom` parameter
- is set to `true` in the last successful call to the `configureBackBuffer()`
- method. The change in height can be detected by registering an event listener
- for the browser zoom change event.
- **/
- public var backBufferHeight(default, null):Int = 0;
-
- /**
- Specifies the width of the back buffer, which can be changed by a successful
- call to the `configureBackBuffer()` method. The width may be modified when the
- browser zoom factor changes if the `wantsBestResolutionOnBrowserZoom` parameter
- is set to `true` in the last successful call to the `configureBackBuffer()`
- method. The change in width can be detected by registering an event listener
- for the browser zoom change event.
- **/
- public var backBufferWidth(default, null):Int = 0;
-
- /**
- The type of graphics library driver used by this rendering context. Indicates
- whether the rendering is using software, a DirectX driver, or an OpenGL driver.
- Also indicates whether hardware rendering failed. If hardware rendering fails,
- Flash Player uses software rendering for Stage3D and `driverInfo` contains one
- of the following values:
-
- * "Software Hw_disabled=userDisabled" - The Enable hardware acceleration
- checkbox in the Adobe Flash Player Settings UI is not selected.
- * "Software Hw_disabled=oldDriver" - There are known problems with the
- hardware graphics driver. Updating the graphics driver may fix this problem.
- * "Software Hw_disabled=unavailable" - Known problems with the hardware
- graphics driver or hardware graphics initialization failure.
- * "Software Hw_disabled=explicit" - The content explicitly requested software
- rendering through requestContext3D.
- * "Software Hw_disabled=domainMemory" - The content uses domainMemory, which
- requires a license when used with Stage3D hardware rendering. Visit
- adobe.com/go/fpl.
- **/
- public var driverInfo(default, null):String = "OpenGL (Direct blitting)";
-
- /**
- Specifies whether errors encountered by the renderer are reported to the
- application.
-
- When `enableErrorChecking` is `true`, the `clear()`, and `drawTriangles()`
- methods are synchronous and can throw errors. When `enableErrorChecking`
- is `false`, the default, the `clear()`, and `drawTriangles()` methods are
- asynchronous and errors are not reported. Enabling error checking reduces
- rendering performance. You should only enable error checking when debugging.
- **/
- public var enableErrorChecking(get, set):Bool;
-
- /**
- Specifies the maximum height of the back buffer. The inital value is the system
- limit in the platform. The property can be set to a value smaller than or equal
- to, but not greater than, the system limit. The property can be set to a value
- greater than or equal to, but not smaller than, the minimum limit. The minimum
- limit is a constant value, 32, when the back buffer is not configured. The
- minimum limit will be the value of the `height` parameter in the last successful
- call to the `configureBackBuffer()` method after the back buffer is configured.
- **/
- public var maxBackBufferHeight(default, null):Int;
-
- /**
- Specifies the maximum width of the back buffer. The inital value is the system
- limit in the platform. The property can be set to a value smaller than or equal
- to, but not greater than, the system limit. The property can be set to a value
- greater than or equal to, but not smaller than, the minimum limit. The minimum
- limit is a constant value, 32, when the back buffer is not configured. The
- minimum limit will be the value of the width parameter in the last successful
- call to the `configureBackBuffer()` method after the back buffer is configured.
- **/
- public var maxBackBufferWidth(default, null):Int;
-
- /**
- The feature-support profile in use by this Context3D object.
- **/
- public var profile(default, null):Context3DProfile = STANDARD;
-
- /**
- Returns the total GPU memory allocated by Stage3D data structures of an
- application.
-
- Whenever a GPU resource object is created, memory utilized is stored in
- Context3D. This memory includes index buffers, vertex buffers,
- textures (excluding video texture), and programs that were created through this
- Context3D.
-
- API totalGPUMemory returns the total memory consumed by the above resources to
- the user. Default value returned is 0. The total GPU memory returned is in bytes.
- The information is only provided in Direct mode on mobile, and in Direct and
- GPU modes on desktop. (On desktop, using `gpu` will
- fall back to `direct`)
-
- This API can be used when the SWF version is 32 or later.
- **/
- public var totalGPUMemory(get, never):Int;
-
- @:noCompletion private static var __driverInfo:String;
- @:noCompletion private static var __glDepthStencil:Int = -1;
- @:noCompletion private static var __glMaxTextureMaxAnisotropy:Int = -1;
- @:noCompletion private static var __glMaxViewportDims:Int = -1;
- @:noCompletion private static var __glMemoryCurrentAvailable:Int = -1;
- @:noCompletion private static var __glMemoryTotalAvailable:Int = -1;
- @:noCompletion private static var __glTextureMaxAnisotropy:Int = -1;
-
- @:noCompletion private var gl:#if lime WebGLRenderContext #else Dynamic #end;
- @:noCompletion private var __backBufferAntiAlias:Int;
- @:noCompletion private var __backBufferTexture:RectangleTexture;
- @:noCompletion private var __backBufferWantsBestResolution:Bool;
- @:noCompletion private var __backBufferWantsBestResolutionOnBrowserZoom:Bool;
- @:noCompletion private var __cleared:Bool;
- @:noCompletion private var __context:#if lime RenderContext #else Dynamic #end;
- @:noCompletion private var __contextState:Context3DState;
- @:noCompletion private var __renderStage3DProgram:Program3D;
- @:noCompletion private var __enableErrorChecking:Bool;
- @:noCompletion private var __fragmentConstants:Float32Array;
- @:noCompletion private var __frontBufferTexture:RectangleTexture;
- @:noCompletion private var __positionScale:Float32Array; // TODO: Better approach?
- @:noCompletion private var __present:Bool;
- @:noCompletion private var __programs:Map;
- @:noCompletion private var __quadIndexBuffer:IndexBuffer3D;
- @:noCompletion private var __quadIndexBufferCount:Int;
- @:noCompletion private var __quadIndexBufferElements:Int;
- @:noCompletion private var __stage:Stage;
- @:noCompletion private var __stage3D:Stage3D;
- @:noCompletion private var __state:Context3DState;
- @:noCompletion private var __vertexConstants:Float32Array;
-
- @:noCompletion private function new(stage:Stage, contextState:Context3DState = null, stage3D:Stage3D = null)
- {
- super();
-
- __stage = stage;
- __contextState = contextState;
- __stage3D = stage3D;
-
- __context = stage.window.context;
- #if (js && html5 && dom)
- gl = GL.context;
- #else
- gl = __context.webgl;
- #end
-
- if (__contextState == null) __contextState = new Context3DState();
- __state = new Context3DState();
-
- #if lime
- __vertexConstants = new Float32Array(4 * 128);
- __fragmentConstants = new Float32Array(4 * 128);
- __positionScale = new Float32Array([1.0, 1.0, 1.0, 1.0]);
- #end
- __programs = new Map();
-
- if (__glMaxViewportDims == -1)
- {
- #if (js && html5)
- __glMaxViewportDims = gl.getParameter(gl.MAX_VIEWPORT_DIMS);
- #else
- __glMaxViewportDims = 16384;
- #end
- }
-
- maxBackBufferWidth = __glMaxViewportDims;
- maxBackBufferHeight = __glMaxViewportDims;
-
- if (__glMaxTextureMaxAnisotropy == -1)
- {
- var extension:Dynamic = gl.getExtension("EXT_texture_filter_anisotropic");
-
- #if (js && html5)
- if (extension == null
- || !Reflect.hasField(extension, "MAX_TEXTURE_MAX_ANISOTROPY_EXT")) extension = gl.getExtension("MOZ_EXT_texture_filter_anisotropic");
- if (extension == null
- || !Reflect.hasField(extension, "MAX_TEXTURE_MAX_ANISOTROPY_EXT")) extension = gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic");
- #end
-
- if (extension != null)
- {
- __glTextureMaxAnisotropy = extension.TEXTURE_MAX_ANISOTROPY_EXT;
- __glMaxTextureMaxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
- }
- else
- {
- __glTextureMaxAnisotropy = 0;
- __glMaxTextureMaxAnisotropy = 0;
- }
- }
-
- #if lime
- if (__glDepthStencil == -1)
- {
- #if (js && html5)
- __glDepthStencil = gl.DEPTH_STENCIL;
- #else
- if (__context.type == OPENGLES && Std.parseFloat(__context.version) >= 3)
- {
- __glDepthStencil = __context.gles3.DEPTH24_STENCIL8;
- }
- else
- {
- var extension = gl.getExtension("OES_packed_depth_stencil");
- if (extension != null)
- {
- __glDepthStencil = extension.DEPTH24_STENCIL8_OES;
- }
- else
- {
- extension = gl.getExtension("EXT_packed_depth_stencil");
- if (extension != null)
- {
- __glDepthStencil = extension.DEPTH24_STENCIL8_EXT;
- }
- else
- {
- __glDepthStencil = 0;
- }
- }
- }
- #end
- }
-
- if (__glMemoryTotalAvailable == -1)
- {
- var extension = gl.getExtension("NVX_gpu_memory_info");
- if (extension != null)
- {
- __glMemoryTotalAvailable = extension.GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX;
- __glMemoryCurrentAvailable = extension.GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX;
- }
- }
- #end
-
- if (__driverInfo == null)
- {
- var vendor = gl.getParameter(gl.VENDOR);
- var version = gl.getParameter(gl.VERSION);
- var renderer = gl.getParameter(gl.RENDERER);
- var glslVersion = gl.getParameter(gl.SHADING_LANGUAGE_VERSION);
-
- __driverInfo = "OpenGL Vendor=" + vendor + " Version=" + version + " Renderer=" + renderer + " GLSL=" + glslVersion;
- }
-
- driverInfo = __driverInfo;
-
- __quadIndexBufferElements = Math.floor(0xFFFF / 4);
- __quadIndexBufferCount = __quadIndexBufferElements * 6;
-
- #if lime
- var data = new UInt16Array(__quadIndexBufferCount);
-
- var index:UInt = 0;
- var vertex:UInt = 0;
-
- for (i in 0...__quadIndexBufferElements)
- {
- data[index] = vertex;
- data[index + 1] = vertex + 1;
- data[index + 2] = vertex + 2;
- data[index + 3] = vertex + 2;
- data[index + 4] = vertex + 1;
- data[index + 5] = vertex + 3;
-
- index += 6;
- vertex += 4;
- }
-
- __quadIndexBuffer = createIndexBuffer(__quadIndexBufferCount);
- __quadIndexBuffer.uploadFromTypedArray(data);
- #end
- }
-
- /**
- Clears the color, depth, and stencil buffers associated with this Context3D
- object and fills them with the specified values.
-
- Set the `mask` parameter to specify which buffers to clear. Use the constants
- defined in the Context3DClearMask class to set the `mask` parameter. Use the
- bitwise OR operator, "|", to add multiple buffers to the mask (or use
- Context3DClearMask.ALL). When rendering to the back buffer, the
- `configureBackBuffer()` method must be called before any `clear()` calls.
-
- **Note:** If you specify a parameter value outside the allowed range, Numeric
- parameter values are silently clamped to the range zero to one. Likewise, if
- stencil is greater than 0xff it is set to 0xff.
-
- @param red the red component of the color with which to clear the color buffer,
- in the range zero to one.
- @param green the green component of the color with which to clear the color
- buffer, in the range zero to one.
- @param blue the blue component of the color with which to clear the color
- buffer, in the range zero to one.
- @param alpha the alpha component of the color with which to clear the color
- buffer, in the range zero to one. The alpha component is not used for blending.
- It is written to the buffer alpha directly.
- @param depth the value with which to clear the depth buffer, in the range
- zero to one.
- @param stencil the 8-bit value with which to clear the stencil buffer, in a
- range of 0x00 to 0xff.
- @param mask specifies which buffers to clear.
- @throws Error Object Disposed: If this Context3D object has been disposed by a calling
- dispose() or because the underlying rendering hardware has been lost.
- @throws Error 3768: The Stage3D API may not be used during background execution.
- **/
- public function clear(red:Float = 0, green:Float = 0, blue:Float = 0, alpha:Float = 1, depth:Float = 1, stencil:UInt = 0,
- mask:UInt = Context3DClearMask.ALL):Void
- {
- __flushGLFramebuffer();
- __flushGLViewport();
-
- var clearMask = 0;
-
- if (mask & Context3DClearMask.COLOR != 0)
- {
- if (__state.renderToTexture == null)
- {
- if (__stage.context3D == this && !__stage.__renderer.__cleared) __stage.__renderer.__cleared = true;
- __cleared = true;
- }
-
- clearMask |= gl.COLOR_BUFFER_BIT;
-
- if (#if openfl_disable_context_cache true #else __contextState.colorMaskRed != true
- || __contextState.colorMaskGreen != true
- || __contextState.colorMaskBlue != true
- || __contextState.colorMaskAlpha != true #end)
- {
- gl.colorMask(true, true, true, true);
- __contextState.colorMaskRed = true;
- __contextState.colorMaskGreen = true;
- __contextState.colorMaskBlue = true;
- __contextState.colorMaskAlpha = true;
- }
-
- gl.clearColor(red, green, blue, alpha);
- }
-
- if (mask & Context3DClearMask.DEPTH != 0)
- {
- clearMask |= gl.DEPTH_BUFFER_BIT;
-
- if (#if openfl_disable_context_cache true #else __contextState.depthMask != true #end)
- {
- gl.depthMask(true);
- __contextState.depthMask = true;
- }
-
- gl.clearDepth(depth);
- }
-
- if (mask & Context3DClearMask.STENCIL != 0)
- {
- clearMask |= gl.STENCIL_BUFFER_BIT;
-
- if (#if openfl_disable_context_cache true #else __contextState.stencilWriteMask != 0xFF #end)
- {
- gl.stencilMask(0xFF);
- __contextState.stencilWriteMask = 0xFF;
- }
-
- gl.clearStencil(stencil);
- __contextState.stencilWriteMask = 0xFF;
- }
-
- if (clearMask == 0) return;
-
- __setGLScissorTest(false);
- gl.clear(clearMask);
- }
-
- /**
- Sets the viewport dimensions and other attributes of the rendering buffer.
-
- Rendering is double-buffered. The back buffer is swapped with the visible,
- front buffer when the `present()` method is called. The minimum size of the
- buffer is 32x32 pixels. The maximum size of the back buffer is limited by the
- device capabilities and can also be set by the user through the properties
- `maxBackBufferWidth` and `maxBackBufferHeight`. Configuring the buffer is a
- slow operation. Avoid changing the buffer size or attributes during normal
- rendering operations.
-
- @param width width in pixels of the buffer.
- @param height height in pixels of the buffer.
- @param antiAlias an integer value specifying the requested antialiasing
- quality. The value correlates to the number of subsamples used when
- antialiasing. Using more subsamples requires more calculations to be performed,
- although the relative performance impact depends on the specific rendering
- hardware. The type of antialiasing and whether antialiasing is performed at all is
- dependent on the device and rendering mode. Antialiasing is not supported at all by
- the software rendering context.
- | --- | --- |
- | 0 | No antialiasing |
- | 2 | Minimal antialiasing |
- | 4 | High-quality antialiasing |
- | 16 | Very high-quality antialiasing |
- @param enableDepthAndStencil `false` indicates no depth or stencil buffer is
- created, `true` creates a depth and a stencil buffer. For an AIR 3.2 or later
- application compiled with SWF version 15 or higher, if the `renderMode` element in
- the application descriptor file is `direct`, then the `depthAndStencil` element in
- the application descriptor file must have the same value as this argument. By
- default, the value of the `depthAndStencil` element is `false`.
- @param wantsBestResolution `true` indicates that if the device supports HiDPI
- screens it will attempt to allocate a larger back buffer than indicated with the
- `width` and `height` parameters. Since this add more pixels and potentially changes
- the result of shader operations this is turned off by default. Use
- `Stage.contentsScaleFactor` to determine by how much the native back buffer was
- scaled up.
- @param wantsBestResolutionOnBrowserZoom `true` indicates that the size of the
- back buffer should increase in proportion to the increase in the browser zoom
- factor. The setting of this value is persistent across multiple browser zooms.
- The default value of the parameter is `false`. Set `maxBackBufferWidth` and
- `maxBackBufferHeight` properties to limit the back buffer size increase. Use
- `backBufferWidth` and `backBufferHeight` to determine the current size of the
- back buffer.
- @throws Error Object Disposed: if this Context3D object has been disposed by a
- calling `dispose()` or because the underlying rendering hardware has been lost.
- @throws Error Bad Input Size: The `width` or `height` parameter is either less
- than the minimum back buffer allowed size or greater than the maximum back buffer
- size allowed.
- @throws Error 3709: The `depthAndStencil` flag in the application descriptor
- must match the `enableDepthAndStencil` Boolean passed to `configureBackBuffer()`
- on the Context3D object.
- **/
- public function configureBackBuffer(width:Int, height:Int, antiAlias:Int, enableDepthAndStencil:Bool = true, wantsBestResolution:Bool = false,
- wantsBestResolutionOnBrowserZoom:Bool = false):Void
- {
- #if !openfl_dpi_aware
- if (wantsBestResolution)
- {
- width = Std.int(width * __stage.window.scale);
- height = Std.int(height * __stage.window.scale);
- }
- #end
-
- if (__stage3D == null)
- {
- backBufferWidth = width;
- backBufferHeight = height;
-
- __backBufferAntiAlias = antiAlias;
- __state.backBufferEnableDepthAndStencil = enableDepthAndStencil;
- __backBufferWantsBestResolution = wantsBestResolution;
- __backBufferWantsBestResolutionOnBrowserZoom = wantsBestResolutionOnBrowserZoom;
- }
- else
- {
- if (__backBufferTexture == null || backBufferWidth != width || backBufferHeight != height)
- {
- if (__backBufferTexture != null) __backBufferTexture.dispose();
- if (__frontBufferTexture != null) __frontBufferTexture.dispose();
-
- __backBufferTexture = createRectangleTexture(width, height, BGRA, true);
- __frontBufferTexture = createRectangleTexture(width, height, BGRA, true);
-
- if (__stage3D.__vertexBuffer == null)
- {
- __stage3D.__vertexBuffer = createVertexBuffer(4, 5);
- }
-
- #if openfl_dpi_aware
- var scaledWidth = width;
- var scaledHeight = height;
- #else
- var scaledWidth = wantsBestResolution ? width : Std.int(width * __stage.window.scale);
- var scaledHeight = wantsBestResolution ? height : Std.int(height * __stage.window.scale);
- #end
- var vertexData = new Vector([
- scaledWidth, scaledHeight, 0, 1, 1, 0, scaledHeight, 0, 0, 1, scaledWidth, 0, 0, 1, 0, 0, 0, 0, 0, 0.0
- ]);
-
- __stage3D.__vertexBuffer.uploadFromVector(vertexData, 0, 20);
-
- if (__stage3D.__indexBuffer == null)
- {
- __stage3D.__indexBuffer = createIndexBuffer(6);
-
- var indexData = new Vector([0, 1, 2, 2, 1, 3]);
-
- __stage3D.__indexBuffer.uploadFromVector(indexData, 0, 6);
- }
- }
-
- backBufferWidth = width;
- backBufferHeight = height;
-
- __backBufferAntiAlias = antiAlias;
- __state.backBufferEnableDepthAndStencil = enableDepthAndStencil;
- __backBufferWantsBestResolution = wantsBestResolution;
- __backBufferWantsBestResolutionOnBrowserZoom = wantsBestResolutionOnBrowserZoom;
- __state.__primaryGLFramebuffer = __backBufferTexture.__getGLFramebuffer(enableDepthAndStencil, antiAlias, 0);
- __frontBufferTexture.__getGLFramebuffer(enableDepthAndStencil, antiAlias, 0);
- }
- }
-
- /**
- Creates a CubeTexture object.
-
- Use a CubeTexture object to upload cube texture bitmaps to the rendering context
- and to reference a cube texture during rendering. A cube texture consists of six
- equal-sized, square textures arranged in a cubic topology and are useful for
- describing environment maps.
-
- You cannot create CubeTexture objects with a CubeTexture constructor; use this
- method instead. After creating a CubeTexture object, upload the texture bitmap
- data using the CubeTexture `uploadFromBitmapData()`, `uploadFromByteArray()`, or
- `uploadCompressedTextureFromByteArray()` methods.
-
- @param size The texture edge length in texels.
- @param format The texel format, of the Context3DTextureFormat enumerated list.
- Texture compression lets you store texture images in compressed format directly on
- the GPU, which saves GPU memory and memory bandwidth. Typically, compressed
- textures are compressed offline and uploaded to the GPU in compressed form
- using the `Texture.uploadCompressedTextureFromByteArray` method. Flash Player 11.4
- and AIR 3.4 on desktop platforms added support for runtime texture compression,
- which may be useful in certain situations, such as when rendering dynamic
- textures from vector art. Note that this feature is not currently available on
- mobile platforms and an ArgumentError (Texture Format Mismatch) will be thrown
- instead. To use runtime texture compression, perform the following steps:
- 1. Create the texture object by calling the `Context3D.createCubeTexture()`
- method, passing either `openfl.display3D.Context3DTextureFormat.COMPRESSED` or
- `openfl.display3D.Context3DTextureFormat.COMPRESSED_ALPHA` as the format
- parameter.
- 2. Using the openfl.display3D.textures.Texture instance returned by
- `createCubeTexture()`, call either
- `openfl.display3D.textures.CubeTexture.uploadFromBitmapData()` or
- `openfl.display3D.textures.CubeTexture.uploadFromByteArray()` to upload and
- compress the texture in one step.
- @param optimizeForRenderToTexture Set to true if the texture is likely to be
- used as a render target.
- @param streamingLevels The MIP map level that must be loaded before the image
- is rendered. Texture streaming offers the ability to load and display the
- smallest mip levels first, progressively displaying higher quality images as the
- textures are loaded. End users can view lower-quality images in an application
- while the higher quality images load.
-
- By default, streamingLevels is 0, meaning that the highest quality image in the
- MIP map must be loaded before the image is rendered. This parameter was added in
- Flash Player 11.3 and AIR 3.3. Using the default value maintains the behavior of
- the previous versions of Flash Player and AIR.
-
- Set streamingLevels to a value between 1 and the number of images in the MIP map
- to enable texture streaming. For example, you have a MIP map that includes at the
- highest quality a main image at 64x64 pixels. Lower quality images in the MIP map
- are 32x32, 16x16, 8x8, 4x4, 2x2, and 1x1 pixels, for 7 images in total, or 7
- levels. Level 0 is the highest quality image. The maximum value of this property
- is log2(min(width,height)). Therefore, for a main image that is 64x64 pixels, the
- maximum value of streamingLevels is 7. Set this property to 3 to render the image
- after the 8x8 pixel image loads.
-
- **Note:** Setting this property to a value > 0 can impact memory usage and
- performance.
-
- @return A new CubeTexture object
- @throws Error Object Disposed: if this Context3D object has been disposed by a
- calling `dispose()` or because the underlying rendering hardware has been lost.
- @throws Error Resource Limit Exceeded: if too many Texture objects are created
- or the amount of memory allocated to textures is exceeded.
- @throws ArgumentError Depth Texture Not Implemented: if you attempt to create
- a depth texture.
- @throws ArgumentError Texture Size Is Zero: if the size parameter is not greater
- than zero.
- @throws ArgumentError Texture Not Power Of Two: if the size parameter is not a
- power of two.
- @throws ArgumentError Texture Too Big: if the size parameter is greater than
- 1024.
- @throws Error Texture Creation Failed: if the CubeTexture object could not be
- created by the rendering context (but information about the reason is not
- available).
- @throws ArgumentError Invalid streaming level: if streamingLevels is greater or
- equal to `log2(size)`.
- **/
- public function createCubeTexture(size:Int, format:Context3DTextureFormat, optimizeForRenderToTexture:Bool, streamingLevels:Int = 0):CubeTexture
- {
- return new CubeTexture(this, size, format, optimizeForRenderToTexture, streamingLevels);
- }
-
- /**
- Creates an IndexBuffer3D object.
-
- Use an IndexBuffer3D object to upload a set of triangle indices to the rendering
- context and to reference that set of indices for rendering. Each index in the
- index buffer references a corresponding vertex in a vertex buffer. Each set of
- three indices identifies a triangle. Pass the IndexBuffer3D object to the
- `drawTriangles()` method to render one or more triangles defined in the index
- buffer.
-
- You cannot create IndexBuffer3D objects with the IndexBuffer3D class constructor;
- use this method instead. After creating a IndexBuffer3D object, upload the
- indices using the IndexBuffer3D `uploadFromVector()` or `uploadFromByteArray()`
- methods.
-
- @param numIndices the number of vertices to be stored in the buffer.
- @param bufferUsage the expected buffer usage. Use one of the constants defined
- in Context3DBufferUsage. The hardware driver can do appropriate optimization
- when you set it correctly. This parameter is only available after Flash 12/AIR 4.
- @return A new IndexBuffer3D object
- @throws Error Object Disposed: if this Context3D object has been disposed by a
- calling `dispose()` or because the underlying rendering hardware has been lost.
- @throws Error Resource Limit Exceeded: if too many index buffers are created
- or the amount of memory allocated to index buffers is exceeded.
- @throws Error 3768: The Stage3D API may not be used during background execution.
- @throws ArgumentError Buffer Too Big: when `numIndices` is greater than or equal
- to 0xf0000.
- **/
- public function createIndexBuffer(numIndices:Int, bufferUsage:Context3DBufferUsage = STATIC_DRAW):IndexBuffer3D
- {
- return new IndexBuffer3D(this, numIndices, bufferUsage);
- }
-
- /**
- Creates a Program3D object.
-
- Use a Program3D object to upload shader programs to the rendering context and
- to reference uploaded programs during rendering. A Program3D object stores
- two programs, a vertex program and a fragment program (also known as a pixel
- program). The programs are written in a binary shader assembly language.
-
- You cannot create Program3D objects with a Program3D constructor; use this method
- instead. After creating a Program3D object, upload the programs using the
- Program3D `upload()` method.
-
- @param format (Experimental) Set the format of this Program3D instance to AGAL
- (default) or to GLSL for use on GL-based renderers
- @return A new Program3D object
- @throws Error Object Disposed: if this Context3D object has been disposed by a
- calling `dispose()` or because the underlying rendering hardware has been lost.
- @throws Error The number of programs exceeds 4096 or the total memory size
- exceeds 16MB (use dispose to free Program3D resources).
- **/
- public function createProgram(format:Context3DProgramFormat = AGAL):Program3D
- {
- return new Program3D(this, format);
- }
-
- /**
- Creates a Rectangle Texture object.
-
- Use a RectangleTexture object to upload texture bitmaps to the rendering context
- and to reference a texture during rendering.
-
- You cannot create RectangleTexture objects with a RectangleTexture constructor;
- use this method instead. After creating a RectangleTexture object, upload the
- texture bitmaps using the Texture `uploadFromBitmapData()` or
- `uploadFromByteArray()` methods.
-
- Note that 32-bit integer textures are stored in a packed BGRA format to match
- the OpenFL BitmapData format. Floating point textures use a conventional RGBA
- format.
-
- Rectangle textures are different from regular 2D textures in that their width and
- height do not have to be powers of two. Also, they do not contain mip maps.
- They are most useful for use in render to texture cases. If a rectangle texture
- is used with a sampler that uses mip map filtering or repeat wrapping the
- drawTriangles call will fail. Rectangle texture also do not allow streaming. The
- only texture formats supported by Rectangle textures are BGRA, BGR_PACKED,
- BGRA_PACKED. The compressed texture formats are not supported by Rectangle
- Textures.
-
- @param width The texture width in texels.
- @param height The texture height in texels.
- @param format The texel format, of the Context3DTextureFormat enumerated list.
- @param optimizeForRenderToTexture Set to true if the texture is likely to be
- used as a render target.
- @return A new RectangleTexture object
- @throws Error Object Disposed: if this Context3D object has been disposed by a
- calling dispose() or because the underlying rendering hardware has been lost.
- @throws Error Resource Limit Exceeded: if too many Texture objects are created
- or the amount of memory allocated to textures is exceeded.
- @throws ArgumentError Texture Size Is Zero: if both the width or height
- parameters are not greater than zero.
- @throws ArgumentError Texture Too Big: if either the width or the height
- parameter is greater than 2048.
- @throws Error Texture Creation Failed: if the Texture object could not be
- created by the rendering context (but information about the reason is not
- available).
- @throws Error Requires Baseline Profile Or Above: if rectangular texture is
- created with baseline constrained profile.
- **/
- public function createRectangleTexture(width:Int, height:Int, format:Context3DTextureFormat, optimizeForRenderToTexture:Bool):RectangleTexture
- {
- return new RectangleTexture(this, width, height, format, optimizeForRenderToTexture);
- }
-
- /**
- Creates a Texture object.
-
- Use a Texture object to upload texture bitmaps to the rendering context and to
- reference a texture during rendering.
-
- You cannot create Texture objects with a Texture constructor; use this method
- instead. After creating a Texture object, upload the texture bitmaps using the
- Texture `uploadFromBitmapData()`, `uploadFromByteArray()`, or
- `uploadCompressedTextureFromByteArray()` methods.
-
- Note that 32-bit integer textures are stored in a packed BGRA format to match
- the OpenFL BitmapData format. Floating point textures use a conventional RGBA
- format.
-
- @param width The texture width in texels.
- @param height The texture height in texels.
- @param format The texel format, of the Context3DTextureFormat enumerated list.
- Texture compression lets you store texture images in compressed format directly
- on the GPU, which saves GPU memory and memory bandwidth. Typically, compressed
- textures are compressed offline and uploaded to the GPU in compressed form using
- the Texture.uploadCompressedTextureFromByteArray method. Flash Player 11.4 and
- AIR 3.4 on desktop platforms added support for runtime texture compression, which
- may be useful in certain situations, such as when rendering dynamic textures from
- vector art. Note that this feature is not currently available on mobile platforms
- and an ArgumentError (Texture Format Mismatch) will be thrown instead. To use
- runtime texture compression, perform the following steps:
- 1. Create the texture object by calling the `Context3D.createTexture()` method,
- passing either `openfl.display3D.Context3DTextureFormat.COMPRESSED` or
- `openfl.display3D.Context3DTextureFormat.COMPRESSED_ALPHA` as the format
- parameter.
- 2. Using the openfl.display3D.textures.Texture instance returned by
- `createTexture()`, call either
- `openfl.display3D.textures.Texture.uploadFromBitmapData()` or
- `openfl.display3D.textures.Texture.uploadFromByteArray()` to upload and compress
- the texture in one step.
- @param optimizeForRenderToTexture Set to true if the texture is likely to be
- used as a render target.
- @param streamingLevels The MIP map level that must be loaded before the image is
- rendered. Texture streaming offers the ability to load and display the smallest
- mip levels first, progressively displaying higher quality images as the textures
- are loaded. End users can view lower-quality images in an application while the
- higher quality images load.
-
- By default, streamingLevels is 0, meaning that the highest quality image in the
- MIP map must be loaded before the image is rendered. This parameter was added in
- Flash Player 11.3 and AIR 3.3. Using the default value maintains the behavior of
- the previous versions of Flash Player and AIR.
-
- Set `streamingLevels` to a value between 1 and the number of images in the MIP map
- to enable texture streaming. For example, you have a MIP map that includes at
- the highest quality a main image at 64x64 pixels. Lower quality images in the
- MIP map are 32x32, 16x16, 8x8, 4x4, 2x2, and 1x1 pixels, for 7 images in total,
- or 7 levels. Level 0 is the highest quality image. The maximum value of this
- property is log2(min(width,height)). Therefore, for a main image that is
- 64x64 pixels, the maximum value of streamingLevels is 7. Set this property to
- 3 to render the image after the 8x8 pixel image loads.
-
- **Note:** Setting this property to a value > 0 can impact memory usage and
- performance.
-
- @return A new Texture object
- @throws Error Object Disposed: if this Context3D object has been disposed by a calling dispose() or because the underlying rendering hardware has been lost.
- @throws Error Resource Limit Exceeded: if too many Texture objects are created or the amount of memory allocated to textures is exceeded.
- @throws ArgumentError Depth Texture Not Implemented: if you attempt to create a depth texture.
- @throws ArgumentError Texture Size Is Zero: if both the width or height parameters are not greater than zero.
- @throws ArgumentError Texture Not Power Of Two: if both the width and height parameters are not a power of two.
- @throws ArgumentError Texture Too Big: if either the width or the height parameter is greater than 2048 for baseline and baseline constrained profile or if either the width or the height parameter is greater than 4096 for profile baseline extended and above.
- @throws Error Texture Creation Failed: if the Texture object could not be created by the rendering context (but information about the reason is not available).
- @throws ArgumentError Invalid streaming level: if streamingLevels is greater or equal to log2(min(width,height)).
- **/
- public function createTexture(width:Int, height:Int, format:Context3DTextureFormat, optimizeForRenderToTexture:Bool, streamingLevels:Int = 0):Texture
- {
- return new Texture(this, width, height, format, optimizeForRenderToTexture, streamingLevels);
- }
-
- /**
- Creates a VertexBuffer3D object.
-
- Use a VertexBuffer3D object to upload a set of vertex data to the rendering
- context. A vertex buffer contains the data needed to render each point in the
- scene geometry. The data attributes associated with each vertex typically
- includes position, color, and texture coordinates and serve as the input to
- the vertex shader program. Identify the data values that correspond to one of
- the inputs of the vertex program using the `setVertexBufferAt()` method. You can
- specify up to sixty-four 32-bit values for each vertex.
-
- You cannot create VertexBuffer3D objects with a VertexBuffer3D constructor; use
- this method instead. After creating a VertexBuffer3D object, upload the vertex
- data using the VertexBuffer3D `uploadFromVector()` or `uploadFromByteArray()`
- methods.
-
- @param numVertices the number of vertices to be stored in the buffer. The
- maximum number of vertices in a single buffer is 65535.
- @param data32PerVertex the number of 32-bit(4-byte) data values associated
- with each vertex. The maximum number of 32-bit data elements per vertex is 64
- (or 256 bytes). Note that only eight attribute registers are accessible by a
- vertex shader program at any given time. Use `setVertextBufferAt()` to select
- attributes from within a vertex buffer.
- @param bufferUsage the expected buffer usage. Use one of the constants defined
- in Context3DBufferUsage. The hardware driver can do appropriate optimization when
- you set it correctly. This parameter is only available after Flash 12/AIR 4
- @return A new VertexBuffer3D object
- @throws Error Object Disposed: if this Context3D object has been disposed by a
- calling `dispose()` or because the underlying rendering hardware has been lost.
- @throws Error Resource Limit Exceeded: if too many vertex buffer objects are
- created or the amount of memory alloted to vertex buffers is exceeded.
- @throws ArgumentError Buffer Too Big: when `numVertices` is greater than 0x10000
- or `data32PerVertex` is greater than 64.
- @throws ArgumentError Buffer Has Zero Size: when `numVertices` is zero or
- `data32PerVertex` is zero.
- @throws ArgumentError Buffer Creation Failed: if the VertexBuffer3D object
- could not be created by the rendering context (but additional information about
- the reason is not available).
- @throws Error 3768: The Stage3D API may not be used during background execution.
- **/
- public function createVertexBuffer(numVertices:Int, data32PerVertex:Int, bufferUsage:Context3DBufferUsage = STATIC_DRAW):VertexBuffer3D
- {
- return new VertexBuffer3D(this, numVertices, data32PerVertex, bufferUsage);
- }
-
- /**
- Creates a VideoTexture object.
-
- Use a VideoTexture object to obtain video frames as texture from NetStream object
- or Camera object and to upload the video frames to the rendering context.
-
- The VideoTexture object cannot be created with the VideoTexture constructor; use
- this method instead. After creating a VideoTexture object, attach NetStream
- object or Camera Object to get the video frames with the VideoTexture
- `attachNetStream()` or `attachCamera()` methods.
-
- Note that this method returns null if the system doesn't support this feature.
-
- VideoTexture does not contain mipmaps. If VideoTexture is used with a sampler
- that uses mip map filtering or repeat wrapping, the drawTriangles call will fail.
- VideoTexture can be treated as BGRA texture by the shaders. The attempt to
- instantiate the VideoTexture Object will fail if the Context3D was requested
- with sotfware rendering mode.
-
- A maximum of 4 VideoTexture objects are available per Context3D instance. On
- mobile the actual number of supported VideoTexture objects may be less than 4
- due to platform limitations.
-
- @return A new VideoTexture object
- @throws Error Object Disposed: if this Context3D object has been disposed by a
- calling `dispose()` or because the underlying rendering hardware has been lost.
- @throws Error Resource Limit Exceeded: if too many Texture objects are created
- or the amount of memory allocated to textures is exceeded.
- @throws Error Texture Creation Failed: if the Texture object could not be
- created by the rendering context (but information about the reason is not
- available).
- **/
- public function createVideoTexture():VideoTexture
- {
- #if (js && html5)
- return new VideoTexture(this);
- #else
- throw new Error("Video textures are not supported on this platform");
- return null;
- #end
- }
-
- /**
- Frees all resources and internal storage associated with this Context3D.
-
- All index buffers, vertex buffers, textures, and programs that were created
- through this Context3D are disposed just as if calling `dispose()` on each of
- them individually. In addition, the Context3D itself is disposed freeing all
- temporary buffers and the back buffer. If you call `configureBackBuffer()`,
- `clear()`, `drawTriangles()`, `createCubeTexture()`, `createTexture()`,
- `createProgram()`, `createIndexBuffer()`, `createVertexBuffer()`, or
- `drawToBitmapData()` after calling `dispose()`, the runtime throws an exception.
-
- Warning: calling `dispose()` on a Context3D while there is still a event
- listener for `Events.CONTEXT3D_CREATE` set on the asociated Stage3D object the
- `dispose()` call will simulate a device loss. It will create a new Context3D on
- the Stage3D and issue the `Events.CONTEXT3D_CREATE` event again. If this is not
- desired remove the event listener from the Stage3D object before calling
- `dispose()` or set the `recreate` parameter to `false`.
-
- @param recreate Whether to allow this Stage3D object to create itself again
- **/
- public function dispose(recreate:Bool = true):Void
- {
- // TODO: Dispose all related buffers
-
- gl = null;
- __dispose();
- }
-
- /**
- Draws the current render buffer to a bitmap.
-
- The current contents of the back render buffer are copied to a BitmapData
- object. This is potentially a very slow operation that can take up to a second.
- Use with care. Note that this function does not copy the front render buffer
- (the one shown on stage), but the buffer being drawn to. To capture the rendered
- image as it appears on the stage, call `drawToBitmapData()` immediately before you
- calling `present()`.
-
- Beginning with AIR 25, two new parameters have been introduced in the API
- `drawToBitmapData()`. This API now takes three parameters. The first one is the
- existing parameter `destination:BitmapData`. The second parameter is
- `srcRect:Rectangle`, which is target rectangle on Stage3D. The third parameter is
- `destPoint:Point`, which is the coordinate on the destination bitmap. The
- parameters `srcRect` and `destPoint` are optional and default to
- `(0,0,bitmapWidth,bitmapHeight)` and `(0,0)`, respectively.
-
- When the image is drawn, it is not scaled to fit the bitmap. Instead, the
- contents are clipped to the size of the destination bitmap.
-
- OpenFL BitmapData objects store colors already multiplied by the alpha component.
- For example, if the "pure" rgb color components of a pixel are (0x0A, 0x12, 0xBB)
- and the alpha component is 0x7F (.5), then the pixel is stored in the
- BitmapData object with the rgba values: (0x05, 0x09, 0x5D, 0x7F). You can set the
- blend factors so that the colors rendered to the buffer are multiplied by alpha
- or perform the operation in the fragment shader. The rendering context does not
- validate that the colors are stored in premultiplied format.
-
- @param destination The target BitmapData for this drawing operation
- @param srcRect The source rectangle in the current Stage3D context
- @param destPoint A destination point to write to in the target BitmapData
- @throws Error Object Disposed: if this Context3D object has been disposed by
- a calling `dispose()` or because the underlying rendering hardware has been lost.
- @throws Error 3768: The Stage3D API may not be used during background execution.
- @throws Error 3802: If either one of the parameters `destPoint:Point` or
- `srcRect:Rectangle` is outside the bitmap/stage3D coordinate bound, or if
- non-numeric(NaN) values are passed as input.
- **/
- public function drawToBitmapData(destination:BitmapData, srcRect:Rectangle = null, destPoint:Point = null):Void
- {
- #if lime
- if (destination == null) return;
-
- var sourceRect = srcRect != null ? srcRect.__toLimeRectangle() : new LimeRectangle(0, 0, backBufferWidth, backBufferHeight);
- var destVector = destPoint != null ? destPoint.__toLimeVector2() : new Vector2();
-
- if (__stage.context3D == this)
- {
- if (__stage.window != null)
- {
- if (__stage3D != null)
- {
- destVector.setTo(Std.int(-__stage3D.x), Std.int(-__stage3D.y));
- }
-
- var image = __stage.window.readPixels();
- destination.image.copyPixels(image, sourceRect, destVector);
- }
- }
- else if (__backBufferTexture != null)
- {
- var cacheRenderToTexture = __state.renderToTexture;
- setRenderToBackBuffer();
-
- __flushGLFramebuffer();
- __flushGLViewport();
- //! EDITED BY NE_EO TO REDUCE GARBAGE MEMORY
- var buffer = UInt8Buff.get(backBufferWidth * backBufferHeight * 4); // new UInt8Array(backBufferWidth * backBufferHeight * 4);
- var data = buffer.buffer;
- gl.readPixels(0, 0, backBufferWidth, backBufferHeight, __backBufferTexture.__format, gl.UNSIGNED_BYTE, data);
-
- var image = new Image(new ImageBuffer(data, backBufferWidth, backBufferHeight, 32, BGRA32));
- destination.image.copyPixels(image, sourceRect, destVector);
-
- if (cacheRenderToTexture != null)
- {
- setRenderToTexture(cacheRenderToTexture, __state.renderToTextureDepthStencil, __state.renderToTextureAntiAlias,
- __state.renderToTextureSurfaceSelector);
- }
-
- buffer.put();
- }
- #end
- }
-
- /**
- Render the specified triangles using the current buffers and state of this
- Context3D object.
-
- For each triangle, the triangle vertices are processed by the vertex shader
- program and the triangle surface is processed by the pixel shader program. The
- output color from the pixel program for each pixel is drawn to the render
- target depending on the stencil operations, depth test, source and destination
- alpha, and the current blend mode. The render destination can be the main render
- buffer or a texture.
-
- If culling is enabled, (with the `setCulling()` method), then triangles can be
- discarded from the scene before the pixel program is run. If stencil and depth
- testing are enabled, then output pixels from the pixel program can be discarded
- without updating the render destination. In addition, the pixel program can
- decide not to output a color for a pixel.
-
- The rendered triangles are not displayed in the viewport until you call the
- `present()` method. After each `present()` call, the `clear()` method must be
- called before the first `drawTriangles()` call or rendering fails.
-
- When `enableErrorChecking` is `false`, this function returns immediately, does
- not wait for results, and throws exceptions only if this Context3D instance has
- been disposed or there are too many draw calls. If the rendering context state
- is invalid rendering fails silently. When the `enableErrorChecking` property is
- `true`, this function returns after the triangles are drawn and throws exceptions
- for any drawing errors or invalid context state.
-
- @param indexBuffer:IndexBuffer3D — a set of vertex indices referencing the
- vertices to render.
- @param firstIndex:int (default = 0) — the index of the first vertex index
- selected to render. Default 0.
- @param numTriangles:int (default = -1) — the number of triangles to render.
- Each triangle consumes three indices. Pass -1 to draw all triangles in the index
- buffer. Default -1.
- @throws Error — Object Disposed: if this Context3D object has been disposed by
- a calling `dispose()` or because the underlying rendering hardware has been lost.
- @throws Error — If this method is called too many times between calls to
- `present()`. The maximum number of calls is 32,768.
-
- The following errors are only thrown when `enableErrorChecking` property is true:
- @throws Error Need To Clear Before Draw: If the buffer has not been cleared
- since the last `present()` call.
- @throws Error If a valid Program3D object is not set.
- @throws Error No Valid Index Buffer Set: If an IndexBuffer3D object is not set.
- @throws Error Sanity Check On Parameters Failed: when the number of triangles
- to be drawn or the `firstIndex` exceed allowed values.
- @throws RangeError — Not Enough Indices In This Buffer: when there aren't enough
- indices in the buffer to define the number of triangles to be drawn.
- @throws Error — Sample Binds Texture Also Bound To Render: when the render target
- is a texture and that texture assigned to a texture input of the current fragment
- program.
- @throws Error — Sample Binds Invalid Texture: an invalid texture is specified as
- the input to the current fragment program.
- @throws Error — Sampler Format Does Not Match Texture Format: when the texture
- assigned as the input to the current fragment program has a different format than
- that specified for the sampler register. For example, a 2D texture is assigned to
- a cube texture sampler.
- @throws Error — Sample Binds Undefined Texture: The current fragment program
- accesses a texture register that has not been set (using `setTextureAt()`).
- @throws Error — Same Texture Needs Same Sampler Params: If a texture is used for
- more than one sampler register, all of the samplers must have the same settings.
- For example, you cannot set one sampler to clamp and another to wrap.
- @throws Error — Texture Bound But Not Used: A texture is set as a shader input,
- but it is not used.
- @throws Error — Stream Is Not Used: A vertex buffer is assigned to a vertex
- attribute input, but the vertex program does not reference the corresponding
- register.
- @throws Error — Stream Is Invalid: a VertexBuffer3D object assigned to a vertex
- program input is not a valid object.
- @throws RangeError — Stream Does Not Have Enough Vertices: A vertex buffer
- supplying data for drawing the specified triangles does not have enough data.
- @throws RangeError — Stream Vertex Offset Out Of Bounds: The offset specified in
- a `setVertexBufferAt()` call is negative or past the end of the buffer.
- @throws Error — Stream Read But Not Set: A vertex attribute used by the current
- vertex program is not set (using `setVertexBufferAt()`).
- **/
- public function drawTriangles(indexBuffer:IndexBuffer3D, firstIndex:Int = 0, numTriangles:Int = -1):Void
- {
- #if !openfl_disable_display_render
- if (__state.renderToTexture == null)
- {
- // TODO: Make sure state is correct for this?
- if (__stage.context3D == this && !__stage.__renderer.__cleared)
- {
- __stage.__renderer.__clear();
- }
- else if (!__cleared)
- {
- // TODO: Throw error if error reporting is enabled?
- clear(0, 0, 0, 0, 1, 0, Context3DClearMask.COLOR);
- }
- }
-
- __flushGL();
- #end
-
- if (__state.program != null)
- {
- __state.program.__flush();
- }
-
- var count = (numTriangles == -1) ? indexBuffer.__numIndices : (numTriangles * 3);
-
- __bindGLElementArrayBuffer(indexBuffer.__id);
- gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, firstIndex * 2);
- }
-
- /**
- Displays the back rendering buffer.
-
- Calling the `present()` method makes the results of all rendering operations
- since the last `present()` call visible and starts a new rendering cycle.
- After calling `present`, you must call `clear()` before making another
- `drawTriangles()` call. Otherwise, this function will alternately clear the
- render buffer to yellow and green or, if `enableErrorChecking` has been set to
- `true`, an exception is thrown.
-
- Calling `present()` also resets the render target, just like calling
- `setRenderToBackBuffer()`.
-
- @throws Error Need To Clear Before Draw: If the `clear()` has not been called
- since the previous call to `present()`. (Two consecutive `present()` calls are
- not allowed without calling `clear()` in between.)
- @throws Error 3768: The Stage3D API may not be used during background execution.
- **/
- public function present():Void
- {
- setRenderToBackBuffer();
-
- if (__stage3D != null && __backBufferTexture != null)
- {
- if (!__cleared)
- {
- // Make sure texture is initialized
- // TODO: Throw error if error reporting is enabled?
- clear(0, 0, 0, 0, 1, 0, Context3DClearMask.COLOR);
- }
-
- var cacheBuffer = __backBufferTexture;
- __backBufferTexture = __frontBufferTexture;
- __frontBufferTexture = cacheBuffer;
-
- __state.__primaryGLFramebuffer = __backBufferTexture.__getGLFramebuffer(__state.backBufferEnableDepthAndStencil, __backBufferAntiAlias, 0);
- __cleared = false;
- }
-
- __present = true;
- }
-
- /**
- Specifies the factors used to blend the output color of a drawing operation with
- the existing color.
-
- The output (source) color of the pixel shader program is combined with the
- existing (destination) color at that pixel according to the following formula:
-
- `result color = (source color * sourceFactor) + (destination color * destinationFactor)`
-
- The destination color is the current color in the render buffer for that pixel.
- Thus it is the result of the most recent `clear()` call and any intervening
- `drawTriangles()` calls.
-
- Use `setBlendFactors()` to set the factors used to multiply the source and
- destination colors before they are added together. The default blend factors
- are, `sourceFactor = Context3DBlendFactor.ONE`, and
- `destinationFactor = Context3DBlendFactor.ZERO`, which results in the source
- color overwriting the destination color (in other words, no blending of the
- two colors occurs). For normal alpha blending, use
- `sourceFactor = Context3DBlendFactor.SOURCE_ALPHA` and
- `destinationFactor = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA`.
-
- Use the constants defined in the Context3DBlendFactor class to set the
- parameters of this function.
-
- @param sourceFactor The factor with which to multiply the source color.
- Defaults to `Context3DBlendFactor.ONE`.
- @param destinationFactor The factor with which to multiply the destination
- color. Defaults to `Context3DBlendFactor.ZERO`.
- @throws Error — Invalid Enum: when `sourceFactor` or `destinationFactor` is
- not one of the recognized values, which are defined in the
- Context3DBlendFactor class.
- **/
- public function setBlendFactors(sourceFactor:Context3DBlendFactor, destinationFactor:Context3DBlendFactor):Void
- {
- setBlendFactorsSeparate(sourceFactor, destinationFactor, sourceFactor, destinationFactor);
- }
-
- @:dox(hide) @:noCompletion private function setBlendFactorsSeparate(sourceRGBFactor:Context3DBlendFactor, destinationRGBFactor:Context3DBlendFactor,
- sourceAlphaFactor:Context3DBlendFactor, destinationAlphaFactor:Context3DBlendFactor):Void
- {
- __state.blendSourceRGBFactor = sourceRGBFactor;
- __state.blendDestinationRGBFactor = destinationRGBFactor;
- __state.blendSourceAlphaFactor = sourceAlphaFactor;
- __state.blendDestinationAlphaFactor = destinationAlphaFactor;
-
- // TODO: Better way to handle this?
- __setGLBlendEquation(gl.FUNC_ADD);
- }
-
- /**
- Sets the mask used when writing colors to the render buffer.
-
- Only color components for which the corresponding color mask parameter is `true`
- are updated when a color is written to the render buffer. For example, if
- you call: `setColorMask(true, false, false, false)`, only the red component
- of a color is written to the buffer until you change the color mask again. The
- color mask does not affect the behavior of the `clear()` method.
-
- @param red set false to block changes to the red channel.
- @param green set false to block changes to the green channel.
- @param blue set false to block changes to the blue channel.
- @param alpha set false to block changes to the alpha channel.
- **/
- public function setColorMask(red:Bool, green:Bool, blue:Bool, alpha:Bool):Void
- {
- __state.colorMaskRed = red;
- __state.colorMaskGreen = green;
- __state.colorMaskBlue = blue;
- __state.colorMaskAlpha = alpha;
- }
-
- /**
- Sets triangle culling mode.
-
- Triangles may be excluded from the scene early in the rendering pipeline based
- on their orientation relative to the view plane. Specify vertex order
- consistently (clockwise or counter-clockwise) as seen from the outside of the
- model to cull correctly.
-
- @param triangleFaceToCull the culling mode. Use one of the constants defined
- in the Context3DTriangleFace class.
- @throws Error Invalid Enum Error: when triangleFaceToCull is not one of the
- values defined in the Context3DTriangleFace class.
- **/
- public function setCulling(triangleFaceToCull:Context3DTriangleFace):Void
- {
- __state.culling = triangleFaceToCull;
- }
-
- /**
- Sets type of comparison used for depth testing.
-
- The depth of the source pixel output from the pixel shader program is compared
- to the current value in the depth buffer. If the comparison evaluates as `false`,
- then the source pixel is discarded. If `true`, then the source pixel is processed
- by the next step in the rendering pipeline, the stencil test. In addition, the
- depth buffer is updated with the depth of the source pixel, as long as the
- `depthMask` parameter is set to `true`.
-
- Sets the test used to compare depth values for source and destination pixels.
- The source pixel is composited with the destination pixel when the comparison is
- `true`. The comparison operator is applied as an infix operator between the
- source and destination pixel values, in that order.
-
- @param depthMask the destination depth value will be updated from the source
- pixel when `true`.
- @param passCompareMode the depth comparison test operation. One of the values
- of Context3DCompareMode.
- **/
- public function setDepthTest(depthMask:Bool, passCompareMode:Context3DCompareMode):Void
- {
- __state.depthMask = depthMask;
- __state.depthCompareMode = passCompareMode;
- }
-
- /**
- Sets vertex and fragment shader programs to use for subsequent rendering.
-
- @param program the Program3D object representing the vertex and fragment
- programs to use.
- **/
- public function setProgram(program:Program3D):Void
- {
- __state.program = program;
- __state.shader = null; // TODO: Merge this logic
-
- if (program != null)
- {
- for (i in 0...program.__samplerStates.length)
- {
- if (__state.samplerStates[i] == null)
- {
- __state.samplerStates[i] = program.__samplerStates[i].clone();
- }
- else
- {
- __state.samplerStates[i].copyFrom(program.__samplerStates[i]);
- }
- }
- }
- }
-
- /**
- Set constants for use by shader programs using values stored in a ByteArray.
-
- Sets constants that can be accessed from the vertex or fragment program.
-
- @param programType one of Context3DProgramType.
- @param firstRegister the index of the first shader program constant to set.
- @param numRegisters the number of registers to set. Every register is read
- as four float values.
- @param data the source ByteArray object
- @param byteArrayOffset an offset into the ByteArray for reading
- @throws TypeError kNullPointerError when data is null.
- @throws RangeError kConstantRegisterOutOfBounds when attempting to set more than
- the maximum number of shader constants.
- @throws RangeError kBadInputSize if `byteArrayOffset` is greater than or equal to
- the length of data or no. of elements in `data - byteArrayOffset` is less than
- `numRegisters*16`
- **/
- public function setProgramConstantsFromByteArray(programType:Context3DProgramType, firstRegister:Int, numRegisters:Int, data:ByteArray,
- byteArrayOffset:UInt):Void
- {
- #if lime
- if (numRegisters == 0 || __state.program == null) return;
-
- if (__state.program != null && __state.program.__format == GLSL)
- {
- // TODO
- }
- else
- {
- // TODO: Cleanup?
-
- if (numRegisters == -1)
- {
- numRegisters = ((data.length >> 2) - byteArrayOffset);
- }
-
- var isVertex = (programType == VERTEX);
- var dest = isVertex ? __vertexConstants : __fragmentConstants;
-
- var floatData = Float32Array.fromBytes(data, 0);
- var outOffset = firstRegister * 4;
- var inOffset = Std.int(byteArrayOffset / 4);
-
- for (i in 0...(numRegisters * 4))
- {
- dest[outOffset + i] = floatData[inOffset + i];
- }
-
- if (__state.program != null)
- {
- __state.program.__markDirty(isVertex, firstRegister, numRegisters);
- }
- }
- #end
- }
-
- /**
- Sets constants for use by shader programs using values stored in a Matrix3D.
-
- Use this function to pass a matrix to a shader program. The function sets 4
- constant registers used by the vertex or fragment program. The matrix is
- assigned to registers row by row. The first constant register is assigned the
- top row of the matrix. You can set 128 registers for a vertex program and 28
- for a fragment program.
-
- @param programType The type of shader program, either
- `Context3DProgramType.VERTEX` or `Context3DProgramType.FRAGMENT`.
- @param firstRegister the index of the first constant register to set. Since
- a Matrix3D has 16 values, four registers are set.
- @param matrix the matrix containing the constant values.
- @param transposedMatrix if `true` the matrix entries are copied to registers
- in transposed order. The default value is `false`.
- @throws TypeError Null Pointer Error: when matrix is `null`.
- @throws RangeError Constant Register Out Of Bounds: when attempting to set more
- than the maximum number of shader constant registers.
- **/
- public function setProgramConstantsFromMatrix(programType:Context3DProgramType, firstRegister:Int, matrix:Matrix3D, transposedMatrix:Bool = false):Void
- {
- #if lime
- if (__state.program != null && __state.program.__format == GLSL)
- {
- __flushGLProgram();
-
- // TODO: Cache value, prevent need to copy
- var data = new Float32Array(16);
- for (i in 0...16)
- {
- data[i] = matrix.rawData[i];
- }
-
- gl.uniformMatrix4fv(cast firstRegister, transposedMatrix, data);
- }
- else
- {
- var isVertex = (programType == VERTEX);
- var dest = isVertex ? __vertexConstants : __fragmentConstants;
- var source = matrix.rawData;
- var i = firstRegister * 4;
-
- if (transposedMatrix)
- {
- dest[i++] = source[0];
- dest[i++] = source[4];
- dest[i++] = source[8];
- dest[i++] = source[12];
-
- dest[i++] = source[1];
- dest[i++] = source[5];
- dest[i++] = source[9];
- dest[i++] = source[13];
-
- dest[i++] = source[2];
- dest[i++] = source[6];
- dest[i++] = source[10];
- dest[i++] = source[14];
-
- dest[i++] = source[3];
- dest[i++] = source[7];
- dest[i++] = source[11];
- dest[i++] = source[15];
- }
- else
- {
- dest[i++] = source[0];
- dest[i++] = source[1];
- dest[i++] = source[2];
- dest[i++] = source[3];
-
- dest[i++] = source[4];
- dest[i++] = source[5];
- dest[i++] = source[6];
- dest[i++] = source[7];
-
- dest[i++] = source[8];
- dest[i++] = source[9];
- dest[i++] = source[10];
- dest[i++] = source[11];
-
- dest[i++] = source[12];
- dest[i++] = source[13];
- dest[i++] = source[14];
- dest[i++] = source[15];
- }
-
- if (__state.program != null)
- {
- __state.program.__markDirty(isVertex, firstRegister, 4);
- }
- }
- #end
- }
-
- /**
- Sets the constant inputs for the shader programs.
-
- Sets an array of constants to be accessed by a vertex or fragment shader
- program. Constants set in Program3D are accessed within the shader programs as
- constant registers. Each constant register is comprised of 4 floating point
- values (x, y, z, w). Therefore every register requires 4 entries in the data
- Vector. The number of registers that you can set for vertex program and
- fragment program depends on the Context3DProfile.
-
- @param programType The type of shader program, either
- `Context3DProgramType.VERTEX` or `Context3DProgramType.FRAGMENT`.
- @param firstRegister the index of the first constant register to set.
- @param data the floating point constant values. There must be at least
- `numRegisters` 4 elements in data.
- @param numRegisters the number of constants to set. Specify -1, the default
- value, to set enough registers to use all of the available data.
- @throws TypeError Null Pointer Error: when data is `null`.
- @throws RangeError Constant Register Out Of Bounds: when attempting to set more
- than the maximum number of shader constant registers.
- @throws RangeError Bad Input Size: When the number of elements in data is less
- than `numRegisters*4`
- **/
- public function setProgramConstantsFromVector(programType:Context3DProgramType, firstRegister:Int, data:Vector, numRegisters:Int = -1):Void
- {
- if (numRegisters == 0) return;
-
- if (__state.program != null && __state.program.__format == GLSL) {}
- else
- {
- if (numRegisters == -1)
- {
- numRegisters = (data.length >> 2);
- }
-
- var isVertex = (programType == VERTEX);
- var dest = isVertex ? __vertexConstants : __fragmentConstants;
- var source = data;
-
- var sourceIndex = 0;
- var destIndex = firstRegister * 4;
-
- for (i in 0...numRegisters)
- {
- dest[destIndex++] = source[sourceIndex++];
- dest[destIndex++] = source[sourceIndex++];
- dest[destIndex++] = source[sourceIndex++];
- dest[destIndex++] = source[sourceIndex++];
- }
-
- if (__state.program != null)
- {
- __state.program.__markDirty(isVertex, firstRegister, numRegisters);
- }
- }
- }
-
- /**
- Sets the back rendering buffer as the render target. Subsequent calls to
- `drawTriangles()` and `clear()` methods result in updates to the back buffer.
- Use this method to resume normal rendering after using the
- `setRenderToTexture()` method.
- **/
- public function setRenderToBackBuffer():Void
- {
- __state.renderToTexture = null;
- }
-
- /**
- Sets the specified texture as the rendering target.
-
- Subsequent calls to `drawTriangles()` and `clear()` methods update the
- specified texture instead of the back buffer. Mip maps are created
- automatically. Use the `setRenderToBackBuffer()` to resume normal rendering to
- the back buffer.
-
- No clear is needed before drawing. If there is no clear operation, the render
- content will be retained. depth buffer and stencil buffer will also not be
- cleared. But it is forced to clear when first drawing. Calling `present()`
- resets the target to the back buffer.
-
- @param texture the target texture to render into. Set to `null` to resume
- rendering to the back buffer (`setRenderToBackBuffer()` and `present` also reset
- the target to the back buffer).
- @param enableDepthAndStencil if `true`, depth and stencil testing are
- available. If `false`, all depth and stencil state is ignored for subsequent
- drawing operations.
- @param antiAlias the antialiasing quality. Use 0 to disable antialiasing;
- higher values improve antialiasing quality, but require more calculations. The
- value is currently ignored by mobile platform and software rendering context.
- @param surfaceSelector specifies which element of the texture to update.
- Texture objects have one surface, so you must specify 0, the default value.
- CubeTexture objects have six surfaces, so you can specify an integer from 0
- through 5.
- @param colorOutputIndex The output color register. Must be 0 for constrained
- or baseline mode. Otherwise specifies the output color register.
- @throws ArgumentError for a mismatched surfaceSelector parameter. The value
- must be 0 for 2D textures and 0..5 for cube maps.
- @throws ArgumentError texture is not derived from the TextureBase class
- (either Texture or CubeTexture classes).
- @throws ArgumentError colorOutputIndex must be an integer is from 0 through 3.
- @throws ArgumentError this call requires a Context3D that is created with the
- standard profile or above.
- **/
- public function setRenderToTexture(texture:TextureBase, enableDepthAndStencil:Bool = false, antiAlias:Int = 0, surfaceSelector:Int = 0):Void
- {
- __state.renderToTexture = texture;
- __state.renderToTextureDepthStencil = enableDepthAndStencil;
- __state.renderToTextureAntiAlias = antiAlias;
- __state.renderToTextureSurfaceSelector = surfaceSelector;
- }
-
- /**
- Manually override texture sampler state.
-
- Texture sampling state is typically set at the time setProgram is called.
- However, you can override texture sampler state with this function. If you do not
- want the program to change sampler state, set the `ignoresamnpler` bit in AGAL
- and use this function.
-
- @param sampler sampler The sampler register to use. Maps to the sampler register
- in AGAL.
- @param wrap Wrapping mode. Defined in Context3DWrapMode. The default is repeat.
- @param filter Texture filtering mode. Defined in Context3DTextureFilter. The
- default is nearest.
- @param mipfilter Mip map filter. Defined in Context3DMipFilter. The default
- is none.
- @throws Error sampler out of range
- @throws Error wrap, filter, mipfilter bad enum
- @throws Error Object Disposed: if this Context3D object has been disposed by a
- calling `dispose()` or because the underlying rendering hardware has been lost.
- **/
- public function setSamplerStateAt(sampler:Int, wrap:Context3DWrapMode, filter:Context3DTextureFilter, mipfilter:Context3DMipFilter):Void
- {
- // if (sampler < 0 || sampler > Context3D.MAX_SAMPLERS) {
-
- // throw new Error ("sampler out of range");
-
- // }
-
- if (__state.samplerStates[sampler] == null)
- {
- __state.samplerStates[sampler] = new SamplerState();
- }
-
- var state = __state.samplerStates[sampler];
- state.wrap = wrap;
- state.filter = filter;
- state.mipfilter = mipfilter;
- }
-
- /**
- Sets a scissor rectangle, which is type of drawing mask. The renderer only draws
- to the area inside the scissor rectangle. Scissoring does not affect clear
- operations.
-
- Pass `null` to turn off scissoring.
-
- @param rectangle The rectangle in which to draw. Specify the rectangle
- position and dimensions in pixels. The coordinate system origin is the top left
- corner of the viewport, with positive values increasing down and to the right
- (the same as the normal OpenFL display coordinate system).
- **/
- public function setScissorRectangle(rectangle:Rectangle):Void
- {
- if (rectangle != null)
- {
- __state.scissorEnabled = true;
- __state.scissorRectangle.copyFrom(rectangle);
- }
- else
- {
- __state.scissorEnabled = false;
- }
- }
-
- /**
- Sets stencil mode and operation.
-
- An 8-bit stencil reference value can be associated with each draw call. During
- rendering, the reference value can be tested against values stored previously
- in the frame buffer. The result of the test can control the draw action and
- whether or how the stored stencil value is updated. In addition, depth testing
- controls whether stencil testing is performed. A failed depth test can also be
- used to control the action taken on the stencil buffer.
-
- In the pixel processing pipeline, depth testing is performed first. If the depth
- test fails, a stencil buffer update action can be taken, but no further evaluation
- of the stencil buffer value can be made. If the depth test passes, then the
- stencil test is performed. Alternate actions can be taken depending on the outcome
- of the stencil test.
-
- The stencil reference value is set using `setStencilReferenceValue()`.
-
- @param triangleFace the triangle orientations allowed to contribute to the
- stencil operation. One of Context3DTriangleFace.
- @param compareMode the test operator used to compare the current stencil
- reference value and the destination pixel stencil value. Destination pixel color
- and depth update is performed when the comparison is true. The stencil actions
- are performed as requested in the following action parameters. The comparison
- operator is applied as an infix operator between the current and destination
- reference values, in that order (in pseudocode:
- `if stencilReference OPERATOR stencilBuffer then pass`). Use one of the constants
- defined in the Context3DCompareMode class.
- @param actionOnBothPass action to be taken when both depth and stencil
- comparisons pass. Use one of the constants defined in the Context3DStencilAction
- class.
- @param actionOnDepthFail action to be taken when depth comparison fails. Use
- one of the constants defined in the Context3DStencilAction class.
- @param actionOnDepthPassStencilFail action to be taken when depth comparison
- passes and the stencil comparison fails. Use one of the constants defined in the
- Context3DStencilAction class.
- @throws Error Invalid Enum Error: when `triangleFace` is not one of the values
- defined in the Context3DTriangleFace class.
- @throws Error Invalid Enum Error: when `compareMode` is not one of the values
- defined in the Context3DCompareMode class.
- @throws Error Invalid Enum Error: when `actionOnBothPass`, `actionOnDepthFail`,
- or `actionOnDepthPassStencilFail` is not one of the values defined in the
- Context3DStencilAction class.
- **/
- public function setStencilActions(triangleFace:Context3DTriangleFace = FRONT_AND_BACK, compareMode:Context3DCompareMode = ALWAYS,
- actionOnBothPass:Context3DStencilAction = KEEP, actionOnDepthFail:Context3DStencilAction = KEEP,
- actionOnDepthPassStencilFail:Context3DStencilAction = KEEP):Void
- {
- __state.stencilTriangleFace = triangleFace;
- __state.stencilCompareMode = compareMode;
- __state.stencilPass = actionOnBothPass;
- __state.stencilDepthFail = actionOnDepthFail;
- __state.stencilFail = actionOnDepthPassStencilFail;
- }
-
- /**
- Sets the stencil comparison value used for stencil tests.
-
- Only the lower 8 bits of the reference value are used. The stencil buffer value
- is also 8 bits in length. Use the `readMask` and `writeMask` to use the stencil
- buffer as a bit field.
-
- @param referenceValue an 8-bit reference value used in reference value
- comparison tests.
- @param readMask an 8-bit mask for applied to both the current stencil
- buffer value and the reference value before the comparison.
- @param writeMask an 8-bit mask applied to the reference value before updating
- the stencil buffer.
- **/
- public function setStencilReferenceValue(referenceValue:UInt, readMask:UInt = 0xFF, writeMask:UInt = 0xFF):Void
- {
- __state.stencilReferenceValue = referenceValue;
- __state.stencilReadMask = readMask;
- __state.stencilWriteMask = writeMask;
- }
-
- /**
- Specifies the texture to use for a texture input register of a fragment program.
-
- A fragment program can read information from up to eight texture objects. Use
- this function to assign a Texture or CubeTexture object to one of the sampler
- registers used by the fragment program.
-
- **Note:** if you change the active fragment program (with setProgram) to a
- shader that uses fewer textures, set the unused registers to `null`:
-
- ``haxe
- setTextureAt(7, null);
- ```
-
- @param sampler the sampler register index, a value from 0 through 7.
- @param texture the texture object to make available, either a Texture or a
- CubeTexture instance.
- **/
- public function setTextureAt(sampler:Int, texture:TextureBase):Void
- {
- // if (sampler < 0 || sampler > Context3D.MAX_SAMPLERS) {
-
- // throw new Error ("sampler out of range");
-
- // }
-
- __state.textures[sampler] = texture;
- }
-
- /**
- Specifies which vertex data components correspond to a single vertex shader
- program input.
-
- Use the setVertexBufferAt method to identify which components of the data
- defined for each vertex in a VertexBuffer3D buffer belong to which inputs to the
- vertex program. The developer of the vertex program determines how much data is
- needed per vertex. That data is mapped from 1 or more VertexBuffer3D stream(s) to
- the attribute registers of the vertex shader program.
-
- The smallest unit of data consumed by the vertex shader is a 32-bit data.
- Offsets into the vertex stream are specified in multiples of 32-bits.
-
- As an example, a programmer might define each vertex with the following data:
-
- ```
- position: x float32
- y float32
- z float32
- color: r unsigned byte
- g unsigned byte
- b unsigned byte
- a unsigned byte
- ```
-
- Assuming the vertex was defined in a VertexBuffer3D object named buffer, it
- would be assigned to a vertex shader with the following code:
-
- ```haxe
- setVertexBufferAt(0, buffer, 0, Context3DVertexBufferFormat.FLOAT_3); // attribute #0 will contain the position information
- setVertexBufferAt(1, buffer, 3, Context3DVertexBufferFormat.BYTES_4); // attribute #1 will contain the color information
- ```
-
- @param index the index of the attribute register in the vertex shader (0
- through 7).
- @param buffer the buffer that contains the source vertex data to be fed to the
- vertex shader.
- @param bufferOffset an offset from the start of the data for a single vertex
- at which to start reading this attribute. In the example above, the position data
- has an offset of 0 because it is the first attribute; color has an offset of 3
- because the color attribute follows the three 32-bit position values. The offset
- is specified in units of 32 bits.
- @param format a value from the Context3DVertexBufferFormat class specifying
- the data type of this attribute.
- @throws Error Invalid Enum: when format is not one of the values defined in
- the Context3DVertexBufferFormat class.
- @throws RangeError Attribute Register Out Of Bounds: when the index parameter
- is outside the range from 0 through 7. (A maximum of eight vertex attribute
- registers can be used by a shader.)
- **/
- public function setVertexBufferAt(index:Int, buffer:VertexBuffer3D, bufferOffset:Int = 0, format:Context3DVertexBufferFormat = FLOAT_4):Void
- {
- if (index < 0) return;
-
- if (buffer == null)
- {
- gl.disableVertexAttribArray(index);
- __bindGLArrayBuffer(null);
- return;
- }
-
- __bindGLArrayBuffer(buffer.__id);
- gl.enableVertexAttribArray(index);
-
- var byteOffset = bufferOffset * 4;
-
- switch (format)
- {
- case BYTES_4:
- gl.vertexAttribPointer(index, 4, gl.UNSIGNED_BYTE, true, buffer.__stride, byteOffset);
-
- case FLOAT_4:
- gl.vertexAttribPointer(index, 4, gl.FLOAT, false, buffer.__stride, byteOffset);
-
- case FLOAT_3:
- gl.vertexAttribPointer(index, 3, gl.FLOAT, false, buffer.__stride, byteOffset);
-
- case FLOAT_2:
- gl.vertexAttribPointer(index, 2, gl.FLOAT, false, buffer.__stride, byteOffset);
-
- case FLOAT_1:
- gl.vertexAttribPointer(index, 1, gl.FLOAT, false, buffer.__stride, byteOffset);
-
- default:
- throw new IllegalOperationError();
- }
- }
-
- @:noCompletion private function __bindGLArrayBuffer(buffer:GLBuffer):Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.__currentGLArrayBuffer != buffer #end)
- {
- gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
- __contextState.__currentGLArrayBuffer = buffer;
- }
- }
-
- @:noCompletion private function __bindGLElementArrayBuffer(buffer:GLBuffer):Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.__currentGLElementArrayBuffer != buffer #end)
- {
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
- __contextState.__currentGLElementArrayBuffer = buffer;
- }
- }
-
- @:noCompletion private function __bindGLFramebuffer(framebuffer:GLFramebuffer):Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.__currentGLFramebuffer != framebuffer #end)
- {
- gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
- __contextState.__currentGLFramebuffer = framebuffer;
- }
- }
-
- @:noCompletion private function __bindGLTexture2D(texture:GLTexture):Void
- {
- // TODO: Need to consider activeTexture ID
-
- // if (#if openfl_disable_context_cache true #else __contextState.__currentGLTexture2D != texture #end) {
-
- gl.bindTexture(gl.TEXTURE_2D, texture);
- __contextState.__currentGLTexture2D = texture;
-
- // }
- }
-
- @:noCompletion private function __bindGLTextureCubeMap(texture:GLTexture):Void
- {
- // TODO: Need to consider activeTexture ID
-
- // if (#if openfl_disable_context_cache true #else __contextState.__currentGLTextureCubeMap != texture #end) {
-
- gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
- __contextState.__currentGLTextureCubeMap = texture;
-
- // }
- }
-
- @:noCompletion private function __dispose():Void
- {
- driverInfo += " (Disposed)";
-
- if (__stage3D != null)
- {
- __stage3D.__indexBuffer = null;
- __stage3D.__vertexBuffer = null;
- __stage3D.context3D = null;
- __stage3D = null;
- }
-
- __backBufferTexture = null;
- __context = null;
- __renderStage3DProgram = null;
- __fragmentConstants = null;
- __frontBufferTexture = null;
- __positionScale = null;
- __present = false;
- __quadIndexBuffer = null;
- __stage = null;
- __vertexConstants = null;
- }
-
- @:noCompletion private function __drawTriangles(firstIndex:Int = 0, count:Int):Void
- {
- #if !openfl_disable_display_render
- if (__state.renderToTexture == null)
- {
- // TODO: Make sure state is correct for this?
- if (__stage.context3D == this && !__stage.__renderer.__cleared)
- {
- __stage.__renderer.__clear();
- }
- else if (!__cleared)
- {
- // TODO: Throw error if error reporting is enabled?
- clear(0, 0, 0, 0, 1, 0, Context3DClearMask.COLOR);
- }
- }
-
- __flushGL();
- #end
-
- if (__state.program != null)
- {
- __state.program.__flush();
- }
-
- gl.drawArrays(gl.TRIANGLES, firstIndex, count);
- }
-
- @:noCompletion private function __flushGL():Void
- {
- __flushGLProgram();
- __flushGLFramebuffer();
- __flushGLViewport();
-
- __flushGLBlend();
- __flushGLColor();
- __flushGLCulling();
- __flushGLDepth();
- __flushGLScissor();
- __flushGLStencil();
- __flushGLTextures();
- }
-
- @:noCompletion private function __flushGLBlend():Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.blendDestinationRGBFactor != __state.blendDestinationRGBFactor
- || __contextState.blendSourceRGBFactor != __state.blendSourceRGBFactor
- || __contextState.blendDestinationAlphaFactor != __state.blendDestinationAlphaFactor
- || __contextState.blendSourceAlphaFactor != __state.blendSourceAlphaFactor #end)
- {
- __setGLBlend(true);
-
- if (__state.blendDestinationRGBFactor == __state.blendDestinationAlphaFactor
- && __state.blendSourceRGBFactor == __state.blendSourceAlphaFactor)
- {
- gl.blendFunc(__getGLBlend(__state.blendSourceRGBFactor), __getGLBlend(__state.blendDestinationRGBFactor));
- }
- else
- {
- gl.blendFuncSeparate(__getGLBlend(__state.blendSourceRGBFactor), __getGLBlend(__state.blendDestinationRGBFactor),
- __getGLBlend(__state.blendSourceAlphaFactor), __getGLBlend(__state.blendDestinationAlphaFactor));
- }
-
- __contextState.blendDestinationRGBFactor = __state.blendDestinationRGBFactor;
- __contextState.blendSourceRGBFactor = __state.blendSourceRGBFactor;
- __contextState.blendDestinationAlphaFactor = __state.blendDestinationAlphaFactor;
- __contextState.blendSourceAlphaFactor = __state.blendSourceAlphaFactor;
- }
- }
-
- @:noCompletion private inline function __flushGLColor():Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.colorMaskRed != __state.colorMaskRed
- || __contextState.colorMaskGreen != __state.colorMaskGreen
- || __contextState.colorMaskBlue != __state.colorMaskBlue
- || __contextState.colorMaskAlpha != __state.colorMaskAlpha #end)
- {
- gl.colorMask(__state.colorMaskRed, __state.colorMaskGreen, __state.colorMaskBlue, __state.colorMaskAlpha);
- __contextState.colorMaskRed = __state.colorMaskRed;
- __contextState.colorMaskGreen = __state.colorMaskGreen;
- __contextState.colorMaskBlue = __state.colorMaskBlue;
- __contextState.colorMaskAlpha = __state.colorMaskAlpha;
- }
- }
-
- @:noCompletion private function __flushGLCulling():Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.culling != __state.culling #end)
- {
- if (__state.culling == NONE)
- {
- __setGLCullFace(false);
- }
- else
- {
- __setGLCullFace(true);
-
- switch (__state.culling)
- {
- case NONE: // skip
- case BACK:
- gl.cullFace(gl.BACK);
- case FRONT:
- gl.cullFace(gl.FRONT);
- case FRONT_AND_BACK:
- gl.cullFace(gl.FRONT_AND_BACK);
- default:
- throw new IllegalOperationError();
- }
- }
-
- __contextState.culling = __state.culling;
- }
- }
-
- @:noCompletion private function __flushGLDepth():Void
- {
- var depthMask = (__state.depthMask
- && (__state.renderToTexture != null ? __state.renderToTextureDepthStencil : __state.backBufferEnableDepthAndStencil));
-
- if (#if openfl_disable_context_cache true #else __contextState.depthMask != depthMask #end)
- {
- gl.depthMask(depthMask);
- __contextState.depthMask = depthMask;
- }
-
- if (#if openfl_disable_context_cache true #else __contextState.depthCompareMode != __state.depthCompareMode #end)
- {
- switch (__state.depthCompareMode)
- {
- case ALWAYS:
- gl.depthFunc(gl.ALWAYS);
- case EQUAL:
- gl.depthFunc(gl.EQUAL);
- case GREATER:
- gl.depthFunc(gl.GREATER);
- case GREATER_EQUAL:
- gl.depthFunc(gl.GEQUAL);
- case LESS:
- gl.depthFunc(gl.LESS);
- case LESS_EQUAL:
- gl.depthFunc(gl.LEQUAL);
- case NEVER:
- gl.depthFunc(gl.NEVER);
- case NOT_EQUAL:
- gl.depthFunc(gl.NOTEQUAL);
- default:
- throw new IllegalOperationError();
- }
-
- __contextState.depthCompareMode = __state.depthCompareMode;
- }
- }
-
- @:noCompletion private function __flushGLFramebuffer():Void
- {
- if (__state.renderToTexture != null)
- {
- if (#if openfl_disable_context_cache true #else __contextState.renderToTexture != __state.renderToTexture
- || __contextState.renderToTextureSurfaceSelector != __state.renderToTextureSurfaceSelector #end)
- {
- var framebuffer = __state.renderToTexture.__getGLFramebuffer(__state.renderToTextureDepthStencil, __state.renderToTextureAntiAlias,
- __state.renderToTextureSurfaceSelector);
- __bindGLFramebuffer(framebuffer);
-
- __contextState.renderToTexture = __state.renderToTexture;
- __contextState.renderToTextureAntiAlias = __state.renderToTextureAntiAlias;
- __contextState.renderToTextureDepthStencil = __state.renderToTextureDepthStencil;
- __contextState.renderToTextureSurfaceSelector = __state.renderToTextureSurfaceSelector;
- }
-
- __setGLDepthTest(__state.renderToTextureDepthStencil);
- __setGLStencilTest(__state.renderToTextureDepthStencil);
-
- __setGLFrontFace(true);
- }
- else
- {
- if (__stage == null && backBufferWidth == 0 && backBufferHeight == 0)
- {
- throw new Error("Context3D backbuffer has not been configured");
- }
-
- if (#if openfl_disable_context_cache true #else __contextState.renderToTexture != null
- || __contextState.__currentGLFramebuffer != __state.__primaryGLFramebuffer
- || __contextState.backBufferEnableDepthAndStencil != __state.backBufferEnableDepthAndStencil #end
- )
- {
- __bindGLFramebuffer(__state.__primaryGLFramebuffer);
-
- __contextState.renderToTexture = null;
- __contextState.backBufferEnableDepthAndStencil = __state.backBufferEnableDepthAndStencil;
- }
-
- __setGLDepthTest(__state.backBufferEnableDepthAndStencil);
- __setGLStencilTest(__state.backBufferEnableDepthAndStencil);
-
- __setGLFrontFace(__stage.context3D != this);
- }
- }
-
- @:noCompletion private function __flushGLProgram():Void
- {
- var shader = __state.shader;
- var program = __state.program;
-
- if (#if openfl_disable_context_cache true #else __contextState.shader != shader #end)
- {
- // TODO: Merge this logic
-
- if (__contextState.shader != null)
- {
- __contextState.shader.__disable();
- }
-
- if (shader != null)
- {
- shader.__enable();
- }
-
- __contextState.shader = shader;
- }
-
- if (#if openfl_disable_context_cache true #else __contextState.program != program #end)
- {
- if (__contextState.program != null)
- {
- __contextState.program.__disable();
- }
-
- if (program != null)
- {
- program.__enable();
- }
-
- __contextState.program = program;
- }
-
- if (program != null && program.__format == AGAL)
- {
- __positionScale[1] = (__stage.context3D == this && __state.renderToTexture == null) ? 1.0 : -1.0;
- program.__setPositionScale(__positionScale);
- }
- }
-
- @:noCompletion private function __flushGLScissor():Void
- {
- if (!__state.scissorEnabled)
- {
- if (#if openfl_disable_context_cache true #else __contextState.scissorEnabled != __state.scissorEnabled #end)
- {
- __setGLScissorTest(false);
- __contextState.scissorEnabled = false;
- }
- }
- else
- {
- __setGLScissorTest(true);
- __contextState.scissorEnabled = true;
-
- var scissorX = Std.int(__state.scissorRectangle.x);
- var scissorY = Std.int(__state.scissorRectangle.y);
- var scissorWidth = Std.int(__state.scissorRectangle.width);
- var scissorHeight = Std.int(__state.scissorRectangle.height);
- #if !openfl_dpi_aware
- if (__backBufferWantsBestResolution)
- {
- scissorX = Std.int(__state.scissorRectangle.x * __stage.window.scale);
- scissorY = Std.int(__state.scissorRectangle.y * __stage.window.scale);
- scissorWidth = Std.int(__state.scissorRectangle.width * __stage.window.scale);
- scissorHeight = Std.int(__state.scissorRectangle.height * __stage.window.scale);
- }
- #end
-
- if (__state.renderToTexture == null && __stage3D == null)
- {
- var contextHeight = Std.int(__stage.window.height * __stage.window.scale);
- scissorY = contextHeight - scissorHeight - scissorY;
- }
-
- if (#if openfl_disable_context_cache true #else __contextState.scissorRectangle.x != scissorX
- || __contextState.scissorRectangle.y != scissorY
- || __contextState.scissorRectangle.width != scissorWidth
- || __contextState.scissorRectangle.height != scissorHeight #end)
- {
- gl.scissor(scissorX, scissorY, scissorWidth, scissorHeight);
- __contextState.scissorRectangle.setTo(scissorX, scissorY, scissorWidth, scissorHeight);
- }
- }
- }
-
- @:noCompletion private function __flushGLStencil():Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.stencilTriangleFace != __state.stencilTriangleFace
- || __contextState.stencilPass != __state.stencilPass
- || __contextState.stencilDepthFail != __state.stencilDepthFail
- || __contextState.stencilFail != __state.stencilFail #end)
- {
- gl.stencilOpSeparate(__getGLTriangleFace(__state.stencilTriangleFace), __getGLStencilAction(__state.stencilFail),
- __getGLStencilAction(__state.stencilDepthFail), __getGLStencilAction(__state.stencilPass));
- __contextState.stencilTriangleFace = __state.stencilTriangleFace;
- __contextState.stencilPass = __state.stencilPass;
- __contextState.stencilDepthFail = __state.stencilDepthFail;
- __contextState.stencilFail = __state.stencilFail;
- }
-
- if (#if openfl_disable_context_cache true #else __contextState.stencilWriteMask != __state.stencilWriteMask #end)
- {
- gl.stencilMask(__state.stencilWriteMask);
- __contextState.stencilWriteMask = __state.stencilWriteMask;
- }
-
- if (#if openfl_disable_context_cache true #else __contextState.stencilCompareMode != __state.stencilCompareMode
- || __contextState.stencilReferenceValue != __state.stencilReferenceValue
- || __contextState.stencilReadMask != __state.stencilReadMask #end
- )
- {
- gl.stencilFunc(__getGLCompareMode(__state.stencilCompareMode), __state.stencilReferenceValue, __state.stencilReadMask);
- __contextState.stencilCompareMode = __state.stencilCompareMode;
- __contextState.stencilReferenceValue = __state.stencilReferenceValue;
- __contextState.stencilReadMask = __state.stencilReadMask;
- }
- }
-
- @:noCompletion private function __flushGLTextures():Void
- {
- var sampler = 0;
- var texture, samplerState;
-
- for (i in 0...__state.textures.length)
- {
- texture = __state.textures[i];
- samplerState = __state.samplerStates[i];
- if (samplerState == null)
- {
- __state.samplerStates[i] = new SamplerState();
- samplerState = __state.samplerStates[i];
- }
-
- gl.activeTexture(gl.TEXTURE0 + sampler);
-
- if (texture != null)
- {
- // if (#if openfl_disable_context_cache true #else texture != __contextState.textures[i] #end) {
-
- // TODO: Cleaner approach?
- if (texture.__textureTarget == gl.TEXTURE_2D)
- {
- __bindGLTexture2D(texture.__getTexture());
- }
- else
- {
- __bindGLTextureCubeMap(texture.__getTexture());
- }
-
- #if (desktop && !html5)
- // TODO: Cache?
- gl.enable(gl.TEXTURE_2D);
- #end
-
- __contextState.textures[i] = texture;
-
- // }
-
- texture.__setSamplerState(samplerState);
- }
- else
- {
- __bindGLTexture2D(null);
- }
-
- if (__state.program != null && __state.program.__format == AGAL && samplerState.textureAlpha)
- {
- gl.activeTexture(gl.TEXTURE0 + sampler + 4);
-
- if (texture != null && texture.__alphaTexture != null)
- {
- if (texture.__alphaTexture.__textureTarget == gl.TEXTURE_2D)
- {
- __bindGLTexture2D(texture.__alphaTexture.__getTexture());
- }
- else
- {
- __bindGLTextureCubeMap(texture.__alphaTexture.__getTexture());
- }
-
- texture.__alphaTexture.__setSamplerState(samplerState);
- gl.uniform1i(__state.program.__agalAlphaSamplerEnabled[sampler].location, 1);
-
- #if (desktop && !html5)
- // TODO: Cache?
- gl.enable(gl.TEXTURE_2D);
- #end
- }
- else
- {
- __bindGLTexture2D(null);
- if (__state.program.__agalAlphaSamplerEnabled[sampler] != null)
- {
- gl.uniform1i(__state.program.__agalAlphaSamplerEnabled[sampler].location, 0);
- }
- }
- }
-
- sampler++;
- }
- }
-
- @:noCompletion private function __flushGLViewport():Void
- {
- // TODO: Cache
-
- if (__state.renderToTexture == null)
- {
- if (__stage.context3D == this)
- {
- var scaledBackBufferWidth = backBufferWidth;
- var scaledBackBufferHeight = backBufferHeight;
- #if !openfl_dpi_aware
- if (__stage3D == null && !__backBufferWantsBestResolution)
- {
- scaledBackBufferWidth = Std.int(backBufferWidth * __stage.window.scale);
- scaledBackBufferHeight = Std.int(backBufferHeight * __stage.window.scale);
- }
- #end
- var x = __stage3D == null ? 0 : Std.int(__stage3D.x);
- var y = Std.int((__stage.window.height * __stage.window.scale) - scaledBackBufferHeight - (__stage3D == null ? 0 : __stage3D.y));
- gl.viewport(x, y, scaledBackBufferWidth, scaledBackBufferHeight);
- }
- else
- {
- gl.viewport(0, 0, backBufferWidth, backBufferHeight);
- }
- }
- else
- {
- var width = 0, height = 0;
-
- // TODO: Avoid use of Std.is
- if ((__state.renderToTexture is Texture))
- {
- var texture2D:Texture = cast __state.renderToTexture;
- width = texture2D.__width;
- height = texture2D.__height;
- }
- else if ((__state.renderToTexture is RectangleTexture))
- {
- var rectTexture:RectangleTexture = cast __state.renderToTexture;
- width = rectTexture.__width;
- height = rectTexture.__height;
- }
- else if ((__state.renderToTexture is CubeTexture))
- {
- var cubeTexture:CubeTexture = cast __state.renderToTexture;
- width = cubeTexture.__size;
- height = cubeTexture.__size;
- }
-
- gl.viewport(0, 0, width, height);
- }
- }
-
- @:noCompletion private function __getGLBlend(blendFactor:Context3DBlendFactor):Int
- {
- switch (blendFactor)
- {
- case DESTINATION_ALPHA:
- return gl.DST_ALPHA;
- case DESTINATION_COLOR:
- return gl.DST_COLOR;
- case ONE:
- return gl.ONE;
- case ONE_MINUS_DESTINATION_ALPHA:
- return gl.ONE_MINUS_DST_ALPHA;
- case ONE_MINUS_DESTINATION_COLOR:
- return gl.ONE_MINUS_DST_COLOR;
- case ONE_MINUS_SOURCE_ALPHA:
- return gl.ONE_MINUS_SRC_ALPHA;
- case ONE_MINUS_SOURCE_COLOR:
- return gl.ONE_MINUS_SRC_COLOR;
- case SOURCE_ALPHA:
- return gl.SRC_ALPHA;
- case SOURCE_COLOR:
- return gl.SRC_COLOR;
- case ZERO:
- return gl.ZERO;
- default:
- throw new IllegalOperationError();
- }
-
- return 0;
- }
-
- @:noCompletion private function __getGLCompareMode(mode:Context3DCompareMode):Int
- {
- return switch (mode)
- {
- case ALWAYS: gl.ALWAYS;
- case EQUAL: gl.EQUAL;
- case GREATER: gl.GREATER;
- case GREATER_EQUAL: gl.GEQUAL;
- case LESS: gl.LESS;
- case LESS_EQUAL: gl.LEQUAL; // TODO : wrong value
- case NEVER: gl.NEVER;
- case NOT_EQUAL: gl.NOTEQUAL;
- default: gl.EQUAL;
- }
- }
-
- @:noCompletion private function __getGLStencilAction(action:Context3DStencilAction):Int
- {
- return switch (action)
- {
- case DECREMENT_SATURATE: gl.DECR;
- case DECREMENT_WRAP: gl.DECR_WRAP;
- case INCREMENT_SATURATE: gl.INCR;
- case INCREMENT_WRAP: gl.INCR_WRAP;
- case INVERT: gl.INVERT;
- case KEEP: gl.KEEP;
- case SET: gl.REPLACE;
- case ZERO: gl.ZERO;
- default: gl.KEEP;
- }
- }
-
- @:noCompletion private function __getGLTriangleFace(face:Context3DTriangleFace):Int
- {
- return switch (face)
- {
- case FRONT: gl.FRONT;
- case BACK: gl.BACK;
- case FRONT_AND_BACK: gl.FRONT_AND_BACK;
- case NONE: gl.NONE;
- default: gl.FRONT_AND_BACK;
- }
- }
-
- @:noCompletion private function __renderStage3D(stage3D:Stage3D):Void
- {
- // Assume this is the primary Context3D
-
- var context = stage3D.context3D;
-
- if (context != null
- && context != this
- && context.__frontBufferTexture != null
- && stage3D.visible
- && backBufferHeight > 0
- && backBufferWidth > 0)
- {
- // if (!__stage.__renderer.__cleared) __stage.__renderer.__clear ();
-
- if (__renderStage3DProgram == null)
- {
- var vertexAssembler = new AGALMiniAssembler();
- vertexAssembler.assemble(Context3DProgramType.VERTEX, "m44 op, va0, vc0\n" + "mov v0, va1");
-
- var fragmentAssembler = new AGALMiniAssembler();
- fragmentAssembler.assemble(Context3DProgramType.FRAGMENT, "tex ft1, v0, fs0 <2d,nearest,nomip>\n" + "mov oc, ft1");
-
- __renderStage3DProgram = createProgram();
- __renderStage3DProgram.upload(vertexAssembler.agalcode, fragmentAssembler.agalcode);
- }
-
- setProgram(__renderStage3DProgram);
-
- setBlendFactors(ONE, ZERO);
- setColorMask(true, true, true, true);
- setCulling(NONE);
- setDepthTest(false, ALWAYS);
- setStencilActions();
- setStencilReferenceValue(0, 0, 0);
- setScissorRectangle(null);
-
- setTextureAt(0, context.__frontBufferTexture);
- setVertexBufferAt(0, stage3D.__vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
- setVertexBufferAt(1, stage3D.__vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
- setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, stage3D.__renderTransform, true);
- drawTriangles(stage3D.__indexBuffer);
-
- __present = true;
- }
- }
-
- @:noCompletion private function __setGLBlend(enable:Bool):Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.__enableGLBlend != enable #end)
- {
- if (enable)
- {
- gl.enable(gl.BLEND);
- }
- else
- {
- gl.disable(gl.BLEND);
- }
- __contextState.__enableGLBlend = enable;
- }
- }
-
- @:noCompletion private function __setGLBlendEquation(value:Int):Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.__glBlendEquation != value #end)
- {
- gl.blendEquation(value);
- __contextState.__glBlendEquation = value;
- }
- }
-
- @:noCompletion private function __setGLCullFace(enable:Bool):Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.__enableGLCullFace != enable #end)
- {
- if (enable)
- {
- gl.enable(gl.CULL_FACE);
- }
- else
- {
- gl.disable(gl.CULL_FACE);
- }
- __contextState.__enableGLCullFace = enable;
- }
- }
-
- @:noCompletion private function __setGLDepthTest(enable:Bool):Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.__enableGLDepthTest != enable #end)
- {
- if (enable)
- {
- gl.enable(gl.DEPTH_TEST);
- }
- else
- {
- gl.disable(gl.DEPTH_TEST);
- }
- __contextState.__enableGLDepthTest = enable;
- }
- }
-
- @:noCompletion private function __setGLFrontFace(counterClockWise:Bool):Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.__frontFaceGLCCW != counterClockWise #end)
- {
- gl.frontFace(counterClockWise ? gl.CCW : gl.CW);
- __contextState.__frontFaceGLCCW = counterClockWise;
- }
- }
-
- @:noCompletion private function __setGLScissorTest(enable:Bool):Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.__enableGLScissorTest != enable #end)
- {
- if (enable)
- {
- gl.enable(gl.SCISSOR_TEST);
- }
- else
- {
- gl.disable(gl.SCISSOR_TEST);
- }
- __contextState.__enableGLScissorTest = enable;
- }
- }
-
- @:noCompletion private function __setGLStencilTest(enable:Bool):Void
- {
- if (#if openfl_disable_context_cache true #else __contextState.__enableGLStencilTest != enable #end)
- {
- if (enable)
- {
- gl.enable(gl.STENCIL_TEST);
- }
- else
- {
- gl.disable(gl.STENCIL_TEST);
- }
- __contextState.__enableGLStencilTest = enable;
- }
- }
-
- // Get & Set Methods
- @:noCompletion private function get_enableErrorChecking():Bool
- {
- return __enableErrorChecking;
- }
-
- @:noCompletion private function set_enableErrorChecking(value:Bool):Bool
- {
- return __enableErrorChecking = value;
- }
-
- @:noCompletion private function get_totalGPUMemory():Int
- {
- if (__glMemoryCurrentAvailable != -1)
- {
- // TODO: Return amount used by this application only
- var current = gl.getParameter(__glMemoryCurrentAvailable);
- var total = gl.getParameter(__glMemoryTotalAvailable);
-
- if (total > 0)
- {
- return (total - current) * 1024;
- }
- }
- return 0;
- }
-}
-#else
-typedef Context3D = flash.display3D.Context3D;
-#end
diff --git a/source/openfl/display3D/utils/UInt8Buff.hx b/source/openfl/display3D/utils/UInt8Buff.hx
deleted file mode 100644
index 46a820477c..0000000000
--- a/source/openfl/display3D/utils/UInt8Buff.hx
+++ /dev/null
@@ -1,95 +0,0 @@
-package openfl.display3D.utils;
-
-import flixel.util.FlxPool.IFlxPooled;
-import flixel.util.FlxPool;
-import lime.utils.UInt8Array;
-
-class UInt8Buff implements IFlxPooled
-{
- static var _pools:Map> = [];
-
- public static function getPool(length:Int) {
- if(!_pools.exists(length)) {
- _pools.set(length, new FlxPool(UInt8Buff));
- }
- return _pools.get(length);
- }
-
- /**
- * Recycle or create new FlxRect.
- * Be sure to put() them back into the pool after you're done with them!
- */
- public static inline function get(length:Int):UInt8Buff
- {
- var rect = getPool(length).get().set(length);
- rect._inPool = false;
- if(rect.buffer == null)
- rect.buffer = new UInt8Array(length);
- return rect;
- }
-
- /**
- * Recycle or create a new FlxRect which will automatically be released
- * to the pool when passed into a flixel function.
- */
- public static inline function weak(length:Int):UInt8Buff
- {
- var rect = get(length);
- rect._weak = true;
- return rect;
- }
-
- var _weak:Bool = false;
- var _inPool:Bool = false;
-
- public var length(get, never):Int;
-
- inline function get_length() {
- return buffer.length;
- }
-
- public var buffer:UInt8Array;
-
- @:keep
- private function new(length:Int)
- {
- set(length);
- }
-
- /**
- * Add this FlxRect to the recycling pool.
- */
- public inline function put():Void
- {
- if (!_inPool)
- {
- _inPool = true;
- _weak = false;
- getPool(length).putUnsafe(this);
- }
- }
-
- /**
- * Add this FlxPoint to the recycling pool if it's a weak reference (allocated via weak()).
- */
- public inline function putWeak():Void
- {
- if (_weak)
- {
- put();
- }
- }
-
- /**
- * Does nothing
- */
- private inline function set(_length:Int):UInt8Buff
- {
- return this;
- }
-
- /**
- * Necessary for IFlxDestroyable.
- */
- public function destroy() {}
-}
diff --git a/source/openfl/media/Sound.hx b/source/openfl/media/Sound.hx
deleted file mode 100644
index 808399c380..0000000000
--- a/source/openfl/media/Sound.hx
+++ /dev/null
@@ -1,705 +0,0 @@
-package openfl.media;
-
-#if !flash
-import haxe.Int64;
-import openfl.events.Event;
-import openfl.events.EventDispatcher;
-import openfl.events.IOErrorEvent;
-import openfl.net.URLRequest;
-import openfl.utils.ByteArray;
-import openfl.utils.Future;
-#if lime
-import openfl.utils._internal.UInt8Array;
-import lime.media.AudioBuffer;
-import lime.media.AudioSource;
-#end
-
-/**
- The Sound class lets you work with sound in an application. The Sound class
- lets you create a Sound object, load and play an external MP3 file into
- that object, close the sound stream, and access data about the sound, such
- as information about the number of bytes in the stream and ID3 metadata.
- More detailed control of the sound is performed through the sound source
- - the SoundChannel or Microphone object for the sound - and through the
- properties in the SoundTransform class that control the output of the sound
- to the computer's speakers.
-
- In Flash Player 10 and later and AIR 1.5 and later, you can also use
- this class to work with sound that is generated dynamically. In this case,
- the Sound object uses the function you assign to a `sampleData`
- event handler to poll for sound data. The sound is played as it is
- retrieved from a ByteArray object that you populate with sound data. You
- can use `Sound.extract()` to extract sound data from a Sound
- object, after which you can manipulate it before writing it back to the
- stream for playback.
-
- To control sounds that are embedded in a SWF file, use the properties in
- the SoundMixer class.
-
- **Note**: The ActionScript 3.0 Sound API differs from ActionScript
- 2.0. In ActionScript 3.0, you cannot take sound objects and arrange them in
- a hierarchy to control their properties.
-
- When you use this class, consider the following security model:
-
-
- * Loading and playing a sound is not allowed if the calling file is in
- a network sandbox and the sound file to be loaded is local.
- * By default, loading and playing a sound is not allowed if the calling
- file is local and tries to load and play a remote sound. A user must grant
- explicit permission to allow this type of access.
- * Certain operations dealing with sound are restricted. The data in a
- loaded sound cannot be accessed by a file in a different domain unless you
- implement a cross-domain policy file. Sound-related APIs that fall under
- this restriction are `Sound.id3`,
- `SoundMixer.computeSpectrum()`,
- `SoundMixer.bufferTime`, and the `SoundTransform`
- class.
-
-
- However, in Adobe AIR, content in the `application` security
- sandbox(content installed with the AIR application) are not restricted by
- these security limitations.
-
- For more information related to security, see the Flash Player Developer
- Center Topic: [Security](http://www.adobe.com/go/devnet_security_en).
-
- @event complete Dispatched when data has loaded successfully.
- @event id3 Dispatched by a Sound object when ID3 data is available
- for an MP3 sound.
- @event ioError Dispatched when an input/output error occurs that causes
- a load operation to fail.
- @event open Dispatched when a load operation starts.
- @event progress Dispatched when data is received as a load operation
- progresses.
- @event sampleData Dispatched when the runtime requests new audio data.
-**/
-#if !openfl_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(lime.media.AudioBuffer)
-@:access(lime.utils.AssetLibrary)
-@:access(openfl.media.SoundMixer)
-@:access(openfl.media.SoundChannel.new)
-@:autoBuild(openfl.utils._internal.AssetsMacro.embedSound())
-class Sound extends EventDispatcher
-{
- /**
- Returns the currently available number of bytes in this sound object. This
- property is usually useful only for externally loaded files.
- **/
- public var bytesLoaded(default, null):Int;
-
- /**
- Returns the total number of bytes in this sound object.
- **/
- public var bytesTotal(default, null):Int;
-
- /**
- Provides access to the metadata that is part of an MP3 file.
- MP3 sound files can contain ID3 tags, which provide metadata about the
- file. If an MP3 sound that you load using the `Sound.load()` method
- contains ID3 tags, you can query these properties. Only ID3 tags that
- use the UTF-8 character set are supported.
-
- Flash Player 9 and later and AIR support ID3 2.0 tags, specifically
- 2.3 and 2.4. The following tables list the standard ID3 2.0 tags and
- the type of content the tags represent. The `Sound.id3` property
- provides access to these tags through the format `my_sound.id3.COMM`,
- `my_sound.id3.TIME`, and so on. The first table describes tags that
- can be accessed either through the ID3 2.0 property name or the
- ActionScript property name. The second table describes ID3 tags that
- are supported but do not have predefined properties in ActionScript.
-
- | ID3 2.0 tag | Corresponding Sound class property |
- | --- | --- |
- | COMM | Sound.id3.comment |
- | TALB | Sound.id3.album |
- | TCON | Sound.id3.genre |
- | TIT2 | Sound.id3.songName |
- | TPE1 | Sound.id3.artist |
- | TRCK | Sound.id3.track |
- | TYER | Sound.id3.year |
-
- The following table describes ID3 tags that are supported but do not
- have predefined properties in the Sound class. You access them by
- calling `mySound.id3.TFLT`, `mySound.id3.TIME`, and so on. **NOTE:**
- None of these tags are supported in Flash Lite 4.
-
- | Property | Description |
- | --- | --- |
- | TFLT | File type |
- | TIME | Time |
- | TIT1 | Content group description |
- | TIT2 | Title/song name/content description |
- | TIT3 | Subtitle/description refinement |
- | TKEY | Initial key |
- | TLAN | Languages |
- | TLEN | Length |
- | TMED | Media type |
- | TOAL | Original album/movie/show title |
- | TOFN | Original filename |
- | TOLY | Original lyricists/text writers |
- | TOPE | Original artists/performers |
- | TORY | Original release year |
- | TOWN | File owner/licensee |
- | TPE1 | Lead performers/soloists |
- | TPE2 | Band/orchestra/accompaniment |
- | TPE3 | Conductor/performer refinement |
- | TPE4 | Interpreted, remixed, or otherwise modified by |
- | TPOS | Part of a set |
- | TPUB | Publisher |
- | TRCK | Track number/position in set |
- | TRDA | Recording dates |
- | TRSN | Internet radio station name |
- | TRSO | Internet radio station owner |
- | TSIZ | Size |
- | TSRC | ISRC (international standard recording code) |
- | TSSE | Software/hardware and settings used for encoding |
- | TYER | Year |
- | WXXX | URL link frame |
-
- When using this property, consider the Flash Player security model:
-
- * The `id3` property of a Sound object is always permitted for SWF
- files that are in the same security sandbox as the sound file. For
- files in other sandboxes, there are security checks.
- * When you load the sound, using the `load()` method of the Sound
- class, you can specify a `context` parameter, which is a
- SoundLoaderContext object. If you set the `checkPolicyFile` property
- of the SoundLoaderContext object to `true`, Flash Player checks for a
- URL policy file on the server from which the sound is loaded. If a
- policy file exists and permits access from the domain of the loading
- SWF file, then the file is allowed to access the `id3` property of the
- Sound object; otherwise it is not.
-
- However, in Adobe AIR, content in the `application` security sandbox
- (content installed with the AIR application) are not restricted by
- these security limitations.
-
- For more information related to security, see the Flash Player
- Developer Center Topic: Security.
- **/
- public var id3(get, never):ID3Info;
-
- /**
- Returns the buffering state of external MP3 files. If the value is
- `true`, any playback is currently suspended while the object
- waits for more data.
- **/
- public var isBuffering(default, null):Bool;
-
- // @:noCompletion @:dox(hide) @:require(flash10_1) public var isURLInaccessible (default, null):Bool;
-
- /**
- The length of the current sound in milliseconds.
- **/
- public var length(get, never):Float;
-
- /**
- The URL from which this sound was loaded. This property is applicable only
- to Sound objects that were loaded using the `Sound.load()`
- method. For Sound objects that are associated with a sound asset from a
- SWF file's library, the value of the `url` property is
- `null`.
-
- When you first call `Sound.load()`, the `url`
- property initially has a value of `null`, because the final URL
- is not yet known. The `url` property will have a non-null value
- as soon as an `open` event is dispatched from the Sound
- object.
-
- The `url` property contains the final, absolute URL from
- which a sound was loaded. The value of `url` is usually the
- same as the value passed to the `stream` parameter of
- `Sound.load()`. However, if you passed a relative URL to
- `Sound.load()` the value of the `url` property
- represents the absolute URL. Additionally, if the original URL request is
- redirected by an HTTP server, the value of the `url` property
- reflects the final URL from which the sound file was actually downloaded.
- This reporting of an absolute, final URL is equivalent to the behavior of
- `LoaderInfo.url`.
-
- In some cases, the value of the `url` property is truncated;
- see the `isURLInaccessible` property for details.
- **/
- public var url(default, null):String;
-
- #if lime
- @:noCompletion private var __buffer:AudioBuffer;
- #end
-
- #if openfljs
- @:noCompletion private static function __init__()
- {
- untyped Object.defineProperties(Sound.prototype, {
- "id3": {get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_id3 (); }")},
- "length": {get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_length (); }")},
- });
- }
- #end
-
- /**
- Creates a new Sound object. If you pass a valid URLRequest object to the
- Sound constructor, the constructor automatically calls the
- `load()` function for the Sound object. If you do not pass a
- valid URLRequest object to the Sound constructor, you must call the
- `load()` function for the Sound object yourself, or the stream
- will not load.
-
- Once `load()` is called on a Sound object, you can't later
- load a different sound file into that Sound object. To load a different
- sound file, create a new Sound object.
- In Flash Player 10 and later and AIR 1.5 and later, instead of using
- `load()`, you can use the `sampleData` event handler
- to load sound dynamically into the Sound object.
-
- @param stream The URL that points to an external MP3 file.
- @param context An optional SoundLoader context object, which can define
- the buffer time(the minimum number of milliseconds of MP3
- data to hold in the Sound object's buffer) and can specify
- whether the application should check for a cross-domain
- policy file prior to loading the sound.
- **/
- public function new(stream:URLRequest = null, context:SoundLoaderContext = null)
- {
- super(this);
-
- bytesLoaded = 0;
- bytesTotal = 0;
- isBuffering = false;
- url = null;
-
- if (stream != null)
- {
- load(stream, context);
- }
- }
-
- /**
- Closes the stream, causing any download of data to cease. No data may
- be read from the stream after the `close()` method is called.
-
- @throws IOError The stream could not be closed, or the stream was not
- open.
- **/
- public function close():Void
- {
- #if lime
- if (__buffer != null)
- {
- __buffer.dispose();
- __buffer = null;
- }
- #end
- }
-
- #if false
- /**
- Extracts raw sound data from a Sound object.
- This method is designed to be used when you are working with
- dynamically generated audio, using a function you assign to the
- `sampleData` event for a different Sound object. That is, you can use
- this method to extract sound data from a Sound object. Then you can
- write the data to the byte array that another Sound object is using to
- stream dynamic audio.
-
- The audio data is placed in the target byte array starting from the
- current position of the byte array. The audio data is always exposed
- as 44100 Hz Stereo. The sample type is a 32-bit floating-point value,
- which can be converted to a Number using `ByteArray.readFloat()`.
-
- @param target A ByteArray object in which the extracted sound samples
- are placed.
- @param length The number of sound samples to extract. A sample
- contains both the left and right channels × that is,
- two 32-bit floating-point values.
- @return The number of samples written to the ByteArray specified in
- the `target` parameter.
- **/
- // @:noCompletion @:dox(hide) @:require(flash10) public function extract (target:ByteArray, length:Float, startPosition:Float = -1):Float;
- #end
-
- #if lime
- /**
- Creates a new Sound from an AudioBuffer immediately.
-
- @param buffer An AudioBuffer instance
- @returns A new Sound
- **/
- public static function fromAudioBuffer(buffer:AudioBuffer):Sound
- {
- var sound = new Sound();
- sound.__buffer = buffer;
- return sound;
- }
- #end
-
- /**
- Creates a new Sound from a file path synchronously. This means that the
- Sound will be returned immediately (if supported).
-
- HTML5 and Flash do not support creating Sound synchronously, so these targets
- always return `null`.
-
- In order to load files from a remote web address, use the `loadFromFile` method,
- which supports asynchronous loading.
-
- @param path A local file path containing a sound
- @returns A new Sound if successful, or `null` if unsuccessful
- **/
- public static function fromFile(path:String):Sound
- {
- #if lime
- return fromAudioBuffer(AudioBuffer.fromFile(path));
- #else
- return null;
- #end
- }
-
- /**
- Initiates loading of an external MP3 file from the specified URL. If you
- provide a valid URLRequest object to the Sound constructor, the
- constructor calls `Sound.load()` for you. You only need to call
- `Sound.load()` yourself if you don't pass a valid URLRequest
- object to the Sound constructor or you pass a `null` value.
-
- Once `load()` is called on a Sound object, you can't later
- load a different sound file into that Sound object. To load a different
- sound file, create a new Sound object.
-
- When using this method, consider the following security model:
-
- * Calling `Sound.load()` is not allowed if the calling file
- is in the local-with-file-system sandbox and the sound is in a network
- sandbox.
- * Access from the local-trusted or local-with-networking sandbox
- requires permission from a website through a URL policy file.
- * You cannot connect to commonly reserved ports. For a complete list
- of blocked ports, see "Restricting Networking APIs" in the _ActionScript
- 3.0 Developer's Guide_.
- * You can prevent a SWF file from using this method by setting the
- `allowNetworking` parameter of the `object` and
- `embed` tags in the HTML page that contains the SWF
- content.
-
- In Flash Player 10 and later, if you use a multipart Content-Type(for
- example "multipart/form-data") that contains an upload(indicated by a
- "filename" parameter in a "content-disposition" header within the POST
- body), the POST operation is subject to the security rules applied to
- uploads:
-
- * The POST operation must be performed in response to a user-initiated
- action, such as a mouse click or key press.
- * If the POST operation is cross-domain(the POST target is not on the
- same server as the SWF file that is sending the POST request), the target
- server must provide a URL policy file that permits cross-domain
- access.
-
-
- Also, for any multipart Content-Type, the syntax must be valid
- (according to the RFC2046 standards). If the syntax appears to be invalid,
- the POST operation is subject to the security rules applied to
- uploads.
-
- In Adobe AIR, content in the `application` security sandbox
- (content installed with the AIR application) are not restricted by these
- security limitations.
-
- For more information related to security, see the Flash Player
- Developer Center Topic: [Security](http://www.adobe.com/go/devnet_security_en).
-
- @param stream A URL that points to an external MP3 file.
- @param context An optional SoundLoader context object, which can define
- the buffer time(the minimum number of milliseconds of MP3
- data to hold in the Sound object's buffer) and can specify
- whether the application should check for a cross-domain
- policy file prior to loading the sound.
- @throws IOError A network error caused the load to fail.
- @throws IOError The `digest` property of the
- `stream` object is not `null`.
- You should only set the `digest` property
- of a URLRequest object when calling the
- `URLLoader.load()` method when loading a
- SWZ file(an Adobe platform component).
- @throws SecurityError Local untrusted files may not communicate with the
- Internet. You can work around this by reclassifying
- this file as local-with-networking or trusted.
- @throws SecurityError You cannot connect to commonly reserved ports. For a
- complete list of blocked ports, see "Restricting
- Networking APIs" in the _ActionScript 3.0
- Developer's Guide_.
- **/
- public function load(stream:URLRequest, context:SoundLoaderContext = null):Void
- {
- url = stream.url;
-
- #if lime
- #if (js && html5)
- var defaultLibrary = lime.utils.Assets.getLibrary("default");
-
- if (defaultLibrary != null && defaultLibrary.cachedAudioBuffers.exists(url))
- {
- AudioBuffer_onURLLoad(defaultLibrary.cachedAudioBuffers.get(url));
- }
- else
- {
- AudioBuffer.loadFromFile(url).onComplete(AudioBuffer_onURLLoad).onError(function(_)
- {
- AudioBuffer_onURLLoad(null);
- });
- }
- #else
- AudioBuffer.loadFromFile(url).onComplete(AudioBuffer_onURLLoad).onError(function(_)
- {
- AudioBuffer_onURLLoad(null);
- });
- #end
- #end
- }
-
- /**
- Load MP3 sound data from a ByteArray object into a Sound object. The data will be read from the current
- ByteArray position and will leave the ByteArray position at the end of the specified bytes length once
- finished. If the MP3 sound data contains ID3 data ID3 events will be dispatched during this function call.
- This function will throw an exception if the ByteArray object does not contain enough data.
-
- @param bytes
- @param bytesLength
- **/
- public function loadCompressedDataFromByteArray(bytes:ByteArray, bytesLength:Int):Void
- {
- if (bytes == null || bytesLength <= 0)
- {
- dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
- return;
- }
-
- if (bytes.position > 0 || bytes.length > bytesLength)
- {
- var copy = new ByteArray(bytesLength);
- copy.writeBytes(bytes, bytes.position, bytesLength);
- bytes = copy;
- }
-
- #if lime
- __buffer = AudioBuffer.fromBytes(bytes);
-
- if (__buffer == null)
- {
- dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
- }
- else
- {
- dispatchEvent(new Event(Event.COMPLETE));
- }
- #else
- dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
- #end
- }
-
- /**
- Creates a new Sound from a file path or web address asynchronously. The file
- load will occur in the background.
-
- Progress, completion and error callbacks will be dispatched in the current
- thread using callbacks attached to a returned Future object.
-
- @param path A local file path or web address containing a sound
- @returns A Future Sound
- **/
- public static function loadFromFile(path:String):Future
- {
- #if lime
- return AudioBuffer.loadFromFile(path).then(function(audioBuffer)
- {
- return Future.withValue(fromAudioBuffer(audioBuffer));
- });
- #else
- return cast Future.withError("Cannot load audio file");
- #end
- }
-
- /**
- Creates a new Sound from a set of file paths or web addresses asynchronously.
- The audio backend will choose the first compatible file format, and will load the file
- it selects in the background.
-
- Progress, completion and error callbacks will be dispatched in the current
- thread using callbacks attached to a returned Future object.
-
- @param paths A set of local file paths or web addresses containing sound
- @returns A Future Sound
- **/
- public static function loadFromFiles(paths:Array):Future
- {
- #if lime
- return AudioBuffer.loadFromFiles(paths).then(function(audioBuffer)
- {
- return Future.withValue(fromAudioBuffer(audioBuffer));
- });
- #else
- return cast Future.withError("Cannot load audio files");
- #end
- }
-
- /**
- Load PCM 32-bit floating point sound data from a ByteArray object into a Sound object. The data will be read
- from the current ByteArray position and will leave the ByteArray position at the end of the specified sample
- length multiplied by either 1 channel or 2 channels if the stereo flag is set once finished.
-
- Starting with Flash Player 11.8, the amount of audio data that can be passed to this function is limited. For
- SWF versions >= 21, this function throws an exception if the amount of audio data passed into this function is
- more than 1800 seconds. That is, samples / sampleRate should be less than or equal to 1800. For swf versions <
- 21, the runtime fails silently if the amount of audio data passed in is more than 12000 seconds. This is
- provided only for backward compatibility.
-
- This function throws an exception if the ByteArray object does not contain enough data.
-
- @param bytes
- @param samples
- @param format
- @param stereo
- @param sampleRate
- **/
- public function loadPCMFromByteArray(bytes:ByteArray, samples:Int, format:String = "float", stereo:Bool = true, sampleRate:Float = 44100):Void
- {
- if (bytes == null)
- {
- dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
- return;
- }
-
- var bitsPerSample = (format == "float" ? 32 : 16); // "short"
- var channels = (stereo ? 2 : 1);
- var bytesLength = Std.int(samples * channels * (bitsPerSample / 8));
-
- if (bytes.position > 0 || bytes.length > bytesLength)
- {
- var copy = new ByteArray(bytesLength);
- copy.writeBytes(bytes, bytes.position, bytesLength);
- bytes = copy;
- }
-
- #if lime
- var audioBuffer = new AudioBuffer();
- audioBuffer.bitsPerSample = bitsPerSample;
- audioBuffer.channels = channels;
- audioBuffer.data = new UInt8Array(bytes);
- audioBuffer.sampleRate = Std.int(sampleRate);
-
- __buffer = audioBuffer;
-
- dispatchEvent(new Event(Event.COMPLETE));
- #else
- dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
- #end
- }
-
- /**
- Generates a new SoundChannel object to play back the sound. This method
- returns a SoundChannel object, which you access to stop the sound and to
- monitor volume.(To control the volume, panning, and balance, access the
- SoundTransform object assigned to the sound channel.)
-
- @param startTime The initial position in milliseconds at which playback
- should start.
- @param loops Defines the number of times a sound loops back to the
- `startTime` value before the sound channel
- stops playback.
- @param sndTransform The initial SoundTransform object assigned to the
- sound channel.
- @return A SoundChannel object, which you use to control the sound. This
- method returns `null` if you have no sound card or if
- you run out of available sound channels. The maximum number of
- sound channels available at once is 32.
- **/
- public function play(startTime:Float = 0.0, loops:Int = 0, sndTransform:SoundTransform = null):SoundChannel
- {
- #if lime
- if (__buffer == null || SoundMixer.__soundChannels.length >= SoundMixer.MAX_ACTIVE_CHANNELS)
- {
- return null;
- }
-
- if (sndTransform == null)
- {
- sndTransform = new SoundTransform();
- }
- else
- {
- sndTransform = sndTransform.clone();
- }
-
- var pan = SoundMixer.__soundTransform.pan + sndTransform.pan;
-
- if (pan > 1) pan = 1;
- else if (pan < -1) pan = -1;
-
- var volume = SoundMixer.__soundTransform.volume * sndTransform.volume;
-
- var source = new AudioSource(__buffer);
- source.offset = Std.int(startTime);
- if (loops > 1) source.loops = loops - 1;
-
- source.gain = volume;
-
- var position = source.position;
- position.x = pan;
- position.z = -1 * Math.sqrt(1 - Math.pow(pan, 2));
- source.position = position;
-
- return new SoundChannel(source, sndTransform);
- #else
- return null;
- #end
- }
-
- // Get & Set Methods
- @:noCompletion private function get_id3():ID3Info
- {
- return new ID3Info();
- }
-
- @:noCompletion private function get_length():Float
- {
- #if lime
- if (__buffer != null)
- {
- #if (js && html5 && howlerjs)
- return __buffer.src.duration() * 1000;
- #else
- if (__buffer.data != null) return (__buffer.data.length >> 0) / __buffer.channels / (__buffer.bitsPerSample >> 3) / __buffer.sampleRate * 1000;
- else if (__buffer.__srcVorbisFile != null) {
- var x = __buffer.__srcVorbisFile.pcmTotal();
- return (x.high * 4294967296. + (x.low >> 0)) / __buffer.sampleRate * 1000;
- }
- #end
- }
- #end
-
- return 0;
- }
-
- // Event Handlers
- #if lime
- @:noCompletion private function AudioBuffer_onURLLoad(buffer:AudioBuffer):Void
- {
- if (buffer == null)
- {
- dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
- }
- else
- {
- __buffer = buffer;
- dispatchEvent(new Event(Event.COMPLETE));
- }
- }
- #end
-}
-#else
-typedef Sound = flash.media.Sound;
-#end
diff --git a/source/openfl/media/SoundChannel.hx b/source/openfl/media/SoundChannel.hx
deleted file mode 100644
index cb054f93d5..0000000000
--- a/source/openfl/media/SoundChannel.hx
+++ /dev/null
@@ -1,400 +0,0 @@
-package openfl.media;
-
-#if !flash
-import haxe.Int64;
-
-import openfl.events.Event;
-import openfl.events.EventDispatcher;
-#if lime
-import lime.media.AudioSource;
-import lime.media.openal.AL;
-#end
-
-/**
- The SoundChannel class controls a sound in an application. Every sound is
- assigned to a sound channel, and the application can have multiple sound
- channels that are mixed together. The SoundChannel class contains a
- `stop()` method, properties for monitoring the amplitude
- (volume) of the channel, and a property for assigning a SoundTransform
- object to the channel.
-
- @event soundComplete Dispatched when a sound has finished playing.
-
- @see [Playing sounds](https://books.openfl.org/openfl-developers-guide/working-with-sound/playing-sounds.html)
- @see `openfl.media.Sound`
-**/
-#if !openfl_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-#if lime_cffi
-@:access(lime._internal.backend.native.NativeAudioSource)
-@:access(lime.media.AudioSource)
-#end
-@:access(openfl.media.Sound)
-@:access(openfl.media.SoundMixer)
-@:final @:keep class SoundChannel extends EventDispatcher
-{
- /**
- The current amplitude(volume) of the left channel, from 0(silent) to 1
- (full amplitude).
- **/
- public var leftPeak(get, null):Float;
-
- /**
- The current amplitude(volume) of the right channel, from 0(silent) to 1
- (full amplitude).
- **/
- public var rightPeak(get, null):Float;
-
- /**
- When the sound is playing, the `position` property indicates in
- milliseconds the current point that is being played in the sound file.
- When the sound is stopped or paused, the `position` property
- indicates the last point that was played in the sound file.
-
- A common use case is to save the value of the `position`
- property when the sound is stopped. You can resume the sound later by
- restarting it from that saved position.
-
- If the sound is looped, `position` is reset to 0 at the
- beginning of each loop.
- **/
- public var position(get, set):Float;
-
- /**
- The SoundTransform object assigned to the sound channel. A SoundTransform
- object includes properties for setting volume, panning, left speaker
- assignment, and right speaker assignment.
- **/
- public var soundTransform(get, set):SoundTransform;
-
- /**
- self explanatory
- */
- public var loopTime(get, set):Float;
- public var endTime(get, set):Null;
- public var pitch(get, set):Float;
- public var loops(get, set):Int;
-
- @:noCompletion private var __sound:Sound;
- @:noCompletion private var __isValid:Bool;
- @:noCompletion private var __soundTransform:SoundTransform;
- @:noCompletion private var __lastPeakTime:Float;
- @:noCompletion private var __leftPeak:Float;
- @:noCompletion private var __rightPeak:Float;
- #if lime
- @:noCompletion private var __source:AudioSource;
- @:noCompletion private var __audioSource(get, set):AudioSource; // forward??? compatibility??
- #end
-
- #if openfljs
- @:noCompletion private static function __init__()
- {
- untyped Object.defineProperties(SoundChannel.prototype, {
- "position": {
- get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_position (); }"),
- set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_position (v); }")
- },
- "soundTransform": {
- get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_soundTransform (); }"),
- set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_soundTransform (v); }")
- },
- });
- }
- #end
-
- @:noCompletion private function new(source:#if lime AudioSource #else Dynamic #end = null, soundTransform:SoundTransform = null):Void
- {
- super(this);
-
- if (soundTransform != null) __soundTransform = soundTransform;
- else __soundTransform = new SoundTransform();
-
- __initAudioSource(source);
-
- SoundMixer.__registerSoundChannel(this);
- }
-
- /**
- Stops the sound playing in the channel.
- **/
- public function stop():Void
- {
- SoundMixer.__unregisterSoundChannel(this);
-
- if (!__isValid) return;
-
- #if lime
- __source.stop();
- #end
- __dispose();
- }
-
- @:noCompletion private function __dispose():Void
- {
- if (!__isValid) return;
-
- #if lime
- __source.onComplete.remove(source_onComplete);
- __source.dispose();
- __source = null;
- #end
- __isValid = false;
- }
-
- @:noCompletion private function __updateTransform():Void
- {
- this.soundTransform = soundTransform;
- }
-
- @:noCompletion private function __updatePeaks(time:Float):Bool
- {
- if (Math.abs(time - __lastPeakTime) < 8) return false;
- __lastPeakTime = time;
-
- #if !macro
- if (!__isValid) return false;
-
- var buffer = __source.buffer;
- var wordSize = buffer.bitsPerSample >> 3, byteSize = 1 << (buffer.bitsPerSample - 1);
- var pos = Math.floor(time * buffer.sampleRate / 1000 * buffer.channels * wordSize);
- var leftMin = 0, leftMax = 0, rightMin = 0, rightMax = 0, size = 0, buf;
-
- #if lime_cffi
- var backend = __source.__backend, i = 0;
- if (backend.streamed) {
- size = backend.bufferLengths[i = backend.bufferLengths.length - backend.requestBuffers];
- buf = backend.bufferDatas[i].buffer;
- pos -= Math.floor(backend.bufferTimes[i] * buffer.sampleRate * buffer.channels * wordSize);
- while (pos > size) {
- if (++i >= backend.bufferLengths.length) return false;
- pos -= size;
- buf = backend.bufferDatas[i].buffer;
- size = backend.bufferLengths[i];
- }
- }
- else
- #end {
- buf = buffer.data #if !js .buffer #end;
- size = #if js buf.byteLength #else buf.length #end;
- }
-
- var s = Math.floor(Math.min(buffer.sampleRate / 80, 512)), c = 0, b;
- pos -= pos % (buffer.channels * wordSize);
-
- while (s > 0) {
- b = funkin.backend.utils.AudioAnalyzer.getByte(buf, pos, wordSize);
- if (c % 2 == 0) ((b > leftMax) ? (leftMax = b) : (if ((b = -b) > leftMin) (leftMin = b)));
- else ((b > rightMax) ? (rightMax = b) : (if ((b = -b) > rightMin) (rightMin = b)));
- if ((pos += wordSize) >= size) #if lime_cffi {
- if (!backend.streamed || ++i >= backend.bufferLengths.length) break;
- pos = 0;
- buf = backend.bufferDatas[i].buffer;
- size = backend.bufferLengths[i];
- }
- #else break; #end
-
- if (++c > buffer.channels) {
- c = 0;
- s--;
- }
- }
-
- if (buffer.channels == 1) __rightPeak = (__leftPeak = (leftMax + leftMin) / byteSize);
- else {
- __leftPeak = (leftMax + leftMin) / byteSize;
- __rightPeak = (rightMax + rightMin) / byteSize;
- }
- #end
-
- return true;
- }
-
- @:noCompletion private function __initAudioSource(source:#if lime AudioSource #else Dynamic #end):Void
- {
- #if lime
- __source = source;
- if (__source == null)
- {
- return;
- }
-
- __source.onComplete.add(source_onComplete);
- __isValid = true;
-
- __source.play();
- #end
- }
-
- // Get & Set Methods
- @:noCompletion private function get_position():Float
- {
- if (!__isValid) return 0;
-
- #if lime
- return __source.currentTime + __source.offset;
- #else
- return 0;
- #end
- }
-
- @:noCompletion private function set_position(value:Float):Float
- {
- if (!__isValid) return 0;
-
- #if lime
- __source.currentTime = value - __source.offset;
- #end
- return value;
- }
-
- @:noCompletion private function get_soundTransform():SoundTransform
- {
- return __soundTransform.clone();
- }
-
- @:noCompletion private function set_soundTransform(value:SoundTransform):SoundTransform
- {
- if (value != null)
- {
- __soundTransform.pan = value.pan;
- __soundTransform.volume = value.volume;
-
- var pan = SoundMixer.__soundTransform.pan + __soundTransform.pan;
-
- if (pan < -1) pan = -1;
- if (pan > 1) pan = 1;
-
- var volume = SoundMixer.__soundTransform.volume * __soundTransform.volume;
-
- if (__isValid)
- {
- #if lime
- // TODO: implement SoundTransform.leftToRight, etc. with Native setAngles?
- __source.gain = volume;
- __source.pan = pan;
- return value;
- #end
- }
- }
-
- return value;
- }
-
- @:noCompletion private function get_pitch():Float
- {
- if (!__isValid) return 1;
-
- #if lime
- return __source.pitch;
- #else
- return 0;
- #end
- }
-
- @:noCompletion private function set_pitch(value:Float):Float
- {
- if (!__isValid) return 1;
-
- #if lime
- return __source.pitch = value;
- #else
- return 0;
- #end
- }
-
- @:noCompletion private function get_loopTime():Float
- {
- if (!__isValid) return -1;
-
- #if lime
- return __source.loopTime;
- #else
- return -1;
- #end
- }
-
- @:noCompletion private function set_loopTime(value:Float):Float
- {
- if (!__isValid) return -1;
-
- #if lime
- return __source.loopTime = value;
- #else
- return -1;
- #end
- }
-
- @:noCompletion private function get_endTime():Null
- {
- if (!__isValid) return null;
-
- #if lime
- return __source.length;
- #else
- return null;
- #end
- }
-
- @:noCompletion private function set_endTime(value:Null):Null
- {
- if (!__isValid) return null;
-
- #if lime
- return __source.length = value;
- #else
- return null;
- #end
- }
-
- @:noCompletion private function get_loops():Int
- {
- if (!__isValid) return 0;
-
- #if lime
- return __source.loops;
- #else
- return 0;
- #end
- }
-
- @:noCompletion private function set_loops(value:Int):Int
- {
- if (!__isValid) return 0;
-
- #if lime
- return __source.loops = value;
- #else
- return 0;
- #end
- }
-
- @:noCompletion private function get_leftPeak():Float
- {
- __updatePeaks(get_position());
- return __leftPeak * (soundTransform == null ? 1 : soundTransform.volume);
- }
-
- @:noCompletion private function get_rightPeak():Float
- {
- __updatePeaks(get_position());
- return __rightPeak * (soundTransform == null ? 1 : soundTransform.volume);
- }
-
-
- // Event Handlers
- @:noCompletion private function source_onComplete():Void
- {
- SoundMixer.__unregisterSoundChannel(this);
-
- __dispose();
- dispatchEvent(new Event(Event.SOUND_COMPLETE));
- }
-
- @:noCompletion private function get___audioSource():AudioSource return __source;
- @:noCompletion private function set___audioSource(source:AudioSource):AudioSource return __source = source;
-}
-#else
-typedef SoundChannel = flash.media.SoundChannel;
-#end
diff --git a/source/openfl/utils/Assets.hx b/source/openfl/utils/Assets.hx
deleted file mode 100644
index c14b9f2c95..0000000000
--- a/source/openfl/utils/Assets.hx
+++ /dev/null
@@ -1,858 +0,0 @@
-package openfl.utils;
-
-#if !macro
-import funkin.backend.system.Main;
-import funkin.options.Options;
-import funkin.backend.system.OptimizedBitmapData;
-#end
-import openfl.utils._internal.Log;
-import openfl.display.BitmapData;
-import openfl.display.MovieClip;
-import openfl.display.Sprite;
-import openfl.events.Event;
-import openfl.events.EventDispatcher;
-import openfl.media.Sound;
-import openfl.text.Font;
-#if lime
-import lime.app.Promise;
-import lime.utils.AssetLibrary as LimeAssetLibrary;
-import lime.utils.Assets as LimeAssets;
-#end
-#if lime_vorbis
-import lime.media.AudioBuffer;
-import lime.media.vorbis.VorbisFile;
-#end
-
-/**
- The Assets class provides a cross-platform interface to access
- embedded images, fonts, sounds and other resource files.
-
- The contents are populated automatically when an application
- is compiled using the OpenFL command-line tools, based on the
- contents of the *.xml project file.
-
- For most platforms, the assets are included in the same directory
- or package as the application, and the paths are handled
- automatically. For web content, the assets are preloaded before
- the start of the rest of the application. You can customize the
- preloader by extending the `NMEPreloader` class,
- and specifying a custom preloader using
- in the project file.
-**/
-#if !openfl_debug
-@:fileXml('tags="haxe,release"')
-@:noDebug
-#end
-@:access(openfl.display.BitmapData)
-@:access(openfl.display.Sprite)
-@:access(openfl.text.Font)
-@:access(openfl.utils.AssetLibrary)
-class Assets
-{
- public static var cache:IAssetCache = new AssetCache();
-
- @:noCompletion private static var dispatcher:EventDispatcher #if !macro = new EventDispatcher() #end;
- private static var libraryBindings:Map = new Map();
-
- public static function addEventListener(type:String, listener:Dynamic, useCapture:Bool = false, priority:Int = 0, useWeakReference:Bool = false):Void
- {
- #if lime
- if (!LimeAssets.onChange.has(LimeAssets_onChange))
- {
- LimeAssets.onChange.add(LimeAssets_onChange);
- }
- #end
-
- dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
- }
-
- public static function dispatchEvent(event:Event):Bool
- {
- return dispatcher.dispatchEvent(event);
- }
-
- /**
- Returns whether a specific asset exists
- @param id The ID or asset path for the asset
- @param type The asset type to match, or null to match any type
- @return Whether the requested asset ID and type exists
- **/
- public static function exists(id:String, type:AssetType = null):Bool
- {
- #if lime
- return LimeAssets.exists(id, cast type);
- #else
- return false;
- #end
- }
-
- /**
- Gets an instance of an embedded bitmap
- @usage var bitmap = new Bitmap (Assets.getBitmapData ("image.png"));
- @param id The ID or asset path for the bitmap
- @param useCache (Optional) Whether to allow use of the asset cache (Default: true)
- @param pushToGPU Whenever the image should be immediately pushed to GPU.
- @return A new BitmapData object
- **/
- public static function getBitmapData(id:String, useCache:Bool = true, pushToGPU:Bool = true):BitmapData
- {
- #if (lime && tools && !display)
- if (useCache && cache.enabled && cache.hasBitmapData(id))
- {
- var bitmapData = cache.getBitmapData(id);
-
- if (isValidBitmapData(bitmapData) && (pushToGPU || bitmapData.readable))
- {
- return bitmapData;
- }
- }
-
- var image = LimeAssets.getImage(id, false);
-
- if (image != null)
- {
- #if flash
- var bitmapData = image.src;
- #else
- var bitmapData:BitmapData = null;
- #if !macro if (pushToGPU && !Main.forceGPUOnlyBitmapsOff && Options.gpuOnlyBitmaps) {
- bitmapData = new OptimizedBitmapData(0, 0, true, 0);
- bitmapData.__fromImage(image);
- } else #end {
- bitmapData = BitmapData.fromImage(image);
- }
- #end
-
- if (useCache && cache.enabled)
- {
- cache.setBitmapData(id, bitmapData);
- }
-
- return bitmapData;
- }
- #end
-
- return null;
- }
-
- /**
- Gets an instance of an embedded binary asset
- @usage var bytes = Assets.getBytes ("file.zip");
- @param id The ID or asset path for the asset
- @return A new ByteArray object
- **/
- public static function getBytes(id:String):ByteArray
- {
- #if lime
- return LimeAssets.getBytes(id);
- #else
- return null;
- #end
- }
-
- /**
- Gets an instance of an embedded font
- @usage var fontName = Assets.getFont ("font.ttf").fontName;
- @param id The ID or asset path for the font
- @param useCache (Optional) Whether to allow use of the asset cache (Default: true)
- @return A new Font object
- **/
- public static function getFont(id:String, useCache:Bool = true):Font
- {
- #if (lime && tools && !display && !macro)
- if (useCache && cache.enabled && cache.hasFont(id))
- {
- return cache.getFont(id);
- }
-
- var limeFont = LimeAssets.getFont(id, false);
-
- if (limeFont != null)
- {
- #if flash
- var font = limeFont.src;
- #else
- var font = new Font();
- font.__fromLimeFont(limeFont);
- #end
-
- if (useCache && cache.enabled)
- {
- cache.setFont(id, font);
- }
-
- return font;
- }
- #end
-
- return new Font();
- }
-
- public static function getLibrary(name:String):#if lime LimeAssetLibrary #else AssetLibrary #end
- {
- #if lime
- return LimeAssets.getLibrary(name);
- #else
- return null;
- #end
- }
-
- /**
- Gets an instance of an included MovieClip
- @usage var movieClip = Assets.getMovieClip ("library:BouncingBall");
- @param id The ID for the MovieClip
- @return A new MovieClip object
- **/
- public static function getMovieClip(id:String):MovieClip
- {
- #if (lime && tools && !display)
- var libraryName = id.substring(0, id.indexOf(":"));
- var symbolName = id.substr(id.indexOf(":") + 1);
- var limeLibrary = getLibrary(libraryName);
-
- if (limeLibrary != null)
- {
- if ((limeLibrary is AssetLibrary))
- {
- var library:AssetLibrary = cast limeLibrary;
-
- if (library.exists(symbolName, cast AssetType.MOVIE_CLIP))
- {
- if (library.isLocal(symbolName, cast AssetType.MOVIE_CLIP))
- {
- return library.getMovieClip(symbolName);
- }
- else
- {
- Log.error("MovieClip asset \"" + id + "\" exists, but only asynchronously");
- return null;
- }
- }
- }
-
- Log.error("There is no MovieClip asset with an ID of \"" + id + "\"");
- }
- else
- {
- Log.error("There is no asset library named \"" + libraryName + "\"");
- }
- #end
-
- return null;
- }
-
- public static function getMusic(id:String, useCache:Bool = true, staticFallback:Bool = true):Sound
- {
- if (useCache && staticFallback && cache.enabled && cache.hasSound(id)) {
- var sound = cache.getSound(id);
- if (isValidSound(sound)) return sound;
- }
- #if (lime_vorbis && lime > "7.9.0" && !macro)
- if (Options.streamedMusic) {
- var path = getPath(id);
- // TODO: What if it is a WAV or non-Vorbis file?
- var vorbisFile = VorbisFile.fromFile(path);
- if (vorbisFile != null) return Sound.fromAudioBuffer(AudioBuffer.fromVorbisFile(vorbisFile));
- }
- #end
- return if (staticFallback) getSound(id, useCache); else null;
- }
-
- /**
- Gets the file path (if available) for an asset
- @usage var path = Assets.getPath ("file.txt");
- @param id The ID or asset path for the asset
- @return The path to the asset, or null if it does not exist
- **/
- public static function getPath(id:String):String
- {
- #if lime
- return LimeAssets.getPath(id);
- #else
- return null;
- #end
- }
-
- /**
- Gets an instance of an embedded sound
- @usage var sound = Assets.getSound ("sound.wav");
- @param id The ID or asset path for the sound
- @param useCache (Optional) Whether to allow use of the asset cache (Default: true)
- @return A new Sound object
- **/
- public static function getSound(id:String, useCache:Bool = true):Sound
- {
- #if (lime && tools && !display)
- if (useCache && cache.enabled && cache.hasSound(id))
- {
- var sound = cache.getSound(id);
-
- if (isValidSound(sound))
- {
- return sound;
- }
- }
-
- var buffer = LimeAssets.getAudioBuffer(id, false);
-
- if (buffer != null)
- {
- #if flash
- var sound = buffer.src;
- #else
- var sound = Sound.fromAudioBuffer(buffer);
- #end
-
- if (useCache && cache.enabled)
- {
- cache.setSound(id, sound);
- }
-
- return sound;
- }
- #end
-
- return null;
- }
-
- /**
- Gets an instance of an embedded text asset
- @usage var text = Assets.getText ("text.txt");
- @param id The ID or asset path for the asset
- @return A new String object
- **/
- public static function getText(id:String):String
- {
- #if lime
- return LimeAssets.getText(id);
- #else
- return null;
- #end
- }
-
- public static function hasEventListener(type:String):Bool
- {
- return dispatcher.hasEventListener(type);
- }
-
- public static function hasLibrary(name:String):Bool
- {
- #if lime
- return LimeAssets.hasLibrary(name);
- #else
- return false;
- #end
- }
-
- /**
- Connects a user-defined class to a related asset class.
-
- This method call is added to the beginning of user-defined class constructors when
- the `@:bind` meta-data is used. This allows insertion of related asset resources in
- compatible super classes, such as `openfl.display.MovieClip`.
- @param className The registered class name of the asset constructor
- @param instance The current class instance to be bound (default is null)
- @return Whether asset binding was successful
- **/
- public static function initBinding(className:String, instance:Dynamic = null):Void
- {
- if (libraryBindings.exists(className))
- {
- var library = libraryBindings.get(className);
- #if !flash
- if (instance == null)
- {
- Sprite.__constructor = function(instance:Sprite)
- {
- instance.__bind(library, className);
- }
- }
- else
- {
- Sprite.__constructor = null;
- instance.__bind(library, className);
- }
- #else
- // TODO: Consolidate behavior
- library.bind(className);
- #end
- }
- else
- {
- Log.warn("No asset is registered as \"" + className + "\"");
- }
- }
-
- /**
- Returns whether an asset is "local", and therefore can be loaded synchronously
- @param id The ID or asset path for the asset
- @param type The asset type to match, or null to match any type
- @param useCache (Optional) Whether to allow use of the asset cache (Default: true)
- @return Whether the asset is local
- **/
- public static function isLocal(id:String, type:AssetType = null, useCache:Bool = true):Bool
- {
- #if (lime && tools && !display)
- if (useCache && cache.enabled)
- {
- if (type == AssetType.IMAGE || type == null)
- {
- if (cache.hasBitmapData(id)) return true;
- }
-
- if (type == AssetType.FONT || type == null)
- {
- if (cache.hasFont(id)) return true;
- }
-
- if (type == AssetType.SOUND || type == AssetType.MUSIC || type == null)
- {
- if (cache.hasSound(id)) return true;
- }
- }
-
- var libraryName = id.substring(0, id.indexOf(":"));
- var symbolName = id.substr(id.indexOf(":") + 1);
- var library = getLibrary(libraryName);
-
- if (library != null)
- {
- return library.isLocal(symbolName, cast type);
- }
- #end
-
- return false;
- }
-
- @:analyzer(ignore) private static function isValidBitmapData(bitmapData:BitmapData):Bool
- {
- #if (lime && tools && !display)
- #if flash
- try
- {
- bitmapData.width;
- return true;
- }
- catch (e:Dynamic)
- {
- return false;
- }
- #else
- return (bitmapData != null && #if !lime_hybrid bitmapData.image != null #else bitmapData.__handle != null #end);
- #end
- #else
- return true;
- #end
- }
-
- @:noCompletion private static function isValidSound(sound:Sound):Bool
- {
- #if ((tools && !display) && (cpp || neko || nodejs))
- return true;
- // return (sound.__handle != null && sound.__handle != 0);
- #else
- return true;
- #end
- }
-
- /**
- Returns a list of all embedded assets (by type)
- @param type The asset type to match, or null to match any type
- @return An array of asset ID values
- **/
- public static function list(type:AssetType = null):Array
- {
- #if lime
- return LimeAssets.list(cast type);
- #else
- return [];
- #end
- }
-
- /**
- Loads an included bitmap asset asynchronously
- @usage Assets.loadBitmapData ("image.png").onComplete (handleImage);
- @param id The ID or asset path for the asset
- @param useCache (Optional) Whether to allow use of the asset cache (Default: true)
- @return Returns a Future
- **/
- public static function loadBitmapData(id:String, useCache:Null = true):Future
- {
- if (useCache == null) useCache = true;
-
- #if (lime && tools && !display)
- var promise = new Promise();
-
- if (useCache && cache.enabled && cache.hasBitmapData(id))
- {
- var bitmapData = cache.getBitmapData(id);
-
- if (isValidBitmapData(bitmapData))
- {
- promise.complete(bitmapData);
- return promise.future;
- }
- }
-
- LimeAssets.loadImage(id, false).onComplete(function(image)
- {
- if (image != null)
- {
- #if flash
- var bitmapData = image.src;
- #else
- var bitmapData:BitmapData = null;
- #if !macro if (!Main.forceGPUOnlyBitmapsOff && Options.gpuOnlyBitmaps) {
- bitmapData = new OptimizedBitmapData(0, 0, true, 0);
- bitmapData.__fromImage(image);
- } else #end {
- bitmapData = BitmapData.fromImage(image);
- }
- #end
-
- if (useCache && cache.enabled)
- {
- cache.setBitmapData(id, bitmapData);
- }
-
- promise.complete(bitmapData);
- }
- else
- {
- promise.error("[Assets] Could not load Image \"" + id + "\"");
- }
- }).onError(promise.error).onProgress(promise.progress);
-
- return promise.future;
- #else
- return Future.withValue(getBitmapData(id, useCache));
- #end
- }
-
- /**
- Loads an included byte asset asynchronously
- @usage Assets.loadBytes ("file.zip").onComplete (handleBytes);
- @param id The ID or asset path for the asset
- @return Returns a Future
- **/
- public static function loadBytes(id:String):Future
- {
- #if lime
- var promise = new Promise();
- var future = LimeAssets.loadBytes(id);
-
- future.onComplete(function(bytes) promise.complete(bytes));
- future.onProgress(function(progress, total) promise.progress(progress, total));
- future.onError(function(msg) promise.error(msg));
-
- return promise.future;
- #else
- return Future.withValue(getBytes(id));
- #end
- }
-
- /**
- Loads an included font asset asynchronously
- @usage Assets.loadFont ("font.ttf").onComplete (handleFont);
- @param id The ID or asset path for the asset
- @param useCache (Optional) Whether to allow use of the asset cache (Default: true)
- @return Returns a Future
- **/
- public static function loadFont(id:String, useCache:Null = true):Future
- {
- if (useCache == null) useCache = true;
-
- #if (lime && tools && !display && !macro)
- var promise = new Promise();
-
- if (useCache && cache.enabled && cache.hasFont(id))
- {
- promise.complete(cache.getFont(id));
- return promise.future;
- }
-
- LimeAssets.loadFont(id)
- .onComplete(function(limeFont)
- {
- #if flash
- var font = limeFont.src;
- #else
- var font = new Font();
- font.__fromLimeFont(limeFont);
- #end
-
- if (useCache && cache.enabled)
- {
- cache.setFont(id, font);
- }
-
- promise.complete(font);
- })
- .onError(promise.error)
- .onProgress(promise.progress);
-
- return promise.future;
- #else
- return Future.withValue(getFont(id, useCache));
- #end
- }
-
- /**
- Load an included AssetLibrary
- @param name The name of the AssetLibrary to load
- @return Returns a Future
- **/
- public static function loadLibrary(name:String):#if java Future #else Future #end
- {
- #if lime
- return LimeAssets.loadLibrary(name).then(function(library)
- {
- var _library:AssetLibrary = null;
-
- if (library != null)
- {
- if ((library is AssetLibrary))
- {
- _library = cast library;
- }
- else
- {
- _library = new AssetLibrary();
- _library.__proxy = library;
- LimeAssets.registerLibrary(name, _library);
- }
- }
-
- return Future.withValue(_library);
- });
- #else
- return cast Future.withError("Cannot load library");
- #end
- }
-
- /**
- Loads an included music asset asynchronously
- @usage Assets.loadMusic ("music.ogg").onComplete (handleMusic);
- @param id The ID or asset path for the asset
- @param useCache (Optional) Whether to allow use of the asset cache (Default: true)
- @return Returns a Future
- **/
- public static function loadMusic(id:String, useCache:Null = true):Future
- {
- if (useCache == null) useCache = true;
-
- #if lime
- #if !html5
- var promise = new Promise();
-
- LimeAssets.loadAudioBuffer(id, useCache)
- .onComplete(function(buffer)
- {
- if (buffer != null)
- {
- #if flash
- var sound = buffer.src;
- #else
- var sound = Sound.fromAudioBuffer(buffer);
- #end
-
- if (useCache && cache.enabled)
- {
- cache.setSound(id, sound);
- }
-
- promise.complete(sound);
- }
- else
- {
- promise.error("[Assets] Could not load Sound \"" + id + "\"");
- }
- })
- .onError(promise.error)
- .onProgress(promise.progress);
- return promise.future;
- #else
- var future = new Future(function() return getMusic(id, useCache));
- return future;
- #end
- #else
- return Future.withValue(getMusic(id, useCache));
- #end
- }
-
- /**
- Loads an included MovieClip asset asynchronously
- @usage Assets.loadMovieClip ("library:BouncingBall").onComplete (handleMovieClip);
- @param id The ID for the asset
- @param useCache (Optional) Whether to allow use of the asset cache (Default: true)
- @return Returns a Future
- **/
- public static function loadMovieClip(id:String):Future
- {
- #if (lime && tools && !display)
- var promise = new Promise();
-
- var libraryName = id.substring(0, id.indexOf(":"));
- var symbolName = id.substr(id.indexOf(":") + 1);
- var limeLibrary = getLibrary(libraryName);
-
- if (limeLibrary != null)
- {
- if ((limeLibrary is AssetLibrary))
- {
- var library:AssetLibrary = cast limeLibrary;
-
- if (library.exists(symbolName, cast AssetType.MOVIE_CLIP))
- {
- promise.completeWith(library.loadMovieClip(symbolName));
- return promise.future;
- }
- }
-
- promise.error("[Assets] There is no MovieClip asset with an ID of \"" + id + "\"");
- }
- else
- {
- promise.error("[Assets] There is no asset library named \"" + libraryName + "\"");
- }
-
- return promise.future;
- #else
- return Future.withValue(getMovieClip(id));
- #end
- }
-
- /**
- Loads an included sound asset asynchronously
- @usage Assets.loadSound ("sound.wav").onComplete (handleSound);
- @param id The ID or asset path for the asset
- @param useCache (Optional) Whether to allow use of the asset cache (Default: true)
- @return Returns a Future
- **/
- public static function loadSound(id:String, useCache:Null = true):Future
- {
- if (useCache == null) useCache = true;
-
- #if lime
- var promise = new Promise();
-
- LimeAssets.loadAudioBuffer(id, useCache)
- .onComplete(function(buffer)
- {
- if (buffer != null)
- {
- #if flash
- var sound = buffer.src;
- #else
- var sound = Sound.fromAudioBuffer(buffer);
- #end
-
- if (useCache && cache.enabled)
- {
- cache.setSound(id, sound);
- }
-
- promise.complete(sound);
- }
- else
- {
- promise.error("[Assets] Could not load Sound \"" + id + "\"");
- }
- })
- .onError(promise.error)
- .onProgress(promise.progress);
- return promise.future;
- #else
- return Future.withValue(getSound(id, useCache));
- #end
- }
-
- /**
- Loads an included text asset asynchronously
- @usage Assets.loadText ("text.txt").onComplete (handleString);
- @param id The ID or asset path for the asset
- @param useCache (Optional) Whether to allow use of the asset cache (Default: true)
- @return Returns a Future
- **/
- public static function loadText(id:String):Future
- {
- #if lime
- var future = LimeAssets.loadText(id);
- return future;
- #else
- return Future.withValue(getText(id));
- #end
- }
-
- /**
- Registers an AssetLibrary binding for use with @:bind or Assets.bind
- @param className The class name to use for the binding
- @param method The AssetLibrary responsible for the binding
- **/
- public static function registerBinding(className:String, library:AssetLibrary):Void
- {
- libraryBindings.set(className, library);
- }
-
- /**
- Registers a new AssetLibrary with the Assets class
- @param name The name (prefix) to use for the library
- @param library An AssetLibrary instance to register
- **/
- public static function registerLibrary(name:String, library:AssetLibrary):Void
- {
- #if lime
- LimeAssets.registerLibrary(name, library);
- #end
- }
-
- public static function removeEventListener(type:String, listener:Dynamic, capture:Bool = false):Void
- {
- dispatcher.removeEventListener(type, listener, capture);
- }
-
- @:noCompletion private static function resolveClass(name:String):Class
- {
- return Type.resolveClass(name);
- }
-
- @:noCompletion private static function resolveEnum(name:String):Enum
- {
- var value = Type.resolveEnum(name);
-
- #if flash
- if (value == null)
- {
- return cast Type.resolveClass(name);
- }
- #end
-
- return value;
- }
-
- public static function unloadLibrary(name:String):Void
- {
- #if lime
- LimeAssets.unloadLibrary(name);
- #end
- }
-
- /**
- Unregisters an AssetLibrary binding for use with @:bind or Assets.bind
- @param className The class name to use for the binding
- @param method The AssetLibrary responsible for the binding
- **/
- public static function unregisterBinding(className:String, library:AssetLibrary):Void
- {
- if (libraryBindings.exists(className) && libraryBindings.get(className) == library)
- {
- libraryBindings.remove(className);
- }
- }
-
- // Event Handlers
- @:noCompletion private static function LimeAssets_onChange():Void
- {
- dispatchEvent(new Event(Event.CHANGE));
- }
-}